blob: c6d8d9f553764dad58107340ff1a1ce243488a80 [file] [log] [blame]
Theodore Ts'o3839e651997-04-26 13:21:57 +00001/*
2 * chattr.c - Change file attributes on an ext2 file system
3 *
4 * Copyright (C) 1993, 1994 Remy Card <card@masi.ibp.fr>
5 * Laboratoire MASI, Institut Blaise Pascal
6 * Universite Pierre et Marie Curie (Paris VI)
7 *
8 * This file can be redistributed under the terms of the GNU General
9 * Public License
10 */
11
12/*
13 * History:
14 * 93/10/30 - Creation
15 * 93/11/13 - Replace stat() calls by lstat() to avoid loops
16 * 94/02/27 - Integrated in Ted's distribution
Theodore Ts'oa88fa0c1999-01-05 07:02:39 +000017 * 98/12/29 - Ignore symlinks when working recursively (G M Sipe)
18 * 98/12/29 - Display version info only when -V specified (G M Sipe)
Theodore Ts'o3839e651997-04-26 13:21:57 +000019 */
20
Theodore Ts'offf18b42001-02-08 03:06:43 +000021#define _LARGEFILE64_SOURCE
Theodore Ts'offf18b42001-02-08 03:06:43 +000022
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000023#include <sys/types.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000024#include <dirent.h>
25#include <fcntl.h>
Theodore Ts'o3839e651997-04-26 13:21:57 +000026#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
Theodore Ts'o19c78dc1997-04-29 16:17:09 +000029#include <string.h>
Theodore Ts'oa418d3a1997-04-26 14:00:26 +000030#ifdef HAVE_ERRNO_H
31#include <errno.h>
32#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +000033#include <sys/param.h>
34#include <sys/stat.h>
Theodore Ts'o54c637d2001-05-14 11:45:38 +000035#include "ext2fs/ext2_fs.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000036
Theodore Ts'o54434922003-12-07 01:28:50 -050037#ifdef __GNUC__
38#define EXT2FS_ATTR(x) __attribute__(x)
39#else
40#define EXT2FS_ATTR(x)
41#endif
42
Theodore Ts'o36caf251999-10-26 14:29:22 +000043#ifndef S_ISLNK /* So we can compile even with gcc-warn */
44# ifdef __S_IFLNK
45# define S_ISLNK(mode) __S_ISTYPE((mode), __S_IFLNK)
46# else
47# define S_ISLNK(mode) 0
48# endif
49#endif
50
Theodore Ts'o3839e651997-04-26 13:21:57 +000051#include "et/com_err.h"
52#include "e2p/e2p.h"
53
54#include "../version.h"
Theodore Ts'od9c56d32000-02-08 00:47:55 +000055#include "nls-enable.h"
Theodore Ts'o3839e651997-04-26 13:21:57 +000056
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +000057static const char * program_name = "chattr";
Theodore Ts'o3839e651997-04-26 13:21:57 +000058
Theodore Ts'o9a718842000-12-31 13:48:12 +000059static int add;
60static int rem;
61static int set;
62static int set_version;
Theodore Ts'o3839e651997-04-26 13:21:57 +000063
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +000064static unsigned long version;
Theodore Ts'o3839e651997-04-26 13:21:57 +000065
Theodore Ts'o9a718842000-12-31 13:48:12 +000066static int recursive;
67static int verbose;
Theodore Ts'o3839e651997-04-26 13:21:57 +000068
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +000069static unsigned long af;
70static unsigned long rf;
71static unsigned long sf;
Theodore Ts'o3839e651997-04-26 13:21:57 +000072
Theodore Ts'ob7056402001-06-08 02:53:20 +000073#ifdef _LFS64_LARGEFILE
74#define LSTAT lstat64
75#define STRUCT_STAT struct stat64
76#else
77#define LSTAT lstat
78#define STRUCT_STAT struct stat
79#endif
80
Theodore Ts'o642935c2006-11-14 23:38:17 -050081static void usage(void)
Theodore Ts'o3839e651997-04-26 13:21:57 +000082{
Theodore Ts'o642935c2006-11-14 23:38:17 -050083 fprintf(stderr,
84 _("Usage: %s [-RV] [-+=AacDdijsSu] [-v version] files...\n"),
85 program_name);
86 exit(1);
Theodore Ts'o3839e651997-04-26 13:21:57 +000087}
88
Theodore Ts'o9a718842000-12-31 13:48:12 +000089struct flags_char {
90 unsigned long flag;
91 char optchar;
92};
93
94static const struct flags_char flags_array[] = {
95 { EXT2_NOATIME_FL, 'A' },
96 { EXT2_SYNC_FL, 'S' },
Theodore Ts'o88372d52002-06-15 18:58:39 -040097 { EXT2_DIRSYNC_FL, 'D' },
Theodore Ts'o9a718842000-12-31 13:48:12 +000098 { EXT2_APPEND_FL, 'a' },
99 { EXT2_COMPR_FL, 'c' },
100 { EXT2_NODUMP_FL, 'd' },
101 { EXT2_IMMUTABLE_FL, 'i' },
102 { EXT3_JOURNAL_DATA_FL, 'j' },
103 { EXT2_SECRM_FL, 's' },
104 { EXT2_UNRM_FL, 'u' },
Theodore Ts'ob3f5b4c2001-11-05 19:22:02 -0500105 { EXT2_NOTAIL_FL, 't' },
Theodore Ts'o15f90112002-11-01 01:53:52 -0500106 { EXT2_TOPDIR_FL, 'T' },
Theodore Ts'o9a718842000-12-31 13:48:12 +0000107 { 0, 0 }
108};
109
110static unsigned long get_flag(char c)
111{
112 const struct flags_char *fp;
113
114 for (fp = flags_array; fp->flag != 0; fp++) {
115 if (fp->optchar == c)
116 return fp->flag;
117 }
118 return 0;
119}
120
121
Theodore Ts'o3839e651997-04-26 13:21:57 +0000122static int decode_arg (int * i, int argc, char ** argv)
123{
124 char * p;
125 char * tmp;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000126 unsigned long fl;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000127
128 switch (argv[*i][0])
129 {
130 case '-':
Theodore Ts'o9a718842000-12-31 13:48:12 +0000131 for (p = &argv[*i][1]; *p; p++) {
132 if (*p == 'R') {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000133 recursive = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000134 continue;
135 }
136 if (*p == 'V') {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000137 verbose = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000138 continue;
139 }
140 if (*p == 'v') {
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000141 (*i)++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000142 if (*i >= argc)
143 usage ();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000144 version = strtol (argv[*i], &tmp, 0);
Theodore Ts'o9a718842000-12-31 13:48:12 +0000145 if (*tmp) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000146 com_err (program_name, 0,
Theodore Ts'o9a718842000-12-31 13:48:12 +0000147 _("bad version - %s\n"),
148 argv[*i]);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000149 usage ();
150 }
151 set_version = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000152 continue;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000153 }
Theodore Ts'o9a718842000-12-31 13:48:12 +0000154 if ((fl = get_flag(*p)) == 0)
155 usage();
156 rf |= fl;
157 rem = 1;
158 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000159 break;
160 case '+':
161 add = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000162 for (p = &argv[*i][1]; *p; p++) {
163 if ((fl = get_flag(*p)) == 0)
164 usage();
165 af |= fl;
166 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000167 break;
168 case '=':
169 set = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000170 for (p = &argv[*i][1]; *p; p++) {
171 if ((fl = get_flag(*p)) == 0)
172 usage();
173 sf |= fl;
174 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000175 break;
176 default:
177 return EOF;
178 break;
179 }
180 return 1;
181}
182
183static int chattr_dir_proc (const char *, struct dirent *, void *);
184
185static void change_attributes (const char * name)
186{
187 unsigned long flags;
Theodore Ts'ob7056402001-06-08 02:53:20 +0000188 STRUCT_STAT st;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000189
Theodore Ts'ob7056402001-06-08 02:53:20 +0000190 if (LSTAT (name, &st) == -1) {
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000191 com_err (program_name, errno, _("while trying to stat %s"),
192 name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000193 return;
194 }
Theodore Ts'oa88fa0c1999-01-05 07:02:39 +0000195 if (S_ISLNK(st.st_mode) && recursive)
196 return;
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000197
198 /* Don't try to open device files, fifos etc. We probably
199 ought to display an error if the file was explicitly given
200 on the command line (whether or not recursive was
201 requested). */
202 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) &&
203 !S_ISDIR(st.st_mode))
204 return;
205
206 if (set) {
207 if (verbose) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000208 printf (_("Flags of %s set as "), name);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000209 print_flags (stdout, sf, 0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000210 printf ("\n");
211 }
212 if (fsetflags (name, sf) == -1)
213 perror (name);
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000214 } else {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000215 if (fgetflags (name, &flags) == -1)
216 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000217 _("while reading flags on %s"), name);
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000218 else {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000219 if (rem)
220 flags &= ~rf;
221 if (add)
222 flags |= af;
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000223 if (verbose) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000224 printf (_("Flags of %s set as "), name);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000225 print_flags (stdout, flags, 0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000226 printf ("\n");
227 }
Theodore Ts'o88372d52002-06-15 18:58:39 -0400228 if (!S_ISDIR(st.st_mode))
229 flags &= ~EXT2_DIRSYNC_FL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000230 if (fsetflags (name, flags) == -1)
231 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000232 _("while setting flags on %s"), name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000233 }
234 }
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000235 if (set_version) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000236 if (verbose)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000237 printf (_("Version of %s set as %lu\n"), name, version);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000238 if (fsetversion (name, version) == -1)
239 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000240 _("while setting version on %s"), name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000241 }
242 if (S_ISDIR(st.st_mode) && recursive)
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000243 iterate_on_dir (name, chattr_dir_proc, NULL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000244}
245
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000246static int chattr_dir_proc (const char * dir_name, struct dirent * de,
Theodore Ts'o54434922003-12-07 01:28:50 -0500247 void * private EXT2FS_ATTR((unused)))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000248{
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000249 if (strcmp (de->d_name, ".") && strcmp (de->d_name, "..")) {
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000250 char *path;
251
252 path = malloc(strlen (dir_name) + 1 + strlen (de->d_name) + 1);
Theodore Ts'o642935c2006-11-14 23:38:17 -0500253 if (!path) {
254 fprintf(stderr, _("Couldn't allocate path variable "
255 "in chattr_dir_proc"));
256 exit(1);
257 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000258 sprintf (path, "%s/%s", dir_name, de->d_name);
259 change_attributes (path);
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000260 free(path);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000261 }
262 return 0;
263}
264
Theodore Ts'o00e54331997-09-16 02:13:52 +0000265int main (int argc, char ** argv)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000266{
267 int i, j;
268 int end_arg = 0;
269
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000270#ifdef ENABLE_NLS
271 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -0500272 setlocale(LC_CTYPE, "");
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000273 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
274 textdomain(NLS_CAT_NAME);
275#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000276 if (argc && *argv)
277 program_name = *argv;
278 i = 1;
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000279 while (i < argc && !end_arg) {
Theodore Ts'o2293a4d2004-01-20 13:39:01 -0500280 /* '--' arg should end option processing */
281 if (strcmp(argv[i], "--") == 0) {
282 i++;
283 end_arg = 1;
284 } else if (decode_arg (&i, argc, argv) == EOF)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000285 end_arg = 1;
286 else
287 i++;
288 }
289 if (i >= argc)
290 usage ();
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000291 if (set && (add || rem)) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500292 fputs(_("= is incompatible with - and +\n"), stderr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000293 exit (1);
294 }
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000295 if ((rf & af) != 0) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500296 fputs("Can't both set and unset same flag.\n", stderr);
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000297 exit (1);
298 }
299 if (!(add || rem || set || set_version)) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500300 fputs(_("Must use '-v', =, - or +\n"), stderr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000301 exit (1);
302 }
Theodore Ts'oa88fa0c1999-01-05 07:02:39 +0000303 if (verbose)
Theodore Ts'o0f8973f2001-08-27 12:44:23 -0400304 fprintf (stderr, "chattr %s (%s)\n",
305 E2FSPROGS_VERSION, E2FSPROGS_DATE);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000306 for (j = i; j < argc; j++)
307 change_attributes (argv[j]);
Theodore Ts'o00e54331997-09-16 02:13:52 +0000308 exit(0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000309}