| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 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> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 57 | #include <stdio.h> | 
|  | 58 | #include <stdlib.h> | 
|  | 59 | #include <string.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 60 | #include <unistd.h> | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 61 | #include <getopt.h> | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 62 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 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 | chown_main(int argc, char **argv) | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 82 | { | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 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); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 88 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 89 | setprogname(*argv); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 90 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 91 | (void)setlocale(LC_ALL, ""); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 92 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 93 | myname = getprogname(); | 
|  | 94 | ischown = (myname[2] == 'o'); | 
|  | 95 | reference = NULL; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 96 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 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; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 140 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 141 | if (argc == 0 || (argc == 1 && reference == NULL)) | 
|  | 142 | usage(); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 143 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 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; | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 158 |  | 
| Elliott Hughes | 0b02467 | 2014-02-28 16:48:49 -0800 | [diff] [blame] | 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); | 
| The Android Open Source Project | dd7bc33 | 2009-03-03 19:32:55 -0800 | [diff] [blame] | 302 | } |