ls: cleanup part 2. ifdef forest is much less scary now :)
[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* junk, 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 *)junk, &newmode))
50                 bb_error_msg_and_die("invalid mode: %s", (char *)junk);
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 {
69         int retval = EXIT_SUCCESS;
70         char *arg, **argp;
71         char *smode;
72
73         /* Convert first encountered -r into ar, -w into aw etc
74          * so that getopt would not eat it */
75         argp = argv;
76         while ((arg = *++argp)) {
77                 /* Mode spec must be the first arg (sans -R etc) */
78                 /* (protect against mishandling e.g. "chmod 644 -r") */
79                 if (arg[0] != '-') {
80                         arg = NULL;
81                         break;
82                 }
83                 /* An option. Not a -- or valid option? */
84                 if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
85                         arg[0] = 'a';
86                         break;
87                 }
88         }
89
90         /* Paerse options */
91         opt_complementary = "-2";
92         getopt32(argc, argv, ("-"OPT_STR) + 1); /* Reuse string */
93         argv += optind;
94
95         /* Restore option-like mode if needed */
96         if (arg) arg[0] = '-';
97
98         /* Ok, ready to do the deed now */
99         smode = *argv++;
100         do {
101                 if (!recursive_action(*argv,
102                                 OPT_RECURSE,    // recurse
103                                 FALSE,          // follow links: GNU doesn't
104                                 FALSE,          // depth first
105                                 fileAction,     // file action
106                                 fileAction,     // dir action
107                                 smode,          // user data
108                                 0)              // depth
109                 ) {
110                         retval = EXIT_FAILURE;
111                 }
112         } while (*++argv);
113
114         return retval;
115 }
116
117 /*
118 Security: chmod is too important and too subtle.
119 This is a test script (busybox chmod versus coreutils).
120 Run it in empty dir. Probably requires bash.
121
122 #!/bin/sh
123 function create() {
124     rm -rf $1; mkdir $1
125     (
126     cd $1 || exit 1
127     mkdir dir
128     >up
129     >file
130     >dir/file
131     ln -s dir linkdir
132     ln -s file linkfile
133     ln -s ../up dir/up
134     )
135 }
136 function tst() {
137     (cd test1; $t1 $1)
138     (cd test2; $t2 $1)
139     (cd test1; ls -lR) >out1
140     (cd test2; ls -lR) >out2
141     echo "chmod $1" >out.diff
142     if ! diff -u out1 out2 >>out.diff; then exit 1; fi
143     mv out.diff out1.diff
144 }
145 t1="/tmp/busybox chmod"
146 t2="/usr/bin/chmod"
147 create test1; create test2
148 tst "a+w file"
149 tst "a-w dir"
150 tst "a+w linkfile"
151 tst "a-w linkdir"
152 tst "-R a+w file"
153 tst "-R a-w dir"
154 tst "-R a+w linkfile"
155 tst "-R a-w linkdir"
156 tst "a-r,a+x linkfile"
157 */