Start 1.33.0 development cycle
[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 //config:config CHATTR
13 //config:       bool "chattr (3.8 kb)"
14 //config:       default y
15 //config:       help
16 //config:       chattr changes the file attributes on a second extended file system.
17
18 //applet:IF_CHATTR(APPLET_NOEXEC(chattr, chattr, BB_DIR_BIN, BB_SUID_DROP, chattr))
19
20 //kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
21
22 //usage:#define chattr_trivial_usage
23 //usage:       "[-R] [-v VERSION] [-+=AacDdijsStTu] FILE..."
24 //usage:#define chattr_full_usage "\n\n"
25 //usage:       "Change ext2 file attributes\n"
26 //usage:     "\n        -R      Recurse"
27 //usage:     "\n        -v VER  Set version/generation number"
28 //-V, -f accepted but ignored
29 //usage:     "\nModifiers:"
30 //usage:     "\n        -,+,=   Remove/add/set attributes"
31 //usage:     "\nAttributes:"
32 //usage:     "\n        A       Don't track atime"
33 //usage:     "\n        a       Append mode only"
34 //usage:     "\n        c       Enable compress"
35 //usage:     "\n        D       Write dir contents synchronously"
36 //usage:     "\n        d       Don't backup with dump"
37 //usage:     "\n        i       Cannot be modified (immutable)"
38 //usage:     "\n        j       Write all data to journal first"
39 //usage:     "\n        s       Zero disk storage when deleted"
40 //usage:     "\n        S       Write synchronously"
41 //usage:     "\n        t       Disable tail-merging of partial blocks with other files"
42 //usage:     "\n        u       Allow file to be undeleted"
43
44 #include "libbb.h"
45 #include "e2fs_lib.h"
46
47 #define OPT_ADD 1
48 #define OPT_REM 2
49 #define OPT_SET 4
50 #define OPT_SET_VER 8
51
52 struct globals {
53         unsigned long version;
54         unsigned long af;
55         unsigned long rf;
56         int flags;
57         smallint recursive;
58 };
59
60 static unsigned long get_flag(char c)
61 {
62         const char *fp = strchr(e2attr_flags_sname_chattr, c);
63         if (fp)
64                 return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr];
65         bb_show_usage();
66 }
67
68 static char** decode_arg(char **argv, struct globals *gp)
69 {
70         unsigned long *fl;
71         const char *arg = *argv;
72         char opt = *arg;
73
74         fl = &gp->af;
75         if (opt == '-') {
76                 gp->flags |= OPT_REM;
77                 fl = &gp->rf;
78         } else if (opt == '+') {
79                 gp->flags |= OPT_ADD;
80         } else { /* if (opt == '=') */
81                 gp->flags |= OPT_SET;
82         }
83
84         while (*++arg) {
85                 if (opt == '-') {
86 //e2fsprogs-1.43.1 accepts:
87 // "-RRR", "-RRRv VER" and even "-ARRRva VER" and "-vvv V1 V2 V3"
88 // but not "-vVER".
89 // IOW: options are parsed as part of "remove attrs" strings,
90 // if "v" is seen, next argv[] is VER, even if more opts/attrs follow in this argv[]!
91                         if (*arg == 'R') {
92                                 gp->recursive = 1;
93                                 continue;
94                         }
95                         if (*arg == 'V') {
96                                 /*"verbose and print program version" (nop for now) */;
97                                 continue;
98                         }
99                         if (*arg == 'f') {
100                                 /*"suppress most error messages" (nop) */;
101                                 continue;
102                         }
103                         if (*arg == 'v') {
104                                 if (!*++argv)
105                                         bb_show_usage();
106                                 gp->version = xatoul(*argv);
107                                 gp->flags |= OPT_SET_VER;
108                                 continue;
109                         }
110 //TODO: "-p PROJECT_NUM" ?
111                         /* not a known option, try as an attribute */
112                 }
113                 *fl |= get_flag(*arg);
114         }
115
116         return argv;
117 }
118
119 static void change_attributes(const char *name, struct globals *gp);
120
121 static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
122 {
123         char *path = concat_subpath_file(dir_name, de->d_name);
124         /* path is NULL if de->d_name is "." or "..", else... */
125         if (path) {
126                 change_attributes(path, gp);
127                 free(path);
128         }
129         return 0;
130 }
131
132 static void change_attributes(const char *name, struct globals *gp)
133 {
134         unsigned long fsflags;
135         struct stat st;
136
137         if (lstat(name, &st) != 0) {
138                 bb_perror_msg("stat %s", name);
139                 return;
140         }
141         if (S_ISLNK(st.st_mode) && gp->recursive)
142                 return;
143
144         /* Don't try to open device files, fifos etc.  We probably
145          * ought to display an error if the file was explicitly given
146          * on the command line (whether or not recursive was
147          * requested).  */
148         if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
149                 return;
150
151         if (gp->flags & OPT_SET_VER)
152                 if (fsetversion(name, gp->version) != 0)
153                         bb_perror_msg("setting version on %s", name);
154
155         if (gp->flags & OPT_SET) {
156                 fsflags = gp->af;
157         } else {
158                 if (fgetflags(name, &fsflags) != 0) {
159                         bb_perror_msg("reading flags on %s", name);
160                         goto skip_setflags;
161                 }
162                 /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
163                         fsflags &= ~gp->rf;
164                 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
165                         fsflags |= gp->af;
166 // What is this? And why it's not done for SET case?
167                 if (!S_ISDIR(st.st_mode))
168                         fsflags &= ~EXT2_DIRSYNC_FL;
169         }
170         if (fsetflags(name, fsflags) != 0)
171                 bb_perror_msg("setting flags on %s", name);
172
173  skip_setflags:
174         if (gp->recursive && S_ISDIR(st.st_mode))
175                 iterate_on_dir(name, chattr_dir_proc, gp);
176 }
177
178 int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
179 int chattr_main(int argc UNUSED_PARAM, char **argv)
180 {
181         struct globals g;
182
183         memset(&g, 0, sizeof(g));
184
185         /* parse the args */
186         for (;;) {
187                 char *arg = *++argv;
188                 if (!arg)
189                         bb_show_usage();
190                 if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=')
191                         break;
192
193                 argv = decode_arg(argv, &g);
194         }
195         /* note: on loop exit, remaining argv[] is never empty */
196
197         /* run sanity checks on all the arguments given us */
198         if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
199                 bb_simple_error_msg_and_die("= is incompatible with - and +");
200         if (g.rf & g.af)
201                 bb_simple_error_msg_and_die("can't set and unset a flag");
202         if (!g.flags)
203                 bb_simple_error_msg_and_die("must use '-v', =, - or +");
204
205         /* now run chattr on all the files passed to us */
206         do change_attributes(*argv, &g); while (*++argv);
207
208         return EXIT_SUCCESS;
209 }