chown.c revision 114005
11553Srgrimes/*
21553Srgrimes * Copyright (c) 1988, 1993, 1994
31553Srgrimes *	The Regents of the University of California.  All rights reserved.
41553Srgrimes *
51553Srgrimes * Redistribution and use in source and binary forms, with or without
61553Srgrimes * modification, are permitted provided that the following conditions
71553Srgrimes * are met:
81553Srgrimes * 1. Redistributions of source code must retain the above copyright
91553Srgrimes *    notice, this list of conditions and the following disclaimer.
101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111553Srgrimes *    notice, this list of conditions and the following disclaimer in the
121553Srgrimes *    documentation and/or other materials provided with the distribution.
131553Srgrimes * 3. All advertising materials mentioning features or use of this software
141553Srgrimes *    must display the following acknowledgement:
151553Srgrimes *	This product includes software developed by the University of
161553Srgrimes *	California, Berkeley and its contributors.
171553Srgrimes * 4. Neither the name of the University nor the names of its contributors
181553Srgrimes *    may be used to endorse or promote products derived from this software
191553Srgrimes *    without specific prior written permission.
201553Srgrimes *
211553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
221553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
231553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
241553Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
251553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
261553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
271553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
281553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
291553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
301553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
311553Srgrimes * SUCH DAMAGE.
321553Srgrimes */
331553Srgrimes
341553Srgrimes#ifndef lint
3528643Sstevestatic const char copyright[] =
361553Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\
371553Srgrimes	The Regents of the University of California.  All rights reserved.\n";
381553Srgrimes#endif /* not lint */
391553Srgrimes
401553Srgrimes#ifndef lint
4128643Ssteve#if 0
421553Srgrimesstatic char sccsid[] = "@(#)chown.c	8.8 (Berkeley) 4/4/94";
4328643Ssteve#endif
441553Srgrimes#endif /* not lint */
451553Srgrimes
4699141Sjmallett#include <sys/cdefs.h>
4799141Sjmallett__FBSDID("$FreeBSD: head/usr.sbin/chown/chown.c 114005 2003-04-25 08:57:55Z johan $");
4899141Sjmallett
491553Srgrimes#include <sys/param.h>
501553Srgrimes#include <sys/stat.h>
511553Srgrimes
521553Srgrimes#include <err.h>
531553Srgrimes#include <errno.h>
541553Srgrimes#include <fts.h>
551553Srgrimes#include <grp.h>
56108443Sobrien#include <libgen.h>
571553Srgrimes#include <pwd.h>
58114005Sjohan#include <stdint.h>
591553Srgrimes#include <stdio.h>
601553Srgrimes#include <stdlib.h>
611553Srgrimes#include <string.h>
621553Srgrimes#include <unistd.h>
631553Srgrimes
6499141Sjmallettvoid	a_gid(const char *);
6599141Sjmallettvoid	a_uid(const char *);
6699141Sjmallettvoid	chownerr(const char *);
6799141Sjmallettu_long	id(const char *, const char *);
6899141Sjmallettvoid	usage(void);
691553Srgrimes
701553Srgrimesuid_t uid;
711553Srgrimesgid_t gid;
7283410Sruint ischown;
7383410Sruconst char *gname;
741553Srgrimes
751553Srgrimesint
7699141Sjmallettmain(int argc, char **argv)
771553Srgrimes{
781553Srgrimes	FTS *ftsp;
791553Srgrimes	FTSENT *p;
8083410Sru	int Hflag, Lflag, Rflag, fflag, hflag, vflag;
8183410Sru	int ch, fts_options, rval;
821553Srgrimes	char *cp;
838857Srgrimes
84108443Sobrien	ischown = (strcmp(basename(argv[0]), "chown") == 0);
858857Srgrimes
8683410Sru	Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
8757830Sobrien	while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
881553Srgrimes		switch (ch) {
891553Srgrimes		case 'H':
901553Srgrimes			Hflag = 1;
9183410Sru			Lflag = 0;
921553Srgrimes			break;
931553Srgrimes		case 'L':
941553Srgrimes			Lflag = 1;
9583410Sru			Hflag = 0;
961553Srgrimes			break;
971553Srgrimes		case 'P':
981553Srgrimes			Hflag = Lflag = 0;
991553Srgrimes			break;
1001553Srgrimes		case 'R':
1011553Srgrimes			Rflag = 1;
1021553Srgrimes			break;
1031553Srgrimes		case 'f':
1041553Srgrimes			fflag = 1;
1051553Srgrimes			break;
1061553Srgrimes		case 'h':
1071553Srgrimes			hflag = 1;
1081553Srgrimes			break;
10957830Sobrien		case 'v':
110114005Sjohan			vflag++;
11157830Sobrien			break;
1121553Srgrimes		case '?':
1131553Srgrimes		default:
1141553Srgrimes			usage();
1151553Srgrimes		}
1161553Srgrimes	argv += optind;
1171553Srgrimes	argc -= optind;
1181553Srgrimes
1191553Srgrimes	if (argc < 2)
1201553Srgrimes		usage();
1211553Srgrimes
1221553Srgrimes	if (Rflag) {
12377333Sru		fts_options = FTS_PHYSICAL;
12483410Sru		if (hflag && (Hflag || Lflag))
12583410Sru			errx(1, "the -R%c and -h options may not be "
12683410Sru			    "specified together", Hflag ? 'H' : 'L');
1271553Srgrimes		if (Hflag)
1281553Srgrimes			fts_options |= FTS_COMFOLLOW;
12983410Sru		else if (Lflag) {
1301553Srgrimes			fts_options &= ~FTS_PHYSICAL;
1311553Srgrimes			fts_options |= FTS_LOGICAL;
1321553Srgrimes		}
13377333Sru	} else
13477522Sru		fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
1351553Srgrimes
13683410Sru	uid = (uid_t)-1;
13783410Sru	gid = (gid_t)-1;
1381553Srgrimes	if (ischown) {
13929656Swosch		if ((cp = strchr(*argv, ':')) != NULL) {
1401553Srgrimes			*cp++ = '\0';
1411553Srgrimes			a_gid(cp);
14229656Swosch		}
14329656Swosch#ifdef SUPPORT_DOT
14429656Swosch		else if ((cp = strchr(*argv, '.')) != NULL) {
145100252Sdwmalone			warnx("separation of user and group with a period is deprecated");
1461553Srgrimes			*cp++ = '\0';
1471553Srgrimes			a_gid(cp);
1488857Srgrimes		}
14929656Swosch#endif
1501553Srgrimes		a_uid(*argv);
1518857Srgrimes	} else
1521553Srgrimes		a_gid(*argv);
1531553Srgrimes
1541553Srgrimes	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
1551553Srgrimes		err(1, NULL);
1561553Srgrimes
1571553Srgrimes	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
1581553Srgrimes		switch (p->fts_info) {
15983410Sru		case FTS_D:			/* Change it at FTS_DP. */
16017597Sadam			if (!Rflag)
16117597Sadam				fts_set(ftsp, p, FTS_SKIP);
16217597Sadam			continue;
16383410Sru		case FTS_DNR:			/* Warn, chown. */
1641553Srgrimes			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
1651553Srgrimes			rval = 1;
1661553Srgrimes			break;
1671553Srgrimes		case FTS_ERR:			/* Warn, continue. */
1681553Srgrimes		case FTS_NS:
1691553Srgrimes			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
1701553Srgrimes			rval = 1;
1711553Srgrimes			continue;
17283410Sru		case FTS_SL:
1731553Srgrimes		case FTS_SLNONE:
1741553Srgrimes			/*
1751553Srgrimes			 * The only symlinks that end up here are ones that
1761553Srgrimes			 * don't point to anything and ones that we found
1771553Srgrimes			 * doing a physical walk.
1781553Srgrimes			 */
17924446Speter			if (hflag)
18024446Speter				break;
18124446Speter			else
18224446Speter				continue;
1831553Srgrimes		default:
1841553Srgrimes			break;
1851553Srgrimes		}
18683410Sru		if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
18783410Sru		    (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
18864014Speter			continue;
18983410Sru		if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) {
19083410Sru			if (!fflag) {
19124446Speter				chownerr(p->fts_path);
19224446Speter				rval = 1;
19324446Speter			}
19424446Speter		} else {
195114005Sjohan			if (vflag) {
196114005Sjohan				printf("%s", p->fts_path);
197114005Sjohan				if (vflag > 1) {
198114005Sjohan					if (ischown) {
199114005Sjohan						printf(": %ju:%ju -> %ju:%ju",
200114005Sjohan						    (uintmax_t)
201114005Sjohan						    p->fts_statp->st_uid,
202114005Sjohan						    (uintmax_t)
203114005Sjohan						    p->fts_statp->st_gid,
204114005Sjohan						    (uid == (uid_t)-1) ?
205114005Sjohan						    (uintmax_t)
206114005Sjohan						    p->fts_statp->st_uid :
207114005Sjohan						    (uintmax_t)uid,
208114005Sjohan						    (gid == (gid_t)-1) ?
209114005Sjohan						    (uintmax_t)
210114005Sjohan						    p->fts_statp->st_gid :
211114005Sjohan						    (uintmax_t)gid);
212114005Sjohan					} else {
213114005Sjohan						printf(": %ju -> %ju",
214114005Sjohan						    (uintmax_t)
215114005Sjohan						    p->fts_statp->st_gid,
216114005Sjohan						    (gid == (gid_t)-1) ?
217114005Sjohan						    (uintmax_t)
218114005Sjohan						    p->fts_statp->st_gid :
219114005Sjohan						    (uintmax_t)gid);
220114005Sjohan					}
221114005Sjohan				}
222114005Sjohan				printf("\n");
223114005Sjohan			}
2241553Srgrimes		}
2251553Srgrimes	}
2261553Srgrimes	if (errno)
2271553Srgrimes		err(1, "fts_read");
2281553Srgrimes	exit(rval);
2291553Srgrimes}
2301553Srgrimes
2311553Srgrimesvoid
23299141Sjmalletta_gid(const char *s)
2331553Srgrimes{
2341553Srgrimes	struct group *gr;
2351553Srgrimes
2361553Srgrimes	if (*s == '\0')			/* Argument was "uid[:.]". */
2371553Srgrimes		return;
2381553Srgrimes	gname = s;
23983410Sru	gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group");
2401553Srgrimes}
2411553Srgrimes
2421553Srgrimesvoid
24399141Sjmalletta_uid(const char *s)
2441553Srgrimes{
2451553Srgrimes	struct passwd *pw;
2461553Srgrimes
2471553Srgrimes	if (*s == '\0')			/* Argument was "[:.]gid". */
2481553Srgrimes		return;
24983410Sru	uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user");
2501553Srgrimes}
2511553Srgrimes
2521553Srgrimesu_long
25399141Sjmallettid(const char *name, const char *type)
2541553Srgrimes{
2551553Srgrimes	u_long val;
2561553Srgrimes	char *ep;
2571553Srgrimes
2581553Srgrimes	/*
2591553Srgrimes	 * XXX
2601553Srgrimes	 * We know that uid_t's and gid_t's are unsigned longs.
2611553Srgrimes	 */
2621553Srgrimes	errno = 0;
2631553Srgrimes	val = strtoul(name, &ep, 10);
2641553Srgrimes	if (errno)
2651553Srgrimes		err(1, "%s", name);
2661553Srgrimes	if (*ep != '\0')
2671553Srgrimes		errx(1, "%s: illegal %s name", name, type);
2681553Srgrimes	return (val);
2691553Srgrimes}
2701553Srgrimes
2711553Srgrimesvoid
27299141Sjmallettchownerr(const char *file)
2731553Srgrimes{
27483410Sru	static uid_t euid = -1;
27583410Sru	static int ngroups = -1;
27683410Sru	gid_t groups[NGROUPS_MAX];
2771553Srgrimes
2781553Srgrimes	/* Check for chown without being root. */
27997732Stjr	if (errno != EPERM || (uid != (uid_t)-1 &&
28097732Stjr	    euid == (uid_t)-1 && (euid = geteuid()) != 0)) {
28197732Stjr		warn("%s", file);
28297732Stjr		return;
28397732Stjr	}
2841553Srgrimes
2851553Srgrimes	/* Check group membership; kernel just returns EPERM. */
28683410Sru	if (gid != (gid_t)-1 && ngroups == -1 &&
28783410Sru	    euid == (uid_t)-1 && (euid = geteuid()) != 0) {
28883410Sru		ngroups = getgroups(NGROUPS_MAX, groups);
2891553Srgrimes		while (--ngroups >= 0 && gid != groups[ngroups]);
29097732Stjr		if (ngroups < 0) {
29197732Stjr			warnx("you are not a member of group %s", gname);
29297732Stjr			return;
29397732Stjr		}
2941553Srgrimes	}
29528643Ssteve	warn("%s", file);
2961553Srgrimes}
2971553Srgrimes
2981553Srgrimesvoid
29999141Sjmallettusage(void)
3001553Srgrimes{
30183410Sru
30283410Sru	if (ischown)
30383410Sru		(void)fprintf(stderr, "%s\n%s\n",
30483410Sru		    "usage: chown [-fhv] [-R [-H | -L | -P]] owner[:group]"
30583410Sru		    " file ...",
30683410Sru		    "       chown [-fhv] [-R [-H | -L | -P]] :group file ...");
30783410Sru	else
30883410Sru		(void)fprintf(stderr, "%s\n",
30983410Sru		    "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ...");
3101553Srgrimes	exit(1);
3111553Srgrimes}
312