chmod.c 4.9 KB

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