Home | History | Annotate | Download | only in other
      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