summaryrefslogtreecommitdiff
path: root/coreutils/chmod.c (plain)
blob: 80913f542a4e19c257e9bb6de705e8b2c7713676
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//config:config CHMOD
13//config: bool "chmod"
14//config: default y
15//config: help
16//config: chmod is used to change the access permission of files.
17
18//applet:IF_CHMOD(APPLET_NOEXEC(chmod, chmod, BB_DIR_BIN, BB_SUID_DROP, chmod))
19
20//kbuild:lib-$(CONFIG_CHMOD) += chmod.o
21
22/* BB_AUDIT SUSv3 compliant */
23/* BB_AUDIT GNU defects - unsupported long options. */
24/* http://www.opengroup.org/onlinepubs/007904975/utilities/chmod.html */
25
26//usage:#define chmod_trivial_usage
27//usage: "[-R"IF_DESKTOP("cvf")"] MODE[,MODE]... FILE..."
28//usage:#define chmod_full_usage "\n\n"
29//usage: "Each MODE is one or more of the letters ugoa, one of the\n"
30//usage: "symbols +-= and one or more of the letters rwxst\n"
31//usage: "\n -R Recurse"
32//usage: IF_DESKTOP(
33//usage: "\n -c List changed files"
34//usage: "\n -v List all files"
35//usage: "\n -f Hide errors"
36//usage: )
37//usage:
38//usage:#define chmod_example_usage
39//usage: "$ ls -l /tmp/foo\n"
40//usage: "-rw-rw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
41//usage: "$ chmod u+x /tmp/foo\n"
42//usage: "$ ls -l /tmp/foo\n"
43//usage: "-rwxrw-r-- 1 root root 0 Apr 12 18:25 /tmp/foo*\n"
44//usage: "$ chmod 444 /tmp/foo\n"
45//usage: "$ ls -l /tmp/foo\n"
46//usage: "-r--r--r-- 1 root root 0 Apr 12 18:25 /tmp/foo\n"
47
48#include "libbb.h"
49
50/* This is a NOEXEC applet. Be very careful! */
51
52
53#define OPT_RECURSE (option_mask32 & 1)
54#define OPT_VERBOSE (IF_DESKTOP(option_mask32 & 2) IF_NOT_DESKTOP(0))
55#define OPT_CHANGED (IF_DESKTOP(option_mask32 & 4) IF_NOT_DESKTOP(0))
56#define OPT_QUIET (IF_DESKTOP(option_mask32 & 8) IF_NOT_DESKTOP(0))
57#define OPT_STR "R" IF_DESKTOP("vcf")
58
59/* coreutils:
60 * chmod never changes the permissions of symbolic links; the chmod
61 * system call cannot change their permissions. This is not a problem
62 * since the permissions of symbolic links are never used.
63 * However, for each symbolic link listed on the command line, chmod changes
64 * the permissions of the pointed-to file. In contrast, chmod ignores
65 * symbolic links encountered during recursive directory traversals.
66 */
67
68static int FAST_FUNC fileAction(const char *fileName, struct stat *statbuf, void* param, int depth)
69{
70 mode_t newmode;
71
72 /* match coreutils behavior */
73 if (depth == 0) {
74 /* statbuf holds lstat result, but we need stat (follow link) */
75 if (stat(fileName, statbuf))
76 goto err;
77 } else { /* depth > 0: skip links */
78 if (S_ISLNK(statbuf->st_mode))
79 return TRUE;
80 }
81
82 newmode = bb_parse_mode((char *)param, statbuf->st_mode);
83 if (newmode == (mode_t)-1)
84 bb_error_msg_and_die("invalid mode '%s'", (char *)param);
85
86 if (chmod(fileName, newmode) == 0) {
87 if (OPT_VERBOSE
88 || (OPT_CHANGED && statbuf->st_mode != newmode)
89 ) {
90 printf("mode of '%s' changed to %04o (%s)\n", fileName,
91 newmode & 07777, bb_mode_string(newmode)+1);
92 }
93 return TRUE;
94 }
95 err:
96 if (!OPT_QUIET)
97 bb_simple_perror_msg(fileName);
98 return FALSE;
99}
100
101int chmod_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
102int chmod_main(int argc UNUSED_PARAM, char **argv)
103{
104 int retval = EXIT_SUCCESS;
105 char *arg, **argp;
106 char *smode;
107
108 /* Convert first encountered -r into ar, -w into aw etc
109 * so that getopt would not eat it */
110 argp = argv;
111 while ((arg = *++argp)) {
112 /* Mode spec must be the first arg (sans -R etc) */
113 /* (protect against mishandling e.g. "chmod 644 -r") */
114 if (arg[0] != '-') {
115 arg = NULL;
116 break;
117 }
118 /* An option. Not a -- or valid option? */
119 if (arg[1] && !strchr("-"OPT_STR, arg[1])) {
120 arg[0] = 'a';
121 break;
122 }
123 }
124
125 /* Parse options */
126 opt_complementary = "-2";
127 getopt32(argv, ("-"OPT_STR) + 1); /* Reuse string */
128 argv += optind;
129
130 /* Restore option-like mode if needed */
131 if (arg) arg[0] = '-';
132
133 /* Ok, ready to do the deed now */
134 smode = *argv++;
135 do {
136 if (!recursive_action(*argv,
137 OPT_RECURSE, // recurse
138 fileAction, // file action
139 fileAction, // dir action
140 smode, // user data
141 0) // depth
142 ) {
143 retval = EXIT_FAILURE;
144 }
145 } while (*++argv);
146
147 return retval;
148}
149
150/*
151Security: chmod is too important and too subtle.
152This is a test script (busybox chmod versus coreutils).
153Run it in empty directory.
154
155#!/bin/sh
156t1="/tmp/busybox chmod"
157t2="/usr/bin/chmod"
158create() {
159 rm -rf $1; mkdir $1
160 (
161 cd $1 || exit 1
162 mkdir dir
163 >up
164 >file
165 >dir/file
166 ln -s dir linkdir
167 ln -s file linkfile
168 ln -s ../up dir/up
169 )
170}
171tst() {
172 (cd test1; $t1 $1)
173 (cd test2; $t2 $1)
174 (cd test1; ls -lR) >out1
175 (cd test2; ls -lR) >out2
176 echo "chmod $1" >out.diff
177 if ! diff -u out1 out2 >>out.diff; then exit 1; fi
178 rm out.diff
179}
180echo "If script produced 'out.diff' file, then at least one testcase failed"
181create test1; create test2
182tst "a+w file"
183tst "a-w dir"
184tst "a+w linkfile"
185tst "a-w linkdir"
186tst "-R a+w file"
187tst "-R a-w dir"
188tst "-R a+w linkfile"
189tst "-R a-w linkdir"
190tst "a-r,a+x linkfile"
191*/
192