GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/chmod/chmod.c Lines: 112 154 72.7 %
Date: 2016-12-06 Branches: 82 132 62.1 %

Line Branch Exec Source
1
/*	$OpenBSD: chmod.c,v 1.39 2015/12/31 23:38:16 guenther Exp $	*/
2
/*	$NetBSD: chmod.c,v 1.12 1995/03/21 09:02:09 cgd Exp $	*/
3
4
/*
5
 * Copyright (c) 1989, 1993, 1994
6
 *	The Regents of the University of California.  All rights reserved.
7
 *
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.
19
 *
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.
31
 */
32
33
#include <sys/types.h>
34
#include <sys/stat.h>
35
36
#include <err.h>
37
#include <errno.h>
38
#include <fcntl.h>
39
#include <fts.h>
40
#include <grp.h>
41
#include <limits.h>
42
#include <locale.h>
43
#include <pwd.h>
44
#include <stdio.h>
45
#include <stdlib.h>
46
#include <string.h>
47
#include <unistd.h>
48
49
int ischflags, ischown, ischgrp, ischmod;
50
extern char *__progname;
51
52
gid_t a_gid(const char *);
53
uid_t a_uid(const char *, int);
54
__dead void usage(void);
55
56
int
57
main(int argc, char *argv[])
58
379
{
59
	FTS *ftsp;
60
	FTSENT *p;
61
	void *set;
62
	unsigned long val;
63
	int oct;
64
	mode_t omode;
65
	int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, atflags;
66
	uid_t uid;
67
	gid_t gid;
68
	u_int32_t fclear, fset;
69
	char *ep, *mode, *cp, *flags;
70
71
379
	setlocale(LC_ALL, "");
72
73
379
	if (strlen(__progname) > 2) {
74
379
		ischown = __progname[2] == 'o';
75
379
		ischgrp = __progname[2] == 'g';
76
379
		ischmod = __progname[2] == 'm';
77
379
		ischflags = __progname[2] == 'f';
78
	}
79
80
379
	uid = (uid_t)-1;
81
379
	gid = (gid_t)-1;
82
379
	Hflag = Lflag = Rflag = fflag = hflag = 0;
83
958
	while ((ch = getopt(argc, argv, "HLPRXfghorstuwx")) != -1)
84


200
		switch (ch) {
85
		case 'H':
86
8
			Hflag = 1;
87
8
			Lflag = 0;
88
8
			break;
89
		case 'L':
90
32
			Lflag = 1;
91
32
			Hflag = 0;
92
32
			break;
93
		case 'P':
94
8
			Hflag = Lflag = 0;
95
8
			break;
96
		case 'R':
97
140
			Rflag = 1;
98
140
			break;
99
		case 'f':		/* no longer documented. */
100
			fflag = 1;
101
			break;
102
		case 'h':
103
12
			hflag = 1;
104
12
			break;
105
		/*
106
		 * If this is a symbolic mode argument rather than
107
		 * an option, we are done with option processing.
108
		 */
109
		case 'g': case 'o': case 'r': case 's':
110
		case 't': case 'u': case 'w': case 'X': case 'x':
111
			if (!ischmod)
112
				usage();
113
			/*
114
			 * If getopt() moved past the argument, back up.
115
			 * If the argument contains option letters before
116
			 * mode letters, setmode() will catch them.
117
			 */
118
			if (optind > 1) {
119
				cp = argv[optind - 1];
120
				if (cp[strlen(cp) - 1] == ch)
121
					--optind;
122
			}
123
			goto done;
124
		default:
125
			usage();
126
		}
127
379
done:
128
379
	argv += optind;
129
379
	argc -= optind;
130
131
379
	if (argc < 2)
132
		usage();
133
134
	/*
135
	 * We alter the symlink itself if doing -h or -RP, or
136
	 * if doing -RH and the symlink wasn't a command line arg.
137
	 */
138
379
	atflags = AT_SYMLINK_NOFOLLOW;
139
140
379
	fts_options = FTS_PHYSICAL;
141
379
	if (Rflag) {
142
140
		if (hflag)
143
			errx(1,
144
		"the -R and -h options may not be specified together.");
145
140
		if (Hflag)
146
8
			fts_options |= FTS_COMFOLLOW;
147
140
		if (Lflag) {
148
32
			fts_options &= ~FTS_PHYSICAL;
149
32
			fts_options |= FTS_LOGICAL;
150
32
			atflags = 0;
151
		}
152
239
	} else if (!hflag)
153
227
		atflags = 0;
154
155
379
	if (ischflags) {
156
28
		if (pledge("stdio rpath fattr wpath cpath", NULL) == -1)
157
			err(1, "pledge");
158
159
28
		flags = *argv;
160
28
		if (*flags >= '0' && *flags <= '7') {
161
			errno = 0;
162
			val = strtoul(flags, &ep, 8);
163
			if (val > UINT_MAX)
164
				errno = ERANGE;
165
			if (errno)
166
				err(1, "invalid flags: %s", flags);
167
			if (*ep)
168
				errx(1, "invalid flags: %s", flags);
169
			fset = val;
170
			oct = 1;
171
		} else {
172
28
			if (strtofflags(&flags, &fset, &fclear))
173
2
				errx(1, "invalid flag: %s", flags);
174
26
			fclear = ~fclear;
175
26
			oct = 0;
176
		}
177
351
	} else if (ischmod) {
178
275
		mode = *argv;
179
275
		if (*mode >= '0' && *mode <= '7') {
180
259
			errno = 0;
181
259
			val = strtoul(mode, &ep, 8);
182
259
			if (val > INT_MAX)
183
				errno = ERANGE;
184
259
			if (errno)
185
				err(1, "invalid file mode: %s", mode);
186
259
			if (*ep)
187
				errx(1, "invalid file mode: %s", mode);
188
259
			omode = val;
189
259
			oct = 1;
190
		} else {
191
16
			if ((set = setmode(mode)) == NULL)
192
2
				errx(1, "invalid file mode: %s", mode);
193
14
			oct = 0;
194
		}
195
76
	} else if (ischown) {
196
		/* Both UID and GID are given. */
197
58
		if ((cp = strchr(*argv, ':')) != NULL) {
198
40
			*cp++ = '\0';
199
40
			gid = a_gid(cp);
200
		}
201
#ifdef SUPPORT_DOT
202
		/* UID and GID are separated by a dot and UID exists. */
203

18
		else if ((cp = strchr(*argv, '.')) != NULL &&
204
		    (uid = a_uid(*argv, 1)) == (uid_t)-1) {
205
			*cp++ = '\0';
206
			gid = a_gid(cp);
207
		}
208
#endif
209
58
		if (uid == (uid_t)-1)
210
58
			uid = a_uid(*argv, 0);
211
	} else
212
18
		gid = a_gid(*argv);
213
214
371
	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
215
		err(1, NULL);
216
2080
	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
217

1338
		switch (p->fts_info) {
218
		case FTS_D:
219
274
			if (!Rflag)
220
32
				fts_set(ftsp, p, FTS_SKIP);
221
274
			if (ischmod)
222
178
				break;
223
			else
224
96
				continue;
225
		case FTS_DNR:			/* Warn, chmod, continue. */
226
			warnc(p->fts_errno, "%s", p->fts_path);
227
			rval = 1;
228
			break;
229
		case FTS_DP:			/* Already changed at FTS_D. */
230
274
			if (ischmod)
231
178
				continue;
232
			else
233
96
				break;
234
		case FTS_ERR:			/* Warn, continue. */
235
		case FTS_NS:
236
1
			warnc(p->fts_errno, "%s", p->fts_path);
237
1
			rval = 1;
238
1
			continue;
239
		case FTS_SL:			/* Ignore. */
240
		case FTS_SLNONE:
241
			/*
242
			 * The only symlinks that end up here are ones that
243
			 * don't point to anything or that loop and ones
244
			 * that we found doing a physical walk.
245
			 */
246

158
			if (!hflag && (fts_options & FTS_LOGICAL))
247
				continue;
248
			break;
249
		default:
250
			break;
251
		}
252
253
		/*
254
		 * For -RH, the decision of how to handle symlinks depends
255
		 * on the level: follow it iff it's a command line arg.
256
		 */
257
1063
		if (fts_options & FTS_COMFOLLOW) {
258
32
			atflags = p->fts_level == FTS_ROOTLEVEL ? 0 :
259
			    AT_SYMLINK_NOFOLLOW;
260
		}
261
262
1063
		if (ischmod) {
263

677
			if (!fchmodat(AT_FDCWD, p->fts_accpath, oct ? omode :
264
			    getmode(set, p->fts_statp->st_mode), atflags)
265
			    || fflag)
266
				continue;
267
386
		} else if (!ischflags) {
268

264
			if (!fchownat(AT_FDCWD, p->fts_accpath, uid, gid,
269
			    atflags) || fflag)
270
				continue;
271
		} else {
272

122
			if (!chflagsat(AT_FDCWD, p->fts_accpath, oct ? fset :
273
			    (p->fts_statp->st_flags | fset) & fclear, atflags))
274
122
				continue;
275
		}
276
277
		/* error case */
278
		warn("%s", p->fts_path);
279
		rval = 1;
280
	}
281
371
	if (errno)
282
		err(1, "fts_read");
283
371
	fts_close(ftsp);
284
371
	exit(rval);
285
}
286
287
/*
288
 * Given a UID or user name in a string, return the UID.  If an empty string
289
 * was given, returns -1.  If silent is 0, exits on invalid user names/UIDs;
290
 * otherwise, returns -1.
291
 */
292
uid_t
293
a_uid(const char *s, int silent)
294
58
{
295
	struct passwd *pw;
296
	const char *errstr;
297
	uid_t uid;
298
299
58
	if (*s == '\0')			/* Argument was "[:.]gid". */
300
		return ((uid_t)-1);
301
302
	/* User name was given. */
303
58
	if ((pw = getpwnam(s)) != NULL)
304
54
		return (pw->pw_uid);
305
306
	/* UID was given. */
307
4
	uid = (uid_t)strtonum(s, 0, UID_MAX, &errstr);
308
4
	if (errstr) {
309
2
		if (silent)
310
			return ((uid_t)-1);
311
		else
312
2
			errx(1, "user is %s: %s", errstr, s);
313
	}
314
315
2
	return (uid);
316
}
317
318
/*
319
 * Given a GID or group name in a string, return the GID.  If an empty string
320
 * was given, returns -1.  Exits on invalid user names/UIDs.
321
 */
322
gid_t
323
a_gid(const char *s)
324
58
{
325
	struct group *gr;
326
	const char *errstr;
327
	gid_t gid;
328
329
58
	if (*s == '\0')			/* Argument was "uid[:.]". */
330
		return ((gid_t)-1);
331
332
	/* Group name was given. */
333
58
	if ((gr = getgrnam(s)) != NULL)
334
54
		return (gr->gr_gid);
335
336
	/* GID was given. */
337
4
	gid = (gid_t)strtonum(s, 0, GID_MAX, &errstr);
338
4
	if (errstr)
339
2
		errx(1, "group is %s: %s", errstr, s);
340
341
2
	return (gid);
342
}
343
344
void
345
usage(void)
346
{
347
	fprintf(stderr,
348
	    "usage: %s [-h] [-R [-H | -L | -P]] %s file ...\n",
349
	    __progname, ischmod ? "mode" : ischflags ? "flags" :
350
	    ischown ? "owner[:group]" : "group");
351
	if (ischown)
352
		fprintf(stderr,
353
		    "       %s [-h] [-R [-H | -L | -P]] :group file ...\n",
354
		    __progname);
355
	exit(1);
356
}