1/* vi: set sw=4 ts=4: */ 2/* 3 * chattr.c - Change file attributes on an ext2 file system 4 * 5 * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr> 6 * Laboratoire MASI, Institut Blaise Pascal 7 * Universite Pierre et Marie Curie (Paris VI) 8 * 9 * This file can be redistributed under the terms of the GNU General 10 * Public License 11 */ 12 13/* 14 * History: 15 * 93/10/30 - Creation 16 * 93/11/13 - Replace stat() calls by lstat() to avoid loops 17 * 94/02/27 - Integrated in Ted's distribution 18 * 98/12/29 - Ignore symlinks when working recursively (G M Sipe) 19 * 98/12/29 - Display version info only when -V specified (G M Sipe) 20 */ 21 22#include "libbb.h" 23#include "e2fs_lib.h" 24 25#define OPT_ADD 1 26#define OPT_REM 2 27#define OPT_SET 4 28#define OPT_SET_VER 8 29 30struct globals { 31 unsigned long version; 32 unsigned long af; 33 unsigned long rf; 34 smallint flags; 35 smallint recursive; 36}; 37 38static unsigned long get_flag(char c) 39{ 40 /* Two separate vectors take less space than vector of structs */ 41 static const char flags_letter[] ALIGN1 = "ASDacdijsutT"; 42 static const unsigned long flags_val[] = { 43 /* A */ EXT2_NOATIME_FL, 44 /* S */ EXT2_SYNC_FL, 45 /* D */ EXT2_DIRSYNC_FL, 46 /* a */ EXT2_APPEND_FL, 47 /* c */ EXT2_COMPR_FL, 48 /* d */ EXT2_NODUMP_FL, 49 /* i */ EXT2_IMMUTABLE_FL, 50 /* j */ EXT3_JOURNAL_DATA_FL, 51 /* s */ EXT2_SECRM_FL, 52 /* u */ EXT2_UNRM_FL, 53 /* t */ EXT2_NOTAIL_FL, 54 /* T */ EXT2_TOPDIR_FL, 55 }; 56 const char *fp; 57 58 for (fp = flags_letter; *fp; fp++) 59 if (*fp == c) 60 return flags_val[fp - flags_letter]; 61 bb_show_usage(); 62} 63 64static int decode_arg(const char *arg, struct globals *gp) 65{ 66 unsigned long *fl; 67 char opt = *arg++; 68 69 fl = &gp->af; 70 if (opt == '-') { 71 gp->flags |= OPT_REM; 72 fl = &gp->rf; 73 } else if (opt == '+') { 74 gp->flags |= OPT_ADD; 75 } else if (opt == '=') { 76 gp->flags |= OPT_SET; 77 } else 78 return 0; 79 80 while (*arg) 81 *fl |= get_flag(*arg++); 82 83 return 1; 84} 85 86static void change_attributes(const char *name, struct globals *gp); 87 88static int chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp) 89{ 90 char *path = concat_subpath_file(dir_name, de->d_name); 91 /* path is NULL if de->d_name is "." or "..", else... */ 92 if (path) { 93 change_attributes(path, gp); 94 free(path); 95 } 96 return 0; 97} 98 99static void change_attributes(const char *name, struct globals *gp) 100{ 101 unsigned long fsflags; 102 struct stat st; 103 104 if (lstat(name, &st) != 0) { 105 bb_perror_msg("stat %s", name); 106 return; 107 } 108 if (S_ISLNK(st.st_mode) && gp->recursive) 109 return; 110 111 /* Don't try to open device files, fifos etc. We probably 112 * ought to display an error if the file was explicitly given 113 * on the command line (whether or not recursive was 114 * requested). */ 115 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) 116 return; 117 118 if (gp->flags & OPT_SET_VER) 119 if (fsetversion(name, gp->version) != 0) 120 bb_perror_msg("setting version on %s", name); 121 122 if (gp->flags & OPT_SET) { 123 fsflags = gp->af; 124 } else { 125 if (fgetflags(name, &fsflags) != 0) { 126 bb_perror_msg("reading flags on %s", name); 127 goto skip_setflags; 128 } 129 /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */ 130 fsflags &= ~gp->rf; 131 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */ 132 fsflags |= gp->af; 133 /* What is this? And why it's not done for SET case? */ 134 if (!S_ISDIR(st.st_mode)) 135 fsflags &= ~EXT2_DIRSYNC_FL; 136 } 137 if (fsetflags(name, fsflags) != 0) 138 bb_perror_msg("setting flags on %s", name); 139 140 skip_setflags: 141 if (gp->recursive && S_ISDIR(st.st_mode)) 142 iterate_on_dir(name, chattr_dir_proc, gp); 143} 144 145int chattr_main(int argc, char **argv); 146int chattr_main(int argc, char **argv) 147{ 148 struct globals g; 149 char *arg; 150 151 memset(&g, 0, sizeof(g)); 152 153 /* parse the args */ 154 while ((arg = *++argv)) { 155 /* take care of -R and -v <version> */ 156 if (arg[0] == '-' 157 && (arg[1] == 'R' || arg[1] == 'v') 158 && !arg[2] 159 ) { 160 if (arg[1] == 'R') { 161 g.recursive = 1; 162 continue; 163 } 164 /* arg[1] == 'v' */ 165 if (!*++argv) 166 bb_show_usage(); 167 g.version = xatoul(*argv); 168 g.flags |= OPT_SET_VER; 169 continue; 170 } 171 172 if (!decode_arg(arg, &g)) 173 break; 174 } 175 176 /* run sanity checks on all the arguments given us */ 177 if (!*argv) 178 bb_show_usage(); 179 if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM))) 180 bb_error_msg_and_die("= is incompatible with - and +"); 181 if (g.rf & g.af) 182 bb_error_msg_and_die("can't set and unset a flag"); 183 if (!g.flags) 184 bb_error_msg_and_die("must use '-v', =, - or +"); 185 186 /* now run chattr on all the files passed to us */ 187 do change_attributes(*argv, &g); while (*++argv); 188 189 return EXIT_SUCCESS; 190} 191