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