parse_mode.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * parse_mode implementation for busybox
  4. *
  5. * Copyright (C) 2003 Manuel Novoa III <mjn3@codepoet.org>
  6. *
  7. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  8. */
  9. /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
  10. #include "libbb.h"
  11. /* This function is used from NOFORK applets. It must not allocate anything */
  12. #define FILEMODEBITS (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
  13. int bb_parse_mode(const char *s, mode_t *current_mode)
  14. {
  15. static const mode_t who_mask[] = {
  16. S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO, /* a */
  17. S_ISUID | S_IRWXU, /* u */
  18. S_ISGID | S_IRWXG, /* g */
  19. S_IRWXO /* o */
  20. };
  21. static const mode_t perm_mask[] = {
  22. S_IRUSR | S_IRGRP | S_IROTH, /* r */
  23. S_IWUSR | S_IWGRP | S_IWOTH, /* w */
  24. S_IXUSR | S_IXGRP | S_IXOTH, /* x */
  25. S_IXUSR | S_IXGRP | S_IXOTH, /* X -- special -- see below */
  26. S_ISUID | S_ISGID, /* s */
  27. S_ISVTX /* t */
  28. };
  29. static const char who_chars[] ALIGN1 = "augo";
  30. static const char perm_chars[] ALIGN1 = "rwxXst";
  31. const char *p;
  32. mode_t wholist;
  33. mode_t permlist;
  34. mode_t new_mode;
  35. char op;
  36. if (((unsigned int)(*s - '0')) < 8) {
  37. unsigned long tmp;
  38. char *e;
  39. tmp = strtoul(s, &e, 8);
  40. if (*e || (tmp > 07777U)) { /* Check range and trailing chars. */
  41. return 0;
  42. }
  43. *current_mode = tmp;
  44. return 1;
  45. }
  46. new_mode = *current_mode;
  47. /* Note: we allow empty clauses, and hence empty modes.
  48. * We treat an empty mode as no change to perms. */
  49. while (*s) { /* Process clauses. */
  50. if (*s == ',') { /* We allow empty clauses. */
  51. ++s;
  52. continue;
  53. }
  54. /* Get a wholist. */
  55. wholist = 0;
  56. WHO_LIST:
  57. p = who_chars;
  58. do {
  59. if (*p == *s) {
  60. wholist |= who_mask[(int)(p-who_chars)];
  61. if (!*++s) {
  62. return 0;
  63. }
  64. goto WHO_LIST;
  65. }
  66. } while (*++p);
  67. do { /* Process action list. */
  68. if ((*s != '+') && (*s != '-')) {
  69. if (*s != '=') {
  70. return 0;
  71. }
  72. /* Since op is '=', clear all bits corresponding to the
  73. * wholist, or all file bits if wholist is empty. */
  74. permlist = ~FILEMODEBITS;
  75. if (wholist) {
  76. permlist = ~wholist;
  77. }
  78. new_mode &= permlist;
  79. }
  80. op = *s++;
  81. /* Check for permcopy. */
  82. p = who_chars + 1; /* Skip 'a' entry. */
  83. do {
  84. if (*p == *s) {
  85. int i = 0;
  86. permlist = who_mask[(int)(p-who_chars)]
  87. & (S_IRWXU | S_IRWXG | S_IRWXO)
  88. & new_mode;
  89. do {
  90. if (permlist & perm_mask[i]) {
  91. permlist |= perm_mask[i];
  92. }
  93. } while (++i < 3);
  94. ++s;
  95. goto GOT_ACTION;
  96. }
  97. } while (*++p);
  98. /* It was not a permcopy, so get a permlist. */
  99. permlist = 0;
  100. PERM_LIST:
  101. p = perm_chars;
  102. do {
  103. if (*p == *s) {
  104. if ((*p != 'X')
  105. || (new_mode & (S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH))
  106. ) {
  107. permlist |= perm_mask[(int)(p-perm_chars)];
  108. }
  109. if (!*++s) {
  110. break;
  111. }
  112. goto PERM_LIST;
  113. }
  114. } while (*++p);
  115. GOT_ACTION:
  116. if (permlist) { /* The permlist was nonempty. */
  117. mode_t tmp = wholist;
  118. if (!wholist) {
  119. mode_t u_mask = umask(0);
  120. umask(u_mask);
  121. tmp = ~u_mask;
  122. }
  123. permlist &= tmp;
  124. if (op == '-') {
  125. new_mode &= ~permlist;
  126. } else {
  127. new_mode |= permlist;
  128. }
  129. }
  130. } while (*s && (*s != ','));
  131. }
  132. *current_mode = new_mode;
  133. return 1;
  134. }