blob: bb968646083e145948c7d95e09c59ab0eb560a57 [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'o818180c1998-06-27 05:11:14 +000081static void fatal_error(const char * fmt_string, int errcode)
Theodore Ts'o3839e651997-04-26 13:21:57 +000082{
83 fprintf (stderr, fmt_string, program_name);
84 exit (errcode);
85}
86
Theodore Ts'o88372d52002-06-15 18:58:39 -040087#define usage() fatal_error(_("usage: %s [-RV] [-+=AacDdijsSu] [-v version] files...\n"), \
Theodore Ts'o3839e651997-04-26 13:21:57 +000088 1)
89
Theodore Ts'o9a718842000-12-31 13:48:12 +000090struct flags_char {
91 unsigned long flag;
92 char optchar;
93};
94
95static const struct flags_char flags_array[] = {
96 { EXT2_NOATIME_FL, 'A' },
97 { EXT2_SYNC_FL, 'S' },
Theodore Ts'o88372d52002-06-15 18:58:39 -040098 { EXT2_DIRSYNC_FL, 'D' },
Theodore Ts'o9a718842000-12-31 13:48:12 +000099 { EXT2_APPEND_FL, 'a' },
100 { EXT2_COMPR_FL, 'c' },
101 { EXT2_NODUMP_FL, 'd' },
102 { EXT2_IMMUTABLE_FL, 'i' },
103 { EXT3_JOURNAL_DATA_FL, 'j' },
104 { EXT2_SECRM_FL, 's' },
105 { EXT2_UNRM_FL, 'u' },
Theodore Ts'ob3f5b4c2001-11-05 19:22:02 -0500106 { EXT2_NOTAIL_FL, 't' },
Theodore Ts'o15f90112002-11-01 01:53:52 -0500107 { EXT2_TOPDIR_FL, 'T' },
Theodore Ts'o9a718842000-12-31 13:48:12 +0000108 { 0, 0 }
109};
110
111static unsigned long get_flag(char c)
112{
113 const struct flags_char *fp;
114
115 for (fp = flags_array; fp->flag != 0; fp++) {
116 if (fp->optchar == c)
117 return fp->flag;
118 }
119 return 0;
120}
121
122
Theodore Ts'o3839e651997-04-26 13:21:57 +0000123static int decode_arg (int * i, int argc, char ** argv)
124{
125 char * p;
126 char * tmp;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000127 unsigned long fl;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000128
129 switch (argv[*i][0])
130 {
131 case '-':
Theodore Ts'o9a718842000-12-31 13:48:12 +0000132 for (p = &argv[*i][1]; *p; p++) {
133 if (*p == 'R') {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000134 recursive = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000135 continue;
136 }
137 if (*p == 'V') {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000138 verbose = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000139 continue;
140 }
141 if (*p == 'v') {
Theodore Ts'o1e3472c1997-04-29 14:53:37 +0000142 (*i)++;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000143 if (*i >= argc)
144 usage ();
Theodore Ts'o3839e651997-04-26 13:21:57 +0000145 version = strtol (argv[*i], &tmp, 0);
Theodore Ts'o9a718842000-12-31 13:48:12 +0000146 if (*tmp) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000147 com_err (program_name, 0,
Theodore Ts'o9a718842000-12-31 13:48:12 +0000148 _("bad version - %s\n"),
149 argv[*i]);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000150 usage ();
151 }
152 set_version = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000153 continue;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000154 }
Theodore Ts'o9a718842000-12-31 13:48:12 +0000155 if ((fl = get_flag(*p)) == 0)
156 usage();
157 rf |= fl;
158 rem = 1;
159 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000160 break;
161 case '+':
162 add = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000163 for (p = &argv[*i][1]; *p; p++) {
164 if ((fl = get_flag(*p)) == 0)
165 usage();
166 af |= fl;
167 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000168 break;
169 case '=':
170 set = 1;
Theodore Ts'o9a718842000-12-31 13:48:12 +0000171 for (p = &argv[*i][1]; *p; p++) {
172 if ((fl = get_flag(*p)) == 0)
173 usage();
174 sf |= fl;
175 }
Theodore Ts'o3839e651997-04-26 13:21:57 +0000176 break;
177 default:
178 return EOF;
179 break;
180 }
181 return 1;
182}
183
184static int chattr_dir_proc (const char *, struct dirent *, void *);
185
186static void change_attributes (const char * name)
187{
188 unsigned long flags;
Theodore Ts'ob7056402001-06-08 02:53:20 +0000189 STRUCT_STAT st;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000190
Theodore Ts'ob7056402001-06-08 02:53:20 +0000191 if (LSTAT (name, &st) == -1) {
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000192 com_err (program_name, errno, _("while trying to stat %s"),
193 name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000194 return;
195 }
Theodore Ts'oa88fa0c1999-01-05 07:02:39 +0000196 if (S_ISLNK(st.st_mode) && recursive)
197 return;
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000198
199 /* Don't try to open device files, fifos etc. We probably
200 ought to display an error if the file was explicitly given
201 on the command line (whether or not recursive was
202 requested). */
203 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) &&
204 !S_ISDIR(st.st_mode))
205 return;
206
207 if (set) {
208 if (verbose) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000209 printf (_("Flags of %s set as "), name);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000210 print_flags (stdout, sf, 0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000211 printf ("\n");
212 }
213 if (fsetflags (name, sf) == -1)
214 perror (name);
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000215 } else {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000216 if (fgetflags (name, &flags) == -1)
217 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000218 _("while reading flags on %s"), name);
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000219 else {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000220 if (rem)
221 flags &= ~rf;
222 if (add)
223 flags |= af;
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000224 if (verbose) {
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000225 printf (_("Flags of %s set as "), name);
Theodore Ts'of3db3561997-04-26 13:34:30 +0000226 print_flags (stdout, flags, 0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000227 printf ("\n");
228 }
Theodore Ts'o88372d52002-06-15 18:58:39 -0400229 if (!S_ISDIR(st.st_mode))
230 flags &= ~EXT2_DIRSYNC_FL;
Theodore Ts'o3839e651997-04-26 13:21:57 +0000231 if (fsetflags (name, flags) == -1)
232 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000233 _("while setting flags on %s"), name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000234 }
235 }
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000236 if (set_version) {
Theodore Ts'o3839e651997-04-26 13:21:57 +0000237 if (verbose)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000238 printf (_("Version of %s set as %lu\n"), name, version);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000239 if (fsetversion (name, version) == -1)
240 com_err (program_name, errno,
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000241 _("while setting version on %s"), name);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000242 }
243 if (S_ISDIR(st.st_mode) && recursive)
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000244 iterate_on_dir (name, chattr_dir_proc, NULL);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000245}
246
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000247static int chattr_dir_proc (const char * dir_name, struct dirent * de,
Theodore Ts'o54434922003-12-07 01:28:50 -0500248 void * private EXT2FS_ATTR((unused)))
Theodore Ts'o3839e651997-04-26 13:21:57 +0000249{
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000250 if (strcmp (de->d_name, ".") && strcmp (de->d_name, "..")) {
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000251 char *path;
252
253 path = malloc(strlen (dir_name) + 1 + strlen (de->d_name) + 1);
254 if (!path)
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000255 fatal_error(_("Couldn't allocate path variable "
256 "in chattr_dir_proc"), 1);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000257 sprintf (path, "%s/%s", dir_name, de->d_name);
258 change_attributes (path);
Theodore Ts'oa418d3a1997-04-26 14:00:26 +0000259 free(path);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000260 }
261 return 0;
262}
263
Theodore Ts'o00e54331997-09-16 02:13:52 +0000264int main (int argc, char ** argv)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000265{
266 int i, j;
267 int end_arg = 0;
268
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000269#ifdef ENABLE_NLS
270 setlocale(LC_MESSAGES, "");
Theodore Ts'o14308a52002-03-05 03:26:52 -0500271 setlocale(LC_CTYPE, "");
Theodore Ts'od9c56d32000-02-08 00:47:55 +0000272 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
273 textdomain(NLS_CAT_NAME);
274#endif
Theodore Ts'o3839e651997-04-26 13:21:57 +0000275 if (argc && *argv)
276 program_name = *argv;
277 i = 1;
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000278 while (i < argc && !end_arg) {
Theodore Ts'o2293a4d2004-01-20 13:39:01 -0500279 /* '--' arg should end option processing */
280 if (strcmp(argv[i], "--") == 0) {
281 i++;
282 end_arg = 1;
283 } else if (decode_arg (&i, argc, argv) == EOF)
Theodore Ts'o3839e651997-04-26 13:21:57 +0000284 end_arg = 1;
285 else
286 i++;
287 }
288 if (i >= argc)
289 usage ();
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000290 if (set && (add || rem)) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500291 fputs(_("= is incompatible with - and +\n"), stderr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000292 exit (1);
293 }
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000294 if ((rf & af) != 0) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500295 fputs("Can't both set and unset same flag.\n", stderr);
Theodore Ts'oe1a0a3e2000-02-11 05:00:19 +0000296 exit (1);
297 }
298 if (!(add || rem || set || set_version)) {
Theodore Ts'o54434922003-12-07 01:28:50 -0500299 fputs(_("Must use '-v', =, - or +\n"), stderr);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000300 exit (1);
301 }
Theodore Ts'oa88fa0c1999-01-05 07:02:39 +0000302 if (verbose)
Theodore Ts'o0f8973f2001-08-27 12:44:23 -0400303 fprintf (stderr, "chattr %s (%s)\n",
304 E2FSPROGS_VERSION, E2FSPROGS_DATE);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000305 for (j = i; j < argc; j++)
306 change_attributes (argv[j]);
Theodore Ts'o00e54331997-09-16 02:13:52 +0000307 exit(0);
Theodore Ts'o3839e651997-04-26 13:21:57 +0000308}