155825Speter/*
2114370Speter * Copyright (c) 1988, 1993, 1994
3114370Speter *	The Regents of the University of California.  All rights reserved.
439818Speter *
5114370Speter * Redistribution and use in source and binary forms, with or without
639818Speter * modification, are permitted provided that the following conditions
739818Speter * are met:
839818Speter * 1. Redistributions of source code must retain the above copyright
983598Speter *    notice, this list of conditions and the following disclaimer.
10114370Speter * 2. Redistributions in binary form must reproduce the above copyright
11114370Speter *    notice, this list of conditions and the following disclaimer in the
12114370Speter *    documentation and/or other materials provided with the distribution.
13114370Speter * 4. Neither the name of the University nor the names of its contributors
14114370Speter *    may be used to endorse or promote products derived from this software
15114370Speter *    without specific prior written permission.
16114370Speter *
17114370Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18114370Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19114370Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20114370Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21114370Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22114370Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23114370Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24114370Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25114370Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26114370Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27114370Speter * SUCH DAMAGE.
28114370Speter */
29114370Speter
30114370Speter#if 0
31114370Speter#ifndef lint
32114370Speterstatic const char copyright[] =
33114370Speter"@(#) Copyright (c) 1988, 1993, 1994\n\
34114370Speter	The Regents of the University of California.  All rights reserved.\n";
35114370Speter#endif /* not lint */
36114370Speter
37114370Speter#ifndef lint
38114370Speterstatic char sccsid[] = "@(#)chown.c	8.8 (Berkeley) 4/4/94";
39114370Speter#endif /* not lint */
40114370Speter#endif
41114370Speter
4239818Speter#include <sys/cdefs.h>
43114370Speter__FBSDID("$FreeBSD$");
44114370Speter
45114370Speter#include <sys/param.h>
46114370Speter#include <sys/stat.h>
47114370Speter
48114370Speter#include <err.h>
4939818Speter#include <errno.h>
5039818Speter#include <fts.h>
51114370Speter#include <grp.h>
52114370Speter#include <libgen.h>
53114370Speter#include <pwd.h>
54114370Speter#include <stdint.h>
55114370Speter#include <stdio.h>
56114370Speter#include <stdlib.h>
57114370Speter#include <string.h>
5839818Speter#include <unistd.h>
59114370Speter
60114370Spetervoid	a_gid(const char *);
61114370Spetervoid	a_uid(const char *);
6239818Spetervoid	chownerr(const char *);
6339818Speteruid_t	id(const char *, const char *);
64114370Spetervoid	usage(void);
65114370Speter
66114370Speteruid_t uid;
67114370Spetergid_t gid;
68114370Speterint ischown;
69114370Speterconst char *gname;
70114370Speter
71114370Speterint
72114370Spetermain(int argc, char **argv)
73114370Speter{
74114370Speter	FTS *ftsp;
75114370Speter	FTSENT *p;
76114370Speter	int Hflag, Lflag, Rflag, fflag, hflag, vflag, xflag;
77114370Speter	int ch, fts_options, rval;
78114370Speter	char *cp;
79114370Speter
8039818Speter	ischown = (strcmp(basename(argv[0]), "chown") == 0);
81114370Speter
82114370Speter	Hflag = Lflag = Rflag = fflag = hflag = vflag = xflag = 0;
8339818Speter	while ((ch = getopt(argc, argv, "HLPRfhvx")) != -1)
84114370Speter		switch (ch) {
85114370Speter		case 'H':
86114370Speter			Hflag = 1;
87114370Speter			Lflag = 0;
88114370Speter			break;
89114370Speter		case 'L':
90129824Stjr			Lflag = 1;
91129824Stjr			Hflag = 0;
92114370Speter			break;
9339818Speter		case 'P':
94114370Speter			Hflag = Lflag = 0;
95114370Speter			break;
96114370Speter		case 'R':
97114370Speter			Rflag = 1;
98114370Speter			break;
99114370Speter		case 'f':
100114370Speter			fflag = 1;
101114370Speter			break;
102114370Speter		case 'h':
103114370Speter			hflag = 1;
104114370Speter			break;
105114370Speter		case 'v':
106114370Speter			vflag++;
107114370Speter			break;
108114370Speter		case 'x':
109114370Speter			xflag = 1;
110114370Speter			break;
11139818Speter		case '?':
112129824Stjr		default:
113129824Stjr			usage();
114114370Speter		}
11539818Speter	argv += optind;
116114370Speter	argc -= optind;
117114370Speter
118114370Speter	if (argc < 2)
119114370Speter		usage();
12039818Speter
121114370Speter	if (Rflag) {
122114370Speter		fts_options = FTS_PHYSICAL;
123114370Speter		if (hflag && (Hflag || Lflag))
12439818Speter			errx(1, "the -R%c and -h options may not be "
12539818Speter			    "specified together", Hflag ? 'H' : 'L');
126114370Speter		if (Hflag)
12739818Speter			fts_options |= FTS_COMFOLLOW;
12839818Speter		else if (Lflag) {
129114370Speter			fts_options &= ~FTS_PHYSICAL;
13039818Speter			fts_options |= FTS_LOGICAL;
131114370Speter		}
132114370Speter	} else
133114370Speter		fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
134114370Speter	if (xflag)
13539818Speter		fts_options |= FTS_XDEV;
136114370Speter
137114370Speter	uid = (uid_t)-1;
13839818Speter	gid = (gid_t)-1;
139114370Speter	if (ischown) {
14039818Speter		if ((cp = strchr(*argv, ':')) != NULL) {
141114370Speter			*cp++ = '\0';
142114370Speter			a_gid(cp);
143114370Speter		}
144114370Speter#ifdef SUPPORT_DOT
145114370Speter		else if ((cp = strchr(*argv, '.')) != NULL) {
14639818Speter			warnx("separation of user and group with a period is deprecated");
147114370Speter			*cp++ = '\0';
14839818Speter			a_gid(cp);
14939818Speter		}
15039818Speter#endif
15139818Speter		a_uid(*argv);
15239818Speter	} else
15339818Speter		a_gid(*argv);
15439818Speter
15539818Speter	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
15639818Speter		err(1, NULL);
15739818Speter
15839818Speter	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
15939818Speter		switch (p->fts_info) {
16039818Speter		case FTS_D:			/* Change it at FTS_DP. */
161114370Speter			if (!Rflag)
16239818Speter				fts_set(ftsp, p, FTS_SKIP);
16339818Speter			continue;
16439818Speter		case FTS_DNR:			/* Warn, chown. */
16539818Speter			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
16639818Speter			rval = 1;
16739818Speter			break;
16839818Speter		case FTS_ERR:			/* Warn, continue. */
16939818Speter		case FTS_NS:
17039818Speter			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
17139818Speter			rval = 1;
17239818Speter			continue;
17339818Speter		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", p->fts_path);
198				if (vflag > 1) {
199					if (ischown) {
200						printf(": %ju:%ju -> %ju:%ju",
201						    (uintmax_t)
202						    p->fts_statp->st_uid,
203						    (uintmax_t)
204						    p->fts_statp->st_gid,
205						    (uid == (uid_t)-1) ?
206						    (uintmax_t)
207						    p->fts_statp->st_uid :
208						    (uintmax_t)uid,
209						    (gid == (gid_t)-1) ?
210						    (uintmax_t)
211						    p->fts_statp->st_gid :
212						    (uintmax_t)gid);
213					} else {
214						printf(": %ju -> %ju",
215						    (uintmax_t)
216						    p->fts_statp->st_gid,
217						    (gid == (gid_t)-1) ?
218						    (uintmax_t)
219						    p->fts_statp->st_gid :
220						    (uintmax_t)gid);
221					}
222				}
223				printf("\n");
224			}
225		}
226	}
227	if (errno)
228		err(1, "fts_read");
229	exit(rval);
230}
231
232void
233a_gid(const char *s)
234{
235	struct group *gr;
236
237	if (*s == '\0')			/* Argument was "uid[:.]". */
238		return;
239	gname = s;
240	gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group");
241}
242
243void
244a_uid(const char *s)
245{
246	struct passwd *pw;
247
248	if (*s == '\0')			/* Argument was "[:.]gid". */
249		return;
250	uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user");
251}
252
253uid_t
254id(const char *name, const char *type)
255{
256	uid_t val;
257	char *ep;
258
259	/*
260	 * XXX
261	 * We know that uid_t's and gid_t's are unsigned longs.
262	 */
263	errno = 0;
264	val = strtoul(name, &ep, 10);
265	if (errno || *ep != '\0')
266		errx(1, "%s: illegal %s name", name, type);
267	return (val);
268}
269
270void
271chownerr(const char *file)
272{
273	static uid_t euid = -1;
274	static int ngroups = -1;
275	static long ngroups_max;
276	gid_t *groups;
277
278	/* Check for chown without being root. */
279	if (errno != EPERM || (uid != (uid_t)-1 &&
280	    euid == (uid_t)-1 && (euid = geteuid()) != 0)) {
281		warn("%s", file);
282		return;
283	}
284
285	/* Check group membership; kernel just returns EPERM. */
286	if (gid != (gid_t)-1 && ngroups == -1 &&
287	    euid == (uid_t)-1 && (euid = geteuid()) != 0) {
288		ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1;
289		if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL)
290			err(1, "malloc");
291		ngroups = getgroups(ngroups_max, groups);
292		while (--ngroups >= 0 && gid != groups[ngroups]);
293		free(groups);
294		if (ngroups < 0) {
295			warnx("you are not a member of group %s", gname);
296			return;
297		}
298	}
299	warn("%s", file);
300}
301
302void
303usage(void)
304{
305
306	if (ischown)
307		(void)fprintf(stderr, "%s\n%s\n",
308		    "usage: chown [-fhvx] [-R [-H | -L | -P]] owner[:group]"
309		    " file ...",
310		    "       chown [-fhvx] [-R [-H | -L | -P]] :group file ...");
311	else
312		(void)fprintf(stderr, "%s\n",
313		    "usage: chgrp [-fhvx] [-R [-H | -L | -P]] group file ...");
314	exit(1);
315}
316