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 <sys/types.h> 23#include <dirent.h> 24#include <fcntl.h> 25#include <stdio.h> 26#include <stdlib.h> 27#include <unistd.h> 28#include <string.h> 29#include <errno.h> 30#include <sys/param.h> 31#include <sys/stat.h> 32#include "ext2fs/ext2_fs.h" 33 34#ifdef __GNUC__ 35# define EXT2FS_ATTR(x) __attribute__(x) 36#else 37# define EXT2FS_ATTR(x) 38#endif 39 40#include "e2fsbb.h" 41#include "e2p/e2p.h" 42 43#define OPT_ADD 1 44#define OPT_REM 2 45#define OPT_SET 4 46#define OPT_SET_VER 8 47static int flags; 48static int recursive; 49 50static unsigned long version; 51 52static unsigned long af; 53static unsigned long rf; 54static unsigned long sf; 55 56struct flags_char { 57 unsigned long flag; 58 char optchar; 59}; 60 61static const struct flags_char flags_array[] = { 62 { EXT2_NOATIME_FL, 'A' }, 63 { EXT2_SYNC_FL, 'S' }, 64 { EXT2_DIRSYNC_FL, 'D' }, 65 { EXT2_APPEND_FL, 'a' }, 66 { EXT2_COMPR_FL, 'c' }, 67 { EXT2_NODUMP_FL, 'd' }, 68 { EXT2_IMMUTABLE_FL, 'i' }, 69 { EXT3_JOURNAL_DATA_FL, 'j' }, 70 { EXT2_SECRM_FL, 's' }, 71 { EXT2_UNRM_FL, 'u' }, 72 { EXT2_NOTAIL_FL, 't' }, 73 { EXT2_TOPDIR_FL, 'T' }, 74 { 0, 0 } 75}; 76 77static unsigned long get_flag(char c) 78{ 79 const struct flags_char *fp; 80 for (fp = flags_array; fp->flag; fp++) 81 if (fp->optchar == c) 82 return fp->flag; 83 bb_show_usage(); 84 return 0; 85} 86 87static int decode_arg(char *arg) 88{ 89 unsigned long *fl; 90 char opt = *arg++; 91 92 if (opt == '-') { 93 flags |= OPT_REM; 94 fl = &rf; 95 } else if (opt == '+') { 96 flags |= OPT_ADD; 97 fl = ⁡ 98 } else if (opt == '=') { 99 flags |= OPT_SET; 100 fl = &sf; 101 } else 102 return EOF; 103 104 for (; *arg; ++arg) 105 (*fl) |= get_flag(*arg); 106 107 return 1; 108} 109 110static int chattr_dir_proc(const char *, struct dirent *, void *); 111 112static void change_attributes(const char * name) 113{ 114 unsigned long fsflags; 115 struct stat st; 116 117 if (lstat(name, &st) == -1) { 118 bb_error_msg("stat %s failed", name); 119 return; 120 } 121 if (S_ISLNK(st.st_mode) && recursive) 122 return; 123 124 /* Don't try to open device files, fifos etc. We probably 125 * ought to display an error if the file was explicitly given 126 * on the command line (whether or not recursive was 127 * requested). */ 128 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) 129 return; 130 131 if (flags & OPT_SET_VER) 132 if (fsetversion(name, version) == -1) 133 bb_error_msg("setting version on %s", name); 134 135 if (flags & OPT_SET) { 136 fsflags = sf; 137 } else { 138 if (fgetflags(name, &fsflags) == -1) { 139 bb_error_msg("reading flags on %s", name); 140 goto skip_setflags; 141 } 142 if (flags & OPT_REM) 143 fsflags &= ~rf; 144 if (flags & OPT_ADD) 145 fsflags |= af; 146 if (!S_ISDIR(st.st_mode)) 147 fsflags &= ~EXT2_DIRSYNC_FL; 148 } 149 if (fsetflags(name, fsflags) == -1) 150 bb_error_msg("setting flags on %s", name); 151 152skip_setflags: 153 if (S_ISDIR(st.st_mode) && recursive) 154 iterate_on_dir(name, chattr_dir_proc, NULL); 155} 156 157static int chattr_dir_proc(const char *dir_name, struct dirent *de, 158 void *private EXT2FS_ATTR((unused))) 159{ 160 /*if (strcmp(de->d_name, ".") || strcmp(de->d_name, "..")) {*/ 161 if (de->d_name[0] == '.' 162 && (!de->d_name[1] || (de->d_name[1] == '.' && !de->d_name[2])) 163 ) { 164 char *path = concat_subpath_file(dir_name, de->d_name); 165 if (path) { 166 change_attributes(path); 167 free(path); 168 } 169 } 170 return 0; 171} 172 173int chattr_main(int argc, char **argv); 174int chattr_main(int argc, char **argv) 175{ 176 int i; 177 char *arg; 178 179 /* parse the args */ 180 for (i = 1; i < argc; ++i) { 181 arg = argv[i]; 182 183 /* take care of -R and -v <version> */ 184 if (arg[0] == '-') { 185 if (arg[1] == 'R' && arg[2] == '\0') { 186 recursive = 1; 187 continue; 188 } else if (arg[1] == 'v' && arg[2] == '\0') { 189 char *tmp; 190 ++i; 191 if (i >= argc) 192 bb_show_usage(); 193 version = strtol(argv[i], &tmp, 0); 194 if (*tmp) 195 bb_error_msg_and_die("bad version '%s'", arg); 196 flags |= OPT_SET_VER; 197 continue; 198 } 199 } 200 201 if (decode_arg(arg) == EOF) 202 break; 203 } 204 205 /* run sanity checks on all the arguments given us */ 206 if (i >= argc) 207 bb_show_usage(); 208 if ((flags & OPT_SET) && ((flags & OPT_ADD) || (flags & OPT_REM))) 209 bb_error_msg_and_die("= is incompatible with - and +"); 210 if ((rf & af) != 0) 211 bb_error_msg_and_die("Can't set and unset a flag"); 212 if (!flags) 213 bb_error_msg_and_die("Must use '-v', =, - or +"); 214 215 /* now run chattr on all the files passed to us */ 216 while (i < argc) 217 change_attributes(argv[i++]); 218 219 return EXIT_SUCCESS; 220} 221