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