blob: 76a5253b62d1b5c0da3973eec0df152c92549e60 [file] [log] [blame]
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +00001/* 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 */
Denys Vlasenko000eda42015-10-18 22:40:23 +020012//config:config CHATTR
Denys Vlasenko4eed2c62017-07-18 22:01:24 +020013//config: bool "chattr (3.2 kb)"
Denys Vlasenko000eda42015-10-18 22:40:23 +020014//config: default y
15//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020016//config: chattr changes the file attributes on a second extended file system.
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000017
Denys Vlasenko99125c02017-08-05 20:38:04 +020018//applet:IF_CHATTR(APPLET_NOEXEC(chattr, chattr, BB_DIR_BIN, BB_SUID_DROP, chattr))
Denys Vlasenko000eda42015-10-18 22:40:23 +020019
20//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000021
Pere Orga6a3e01d2011-04-01 22:56:30 +020022//usage:#define chattr_trivial_usage
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020023//usage: "[-R] [-v VERSION] [-+=AacDdijsStTu] FILE..."
Pere Orga6a3e01d2011-04-01 22:56:30 +020024//usage:#define chattr_full_usage "\n\n"
Denys Vlasenko36647ab2015-10-19 01:29:20 +020025//usage: "Change ext2 file attributes\n"
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020026//usage: "\n -R Recurse"
27//usage: "\n -v VER Set version/generation number"
28//-V, -f accepted but ignored
Pere Orga6a3e01d2011-04-01 22:56:30 +020029//usage: "\nModifiers:"
Denys Vlasenko36647ab2015-10-19 01:29:20 +020030//usage: "\n -,+,= Remove/add/set attributes"
Pere Orga6a3e01d2011-04-01 22:56:30 +020031//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"
Denys Vlasenko36647ab2015-10-19 01:29:20 +020040//usage: "\n S Write synchronously"
Pere Orga6a3e01d2011-04-01 22:56:30 +020041//usage: "\n t Disable tail-merging of partial blocks with other files"
42//usage: "\n u Allow file to be undeleted"
Pere Orga6a3e01d2011-04-01 22:56:30 +020043
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000044#include "libbb.h"
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000045#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
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000051
Denis Vlasenko8acf5212007-04-15 11:48:27 +000052struct globals {
53 unsigned long version;
54 unsigned long af;
55 unsigned long rf;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020056 int flags;
Denis Vlasenko8acf5212007-04-15 11:48:27 +000057 smallint recursive;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000058};
59
60static unsigned long get_flag(char c)
61{
Denis Vlasenkod059ddc2007-10-30 19:36:07 +000062 const char *fp = strchr(e2attr_flags_sname_chattr, c);
63 if (fp)
64 return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr];
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000065 bb_show_usage();
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000066}
67
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020068static char** decode_arg(char **argv, struct globals *gp)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000069{
70 unsigned long *fl;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020071 const char *arg = *argv;
72 char opt = *arg;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000073
Denis Vlasenko8acf5212007-04-15 11:48:27 +000074 fl = &gp->af;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000075 if (opt == '-') {
Denis Vlasenko8acf5212007-04-15 11:48:27 +000076 gp->flags |= OPT_REM;
77 fl = &gp->rf;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000078 } else if (opt == '+') {
Denis Vlasenko8acf5212007-04-15 11:48:27 +000079 gp->flags |= OPT_ADD;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020080 } else { /* if (opt == '=') */
Denis Vlasenko8acf5212007-04-15 11:48:27 +000081 gp->flags |= OPT_SET;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020082 }
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000083
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020084 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 }
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000115
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200116 return argv;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000117}
118
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000119static void change_attributes(const char *name, struct globals *gp);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000120
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +0200121static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000122{
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) {
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000126 change_attributes(path, gp);
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000127 free(path);
128 }
129 return 0;
130}
131
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000132static void change_attributes(const char *name, struct globals *gp)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000133{
134 unsigned long fsflags;
135 struct stat st;
136
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000137 if (lstat(name, &st) != 0) {
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000138 bb_perror_msg("stat %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000139 return;
140 }
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000141 if (S_ISLNK(st.st_mode) && gp->recursive)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000142 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
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000151 if (gp->flags & OPT_SET_VER)
152 if (fsetversion(name, gp->version) != 0)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000153 bb_perror_msg("setting version on %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000154
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000155 if (gp->flags & OPT_SET) {
156 fsflags = gp->af;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000157 } else {
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000158 if (fgetflags(name, &fsflags) != 0) {
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000159 bb_perror_msg("reading flags on %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000160 goto skip_setflags;
161 }
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000162 /*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;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200166// What is this? And why it's not done for SET case?
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000167 if (!S_ISDIR(st.st_mode))
168 fsflags &= ~EXT2_DIRSYNC_FL;
169 }
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000170 if (fsetflags(name, fsflags) != 0)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000171 bb_perror_msg("setting flags on %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000172
173 skip_setflags:
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000174 if (gp->recursive && S_ISDIR(st.st_mode))
175 iterate_on_dir(name, chattr_dir_proc, gp);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000176}
177
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000178int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000179int chattr_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000180{
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000181 struct globals g;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000182
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000183 memset(&g, 0, sizeof(g));
184
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000185 /* parse the args */
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200186 for (;;) {
187 char *arg = *++argv;
188 if (!arg)
189 bb_show_usage();
190 if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=')
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000191 break;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200192
193 argv = decode_arg(argv, &g);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000194 }
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200195 /* note: on loop exit, remaining argv[] is never empty */
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000196
197 /* run sanity checks on all the arguments given us */
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000198 if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000199 bb_error_msg_and_die("= is incompatible with - and +");
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000200 if (g.rf & g.af)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000201 bb_error_msg_and_die("can't set and unset a flag");
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000202 if (!g.flags)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000203 bb_error_msg_and_die("must use '-v', =, - or +");
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000204
205 /* now run chattr on all the files passed to us */
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000206 do change_attributes(*argv, &g); while (*++argv);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000207
208 return EXIT_SUCCESS;
209}