od: another fix for --strings
[platform/upstream/busybox.git] / coreutils / chown.c
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 source tree.
8  */
9
10 /* BB_AUDIT SUSv3 defects - none? */
11 /* http://www.opengroup.org/onlinepubs/007904975/utilities/chown.html */
12
13 //usage:#define chown_trivial_usage
14 //usage:       "[-RhLHP"IF_DESKTOP("cvf")"]... OWNER[<.|:>[GROUP]] FILE..."
15 //usage:#define chown_full_usage "\n\n"
16 //usage:       "Change the owner and/or group of each FILE to OWNER and/or GROUP\n"
17 //usage:     "\nOptions:"
18 //usage:     "\n        -R      Recurse"
19 //usage:     "\n        -h      Affect symlinks instead of symlink targets"
20 //usage:     "\n        -L      Traverse all symlinks to directories"
21 //usage:     "\n        -H      Traverse symlinks on command line only"
22 //usage:     "\n        -P      Don't traverse symlinks (default)"
23 //usage:        IF_DESKTOP(
24 //usage:     "\n        -c      List changed files"
25 //usage:     "\n        -v      List all files"
26 //usage:     "\n        -f      Hide errors"
27 //usage:        )
28 //usage:
29 //usage:#define chown_example_usage
30 //usage:       "$ ls -l /tmp/foo\n"
31 //usage:       "-r--r--r--    1 andersen andersen        0 Apr 12 18:25 /tmp/foo\n"
32 //usage:       "$ chown root /tmp/foo\n"
33 //usage:       "$ ls -l /tmp/foo\n"
34 //usage:       "-r--r--r--    1 root     andersen        0 Apr 12 18:25 /tmp/foo\n"
35 //usage:       "$ chown root.root /tmp/foo\n"
36 //usage:       "ls -l /tmp/foo\n"
37 //usage:       "-r--r--r--    1 root     root            0 Apr 12 18:25 /tmp/foo\n"
38
39 #include "libbb.h"
40
41 /* This is a NOEXEC applet. Be very careful! */
42
43
44 #define OPT_STR     ("Rh" IF_DESKTOP("vcfLHP"))
45 #define BIT_RECURSE 1
46 #define OPT_RECURSE (opt & 1)
47 #define OPT_NODEREF (opt & 2)
48 #define OPT_VERBOSE (IF_DESKTOP(opt & 0x04) IF_NOT_DESKTOP(0))
49 #define OPT_CHANGED (IF_DESKTOP(opt & 0x08) IF_NOT_DESKTOP(0))
50 #define OPT_QUIET   (IF_DESKTOP(opt & 0x10) IF_NOT_DESKTOP(0))
51 /* POSIX options
52  * -L traverse every symbolic link to a directory encountered
53  * -H if a command line argument is a symbolic link to a directory, traverse it
54  * -P do not traverse any symbolic links (default)
55  * We do not conform to the following:
56  * "Specifying more than one of -H, -L, and -P is not an error.
57  * The last option specified shall determine the behavior of the utility." */
58 /* -L */
59 #define BIT_TRAVERSE 0x20
60 #define OPT_TRAVERSE (IF_DESKTOP(opt & BIT_TRAVERSE) IF_NOT_DESKTOP(0))
61 /* -H or -L */
62 #define BIT_TRAVERSE_TOP (0x20|0x40)
63 #define OPT_TRAVERSE_TOP (IF_DESKTOP(opt & BIT_TRAVERSE_TOP) IF_NOT_DESKTOP(0))
64
65 #if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
66 static const char chown_longopts[] ALIGN1 =
67         "recursive\0"        No_argument   "R"
68         "dereference\0"      No_argument   "\xff"
69         "no-dereference\0"   No_argument   "h"
70 # if ENABLE_DESKTOP
71         "changes\0"          No_argument   "c"
72         "silent\0"           No_argument   "f"
73         "quiet\0"            No_argument   "f"
74         "verbose\0"          No_argument   "v"
75 # endif
76         ;
77 #endif
78
79 typedef int (*chown_fptr)(const char *, uid_t, gid_t);
80
81 struct param_t {
82         struct bb_uidgid_t ugid;
83         chown_fptr chown_func;
84 };
85
86 static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf,
87                 void *vparam, int depth UNUSED_PARAM)
88 {
89 #define param  (*(struct param_t*)vparam)
90 #define opt option_mask32
91         uid_t u = (param.ugid.uid == (uid_t)-1L) ? statbuf->st_uid : param.ugid.uid;
92         gid_t g = (param.ugid.gid == (gid_t)-1L) ? statbuf->st_gid : param.ugid.gid;
93
94         if (param.chown_func(fileName, u, g) == 0) {
95                 if (OPT_VERBOSE
96                  || (OPT_CHANGED && (statbuf->st_uid != u || statbuf->st_gid != g))
97                 ) {
98                         printf("changed ownership of '%s' to %u:%u\n",
99                                         fileName, (unsigned)u, (unsigned)g);
100                 }
101                 return TRUE;
102         }
103         if (!OPT_QUIET)
104                 bb_simple_perror_msg(fileName);
105         return FALSE;
106 #undef opt
107 #undef param
108 }
109
110 int chown_main(int argc UNUSED_PARAM, char **argv)
111 {
112         int retval = EXIT_SUCCESS;
113         int opt, flags;
114         struct param_t param;
115
116         /* Just -1 might not work: uid_t may be unsigned long */
117         param.ugid.uid = -1L;
118         param.ugid.gid = -1L;
119
120 #if ENABLE_FEATURE_CHOWN_LONG_OPTIONS
121         applet_long_options = chown_longopts;
122 #endif
123         opt_complementary = "-2";
124         opt = getopt32(argv, OPT_STR);
125         argv += optind;
126
127         /* This matches coreutils behavior (almost - see below) */
128         param.chown_func = chown;
129         if (OPT_NODEREF
130             /* || (OPT_RECURSE && !OPT_TRAVERSE_TOP): */
131             IF_DESKTOP( || (opt & (BIT_RECURSE|BIT_TRAVERSE_TOP)) == BIT_RECURSE)
132         ) {
133                 param.chown_func = lchown;
134         }
135
136         flags = ACTION_DEPTHFIRST; /* match coreutils order */
137         if (OPT_RECURSE)
138                 flags |= ACTION_RECURSE;
139         if (OPT_TRAVERSE_TOP)
140                 flags |= ACTION_FOLLOWLINKS_L0; /* -H/-L: follow links on depth 0 */
141         if (OPT_TRAVERSE)
142                 flags |= ACTION_FOLLOWLINKS; /* follow links if -L */
143
144         parse_chown_usergroup_or_die(&param.ugid, argv[0]);
145
146         /* Ok, ready to do the deed now */
147         while (*++argv) {
148                 if (!recursive_action(*argv,
149                                 flags,          /* flags */
150                                 fileAction,     /* file action */
151                                 fileAction,     /* dir action */
152                                 &param,         /* user data */
153                                 0)              /* depth */
154                 ) {
155                         retval = EXIT_FAILURE;
156                 }
157         }
158
159         return retval;
160 }
161
162 /*
163 Testcase. Run in empty directory.
164
165 #!/bin/sh
166 t1="/tmp/busybox chown"
167 t2="/usr/bin/chown"
168 create() {
169     rm -rf $1; mkdir $1
170     (
171     cd $1 || exit 1
172     mkdir dir dir2
173     >up
174     >file
175     >dir/file
176     >dir2/file
177     ln -s dir linkdir
178     ln -s file linkfile
179     ln -s ../up dir/linkup
180     ln -s ../dir2 dir/linkupdir2
181     )
182     chown -R 0:0 $1
183 }
184 tst() {
185     create test1
186     create test2
187     echo "[$1]" >>test1.out
188     echo "[$1]" >>test2.out
189     (cd test1; $t1 $1) >>test1.out 2>&1
190     (cd test2; $t2 $1) >>test2.out 2>&1
191     (cd test1; ls -lnR) >out1
192     (cd test2; ls -lnR) >out2
193     echo "chown $1" >out.diff
194     if ! diff -u out1 out2 >>out.diff; then exit 1; fi
195     rm out.diff
196 }
197 tst_for_each() {
198     tst "$1 1:1 file"
199     tst "$1 1:1 dir"
200     tst "$1 1:1 linkdir"
201     tst "$1 1:1 linkfile"
202 }
203 echo "If script produced 'out.diff' file, then at least one testcase failed"
204 >test1.out
205 >test2.out
206 # These match coreutils 6.8:
207 tst_for_each "-v"
208 tst_for_each "-vR"
209 tst_for_each "-vRP"
210 tst_for_each "-vRL"
211 tst_for_each "-vRH"
212 tst_for_each "-vh"
213 tst_for_each "-vhR"
214 tst_for_each "-vhRP"
215 tst_for_each "-vhRL"
216 tst_for_each "-vhRH"
217 # Fix `name' in coreutils output
218 sed 's/`/'"'"'/g' -i test2.out
219 # Compare us with coreutils output
220 diff -u test1.out test2.out
221
222 */