chmod.c revision 1.30 1 /* $NetBSD: chmod.c,v 1.30 2003/08/07 09:05:02 agc Exp $ */
2
3 /*
4 * Copyright (c) 1989, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 #ifndef lint
34 __COPYRIGHT(
35 "@(#) Copyright (c) 1989, 1993, 1994\n\
36 The Regents of the University of California. All rights reserved.\n");
37 #endif /* not lint */
38
39 #ifndef lint
40 #if 0
41 static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94";
42 #else
43 __RCSID("$NetBSD: chmod.c,v 1.30 2003/08/07 09:05:02 agc Exp $");
44 #endif
45 #endif /* not lint */
46
47 #include <sys/param.h>
48 #include <sys/stat.h>
49 #include <sys/types.h>
50
51 #include <err.h>
52 #include <errno.h>
53 #include <fts.h>
54 #include <limits.h>
55 #include <locale.h>
56 #include <stdio.h>
57 #include <stdlib.h>
58 #include <string.h>
59 #include <unistd.h>
60 #include <vis.h>
61
62 int stdout_ok;
63
64 int main(int, char *[]);
65 void usage(void);
66 char *printescaped(const char *);
67
68 int
69 main(int argc, char *argv[])
70 {
71 FTS *ftsp;
72 FTSENT *p;
73 mode_t *set;
74 int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval;
75 char *mode, *fn;
76 int (*change_mode)(const char *, mode_t);
77
78 setprogname(argv[0]);
79 (void)setlocale(LC_ALL, "");
80
81 Hflag = Lflag = Rflag = fflag = hflag = 0;
82 while ((ch = getopt(argc, argv, "HLPRXfghorstuwx")) != -1)
83 switch (ch) {
84 case 'H':
85 Hflag = 1;
86 Lflag = 0;
87 break;
88 case 'L':
89 Lflag = 1;
90 Hflag = 0;
91 break;
92 case 'P':
93 Hflag = Lflag = 0;
94 break;
95 case 'R':
96 Rflag = 1;
97 break;
98 case 'f': /* XXX: undocumented. */
99 fflag = 1;
100 break;
101 case 'h':
102 /*
103 * In System V the -h option causes chmod to
104 * change the mode of the symbolic link.
105 * 4.4BSD's symbolic links didn't have modes,
106 * so it was an undocumented noop. In NetBSD
107 * 1.3, lchmod(2) is introduced and this
108 * option does real work.
109 */
110 hflag = 1;
111 break;
112 /*
113 * XXX
114 * "-[rwx]" are valid mode commands. If they are the entire
115 * argument, getopt has moved past them, so decrement optind.
116 * Regardless, we're done argument processing.
117 */
118 case 'g': case 'o': case 'r': case 's':
119 case 't': case 'u': case 'w': case 'X': case 'x':
120 if (argv[optind - 1][0] == '-' &&
121 argv[optind - 1][1] == ch &&
122 argv[optind - 1][2] == '\0')
123 --optind;
124 goto done;
125 case '?':
126 default:
127 usage();
128 }
129 done: argv += optind;
130 argc -= optind;
131
132 if (argc < 2)
133 usage();
134
135 stdout_ok = isatty(STDOUT_FILENO);
136
137 fts_options = FTS_PHYSICAL;
138 if (Rflag) {
139 if (hflag) {
140 errx(EXIT_FAILURE,
141 "the -R and -h options may not be specified together.");
142 /* NOTREACHED */
143 }
144 if (Hflag)
145 fts_options |= FTS_COMFOLLOW;
146 if (Lflag) {
147 fts_options &= ~FTS_PHYSICAL;
148 fts_options |= FTS_LOGICAL;
149 }
150 } else if (!hflag)
151 fts_options |= FTS_COMFOLLOW;
152 if (hflag)
153 change_mode = lchmod;
154 else
155 change_mode = chmod;
156
157 mode = *argv;
158 if ((set = setmode(mode)) == NULL) {
159 errx(EXIT_FAILURE, "invalid file mode: %s", mode);
160 /* NOTREACHED */
161 }
162
163 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) {
164 err(EXIT_FAILURE, "fts_open");
165 /* NOTREACHED */
166 }
167 for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
168 switch (p->fts_info) {
169 case FTS_D:
170 if (!Rflag)
171 (void)fts_set(ftsp, p, FTS_SKIP);
172 break;
173 case FTS_DNR: /* Warn, chmod, continue. */
174 fn = printescaped(p->fts_path);
175 warnx("%s: %s", fn, strerror(p->fts_errno));
176 free(fn);
177 rval = 1;
178 break;
179 case FTS_DP: /* Already changed at FTS_D. */
180 continue;
181 case FTS_ERR: /* Warn, continue. */
182 case FTS_NS:
183 fn = printescaped(p->fts_path);
184 warnx("%s: %s", fn, strerror(p->fts_errno));
185 free(fn);
186 rval = 1;
187 continue;
188 case FTS_SL: /* Ignore. */
189 case FTS_SLNONE:
190 /*
191 * The only symlinks that end up here are ones that
192 * don't point to anything and ones that we found
193 * doing a physical walk.
194 */
195 if (!hflag)
196 continue;
197 /* else */
198 /* FALLTHROUGH */
199 default:
200 break;
201 }
202 if ((*change_mode)(p->fts_accpath,
203 getmode(set, p->fts_statp->st_mode)) && !fflag) {
204 fn = printescaped(p->fts_path);
205 warn("%s", fn);
206 free(fn);
207 rval = 1;
208 }
209 }
210 if (errno) {
211 err(EXIT_FAILURE, "fts_read");
212 /* NOTREACHED */
213 }
214 exit(rval);
215 /* NOTREACHED */
216 }
217
218 void
219 usage(void)
220 {
221 (void)fprintf(stderr,
222 "usage: %s [-R [-H | -L | -P]] [-h] mode file ...\n",
223 getprogname());
224 exit(1);
225 /* NOTREACHED */
226 }
227
228 char *
229 printescaped(const char *src)
230 {
231 size_t len;
232 char *retval;
233
234 len = strlen(src);
235 if (len != 0 && SIZE_T_MAX/len <= 4) {
236 errx(EXIT_FAILURE, "%s: name too long", src);
237 /* NOTREACHED */
238 }
239
240 retval = (char *)malloc(4*len+1);
241 if (retval != NULL) {
242 if (stdout_ok)
243 (void)strvis(retval, src, VIS_NL | VIS_CSTYLE);
244 else
245 (void)strcpy(retval, src);
246 return retval;
247 } else
248 errx(EXIT_FAILURE, "out of memory!");
249 /* NOTREACHED */
250 }
251