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