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