chown.c revision 17597
1/*
2 * Copyright (c) 1988, 1993, 1994
3 *	The Regents of the University of California.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by the University of
16 *	California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 *    may be used to endorse or promote products derived from this software
19 *    without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
34#ifndef lint
35static char copyright[] =
36"@(#) Copyright (c) 1988, 1993, 1994\n\
37	The Regents of the University of California.  All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)chown.c	8.8 (Berkeley) 4/4/94";
42#endif /* not lint */
43
44#include <sys/param.h>
45#include <sys/stat.h>
46
47#include <ctype.h>
48#include <dirent.h>
49#include <err.h>
50#include <errno.h>
51#include <fts.h>
52#include <grp.h>
53#include <pwd.h>
54#include <stdio.h>
55#include <stdlib.h>
56#include <string.h>
57#include <unistd.h>
58
59void	a_gid __P((char *));
60void	a_uid __P((char *));
61void	chownerr __P((char *));
62u_long	id __P((char *, char *));
63void	usage __P((void));
64
65uid_t uid;
66gid_t gid;
67int Rflag, ischown, fflag;
68char *gname, *myname;
69
70int
71main(argc, argv)
72	int argc;
73	char *argv[];
74{
75	FTS *ftsp;
76	FTSENT *p;
77	int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
78	char *cp;
79
80	myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
81	ischown = myname[2] == 'o';
82
83	Hflag = Lflag = Pflag = hflag = 0;
84	while ((ch = getopt(argc, argv, "HLPRfh")) != EOF)
85		switch (ch) {
86		case 'H':
87			Hflag = 1;
88			Lflag = Pflag = 0;
89			break;
90		case 'L':
91			Lflag = 1;
92			Hflag = Pflag = 0;
93			break;
94		case 'P':
95			Pflag = 1;
96			Hflag = Lflag = 0;
97			break;
98		case 'R':
99			Rflag = 1;
100			break;
101		case 'f':
102			fflag = 1;
103			break;
104		case 'h':
105			/*
106			 * In System V (and probably POSIX.2) the -h option
107			 * causes chown/chgrp to change the owner/group of
108			 * the symbolic link.  4.4BSD's symbolic links don't
109			 * have owners/groups, so it's an undocumented noop.
110			 * Do syntax checking, though.
111			 */
112			hflag = 1;
113			break;
114		case '?':
115		default:
116			usage();
117		}
118	argv += optind;
119	argc -= optind;
120
121	if (argc < 2)
122		usage();
123
124	fts_options = FTS_PHYSICAL;
125	if (Rflag) {
126		if (hflag)
127			errx(1,
128		"the -R and -h options may not be specified together.");
129		if (Hflag)
130			fts_options |= FTS_COMFOLLOW;
131		if (Lflag) {
132			fts_options &= ~FTS_PHYSICAL;
133			fts_options |= FTS_LOGICAL;
134		}
135	}
136
137	uid = gid = -1;
138	if (ischown) {
139#ifdef SUPPORT_DOT
140		if ((cp = strchr(*argv, '.')) != NULL) {
141			*cp++ = '\0';
142			a_gid(cp);
143		} else
144#endif
145		if ((cp = strchr(*argv, ':')) != NULL) {
146			*cp++ = '\0';
147			a_gid(cp);
148		}
149		a_uid(*argv);
150	} else
151		a_gid(*argv);
152
153	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
154		err(1, NULL);
155
156	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
157		switch (p->fts_info) {
158		case FTS_D: 			/* Change it at FTS_DP. */
159			if (!Rflag)
160				fts_set(ftsp, p, FTS_SKIP);
161			continue;
162		case FTS_DNR:			/* Warn, chown, continue. */
163			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
164			rval = 1;
165			break;
166		case FTS_ERR:			/* Warn, continue. */
167		case FTS_NS:
168			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
169			rval = 1;
170			continue;
171		case FTS_SL:			/* Ignore. */
172		case FTS_SLNONE:
173			/*
174			 * The only symlinks that end up here are ones that
175			 * don't point to anything and ones that we found
176			 * doing a physical walk.
177			 */
178			continue;
179		default:
180			break;
181		}
182		if (chown(p->fts_accpath, uid, gid) && !fflag) {
183			chownerr(p->fts_path);
184			rval = 1;
185		}
186	}
187	if (errno)
188		err(1, "fts_read");
189	exit(rval);
190}
191
192void
193a_gid(s)
194	char *s;
195{
196	struct group *gr;
197
198	if (*s == '\0')			/* Argument was "uid[:.]". */
199		return;
200	gname = s;
201	gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
202}
203
204void
205a_uid(s)
206	char *s;
207{
208	struct passwd *pw;
209
210	if (*s == '\0')			/* Argument was "[:.]gid". */
211		return;
212	uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
213}
214
215u_long
216id(name, type)
217	char *name, *type;
218{
219	u_long val;
220	char *ep;
221
222	/*
223	 * XXX
224	 * We know that uid_t's and gid_t's are unsigned longs.
225	 */
226	errno = 0;
227	val = strtoul(name, &ep, 10);
228	if (errno)
229		err(1, "%s", name);
230	if (*ep != '\0')
231		errx(1, "%s: illegal %s name", name, type);
232	return (val);
233}
234
235void
236chownerr(file)
237	char *file;
238{
239	static int euid = -1, ngroups = -1;
240	gid_t groups[NGROUPS];
241
242	/* Check for chown without being root. */
243	if (errno != EPERM ||
244	    uid != -1 && euid == -1 && (euid = geteuid()) != 0) {
245		if (fflag)
246			exit(0);
247		err(1, "%s", file);
248	}
249
250	/* Check group membership; kernel just returns EPERM. */
251	if (gid != -1 && ngroups == -1) {
252		ngroups = getgroups(NGROUPS, groups);
253		while (--ngroups >= 0 && gid != groups[ngroups]);
254		if (ngroups < 0) {
255			if (fflag)
256				exit(0);
257			errx(1, "you are not a member of group %s", gname);
258		}
259	}
260
261	if (!fflag)
262		warn("%s", file);
263}
264
265void
266usage()
267{
268	(void)fprintf(stderr,
269	    "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n",
270	    myname, ischown ? "[owner][:group]" : "group");
271	exit(1);
272}
273