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