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