Whamcloud - gitweb
Fixx gcc -Wall nitpicks.
[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] [-+=AacDdijsSu] [-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_DIRSYNC_FL, 'D' },
93         { EXT2_APPEND_FL, 'a' },
94         { EXT2_COMPR_FL, 'c' },
95         { EXT2_NODUMP_FL, 'd' },
96         { EXT2_IMMUTABLE_FL, 'i' },
97         { EXT3_JOURNAL_DATA_FL, 'j' },
98         { EXT2_SECRM_FL, 's' },
99         { EXT2_UNRM_FL, 'u' },
100         { EXT2_NOTAIL_FL, 't' },
101         { EXT2_TOPDIR_FL, 'T' },
102         { 0, 0 }
103 };
104
105 static unsigned long get_flag(char c)
106 {
107         const struct flags_char *fp;
108         
109         for (fp = flags_array; fp->flag != 0; fp++) {
110                 if (fp->optchar == c)
111                         return fp->flag;
112         }
113         return 0;
114 }
115
116
117 static int decode_arg (int * i, int argc, char ** argv)
118 {
119         char * p;
120         char * tmp;
121         unsigned long fl;
122
123         switch (argv[*i][0])
124         {
125         case '-':
126                 for (p = &argv[*i][1]; *p; p++) {
127                         if (*p == 'R') {
128                                 recursive = 1;
129                                 continue;
130                         }
131                         if (*p == 'V') {
132                                 verbose = 1;
133                                 continue;
134                         }
135                         if (*p == 'v') {
136                                 (*i)++;
137                                 if (*i >= argc)
138                                         usage ();
139                                 version = strtol (argv[*i], &tmp, 0);
140                                 if (*tmp) {
141                                         com_err (program_name, 0,
142                                                  _("bad version - %s\n"), 
143                                                  argv[*i]);
144                                         usage ();
145                                 }
146                                 set_version = 1;
147                                 continue;
148                         }
149                         if ((fl = get_flag(*p)) == 0)
150                                 usage();
151                         rf |= fl;
152                         rem = 1;
153                 }
154                 break;
155         case '+':
156                 add = 1;
157                 for (p = &argv[*i][1]; *p; p++) {
158                         if ((fl = get_flag(*p)) == 0)
159                                 usage();
160                         af |= fl;
161                 }
162                 break;
163         case '=':
164                 set = 1;
165                 for (p = &argv[*i][1]; *p; p++) {
166                         if ((fl = get_flag(*p)) == 0)
167                                 usage();
168                         sf |= fl;
169                 }
170                 break;
171         default:
172                 return EOF;
173                 break;
174         }
175         return 1;
176 }
177
178 static int chattr_dir_proc (const char *, struct dirent *, void *);
179
180 static void change_attributes (const char * name)
181 {
182         unsigned long flags;
183         STRUCT_STAT     st;
184
185         if (LSTAT (name, &st) == -1) {
186                 com_err (program_name, errno, _("while trying to stat %s"), 
187                          name);
188                 return;
189         }
190         if (S_ISLNK(st.st_mode) && recursive)
191                 return;
192
193         /* Don't try to open device files, fifos etc.  We probably
194            ought to display an error if the file was explicitly given
195            on the command line (whether or not recursive was
196            requested).  */
197         if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) &&
198             !S_ISDIR(st.st_mode))
199                 return;
200
201         if (set) {
202                 if (verbose) {
203                         printf (_("Flags of %s set as "), name);
204                         print_flags (stdout, sf, 0);
205                         printf ("\n");
206                 }
207                 if (fsetflags (name, sf) == -1)
208                         perror (name);
209         } else {
210                 if (fgetflags (name, &flags) == -1)
211                         com_err (program_name, errno,
212                                  _("while reading flags on %s"), name);
213                 else {
214                         if (rem)
215                                 flags &= ~rf;
216                         if (add)
217                                 flags |= af;
218                         if (verbose) {
219                                 printf (_("Flags of %s set as "), name);
220                                 print_flags (stdout, flags, 0);
221                                 printf ("\n");
222                         }
223                         if (!S_ISDIR(st.st_mode))
224                                 flags &= ~EXT2_DIRSYNC_FL;
225                         if (fsetflags (name, flags) == -1)
226                                 com_err (program_name, errno,
227                                          _("while setting flags on %s"), name);
228                 }
229         }
230         if (set_version) {
231                 if (verbose)
232                         printf (_("Version of %s set as %lu\n"), name, version);
233                 if (fsetversion (name, version) == -1)
234                         com_err (program_name, errno,
235                                  _("while setting version on %s"), name);
236         }
237         if (S_ISDIR(st.st_mode) && recursive)
238                 iterate_on_dir (name, chattr_dir_proc, NULL);
239 }
240
241 static int chattr_dir_proc (const char * dir_name, struct dirent * de,
242                             void * unused_private)
243 {
244         if (strcmp (de->d_name, ".") && strcmp (de->d_name, "..")) {
245                 char *path;
246
247                 path = malloc(strlen (dir_name) + 1 + strlen (de->d_name) + 1);
248                 if (!path)
249                         fatal_error(_("Couldn't allocate path variable "
250                                     "in chattr_dir_proc"), 1);
251                 sprintf (path, "%s/%s", dir_name, de->d_name);
252                 change_attributes (path);
253                 free(path);
254         }
255         return 0;
256 }
257
258 int main (int argc, char ** argv)
259 {
260         int i, j;
261         int end_arg = 0;
262
263 #ifdef ENABLE_NLS
264         setlocale(LC_MESSAGES, "");
265         setlocale(LC_CTYPE, "");
266         bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
267         textdomain(NLS_CAT_NAME);
268 #endif
269         if (argc && *argv)
270                 program_name = *argv;
271         i = 1;
272         while (i < argc && !end_arg) {
273                 if (decode_arg (&i, argc, argv) == EOF)
274                         end_arg = 1;
275                 else
276                         i++;
277         }
278         if (i >= argc)
279                 usage ();
280         if (set && (add || rem)) {
281                 fprintf (stderr, _("= is incompatible with - and +\n"));
282                 exit (1);
283         }
284         if ((rf & af) != 0) {
285                 fprintf (stderr, "Can't both set and unset same flag.\n");
286                 exit (1);
287         }
288         if (!(add || rem || set || set_version)) {
289                 fprintf (stderr, _("Must use '-v', =, - or +\n"));
290                 exit (1);
291         }
292         if (verbose)
293                 fprintf (stderr, "chattr %s (%s)\n",
294                          E2FSPROGS_VERSION, E2FSPROGS_DATE);
295         for (j = i; j < argc; j++)
296                 change_attributes (argv[j]);
297         exit(0);
298 }