xref: /csrg-svn/usr.sbin/chown/chown.c (revision 66703)
122488Sdist /*
266539Sbostic  * Copyright (c) 1988, 1993, 1994
361813Sbostic  *	The Regents of the University of California.  All rights reserved.
434039Sbostic  *
542792Sbostic  * %sccs.include.redist.c%
622488Sdist  */
718450Smckusick 
822488Sdist #ifndef lint
961813Sbostic static char copyright[] =
1066539Sbostic "@(#) Copyright (c) 1988, 1993, 1994\n\
1161813Sbostic 	The Regents of the University of California.  All rights reserved.\n";
1234039Sbostic #endif /* not lint */
1322488Sdist 
1422488Sdist #ifndef lint
15*66703Sbostic static char sccsid[] = "@(#)chown.c	8.8 (Berkeley) 04/04/94";
1634039Sbostic #endif /* not lint */
1722488Sdist 
1834039Sbostic #include <sys/param.h>
19975Sbill #include <sys/stat.h>
2066539Sbostic 
2166539Sbostic #include <ctype.h>
2238427Sbostic #include <dirent.h>
2366539Sbostic #include <err.h>
2466539Sbostic #include <errno.h>
2545426Sbostic #include <fts.h>
2666539Sbostic #include <grp.h>
27975Sbill #include <pwd.h>
2834039Sbostic #include <stdio.h>
2947178Sbostic #include <stdlib.h>
3045426Sbostic #include <string.h>
3166539Sbostic #include <unistd.h>
32975Sbill 
3366539Sbostic void	a_gid __P((char *));
3466539Sbostic void	a_uid __P((char *));
3566539Sbostic void	chownerr __P((char *));
3666539Sbostic u_long	id __P((char *, char *));
3766539Sbostic void	usage __P((void));
3866539Sbostic 
3966539Sbostic uid_t uid;
4066539Sbostic gid_t gid;
4166539Sbostic int Rflag, ischown, fflag;
4241090Smarc char *gname, *myname;
43975Sbill 
4466539Sbostic int
main(argc,argv)45975Sbill main(argc, argv)
4634039Sbostic 	int argc;
4766539Sbostic 	char *argv[];
48975Sbill {
4966539Sbostic 	FTS *ftsp;
5066539Sbostic 	FTSENT *p;
5166539Sbostic 	int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
5266539Sbostic 	char *cp;
5353772Selan 
5434039Sbostic 	myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
5534039Sbostic 	ischown = myname[2] == 'o';
5653772Selan 
5766539Sbostic 	Hflag = Lflag = Pflag = hflag = 0;
5866539Sbostic 	while ((ch = getopt(argc, argv, "HLPRfh")) != EOF)
5966539Sbostic 		switch (ch) {
6066539Sbostic 		case 'H':
6166539Sbostic 			Hflag = 1;
6266539Sbostic 			Lflag = Pflag = 0;
6366539Sbostic 			break;
6466539Sbostic 		case 'L':
6566539Sbostic 			Lflag = 1;
6666539Sbostic 			Hflag = Pflag = 0;
6766539Sbostic 			break;
6866539Sbostic 		case 'P':
6966539Sbostic 			Pflag = 1;
7066539Sbostic 			Hflag = Lflag = 0;
7166539Sbostic 			break;
7234039Sbostic 		case 'R':
7366539Sbostic 			Rflag = 1;
7434039Sbostic 			break;
7524505Ssam 		case 'f':
7645426Sbostic 			fflag = 1;
7724505Ssam 			break;
7853713Selan 		case 'h':
7966539Sbostic 			/*
8066539Sbostic 			 * In System V (and probably POSIX.2) the -h option
8166539Sbostic 			 * causes chown/chgrp to change the owner/group of
8266539Sbostic 			 * the symbolic link.  4.4BSD's symbolic links don't
8366539Sbostic 			 * have owners/groups, so it's an undocumented noop.
8466539Sbostic 			 * Do syntax checking, though.
8566539Sbostic 			 */
8653772Selan 			hflag = 1;
8753713Selan 			break;
8834039Sbostic 		case '?':
8934039Sbostic 		default:
9034039Sbostic 			usage();
9134039Sbostic 		}
9234039Sbostic 	argv += optind;
9334039Sbostic 	argc -= optind;
9424505Ssam 
9534039Sbostic 	if (argc < 2)
9634039Sbostic 		usage();
9724505Ssam 
9866555Sbostic 	fts_options = FTS_PHYSICAL;
9966539Sbostic 	if (Rflag) {
10066539Sbostic 		if (hflag)
10166539Sbostic 			errx(1,
10266539Sbostic 		"the -R and -h options may not be specified together.");
10366539Sbostic 		if (Hflag)
10466539Sbostic 			fts_options |= FTS_COMFOLLOW;
10566539Sbostic 		if (Lflag) {
10666539Sbostic 			fts_options &= ~FTS_PHYSICAL;
10766539Sbostic 			fts_options |= FTS_LOGICAL;
10866539Sbostic 		}
10966539Sbostic 	}
11066539Sbostic 
11145426Sbostic 	uid = gid = -1;
11234039Sbostic 	if (ischown) {
11345426Sbostic #ifdef SUPPORT_DOT
11466539Sbostic 		if ((cp = strchr(*argv, '.')) != NULL) {
11534039Sbostic 			*cp++ = '\0';
11647178Sbostic 			a_gid(cp);
11745426Sbostic 		} else
11845426Sbostic #endif
11966539Sbostic 		if ((cp = strchr(*argv, ':')) != NULL) {
12045426Sbostic 			*cp++ = '\0';
12147178Sbostic 			a_gid(cp);
12245426Sbostic 		}
12347178Sbostic 		a_uid(*argv);
12466539Sbostic 	} else
12547178Sbostic 		a_gid(*argv);
12634039Sbostic 
12766539Sbostic 	if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
12866539Sbostic 		err(1, NULL);
12966539Sbostic 
13066539Sbostic 	for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
13166539Sbostic 		switch (p->fts_info) {
13266539Sbostic 		case FTS_D:
13366539Sbostic 			if (Rflag)		/* Change it at FTS_DP. */
13445426Sbostic 				continue;
13566539Sbostic 			fts_set(ftsp, p, FTS_SKIP);
13666539Sbostic 			break;
13766542Sbostic 		case FTS_DNR:			/* Warn, chown, continue. */
13866598Sbostic 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
13966539Sbostic 			rval = 1;
14066542Sbostic 			break;
14166542Sbostic 		case FTS_ERR:			/* Warn, continue. */
14266555Sbostic 		case FTS_NS:
14366598Sbostic 			warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
14466542Sbostic 			rval = 1;
14553713Selan 			continue;
14666542Sbostic 		case FTS_SL:			/* Ignore. */
14766539Sbostic 		case FTS_SLNONE:
14866539Sbostic 			/*
14966539Sbostic 			 * The only symlinks that end up here are ones that
15066539Sbostic 			 * don't point to anything and ones that we found
15166539Sbostic 			 * doing a physical walk.
15266539Sbostic 			 */
15366539Sbostic 			continue;
15466542Sbostic 		default:
15566542Sbostic 			break;
15653713Selan 		}
15766539Sbostic 		if (chown(p->fts_accpath, uid, gid) && !fflag) {
15853713Selan 			chownerr(p->fts_path);
15966539Sbostic 			rval = 1;
16066539Sbostic 		}
16136959Sbostic 	}
16266548Sbostic 	if (errno)
16366548Sbostic 		err(1, "fts_read");
16466539Sbostic 	exit(rval);
16534039Sbostic }
16634039Sbostic 
16766539Sbostic void
a_gid(s)16847178Sbostic a_gid(s)
16966539Sbostic 	char *s;
17034039Sbostic {
17147178Sbostic 	struct group *gr;
17234039Sbostic 
17366539Sbostic 	if (*s == '\0')			/* Argument was "uid[:.]". */
17434039Sbostic 		return;
17547178Sbostic 	gname = s;
17666539Sbostic 	gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
177975Sbill }
178975Sbill 
17966539Sbostic void
a_uid(s)18047178Sbostic a_uid(s)
18166539Sbostic 	char *s;
182975Sbill {
18347178Sbostic 	struct passwd *pw;
184975Sbill 
18566539Sbostic 	if (*s == '\0')			/* Argument was "[:.]gid". */
18634039Sbostic 		return;
18766539Sbostic 	uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
188975Sbill }
18918450Smckusick 
19066539Sbostic u_long
id(name,type)19166539Sbostic id(name, type)
19266539Sbostic 	char *name, *type;
19366539Sbostic {
19466539Sbostic 	u_long val;
19566539Sbostic 	char *ep;
19666539Sbostic 
19766539Sbostic 	/*
19866539Sbostic 	 * XXX
19966539Sbostic 	 * We know that uid_t's and gid_t's are unsigned longs.
20066539Sbostic 	 */
20166539Sbostic 	errno = 0;
20266539Sbostic 	val = strtoul(name, &ep, 10);
20366539Sbostic 	if (errno)
20466539Sbostic 		err(1, "%s", name);
20566539Sbostic 	if (*ep != '\0')
20666539Sbostic 		errx(1, "%s: illegal %s name", name, type);
20766539Sbostic 	return (val);
20866539Sbostic }
20966539Sbostic 
21066539Sbostic void
chownerr(file)21134069Sbostic chownerr(file)
21234069Sbostic 	char *file;
21334069Sbostic {
21434069Sbostic 	static int euid = -1, ngroups = -1;
21566539Sbostic 	int groups[NGROUPS];
21634069Sbostic 
21766539Sbostic 	/* Check for chown without being root. */
21866539Sbostic 	if (errno != EPERM ||
21966539Sbostic 	    uid != -1 && euid == -1 && (euid = geteuid()) != 0) {
22034069Sbostic 		if (fflag)
22134069Sbostic 			exit(0);
22266539Sbostic 		err(1, "%s", file);
22334069Sbostic 	}
22466539Sbostic 
22566539Sbostic 	/* Check group membership; kernel just returns EPERM. */
22634069Sbostic 	if (gid != -1 && ngroups == -1) {
22734069Sbostic 		ngroups = getgroups(NGROUPS, groups);
22834069Sbostic 		while (--ngroups >= 0 && gid != groups[ngroups]);
22934069Sbostic 		if (ngroups < 0) {
23034069Sbostic 			if (fflag)
23134069Sbostic 				exit(0);
23266539Sbostic 			errx(1, "you are not a member of group %s", gname);
23334069Sbostic 		}
23434069Sbostic 	}
23534069Sbostic 
23666539Sbostic 	if (!fflag)
23766539Sbostic 		warn("%s", file);
23824505Ssam }
23924505Ssam 
24066539Sbostic void
usage()24134039Sbostic usage()
24224505Ssam {
24366539Sbostic 	(void)fprintf(stderr,
24466539Sbostic 	    "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n",
24566539Sbostic 	    myname, ischown ? "[owner][:group]" : "group");
24645426Sbostic 	exit(1);
24724505Ssam }
248