chown.c revision 208815
11553Srgrimes/* 21553Srgrimes * Copyright (c) 1988, 1993, 1994 31553Srgrimes * The Regents of the University of California. All rights reserved. 41553Srgrimes * 51553Srgrimes * Redistribution and use in source and binary forms, with or without 61553Srgrimes * modification, are permitted provided that the following conditions 71553Srgrimes * are met: 81553Srgrimes * 1. Redistributions of source code must retain the above copyright 91553Srgrimes * notice, this list of conditions and the following disclaimer. 101553Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111553Srgrimes * notice, this list of conditions and the following disclaimer in the 121553Srgrimes * documentation and/or other materials provided with the distribution. 131553Srgrimes * 4. Neither the name of the University nor the names of its contributors 141553Srgrimes * may be used to endorse or promote products derived from this software 151553Srgrimes * without specific prior written permission. 161553Srgrimes * 171553Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181553Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191553Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201553Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211553Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221553Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231553Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241553Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251553Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261553Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271553Srgrimes * SUCH DAMAGE. 281553Srgrimes */ 291553Srgrimes 30114601Sobrien#if 0 311553Srgrimes#ifndef lint 3228643Sstevestatic const char copyright[] = 331553Srgrimes"@(#) Copyright (c) 1988, 1993, 1994\n\ 341553Srgrimes The Regents of the University of California. All rights reserved.\n"; 351553Srgrimes#endif /* not lint */ 361553Srgrimes 371553Srgrimes#ifndef lint 381553Srgrimesstatic char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94"; 39114601Sobrien#endif /* not lint */ 4028643Ssteve#endif 41119856Scharnier 4299141Sjmallett#include <sys/cdefs.h> 4399141Sjmallett__FBSDID("$FreeBSD: head/usr.sbin/chown/chown.c 208815 2010-06-05 08:11:11Z trasz $"); 4499141Sjmallett 451553Srgrimes#include <sys/param.h> 461553Srgrimes#include <sys/stat.h> 471553Srgrimes 481553Srgrimes#include <err.h> 491553Srgrimes#include <errno.h> 501553Srgrimes#include <fts.h> 511553Srgrimes#include <grp.h> 52108443Sobrien#include <libgen.h> 531553Srgrimes#include <pwd.h> 54114005Sjohan#include <stdint.h> 551553Srgrimes#include <stdio.h> 561553Srgrimes#include <stdlib.h> 571553Srgrimes#include <string.h> 581553Srgrimes#include <unistd.h> 591553Srgrimes 6099141Sjmallettvoid a_gid(const char *); 6199141Sjmallettvoid a_uid(const char *); 6299141Sjmallettvoid chownerr(const char *); 63119856Scharnieruid_t id(const char *, const char *); 6499141Sjmallettvoid usage(void); 651553Srgrimes 661553Srgrimesuid_t uid; 671553Srgrimesgid_t gid; 6883410Sruint ischown; 6983410Sruconst char *gname; 701553Srgrimes 711553Srgrimesint 7299141Sjmallettmain(int argc, char **argv) 731553Srgrimes{ 741553Srgrimes FTS *ftsp; 751553Srgrimes FTSENT *p; 76204165Sgavin int Hflag, Lflag, Rflag, fflag, hflag, vflag, xflag; 7783410Sru int ch, fts_options, rval; 781553Srgrimes char *cp; 798857Srgrimes 80108443Sobrien ischown = (strcmp(basename(argv[0]), "chown") == 0); 818857Srgrimes 82204165Sgavin Hflag = Lflag = Rflag = fflag = hflag = vflag = xflag = 0; 83204165Sgavin while ((ch = getopt(argc, argv, "HLPRfhvx")) != -1) 841553Srgrimes switch (ch) { 851553Srgrimes case 'H': 861553Srgrimes Hflag = 1; 8783410Sru Lflag = 0; 881553Srgrimes break; 891553Srgrimes case 'L': 901553Srgrimes Lflag = 1; 9183410Sru Hflag = 0; 921553Srgrimes break; 931553Srgrimes case 'P': 941553Srgrimes Hflag = Lflag = 0; 951553Srgrimes break; 961553Srgrimes case 'R': 971553Srgrimes Rflag = 1; 981553Srgrimes break; 991553Srgrimes case 'f': 1001553Srgrimes fflag = 1; 1011553Srgrimes break; 1021553Srgrimes case 'h': 1031553Srgrimes hflag = 1; 1041553Srgrimes break; 10557830Sobrien case 'v': 106114005Sjohan vflag++; 10757830Sobrien break; 108204165Sgavin case 'x': 109204165Sgavin xflag = 1; 110204165Sgavin break; 1111553Srgrimes case '?': 1121553Srgrimes default: 1131553Srgrimes usage(); 1141553Srgrimes } 1151553Srgrimes argv += optind; 1161553Srgrimes argc -= optind; 1171553Srgrimes 1181553Srgrimes if (argc < 2) 1191553Srgrimes usage(); 1201553Srgrimes 1211553Srgrimes if (Rflag) { 12277333Sru fts_options = FTS_PHYSICAL; 12383410Sru if (hflag && (Hflag || Lflag)) 12483410Sru errx(1, "the -R%c and -h options may not be " 12583410Sru "specified together", Hflag ? 'H' : 'L'); 1261553Srgrimes if (Hflag) 1271553Srgrimes fts_options |= FTS_COMFOLLOW; 12883410Sru else if (Lflag) { 1291553Srgrimes fts_options &= ~FTS_PHYSICAL; 1301553Srgrimes fts_options |= FTS_LOGICAL; 1311553Srgrimes } 13277333Sru } else 13377522Sru fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; 134204165Sgavin if (xflag) 135204165Sgavin fts_options |= FTS_XDEV; 1361553Srgrimes 13783410Sru uid = (uid_t)-1; 13883410Sru gid = (gid_t)-1; 1391553Srgrimes if (ischown) { 14029656Swosch if ((cp = strchr(*argv, ':')) != NULL) { 1411553Srgrimes *cp++ = '\0'; 1421553Srgrimes a_gid(cp); 14329656Swosch } 14429656Swosch#ifdef SUPPORT_DOT 14529656Swosch else if ((cp = strchr(*argv, '.')) != NULL) { 146100252Sdwmalone warnx("separation of user and group with a period is deprecated"); 1471553Srgrimes *cp++ = '\0'; 1481553Srgrimes a_gid(cp); 1498857Srgrimes } 15029656Swosch#endif 1511553Srgrimes a_uid(*argv); 1528857Srgrimes } else 1531553Srgrimes a_gid(*argv); 1541553Srgrimes 1551553Srgrimes if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) 1561553Srgrimes err(1, NULL); 1571553Srgrimes 1581553Srgrimes for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 1591553Srgrimes switch (p->fts_info) { 16083410Sru case FTS_D: /* Change it at FTS_DP. */ 16117597Sadam if (!Rflag) 16217597Sadam fts_set(ftsp, p, FTS_SKIP); 16317597Sadam continue; 16483410Sru case FTS_DNR: /* Warn, chown. */ 1651553Srgrimes warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 1661553Srgrimes rval = 1; 1671553Srgrimes break; 1681553Srgrimes case FTS_ERR: /* Warn, continue. */ 1691553Srgrimes case FTS_NS: 1701553Srgrimes warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 1711553Srgrimes rval = 1; 1721553Srgrimes continue; 17383410Sru case FTS_SL: 1741553Srgrimes case FTS_SLNONE: 1751553Srgrimes /* 1761553Srgrimes * The only symlinks that end up here are ones that 1771553Srgrimes * don't point to anything and ones that we found 1781553Srgrimes * doing a physical walk. 1791553Srgrimes */ 18024446Speter if (hflag) 18124446Speter break; 18224446Speter else 18324446Speter continue; 1841553Srgrimes default: 1851553Srgrimes break; 1861553Srgrimes } 18783410Sru if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) && 18883410Sru (gid == (gid_t)-1 || gid == p->fts_statp->st_gid)) 18964014Speter continue; 19083410Sru if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) { 19183410Sru if (!fflag) { 19224446Speter chownerr(p->fts_path); 19324446Speter rval = 1; 19424446Speter } 19524446Speter } else { 196114005Sjohan if (vflag) { 197114005Sjohan printf("%s", p->fts_path); 198114005Sjohan if (vflag > 1) { 199114005Sjohan if (ischown) { 200114005Sjohan printf(": %ju:%ju -> %ju:%ju", 201114005Sjohan (uintmax_t) 202114005Sjohan p->fts_statp->st_uid, 203114005Sjohan (uintmax_t) 204114005Sjohan p->fts_statp->st_gid, 205114005Sjohan (uid == (uid_t)-1) ? 206114005Sjohan (uintmax_t) 207114005Sjohan p->fts_statp->st_uid : 208114005Sjohan (uintmax_t)uid, 209114005Sjohan (gid == (gid_t)-1) ? 210114005Sjohan (uintmax_t) 211114005Sjohan p->fts_statp->st_gid : 212114005Sjohan (uintmax_t)gid); 213114005Sjohan } else { 214114005Sjohan printf(": %ju -> %ju", 215114005Sjohan (uintmax_t) 216114005Sjohan p->fts_statp->st_gid, 217114005Sjohan (gid == (gid_t)-1) ? 218114005Sjohan (uintmax_t) 219114005Sjohan p->fts_statp->st_gid : 220114005Sjohan (uintmax_t)gid); 221114005Sjohan } 222114005Sjohan } 223114005Sjohan printf("\n"); 224114005Sjohan } 2251553Srgrimes } 2261553Srgrimes } 2271553Srgrimes if (errno) 2281553Srgrimes err(1, "fts_read"); 2291553Srgrimes exit(rval); 2301553Srgrimes} 2311553Srgrimes 2321553Srgrimesvoid 23399141Sjmalletta_gid(const char *s) 2341553Srgrimes{ 2351553Srgrimes struct group *gr; 2361553Srgrimes 2371553Srgrimes if (*s == '\0') /* Argument was "uid[:.]". */ 2381553Srgrimes return; 2391553Srgrimes gname = s; 24083410Sru gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group"); 2411553Srgrimes} 2421553Srgrimes 2431553Srgrimesvoid 24499141Sjmalletta_uid(const char *s) 2451553Srgrimes{ 2461553Srgrimes struct passwd *pw; 2471553Srgrimes 2481553Srgrimes if (*s == '\0') /* Argument was "[:.]gid". */ 2491553Srgrimes return; 25083410Sru uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user"); 2511553Srgrimes} 2521553Srgrimes 253119856Scharnieruid_t 25499141Sjmallettid(const char *name, const char *type) 2551553Srgrimes{ 256119856Scharnier uid_t val; 2571553Srgrimes char *ep; 2581553Srgrimes 2591553Srgrimes /* 2601553Srgrimes * XXX 2611553Srgrimes * We know that uid_t's and gid_t's are unsigned longs. 2621553Srgrimes */ 2631553Srgrimes errno = 0; 2641553Srgrimes val = strtoul(name, &ep, 10); 265208792Strasz if (errno || *ep != '\0') 2661553Srgrimes errx(1, "%s: illegal %s name", name, type); 2671553Srgrimes return (val); 2681553Srgrimes} 2691553Srgrimes 2701553Srgrimesvoid 27199141Sjmallettchownerr(const char *file) 2721553Srgrimes{ 27383410Sru static uid_t euid = -1; 27483410Sru static int ngroups = -1; 275194494Sbrooks static long ngroups_max; 276194494Sbrooks gid_t *groups; 2771553Srgrimes 2781553Srgrimes /* Check for chown without being root. */ 27997732Stjr if (errno != EPERM || (uid != (uid_t)-1 && 28097732Stjr euid == (uid_t)-1 && (euid = geteuid()) != 0)) { 28197732Stjr warn("%s", file); 28297732Stjr return; 28397732Stjr } 2841553Srgrimes 2851553Srgrimes /* Check group membership; kernel just returns EPERM. */ 28683410Sru if (gid != (gid_t)-1 && ngroups == -1 && 28783410Sru euid == (uid_t)-1 && (euid = geteuid()) != 0) { 288194494Sbrooks ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; 289194494Sbrooks if ((groups = malloc(sizeof(gid_t) * ngroups_max)) == NULL) 290194494Sbrooks err(1, "malloc"); 291194494Sbrooks ngroups = getgroups(ngroups_max, groups); 2921553Srgrimes while (--ngroups >= 0 && gid != groups[ngroups]); 293208815Strasz free(groups); 29497732Stjr if (ngroups < 0) { 29597732Stjr warnx("you are not a member of group %s", gname); 29697732Stjr return; 29797732Stjr } 2981553Srgrimes } 29928643Ssteve warn("%s", file); 3001553Srgrimes} 3011553Srgrimes 3021553Srgrimesvoid 30399141Sjmallettusage(void) 3041553Srgrimes{ 30583410Sru 30683410Sru if (ischown) 30783410Sru (void)fprintf(stderr, "%s\n%s\n", 308204165Sgavin "usage: chown [-fhvx] [-R [-H | -L | -P]] owner[:group]" 30983410Sru " file ...", 310204165Sgavin " chown [-fhvx] [-R [-H | -L | -P]] :group file ..."); 31183410Sru else 31283410Sru (void)fprintf(stderr, "%s\n", 313204165Sgavin "usage: chgrp [-fhvx] [-R [-H | -L | -P]] group file ..."); 3141553Srgrimes exit(1); 3151553Srgrimes} 316