chown.c 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * Mini chown implementation for busybox
  4. *
  5. * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
  6. *
  7. * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
  8. */
  9. /* BB_AUDIT SUSv3 defects - none? */
  10. /* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
  11. #include "libbb.h"
  12. /* This is a NOEXEC applet. Be very careful! */
  13. #define OPT_STR ("Rh" IF_DESKTOP("vcfLHP"))
  14. #define BIT_RECURSE 1
  15. #define OPT_RECURSE (opt & 1)
  16. #define OPT_NODEREF (opt & 2)
  17. #define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0))
  18. #define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0))
  19. #define OPT_QUIET (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0))
  20. /* POSIX options
  21. * -L traverse every symbolic link to a directory encountered
  22. * -H if a command line argument is a symbolic link to a directory, traverse it
  23. * -P do not traverse any symbolic links (default)
  24. * We do not conform to the following:
  25. * "Specifying more than one of -H, -L, and -P is not an error.
  26. * The last option specified shall determine the behavior of the utility." */
  27. /* -L */
  28. #define BIT_TRAVERSE 0x20
  29. #define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0))
  30. /* -H or -L */
  31. #define BIT_TRAVERSE_TOP (0x20|0x40)
  32. #define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0))
  33. #if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
  34. static const char chown_longopts[] ALIGN1 =
  35. "recursive\0" No_argument "R"
  36. "dereference\0" No_argument "\xff"
  37. "no-dereference\0" No_argument "h"
  38. # if ENABLE_DESKTOP
  39. "changes\0" No_argument "c"
  40. "silent\0" No_argument "f"
  41. "quiet\0" No_argument "f"
  42. "verbose\0" No_argument "v"
  43. # endif
  44. ;
  45. #endif
  46. typedef int (*chown_fptr)(const char *, uid_t, gid_t);
  47. struct param_t {
  48. struct bb_uidgid_t ugid;
  49. chown_fptr chown_func;
  50. };
  51. static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf,
  52. void *vparam, int depth UNUSED_PARAM)
  53. {
  54. #define param (*(struct param_t*)vparam)
  55. #define opt option_mask32
  56. uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid;
  57. gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid;
  58. if (param.chown_func(fileName, u, g) == 0) {
  59. if (OPT_VERBOSE
  60. || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
  61. ) {
  62. printf("changed ownership of '%s' to %u:%u\n",
  63. fileName, (unsigned)u, (unsigned)g);
  64. }
  65. return TRUE;
  66. }
  67. if (!OPT_QUIET)
  68. bb_simple_perror_msg(fileName);
  69. return FALSE;
  70. #undef opt
  71. #undef param
  72. }
  73. int chown_main(int argc UNUSED_PARAM, char **argv)
  74. {
  75. int retval = EXIT_SUCCESS;
  76. int opt, flags;
  77. struct param_t param;
  78. /* Just -1 might not work: uid_t may be unsigned long */
  79. param.ugid.uid = -1L;
  80. param.ugid.gid = -1L;
  81. #if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
  82. applet_long_options = chown_longopts;
  83. #endif
  84. opt_complementary = "-2";
  85. opt = getopt32(argv, OPT_STR);
  86. argv += optind;
  87. /* This matches coreutils behavior (almost - see below) */
  88. param.chown_func = chown;
  89. if (OPT_NODEREF
  90. /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
  91. IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
  92. ) {
  93. param.chown_func = lchown;
  94. }
  95. flags = ACTION_DEPTHFIRST; /* match coreutils order */
  96. if (OPT_RECURSE)
  97. flags |= ACTION_RECURSE;
  98. if (OPT_TRAVERSE_TOP)
  99. flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */
  100. if (OPT_TRAVERSE)
  101. flags |= ACTION_FOLLOWLINKS; /* follow links if -L */
  102. parse_chown_usergroup_or_die(&param.ugid, argv[0]);
  103. /* Ok, ready to do the deed now */
  104. while (*++argv) {
  105. if (!recursive_action(*argv,
  106. flags, /* flags */
  107. fileAction, /* file action */
  108. fileAction, /* dir action */
  109. &param, /* user data */
  110. 0) /* depth */
  111. ) {
  112. retval = EXIT_FAILURE;
  113. }
  114. }
  115. return retval;
  116. }
  117. /*
  118. Testcase. Run in empty directory.
  119. #!/bin/sh
  120. t1="/tmp/busybox chown"
  121. t2="/usr/bin/chown"
  122. create() {
  123. rm -rf $1; mkdir $1
  124. (
  125. cd $1 || exit 1
  126. mkdir dir dir2
  127. >up
  128. >file
  129. >dir/file
  130. >dir2/file
  131. ln -s dir linkdir
  132. ln -s file linkfile
  133. ln -s ../up dir/linkup
  134. ln -s ../dir2 dir/linkupdir2
  135. )
  136. chown -R 0:0 $1
  137. }
  138. tst() {
  139. create test1
  140. create test2
  141. echo "[$1]" >>test1.out
  142. echo "[$1]" >>test2.out
  143. (cd test1; $t1 $1) >>test1.out 2>&1
  144. (cd test2; $t2 $1) >>test2.out 2>&1
  145. (cd test1; ls -lnR) >out1
  146. (cd test2; ls -lnR) >out2
  147. echo "chown $1" >out.diff
  148. if ! diff -u out1 out2 >>out.diff; then exit 1; fi
  149. rm out.diff
  150. }
  151. tst_for_each() {
  152. tst "$1 1:1 file"
  153. tst "$1 1:1 dir"
  154. tst "$1 1:1 linkdir"
  155. tst "$1 1:1 linkfile"
  156. }
  157. echo "If script produced 'out.diff' file, then at least one testcase failed"
  158. >test1.out
  159. >test2.out
  160. # These match coreutils 6.8:
  161. tst_for_each "-v"
  162. tst_for_each "-vR"
  163. tst_for_each "-vRP"
  164. tst_for_each "-vRL"
  165. tst_for_each "-vRH"
  166. tst_for_each "-vh"
  167. tst_for_each "-vhR"
  168. tst_for_each "-vhRP"
  169. tst_for_each "-vhRL"
  170. tst_for_each "-vhRH"
  171. # Fix `name' in coreutils output
  172. sed 's/`/'"'"'/g' -i test2.out
  173. # Compare us with coreutils output
  174. diff -u test1.out test2.out
  175. */