1 /* $NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1993, 1994, 2003 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 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994, 2003\ 35 The Regents of the University of California. All rights reserved."); 36 #endif /* not lint */ 37 38 #ifndef lint 39 #if 0 40 static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; 41 #else 42 __RCSID("$NetBSD: chown.c,v 1.8 2012/10/24 01:12:51 enami Exp $"); 43 #endif 44 #endif /* not lint */ 45 46 #include <sys/types.h> 47 #include <sys/stat.h> 48 49 #include <ctype.h> 50 #include <dirent.h> 51 #include <err.h> 52 #include <errno.h> 53 #include <locale.h> 54 #include <fts.h> 55 #include <grp.h> 56 #include <pwd.h> 57 #include <stdio.h> 58 #include <stdlib.h> 59 #include <string.h> 60 #include <unistd.h> 61 #include <getopt.h> 62 63 static void a_gid(const char *); 64 static void a_uid(const char *); 65 static id_t id(const char *, const char *); 66 __dead static void usage(void); 67 68 static uid_t uid; 69 static gid_t gid; 70 static int ischown; 71 static const char *myname; 72 73 struct option chown_longopts[] = { 74 { "reference", required_argument, 0, 75 1 }, 76 { NULL, 0, 0, 77 0 }, 78 }; 79 80 int 81 main(int argc, char **argv) 82 { 83 FTS *ftsp; 84 FTSENT *p; 85 int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, vflag; 86 char *cp, *reference; 87 int (*change_owner)(const char *, uid_t, gid_t); 88 89 setprogname(*argv); 90 91 (void)setlocale(LC_ALL, ""); 92 93 myname = getprogname(); 94 ischown = (myname[2] == 'o'); 95 reference = NULL; 96 97 Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; 98 while ((ch = getopt_long(argc, argv, "HLPRfhv", 99 chown_longopts, NULL)) != -1) 100 switch (ch) { 101 case 1: 102 reference = optarg; 103 break; 104 case 'H': 105 Hflag = 1; 106 Lflag = 0; 107 break; 108 case 'L': 109 Lflag = 1; 110 Hflag = 0; 111 break; 112 case 'P': 113 Hflag = Lflag = 0; 114 break; 115 case 'R': 116 Rflag = 1; 117 break; 118 case 'f': 119 fflag = 1; 120 break; 121 case 'h': 122 /* 123 * In System V the -h option causes chown/chgrp to 124 * change the owner/group of the symbolic link. 125 * 4.4BSD's symbolic links didn't have owners/groups, 126 * so it was an undocumented noop. 127 * In NetBSD 1.3, lchown(2) is introduced. 128 */ 129 hflag = 1; 130 break; 131 case 'v': 132 vflag = 1; 133 break; 134 case '?': 135 default: 136 usage(); 137 } 138 argv += optind; 139 argc -= optind; 140 141 if (argc == 0 || (argc == 1 && reference == NULL)) 142 usage(); 143 144 fts_options = FTS_PHYSICAL; 145 if (Rflag) { 146 if (Hflag) 147 fts_options |= FTS_COMFOLLOW; 148 if (Lflag) { 149 if (hflag) 150 errx(EXIT_FAILURE, 151 "the -L and -h options " 152 "may not be specified together."); 153 fts_options &= ~FTS_PHYSICAL; 154 fts_options |= FTS_LOGICAL; 155 } 156 } else if (!hflag) 157 fts_options |= FTS_COMFOLLOW; 158 159 uid = (uid_t)-1; 160 gid = (gid_t)-1; 161 if (reference == NULL) { 162 if (ischown) { 163 if ((cp = strchr(*argv, ':')) != NULL) { 164 *cp++ = '\0'; 165 a_gid(cp); 166 } 167 #ifdef SUPPORT_DOT 168 else if ((cp = strrchr(*argv, '.')) != NULL) { 169 if (uid_from_user(*argv, &uid) == -1) { 170 *cp++ = '\0'; 171 a_gid(cp); 172 } 173 } 174 #endif 175 a_uid(*argv); 176 } else 177 a_gid(*argv); 178 argv++; 179 } else { 180 struct stat st; 181 182 if (stat(reference, &st) == -1) 183 err(EXIT_FAILURE, "Cannot stat `%s'", reference); 184 if (ischown) 185 uid = st.st_uid; 186 gid = st.st_gid; 187 } 188 189 if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) 190 err(EXIT_FAILURE, "fts_open"); 191 192 for (rval = EXIT_SUCCESS; (p = fts_read(ftsp)) != NULL;) { 193 change_owner = chown; 194 switch (p->fts_info) { 195 case FTS_D: 196 if (!Rflag) /* Change it at FTS_DP. */ 197 fts_set(ftsp, p, FTS_SKIP); 198 continue; 199 case FTS_DNR: /* Warn, chown, continue. */ 200 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 201 rval = EXIT_FAILURE; 202 break; 203 case FTS_ERR: /* Warn, continue. */ 204 case FTS_NS: 205 warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 206 rval = EXIT_FAILURE; 207 continue; 208 case FTS_SL: /* Ignore unless -h. */ 209 /* 210 * All symlinks we found while doing a physical 211 * walk end up here. 212 */ 213 if (!hflag) 214 continue; 215 /* 216 * Note that if we follow a symlink, fts_info is 217 * not FTS_SL but FTS_F or whatever. And we should 218 * use lchown only for FTS_SL and should use chown 219 * for others. 220 */ 221 change_owner = lchown; 222 break; 223 case FTS_SLNONE: /* Ignore. */ 224 /* 225 * The only symlinks that end up here are ones that 226 * don't point to anything. Note that if we are 227 * doing a phisycal walk, we never reach here unless 228 * we asked to follow explicitly. 229 */ 230 continue; 231 default: 232 break; 233 } 234 235 if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) { 236 warn("%s", p->fts_path); 237 rval = EXIT_FAILURE; 238 } else { 239 if (vflag) 240 printf("%s\n", p->fts_path); 241 } 242 } 243 if (errno) 244 err(EXIT_FAILURE, "fts_read"); 245 exit(rval); 246 /* NOTREACHED */ 247 } 248 249 static void 250 a_gid(const char *s) 251 { 252 struct group *gr; 253 254 if (*s == '\0') /* Argument was "uid[:.]". */ 255 return; 256 gr = *s == '#' ? NULL : getgrnam(s); 257 if (gr == NULL) 258 gid = id(s, "group"); 259 else 260 gid = gr->gr_gid; 261 return; 262 } 263 264 static void 265 a_uid(const char *s) 266 { 267 if (*s == '\0') /* Argument was "[:.]gid". */ 268 return; 269 if (*s == '#' || uid_from_user(s, &uid) == -1) { 270 uid = id(s, "user"); 271 } 272 return; 273 } 274 275 static id_t 276 id(const char *name, const char *type) 277 { 278 id_t val; 279 char *ep; 280 281 errno = 0; 282 if (*name == '#') 283 name++; 284 val = (id_t)strtoul(name, &ep, 10); 285 if (errno) 286 err(EXIT_FAILURE, "%s", name); 287 if (*ep != '\0') 288 errx(EXIT_FAILURE, "%s: invalid %s name", name, type); 289 return (val); 290 } 291 292 static void 293 usage(void) 294 { 295 296 (void)fprintf(stderr, 297 "Usage: %s [-R [-H | -L | -P]] [-fhv] %s file ...\n" 298 "\t%s [-R [-H | -L | -P]] [-fhv] --reference=rfile file ...\n", 299 myname, ischown ? "owner:group|owner|:group" : "group", 300 myname); 301 exit(EXIT_FAILURE); 302 } 303