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