1 1.39 kre /* $NetBSD: chmod.c,v 1.39 2023/05/05 04:14:02 kre Exp $ */ 2 1.12 cgd 3 1.1 cgd /* 4 1.9 mycroft * Copyright (c) 1989, 1993, 1994 5 1.9 mycroft * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.30 agc * 3. Neither the name of the University nor the names of its contributors 16 1.1 cgd * may be used to endorse or promote products derived from this software 17 1.1 cgd * without specific prior written permission. 18 1.1 cgd * 19 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 1.1 cgd * SUCH DAMAGE. 30 1.1 cgd */ 31 1.1 cgd 32 1.14 thorpej #include <sys/cdefs.h> 33 1.1 cgd #ifndef lint 34 1.14 thorpej __COPYRIGHT( 35 1.34 lukem "@(#) Copyright (c) 1989, 1993, 1994\ 36 1.34 lukem The Regents of the University of California. All rights reserved."); 37 1.1 cgd #endif /* not lint */ 38 1.1 cgd 39 1.1 cgd #ifndef lint 40 1.12 cgd #if 0 41 1.12 cgd static char sccsid[] = "@(#)chmod.c 8.8 (Berkeley) 4/1/94"; 42 1.12 cgd #else 43 1.39 kre __RCSID("$NetBSD: chmod.c,v 1.39 2023/05/05 04:14:02 kre Exp $"); 44 1.12 cgd #endif 45 1.1 cgd #endif /* not lint */ 46 1.1 cgd 47 1.29 jschauma #include <sys/param.h> 48 1.29 jschauma #include <sys/stat.h> 49 1.1 cgd #include <sys/types.h> 50 1.9 mycroft 51 1.9 mycroft #include <err.h> 52 1.7 mycroft #include <errno.h> 53 1.1 cgd #include <fts.h> 54 1.26 wiz #include <limits.h> 55 1.13 kleink #include <locale.h> 56 1.1 cgd #include <stdio.h> 57 1.7 mycroft #include <stdlib.h> 58 1.1 cgd #include <string.h> 59 1.9 mycroft #include <unistd.h> 60 1.37 christos #include <getopt.h> 61 1.29 jschauma 62 1.36 joerg __dead static void usage(void); 63 1.7 mycroft 64 1.37 christos struct option chmod_longopts[] = { 65 1.37 christos { "reference", required_argument, 0, 66 1.37 christos 1 }, 67 1.37 christos { NULL, 0, 0, 68 1.37 christos 0 }, 69 1.37 christos }; 70 1.37 christos 71 1.7 mycroft int 72 1.26 wiz main(int argc, char *argv[]) 73 1.1 cgd { 74 1.9 mycroft FTS *ftsp; 75 1.9 mycroft FTSENT *p; 76 1.37 christos void *set; 77 1.39 kre mode_t mval, nval; 78 1.39 kre int Hflag, Lflag, Rflag, ch, dflag, fflag, fts_options, hflag, rval; 79 1.37 christos char *mode, *reference; 80 1.26 wiz int (*change_mode)(const char *, mode_t); 81 1.14 thorpej 82 1.26 wiz setprogname(argv[0]); 83 1.18 mycroft (void)setlocale(LC_ALL, ""); 84 1.1 cgd 85 1.39 kre Hflag = Lflag = Rflag = dflag = fflag = hflag = 0; 86 1.37 christos reference = NULL; 87 1.39 kre while ((ch = getopt_long(argc, argv, "HLPRXdfghorstuwx", 88 1.37 christos chmod_longopts, NULL)) != -1) 89 1.9 mycroft switch (ch) { 90 1.37 christos case 1: 91 1.37 christos reference = optarg; 92 1.37 christos break; 93 1.9 mycroft case 'H': 94 1.9 mycroft Hflag = 1; 95 1.18 mycroft Lflag = 0; 96 1.9 mycroft break; 97 1.9 mycroft case 'L': 98 1.9 mycroft Lflag = 1; 99 1.18 mycroft Hflag = 0; 100 1.9 mycroft break; 101 1.9 mycroft case 'P': 102 1.9 mycroft Hflag = Lflag = 0; 103 1.9 mycroft break; 104 1.1 cgd case 'R': 105 1.9 mycroft Rflag = 1; 106 1.1 cgd break; 107 1.39 kre case 'd': 108 1.39 kre dflag = 1; 109 1.39 kre break; 110 1.35 snj case 'f': 111 1.1 cgd fflag = 1; 112 1.1 cgd break; 113 1.9 mycroft case 'h': 114 1.9 mycroft /* 115 1.27 bjh21 * In System V the -h option causes chmod to 116 1.27 bjh21 * change the mode of the symbolic link. 117 1.27 bjh21 * 4.4BSD's symbolic links didn't have modes, 118 1.27 bjh21 * so it was an undocumented noop. In NetBSD 119 1.27 bjh21 * 1.3, lchmod(2) is introduced and this 120 1.27 bjh21 * option does real work. 121 1.9 mycroft */ 122 1.9 mycroft hflag = 1; 123 1.9 mycroft break; 124 1.9 mycroft /* 125 1.9 mycroft * XXX 126 1.9 mycroft * "-[rwx]" are valid mode commands. If they are the entire 127 1.9 mycroft * argument, getopt has moved past them, so decrement optind. 128 1.9 mycroft * Regardless, we're done argument processing. 129 1.9 mycroft */ 130 1.9 mycroft case 'g': case 'o': case 'r': case 's': 131 1.9 mycroft case 't': case 'u': case 'w': case 'X': case 'x': 132 1.9 mycroft if (argv[optind - 1][0] == '-' && 133 1.9 mycroft argv[optind - 1][1] == ch && 134 1.9 mycroft argv[optind - 1][2] == '\0') 135 1.9 mycroft --optind; 136 1.1 cgd goto done; 137 1.1 cgd case '?': 138 1.1 cgd default: 139 1.1 cgd usage(); 140 1.1 cgd } 141 1.1 cgd done: argv += optind; 142 1.1 cgd argc -= optind; 143 1.1 cgd 144 1.38 christos if (argc == 0 || (argc == 1 && reference == NULL)) 145 1.1 cgd usage(); 146 1.1 cgd 147 1.9 mycroft fts_options = FTS_PHYSICAL; 148 1.9 mycroft if (Rflag) { 149 1.29 jschauma if (hflag) { 150 1.29 jschauma errx(EXIT_FAILURE, 151 1.9 mycroft "the -R and -h options may not be specified together."); 152 1.29 jschauma /* NOTREACHED */ 153 1.29 jschauma } 154 1.9 mycroft if (Hflag) 155 1.9 mycroft fts_options |= FTS_COMFOLLOW; 156 1.9 mycroft if (Lflag) { 157 1.9 mycroft fts_options &= ~FTS_PHYSICAL; 158 1.9 mycroft fts_options |= FTS_LOGICAL; 159 1.9 mycroft } 160 1.28 bjh21 } else if (!hflag) 161 1.28 bjh21 fts_options |= FTS_COMFOLLOW; 162 1.16 enami if (hflag) 163 1.16 enami change_mode = lchmod; 164 1.16 enami else 165 1.16 enami change_mode = chmod; 166 1.9 mycroft 167 1.37 christos if (reference == NULL) { 168 1.37 christos mode = *argv++; 169 1.37 christos if ((set = setmode(mode)) == NULL) { 170 1.37 christos err(EXIT_FAILURE, "Cannot set file mode `%s'", mode); 171 1.37 christos /* NOTREACHED */ 172 1.37 christos } 173 1.37 christos mval = 0; 174 1.37 christos } else { 175 1.37 christos struct stat st; 176 1.37 christos 177 1.37 christos if (stat(reference, &st) == -1) 178 1.37 christos err(EXIT_FAILURE, "Cannot stat `%s'", reference); 179 1.37 christos mval = st.st_mode; 180 1.37 christos set = NULL; 181 1.29 jschauma } 182 1.1 cgd 183 1.37 christos if ((ftsp = fts_open(argv, fts_options, 0)) == NULL) { 184 1.29 jschauma err(EXIT_FAILURE, "fts_open"); 185 1.29 jschauma /* NOTREACHED */ 186 1.29 jschauma } 187 1.9 mycroft for (rval = 0; (p = fts_read(ftsp)) != NULL;) { 188 1.9 mycroft switch (p->fts_info) { 189 1.9 mycroft case FTS_D: 190 1.11 mycroft if (!Rflag) 191 1.19 mycroft (void)fts_set(ftsp, p, FTS_SKIP); 192 1.9 mycroft break; 193 1.9 mycroft case FTS_DNR: /* Warn, chmod, continue. */ 194 1.31 jschauma warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 195 1.9 mycroft rval = 1; 196 1.9 mycroft break; 197 1.11 mycroft case FTS_DP: /* Already changed at FTS_D. */ 198 1.11 mycroft continue; 199 1.9 mycroft case FTS_ERR: /* Warn, continue. */ 200 1.9 mycroft case FTS_NS: 201 1.31 jschauma warnx("%s: %s", p->fts_path, strerror(p->fts_errno)); 202 1.9 mycroft rval = 1; 203 1.9 mycroft continue; 204 1.9 mycroft case FTS_SL: /* Ignore. */ 205 1.9 mycroft case FTS_SLNONE: 206 1.9 mycroft /* 207 1.9 mycroft * The only symlinks that end up here are ones that 208 1.9 mycroft * don't point to anything and ones that we found 209 1.9 mycroft * doing a physical walk. 210 1.9 mycroft */ 211 1.16 enami if (!hflag) 212 1.16 enami continue; 213 1.16 enami /* else */ 214 1.16 enami /* FALLTHROUGH */ 215 1.9 mycroft default: 216 1.9 mycroft break; 217 1.9 mycroft } 218 1.39 kre nval = set ? getmode(set, p->fts_statp->st_mode) : mval; 219 1.39 kre if (dflag && nval == p->fts_statp->st_mode) 220 1.39 kre continue; 221 1.39 kre if ((*change_mode)(p->fts_accpath, nval) && !fflag) { 222 1.31 jschauma warn("%s", p->fts_path); 223 1.9 mycroft rval = 1; 224 1.4 deraadt } 225 1.4 deraadt } 226 1.29 jschauma if (errno) { 227 1.29 jschauma err(EXIT_FAILURE, "fts_read"); 228 1.29 jschauma /* NOTREACHED */ 229 1.29 jschauma } 230 1.9 mycroft exit(rval); 231 1.18 mycroft /* NOTREACHED */ 232 1.1 cgd } 233 1.1 cgd 234 1.36 joerg static void 235 1.26 wiz usage(void) 236 1.1 cgd { 237 1.9 mycroft (void)fprintf(stderr, 238 1.39 kre "Usage: %s [-R [-H | -L | -P]] [-dfh] mode file ...\n" 239 1.39 kre "\t%s [-R [-H | -L | -P]] [-dfh] --reference=rfile file ...\n", 240 1.37 christos getprogname(), getprogname()); 241 1.1 cgd exit(1); 242 1.20 mycroft /* NOTREACHED */ 243 1.1 cgd } 244