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