Whamcloud - gitweb
Read mke2fs.conf and e2fsck.conf from root_sysconfdir rather than harcoded /etc.
[tools/e2fsprogs.git] / misc / chattr.c
1 /*
2  * chattr.c             - Change file attributes on an ext2 file system
3  *
4  * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
5  *                           Laboratoire MASI, Institut Blaise Pascal
6  *                           Universite Pierre et Marie Curie (Paris VI)
7  *
8  * This file can be redistributed under the terms of the GNU General
9  * Public License
10  */
11
12 /*
13  * History:
14  * 93/10/30     - Creation
15  * 93/11/13     - Replace stat() calls by lstat() to avoid loops
16  * 94/02/27     - Integrated in Ted's distribution
17  * 98/12/29     - Ignore symlinks when working recursively (G M Sipe)
18  * 98/12/29     - Display version info only when -V specified (G M Sipe)
19  */
20
21 #define _LARGEFILE64_SOURCE
22
23 #include <sys/types.h>
24 #include <dirent.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <unistd.h>
29 #include <string.h>
30 #ifdef HAVE_ERRNO_H
31 #include <errno.h>
32 #endif
33 #include <sys/param.h>
34 #include <sys/stat.h>
35 #include "ext2fs/ext2_fs.h"
36
37 #ifdef __GNUC__
38 #define EXT2FS_ATTR(x) __attribute__(x)
39 #else
40 #define EXT2FS_ATTR(x)
41 #endif
42
43 #ifndef S_ISLNK                 /* So we can compile even with gcc-warn */
44 # ifdef __S_IFLNK
45 #  define S_ISLNK(mode)  __S_ISTYPE((mode), __S_IFLNK)
46 # else
47 #  define S_ISLNK(mode)  0
48 # endif
49 #endif
50
51 #include "et/com_err.h"
52 #include "e2p/e2p.h"
53
54 #include "../version.h"
55 #include "nls-enable.h"
56
57 static const char * program_name = "chattr";
58
59 static int add;
60 static int rem;
61 static int set;
62 static int set_version;
63
64 static unsigned long version;
65
66 static int recursive;
67 static int verbose;
68
69 static unsigned long af;
70 static unsigned long rf;
71 static unsigned long sf;
72
73 #ifdef _LFS64_LARGEFILE
74 #define LSTAT           lstat64
75 #define STRUCT_STAT     struct stat64
76 #else
77 #define LSTAT           lstat
78 #define STRUCT_STAT     struct stat
79 #endif
80
81 static void fatal_error(const char * fmt_string, int errcode)
82 {
83         fprintf (stderr, fmt_string, program_name);
84         exit (errcode);
85 }
86
87 #define usage() fatal_error(_("Usage: %s [-RV] [-+=AacDdijsSu] [-v version] files...\n"), \
88                              1)
89
90 struct flags_char {
91         unsigned long   flag;
92         char            optchar;
93 };
94
95 static const struct flags_char flags_array[] = {
96         { EXT2_NOATIME_FL, 'A' },
97         { EXT2_SYNC_FL, 'S' },
98         { EXT2_DIRSYNC_FL, 'D' },
99         { EXT2_APPEND_FL, 'a' },
100         { EXT2_COMPR_FL, 'c' },
101         { EXT2_NODUMP_FL, 'd' },
102         { EXT2_IMMUTABLE_FL, 'i' },
103         { EXT3_JOURNAL_DATA_FL, 'j' },
104         { EXT2_SECRM_FL, 's' },
105         { EXT2_UNRM_FL, 'u' },
106         { EXT2_NOTAIL_FL, 't' },
107         { EXT2_TOPDIR_FL, 'T' },
108         { 0, 0 }
109 };
110
111 static unsigned long get_flag(char c)
112 {
113         const struct flags_char *fp;
114         
115         for (fp = flags_array; fp->flag != 0; fp++) {
116                 if (fp->optchar == c)
117                         return fp->flag;
118         }
119         return 0;
120 }
121
122
123 static int decode_arg (int * i, int argc, char ** argv)
124 {
125         char * p;
126         char * tmp;
127         unsigned long fl;
128
129         switch (argv[*i][0])
130         {
131         case '-':
132                 for (p = &argv[*i][1]; *p; p++) {
133                         if (*p == 'R') {
134                                 recursive = 1;
135                                 continue;
136                         }
137                         if (*p == 'V') {
138                                 verbose = 1;
139                                 continue;
140                         }
141                         if (*p == 'v') {
142                                 (*i)++;
143                                 if (*i >= argc)
144                                         usage ();
145                                 version = strtol (argv[*i], &tmp, 0);
146                                 if (*tmp) {
147                                         com_err (program_name, 0,
148                                                  _("bad version - %s\n"), 
149                                                  argv[*i]);
150                                         usage ();
151                                 }
152                                 set_version = 1;
153                                 continue;
154                         }
155                         if ((fl = get_flag(*p)) == 0)
156                                 usage();
157                         rf |= fl;
158                         rem = 1;
159                 }
160                 break;
161         case '+':
162                 add = 1;
163                 for (p = &argv[*i][1]; *p; p++) {
164                         if ((fl = get_flag(*p)) == 0)
165                                 usage();
166                         af |= fl;
167                 }
168                 break;
169         case '=':
170                 set = 1;
171                 for (p = &argv[*i][1]; *p; p++) {
172                         if ((fl = get_flag(*p)) == 0)
173                                 usage();
174                         sf |= fl;
175                 }
176                 break;
177         default:
178                 return EOF;
179                 break;
180         }
181         return 1;
182 }
183
184 static int chattr_dir_proc (const char *, struct dirent *, void *);
185
186 static void change_attributes (const char * name)
187 {
188         unsigned long flags;
189         STRUCT_STAT     st;
190
191         if (LSTAT (name, &st) == -1) {
192                 com_err (program_name, errno, _("while trying to stat %s"), 
193                          name);
194                 return;
195         }
196         if (S_ISLNK(st.st_mode) && recursive)
197                 return;
198
199         /* Don't try to open device files, fifos etc.  We probably
200            ought to display an error if the file was explicitly given
201            on the command line (whether or not recursive was
202            requested).  */
203         if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) &&
204             !S_ISDIR(st.st_mode))
205                 return;
206
207         if (set) {
208                 if (verbose) {
209                         printf (_("Flags of %s set as "), name);
210                         print_flags (stdout, sf, 0);
211                         printf ("\n");
212                 }
213                 if (fsetflags (name, sf) == -1)
214                         perror (name);
215         } else {
216                 if (fgetflags (name, &flags) == -1)
217                         com_err (program_name, errno,
218                                  _("while reading flags on %s"), name);
219                 else {
220                         if (rem)
221                                 flags &= ~rf;
222                         if (add)
223                                 flags |= af;
224                         if (verbose) {
225                                 printf (_("Flags of %s set as "), name);
226                                 print_flags (stdout, flags, 0);
227                                 printf ("\n");
228                         }
229                         if (!S_ISDIR(st.st_mode))
230                                 flags &= ~EXT2_DIRSYNC_FL;
231                         if (fsetflags (name, flags) == -1)
232                                 com_err (program_name, errno,
233                                          _("while setting flags on %s"), name);
234                 }
235         }
236         if (set_version) {
237                 if (verbose)
238                         printf (_("Version of %s set as %lu\n"), name, version);
239                 if (fsetversion (name, version) == -1)
240                         com_err (program_name, errno,
241                                  _("while setting version on %s"), name);
242         }
243         if (S_ISDIR(st.st_mode) && recursive)
244                 iterate_on_dir (name, chattr_dir_proc, NULL);
245 }
246
247 static int chattr_dir_proc (const char * dir_name, struct dirent * de,
248                             void * private EXT2FS_ATTR((unused)))
249 {
250         if (strcmp (de->d_name, ".") && strcmp (de->d_name, "..")) {
251                 char *path;
252
253                 path = malloc(strlen (dir_name) + 1 + strlen (de->d_name) + 1);
254                 if (!path)
255                         fatal_error(_("Couldn't allocate path variable "
256                                     "in chattr_dir_proc"), 1);
257                 sprintf (path, "%s/%s", dir_name, de->d_name);
258                 change_attributes (path);
259                 free(path);
260         }
261         return 0;
262 }
263
264 int main (int argc, char ** argv)
265 {
266         int i, j;
267         int end_arg = 0;
268
269 #ifdef ENABLE_NLS
270         setlocale(LC_MESSAGES, "");
271         setlocale(LC_CTYPE, "");
272         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
273         textdomain(NLS_CAT_NAME);
274 #endif
275         if (argc && *argv)
276                 program_name = *argv;
277         i = 1;
278         while (i < argc && !end_arg) {
279                 /* '--' arg should end option processing */
280                 if (strcmp(argv[i], "--") == 0) {
281                         i++;
282                         end_arg = 1;
283                 } else if (decode_arg (&i, argc, argv) == EOF)
284                         end_arg = 1;
285                 else
286                         i++;
287         }
288         if (i >= argc)
289                 usage ();
290         if (set && (add || rem)) {
291                 fputs(_("= is incompatible with - and +\n"), stderr);
292                 exit (1);
293         }
294         if ((rf & af) != 0) {
295                 fputs("Can't both set and unset same flag.\n", stderr);
296                 exit (1);
297         }
298         if (!(add || rem || set || set_version)) {
299                 fputs(_("Must use '-v', =, - or +\n"), stderr);
300                 exit (1);
301         }
302         if (verbose)
303                 fprintf (stderr, "chattr %s (%s)\n",
304                          E2FSPROGS_VERSION, E2FSPROGS_DATE);
305         for (j = i; j < argc; j++)
306                 change_attributes (argv[j]);
307         exit(0);
308 }