1 /* 2 * Copyright (c) 1988, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char copyright[] = 10 "@(#) Copyright (c) 1988, 1993, 1994\n\ 11 The Regents of the University of California. All rights reserved.\n"; 12 #endif /* not lint */ 13 14 #ifndef lint 15 static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 04/04/94"; 16 #endif /* not lint */ 17 18 #include <sys/param.h> 19 #include <sys/stat.h> 20 21 #include <ctype.h> 22 #include <dirent.h> 23 #include <err.h> 24 #include <errno.h> 25 #include <fts.h> 26 #include <grp.h> 27 #include <pwd.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <string.h> 31 #include <unistd.h> 32 33 void a_gid __P((char *)); 34 void a_uid __P((char *)); 35 void chownerr __P((char *)); 36 u_long id __P((char *, char *)); 37 void usage __P((void)); 38 39 uid_t uid; 40 gid_t gid; 41 int Rflag, ischown, fflag; 42 char *gname, *myname; 43 44 int 45 main(argc, argv) 46 int argc; 47 char *argv[]; 48 { 49 FTS *ftsp; 50 FTSENT *p; 51 int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval; 52 char *cp; 53 54 myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv; 55 ischown = myname[2] == 'o'; 56 57 Hflag = Lflag = Pflag = hflag = 0; 58 while ((ch = getopt(argc, argv, "HLPRfh")) != EOF) 59 switch (ch) { 60 case 'H': 61 Hflag = 1; 62 Lflag = Pflag = 0; 63 break; 64 case 'L': 65 Lflag = 1; 66 Hflag = Pflag = 0; 67 break; 68 case 'P': 69 Pflag = 1; 70 Hflag = Lflag = 0; 71 break; 72 case 'R': 73 Rflag = 1; 74 break; 75 case 'f': 76 fflag = 1; 77 break; 78 case 'h': 79 /* 80 * In System V (and probably POSIX.2) the -h option 81 * causes chown/chgrp to change the owner/group of 82 * the symbolic link. 4.4BSD's symbolic links don't 83 * have owners/groups, so it's an undocumented noop. 84 * Do syntax checking, though. 85 */ 86 hflag = 1; 87 break; 88 case '?': 89 default: 90 usage(); 91 } 92 argv += optind; 93 argc -= optind; 94 95 if (argc < 2) 96 usage(); 97 98 fts_options = FTS_PHYSICAL; 99 if (Rflag) { 100 if (hflag) 101 errx(1, 102 "the -R and -h options may not be specified together."); 103 if (Hflag) 104 fts_options |= FTS_COMFOLLOW; 105 if (Lflag) { 106 fts_options &= ~FTS_PHYSICAL; 107 fts_options |= FTS_LOGICAL; 108 } 109 } 110 111 uid = gid = -1; 112 if (ischown) { 113 #ifdef SUPPORT_DOT 114 if ((cp = strchr(*argv, '.')) != NULL) { 115 *cp++ = '\0'; 116 a_gid(cp); 117 } else 118 #endif 119 if ((cp = strchr(*argv, ':')) != NULL) { 120 *cp++ = '\0'; 121 a_gid(cp); 122 } 123 a_uid(*argv); 124 } else 125 a_gid(*argv); 126 127 if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) 128 err(1, NULL); 129 130 for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 131 switch (p->fts_info) { 132 case FTS_D: 133 if (Rflag) /* Change it at FTS_DP. */ 134 continue; 135 fts_set(ftsp, p, FTS_SKIP); 136 break; 137 case FTS_DNR: /* Warn, chown, continue. */ 138 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 139 rval = 1; 140 break; 141 case FTS_ERR: /* Warn, continue. */ 142 case FTS_NS: 143 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 144 rval = 1; 145 continue; 146 case FTS_SL: /* Ignore. */ 147 case FTS_SLNONE: 148 /* 149 * The only symlinks that end up here are ones that 150 * don't point to anything and ones that we found 151 * doing a physical walk. 152 */ 153 continue; 154 default: 155 break; 156 } 157 if (chown(p->fts_accpath, uid, gid) && !fflag) { 158 chownerr(p->fts_path); 159 rval = 1; 160 } 161 } 162 if (errno) 163 err(1, "fts_read"); 164 exit(rval); 165 } 166 167 void 168 a_gid(s) 169 char *s; 170 { 171 struct group *gr; 172 173 if (*s == '\0') /* Argument was "uid[:.]". */ 174 return; 175 gname = s; 176 gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid; 177 } 178 179 void 180 a_uid(s) 181 char *s; 182 { 183 struct passwd *pw; 184 185 if (*s == '\0') /* Argument was "[:.]gid". */ 186 return; 187 uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid; 188 } 189 190 u_long 191 id(name, type) 192 char *name, *type; 193 { 194 u_long val; 195 char *ep; 196 197 /* 198 * XXX 199 * We know that uid_t's and gid_t's are unsigned longs. 200 */ 201 errno = 0; 202 val = strtoul(name, &ep, 10); 203 if (errno) 204 err(1, "%s", name); 205 if (*ep != '\0') 206 errx(1, "%s: illegal %s name", name, type); 207 return (val); 208 } 209 210 void 211 chownerr(file) 212 char *file; 213 { 214 static int euid = -1, ngroups = -1; 215 int groups[NGROUPS]; 216 217 /* Check for chown without being root. */ 218 if (errno != EPERM || 219 uid != -1 && euid == -1 && (euid = geteuid()) != 0) { 220 if (fflag) 221 exit(0); 222 err(1, "%s", file); 223 } 224 225 /* Check group membership; kernel just returns EPERM. */ 226 if (gid != -1 && ngroups == -1) { 227 ngroups = getgroups(NGROUPS, groups); 228 while (--ngroups >= 0 && gid != groups[ngroups]); 229 if (ngroups < 0) { 230 if (fflag) 231 exit(0); 232 errx(1, "you are not a member of group %s", gname); 233 } 234 } 235 236 if (!fflag) 237 warn("%s", file); 238 } 239 240 void 241 usage() 242 { 243 (void)fprintf(stderr, 244 "usage: %s [-R [-H | -L | -P]] [-f] %s file ...\n", 245 myname, ischown ? "[owner][:group]" : "group"); 246 exit(1); 247 } 248