Whamcloud - gitweb
Remove EXT2FS_VERSION from the version display, since it
[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 #ifndef S_ISLNK                 /* So we can compile even with gcc-warn */
38 # ifdef __S_IFLNK
39 #  define S_ISLNK(mode)  __S_ISTYPE((mode), __S_IFLNK)
40 # else
41 #  define S_ISLNK(mode)  0
42 # endif
43 #endif
44
45 #include "et/com_err.h"
46 #include "e2p/e2p.h"
47
48 #include "../version.h"
49 #include "nls-enable.h"
50
51 static const char * program_name = "chattr";
52
53 static int add;
54 static int rem;
55 static int set;
56 static int set_version;
57
58 static unsigned long version;
59
60 static int recursive;
61 static int verbose;
62
63 static unsigned long af;
64 static unsigned long rf;
65 static unsigned long sf;
66
67 #ifdef _LFS64_LARGEFILE
68 #define LSTAT           lstat64
69 #define STRUCT_STAT     struct stat64
70 #else
71 #define LSTAT           lstat
72 #define STRUCT_STAT     struct stat
73 #endif
74
75 static void fatal_error(const char * fmt_string, int errcode)
76 {
77         fprintf (stderr, fmt_string, program_name);
78         exit (errcode);
79 }
80
81 #define usage() fatal_error(_("usage: %s [-RV] [-+=AacdijsSu] [-v version] files...\n"), \
82                              1)
83
84 struct flags_char {
85         unsigned long   flag;
86         char            optchar;
87 };
88
89 static const struct flags_char flags_array[] = {
90         { EXT2_NOATIME_FL, 'A' },
91         { EXT2_SYNC_FL, 'S' },
92         { EXT2_APPEND_FL, 'a' },
93         { EXT2_COMPR_FL, 'c' },
94         { EXT2_NODUMP_FL, 'd' },
95         { EXT2_IMMUTABLE_FL, 'i' },
96         { EXT3_JOURNAL_DATA_FL, 'j' },
97         { EXT2_SECRM_FL, 's' },
98         { EXT2_UNRM_FL, 'u' },
99         { 0, 0 }
100 };
101
102 static unsigned long get_flag(char c)
103 {
104         const struct flags_char *fp;
105         
106         for (fp = flags_array; fp->flag != 0; fp++) {
107                 if (fp->optchar == c)
108                         return fp->flag;
109         }
110         return 0;
111 }
112
113
114 static int decode_arg (int * i, int argc, char ** argv)
115 {
116         char * p;
117         char * tmp;
118         unsigned long fl;
119
120         switch (argv[*i][0])
121         {
122         case '-':
123                 for (p = &argv[*i][1]; *p; p++) {
124                         if (*p == 'R') {
125                                 recursive = 1;
126                                 continue;
127                         }
128                         if (*p == 'V') {
129                                 verbose = 1;
130                                 continue;
131                         }
132                         if (*p == 'v') {
133                                 (*i)++;
134                                 if (*i >= argc)
135                                         usage ();
136                                 version = strtol (argv[*i], &tmp, 0);
137                                 if (*tmp) {
138                                         com_err (program_name, 0,
139                                                  _("bad version - %s\n"), 
140                                                  argv[*i]);
141                                         usage ();
142                                 }
143                                 set_version = 1;
144                                 continue;
145                         }
146                         if ((fl = get_flag(*p)) == 0)
147                                 usage();
148                         rf |= fl;
149                         rem = 1;
150                 }
151                 break;
152         case '+':
153                 add = 1;
154                 for (p = &argv[*i][1]; *p; p++) {
155                         if ((fl = get_flag(*p)) == 0)
156                                 usage();
157                         af |= fl;
158                 }
159                 break;
160         case '=':
161                 set = 1;
162                 for (p = &argv[*i][1]; *p; p++) {
163                         if ((fl = get_flag(*p)) == 0)
164                                 usage();
165                         sf |= fl;
166                 }
167                 break;
168         default:
169                 return EOF;
170                 break;
171         }
172         return 1;
173 }
174
175 static int chattr_dir_proc (const char *, struct dirent *, void *);
176
177 static void change_attributes (const char * name)
178 {
179         unsigned long flags;
180         STRUCT_STAT     st;
181
182         if (LSTAT (name, &st) == -1) {
183                 com_err (program_name, errno, _("while trying to stat %s"), 
184                          name);
185                 return;
186         }
187         if (S_ISLNK(st.st_mode) && recursive)
188                 return;
189
190         /* Don't try to open device files, fifos etc.  We probably
191            ought to display an error if the file was explicitly given
192            on the command line (whether or not recursive was
193            requested).  */
194         if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) &&
195             !S_ISDIR(st.st_mode))
196                 return;
197
198         if (set) {
199                 if (verbose) {
200                         printf (_("Flags of %s set as "), name);
201                         print_flags (stdout, sf, 0);
202                         printf ("\n");
203                 }
204                 if (fsetflags (name, sf) == -1)
205                         perror (name);
206         } else {
207                 if (fgetflags (name, &flags) == -1)
208                         com_err (program_name, errno,
209                                  _("while reading flags on %s"), name);
210                 else {
211                         if (rem)
212                                 flags &= ~rf;
213                         if (add)
214                                 flags |= af;
215                         if (verbose) {
216                                 printf (_("Flags of %s set as "), name);
217                                 print_flags (stdout, flags, 0);
218                                 printf ("\n");
219                         }
220                         if (fsetflags (name, flags) == -1)
221                                 com_err (program_name, errno,
222                                          _("while setting flags on %s"), name);
223                 }
224         }
225         if (set_version) {
226                 if (verbose)
227                         printf (_("Version of %s set as %lu\n"), name, version);
228                 if (fsetversion (name, version) == -1)
229                         com_err (program_name, errno,
230                                  _("while setting version on %s"), name);
231         }
232         if (S_ISDIR(st.st_mode) && recursive)
233                 iterate_on_dir (name, chattr_dir_proc, NULL);
234 }
235
236 static int chattr_dir_proc (const char * dir_name, struct dirent * de,
237                             void * unused_private)
238 {
239         if (strcmp (de->d_name, ".") && strcmp (de->d_name, "..")) {
240                 char *path;
241
242                 path = malloc(strlen (dir_name) + 1 + strlen (de->d_name) + 1);
243                 if (!path)
244                         fatal_error(_("Couldn't allocate path variable "
245                                     "in chattr_dir_proc"), 1);
246                 sprintf (path, "%s/%s", dir_name, de->d_name);
247                 change_attributes (path);
248                 free(path);
249         }
250         return 0;
251 }
252
253 int main (int argc, char ** argv)
254 {
255         int i, j;
256         int end_arg = 0;
257
258 #ifdef ENABLE_NLS
259         setlocale(LC_MESSAGES, "");
260         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
261         textdomain(NLS_CAT_NAME);
262 #endif
263         if (argc && *argv)
264                 program_name = *argv;
265         i = 1;
266         while (i < argc && !end_arg) {
267                 if (decode_arg (&i, argc, argv) == EOF)
268                         end_arg = 1;
269                 else
270                         i++;
271         }
272         if (i >= argc)
273                 usage ();
274         if (set && (add || rem)) {
275                 fprintf (stderr, _("= is incompatible with - and +\n"));
276                 exit (1);
277         }
278         if ((rf & af) != 0) {
279                 fprintf (stderr, "Can't both set and unset same flag.\n");
280                 exit (1);
281         }
282         if (!(add || rem || set || set_version)) {
283                 fprintf (stderr, _("Must use '-v', =, - or +\n"));
284                 exit (1);
285         }
286         if (verbose)
287                 fprintf (stderr, "chattr %s (%s)\n",
288                          E2FSPROGS_VERSION, E2FSPROGS_DATE);
289         for (j = i; j < argc; j++)
290                 change_attributes (argv[j]);
291         exit(0);
292 }