Home | History | Annotate | Download | only in misc
      1 /*
      2  * chattr.c		- Change file attributes on an ext2 file system
      3  *
      4  * Copyright (C) 1993, 1994  Remy Card <card (at) 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 
     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 
    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 
    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 
    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 
    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 
    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