chmod.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  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: "Each MODE is one or more of the letters ugoa, one of the\n"
  26. //usage: "symbols +-= and one or more of the letters rwxst\n"
  27. //usage: "\n -R Recurse"
  28. //usage: IF_DESKTOP(
  29. //usage: "\n -c List changed files"
  30. //usage: "\n -v List all files"
  31. //usage: "\n -f Hide errors"
  32. //usage: )
  33. //usage:
  34. //usage:#define chmod_example_usage
  35. //usage: "$ ls -l /tmp/foo\n"
  36. //usage: "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
  37. //usage: "$ chmod u+x /tmp/foo\n"
  38. //usage: "$ ls -l /tmp/foo\n"
  39. //usage: "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n"
  40. //usage: "$ chmod 444 /tmp/foo\n"
  41. //usage: "$ ls -l /tmp/foo\n"
  42. //usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
  43. #include "libbb.h"
  44. /* This is a NOEXEC applet. Be very careful! */
  45. #define OPT_RECURSE (option_mask32 & 1)
  46. #define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0))
  47. #define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0))
  48. #define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0))
  49. #define OPT_STR "R" IF_DESKTOP("vcf")
  50. /* coreutils:
  51. * chmod never changes the permissions of symbolic links; the chmod
  52. * system call cannot change their permissions. This is not a problem
  53. * since the permissions of symbolic links are never used.
  54. * However, for each symbolic link listed on the command line, chmod changes
  55. * the permissions of the pointed-to file. In contrast, chmod ignores
  56. * symbolic links encountered during recursive directory traversals.
  57. */
  58. static int FAST_FUNC fileAction(struct recursive_state *state,
  59. const char *fileName,
  60. struct stat *statbuf)
  61. {
  62. mode_t newmode;
  63. /* match coreutils behavior */
  64. if (state->depth == 0) {
  65. /* statbuf holds lstat result, but we need stat (follow link) */
  66. if (stat(fileName, statbuf))
  67. goto err;
  68. } else { /* depth > 0: skip links */
  69. if (S_ISLNK(statbuf->st_mode))
  70. return TRUE;
  71. }
  72. newmode = bb_parse_mode((char *)state->userData, statbuf->st_mode);
  73. if (newmode == (mode_t)-1)
  74. bb_error_msg_and_die("invalid mode '%s'", (char *)state->userData);
  75. if (chmod(fileName, newmode) == 0) {
  76. if (OPT_VERBOSE
  77. || (OPT_CHANGED && statbuf->st_mode != newmode)
  78. ) {
  79. printf("mode of '%s' changed to %04o (%s)\n", fileName,
  80. newmode & 07777, bb_mode_string(newmode)+1);
  81. }
  82. return TRUE;
  83. }
  84. err:
  85. if (!OPT_QUIET)
  86. bb_simple_perror_msg(fileName);
  87. return FALSE;
  88. }
  89. int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  90. int chmod_main(int argc UNUSED_PARAM, char **argv)
  91. {
  92. int retval = EXIT_SUCCESS;
  93. char *arg, **argp;
  94. char *smode;
  95. /* Convert first encountered -r into ar, -w into aw etc
  96. * so that getopt would not eat it */
  97. argp = argv;
  98. while ((arg = *++argp)) {
  99. /* Mode spec must be the first arg (sans -R etc) */
  100. /* (protect against mishandling e.g. "chmod 644 -r") */
  101. if (arg[0] != '-') {
  102. arg = NULL;
  103. break;
  104. }
  105. /* An option. Not a -- or valid option? */
  106. if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
  107. arg[0] = 'a';
  108. break;
  109. }
  110. }
  111. /* Parse options */
  112. getopt32(argv, "^" OPT_STR "\0" "-2");
  113. argv += optind;
  114. /* Restore option-like mode if needed */
  115. if (arg) arg[0] = '-';
  116. /* Ok, ready to do the deed now */
  117. smode = *argv++;
  118. do {
  119. if (!recursive_action(*argv,
  120. OPT_RECURSE, // recurse
  121. fileAction, // file action
  122. fileAction, // dir action
  123. smode) // user data
  124. ) {
  125. retval = EXIT_FAILURE;
  126. }
  127. } while (*++argv);
  128. return retval;
  129. }
  130. /*
  131. Security: chmod is too important and too subtle.
  132. This is a test script (busybox chmod versus coreutils).
  133. Run it in empty directory.
  134. #!/bin/sh
  135. t1="/tmp/busybox chmod"
  136. t2="/usr/bin/chmod"
  137. create() {
  138. rm -rf $1; mkdir $1
  139. (
  140. cd $1 || exit 1
  141. mkdir dir
  142. >up
  143. >file
  144. >dir/file
  145. ln -s dir linkdir
  146. ln -s file linkfile
  147. ln -s ../up dir/up
  148. )
  149. }
  150. tst() {
  151. (cd test1; $t1 $1)
  152. (cd test2; $t2 $1)
  153. (cd test1; ls -lR) >out1
  154. (cd test2; ls -lR) >out2
  155. echo "chmod $1" >out.diff
  156. if ! diff -u out1 out2 >>out.diff; then exit 1; fi
  157. rm out.diff
  158. }
  159. echo "If script produced 'out.diff' file, then at least one testcase failed"
  160. create test1; create test2
  161. tst "a+w file"
  162. tst "a-w dir"
  163. tst "a+w linkfile"
  164. tst "a-w linkdir"
  165. tst "-R a+w file"
  166. tst "-R a-w dir"
  167. tst "-R a+w linkfile"
  168. tst "-R a-w linkdir"
  169. tst "a-r,a+x linkfile"
  170. */