chattr.c revision 36caf25f8d61eb8ffddc9895463bce5807e96808
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#include <sys/types.h>
22#include <dirent.h>
23#include <fcntl.h>
24#include <stdio.h>
25#include <stdlib.h>
26#include <unistd.h>
27#include <string.h>
28#ifdef HAVE_ERRNO_H
29#include <errno.h>
30#endif
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <linux/ext2_fs.h>
34
35#ifndef S_ISLNK			/* So we can compile even with gcc-warn */
36# ifdef __S_IFLNK
37#  define S_ISLNK(mode)	 __S_ISTYPE((mode), __S_IFLNK)
38# else
39#  define S_ISLNK(mode)  0
40# endif
41#endif
42
43#include "et/com_err.h"
44#include "e2p/e2p.h"
45
46#include "../version.h"
47
48const char * program_name = "chattr";
49
50int add = 0;
51int rem = 0;
52int set = 0;
53int set_version = 0;
54
55unsigned long version;
56
57int recursive = 0;
58int verbose = 0;
59
60unsigned long af;
61unsigned long rf;
62unsigned long sf;
63
64static void fatal_error(const char * fmt_string, int errcode)
65{
66	fprintf (stderr, fmt_string, program_name);
67	exit (errcode);
68}
69
70#define usage() fatal_error("usage: %s [-RV] [-+=AacdisSu] [-v version] files...\n", \
71			     1)
72
73static int decode_arg (int * i, int argc, char ** argv)
74{
75	char * p;
76	char * tmp;
77
78	switch (argv[*i][0])
79	{
80	case '-':
81		for (p = &argv[*i][1]; *p; p++)
82			switch (*p)
83			{
84			case 'R':
85				recursive = 1;
86				break;
87			case 'S':
88				rf |= EXT2_SYNC_FL;
89				rem = 1;
90				break;
91			case 'V':
92				verbose = 1;
93				break;
94#ifdef	EXT2_APPEND_FL
95			case 'a':
96				rf |= EXT2_APPEND_FL;
97				rem = 1;
98				break;
99#endif
100#ifdef EXT2_NOATIME_FL
101			case 'A':
102				rf |= EXT2_NOATIME_FL;
103				rem = 1;
104				break;
105#endif
106			case 'c':
107				rf |= EXT2_COMPR_FL;
108				rem = 1;
109				break;
110#ifdef	EXT2_NODUMP_FL
111			case 'd':
112				rf |= EXT2_NODUMP_FL;
113				rem = 1;
114				break;
115#endif
116#ifdef	EXT2_IMMUTABLE_FL
117			case 'i':
118				rf |= EXT2_IMMUTABLE_FL;
119				rem = 1;
120				break;
121#endif
122			case 's':
123				rf |= EXT2_SECRM_FL;
124				rem = 1;
125				break;
126			case 'u':
127				rf |= EXT2_UNRM_FL;
128				rem = 1;
129				break;
130			case 'v':
131				(*i)++;
132				if (*i >= argc)
133					usage ();
134				version = strtol (argv[*i], &tmp, 0);
135				if (*tmp)
136				{
137					com_err (program_name, 0,
138						 "bad version - %s\n", argv[*i]);
139					usage ();
140				}
141				set_version = 1;
142				break;
143			default:
144				fprintf (stderr, "%s: Unrecognized argument: %c\n",
145					 program_name, *p);
146				usage ();
147			}
148		break;
149	case '+':
150		add = 1;
151		for (p = &argv[*i][1]; *p; p++)
152			switch (*p)
153			{
154			case 'S':
155				af |= EXT2_SYNC_FL;
156				break;
157#ifdef	EXT2_APPEND_FL
158			case 'a':
159				af |= EXT2_APPEND_FL;
160				break;
161#endif
162#ifdef EXT2_NOATIME_FL
163			case 'A':
164				af |= EXT2_NOATIME_FL;
165				break;
166#endif
167			case 'c':
168				af |= EXT2_COMPR_FL;
169				break;
170#ifdef	EXT2_NODUMP_FL
171			case 'd':
172				af |= EXT2_NODUMP_FL;
173				break;
174#endif
175#ifdef	EXT2_IMMUTABLE_FL
176			case 'i':
177				af |= EXT2_IMMUTABLE_FL;
178				break;
179#endif
180			case 's':
181				af |= EXT2_SECRM_FL;
182				break;
183			case 'u':
184				af |= EXT2_UNRM_FL;
185				break;
186			default:
187				usage ();
188			}
189		break;
190	case '=':
191		set = 1;
192		for (p = &argv[*i][1]; *p; p++)
193			switch (*p)
194			{
195			case 'S':
196				sf |= EXT2_SYNC_FL;
197				break;
198#ifdef	EXT2_APPEND_FL
199			case 'a':
200				sf |= EXT2_APPEND_FL;
201				break;
202#endif
203#ifdef EXT2_NOATIME_FL
204			case 'A':
205				sf |= EXT2_NOATIME_FL;
206				break;
207#endif
208			case 'c':
209				sf |= EXT2_COMPR_FL;
210				break;
211#ifdef	EXT2_NODUMP_FL
212			case 'd':
213				sf |= EXT2_NODUMP_FL;
214				break;
215#endif
216#ifdef	EXT2_IMMUTABLE_FL
217			case 'i':
218				sf |= EXT2_IMMUTABLE_FL;
219				break;
220#endif
221			case 's':
222				sf |= EXT2_SECRM_FL;
223				break;
224			case 'u':
225				sf |= EXT2_UNRM_FL;
226				break;
227			default:
228				usage ();
229			}
230		break;
231	default:
232		return EOF;
233		break;
234	}
235	return 1;
236}
237
238static int chattr_dir_proc (const char *, struct dirent *, void *);
239
240static void change_attributes (const char * name)
241{
242	unsigned long flags;
243	struct stat st;
244
245	if (lstat (name, &st) == -1)
246	{
247		com_err (program_name, errno, "while stating %s", name);
248		return;
249	}
250	if (S_ISLNK(st.st_mode) && recursive)
251		return;
252	if (set)
253	{
254		if (verbose)
255		{
256			printf ("Flags of %s set as ", name);
257			print_flags (stdout, sf, 0);
258			printf ("\n");
259		}
260		if (fsetflags (name, sf) == -1)
261			perror (name);
262	}
263	else
264	{
265		if (fgetflags (name, &flags) == -1)
266			com_err (program_name, errno,
267			         "while reading flags on %s", name);
268		else
269		{
270			if (rem)
271				flags &= ~rf;
272			if (add)
273				flags |= af;
274			if (verbose)
275			{
276				printf ("Flags of %s set as ", name);
277				print_flags (stdout, flags, 0);
278				printf ("\n");
279			}
280			if (fsetflags (name, flags) == -1)
281				com_err (program_name, errno,
282				         "while setting flags on %s", name);
283		}
284	}
285	if (set_version)
286	{
287		if (verbose)
288			printf ("Version of %s set as %lu\n", name, version);
289		if (fsetversion (name, version) == -1)
290			com_err (program_name, errno,
291			         "while setting version on %s", name);
292	}
293	if (S_ISDIR(st.st_mode) && recursive)
294		iterate_on_dir (name, chattr_dir_proc, (void *) NULL);
295}
296
297static int chattr_dir_proc (const char * dir_name, struct dirent * de, void * private)
298{
299	if (strcmp (de->d_name, ".") && strcmp (de->d_name, ".."))
300	{
301	        char *path;
302
303		path = malloc(strlen (dir_name) + 1 + strlen (de->d_name) + 1);
304		if (!path)
305			fatal_error("Couldn't allocate path variable "
306				    "in chattr_dir_proc", 1);
307		sprintf (path, "%s/%s", dir_name, de->d_name);
308		change_attributes (path);
309		free(path);
310	}
311	return 0;
312}
313
314int main (int argc, char ** argv)
315{
316	int i, j;
317	int end_arg = 0;
318
319	if (argc && *argv)
320		program_name = *argv;
321	i = 1;
322	while (i < argc && !end_arg)
323	{
324		if (decode_arg (&i, argc, argv) == EOF)
325			end_arg = 1;
326		else
327			i++;
328	}
329	if (i >= argc)
330		usage ();
331	if (set && (add || rem))
332	{
333		fprintf (stderr, "= is incompatible with - and +\n");
334		exit (1);
335	}
336	if (!(add || rem || set || set_version))
337	{
338		fprintf (stderr, "Must use '-v', =, - or +\n");
339		exit (1);
340	}
341	if (verbose)
342		fprintf (stderr, "chattr %s, %s for EXT2 FS %s, %s\n",
343			 E2FSPROGS_VERSION, E2FSPROGS_DATE,
344			 EXT2FS_VERSION, EXT2FS_DATE);
345	for (j = i; j < argc; j++)
346		change_attributes (argv[j]);
347	exit(0);
348}
349