chmod.c revision 196711
11558Srgrimes/*- 298542Smckusick * Copyright (c) 1989, 1993, 1994 398542Smckusick * The Regents of the University of California. All rights reserved. 498542Smckusick * 598542Smckusick * Redistribution and use in source and binary forms, with or without 698542Smckusick * modification, are permitted provided that the following conditions 798542Smckusick * are met: 898542Smckusick * 1. Redistributions of source code must retain the above copyright 998542Smckusick * notice, this list of conditions and the following disclaimer. 1098542Smckusick * 2. Redistributions in binary form must reproduce the above copyright 1198542Smckusick * notice, this list of conditions and the following disclaimer in the 1298542Smckusick * documentation and/or other materials provided with the distribution. 1398542Smckusick * 4. Neither the name of the University nor the names of its contributors 141558Srgrimes * may be used to endorse or promote products derived from this software 151558Srgrimes * without specific prior written permission. 161558Srgrimes * 171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271558Srgrimes * SUCH DAMAGE. 281558Srgrimes */ 291558Srgrimes 301558Srgrimes#if 0 311558Srgrimes#ifndef lint 321558Srgrimesstatic char const copyright[] = 331558Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 341558Srgrimes The Regents of the University of California. All rights reserved.\n"; 351558Srgrimes#endif /* not lint */ 361558Srgrimes 371558Srgrimes#ifndef lint 381558Srgrimesstatic char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; 391558Srgrimes#endif /* not lint */ 401558Srgrimes#endif 411558Srgrimes#include <sys/cdefs.h> 421558Srgrimes__FBSDID("$FreeBSD: head/bin/chmod/chmod.c 196711 2009-08-31 20:42:07Z trasz $"); 431558Srgrimes 441558Srgrimes#include <sys/types.h> 451558Srgrimes#include <sys/param.h> 461558Srgrimes#include <sys/stat.h> 4736998Scharnier 481558Srgrimes#include <err.h> 491558Srgrimes#include <errno.h> 501558Srgrimes#include <fts.h> 511558Srgrimes#include <limits.h> 521558Srgrimes#include <stdio.h> 5336998Scharnier#include <stdlib.h> 5423673Speter#include <string.h> 5536998Scharnier#include <unistd.h> 5636998Scharnier 5750476Speterstatic void usage(void); 581558Srgrimesstatic int may_have_nfs4acl(const FTSENT *ent, int hflag); 591558Srgrimes 601558Srgrimesint 611558Srgrimesmain(int argc, char *argv[]) 6296478Sphk{ 631558Srgrimes FTS *ftsp; 6498542Smckusick FTSENT *p; 651558Srgrimes mode_t *set; 661558Srgrimes int Hflag, Lflag, Rflag, ch, fflag, fts_options, hflag, rval, error; 6723673Speter int vflag; 68105741Sjmallett char *mode; 691558Srgrimes mode_t newmode; 701558Srgrimes 7199826Sjmallett set = NULL; 721558Srgrimes Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; 731558Srgrimes while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1) 7423673Speter switch (ch) { 751558Srgrimes case 'H': 7699826Sjmallett Hflag = 1; 77109510Sjmallett Lflag = 0; 781558Srgrimes break; 7999826Sjmallett case 'L': 801558Srgrimes Lflag = 1; 8192839Simp Hflag = 0; 82109519Sjmallett break; 8392839Simp case 'P': 8492839Simp Hflag = Lflag = 0; 851558Srgrimes break; 861558Srgrimes case 'R': 8792839Simp Rflag = 1; 881558Srgrimes break; 89109507Sjmallett case 'f': 901558Srgrimes fflag = 1; 91109507Sjmallett break; 92109507Sjmallett case 'h': 93109507Sjmallett /* 94109507Sjmallett * In System V (and probably POSIX.2) the -h option 951558Srgrimes * causes chmod to change the mode of the symbolic 961558Srgrimes * link. 4.4BSD's symbolic links didn't have modes, 971558Srgrimes * so it was an undocumented noop. In FreeBSD 3.0, 981558Srgrimes * lchmod(2) is introduced and this option does real 991558Srgrimes * work. 1001558Srgrimes */ 101109507Sjmallett hflag = 1; 102109507Sjmallett break; 1031558Srgrimes /* 1041558Srgrimes * XXX 1051558Srgrimes * "-[rwx]" are valid mode commands. If they are the entire 1061558Srgrimes * argument, getopt has moved past them, so decrement optind. 10792839Simp * Regardless, we're done argument processing. 1081558Srgrimes */ 10998542Smckusick case 'g': case 'o': case 'r': case 's': 11098542Smckusick case 't': case 'u': case 'w': case 'X': case 'x': 11199827Sjmallett if (argv[optind - 1][0] == '-' && 1121558Srgrimes argv[optind - 1][1] == ch && 11399826Sjmallett argv[optind - 1][2] == '\0') 11498542Smckusick --optind; 11523673Speter goto done; 116101688Sjmallett case 'v': 117101688Sjmallett vflag++; 11898542Smckusick break; 11998542Smckusick case '?': 12098542Smckusick default: 12198542Smckusick usage(); 122107294Smckusick } 12398542Smckusickdone: argv += optind; 12498542Smckusick argc -= optind; 12598542Smckusick 126101688Sjmallett if (argc < 2) 127101688Sjmallett usage(); 12898542Smckusick 129107031Speter if (Rflag) { 13098542Smckusick fts_options = FTS_PHYSICAL; 131107031Speter if (hflag) 13298542Smckusick errx(1, 13398542Smckusick "the -R and -h options may not be specified together."); 13498542Smckusick if (Hflag) 135101688Sjmallett fts_options |= FTS_COMFOLLOW; 136101688Sjmallett if (Lflag) { 137101688Sjmallett fts_options &= ~FTS_PHYSICAL; 13898542Smckusick fts_options |= FTS_LOGICAL; 1391558Srgrimes } 1401558Srgrimes } else 1411558Srgrimes fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; 1421558Srgrimes 1431558Srgrimes mode = *argv; 1441558Srgrimes if ((set = setmode(mode)) == NULL) 14598542Smckusick errx(1, "invalid file mode: %s", mode); 1461558Srgrimes 14798542Smckusick if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) 148101688Sjmallett err(1, "fts_open"); 149101688Sjmallett for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 15098542Smckusick switch (p->fts_info) { 15198542Smckusick case FTS_D: /* Change it at FTS_DP. */ 15298542Smckusick if (!Rflag) 15398542Smckusick fts_set(ftsp, p, FTS_SKIP); 15498542Smckusick continue; 15598542Smckusick case FTS_DNR: /* Warn, chmod, continue. */ 15698542Smckusick warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 15798542Smckusick rval = 1; 15898542Smckusick break; 15998542Smckusick case FTS_ERR: /* Warn, continue. */ 16098542Smckusick case FTS_NS: 16198542Smckusick warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 162101688Sjmallett rval = 1; 163101688Sjmallett continue; 16498542Smckusick case FTS_SL: /* Ignore. */ 16598542Smckusick case FTS_SLNONE: 16698542Smckusick /* 16798542Smckusick * The only symlinks that end up here are ones that 16898542Smckusick * don't point to anything and ones that we found 16998542Smckusick * doing a physical walk. 17098542Smckusick */ 17198542Smckusick if (!hflag) 17298542Smckusick continue; 17398542Smckusick /* else */ 17498542Smckusick /* FALLTHROUGH */ 17598542Smckusick default: 17698542Smckusick break; 17798542Smckusick } 17898542Smckusick newmode = getmode(set, p->fts_statp->st_mode); 17998542Smckusick /* 18098542Smckusick * With NFSv4 ACLs, it is possible that applying a mode 18198542Smckusick * identical to the one computed from an ACL will change 18298542Smckusick * that ACL. 18398542Smckusick */ 18498542Smckusick if (may_have_nfs4acl(p, hflag) == 0 && 185101688Sjmallett (newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) 186101688Sjmallett continue; 187101688Sjmallett if (hflag) 18898542Smckusick error = lchmod(p->fts_accpath, newmode); 1891558Srgrimes else 1901558Srgrimes error = chmod(p->fts_accpath, newmode); 1912154Sdg if (error && !fflag) { 1922154Sdg warn("%s", p->fts_path); 19348875Smpp rval = 1; 19448875Smpp } else { 19548875Smpp if (vflag) { 19648875Smpp (void)printf("%s", p->fts_path); 19748875Smpp 19848875Smpp if (vflag > 1) { 19948875Smpp char m1[12], m2[12]; 20098542Smckusick 20198542Smckusick strmode(p->fts_statp->st_mode, m1); 20298542Smckusick strmode((p->fts_statp->st_mode & 20398542Smckusick S_IFMT) | newmode, m2); 20498542Smckusick 20598542Smckusick (void)printf(": 0%o [%s] -> 0%o [%s]", 20698542Smckusick p->fts_statp->st_mode, m1, 20798542Smckusick (p->fts_statp->st_mode & S_IFMT) | 20898542Smckusick newmode, m2); 20948875Smpp } 2101558Srgrimes (void)printf("\n"); 21171073Siedowse } 212101688Sjmallett 21398542Smckusick } 2141558Srgrimes } 2151558Srgrimes if (errno) 2161558Srgrimes err(1, "fts_read"); 2171558Srgrimes free(set); 2181558Srgrimes exit(rval); 2191558Srgrimes} 2201558Srgrimes 2211558Srgrimesstatic void 22298542Smckusickusage(void) 223101688Sjmallett{ 22498542Smckusick (void)fprintf(stderr, 22598542Smckusick "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); 22698542Smckusick exit(1); 22798542Smckusick} 22898542Smckusick 2291558Srgrimesstatic int 230109519Sjmallettmay_have_nfs4acl(const FTSENT *ent, int hflag) 231109519Sjmallett{ 2321558Srgrimes int ret; 233109519Sjmallett static dev_t previous_dev = NODEV; 23499826Sjmallett static int supports_acls = -1; 2351558Srgrimes 2361558Srgrimes if (previous_dev != ent->fts_statp->st_dev) { 237109461Sjmallett previous_dev = ent->fts_statp->st_dev; 238109461Sjmallett supports_acls = 0; 239109461Sjmallett 240109461Sjmallett if (hflag) 241105738Sjmallett ret = lpathconf(ent->fts_accpath, _PC_ACL_NFS4); 2421558Srgrimes else 24399827Sjmallett ret = pathconf(ent->fts_accpath, _PC_ACL_NFS4); 2441558Srgrimes if (ret > 0) 2451558Srgrimes supports_acls = 1; 246109519Sjmallett else if (ret < 0 && errno != EINVAL) 2471558Srgrimes warn("%s", ent->fts_path); 24898542Smckusick } 2491558Srgrimes 2501558Srgrimes return (supports_acls); 2511558Srgrimes} 252109519Sjmallett