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