chmod.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini chmod implementation for busybox
  4. *
  5. * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  6. *
  7. * Reworked by (C) 2002 Vladimir Oleynik <dzo@simtreas.ru>
  8. * to correctly parse '-rwxgoa'
  9. *
  10. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  11. */
  12. //config:config CHMOD
  13. //config: bool "chmod (5.5 kb)"
  14. //config: default y
  15. //config: help
  16. //config: chmod is used to change the access permission of files.
  17. //applet:IF_CHMOD(APPLET_NOEXEC(chmod, chmod, BB_DIR_BIN, BB_SUID_DROP, chmod))
  18. //kbuild:lib-$(CONFIG_CHMOD) += chmod.o
  19. /* BB_AUDIT SUSv3 compliant */
  20. /* BB_AUDIT GNU defects - unsupported long options. */
  21. /* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
  22. //usage:#define chmod_trivial_usage
  23. //usage: "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..."
  24. //usage:#define chmod_full_usage "\n\n"
  25. //usage: "MODE is octal number (bit pattern sstrwxrwxrwx) or [ugoa]{+|-|=}[rwxXst]"
  26. //usage: "\n"
  27. //next 4 options are the same for chmod/chown/chgrp:
  28. //usage: "\n -R Recurse"
  29. //usage: IF_DESKTOP(
  30. //usage: "\n -c List changed files"
  31. //usage: "\n -v Verbose"
  32. //usage: "\n -f Hide errors"
  33. //usage: )
  34. //usage:
  35. //usage:#define chmod_example_usage
  36. //usage: "$ ls -l /tmp/foo\n"
  37. //usage: "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
  38. //usage: "$ chmod u+x /tmp/foo\n"
  39. //usage: "$ ls -l /tmp/foo\n"
  40. //usage: "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n"
  41. //usage: "$ chmod 444 /tmp/foo\n"
  42. //usage: "$ ls -l /tmp/foo\n"
  43. //usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
  44. #include "libbb.h"
  45. /* This is a NOEXEC applet. Be very careful! */
  46. #define OPT_RECURSE (option_mask32 & 1)
  47. #define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0))
  48. #define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0))
  49. #define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0))
  50. #define OPT_STR "R" IF_DESKTOP("vcf")
  51. /* coreutils:
  52. * chmod never changes the permissions of symbolic links; the chmod
  53. * system call cannot change their permissions. This is not a problem
  54. * since the permissions of symbolic links are never used.
  55. * However, for each symbolic link listed on the command line, chmod changes
  56. * the permissions of the pointed-to file. In contrast, chmod ignores
  57. * symbolic links encountered during recursive directory traversals.
  58. */
  59. static int FAST_FUNC fileAction(struct recursive_state *state,
  60. const char *fileName,
  61. struct stat *statbuf)
  62. {
  63. mode_t newmode;
  64. /* match coreutils behavior */
  65. if (state->depth == 0) {
  66. /* statbuf holds lstat result, but we need stat (follow link) */
  67. if (stat(fileName, statbuf))
  68. goto err;
  69. } else { /* depth > 0: skip links */
  70. if (S_ISLNK(statbuf->st_mode))
  71. return TRUE;
  72. }
  73. newmode = bb_parse_mode((char *)state->userData, statbuf->st_mode);
  74. if (newmode == (mode_t)-1)
  75. bb_error_msg_and_die("invalid mode '%s'", (char *)state->userData);
  76. if (chmod(fileName, newmode) == 0) {
  77. if (OPT_VERBOSE
  78. || (OPT_CHANGED
  79. && (statbuf->st_mode & 07777) != (newmode & 07777))
  80. ) {
  81. char modestr[12];
  82. printf("mode of '%s' changed to %04o (%s)\n", fileName,
  83. newmode & 07777, bb_mode_string(modestr, newmode)+1);
  84. }
  85. return TRUE;
  86. }
  87. err:
  88. if (!OPT_QUIET)
  89. bb_simple_perror_msg(fileName);
  90. return FALSE;
  91. }
  92. int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  93. int chmod_main(int argc UNUSED_PARAM, char **argv)
  94. {
  95. int retval = EXIT_SUCCESS;
  96. char *arg, **argp;
  97. char *smode;
  98. /* Convert first encountered -r into ar, -w into aw etc
  99. * so that getopt would not eat it */
  100. argp = argv;
  101. while ((arg = *++argp)) {
  102. /* Mode spec must be the first arg (sans -R etc) */
  103. /* (protect against mishandling e.g. "chmod 644 -r") */
  104. if (arg[0] != '-') {
  105. arg = NULL;
  106. break;
  107. }
  108. /* An option. Not a -- or valid option? */
  109. if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
  110. arg[0] = 'a';
  111. break;
  112. }
  113. }
  114. /* Parse options */
  115. getopt32(argv, "^" OPT_STR "\0" "-2");
  116. argv += optind;
  117. /* Restore option-like mode if needed */
  118. if (arg) arg[0] = '-';
  119. /* Ok, ready to do the deed now */
  120. smode = *argv++;
  121. do {
  122. if (!recursive_action(*argv,
  123. OPT_RECURSE, // recurse
  124. fileAction, // file action
  125. fileAction, // dir action
  126. smode) // user data
  127. ) {
  128. retval = EXIT_FAILURE;
  129. }
  130. } while (*++argv);
  131. return retval;
  132. }
  133. /*
  134. Security: chmod is too important and too subtle.
  135. This is a test script (busybox chmod versus coreutils).
  136. Run it in empty directory.
  137. #!/bin/sh
  138. t1="/tmp/busybox chmod"
  139. t2="/usr/bin/chmod"
  140. create() {
  141. rm -rf $1; mkdir $1
  142. (
  143. cd $1 || exit 1
  144. mkdir dir
  145. >up
  146. >file
  147. >dir/file
  148. ln -s dir linkdir
  149. ln -s file linkfile
  150. ln -s ../up dir/up
  151. )
  152. }
  153. tst() {
  154. (cd test1; $t1 $1)
  155. (cd test2; $t2 $1)
  156. (cd test1; ls -lR) >out1
  157. (cd test2; ls -lR) >out2
  158. echo "chmod $1" >out.diff
  159. if ! diff -u out1 out2 >>out.diff; then exit 1; fi
  160. rm out.diff
  161. }
  162. echo "If script produced 'out.diff' file, then at least one testcase failed"
  163. create test1; create test2
  164. tst "a+w file"
  165. tst "a-w dir"
  166. tst "a+w linkfile"
  167. tst "a-w linkdir"
  168. tst "-R a+w file"
  169. tst "-R a-w dir"
  170. tst "-R a+w linkfile"
  171. tst "-R a-w linkdir"
  172. tst "a-r,a+x linkfile"
  173. */