Michael Opdenacker contributed a readahead applet.
[oweals/busybox.git] / e2fsprogs / chattr.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * chattr.c             - Change file attributes on an ext2 file system
4  *
5  * Copyright (C) 1993, 1994  Remy Card <card@masi.ibp.fr>
6  *                           Laboratoire MASI, Institut Blaise Pascal
7  *                           Universite Pierre et Marie Curie (Paris VI)
8  *
9  * This file can be redistributed under the terms of the GNU General
10  * Public License
11  */
12
13 /*
14  * History:
15  * 93/10/30     - Creation
16  * 93/11/13     - Replace stat() calls by lstat() to avoid loops
17  * 94/02/27     - Integrated in Ted's distribution
18  * 98/12/29     - Ignore symlinks when working recursively (G M Sipe)
19  * 98/12/29     - Display version info only when -V specified (G M Sipe)
20  */
21
22 #include <sys/types.h>
23 #include <dirent.h>
24 #include <fcntl.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include "ext2fs/ext2_fs.h"
33
34 #ifdef __GNUC__
35 # define EXT2FS_ATTR(x) __attribute__(x)
36 #else
37 # define EXT2FS_ATTR(x)
38 #endif
39
40 #include "e2fsbb.h"
41 #include "e2p/e2p.h"
42
43 #define OPT_ADD 1
44 #define OPT_REM 2
45 #define OPT_SET 4
46 #define OPT_SET_VER 8
47 static int flags;
48 static int recursive;
49
50 static unsigned long version;
51
52 static unsigned long af;
53 static unsigned long rf;
54 static unsigned long sf;
55
56 #ifdef CONFIG_LFS
57 # define LSTAT lstat64
58 # define STRUCT_STAT struct stat64
59 #else
60 # define LSTAT lstat
61 # define STRUCT_STAT struct stat
62 #endif
63
64 struct flags_char {
65         unsigned long flag;
66         char optchar;
67 };
68
69 static const struct flags_char flags_array[] = {
70         { EXT2_NOATIME_FL,      'A' },
71         { EXT2_SYNC_FL,         'S' },
72         { EXT2_DIRSYNC_FL,      'D' },
73         { EXT2_APPEND_FL,       'a' },
74         { EXT2_COMPR_FL,        'c' },
75         { EXT2_NODUMP_FL,       'd' },
76         { EXT2_IMMUTABLE_FL,    'i' },
77         { EXT3_JOURNAL_DATA_FL, 'j' },
78         { EXT2_SECRM_FL,        's' },
79         { EXT2_UNRM_FL,         'u' },
80         { EXT2_NOTAIL_FL,       't' },
81         { EXT2_TOPDIR_FL,       'T' },
82         { 0, 0 }
83 };
84
85 static unsigned long get_flag(char c)
86 {
87         const struct flags_char *fp;
88         for (fp = flags_array; fp->flag; fp++)
89                 if (fp->optchar == c)
90                         return fp->flag;
91         bb_show_usage();
92         return 0;
93 }
94
95 static int decode_arg(char *arg)
96 {
97         unsigned long *fl;
98         char opt = *arg++;
99
100         if (opt == '-') {
101                 flags |= OPT_REM;
102                 fl = &rf;
103         } else if (opt == '+') {
104                 flags |= OPT_ADD;
105                 fl = &af;
106         } else if (opt == '=') {
107                 flags |= OPT_SET;
108                 fl = &sf;
109         } else
110                 return EOF;
111
112         for (; *arg ; ++arg)
113                 (*fl) |= get_flag(*arg);
114
115         return 1;
116 }
117
118 static int chattr_dir_proc(const char *, struct dirent *, void *);
119
120 static void change_attributes(const char * name)
121 {
122         unsigned long fsflags;
123         STRUCT_STAT     st;
124
125         if (LSTAT(name, &st) == -1) {
126                 bb_error_msg("stat %s failed", name);
127                 return;
128         }
129         if (S_ISLNK(st.st_mode) && recursive)
130                 return;
131
132         /* Don't try to open device files, fifos etc.  We probably
133          * ought to display an error if the file was explicitly given
134          * on the command line (whether or not recursive was
135          * requested).  */
136         if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
137                 return;
138
139         if (flags & OPT_SET_VER)
140                 if (fsetversion(name, version) == -1)
141                         bb_error_msg("setting version on %s", name);
142
143         if (flags & OPT_SET) {
144                 fsflags = sf;
145         } else {
146                 if (fgetflags(name, &fsflags) == -1) {
147                         bb_error_msg("reading flags on %s", name);
148                         goto skip_setflags;
149                 }
150                 if (flags & OPT_REM)
151                         fsflags &= ~rf;
152                 if (flags & OPT_ADD)
153                         fsflags |= af;
154                 if (!S_ISDIR(st.st_mode))
155                         fsflags &= ~EXT2_DIRSYNC_FL;
156         }
157         if (fsetflags(name, fsflags) == -1)
158                 bb_error_msg("setting flags on %s", name);
159
160 skip_setflags:
161         if (S_ISDIR(st.st_mode) && recursive)
162                 iterate_on_dir(name, chattr_dir_proc, NULL);
163 }
164
165 static int chattr_dir_proc(const char *dir_name, struct dirent *de,
166                            void *private EXT2FS_ATTR((unused)))
167 {
168         /*if (strcmp(de->d_name, ".") && strcmp(de->d_name, "..")) {*/
169         if (de->d_name[0] == '.' && (de->d_name[1] == '\0' || \
170            (de->d_name[1] == '.' && de->d_name[2] == '\0'))) {
171                 char *path = concat_subpath_file(dir_name, de->d_name);
172                 if (path) {
173                         change_attributes(path);
174                         free(path);
175                 }
176         }
177         return 0;
178 }
179
180 int chattr_main(int argc, char **argv)
181 {
182         int i;
183         char *arg;
184
185         /* parse the args */
186         for (i = 1; i < argc; ++i) {
187                 arg = argv[i];
188
189                 /* take care of -R and -v <version> */
190                 if (arg[0] == '-') {
191                         if (arg[1] == 'R' && arg[2] == '\0') {
192                                 recursive = 1;
193                                 continue;
194                         } else if (arg[1] == 'v' && arg[2] == '\0') {
195                                 char *tmp;
196                                 ++i;
197                                 if (i >= argc)
198                                         bb_show_usage();
199                                 version = strtol(argv[i], &tmp, 0);
200                                 if (*tmp)
201                                         bb_error_msg_and_die("bad version '%s'", arg);
202                                 flags |= OPT_SET_VER;
203                                 continue;
204                         }
205                 }
206
207                 if (decode_arg(arg) == EOF)
208                         break;
209         }
210
211         /* run sanity checks on all the arguments given us */
212         if (i >= argc)
213                 bb_show_usage();
214         if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM)))
215                 bb_error_msg_and_die("= is incompatible with - and +");
216         if ((rf & af) != 0)
217                 bb_error_msg_and_die("Can't set and unset a flag");
218         if (!flags)
219                 bb_error_msg_and_die("Must use '-v', =, - or +");
220
221         /* now run chattr on all the files passed to us */
222         while (i < argc)
223                 change_attributes(argv[i++]);
224
225         return EXIT_SUCCESS;
226 }