sync
[bitrig.git] / bin / chmod / chmod.c
blobdf30e703c9f932563e40e71ab4f8df1534032ad9
1 /* $OpenBSD: chmod.c,v 1.28 2012/12/04 02:24:46 deraadt Exp $ */
2 /* $NetBSD: chmod.c,v 1.12 1995/03/21 09:02:09 cgd Exp $ */
4 /*
5 * Copyright (c) 1989, 1993, 1994
6 * The Regents of the University of California. All rights reserved.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
33 #include <sys/types.h>
34 #include <sys/stat.h>
36 #include <err.h>
37 #include <errno.h>
38 #include <fts.h>
39 #include <grp.h>
40 #include <limits.h>
41 #include <locale.h>
42 #include <pwd.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
48 int ischflags, ischown, ischgrp, ischmod;
49 extern char *__progname;
51 gid_t a_gid(const char *);
52 uid_t a_uid(const char *, int);
53 __dead void usage(void);
55 int
56 main(int argc, char *argv[])
58 FTS *ftsp;
59 FTSENT *p;
60 void *set;
61 long val;
62 int oct;
63 mode_t omode;
64 int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval;
65 uid_t uid;
66 gid_t gid;
67 u_int32_t fclear, fset;
68 char *ep, *mode, *cp, *flags;
69 #ifdef lint
70 set = NULL;
71 oct = omode = 0;
72 #endif
74 setlocale(LC_ALL, "");
76 ischown = __progname[2] == 'o';
77 ischgrp = __progname[2] == 'g';
78 ischmod = __progname[2] == 'm';
79 ischflags = __progname[2] == 'f';
81 uid = (uid_t)-1;
82 gid = (gid_t)-1;
83 Hflag = Lflag = Rflag = fflag = hflag = 0;
84 while ((ch = getopt(argc, argv, "HLPRXfghorstuwx")) != -1)
85 switch (ch) {
86 case 'H':
87 Hflag = 1;
88 Lflag = 0;
89 break;
90 case 'L':
91 Lflag = 1;
92 Hflag = 0;
93 break;
94 case 'P':
95 Hflag = Lflag = 0;
96 break;
97 case 'R':
98 Rflag = 1;
99 break;
100 case 'f': /* no longer documented. */
101 fflag = 1;
102 break;
103 case 'h':
105 * In System V (and probably POSIX.2) the -h option
106 * causes chmod to change the mode of the symbolic
107 * link. 4.4BSD's symbolic links don't have modes,
108 * so it's an undocumented noop. Do syntax checking,
109 * though.
111 hflag = 1;
112 break;
114 * XXX
115 * "-[rwx]" are valid mode commands. If they are the entire
116 * argument, getopt has moved past them, so decrement optind.
117 * Regardless, we're done argument processing.
119 case 'g': case 'o': case 'r': case 's':
120 case 't': case 'u': case 'w': case 'X': case 'x':
121 if (!ischmod)
122 usage();
123 if (argv[optind - 1][0] == '-' &&
124 argv[optind - 1][1] == ch &&
125 argv[optind - 1][2] == '\0')
126 --optind;
127 goto done;
128 default:
129 usage();
131 done:
132 argv += optind;
133 argc -= optind;
135 if (argc < 2)
136 usage();
138 fts_options = FTS_PHYSICAL;
139 if (Rflag) {
140 if (hflag)
141 errx(1,
142 "the -R and -h options may not be specified together.");
143 if (Hflag)
144 fts_options |= FTS_COMFOLLOW;
145 if (Lflag) {
146 fts_options &= ~FTS_PHYSICAL;
147 fts_options |= FTS_LOGICAL;
151 if (ischflags) {
152 flags = *argv;
153 if (*flags >= '0' && *flags <= '7') {
154 errno = 0;
155 val = strtoul(flags, &ep, 8);
156 if (val > UINT_MAX)
157 errno = ERANGE;
158 if (errno)
159 err(1, "invalid flags: %s", flags);
160 if (*ep)
161 errx(1, "invalid flags: %s", flags);
162 fset = val;
163 oct = 1;
164 } else {
165 if (strtofflags(&flags, &fset, &fclear))
166 errx(1, "invalid flag: %s", flags);
167 fclear = ~fclear;
168 oct = 0;
170 } else if (ischmod) {
171 mode = *argv;
172 if (*mode >= '0' && *mode <= '7') {
173 errno = 0;
174 val = strtol(mode, &ep, 8);
175 if (val > INT_MAX || val < 0)
176 errno = ERANGE;
177 if (errno)
178 err(1, "invalid file mode: %s", mode);
179 if (*ep)
180 errx(1, "invalid file mode: %s", mode);
181 omode = val;
182 oct = 1;
183 } else {
184 if ((set = setmode(mode)) == NULL)
185 errx(1, "invalid file mode: %s", mode);
186 oct = 0;
188 } else if (ischown) {
189 /* Both UID and GID are given. */
190 if ((cp = strchr(*argv, ':')) != NULL) {
191 *cp++ = '\0';
192 gid = a_gid(cp);
194 #ifdef SUPPORT_DOT
195 /* UID and GID are separated by a dot and UID exists. */
196 else if ((cp = strchr(*argv, '.')) != NULL &&
197 (uid = a_uid(*argv, 1)) == (uid_t)-1) {
198 *cp++ = '\0';
199 gid = a_gid(cp);
201 #endif
202 if (uid == (uid_t)-1)
203 uid = a_uid(*argv, 0);
204 } else
205 gid = a_gid(*argv);
207 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
208 err(1, NULL);
209 for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
210 switch (p->fts_info) {
211 case FTS_D:
212 if (!Rflag)
213 fts_set(ftsp, p, FTS_SKIP);
214 if (ischmod)
215 break;
216 else
217 continue;
218 case FTS_DNR: /* Warn, chmod, continue. */
219 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
220 rval = 1;
221 break;
222 case FTS_DP: /* Already changed at FTS_D. */
223 if (ischmod)
224 continue;
225 else
226 break;
227 case FTS_ERR: /* Warn, continue. */
228 case FTS_NS:
229 warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
230 rval = 1;
231 continue;
232 case FTS_SL: /* Ignore. */
233 case FTS_SLNONE:
235 * The only symlinks that end up here are ones that
236 * don't point to anything and ones that we found
237 * doing a physical walk.
239 if (ischflags || ischmod || !hflag)
240 continue;
241 break;
242 default:
243 break;
245 if (ischflags) {
246 if (oct) {
247 if (!chflags(p->fts_accpath, fset))
248 continue;
249 } else {
250 p->fts_statp->st_flags |= fset;
251 p->fts_statp->st_flags &= fclear;
252 if (!chflags(p->fts_accpath, p->fts_statp->st_flags))
253 continue;
255 warn("%s", p->fts_path);
256 rval = 1;
257 } else if (ischmod && chmod(p->fts_accpath, oct ? omode :
258 getmode(set, p->fts_statp->st_mode)) && !fflag) {
259 warn("%s", p->fts_path);
260 rval = 1;
261 } else if (!ischmod && !ischflags &&
262 (hflag ? lchown(p->fts_accpath, uid, gid) :
263 chown(p->fts_accpath, uid, gid)) && !fflag) {
264 warn("%s", p->fts_path);
265 rval = 1;
268 if (errno)
269 err(1, "fts_read");
270 exit(rval);
274 * Given a UID or user name in a string, return the UID. If an empty string
275 * was given, returns -1. If silent is 0, exits on invalid user names/UIDs;
276 * otherwise, returns -1.
278 uid_t
279 a_uid(const char *s, int silent)
281 struct passwd *pw;
282 const char *errstr;
283 uid_t uid;
285 if (*s == '\0') /* Argument was "[:.]gid". */
286 return ((uid_t)-1);
288 /* User name was given. */
289 if ((pw = getpwnam(s)) != NULL)
290 return (pw->pw_uid);
292 /* UID was given. */
293 uid = (uid_t)strtonum(s, 0, UID_MAX, &errstr);
294 if (errstr) {
295 if (silent)
296 return ((uid_t)-1);
297 else
298 errx(1, "user is %s: %s", errstr, s);
301 return (uid);
305 * Given a GID or group name in a string, return the GID. If an empty string
306 * was given, returns -1. Exits on invalid user names/UIDs.
308 gid_t
309 a_gid(const char *s)
311 struct group *gr;
312 const char *errstr;
313 gid_t gid;
315 if (*s == '\0') /* Argument was "uid[:.]". */
316 return ((gid_t)-1);
318 /* Group name was given. */
319 if ((gr = getgrnam(s)) != NULL)
320 return (gr->gr_gid);
322 /* GID was given. */
323 gid = (gid_t)strtonum(s, 0, GID_MAX, &errstr);
324 if (errstr)
325 errx(1, "group is %s: %s", errstr, s);
327 return (gid);
330 void
331 usage(void)
333 if (ischmod || ischflags)
334 fprintf(stderr,
335 "usage: %s [-R [-H | -L | -P]] %s file ...\n",
336 __progname, ischmod ? "mode" : "flags");
337 else
338 fprintf(stderr,
339 "usage: %s [-h] [-R [-H | -L | -P]] %s file ...\n",
340 __progname, ischown ? "owner[:group]" : "group");
341 if (ischown)
342 fprintf(stderr,
343 " %s [-h] [-R [-H | -L | -P]] :group file ...\n",
344 __progname);
345 exit(1);