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
47static int flags;
48static int recursive;
49
50static unsigned long version;
51
52static unsigned long af;
53static unsigned long rf;
54static unsigned long sf;
55
56struct flags_char {
57	unsigned long flag;
58	char optchar;
59};
60
61static const struct flags_char flags_array[] = {
62	{ EXT2_NOATIME_FL,      'A' },
63	{ EXT2_SYNC_FL,         'S' },
64	{ EXT2_DIRSYNC_FL,      'D' },
65	{ EXT2_APPEND_FL,       'a' },
66	{ EXT2_COMPR_FL,        'c' },
67	{ EXT2_NODUMP_FL,       'd' },
68	{ EXT2_IMMUTABLE_FL,    'i' },
69	{ EXT3_JOURNAL_DATA_FL, 'j' },
70	{ EXT2_SECRM_FL,        's' },
71	{ EXT2_UNRM_FL,         'u' },
72	{ EXT2_NOTAIL_FL,       't' },
73	{ EXT2_TOPDIR_FL,       'T' },
74	{ 0, 0 }
75};
76
77static unsigned long get_flag(char c)
78{
79	const struct flags_char *fp;
80	for (fp = flags_array; fp->flag; fp++)
81		if (fp->optchar == c)
82			return fp->flag;
83	bb_show_usage();
84	return 0;
85}
86
87static int decode_arg(char *arg)
88{
89	unsigned long *fl;
90	char opt = *arg++;
91
92	if (opt == '-') {
93		flags |= OPT_REM;
94		fl = &rf;
95	} else if (opt == '+') {
96		flags |= OPT_ADD;
97		fl = &af;
98	} else if (opt == '=') {
99		flags |= OPT_SET;
100		fl = &sf;
101	} else
102		return EOF;
103
104	for (; *arg; ++arg)
105		(*fl) |= get_flag(*arg);
106
107	return 1;
108}
109
110static int chattr_dir_proc(const char *, struct dirent *, void *);
111
112static void change_attributes(const char * name)
113{
114	unsigned long fsflags;
115	struct stat st;
116
117	if (lstat(name, &st) == -1) {
118		bb_error_msg("stat %s failed", name);
119		return;
120	}
121	if (S_ISLNK(st.st_mode) && recursive)
122		return;
123
124	/* Don't try to open device files, fifos etc.  We probably
125	 * ought to display an error if the file was explicitly given
126	 * on the command line (whether or not recursive was
127	 * requested).  */
128	if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
129		return;
130
131	if (flags & OPT_SET_VER)
132		if (fsetversion(name, version) == -1)
133			bb_error_msg("setting version on %s", name);
134
135	if (flags & OPT_SET) {
136		fsflags = sf;
137	} else {
138		if (fgetflags(name, &fsflags) == -1) {
139			bb_error_msg("reading flags on %s", name);
140			goto skip_setflags;
141		}
142		if (flags & OPT_REM)
143			fsflags &= ~rf;
144		if (flags & OPT_ADD)
145			fsflags |= af;
146		if (!S_ISDIR(st.st_mode))
147			fsflags &= ~EXT2_DIRSYNC_FL;
148	}
149	if (fsetflags(name, fsflags) == -1)
150		bb_error_msg("setting flags on %s", name);
151
152skip_setflags:
153	if (S_ISDIR(st.st_mode) && recursive)
154		iterate_on_dir(name, chattr_dir_proc, NULL);
155}
156
157static int chattr_dir_proc(const char *dir_name, struct dirent *de,
158			   void *private EXT2FS_ATTR((unused)))
159{
160	/*if (strcmp(de->d_name, ".") || strcmp(de->d_name, "..")) {*/
161	if (de->d_name[0] == '.'
162	 && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2]))
163	) {
164		char *path = concat_subpath_file(dir_name, de->d_name);
165		if (path) {
166			change_attributes(path);
167			free(path);
168		}
169	}
170	return 0;
171}
172
173int chattr_main(int argc, char **argv);
174int chattr_main(int argc, char **argv)
175{
176	int i;
177	char *arg;
178
179	/* parse the args */
180	for (i = 1; i < argc; ++i) {
181		arg = argv[i];
182
183		/* take care of -R and -v <version> */
184		if (arg[0] == '-') {
185			if (arg[1] == 'R' && arg[2] == '\0') {
186				recursive = 1;
187				continue;
188			} else if (arg[1] == 'v' && arg[2] == '\0') {
189				char *tmp;
190				++i;
191				if (i >= argc)
192					bb_show_usage();
193				version = strtol(argv[i], &tmp, 0);
194				if (*tmp)
195					bb_error_msg_and_die("bad version '%s'", arg);
196				flags |= OPT_SET_VER;
197				continue;
198			}
199		}
200
201		if (decode_arg(arg) == EOF)
202			break;
203	}
204
205	/* run sanity checks on all the arguments given us */
206	if (i >= argc)
207		bb_show_usage();
208	if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM)))
209		bb_error_msg_and_die("= is incompatible with - and +");
210	if ((rf & af) != 0)
211		bb_error_msg_and_die("Can't set and unset a flag");
212	if (!flags)
213		bb_error_msg_and_die("Must use '-v', =, - or +");
214
215	/* now run chattr on all the files passed to us */
216	while (i < argc)
217		change_attributes(argv[i++]);
218
219	return EXIT_SUCCESS;
220}
221