35424f842cf386cff2e05bbad2f15dc2a830f633
[platform/upstream/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 "libbb.h"
23 #include "e2fs_lib.h"
24
25 #define OPT_ADD 1
26 #define OPT_REM 2
27 #define OPT_SET 4
28 #define OPT_SET_VER 8
29
30 struct globals {
31         unsigned long version;
32         unsigned long af;
33         unsigned long rf;
34         smallint flags;
35         smallint recursive;
36 };
37
38 static unsigned long get_flag(char c)
39 {
40         /* Two separate vectors take less space than vector of structs */
41         static const char flags_letter[] ALIGN1 = "ASDacdijsutT";
42         static const unsigned long flags_val[] = {
43                 /* A */ EXT2_NOATIME_FL,
44                 /* S */ EXT2_SYNC_FL,
45                 /* D */ EXT2_DIRSYNC_FL,
46                 /* a */ EXT2_APPEND_FL,
47                 /* c */ EXT2_COMPR_FL,
48                 /* d */ EXT2_NODUMP_FL,
49                 /* i */ EXT2_IMMUTABLE_FL,
50                 /* j */ EXT3_JOURNAL_DATA_FL,
51                 /* s */ EXT2_SECRM_FL,
52                 /* u */ EXT2_UNRM_FL,
53                 /* t */ EXT2_NOTAIL_FL,
54                 /* T */ EXT2_TOPDIR_FL,
55         };
56         const char *fp;
57
58         for (fp = flags_letter; *fp; fp++)
59                 if (*fp == c)
60                         return flags_val[fp - flags_letter];
61         bb_show_usage();
62 }
63
64 static int decode_arg(const char *arg, struct globals *gp)
65 {
66         unsigned long *fl;
67         char opt = *arg++;
68
69         fl = &gp->af;
70         if (opt == '-') {
71                 gp->flags |= OPT_REM;
72                 fl = &gp->rf;
73         } else if (opt == '+') {
74                 gp->flags |= OPT_ADD;
75         } else if (opt == '=') {
76                 gp->flags |= OPT_SET;
77         } else
78                 return 0;
79
80         while (*arg)
81                 *fl |= get_flag(*arg++);
82
83         return 1;
84 }
85
86 static void change_attributes(const char *name, struct globals *gp);
87
88 static int chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
89 {
90         char *path = concat_subpath_file(dir_name, de->d_name);
91         /* path is NULL if de->d_name is "." or "..", else... */
92         if (path) {
93                 change_attributes(path, gp);
94                 free(path);
95         }
96         return 0;
97 }
98
99 static void change_attributes(const char *name, struct globals *gp)
100 {
101         unsigned long fsflags;
102         struct stat st;
103
104         if (lstat(name, &st) != 0) {
105                 bb_perror_msg("stat %s", name);
106                 return;
107         }
108         if (S_ISLNK(st.st_mode) && gp->recursive)
109                 return;
110
111         /* Don't try to open device files, fifos etc.  We probably
112          * ought to display an error if the file was explicitly given
113          * on the command line (whether or not recursive was
114          * requested).  */
115         if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
116                 return;
117
118         if (gp->flags & OPT_SET_VER)
119                 if (fsetversion(name, gp->version) != 0)
120                         bb_perror_msg("setting version on %s", name);
121
122         if (gp->flags & OPT_SET) {
123                 fsflags = gp->af;
124         } else {
125                 if (fgetflags(name, &fsflags) != 0) {
126                         bb_perror_msg("reading flags on %s", name);
127                         goto skip_setflags;
128                 }
129                 /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
130                         fsflags &= ~gp->rf;
131                 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
132                         fsflags |= gp->af;
133                 /* What is this? And why it's not done for SET case? */
134                 if (!S_ISDIR(st.st_mode))
135                         fsflags &= ~EXT2_DIRSYNC_FL;
136         }
137         if (fsetflags(name, fsflags) != 0)
138                 bb_perror_msg("setting flags on %s", name);
139
140  skip_setflags:
141         if (gp->recursive && S_ISDIR(st.st_mode))
142                 iterate_on_dir(name, chattr_dir_proc, gp);
143 }
144
145 int chattr_main(int argc, char **argv);
146 int chattr_main(int argc, char **argv)
147 {
148         struct globals g;
149         char *arg;
150
151         memset(&g, 0, sizeof(g));
152
153         /* parse the args */
154         while ((arg = *++argv)) {
155                 /* take care of -R and -v <version> */
156                 if (arg[0] == '-'
157                  && (arg[1] == 'R' || arg[1] == 'v')
158                  && !arg[2]
159                 ) {
160                         if (arg[1] == 'R') {
161                                 g.recursive = 1;
162                                 continue;
163                         }
164                         /* arg[1] == 'v' */
165                         if (!*++argv)
166                                 bb_show_usage();
167                         g.version = xatoul(*argv);
168                         g.flags |= OPT_SET_VER;
169                         continue;
170                 }
171
172                 if (!decode_arg(arg, &g))
173                         break;
174         }
175
176         /* run sanity checks on all the arguments given us */
177         if (!*argv)
178                 bb_show_usage();
179         if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
180                 bb_error_msg_and_die("= is incompatible with - and +");
181         if (g.rf & g.af)
182                 bb_error_msg_and_die("can't set and unset a flag");
183         if (!g.flags)
184                 bb_error_msg_and_die("must use '-v', =, - or +");
185
186         /* now run chattr on all the files passed to us */
187         do change_attributes(*argv, &g); while (*++argv);
188
189         return EXIT_SUCCESS;
190 }