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 // Set file flags on a Linux second extended file system.
    190 static inline int ext2_setflag(int fd, struct stat *sb, unsigned long flag)
    191 {
    192   if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) {
    193     errno = EOPNOTSUPP;
    194     return -1;
    195   }
    196   return (ioctl(fd, FS_IOC_SETFLAGS, (void*)&flag));
    197 }
    198 
    199 static unsigned long get_flag_val(char ch)
    200 {
    201   struct ext2_attr *ptr = e2attrs;
    202 
    203   for (; ptr->name; ptr++)
    204     if (ptr->opt == ch) return ptr->flag;
    205   help_exit("bad '%c'", ch);
    206 }
    207 
    208 // Parse command line argument and fill the chattr structure.
    209 static void parse_cmdline_arg(char ***argv)
    210 {
    211   char *arg = **argv, *ptr = NULL;
    212 
    213   while (arg) {
    214     switch (arg[0]) {
    215       case '-':
    216         for (ptr = ++arg; *ptr; ptr++) {
    217           if (*ptr == 'R') {
    218             chattr.recursive = 1;
    219             continue;
    220           } else if (*ptr == 'v') {// get version from next argv.
    221             char *endptr;
    222 
    223             errno = 0;
    224             arg = *(*argv += 1);
    225             if (!arg) help_exit("bad -v");
    226             if (*arg == '-') perror_exit("Invalid Number '%s'", arg);
    227             chattr.version = strtoul(arg, &endptr, 0);
    228             if (errno || *endptr) perror_exit("bad version '%s'", arg);
    229             chattr.vflag = 1;
    230             continue;
    231           } else chattr.rm |= get_flag_val(*ptr);
    232         }
    233         break;
    234       case '+':
    235         for (ptr = ++arg; *ptr; ptr++)
    236           chattr.add |= get_flag_val(*ptr);
    237         break;
    238       case '=':
    239         for (ptr = ++arg; *ptr; ptr++)
    240           chattr.set |= get_flag_val(*ptr);
    241         break;
    242       default: return;
    243     }
    244     arg = *(*argv += 1);
    245   }
    246 }
    247 
    248 // Update attribute of given file.
    249 static int update_attr(struct dirtree *root)
    250 {
    251   unsigned long fval = 0;
    252   char *fpath = NULL;
    253   int fd;
    254 
    255   if (!dirtree_notdotdot(root)) return 0;
    256 
    257   /*
    258    * if file is a link and recursive is set or file is not regular+link+dir
    259    * (like fifo or dev file) then escape the file.
    260    */
    261   if ((S_ISLNK(root->st.st_mode) && chattr.recursive)
    262     || (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode)
    263       && !S_ISDIR(root->st.st_mode)))
    264     return 0;
    265 
    266   fpath = dirtree_path(root, NULL);
    267   if (-1 == (fd=open(fpath, O_RDONLY | O_NONBLOCK))) {
    268     free(fpath);
    269     return DIRTREE_ABORT;
    270   }
    271   // Get current attr of file.
    272   if (ext2_getflag(fd, &(root->st), &fval) < 0) {
    273     perror_msg("read flags of '%s'", fpath);
    274     free(fpath);
    275     xclose(fd);
    276     return DIRTREE_ABORT;
    277   }
    278   if (chattr.set) { // for '=' operator.
    279     if (ext2_setflag(fd, &(root->st), chattr.set) < 0)
    280       perror_msg("setting flags '%s'", fpath);
    281   } else { // for '-' / '+' operator.
    282     fval &= ~(chattr.rm);
    283     fval |= chattr.add;
    284     if (!S_ISDIR(root->st.st_mode)) fval &= ~FS_DIRSYNC_FL;
    285     if (ext2_setflag(fd, &(root->st), fval) < 0)
    286       perror_msg("setting flags '%s'", fpath);
    287   }
    288   if (chattr.vflag) { // set file version
    289     if (ioctl(fd, FS_IOC_SETVERSION, (void*)&chattr.version) < 0)
    290       perror_msg("while setting version on '%s'", fpath);
    291   }
    292   free(fpath);
    293   xclose(fd);
    294 
    295   if (S_ISDIR(root->st.st_mode) && chattr.recursive) return DIRTREE_RECURSE;
    296   return 0;
    297 }
    298 
    299 void chattr_main(void)
    300 {
    301   char **argv = toys.optargs;
    302 
    303   memset(&chattr, 0, sizeof(struct _chattr));
    304   parse_cmdline_arg(&argv);
    305   if (!*argv) help_exit("no file");
    306   if (chattr.set && (chattr.add || chattr.rm))
    307     error_exit("no '=' with '-' or '+'");
    308   if (chattr.rm & chattr.add) error_exit("set/unset same flag");
    309   if (!(chattr.add || chattr.rm || chattr.set || chattr.vflag))
    310     error_exit("need '-v', '=', '-' or '+'");
    311   for (; *argv; argv++) dirtree_read(*argv, update_attr);
    312   toys.exitval = 0; //always set success at this point.
    313 }
    314