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*66555Sbostic static char sccsid[] = "@(#)chown.c 8.6 (Berkeley) 03/31/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 99*66555Sbostic 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. */ 13966539Sbostic errno = p->fts_errno; 14066539Sbostic warn("%s", p->fts_path); 14166539Sbostic rval = 1; 14266542Sbostic break; 14366542Sbostic case FTS_ERR: /* Warn, continue. */ 144*66555Sbostic case FTS_NS: 14566542Sbostic errno = p->fts_errno; 14666542Sbostic warn("%s", p->fts_path); 14766542Sbostic rval = 1; 14853713Selan continue; 14966542Sbostic case FTS_SL: /* Ignore. */ 15066539Sbostic case FTS_SLNONE: 15166539Sbostic /* 15266539Sbostic * The only symlinks that end up here are ones that 15366539Sbostic * don't point to anything and ones that we found 15466539Sbostic * doing a physical walk. 15566539Sbostic */ 15666539Sbostic continue; 15766542Sbostic default: 15866542Sbostic break; 15953713Selan } 16066539Sbostic if (chown(p->fts_accpath, uid, gid) && !fflag) { 16153713Selan chownerr(p->fts_path); 16266539Sbostic rval = 1; 16366539Sbostic } 16436959Sbostic } 16566548Sbostic if (errno) 16666548Sbostic err(1, "fts_read"); 16766539Sbostic exit(rval); 16834039Sbostic } 16934039Sbostic 17066539Sbostic void 17147178Sbostic a_gid(s) 17266539Sbostic char *s; 17334039Sbostic { 17447178Sbostic struct group *gr; 17534039Sbostic 17666539Sbostic if (*s == '\0') /* Argument was "uid[:.]". */ 17734039Sbostic return; 17847178Sbostic gname = s; 17966539Sbostic gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid; 180975Sbill } 181975Sbill 18266539Sbostic void 18347178Sbostic a_uid(s) 18466539Sbostic char *s; 185975Sbill { 18647178Sbostic struct passwd *pw; 187975Sbill 18866539Sbostic if (*s == '\0') /* Argument was "[:.]gid". */ 18934039Sbostic return; 19066539Sbostic uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid; 191975Sbill } 19218450Smckusick 19366539Sbostic u_long 19466539Sbostic id(name, type) 19566539Sbostic char *name, *type; 19666539Sbostic { 19766539Sbostic u_long val; 19866539Sbostic char *ep; 19966539Sbostic 20066539Sbostic /* 20166539Sbostic * XXX 20266539Sbostic * We know that uid_t's and gid_t's are unsigned longs. 20366539Sbostic */ 20466539Sbostic errno = 0; 20566539Sbostic val = strtoul(name, &ep, 10); 20666539Sbostic if (errno) 20766539Sbostic err(1, "%s", name); 20866539Sbostic if (*ep != '\0') 20966539Sbostic errx(1, "%s: illegal %s name", name, type); 21066539Sbostic return (val); 21166539Sbostic } 21266539Sbostic 21366539Sbostic void 21434069Sbostic chownerr(file) 21534069Sbostic char *file; 21634069Sbostic { 21734069Sbostic static int euid = -1, ngroups = -1; 21866539Sbostic int groups[NGROUPS]; 21934069Sbostic 22066539Sbostic /* Check for chown without being root. */ 22166539Sbostic if (errno != EPERM || 22266539Sbostic uid != -1 && euid == -1 && (euid = geteuid()) != 0) { 22334069Sbostic if (fflag) 22434069Sbostic exit(0); 22566539Sbostic err(1, "%s", file); 22634069Sbostic } 22766539Sbostic 22866539Sbostic /* Check group membership; kernel just returns EPERM. */ 22934069Sbostic if (gid != -1 && ngroups == -1) { 23034069Sbostic ngroups = getgroups(NGROUPS, groups); 23134069Sbostic while (--ngroups >= 0 && gid != groups[ngroups]); 23234069Sbostic if (ngroups < 0) { 23334069Sbostic if (fflag) 23434069Sbostic exit(0); 23566539Sbostic errx(1, "you are not a member of group %s", gname); 23634069Sbostic } 23734069Sbostic } 23834069Sbostic 23966539Sbostic if (!fflag) 24066539Sbostic warn("%s", file); 24124505Ssam } 24224505Ssam 24366539Sbostic void 24434039Sbostic usage() 24524505Ssam { 24666539Sbostic (void)fprintf(stderr, 24766539Sbostic "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n", 24866539Sbostic myname, ischown ? "[owner][:group]" : "group"); 24945426Sbostic exit(1); 25024505Ssam } 251