1139969Simp/*- 21556Srgrimes * Copyright (c) 1989, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * Redistribution and use in source and binary forms, with or without 61556Srgrimes * modification, are permitted provided that the following conditions 71556Srgrimes * are met: 81556Srgrimes * 1. Redistributions of source code must retain the above copyright 91556Srgrimes * notice, this list of conditions and the following disclaimer. 101556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111556Srgrimes * notice, this list of conditions and the following disclaimer in the 121556Srgrimes * documentation and/or other materials provided with the distribution. 131556Srgrimes * 4. Neither the name of the University nor the names of its contributors 141556Srgrimes * may be used to endorse or promote products derived from this software 151556Srgrimes * without specific prior written permission. 161556Srgrimes * 171556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271556Srgrimes * SUCH DAMAGE. 281556Srgrimes */ 291556Srgrimes 30114433Sobrien#if 0 311556Srgrimes#ifndef lint 3220411Sstevestatic char const copyright[] = 331556Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 341556Srgrimes The Regents of the University of California. All rights reserved.\n"; 351556Srgrimes#endif /* not lint */ 361556Srgrimes 371556Srgrimes#ifndef lint 3836002Scharnierstatic char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; 39114433Sobrien#endif /* not lint */ 4035773Scharnier#endif 4199109Sobrien#include <sys/cdefs.h> 4299109Sobrien__FBSDID("$FreeBSD$"); 431556Srgrimes 44196711Strasz#include <sys/param.h> 451556Srgrimes#include <sys/stat.h> 461556Srgrimes 471556Srgrimes#include <err.h> 481556Srgrimes#include <errno.h> 491556Srgrimes#include <fts.h> 506170Sbde#include <limits.h> 511556Srgrimes#include <stdio.h> 521556Srgrimes#include <stdlib.h> 531556Srgrimes#include <string.h> 541556Srgrimes#include <unistd.h> 551556Srgrimes 56194795Sdelphijstatic void usage(void); 57196711Straszstatic int may_have_nfs4acl(const FTSENT *ent, int hflag); 581556Srgrimes 591556Srgrimesint 6090107Simpmain(int argc, char *argv[]) 611556Srgrimes{ 621556Srgrimes FTS *ftsp; 631556Srgrimes FTSENT *p; 641556Srgrimes mode_t *set; 65196753Strasz int Hflag, Lflag, Rflag, ch, error, fflag, fts_options, hflag, rval; 6653780Sobrien int vflag; 67121794Stobez char *mode; 68121794Stobez mode_t newmode; 691556Srgrimes 707165Sjoerg set = NULL; 7191078Smarkm Hflag = Lflag = Rflag = fflag = hflag = vflag = 0; 7277342Sru while ((ch = getopt(argc, argv, "HLPRXfghorstuvwx")) != -1) 731556Srgrimes switch (ch) { 741556Srgrimes case 'H': 751556Srgrimes Hflag = 1; 7691078Smarkm Lflag = 0; 771556Srgrimes break; 781556Srgrimes case 'L': 791556Srgrimes Lflag = 1; 8091078Smarkm Hflag = 0; 811556Srgrimes break; 821556Srgrimes case 'P': 831556Srgrimes Hflag = Lflag = 0; 841556Srgrimes break; 851556Srgrimes case 'R': 861556Srgrimes Rflag = 1; 871556Srgrimes break; 8849544Schris case 'f': 891556Srgrimes fflag = 1; 901556Srgrimes break; 911556Srgrimes case 'h': 921556Srgrimes /* 931556Srgrimes * In System V (and probably POSIX.2) the -h option 941556Srgrimes * causes chmod to change the mode of the symbolic 9577342Sru * link. 4.4BSD's symbolic links didn't have modes, 9677342Sru * so it was an undocumented noop. In FreeBSD 3.0, 9777342Sru * lchmod(2) is introduced and this option does real 9877342Sru * work. 991556Srgrimes */ 1001556Srgrimes hflag = 1; 1011556Srgrimes break; 1021556Srgrimes /* 1031556Srgrimes * XXX 1041556Srgrimes * "-[rwx]" are valid mode commands. If they are the entire 1051556Srgrimes * argument, getopt has moved past them, so decrement optind. 1061556Srgrimes * Regardless, we're done argument processing. 1071556Srgrimes */ 1081556Srgrimes case 'g': case 'o': case 'r': case 's': 1091556Srgrimes case 't': case 'u': case 'w': case 'X': case 'x': 1101556Srgrimes if (argv[optind - 1][0] == '-' && 1111556Srgrimes argv[optind - 1][1] == ch && 1121556Srgrimes argv[optind - 1][2] == '\0') 1131556Srgrimes --optind; 1141556Srgrimes goto done; 11553780Sobrien case 'v': 116101297Sobrien vflag++; 11753780Sobrien break; 1181556Srgrimes case '?': 1191556Srgrimes default: 1201556Srgrimes usage(); 1211556Srgrimes } 1221556Srgrimesdone: argv += optind; 1231556Srgrimes argc -= optind; 1241556Srgrimes 1251556Srgrimes if (argc < 2) 1261556Srgrimes usage(); 1271556Srgrimes 1281556Srgrimes if (Rflag) { 12977333Sru fts_options = FTS_PHYSICAL; 1301556Srgrimes if (hflag) 1311556Srgrimes errx(1, 1321556Srgrimes "the -R and -h options may not be specified together."); 1331556Srgrimes if (Hflag) 1341556Srgrimes fts_options |= FTS_COMFOLLOW; 1351556Srgrimes if (Lflag) { 1361556Srgrimes fts_options &= ~FTS_PHYSICAL; 1371556Srgrimes fts_options |= FTS_LOGICAL; 1381556Srgrimes } 13977333Sru } else 14077522Sru fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL; 1411556Srgrimes 1421556Srgrimes mode = *argv; 143121794Stobez if ((set = setmode(mode)) == NULL) 144121794Stobez errx(1, "invalid file mode: %s", mode); 1451556Srgrimes 1461556Srgrimes if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL) 14799743Sdillon err(1, "fts_open"); 1481556Srgrimes for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 1491556Srgrimes switch (p->fts_info) { 15017496Sadam case FTS_D: /* Change it at FTS_DP. */ 15117496Sadam if (!Rflag) 15217496Sadam fts_set(ftsp, p, FTS_SKIP); 15317496Sadam continue; 1541556Srgrimes case FTS_DNR: /* Warn, chmod, continue. */ 1551556Srgrimes warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 1561556Srgrimes rval = 1; 1571556Srgrimes break; 1581556Srgrimes case FTS_ERR: /* Warn, continue. */ 1591556Srgrimes case FTS_NS: 1601556Srgrimes warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 1611556Srgrimes rval = 1; 1621556Srgrimes continue; 1631556Srgrimes case FTS_SL: /* Ignore. */ 1641556Srgrimes case FTS_SLNONE: 1651556Srgrimes /* 1661556Srgrimes * The only symlinks that end up here are ones that 1671556Srgrimes * don't point to anything and ones that we found 1681556Srgrimes * doing a physical walk. 1691556Srgrimes */ 17077342Sru if (!hflag) 17177342Sru continue; 17277342Sru /* FALLTHROUGH */ 1731556Srgrimes default: 1741556Srgrimes break; 1751556Srgrimes } 176121794Stobez newmode = getmode(set, p->fts_statp->st_mode); 177195243Strasz /* 178195243Strasz * With NFSv4 ACLs, it is possible that applying a mode 179195243Strasz * identical to the one computed from an ACL will change 180195243Strasz * that ACL. 181195243Strasz */ 182196711Strasz if (may_have_nfs4acl(p, hflag) == 0 && 183195243Strasz (newmode & ALLPERMS) == (p->fts_statp->st_mode & ALLPERMS)) 184195243Strasz continue; 185196711Strasz if (hflag) 186196711Strasz error = lchmod(p->fts_accpath, newmode); 187196711Strasz else 188196711Strasz error = chmod(p->fts_accpath, newmode); 189196753Strasz if (error) { 190196753Strasz if (!fflag) { 191196753Strasz warn("%s", p->fts_path); 192196753Strasz rval = 1; 193196753Strasz } 19453780Sobrien } else { 195101297Sobrien if (vflag) { 196123565Sru (void)printf("%s", p->fts_path); 197101297Sobrien 198101297Sobrien if (vflag > 1) { 199101297Sobrien char m1[12], m2[12]; 200101297Sobrien 201101297Sobrien strmode(p->fts_statp->st_mode, m1); 202101297Sobrien strmode((p->fts_statp->st_mode & 203101297Sobrien S_IFMT) | newmode, m2); 204101297Sobrien (void)printf(": 0%o [%s] -> 0%o [%s]", 205101297Sobrien p->fts_statp->st_mode, m1, 206101297Sobrien (p->fts_statp->st_mode & S_IFMT) | 207101297Sobrien newmode, m2); 208101297Sobrien } 209101297Sobrien (void)printf("\n"); 210101297Sobrien } 2111556Srgrimes } 2121556Srgrimes } 2131556Srgrimes if (errno) 2141556Srgrimes err(1, "fts_read"); 2151556Srgrimes exit(rval); 2161556Srgrimes} 2171556Srgrimes 218194795Sdelphijstatic void 21990107Simpusage(void) 2201556Srgrimes{ 2211556Srgrimes (void)fprintf(stderr, 22277342Sru "usage: chmod [-fhv] [-R [-H | -L | -P]] mode file ...\n"); 2231556Srgrimes exit(1); 2241556Srgrimes} 225195243Strasz 226195243Straszstatic int 227196711Straszmay_have_nfs4acl(const FTSENT *ent, int hflag) 228195243Strasz{ 229195243Strasz int ret; 230196711Strasz static dev_t previous_dev = NODEV; 231195243Strasz static int supports_acls = -1; 232195243Strasz 233195243Strasz if (previous_dev != ent->fts_statp->st_dev) { 234195243Strasz previous_dev = ent->fts_statp->st_dev; 235195243Strasz supports_acls = 0; 236195243Strasz 237196711Strasz if (hflag) 238196711Strasz ret = lpathconf(ent->fts_accpath, _PC_ACL_NFS4); 239196711Strasz else 240196711Strasz ret = pathconf(ent->fts_accpath, _PC_ACL_NFS4); 241195243Strasz if (ret > 0) 242195243Strasz supports_acls = 1; 243195243Strasz else if (ret < 0 && errno != EINVAL) 244195243Strasz warn("%s", ent->fts_path); 245195243Strasz } 246195243Strasz 247195243Strasz return (supports_acls); 248195243Strasz} 249