busybox/e2fsprogs/chattr.c
<<
>>
Prefs
   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//config:config CHATTR
  13//config:       bool "chattr (3.8 kb)"
  14//config:       default y
  15//config:       help
  16//config:       chattr changes the file attributes on a second extended file system.
  17
  18//applet:IF_CHATTR(APPLET_NOEXEC(chattr, chattr, BB_DIR_BIN, BB_SUID_DROP, chattr))
  19
  20//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
  21
  22//usage:#define chattr_trivial_usage
  23//usage:       "[-R] [-v VERSION] [-+=AacDdijsStTu] FILE..."
  24//usage:#define chattr_full_usage "\n\n"
  25//usage:       "Change ext2 file attributes\n"
  26//usage:     "\n        -R      Recurse"
  27//usage:     "\n        -v VER  Set version/generation number"
  28//-V, -f accepted but ignored
  29//usage:     "\nModifiers:"
  30//usage:     "\n        -,+,=   Remove/add/set attributes"
  31//usage:     "\nAttributes:"
  32//usage:     "\n        A       Don't track atime"
  33//usage:     "\n        a       Append mode only"
  34//usage:     "\n        c       Enable compress"
  35//usage:     "\n        D       Write dir contents synchronously"
  36//usage:     "\n        d       Don't backup with dump"
  37//usage:     "\n        i       Cannot be modified (immutable)"
  38//usage:     "\n        j       Write all data to journal first"
  39//usage:     "\n        s       Zero disk storage when deleted"
  40//usage:     "\n        S       Write synchronously"
  41//usage:     "\n        t       Disable tail-merging of partial blocks with other files"
  42//usage:     "\n        u       Allow file to be undeleted"
  43
  44#include "libbb.h"
  45#include "e2fs_lib.h"
  46
  47#define OPT_ADD 1
  48#define OPT_REM 2
  49#define OPT_SET 4
  50#define OPT_SET_VER 8
  51
  52struct globals {
  53        unsigned long version;
  54        unsigned long af;
  55        unsigned long rf;
  56        int flags;
  57        smallint recursive;
  58};
  59
  60static unsigned long get_flag(char c)
  61{
  62        const char *fp = strchr(e2attr_flags_sname_chattr, c);
  63        if (fp)
  64                return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr];
  65        bb_show_usage();
  66}
  67
  68static char** decode_arg(char **argv, struct globals *gp)
  69{
  70        unsigned long *fl;
  71        const char *arg = *argv;
  72        char opt = *arg;
  73
  74        fl = &gp->af;
  75        if (opt == '-') {
  76                gp->flags |= OPT_REM;
  77                fl = &gp->rf;
  78        } else if (opt == '+') {
  79                gp->flags |= OPT_ADD;
  80        } else { /* if (opt == '=') */
  81                gp->flags |= OPT_SET;
  82        }
  83
  84        while (*++arg) {
  85                if (opt == '-') {
  86//e2fsprogs-1.43.1 accepts:
  87// "-RRR", "-RRRv VER" and even "-ARRRva VER" and "-vvv V1 V2 V3"
  88// but not "-vVER".
  89// IOW: options are parsed as part of "remove attrs" strings,
  90// if "v" is seen, next argv[] is VER, even if more opts/attrs follow in this argv[]!
  91                        if (*arg == 'R') {
  92                                gp->recursive = 1;
  93                                continue;
  94                        }
  95                        if (*arg == 'V') {
  96                                /*"verbose and print program version" (nop for now) */;
  97                                continue;
  98                        }
  99                        if (*arg == 'f') {
 100                                /*"suppress most error messages" (nop) */;
 101                                continue;
 102                        }
 103                        if (*arg == 'v') {
 104                                if (!*++argv)
 105                                        bb_show_usage();
 106                                gp->version = xatoul(*argv);
 107                                gp->flags |= OPT_SET_VER;
 108                                continue;
 109                        }
 110//TODO: "-p PROJECT_NUM" ?
 111                        /* not a known option, try as an attribute */
 112                }
 113                *fl |= get_flag(*arg);
 114        }
 115
 116        return argv;
 117}
 118
 119static void change_attributes(const char *name, struct globals *gp);
 120
 121static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
 122{
 123        char *path = concat_subpath_file(dir_name, de->d_name);
 124        /* path is NULL if de->d_name is "." or "..", else... */
 125        if (path) {
 126                change_attributes(path, gp);
 127                free(path);
 128        }
 129        return 0;
 130}
 131
 132static void change_attributes(const char *name, struct globals *gp)
 133{
 134        unsigned long fsflags;
 135        struct stat st;
 136
 137        if (lstat(name, &st) != 0) {
 138                bb_perror_msg("stat %s", name);
 139                return;
 140        }
 141        if (S_ISLNK(st.st_mode) && gp->recursive)
 142                return;
 143
 144        /* Don't try to open device files, fifos etc.  We probably
 145         * ought to display an error if the file was explicitly given
 146         * on the command line (whether or not recursive was
 147         * requested).  */
 148        if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
 149                return;
 150
 151        if (gp->flags & OPT_SET_VER)
 152                if (fsetversion(name, gp->version) != 0)
 153                        bb_perror_msg("setting version on %s", name);
 154
 155        if (gp->flags & OPT_SET) {
 156                fsflags = gp->af;
 157        } else {
 158                if (fgetflags(name, &fsflags) != 0) {
 159                        bb_perror_msg("reading flags on %s", name);
 160                        goto skip_setflags;
 161                }
 162                /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
 163                        fsflags &= ~gp->rf;
 164                /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
 165                        fsflags |= gp->af;
 166// What is this? And why it's not done for SET case?
 167                if (!S_ISDIR(st.st_mode))
 168                        fsflags &= ~EXT2_DIRSYNC_FL;
 169        }
 170        if (fsetflags(name, fsflags) != 0)
 171                bb_perror_msg("setting flags on %s", name);
 172
 173 skip_setflags:
 174        if (gp->recursive && S_ISDIR(st.st_mode))
 175                iterate_on_dir(name, chattr_dir_proc, gp);
 176}
 177
 178int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 179int chattr_main(int argc UNUSED_PARAM, char **argv)
 180{
 181        struct globals g;
 182
 183        memset(&g, 0, sizeof(g));
 184
 185        /* parse the args */
 186        for (;;) {
 187                char *arg = *++argv;
 188                if (!arg)
 189                        bb_show_usage();
 190                if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=')
 191                        break;
 192
 193                argv = decode_arg(argv, &g);
 194        }
 195        /* note: on loop exit, remaining argv[] is never empty */
 196
 197        /* run sanity checks on all the arguments given us */
 198        if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
 199                bb_error_msg_and_die("= is incompatible with - and +");
 200        if (g.rf & g.af)
 201                bb_error_msg_and_die("can't set and unset a flag");
 202        if (!g.flags)
 203                bb_error_msg_and_die("must use '-v', =, - or +");
 204
 205        /* now run chattr on all the files passed to us */
 206        do change_attributes(*argv, &g); while (*++argv);
 207
 208        return EXIT_SUCCESS;
 209}
 210