1 /* chmod -- change permission modes of files
2 Copyright (C) 89, 90, 91, 1995-2004 Free Software Foundation, Inc.
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
18 /* Written by David MacKenzie <djm@gnu.ai.mit.edu> */
23 #include <sys/types.h>
30 #include "modechange.h"
32 #include "root-dev-ino.h"
35 /* The official name of this program (e.g., no `g' prefix). */
36 #define PROGRAM_NAME "chmod"
38 #define AUTHORS "David MacKenzie", "Jim Meyering"
45 CH_NO_CHANGE_REQUESTED
50 /* Print a message for each file that is processed. */
53 /* Print a message for each file whose attributes we change. */
56 /* Do not be verbose. This is the default. */
60 /* The name the program was run with. */
63 /* If true, change the modes of directories recursively. */
66 /* If true, force silence (no error messages). */
67 static bool force_silent;
69 /* Level of verbosity. */
70 static enum Verbosity verbosity = V_off;
72 /* The argument to the --reference option. Use the owner and group IDs
73 of this file. This file must exist. */
74 static char *reference_file;
76 /* Pointer to the device and inode numbers of `/', when --recursive.
78 static struct dev_ino *root_dev_ino;
80 /* For long options that have no equivalent short option, use a
81 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
84 NO_PRESERVE_ROOT = CHAR_MAX + 1,
89 static struct option const long_options[] =
91 {"changes", no_argument, 0, 'c'},
92 {"recursive", no_argument, 0, 'R'},
93 {"no-preserve-root", no_argument, 0, NO_PRESERVE_ROOT},
94 {"preserve-root", no_argument, 0, PRESERVE_ROOT},
95 {"quiet", no_argument, 0, 'f'},
96 {"reference", required_argument, 0, REFERENCE_FILE_OPTION},
97 {"silent", no_argument, 0, 'f'},
98 {"verbose", no_argument, 0, 'v'},
99 {GETOPT_HELP_OPTION_DECL},
100 {GETOPT_VERSION_OPTION_DECL},
104 /* Return true if the chmodable permission bits of FILE changed.
105 The old mode was OLD_MODE, but it was changed to NEW_MODE. */
108 mode_changed (char const *file, mode_t old_mode, mode_t new_mode)
110 if (new_mode & (S_ISUID | S_ISGID | S_ISVTX))
112 /* The new mode contains unusual bits that the call to chmod may
113 have silently cleared. Check whether they actually changed. */
115 struct stat new_stats;
117 if (stat (file, &new_stats) != 0)
120 error (0, errno, _("getting new attributes of %s"), quote (file));
124 new_mode = new_stats.st_mode;
127 return ((old_mode ^ new_mode) & CHMOD_MODE_BITS) != 0;
130 /* Tell the user how/if the MODE of FILE has been changed.
131 CHANGED describes what (if anything) has happened. */
134 describe_change (const char *file, mode_t mode,
135 enum Change_status changed)
137 char perms[11]; /* "-rwxrwxrwx" ls-style modes. */
140 if (changed == CH_NOT_APPLIED)
142 printf (_("neither symbolic link %s nor referent has been changed\n"),
147 mode_string (mode, perms);
148 perms[10] = '\0'; /* `mode_string' does not null terminate. */
152 fmt = _("mode of %s changed to %04lo (%s)\n");
155 fmt = _("failed to change mode of %s to %04lo (%s)\n");
157 case CH_NO_CHANGE_REQUESTED:
158 fmt = _("mode of %s retained as %04lo (%s)\n");
163 printf (fmt, quote (file),
164 (unsigned long int) (mode & CHMOD_MODE_BITS), &perms[1]);
167 /* Change the mode of FILE according to the list of operations CHANGES.
168 Return true if successful. This function is called
169 once for every file system object that fts encounters. */
172 process_file (FTS *fts, FTSENT *ent, const struct mode_change *changes)
174 char const *file_full_name = ent->fts_path;
175 char const *file = ent->fts_accpath;
176 const struct stat *file_stats = ent->fts_statp;
177 mode_t new_mode IF_LINT (= 0);
180 bool symlink_changed = true;
182 switch (ent->fts_info)
188 error (0, ent->fts_errno, _("cannot access %s"), quote (file_full_name));
193 error (0, ent->fts_errno, _("%s"), quote (file_full_name));
198 error (0, ent->fts_errno, _("cannot read directory %s"),
199 quote (file_full_name));
209 if (do_chmod && ROOT_DEV_INO_CHECK (root_dev_ino, file_stats))
211 ROOT_DEV_INO_WARN (file_full_name);
218 new_mode = mode_adjust (file_stats->st_mode, changes);
220 if (S_ISLNK (file_stats->st_mode))
221 symlink_changed = false;
224 ok = (chmod (file, new_mode) == 0);
226 if (! (ok | force_silent))
227 error (0, errno, _("changing permissions of %s"),
228 quote (file_full_name));
232 if (verbosity != V_off)
235 (ok && symlink_changed
236 && mode_changed (file, file_stats->st_mode, new_mode));
238 if (changed || verbosity == V_high)
240 enum Change_status ch_status =
242 : !symlink_changed ? CH_NOT_APPLIED
243 : !changed ? CH_NO_CHANGE_REQUESTED
245 describe_change (file_full_name, new_mode, ch_status);
250 fts_set (fts, ent, FTS_SKIP);
255 /* Recursively change the modes of the specified FILES (the last entry
256 of which is NULL) according to the list of operations CHANGES.
257 BIT_FLAGS controls how fts works.
258 Return true if successful. */
261 process_files (char **files, int bit_flags, const struct mode_change *changes)
265 FTS *fts = xfts_open (files, bit_flags, NULL);
271 ent = fts_read (fts);
276 /* FIXME: try to give a better message */
277 error (0, errno, _("fts_read failed"));
283 ok &= process_file (fts, ent, changes);
286 /* Ignore failure, since the only way it can do so is in failing to
287 return to the original directory, and since we're about to exit,
288 that doesn't matter. */
297 if (status != EXIT_SUCCESS)
298 fprintf (stderr, _("Try `%s --help' for more information.\n"),
303 Usage: %s [OPTION]... MODE[,MODE]... FILE...\n\
304 or: %s [OPTION]... OCTAL-MODE FILE...\n\
305 or: %s [OPTION]... --reference=RFILE FILE...\n\
307 program_name, program_name, program_name);
309 Change the mode of each FILE to MODE.\n\
311 -c, --changes like verbose but report only when a change is made\n\
314 --no-preserve-root do not treat `/' specially (the default)\n\
315 --preserve-root fail to operate recursively on `/'\n\
318 -f, --silent, --quiet suppress most error messages\n\
319 -v, --verbose output a diagnostic for every file processed\n\
320 --reference=RFILE use RFILE's mode instead of MODE values\n\
321 -R, --recursive change files and directories recursively\n\
323 fputs (HELP_OPTION_DESCRIPTION, stdout);
324 fputs (VERSION_OPTION_DESCRIPTION, stdout);
327 Each MODE is one or more of the letters ugoa, one of the symbols +-= and\n\
328 one or more of the letters rwxXstugo.\n\
330 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
335 /* Parse the ASCII mode given on the command line into a linked list
336 of `struct mode_change' and apply that to each file argument. */
339 main (int argc, char **argv)
341 struct mode_change *changes;
343 int modeind = 0; /* Index of the mode argument in `argv'. */
345 bool preserve_root = false;
348 initialize_main (&argc, &argv);
349 program_name = argv[0];
350 setlocale (LC_ALL, "");
351 bindtextdomain (PACKAGE, LOCALEDIR);
352 textdomain (PACKAGE);
354 atexit (close_stdout);
356 recurse = force_silent = false;
360 thisind = optind ? optind : 1;
362 c = getopt_long (argc, argv, "RcfvrwxXstugoa,+-=", long_options, NULL);
382 if (modeind != 0 && modeind != thisind)
384 static char char_string[2] = {0, 0};
386 error (EXIT_FAILURE, 0,
387 _("invalid character %s in mode string %s"),
388 quote_n (0, char_string), quote_n (1, argv[thisind]));
392 case NO_PRESERVE_ROOT:
393 preserve_root = false;
396 preserve_root = true;
398 case REFERENCE_FILE_OPTION:
399 reference_file = optarg;
405 verbosity = V_changes_only;
413 case_GETOPT_HELP_CHAR;
414 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
416 usage (EXIT_FAILURE);
420 if (modeind == 0 && reference_file == NULL)
425 if (modeind == 0 || modeind != argc - 1)
426 error (0, 0, _("missing operand"));
428 error (0, 0, _("missing operand after %s"), quote (argv[argc - 1]));
429 usage (EXIT_FAILURE);
432 changes = (reference_file ? mode_create_from_ref (reference_file)
433 : mode_compile (argv[modeind], MODE_MASK_ALL));
435 if (changes == MODE_INVALID)
436 error (EXIT_FAILURE, 0,
437 _("invalid mode string: %s"), quote (argv[modeind]));
438 else if (changes == MODE_MEMORY_EXHAUSTED)
440 else if (changes == MODE_BAD_REFERENCE)
441 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
442 quote (reference_file));
444 if (recurse & preserve_root)
446 static struct dev_ino dev_ino_buf;
447 root_dev_ino = get_root_dev_ino (&dev_ino_buf);
448 if (root_dev_ino == NULL)
449 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
457 ok = process_files (argv + optind, FTS_COMFOLLOW, changes);
459 exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);