blob: 1d267f7a4715b6a4c90e2c9d7e3f3867a15b5d1b [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 Vlasenkob097a842018-12-28 03:20:17 +010013//config: bool "chattr (3.8 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"
Denys Vlasenkoe2b92152021-06-14 20:47:20 +020027//TODD? "\n -p NUM Set project number"
28//usage: "\n -v NUM Set version/generation number"
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020029//-V, -f accepted but ignored
Pere Orga6a3e01d2011-04-01 22:56:30 +020030//usage: "\nModifiers:"
Denys Vlasenko36647ab2015-10-19 01:29:20 +020031//usage: "\n -,+,= Remove/add/set attributes"
Pere Orga6a3e01d2011-04-01 22:56:30 +020032//usage: "\nAttributes:"
Denys Vlasenkoe2b92152021-06-14 20:47:20 +020033//usage: "\n A No atime"
34//usage: "\n a Append only"
35//usage: "\n c Compressed"
36//usage: "\n D Synchronous dir updates"
Pere Orga6a3e01d2011-04-01 22:56:30 +020037//usage: "\n d Don't backup with dump"
Denys Vlasenkoe2b92152021-06-14 20:47:20 +020038//usage: "\n i Immutable"
39//usage: "\n j Write data to journal first"
40//usage: "\n s Zero storage when deleted"
41//usage: "\n S Synchronous file updates"
42//usage: "\n t Don't tail-merge with other files"
43//usage: "\n u Allow undelete"
Pere Orga6a3e01d2011-04-01 22:56:30 +020044
Denis Vlasenkob6adbf12007-05-26 19:00:18 +000045#include "libbb.h"
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000046#include "e2fs_lib.h"
47
48#define OPT_ADD 1
49#define OPT_REM 2
50#define OPT_SET 4
51#define OPT_SET_VER 8
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000052
Denis Vlasenko8acf5212007-04-15 11:48:27 +000053struct globals {
54 unsigned long version;
55 unsigned long af;
56 unsigned long rf;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020057 int flags;
Denis Vlasenko8acf5212007-04-15 11:48:27 +000058 smallint recursive;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000059};
60
61static unsigned long get_flag(char c)
62{
Denis Vlasenkod059ddc2007-10-30 19:36:07 +000063 const char *fp = strchr(e2attr_flags_sname_chattr, c);
64 if (fp)
65 return e2attr_flags_value_chattr[fp - e2attr_flags_sname_chattr];
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000066 bb_show_usage();
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000067}
68
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020069static char** decode_arg(char **argv, struct globals *gp)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000070{
71 unsigned long *fl;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020072 const char *arg = *argv;
73 char opt = *arg;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000074
Denis Vlasenko8acf5212007-04-15 11:48:27 +000075 fl = &gp->af;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000076 if (opt == '-') {
Denis Vlasenko8acf5212007-04-15 11:48:27 +000077 gp->flags |= OPT_REM;
78 fl = &gp->rf;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000079 } else if (opt == '+') {
Denis Vlasenko8acf5212007-04-15 11:48:27 +000080 gp->flags |= OPT_ADD;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020081 } else { /* if (opt == '=') */
Denis Vlasenko8acf5212007-04-15 11:48:27 +000082 gp->flags |= OPT_SET;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020083 }
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +000084
Denys Vlasenkod3147cd2017-08-05 20:33:48 +020085 while (*++arg) {
86 if (opt == '-') {
87//e2fsprogs-1.43.1 accepts:
88// "-RRR", "-RRRv VER" and even "-ARRRva VER" and "-vvv V1 V2 V3"
89// but not "-vVER".
90// IOW: options are parsed as part of "remove attrs" strings,
91// if "v" is seen, next argv[] is VER, even if more opts/attrs follow in this argv[]!
92 if (*arg == 'R') {
93 gp->recursive = 1;
94 continue;
95 }
96 if (*arg == 'V') {
97 /*"verbose and print program version" (nop for now) */;
98 continue;
99 }
100 if (*arg == 'f') {
101 /*"suppress most error messages" (nop) */;
102 continue;
103 }
104 if (*arg == 'v') {
105 if (!*++argv)
106 bb_show_usage();
107 gp->version = xatoul(*argv);
108 gp->flags |= OPT_SET_VER;
109 continue;
110 }
111//TODO: "-p PROJECT_NUM" ?
112 /* not a known option, try as an attribute */
113 }
114 *fl |= get_flag(*arg);
115 }
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000116
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200117 return argv;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000118}
119
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000120static void change_attributes(const char *name, struct globals *gp);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000121
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +0200122static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, void *gp)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000123{
124 char *path = concat_subpath_file(dir_name, de->d_name);
125 /* path is NULL if de->d_name is "." or "..", else... */
126 if (path) {
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000127 change_attributes(path, gp);
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000128 free(path);
129 }
130 return 0;
131}
132
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000133static void change_attributes(const char *name, struct globals *gp)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000134{
135 unsigned long fsflags;
136 struct stat st;
137
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000138 if (lstat(name, &st) != 0) {
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000139 bb_perror_msg("stat %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000140 return;
141 }
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000142 if (S_ISLNK(st.st_mode) && gp->recursive)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000143 return;
144
145 /* Don't try to open device files, fifos etc. We probably
146 * ought to display an error if the file was explicitly given
147 * on the command line (whether or not recursive was
148 * requested). */
149 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
150 return;
151
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000152 if (gp->flags & OPT_SET_VER)
153 if (fsetversion(name, gp->version) != 0)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000154 bb_perror_msg("setting version on %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000155
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000156 if (gp->flags & OPT_SET) {
157 fsflags = gp->af;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000158 } else {
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000159 if (fgetflags(name, &fsflags) != 0) {
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000160 bb_perror_msg("reading flags on %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000161 goto skip_setflags;
162 }
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000163 /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
164 fsflags &= ~gp->rf;
165 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
166 fsflags |= gp->af;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200167// What is this? And why it's not done for SET case?
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000168 if (!S_ISDIR(st.st_mode))
169 fsflags &= ~EXT2_DIRSYNC_FL;
170 }
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000171 if (fsetflags(name, fsflags) != 0)
Denis Vlasenko5dd7ef02006-12-26 03:36:28 +0000172 bb_perror_msg("setting flags on %s", name);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000173
174 skip_setflags:
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000175 if (gp->recursive && S_ISDIR(st.st_mode))
176 iterate_on_dir(name, chattr_dir_proc, gp);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000177}
178
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +0000179int chattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000180int chattr_main(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000181{
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000182 struct globals g;
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000183
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000184 memset(&g, 0, sizeof(g));
185
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000186 /* parse the args */
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200187 for (;;) {
188 char *arg = *++argv;
189 if (!arg)
190 bb_show_usage();
191 if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=')
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000192 break;
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200193
194 argv = decode_arg(argv, &g);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000195 }
Denys Vlasenkod3147cd2017-08-05 20:33:48 +0200196 /* note: on loop exit, remaining argv[] is never empty */
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000197
198 /* run sanity checks on all the arguments given us */
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000199 if ((g.flags & OPT_SET) && (g.flags & (OPT_ADD|OPT_REM)))
James Byrne69374872019-07-02 11:35:03 +0200200 bb_simple_error_msg_and_die("= is incompatible with - and +");
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000201 if (g.rf & g.af)
James Byrne69374872019-07-02 11:35:03 +0200202 bb_simple_error_msg_and_die("can't set and unset a flag");
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000203 if (!g.flags)
James Byrne69374872019-07-02 11:35:03 +0200204 bb_simple_error_msg_and_die("must use '-v', =, - or +");
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000205
206 /* now run chattr on all the files passed to us */
Denis Vlasenko8acf5212007-04-15 11:48:27 +0000207 do change_attributes(*argv, &g); while (*++argv);
Denis Vlasenkoc4f623e2006-12-26 01:30:59 +0000208
209 return EXIT_SUCCESS;
210}