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