164562Sgshapiro/* $NetBSD: chown.c,v 1.4 2011/01/13 22:28:36 haad Exp $ */ 2157001Sgshapiro 364562Sgshapiro/* 464562Sgshapiro * Copyright (c) 1988, 1993, 1994, 2003 564562Sgshapiro * The Regents of the University of California. All rights reserved. 664562Sgshapiro * 764562Sgshapiro * Redistribution and use in source and binary forms, with or without 864562Sgshapiro * modification, are permitted provided that the following conditions 964562Sgshapiro * are met: 1064562Sgshapiro * 1. Redistributions of source code must retain the above copyright 1164562Sgshapiro * notice, this list of conditions and the following disclaimer. 1264562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 1364562Sgshapiro * notice, this list of conditions and the following disclaimer in the 1464562Sgshapiro * documentation and/or other materials provided with the distribution. 1564562Sgshapiro * 3. Neither the name of the University nor the names of its contributors 1690792Sgshapiro * may be used to endorse or promote products derived from this software 1790792Sgshapiro * without specific prior written permission. 1890792Sgshapiro * 1964562Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2064562Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2164562Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22203004Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2364562Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2464562Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2564562Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2664562Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2764562Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2864562Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29168515Sgshapiro * SUCH DAMAGE. 30168515Sgshapiro */ 3164562Sgshapiro 3264562Sgshapiro#include <sys/cdefs.h> 3364562Sgshapiro#ifndef lint 3464562Sgshapiro__COPYRIGHT("@(#) Copyright (c) 1988, 1993, 1994, 2003\ 35168515Sgshapiro The Regents of the University of California. All rights reserved."); 36168515Sgshapiro#endif /* not lint */ 37168515Sgshapiro 38168515Sgshapiro#ifndef lint 39168515Sgshapiro#if 0 40168515Sgshapirostatic char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; 41168515Sgshapiro#else 42168515Sgshapiro__RCSID("$NetBSD: chown.c,v 1.4 2011/01/13 22:28:36 haad Exp $"); 4364562Sgshapiro#endif 4464562Sgshapiro#endif /* not lint */ 45168515Sgshapiro 46168515Sgshapiro#include <sys/types.h> 47168515Sgshapiro#include <sys/stat.h> 48168515Sgshapiro 49168515Sgshapiro#include <ctype.h> 50168515Sgshapiro#include <dirent.h> 51168515Sgshapiro#include <err.h> 52168515Sgshapiro#include <errno.h> 53168515Sgshapiro#include <locale.h> 54168515Sgshapiro#include <fts.h> 55168515Sgshapiro#include <grp.h> 56168515Sgshapiro#include <pwd.h> 57168515Sgshapiro#include <stdio.h> 58168515Sgshapiro#include <stdlib.h> 59168515Sgshapiro#include <string.h> 60168515Sgshapiro#include <unistd.h> 61168515Sgshapiro 62168515Sgshapirostatic void a_gid(const char *); 63168515Sgshapirostatic void a_uid(const char *); 64168515Sgshapirostatic id_t id(const char *, const char *); 65168515Sgshapiro__dead static void usage(void); 66168515Sgshapiro 67168515Sgshapirostatic uid_t uid; 68168515Sgshapirostatic gid_t gid; 69168515Sgshapirostatic int ischown; 70168515Sgshapirostatic char *myname; 71168515Sgshapiro 72168515Sgshapiroint 73168515Sgshapiromain(int argc, char **argv) 74168515Sgshapiro{ 75168515Sgshapiro FTS *ftsp; 76168515Sgshapiro FTSENT *p; 77168515Sgshapiro int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, vflag; 78168515Sgshapiro char *cp; 79168515Sgshapiro int (*change_owner)(const char *, uid_t, gid_t); 80168515Sgshapiro 81168515Sgshapiro (void)setlocale(LC_ALL, ""); 82168515Sgshapiro 83168515Sgshapiro myname = (cp = strrchr(*argv, '/')) ? cp + 1 : *argv; 84168515Sgshapiro ischown = (myname[2] == 'o'); 85168515Sgshapiro 86168515Sgshapiro Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; 87168515Sgshapiro while ((ch = getopt(argc, argv, "HLPRfhv")) != -1) 88168515Sgshapiro switch (ch) { 89168515Sgshapiro case 'H': 90168515Sgshapiro Hflag = 1; 91168515Sgshapiro Lflag = 0; 92168515Sgshapiro break; 93168515Sgshapiro case 'L': 94168515Sgshapiro Lflag = 1; 95168515Sgshapiro Hflag = 0; 96168515Sgshapiro break; 97168515Sgshapiro case 'P': 98168515Sgshapiro Hflag = Lflag = 0; 99168515Sgshapiro break; 100168515Sgshapiro case 'R': 101168515Sgshapiro Rflag = 1; 102168515Sgshapiro break; 103168515Sgshapiro case 'f': 104168515Sgshapiro fflag = 1; 105168515Sgshapiro break; 106168515Sgshapiro case 'h': 107168515Sgshapiro /* 108168515Sgshapiro * In System V the -h option causes chown/chgrp to 109168515Sgshapiro * change the owner/group of the symbolic link. 110168515Sgshapiro * 4.4BSD's symbolic links didn't have owners/groups, 111168515Sgshapiro * so it was an undocumented noop. 112168515Sgshapiro * In NetBSD 1.3, lchown(2) is introduced. 113168515Sgshapiro */ 114168515Sgshapiro hflag = 1; 115168515Sgshapiro break; 116168515Sgshapiro case 'v': 117168515Sgshapiro vflag = 1; 118168515Sgshapiro break; 119168515Sgshapiro case '?': 120168515Sgshapiro default: 121168515Sgshapiro usage(); 122168515Sgshapiro } 123168515Sgshapiro argv += optind; 12464562Sgshapiro argc -= optind; 12590792Sgshapiro 12690792Sgshapiro if (argc < 2) 12790792Sgshapiro usage(); 12890792Sgshapiro 12990792Sgshapiro fts_options = FTS_PHYSICAL; 13064562Sgshapiro if (Rflag) { 13164562Sgshapiro if (Hflag) 13264562Sgshapiro fts_options |= FTS_COMFOLLOW; 13364562Sgshapiro if (Lflag) { 13471345Sgshapiro if (hflag) 13571345Sgshapiro errx(EXIT_FAILURE, 13671345Sgshapiro "the -L and -h options " 13771345Sgshapiro "may not be specified together."); 13871345Sgshapiro fts_options &= ~FTS_PHYSICAL; 13971345Sgshapiro fts_options |= FTS_LOGICAL; 14071345Sgshapiro } 141168515Sgshapiro } else if (!hflag) 142168515Sgshapiro fts_options |= FTS_COMFOLLOW; 143168515Sgshapiro 144168515Sgshapiro uid = (uid_t)-1; 145168515Sgshapiro gid = (gid_t)-1; 146168515Sgshapiro if (ischown) { 147168515Sgshapiro if ((cp = strchr(*argv, ':')) != NULL) { 148168515Sgshapiro *cp++ = '\0'; 149168515Sgshapiro a_gid(cp); 150168515Sgshapiro } 151168515Sgshapiro#ifdef SUPPORT_DOT 152168515Sgshapiro else if ((cp = strrchr(*argv, '.')) != NULL) { 153168515Sgshapiro if (uid_from_user(*argv, &uid) == -1) { 154168515Sgshapiro *cp++ = '\0'; 155168515Sgshapiro a_gid(cp); 156168515Sgshapiro } 157168515Sgshapiro } 158168515Sgshapiro#endif 159168515Sgshapiro a_uid(*argv); 160168515Sgshapiro } else 161168515Sgshapiro a_gid(*argv); 162168515Sgshapiro 163168515Sgshapiro if ((ftsp = fts_open(++argv, fts_options, NULL)) == NULL) 164168515Sgshapiro err(EXIT_FAILURE, "fts_open"); 165168515Sgshapiro 166168515Sgshapiro for (rval = EXIT_SUCCESS; (p = fts_read(ftsp)) != NULL;) { 167168515Sgshapiro change_owner = chown; 168168515Sgshapiro switch (p->fts_info) { 169132943Sgshapiro case FTS_D: 170111823Sgshapiro if (!Rflag) /* Change it at FTS_DP. */ 171111823Sgshapiro fts_set(ftsp, p, FTS_SKIP); 172111823Sgshapiro continue; 173111823Sgshapiro case FTS_DNR: /* Warn, chown, continue. */ 174111823Sgshapiro warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 175111823Sgshapiro rval = EXIT_FAILURE; 176111823Sgshapiro break; 177111823Sgshapiro case FTS_ERR: /* Warn, continue. */ 178111823Sgshapiro case FTS_NS: 179111823Sgshapiro warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 180111823Sgshapiro rval = EXIT_FAILURE; 181111823Sgshapiro continue; 182111823Sgshapiro case FTS_SL: /* Ignore unless -h. */ 183111823Sgshapiro /* 184111823Sgshapiro * All symlinks we found while doing a physical 185111823Sgshapiro * walk end up here. 186111823Sgshapiro */ 187111823Sgshapiro if (!hflag) 188111823Sgshapiro continue; 189111823Sgshapiro /* 190111823Sgshapiro * Note that if we follow a symlink, fts_info is 191111823Sgshapiro * not FTS_SL but FTS_F or whatever. And we should 192111823Sgshapiro * use lchown only for FTS_SL and should use chown 193111823Sgshapiro * for others. 194111823Sgshapiro */ 195111823Sgshapiro change_owner = lchown; 196111823Sgshapiro break; 197111823Sgshapiro case FTS_SLNONE: /* Ignore. */ 198111823Sgshapiro /* 199111823Sgshapiro * The only symlinks that end up here are ones that 200111823Sgshapiro * don't point to anything. Note that if we are 201111823Sgshapiro * doing a phisycal walk, we never reach here unless 202111823Sgshapiro * we asked to follow explicitly. 203111823Sgshapiro */ 204111823Sgshapiro continue; 205111823Sgshapiro default: 206132943Sgshapiro break; 207111823Sgshapiro } 208111823Sgshapiro 209111823Sgshapiro if ((*change_owner)(p->fts_accpath, uid, gid) && !fflag) { 210111823Sgshapiro warn("%s", p->fts_path); 211111823Sgshapiro rval = EXIT_FAILURE; 212111823Sgshapiro } else { 213111823Sgshapiro if (vflag) 214111823Sgshapiro printf("%s\n", p->fts_path); 215111823Sgshapiro } 216111823Sgshapiro } 217111823Sgshapiro if (errno) 218111823Sgshapiro err(EXIT_FAILURE, "fts_read"); 219111823Sgshapiro exit(rval); 220111823Sgshapiro /* NOTREACHED */ 221111823Sgshapiro} 222157001Sgshapiro 223111823Sgshapirostatic void 224111823Sgshapiroa_gid(const char *s) 225111823Sgshapiro{ 226111823Sgshapiro struct group *gr; 227111823Sgshapiro 228111823Sgshapiro if (*s == '\0') /* Argument was "uid[:.]". */ 229111823Sgshapiro return; 230111823Sgshapiro gr = *s == '#' ? NULL : getgrnam(s); 231111823Sgshapiro if (gr == NULL) 232111823Sgshapiro gid = id(s, "group"); 233132943Sgshapiro else 234111823Sgshapiro gid = gr->gr_gid; 23564562Sgshapiro return; 23664562Sgshapiro} 23764562Sgshapiro 23877349Sgshapirostatic void 23964562Sgshapiroa_uid(const char *s) 24064562Sgshapiro{ 24190792Sgshapiro if (*s == '\0') /* Argument was "[:.]gid". */ 24290792Sgshapiro return; 24390792Sgshapiro if (*s == '#' || uid_from_user(s, &uid) == -1) { 24490792Sgshapiro uid = id(s, "user"); 24590792Sgshapiro } 24690792Sgshapiro return; 24790792Sgshapiro} 24866494Sgshapiro 24964562Sgshapirostatic id_t 25064562Sgshapiroid(const char *name, const char *type) 25164562Sgshapiro{ 25277349Sgshapiro id_t val; 25390792Sgshapiro char *ep; 25464562Sgshapiro 25564562Sgshapiro errno = 0; 25664562Sgshapiro if (*name == '#') 25764562Sgshapiro name++; 25864562Sgshapiro val = (id_t)strtoul(name, &ep, 10); 25964562Sgshapiro if (errno) 26064562Sgshapiro err(EXIT_FAILURE, "%s", name); 26164562Sgshapiro if (*ep != '\0') 26264562Sgshapiro errx(EXIT_FAILURE, "%s: invalid %s name", name, type); 26364562Sgshapiro return (val); 26464562Sgshapiro} 26564562Sgshapiro 26664562Sgshapirostatic void 267120256Sgshapirousage(void) 26864562Sgshapiro{ 26964562Sgshapiro 27064562Sgshapiro (void)fprintf(stderr, 27164562Sgshapiro "usage: %s [-R [-H | -L | -P]] [-fhv] %s file ...\n", 27264562Sgshapiro myname, ischown ? "[owner][:group]" : "group"); 27364562Sgshapiro exit(EXIT_FAILURE); 27464562Sgshapiro} 27564562Sgshapiro