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*66598Sbostic static char sccsid[] = "@(#)chown.c 8.7 (Berkeley) 04/01/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 45975Sbill main(argc, argv) 4634039Sbostic int argc; 4766539Sbostic char *argv[]; 48975Sbill { 4934039Sbostic extern int optind; 5066539Sbostic FTS *ftsp; 5166539Sbostic FTSENT *p; 5266539Sbostic int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval; 5366539Sbostic char *cp; 5453772Selan 5534039Sbostic myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv; 5634039Sbostic ischown = myname[2] == 'o'; 5753772Selan 5866539Sbostic Hflag = Lflag = Pflag = hflag = 0; 5966539Sbostic while ((ch = getopt(argc, argv, "HLPRfh")) != EOF) 6066539Sbostic switch (ch) { 6166539Sbostic case 'H': 6266539Sbostic Hflag = 1; 6366539Sbostic Lflag = Pflag = 0; 6466539Sbostic break; 6566539Sbostic case 'L': 6666539Sbostic Lflag = 1; 6766539Sbostic Hflag = Pflag = 0; 6866539Sbostic break; 6966539Sbostic case 'P': 7066539Sbostic Pflag = 1; 7166539Sbostic Hflag = Lflag = 0; 7266539Sbostic break; 7334039Sbostic case 'R': 7466539Sbostic Rflag = 1; 7534039Sbostic break; 7624505Ssam case 'f': 7745426Sbostic fflag = 1; 7824505Ssam break; 7953713Selan case 'h': 8066539Sbostic /* 8166539Sbostic * In System V (and probably POSIX.2) the -h option 8266539Sbostic * causes chown/chgrp to change the owner/group of 8366539Sbostic * the symbolic link. 4.4BSD's symbolic links don't 8466539Sbostic * have owners/groups, so it's an undocumented noop. 8566539Sbostic * Do syntax checking, though. 8666539Sbostic */ 8753772Selan hflag = 1; 8853713Selan break; 8934039Sbostic case '?': 9034039Sbostic default: 9134039Sbostic usage(); 9234039Sbostic } 9334039Sbostic argv += optind; 9434039Sbostic argc -= optind; 9524505Ssam 9634039Sbostic if (argc < 2) 9734039Sbostic usage(); 9824505Ssam 9966555Sbostic fts_options = FTS_PHYSICAL; 10066539Sbostic if (Rflag) { 10166539Sbostic if (hflag) 10266539Sbostic errx(1, 10366539Sbostic "the -R and -h options may not be specified together."); 10466539Sbostic if (Hflag) 10566539Sbostic fts_options |= FTS_COMFOLLOW; 10666539Sbostic if (Lflag) { 10766539Sbostic fts_options &= ~FTS_PHYSICAL; 10866539Sbostic fts_options |= FTS_LOGICAL; 10966539Sbostic } 11066539Sbostic } 11166539Sbostic 11245426Sbostic uid = gid = -1; 11334039Sbostic if (ischown) { 11445426Sbostic #ifdef SUPPORT_DOT 11566539Sbostic if ((cp = strchr(*argv, '.')) != NULL) { 11634039Sbostic *cp++ = '\0'; 11747178Sbostic a_gid(cp); 11845426Sbostic } else 11945426Sbostic #endif 12066539Sbostic if ((cp = strchr(*argv, ':')) != NULL) { 12145426Sbostic *cp++ = '\0'; 12247178Sbostic a_gid(cp); 12345426Sbostic } 12447178Sbostic a_uid(*argv); 12566539Sbostic } else 12647178Sbostic a_gid(*argv); 12734039Sbostic 12866539Sbostic if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) 12966539Sbostic err(1, NULL); 13066539Sbostic 13166539Sbostic for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 13266539Sbostic switch (p->fts_info) { 13366539Sbostic case FTS_D: 13466539Sbostic if (Rflag) /* Change it at FTS_DP. */ 13545426Sbostic continue; 13666539Sbostic fts_set(ftsp, p, FTS_SKIP); 13766539Sbostic break; 13866542Sbostic case FTS_DNR: /* Warn, chown, continue. */ 139*66598Sbostic warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 14066539Sbostic rval = 1; 14166542Sbostic break; 14266542Sbostic case FTS_ERR: /* Warn, continue. */ 14366555Sbostic case FTS_NS: 144*66598Sbostic warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 14566542Sbostic rval = 1; 14653713Selan continue; 14766542Sbostic case FTS_SL: /* Ignore. */ 14866539Sbostic case FTS_SLNONE: 14966539Sbostic /* 15066539Sbostic * The only symlinks that end up here are ones that 15166539Sbostic * don't point to anything and ones that we found 15266539Sbostic * doing a physical walk. 15366539Sbostic */ 15466539Sbostic continue; 15566542Sbostic default: 15666542Sbostic break; 15753713Selan } 15866539Sbostic if (chown(p->fts_accpath, uid, gid) && !fflag) { 15953713Selan chownerr(p->fts_path); 16066539Sbostic rval = 1; 16166539Sbostic } 16236959Sbostic } 16366548Sbostic if (errno) 16466548Sbostic err(1, "fts_read"); 16566539Sbostic exit(rval); 16634039Sbostic } 16734039Sbostic 16866539Sbostic void 16947178Sbostic a_gid(s) 17066539Sbostic char *s; 17134039Sbostic { 17247178Sbostic struct group *gr; 17334039Sbostic 17466539Sbostic if (*s == '\0') /* Argument was "uid[:.]". */ 17534039Sbostic return; 17647178Sbostic gname = s; 17766539Sbostic gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid; 178975Sbill } 179975Sbill 18066539Sbostic void 18147178Sbostic a_uid(s) 18266539Sbostic char *s; 183975Sbill { 18447178Sbostic struct passwd *pw; 185975Sbill 18666539Sbostic if (*s == '\0') /* Argument was "[:.]gid". */ 18734039Sbostic return; 18866539Sbostic uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid; 189975Sbill } 19018450Smckusick 19166539Sbostic u_long 19266539Sbostic id(name, type) 19366539Sbostic char *name, *type; 19466539Sbostic { 19566539Sbostic u_long val; 19666539Sbostic char *ep; 19766539Sbostic 19866539Sbostic /* 19966539Sbostic * XXX 20066539Sbostic * We know that uid_t's and gid_t's are unsigned longs. 20166539Sbostic */ 20266539Sbostic errno = 0; 20366539Sbostic val = strtoul(name, &ep, 10); 20466539Sbostic if (errno) 20566539Sbostic err(1, "%s", name); 20666539Sbostic if (*ep != '\0') 20766539Sbostic errx(1, "%s: illegal %s name", name, type); 20866539Sbostic return (val); 20966539Sbostic } 21066539Sbostic 21166539Sbostic void 21234069Sbostic chownerr(file) 21334069Sbostic char *file; 21434069Sbostic { 21534069Sbostic static int euid = -1, ngroups = -1; 21666539Sbostic int groups[NGROUPS]; 21734069Sbostic 21866539Sbostic /* Check for chown without being root. */ 21966539Sbostic if (errno != EPERM || 22066539Sbostic uid != -1 && euid == -1 && (euid = geteuid()) != 0) { 22134069Sbostic if (fflag) 22234069Sbostic exit(0); 22366539Sbostic err(1, "%s", file); 22434069Sbostic } 22566539Sbostic 22666539Sbostic /* Check group membership; kernel just returns EPERM. */ 22734069Sbostic if (gid != -1 && ngroups == -1) { 22834069Sbostic ngroups = getgroups(NGROUPS, groups); 22934069Sbostic while (--ngroups >= 0 && gid != groups[ngroups]); 23034069Sbostic if (ngroups < 0) { 23134069Sbostic if (fflag) 23234069Sbostic exit(0); 23366539Sbostic errx(1, "you are not a member of group %s", gname); 23434069Sbostic } 23534069Sbostic } 23634069Sbostic 23766539Sbostic if (!fflag) 23866539Sbostic warn("%s", file); 23924505Ssam } 24024505Ssam 24166539Sbostic void 24234039Sbostic usage() 24324505Ssam { 24466539Sbostic (void)fprintf(stderr, 24566539Sbostic "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n", 24666539Sbostic myname, ischown ? "[owner][:group]" : "group"); 24745426Sbostic exit(1); 24824505Ssam } 249