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