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