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