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