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