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