1 /* lsattr.c - List file attributes on a Linux second extended file system. 2 * 3 * Copyright 2013 Ranjan Kumar <ranjankumar.bth (at) gmail.com> 4 * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com> 5 * 6 * No Standard. 7 * 8 * TODO cleanup 9 10 USE_LSATTR(NEWTOY(lsattr, "vldaR", TOYFLAG_BIN)) 11 USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN)) 12 13 config LSATTR 14 bool "lsattr" 15 default y 16 help 17 usage: lsattr [-Radlv] [Files...] 18 19 List file attributes on a Linux second extended file system. 20 (AacDdijsStu defined in chattr --help) 21 22 -R Recursively list attributes of directories and their contents 23 -a List all files in directories, including files that start with '.' 24 -d List directories like other files, rather than listing their contents 25 -l List long flag names 26 -v List the file's version/generation number 27 28 config CHATTR 29 bool "chattr" 30 default y 31 help 32 usage: chattr [-R] [-+=AacDdijsStTu] [-v version] [File...] 33 34 Change file attributes on a Linux second extended file system. 35 36 -R Recurse 37 -v Set the file's version/generation number 38 39 Operators: 40 '-' Remove attributes 41 '+' Add attributes 42 '=' Set attributes 43 44 Attributes: 45 A Don't track atime 46 a Append mode only 47 c Enable compress 48 D Write dir contents synchronously 49 d Don't backup with dump 50 i Cannot be modified (immutable) 51 j Write all data to journal first 52 s Zero disk storage when deleted 53 S Write file contents synchronously 54 t Disable tail-merging of partial blocks with other files 55 u Allow file to be undeleted 56 */ 57 #define FOR_lsattr 58 #include "toys.h" 59 #include <linux/fs.h> 60 61 static struct ext2_attr { 62 char *name; 63 unsigned long flag; 64 char opt; 65 } e2attrs[] = { 66 {"Secure_Deletion", FS_SECRM_FL, 's'}, // Secure deletion 67 {"Undelete", FS_UNRM_FL, 'u'}, // Undelete 68 {"Compression_Requested", FS_COMPR_FL, 'c'}, // Compress file 69 {"Synchronous_Updates", FS_SYNC_FL, 'S'}, // Synchronous updates 70 {"Immutable", FS_IMMUTABLE_FL, 'i'}, // Immutable file 71 {"Append_Only", FS_APPEND_FL, 'a'}, // writes to file may only append 72 {"No_Dump", FS_NODUMP_FL, 'd'}, // do not dump file 73 {"No_Atime", FS_NOATIME_FL, 'A'}, // do not update atime 74 {"Indexed_directory", FS_INDEX_FL, 'I'}, // hash-indexed directory 75 {"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'}, // file data should be journaled 76 {"No_Tailmerging", FS_NOTAIL_FL, 't'}, // file tail should not be merged 77 {"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'}, // dirsync behaviour (directories only) 78 {"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'}, // Top of directory hierarchies 79 {NULL, -1, 0}, 80 }; 81 82 // Get file flags on a Linux second extended file system. 83 static int ext2_getflag(int fd, struct stat *sb, unsigned long *flag) 84 { 85 if(!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { 86 errno = EOPNOTSUPP; 87 return -1; 88 } 89 return (ioctl(fd, FS_IOC_GETFLAGS, (void*)flag)); 90 } 91 92 static void print_file_attr(char *path) 93 { 94 unsigned long flag = 0, version = 0; 95 int fd; 96 struct stat sb; 97 98 if (!stat(path, &sb) && !S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) { 99 errno = EOPNOTSUPP; 100 goto LABEL1; 101 } 102 if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto LABEL1; 103 104 if (toys.optflags & FLAG_v) { 105 if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto LABEL2; 106 xprintf("%5lu ", version); 107 } 108 109 if (ext2_getflag(fd, &sb, &flag) < 0) perror_msg("reading flags '%s'", path); 110 else { 111 struct ext2_attr *ptr = e2attrs; 112 113 if (toys.optflags & FLAG_l) { 114 int name_found = 0; 115 116 xprintf("%-50s ", path); 117 for (; ptr->name; ptr++) { 118 if (flag & ptr->flag) { 119 if (name_found) xprintf(", "); //for formatting. 120 xprintf("%s", ptr->name); 121 name_found = 1; 122 } 123 } 124 if (!name_found) xprintf("---"); 125 xputc('\n'); 126 } else { 127 int index = 0; 128 129 for (; ptr->name; ptr++) 130 toybuf[index++] = (flag & ptr->flag) ? ptr->opt : '-'; 131 toybuf[index] = '\0'; 132 xprintf("%s %s\n", toybuf, path); 133 } 134 } 135 xclose(fd); 136 return; 137 LABEL2: xclose(fd); 138 LABEL1: perror_msg("reading '%s'", path); 139 } 140 141 // Get directory information. 142 static int retell_dir(struct dirtree *root) 143 { 144 char *fpath = NULL; 145 146 if (root->again) { 147 xputc('\n'); 148 return 0; 149 } 150 if (S_ISDIR(root->st.st_mode) && !root->parent) 151 return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN); 152 153 fpath = dirtree_path(root, NULL); 154 //Special case: with '-a' option and '.'/'..' also included in printing list. 155 if ((root->name[0] != '.') || (toys.optflags & FLAG_a)) { 156 print_file_attr(fpath); 157 if (S_ISDIR(root->st.st_mode) && (toys.optflags & FLAG_R) 158 && dirtree_notdotdot(root)) { 159 xprintf("\n%s:\n", fpath); 160 free(fpath); 161 return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN); 162 } 163 } 164 free(fpath); 165 return 0; 166 } 167 168 void lsattr_main(void) 169 { 170 if (!*toys.optargs) dirtree_read(".", retell_dir); 171 else 172 for (; *toys.optargs; toys.optargs++) { 173 struct stat sb; 174 175 if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs); 176 else if (S_ISDIR(sb.st_mode) && !(toys.optflags & FLAG_d)) 177 dirtree_read(*toys.optargs, retell_dir); 178 else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir" 179 } 180 } 181 182 // Switch gears from lsattr to chattr. 183 #define CLEANUP_lsattr 184 #define FOR_chattr 185 #include "generated/flags.h" 186 187 static struct _chattr { 188 unsigned long add, rm, set, version; 189 unsigned char vflag, recursive; 190 } chattr; 191 192 // Set file flags on a Linux second extended file system. 193 static inline int ext2_setflag(int fd, struct stat *sb, unsigned long flag) 194 { 195 if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { 196 errno = EOPNOTSUPP; 197 return -1; 198 } 199 return (ioctl(fd, FS_IOC_SETFLAGS, (void*)&flag)); 200 } 201 202 static unsigned long get_flag_val(char ch) 203 { 204 struct ext2_attr *ptr = e2attrs; 205 206 for (; ptr->name; ptr++) 207 if (ptr->opt == ch) return ptr->flag; 208 help_exit("bad '%c'", ch); 209 } 210 211 // Parse command line argument and fill the chattr structure. 212 static void parse_cmdline_arg(char ***argv) 213 { 214 char *arg = **argv, *ptr = NULL; 215 216 while (arg) { 217 switch (arg[0]) { 218 case '-': 219 for (ptr = ++arg; *ptr; ptr++) { 220 if (*ptr == 'R') { 221 chattr.recursive = 1; 222 continue; 223 } else if (*ptr == 'v') {// get version from next argv. 224 char *endptr; 225 226 errno = 0; 227 arg = *(*argv += 1); 228 if (!arg) help_exit("bad -v"); 229 if (*arg == '-') perror_exit("Invalid Number '%s'", arg); 230 chattr.version = strtoul(arg, &endptr, 0); 231 if (errno || *endptr) perror_exit("bad version '%s'", arg); 232 chattr.vflag = 1; 233 continue; 234 } else chattr.rm |= get_flag_val(*ptr); 235 } 236 break; 237 case '+': 238 for (ptr = ++arg; *ptr; ptr++) 239 chattr.add |= get_flag_val(*ptr); 240 break; 241 case '=': 242 for (ptr = ++arg; *ptr; ptr++) 243 chattr.set |= get_flag_val(*ptr); 244 break; 245 default: return; 246 } 247 arg = *(*argv += 1); 248 } 249 } 250 251 // Update attribute of given file. 252 static int update_attr(struct dirtree *root) 253 { 254 unsigned long fval = 0; 255 char *fpath = NULL; 256 int fd; 257 258 if (!dirtree_notdotdot(root)) return 0; 259 260 /* 261 * if file is a link and recursive is set or file is not regular+link+dir 262 * (like fifo or dev file) then escape the file. 263 */ 264 if ((S_ISLNK(root->st.st_mode) && chattr.recursive) 265 || (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode) 266 && !S_ISDIR(root->st.st_mode))) 267 return 0; 268 269 fpath = dirtree_path(root, NULL); 270 if (-1 == (fd=open(fpath, O_RDONLY | O_NONBLOCK))) { 271 free(fpath); 272 return DIRTREE_ABORT; 273 } 274 // Get current attr of file. 275 if (ext2_getflag(fd, &(root->st), &fval) < 0) { 276 perror_msg("read flags of '%s'", fpath); 277 free(fpath); 278 xclose(fd); 279 return DIRTREE_ABORT; 280 } 281 if (chattr.set) { // for '=' operator. 282 if (ext2_setflag(fd, &(root->st), chattr.set) < 0) 283 perror_msg("setting flags '%s'", fpath); 284 } else { // for '-' / '+' operator. 285 fval &= ~(chattr.rm); 286 fval |= chattr.add; 287 if (!S_ISDIR(root->st.st_mode)) fval &= ~FS_DIRSYNC_FL; 288 if (ext2_setflag(fd, &(root->st), fval) < 0) 289 perror_msg("setting flags '%s'", fpath); 290 } 291 if (chattr.vflag) { // set file version 292 if (ioctl(fd, FS_IOC_SETVERSION, (void*)&chattr.version) < 0) 293 perror_msg("while setting version on '%s'", fpath); 294 } 295 free(fpath); 296 xclose(fd); 297 298 if (S_ISDIR(root->st.st_mode) && chattr.recursive) return DIRTREE_RECURSE; 299 return 0; 300 } 301 302 void chattr_main(void) 303 { 304 char **argv = toys.optargs; 305 306 memset(&chattr, 0, sizeof(struct _chattr)); 307 parse_cmdline_arg(&argv); 308 if (!*argv) help_exit("no file"); 309 if (chattr.set && (chattr.add || chattr.rm)) 310 error_exit("no '=' with '-' or '+'"); 311 if (chattr.rm & chattr.add) error_exit("set/unset same flag"); 312 if (!(chattr.add || chattr.rm || chattr.set || chattr.vflag)) 313 error_exit("need '-v', '=', '-' or '+'"); 314 for (; *argv; argv++) dirtree_read(*argv, update_attr); 315 toys.exitval = 0; //always set success at this point. 316 } 317