1 /* chmod -- change permission modes of files
2 Copyright (C) 89, 90, 91, 1995-2003 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"
34 /* The official name of this program (e.g., no `g' prefix). */
35 #define PROGRAM_NAME "chmod"
37 #define WRITTEN_BY _("Written by David MacKenzie.")
43 CH_NO_CHANGE_REQUESTED
48 /* Print a message for each file that is processed. */
51 /* Print a message for each file whose attributes we change. */
54 /* Do not be verbose. This is the default. */
58 static int change_dir_mode (const char *dir, const struct mode_change *changes);
60 /* The name the program was run with. */
63 /* If nonzero, change the modes of directories recursively. */
66 /* If nonzero, force silence (no error messages). */
67 static int 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 {"recursive", no_argument, 0, 'R'},
92 {"changes", no_argument, 0, 'c'},
93 {"preserve-root", no_argument, 0, PRESERVE_ROOT},
94 {"no-preserve-root", no_argument, 0, NO_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},
105 mode_changed (const char *file, mode_t old_mode)
107 struct stat new_stats;
109 if (stat (file, &new_stats))
111 if (force_silent == 0)
112 error (0, errno, _("getting new attributes of %s"), quote (file));
116 return old_mode != new_stats.st_mode;
119 /* Tell the user how/if the MODE of FILE has been changed.
120 CHANGED describes what (if anything) has happened. */
123 describe_change (const char *file, mode_t mode,
124 enum Change_status changed)
126 char perms[11]; /* "-rwxrwxrwx" ls-style modes. */
129 mode_string (mode, perms);
130 perms[10] = '\0'; /* `mode_string' does not null terminate. */
134 fmt = _("mode of %s changed to %04lo (%s)\n");
137 fmt = _("failed to change mode of %s to %04lo (%s)\n");
139 case CH_NO_CHANGE_REQUESTED:
140 fmt = _("mode of %s retained as %04lo (%s)\n");
145 printf (fmt, quote (file),
146 (unsigned long) (mode & CHMOD_MODE_BITS), &perms[1]);
149 /* Change the mode of FILE according to the list of operations CHANGES.
150 If DEREF_SYMLINK is nonzero and FILE is a symbolic link, change the
151 mode of the referenced file. If DEREF_SYMLINK is zero, ignore symbolic
152 links. Return 0 if successful, 1 if errors occurred. */
155 change_file_mode (const char *file, const struct mode_change *changes,
156 const int deref_symlink)
158 struct stat file_stats;
164 if (deref_symlink ? stat (file, &file_stats) : lstat (file, &file_stats))
166 if (force_silent == 0)
167 error (0, errno, _("failed to get attributes of %s"), quote (file));
171 if (root_dev_ino && SAME_INODE (file_stats, *root_dev_ino))
173 if (STREQ (file, "/"))
174 error (0, 0, _("it is dangerous to operate recursively on %s"),
178 _("it is dangerous to operate recursively on %s (same as %s)"),
179 quote_n (0, file), quote_n (1, "/"));
180 error (0, 0, _("use --no-preserve-root to override this failsafe"));
184 if (S_ISLNK (file_stats.st_mode))
187 newmode = mode_adjust (file_stats.st_mode, changes);
189 fail = chmod (file, newmode);
192 if (verbosity == V_high
193 || (verbosity == V_changes_only
194 && !fail && mode_changed (file, file_stats.st_mode)))
195 describe_change (file, newmode, (fail ? CH_FAILED : CH_SUCCEEDED));
199 if (force_silent == 0)
200 error (0, saved_errno, _("changing permissions of %s"),
205 if (recurse && S_ISDIR (file_stats.st_mode))
208 ASSIGN_STRDUPA (f, file);
209 strip_trailing_slashes (f);
210 errors |= change_dir_mode (f, changes);
215 /* Recursively change the modes of the files in directory DIR
216 according to the list of operations CHANGES.
217 Return 0 if successful, 1 if errors occurred. */
220 change_dir_mode (const char *dir, const struct mode_change *changes)
222 char *name_space, *namep;
223 char *path; /* Full path of each entry to process. */
224 unsigned dirlength; /* Length of DIR and '\0'. */
225 unsigned filelength; /* Length of each pathname to process. */
226 unsigned pathlength; /* Bytes allocated for `path'. */
229 name_space = savedir (dir);
230 if (name_space == NULL)
232 if (force_silent == 0)
233 error (0, errno, "%s", quote (dir));
237 dirlength = strlen (dir) + 1; /* + 1 is for the trailing '/'. */
238 pathlength = dirlength + 1;
239 /* Give `path' a dummy value; it will be reallocated before first use. */
240 path = xmalloc (pathlength);
242 path[dirlength - 1] = '/';
244 for (namep = name_space; *namep; namep += filelength - dirlength)
246 filelength = dirlength + strlen (namep) + 1;
247 if (filelength > pathlength)
249 pathlength = filelength * 2;
250 path = xrealloc (path, pathlength);
252 strcpy (path + dirlength, namep);
253 errors |= change_file_mode (path, changes, 0);
264 fprintf (stderr, _("Try `%s --help' for more information.\n"),
269 Usage: %s [OPTION]... MODE[,MODE]... FILE...\n\
270 or: %s [OPTION]... OCTAL-MODE FILE...\n\
271 or: %s [OPTION]... --reference=RFILE FILE...\n\
273 program_name, program_name, program_name);
275 Change the mode of each FILE to MODE.\n\
277 -c, --changes like verbose but report only when a change is made\n\
278 --preserve-root fail to operate recursively on `/'\n\
279 --no-preserve-root do not treat `/' specially (the default)\n\
280 -f, --silent, --quiet suppress most error messages\n\
281 -v, --verbose output a diagnostic for every file processed\n\
282 --reference=RFILE use RFILE's mode instead of MODE values\n\
283 -R, --recursive change files and directories recursively\n\
285 fputs (HELP_OPTION_DESCRIPTION, stdout);
286 fputs (VERSION_OPTION_DESCRIPTION, stdout);
289 Each MODE is one or more of the letters ugoa, one of the symbols +-= and\n\
290 one or more of the letters rwxXstugo.\n\
292 printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
297 /* Call lstat to get the device and inode numbers for `/'.
298 Upon failure, return NULL. Otherwise, set the members of
299 *ROOT_D_I accordingly and return ROOT_D_I. */
300 static struct dev_ino *
301 get_root_dev_ino (struct dev_ino *root_d_i)
304 if (lstat ("/", &statbuf))
306 root_d_i->st_ino = statbuf.st_ino;
307 root_d_i->st_dev = statbuf.st_dev;
311 /* Parse the ASCII mode given on the command line into a linked list
312 of `struct mode_change' and apply that to each file argument. */
315 main (int argc, char **argv)
317 struct mode_change *changes;
319 int modeind = 0; /* Index of the mode argument in `argv'. */
321 bool preserve_root = false;
324 initialize_main (&argc, &argv);
325 program_name = argv[0];
326 setlocale (LC_ALL, "");
327 bindtextdomain (PACKAGE, LOCALEDIR);
328 textdomain (PACKAGE);
330 atexit (close_stdout);
332 recurse = force_silent = 0;
336 thisind = optind ? optind : 1;
338 c = getopt_long (argc, argv, "RcfvrwxXstugoa,+-=", long_options, NULL);
360 if (modeind != 0 && modeind != thisind)
362 static char char_string[2] = {0, 0};
364 error (EXIT_FAILURE, 0,
365 _("invalid character %s in mode string %s"),
366 quote_n (0, char_string), quote_n (1, argv[thisind]));
370 case NO_PRESERVE_ROOT:
371 preserve_root = false;
374 preserve_root = true;
376 case REFERENCE_FILE_OPTION:
377 reference_file = optarg;
383 verbosity = V_changes_only;
391 case_GETOPT_HELP_CHAR;
392 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, WRITTEN_BY);
394 usage (EXIT_FAILURE);
398 if (modeind == 0 && reference_file == NULL)
403 error (0, 0, _("too few arguments"));
404 usage (EXIT_FAILURE);
407 changes = (reference_file ? mode_create_from_ref (reference_file)
408 : mode_compile (argv[modeind], MODE_MASK_ALL));
410 if (changes == MODE_INVALID)
411 error (EXIT_FAILURE, 0,
412 _("invalid mode string: %s"), quote (argv[modeind]));
413 else if (changes == MODE_MEMORY_EXHAUSTED)
415 else if (changes == MODE_BAD_REFERENCE)
416 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
417 quote (reference_file));
419 if (recurse && preserve_root)
421 static struct dev_ino dev_ino_buf;
422 root_dev_ino = get_root_dev_ino (&dev_ino_buf);
423 if (root_dev_ino == NULL)
424 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
432 for (; optind < argc; ++optind)
433 errors |= change_file_mode (argv[optind], changes, 1);