1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1988, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #if 0 33 #ifndef lint 34 static const char copyright[] = 35 "@(#) Copyright (c) 1988, 1993, 1994\n\ 36 The Regents of the University of California. All rights reserved.\n"; 37 #endif /* not lint */ 38 39 #ifndef lint 40 static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; 41 #endif /* not lint */ 42 #endif 43 44 #include <sys/cdefs.h> 45 __FBSDID("$FreeBSD$"); 46 47 #include <sys/param.h> 48 #include <sys/stat.h> 49 50 #include <err.h> 51 #include <errno.h> 52 #include <fcntl.h> 53 #include <fts.h> 54 #include <grp.h> 55 #include <libgen.h> 56 #include <pwd.h> 57 #include <signal.h> 58 #include <stdint.h> 59 #include <stdio.h> 60 #include <stdlib.h> 61 #include <string.h> 62 #include <unistd.h> 63 64 static void a_gid(const char *); 65 static void a_uid(const char *); 66 static void chownerr(const char *); 67 static uid_t id(const char *, const char *); 68 static void usage(void); 69 static void print_info(const FTSENT *, int); 70 71 static uid_t uid; 72 static gid_t gid; 73 static int ischown; 74 static const char *gname; 75 static volatile sig_atomic_t siginfo; 76 77 static void 78 siginfo_handler(int sig __unused) 79 { 80 81 siginfo = 1; 82 } 83 84 int 85 main(int argc, char **argv) 86 { 87 FTS *ftsp; 88 FTSENT *p; 89 int Hflag, Lflag, Rflag, fflag, hflag, vflag, xflag; 90 int ch, fts_options, rval; 91 char *cp; 92 93 ischown = (strcmp(basename(argv[0]), "chown") == 0); 94 95 Hflag = Lflag = Rflag = fflag = hflag = vflag = xflag = 0; 96 while ((ch = getopt(argc, argv, "HLPRfhvx")) != -1) 97 switch (ch) { 98 case 'H': 99 Hflag = 1; 100 Lflag = 0; 101 break; 102 case 'L': 103 Lflag = 1; 104 Hflag = 0; 105 break; 106 case 'P': 107 Hflag = Lflag = 0; 108 break; 109 case 'R': 110 Rflag = 1; 111 break; 112 case 'f': 113 fflag = 1; 114 break; 115 case 'h': 116 hflag = 1; 117 break; 118 case 'v': 119 vflag++; 120 break; 121 case 'x': 122 xflag = 1; 123 break; 124 case '?': 125 default: 126 usage(); 127 } 128 argv += optind; 129 argc -= optind; 130 131 if (argc < 2) 132 usage(); 133 134 (void)signal(SIGINFO, siginfo_handler); 135 136 if (Rflag) { 137 if (hflag && (Hflag || Lflag)) 138 errx(1, "the -R%c and -h options may not be " 139 "specified together", Hflag ? 'H' : 'L'); 140 if (Lflag) { 141 fts_options = FTS_LOGICAL; 142 } else { 143 fts_options = FTS_PHYSICAL; 144 145 if (Hflag) { 146 fts_options |= FTS_COMFOLLOW; 147 } 148 } 149 } else if (hflag) { 150 fts_options = FTS_PHYSICAL; 151 } else { 152 fts_options = FTS_LOGICAL; 153 } 154 155 if (xflag) 156 fts_options |= FTS_XDEV; 157 158 uid = (uid_t)-1; 159 gid = (gid_t)-1; 160 if (ischown) { 161 if ((cp = strchr(*argv, ':')) != NULL) { 162 *cp++ = '\0'; 163 a_gid(cp); 164 } 165 #ifdef SUPPORT_DOT 166 else if ((cp = strchr(*argv, '.')) != NULL) { 167 warnx("separation of user and group with a period is deprecated"); 168 *cp++ = '\0'; 169 a_gid(cp); 170 } 171 #endif 172 a_uid(*argv); 173 } else 174 a_gid(*argv); 175 176 if ((ftsp = fts_open(++argv, fts_options, NULL)) == NULL) 177 err(1, NULL); 178 179 for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 180 int atflag; 181 182 if ((fts_options & FTS_LOGICAL) || 183 ((fts_options & FTS_COMFOLLOW) && 184 p->fts_level == FTS_ROOTLEVEL)) 185 atflag = 0; 186 else 187 atflag = AT_SYMLINK_NOFOLLOW; 188 189 switch (p->fts_info) { 190 case FTS_D: /* Change it at FTS_DP. */ 191 if (!Rflag) 192 fts_set(ftsp, p, FTS_SKIP); 193 continue; 194 case FTS_DNR: /* Warn, chown. */ 195 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 196 rval = 1; 197 break; 198 case FTS_ERR: /* Warn, continue. */ 199 case FTS_NS: 200 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 201 rval = 1; 202 continue; 203 default: 204 break; 205 } 206 if (siginfo) { 207 print_info(p, 2); 208 siginfo = 0; 209 } 210 if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && 211 (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) 212 continue; 213 if (fchownat(AT_FDCWD, p->fts_accpath, uid, gid, atflag) 214 == -1 && !fflag) { 215 chownerr(p->fts_path); 216 rval = 1; 217 } else if (vflag) 218 print_info(p, vflag); 219 } 220 if (errno) 221 err(1, "fts_read"); 222 exit(rval); 223 } 224 225 static void 226 a_gid(const char *s) 227 { 228 struct group *gr; 229 230 if (*s == '\0') /* Argument was "uid[:.]". */ 231 return; 232 gname = s; 233 gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group"); 234 } 235 236 static void 237 a_uid(const char *s) 238 { 239 struct passwd *pw; 240 241 if (*s == '\0') /* Argument was "[:.]gid". */ 242 return; 243 uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user"); 244 } 245 246 static uid_t 247 id(const char *name, const char *type) 248 { 249 uid_t val; 250 char *ep; 251 252 /* 253 * XXX 254 * We know that uid_t's and gid_t's are unsigned longs. 255 */ 256 errno = 0; 257 val = strtoul(name, &ep, 10); 258 if (errno || *ep != '\0') 259 errx(1, "%s: illegal %s name", name, type); 260 return (val); 261 } 262 263 static void 264 chownerr(const char *file) 265 { 266 static uid_t euid = -1; 267 static int ngroups = -1; 268 static long ngroups_max; 269 gid_t *groups; 270 271 /* Check for chown without being root. */ 272 if (errno != EPERM || (uid != (uid_t)-1 && 273 euid == (uid_t)-1 && (euid = geteuid()) != 0)) { 274 warn("%s", file); 275 return; 276 } 277 278 /* Check group membership; kernel just returns EPERM. */ 279 if (gid != (gid_t)-1 && ngroups == -1 && 280 euid == (uid_t)-1 && (euid = geteuid()) != 0) { 281 ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; 282 if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) 283 err(1, "malloc"); 284 ngroups = getgroups(ngroups_max, groups); 285 while (--ngroups >= 0 && gid != groups[ngroups]); 286 free(groups); 287 if (ngroups < 0) { 288 warnx("you are not a member of group %s", gname); 289 return; 290 } 291 } 292 warn("%s", file); 293 } 294 295 static void 296 usage(void) 297 { 298 299 if (ischown) 300 (void)fprintf(stderr, "%s\n%s\n", 301 "usage: chown [-fhvx] [-R [-H | -L | -P]] owner[:group]" 302 " file ...", 303 " chown [-fhvx] [-R [-H | -L | -P]] :group file ..."); 304 else 305 (void)fprintf(stderr, "%s\n", 306 "usage: chgrp [-fhvx] [-R [-H | -L | -P]] group file ..."); 307 exit(1); 308 } 309 310 static void 311 print_info(const FTSENT *p, int vflag) 312 { 313 314 printf("%s", p->fts_path); 315 if (vflag > 1) { 316 if (ischown) { 317 printf(": %ju:%ju -> %ju:%ju", 318 (uintmax_t)p->fts_statp->st_uid, 319 (uintmax_t)p->fts_statp->st_gid, 320 (uid == (uid_t)-1) ? 321 (uintmax_t)p->fts_statp->st_uid : (uintmax_t)uid, 322 (gid == (gid_t)-1) ? 323 (uintmax_t)p->fts_statp->st_gid : (uintmax_t)gid); 324 } else { 325 printf(": %ju -> %ju", (uintmax_t)p->fts_statp->st_gid, 326 (gid == (gid_t)-1) ? 327 (uintmax_t)p->fts_statp->st_gid : (uintmax_t)gid); 328 } 329 } 330 printf("\n"); 331 } 332