chown.c revision 83410
1285SN/A/*
2821Saefimov * Copyright (c) 1988, 1993, 1994
3285SN/A *	The Regents of the University of California.  All rights reserved.
4285SN/A *
5285SN/A * Redistribution and use in source and binary forms, with or without
6285SN/A * modification, are permitted provided that the following conditions
7285SN/A * are met:
8285SN/A * 1. Redistributions of source code must retain the above copyright
9285SN/A *    notice, this list of conditions and the following disclaimer.
10285SN/A * 2. Redistributions in binary form must reproduce the above copyright
11285SN/A *    notice, this list of conditions and the following disclaimer in the
12285SN/A *    documentation and/or other materials provided with the distribution.
13285SN/A * 3. All advertising materials mentioning features or use of this software
14285SN/A *    must display the following acknowledgement:
15285SN/A *	This product includes software developed by the University of
16285SN/A *	California, Berkeley and its contributors.
17285SN/A * 4. Neither the name of the University nor the names of its contributors
18285SN/A *    may be used to endorse or promote products derived from this software
19285SN/A *    without specific prior written permission.
20285SN/A *
21285SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22285SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23285SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24285SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25285SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26285SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27285SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28821Saefimov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29821Saefimov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30821Saefimov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31367SN/A * SUCH DAMAGE.
32367SN/A */
33821Saefimov
34367SN/A#ifndef lint
35285SN/Astatic const char copyright[] =
36285SN/A"@(#) Copyright (c) 1988, 1993, 1994\n\
37285SN/A	The Regents of the University of California.  All rights reserved.\n";
38285SN/A#endif /* not lint */
39285SN/A
40285SN/A#ifndef lint
41821Saefimov#if 0
42285SN/Astatic char sccsid[] = "@(#)chown.c	8.8 (Berkeley) 4/4/94";
43285SN/A#else
44821Saefimovstatic const char rcsid[] =
45821Saefimov  "$FreeBSD: head/usr.sbin/chown/chown.c 83410 2001-09-13 14:55:59Z ru $";
46821Saefimov#endif
47285SN/A#endif /* not lint */
48821Saefimov
49821Saefimov#include <sys/param.h>
50285SN/A#include <sys/stat.h>
51285SN/A
52285SN/A#include <err.h>
53821Saefimov#include <errno.h>
54285SN/A#include <fts.h>
55285SN/A#include <grp.h>
56821Saefimov#include <pwd.h>
57821Saefimov#include <stdio.h>
58285SN/A#include <stdlib.h>
59285SN/A#include <string.h>
60285SN/A#include <unistd.h>
61821Saefimov
62285SN/Avoid	a_gid __P((const char *));
63285SN/Avoid	a_uid __P((const char *));
64285SN/Avoid	chownerr __P((const char *));
65285SN/Au_long	id __P((const char *, const char *));
66285SN/Avoid	usage __P((void));
67285SN/A
68285SN/Auid_t uid;
69821Saefimovgid_t gid;
70285SN/Aint ischown;
71285SN/Aconst char *gname;
72285SN/A
73821Saefimovint
74285SN/Amain(argc, argv)
75285SN/A	int argc;
76285SN/A	char *argv[];
77285SN/A{
78285SN/A	FTS *ftsp;
79285SN/A	FTSENT *p;
80285SN/A	int Hflag, Lflag, Rflag, fflag, hflag, vflag;
81821Saefimov	int ch, fts_options, rval;
82821Saefimov	char *cp;
83821Saefimov
84821Saefimov	cp = strrchr(argv[0], '/');
85821Saefimov	cp = (cp != NULL) ? cp + 1 : argv[0];
86821Saefimov	ischown = (strcmp(cp, "chown") == 0);
87821Saefimov
88821Saefimov	Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
89821Saefimov	while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
90821Saefimov		switch (ch) {
91821Saefimov		case 'H':
92821Saefimov			Hflag = 1;
93821Saefimov			Lflag = 0;
94821Saefimov			break;
95821Saefimov		case 'L':
96821Saefimov			Lflag = 1;
97821Saefimov			Hflag = 0;
98821Saefimov			break;
99821Saefimov		case 'P':
100821Saefimov			Hflag = Lflag = 0;
101821Saefimov			break;
102821Saefimov		case 'R':
103821Saefimov			Rflag = 1;
104821Saefimov			break;
105821Saefimov		case 'f':
106821Saefimov			fflag = 1;
107821Saefimov			break;
108821Saefimov		case 'h':
109821Saefimov			hflag = 1;
110821Saefimov			break;
111821Saefimov		case 'v':
112821Saefimov			vflag = 1;
113821Saefimov			break;
114821Saefimov		case '?':
115821Saefimov		default:
116821Saefimov			usage();
117821Saefimov		}
118285SN/A	argv += optind;
119285SN/A	argc -= optind;
120285SN/A
121821Saefimov	if (argc < 2)
122285SN/A		usage();
123285SN/A
124285SN/A	if (Rflag) {
125285SN/A		fts_options = FTS_PHYSICAL;
126285SN/A		if (hflag && (Hflag || Lflag))
127285SN/A			errx(1, "the -R%c and -h options may not be "
128285SN/A			    "specified together", Hflag ? 'H' : 'L');
129821Saefimov		if (Hflag)
130285SN/A			fts_options |= FTS_COMFOLLOW;
131285SN/A		else if (Lflag) {
132821Saefimov			fts_options &= ~FTS_PHYSICAL;
133821Saefimov			fts_options |= FTS_LOGICAL;
134821Saefimov		}
135821Saefimov	} else
136285SN/A		fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
137821Saefimov
138821Saefimov	uid = (uid_t)-1;
139821Saefimov	gid = (gid_t)-1;
140285SN/A	if (ischown) {
141285SN/A		if ((cp = strchr(*argv, ':')) != NULL) {
142285SN/A			*cp++ = '\0';
143285SN/A			a_gid(cp);
144		}
145#ifdef SUPPORT_DOT
146		else if ((cp = strchr(*argv, '.')) != NULL) {
147			*cp++ = '\0';
148			a_gid(cp);
149		}
150#endif
151		a_uid(*argv);
152	} else
153		a_gid(*argv);
154
155	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
156		err(1, NULL);
157
158	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
159		switch (p->fts_info) {
160		case FTS_D:			/* Change it at FTS_DP. */
161			if (!Rflag)
162				fts_set(ftsp, p, FTS_SKIP);
163			continue;
164		case FTS_DNR:			/* Warn, chown. */
165			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
166			rval = 1;
167			break;
168		case FTS_ERR:			/* Warn, continue. */
169		case FTS_NS:
170			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
171			rval = 1;
172			continue;
173		case FTS_SL:
174		case FTS_SLNONE:
175			/*
176			 * The only symlinks that end up here are ones that
177			 * don't point to anything and ones that we found
178			 * doing a physical walk.
179			 */
180			if (hflag)
181				break;
182			else
183				continue;
184		default:
185			break;
186		}
187		if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
188		    (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
189			continue;
190		if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) {
191			if (!fflag) {
192				chownerr(p->fts_path);
193				rval = 1;
194			}
195		} else {
196			if (vflag)
197				printf("%s\n", p->fts_path);
198		}
199	}
200	if (errno)
201		err(1, "fts_read");
202	exit(rval);
203}
204
205void
206a_gid(s)
207	const char *s;
208{
209	struct group *gr;
210
211	if (*s == '\0')			/* Argument was "uid[:.]". */
212		return;
213	gname = s;
214	gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group");
215}
216
217void
218a_uid(s)
219	const char *s;
220{
221	struct passwd *pw;
222
223	if (*s == '\0')			/* Argument was "[:.]gid". */
224		return;
225	uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user");
226}
227
228u_long
229id(name, type)
230	const char *name, *type;
231{
232	u_long val;
233	char *ep;
234
235	/*
236	 * XXX
237	 * We know that uid_t's and gid_t's are unsigned longs.
238	 */
239	errno = 0;
240	val = strtoul(name, &ep, 10);
241	if (errno)
242		err(1, "%s", name);
243	if (*ep != '\0')
244		errx(1, "%s: illegal %s name", name, type);
245	return (val);
246}
247
248void
249chownerr(file)
250	const char *file;
251{
252	static uid_t euid = -1;
253	static int ngroups = -1;
254	gid_t groups[NGROUPS_MAX];
255
256	/* Check for chown without being root. */
257	if (errno != EPERM ||
258	    (uid != (uid_t)-1 && euid == (uid_t)-1 && (euid = geteuid()) != 0))
259		err(1, "%s", file);
260
261	/* Check group membership; kernel just returns EPERM. */
262	if (gid != (gid_t)-1 && ngroups == -1 &&
263	    euid == (uid_t)-1 && (euid = geteuid()) != 0) {
264		ngroups = getgroups(NGROUPS_MAX, groups);
265		while (--ngroups >= 0 && gid != groups[ngroups]);
266		if (ngroups < 0)
267			errx(1, "you are not a member of group %s", gname);
268	}
269	warn("%s", file);
270}
271
272void
273usage()
274{
275
276	if (ischown)
277		(void)fprintf(stderr, "%s\n%s\n",
278		    "usage: chown [-fhv] [-R [-H | -L | -P]] owner[:group]"
279		    " file ...",
280		    "       chown [-fhv] [-R [-H | -L | -P]] :group file ...");
281	else
282		(void)fprintf(stderr, "%s\n",
283		    "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ...");
284	exit(1);
285}
286