1 /*
2 * tune2fs.c - Change the file system parameters on an ext2 file system
3 *
4 * Copyright (C) 1992, 1993, 1994 Remy Card <card@masi.ibp.fr>
5 * Laboratoire MASI, Institut Blaise Pascal
6 * Universite Pierre et Marie Curie (Paris VI)
7 *
8 * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o.
9 *
10 * %Begin-Header%
11 * This file may be redistributed under the terms of the GNU Public
12 * License.
13 * %End-Header%
14 */
15
16 /*
17 * History:
18 * 93/06/01 - Creation
19 * 93/10/31 - Added the -c option to change the maximal mount counts
20 * 93/12/14 - Added -l flag to list contents of superblock
21 * M.J.E. Mol (marcel@duteca.et.tudelft.nl)
22 * F.W. ten Wolde (franky@duteca.et.tudelft.nl)
23 * 93/12/29 - Added the -e option to change errors behavior
24 * 94/02/27 - Ported to use the ext2fs library
25 * 94/03/06 - Added the checks interval from Uwe Ohse (uwe@tirka.gun.de)
26 */
27
28 #define _XOPEN_SOURCE 500 /* for inclusion of strptime() */
29 #define _BSD_SOURCE /* for inclusion of strcasecmp() */
30 #include <fcntl.h>
31 #include <grp.h>
32 #ifdef HAVE_GETOPT_H
33 #include <getopt.h>
34 #else
35 extern char *optarg;
36 extern int optind;
37 #endif
38 #include <pwd.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <time.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45
46 #include "ext2fs/ext2_fs.h"
47 #include "ext2fs/ext2fs.h"
48 #include "et/com_err.h"
49 #include "uuid/uuid.h"
50 #include "e2p/e2p.h"
51 #include "jfs_user.h"
52 #include "util.h"
53 #include "blkid/blkid.h"
54
55 #include "../version.h"
56 #include "nls-enable.h"
57
58 /*
59 * Tune2fs supports these features in addition to the standard features.
60 */
61 #define EXT2_TUNE2FS_INCOMPAT (EXT3_FEATURE_INCOMPAT_EXTENTS)
62 #define EXT2_TUNE2FS_RO_COMPAT (EXT4_FEATURE_RO_COMPAT_HUGE_FILE|\
63 EXT4_FEATURE_RO_COMPAT_GDT_CSUM| \
64 EXT4_FEATURE_RO_COMPAT_DIR_NLINK| \
65 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE)
66
67
68 const char * program_name = "tune2fs";
69 char * device_name;
70 char * new_label, *new_last_mounted, *new_UUID;
71 char * io_options;
72 static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
73 static int m_flag, M_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
74 static time_t last_check_time;
75 static int print_label;
76 static int max_mount_count, mount_count, mount_flags;
77 static unsigned long interval, reserved_blocks;
78 static double reserved_ratio;
79 static unsigned long resgid, resuid;
80 static unsigned short errors;
81 static int open_flag;
82 static char *features_cmd;
83 static char *mntopts_cmd;
84 static int stride, stripe_width;
85 static int stride_set, stripe_width_set;
86 static char *extended_cmd;
87
88 int journal_size, journal_flags;
89 char *journal_device;
90
91 static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n");
92
93 void do_findfs(int argc, char **argv);
94
usage(void)95 static void usage(void)
96 {
97 fprintf(stderr,
98 _("Usage: %s [-c max_mounts_count] [-e errors_behavior] "
99 "[-g group]\n"
100 "\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n"
101 "\t[-m reserved_blocks_percent] "
102 "[-o [^]mount_options[,...]] \n"
103 "\t[-r reserved_blocks_count] [-u user] [-C mount_count] "
104 "[-L volume_label]\n"
105 "\t[-M last_mounted_dir] [-O [^]feature[,...]]\n"
106 "\t[-E extended-option[,...]] [-T last_check_time] "
107 "[-U UUID] device\n"), program_name);
108 exit (1);
109 }
110
111 static __u32 ok_features[3] = {
112 /* Compat */
113 EXT3_FEATURE_COMPAT_HAS_JOURNAL |
114 EXT2_FEATURE_COMPAT_DIR_INDEX,
115 /* Incompat */
116 EXT2_FEATURE_INCOMPAT_FILETYPE,
117 /* R/O compat */
118 EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
119 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER
120 };
121
122 static __u32 clear_ok_features[3] = {
123 /* Compat */
124 EXT3_FEATURE_COMPAT_HAS_JOURNAL |
125 EXT2_FEATURE_COMPAT_RESIZE_INODE |
126 EXT2_FEATURE_COMPAT_DIR_INDEX,
127 /* Incompat */
128 EXT2_FEATURE_INCOMPAT_FILETYPE,
129 /* R/O compat */
130 EXT2_FEATURE_RO_COMPAT_LARGE_FILE
131 };
132
133 /*
134 * Remove an external journal from the filesystem
135 */
remove_journal_device(ext2_filsys fs)136 static void remove_journal_device(ext2_filsys fs)
137 {
138 char *journal_path;
139 ext2_filsys jfs;
140 char buf[1024];
141 journal_superblock_t *jsb;
142 int i, nr_users;
143 errcode_t retval;
144 int commit_remove_journal = 0;
145 io_manager io_ptr;
146
147 if (f_flag)
148 commit_remove_journal = 1; /* force removal even if error */
149
150 uuid_unparse(fs->super->s_journal_uuid, buf);
151 journal_path = blkid_get_devname(NULL, "UUID", buf);
152
153 if (!journal_path) {
154 journal_path =
155 ext2fs_find_block_device(fs->super->s_journal_dev);
156 if (!journal_path)
157 return;
158 }
159
160 #ifdef CONFIG_TESTIO_DEBUG
161 io_ptr = test_io_manager;
162 test_io_backing_manager = unix_io_manager;
163 #else
164 io_ptr = unix_io_manager;
165 #endif
166 retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
167 EXT2_FLAG_JOURNAL_DEV_OK, 0,
168 fs->blocksize, io_ptr, &jfs);
169 if (retval) {
170 com_err(program_name, retval,
171 _("while trying to open external journal"));
172 goto no_valid_journal;
173 }
174 if (!(jfs->super->s_feature_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) {
175 fprintf(stderr, _("%s is not a journal device.\n"),
176 journal_path);
177 goto no_valid_journal;
178 }
179
180 /* Get the journal superblock */
181 if ((retval = io_channel_read_blk(jfs->io, 1, -1024, buf))) {
182 com_err(program_name, retval,
183 _("while reading journal superblock"));
184 goto no_valid_journal;
185 }
186
187 jsb = (journal_superblock_t *) buf;
188 if ((jsb->s_header.h_magic != (unsigned) ntohl(JFS_MAGIC_NUMBER)) ||
189 (jsb->s_header.h_blocktype != (unsigned) ntohl(JFS_SUPERBLOCK_V2))) {
190 fputs(_("Journal superblock not found!\n"), stderr);
191 goto no_valid_journal;
192 }
193
194 /* Find the filesystem UUID */
195 nr_users = ntohl(jsb->s_nr_users);
196 for (i=0; i < nr_users; i++) {
197 if (memcmp(fs->super->s_uuid,
198 &jsb->s_users[i*16], 16) == 0)
199 break;
200 }
201 if (i >= nr_users) {
202 fputs(_("Filesystem's UUID not found on journal device.\n"),
203 stderr);
204 commit_remove_journal = 1;
205 goto no_valid_journal;
206 }
207 nr_users--;
208 for (i=0; i < nr_users; i++)
209 memcpy(&jsb->s_users[i*16], &jsb->s_users[(i+1)*16], 16);
210 jsb->s_nr_users = htonl(nr_users);
211
212 /* Write back the journal superblock */
213 if ((retval = io_channel_write_blk(jfs->io, 1, -1024, buf))) {
214 com_err(program_name, retval,
215 "while writing journal superblock.");
216 goto no_valid_journal;
217 }
218
219 commit_remove_journal = 1;
220
221 no_valid_journal:
222 if (commit_remove_journal == 0) {
223 fputs(_("Journal NOT removed\n"), stderr);
224 exit(1);
225 }
226 fs->super->s_journal_dev = 0;
227 uuid_clear(fs->super->s_journal_uuid);
228 ext2fs_mark_super_dirty(fs);
229 fputs(_("Journal removed\n"), stdout);
230 free(journal_path);
231 }
232
233 /* Helper function for remove_journal_inode */
release_blocks_proc(ext2_filsys fs,blk_t * blocknr,int blockcnt EXT2FS_ATTR ((unused)),void * private EXT2FS_ATTR ((unused)))234 static int release_blocks_proc(ext2_filsys fs, blk_t *blocknr,
235 int blockcnt EXT2FS_ATTR((unused)),
236 void *private EXT2FS_ATTR((unused)))
237 {
238 blk_t block;
239 int group;
240
241 block = *blocknr;
242 ext2fs_unmark_block_bitmap(fs->block_map,block);
243 group = ext2fs_group_of_blk(fs, block);
244 fs->group_desc[group].bg_free_blocks_count++;
245 fs->super->s_free_blocks_count++;
246 return 0;
247 }
248
249 /*
250 * Remove the journal inode from the filesystem
251 */
remove_journal_inode(ext2_filsys fs)252 static void remove_journal_inode(ext2_filsys fs)
253 {
254 struct ext2_inode inode;
255 errcode_t retval;
256 ino_t ino = fs->super->s_journal_inum;
257
258 retval = ext2fs_read_inode(fs, ino, &inode);
259 if (retval) {
260 com_err(program_name, retval,
261 _("while reading journal inode"));
262 exit(1);
263 }
264 if (ino == EXT2_JOURNAL_INO) {
265 retval = ext2fs_read_bitmaps(fs);
266 if (retval) {
267 com_err(program_name, retval,
268 _("while reading bitmaps"));
269 exit(1);
270 }
271 retval = ext2fs_block_iterate(fs, ino, 0, NULL,
272 release_blocks_proc, NULL);
273 if (retval) {
274 com_err(program_name, retval,
275 _("while clearing journal inode"));
276 exit(1);
277 }
278 memset(&inode, 0, sizeof(inode));
279 ext2fs_mark_bb_dirty(fs);
280 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
281 } else
282 inode.i_flags &= ~EXT2_IMMUTABLE_FL;
283 retval = ext2fs_write_inode(fs, ino, &inode);
284 if (retval) {
285 com_err(program_name, retval,
286 _("while writing journal inode"));
287 exit(1);
288 }
289 fs->super->s_journal_inum = 0;
290 ext2fs_mark_super_dirty(fs);
291 }
292
293 /*
294 * Update the default mount options
295 */
update_mntopts(ext2_filsys fs,char * mntopts)296 static void update_mntopts(ext2_filsys fs, char *mntopts)
297 {
298 struct ext2_super_block *sb= fs->super;
299
300 if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) {
301 fprintf(stderr, _("Invalid mount option set: %s\n"),
302 mntopts);
303 exit(1);
304 }
305 ext2fs_mark_super_dirty(fs);
306 }
307
308 /*
309 * Update the feature set as provided by the user.
310 */
update_feature_set(ext2_filsys fs,char * features)311 static void update_feature_set(ext2_filsys fs, char *features)
312 {
313 struct ext2_super_block *sb= fs->super;
314 __u32 old_compat, old_incompat, old_ro_compat;
315 __u32 old_features[3];
316 int type_err;
317 unsigned int mask_err;
318
319 #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
320 ((&sb->s_feature_compat)[(type)] & (mask)))
321 #define FEATURE_OFF(type, mask) ((old_features[(type)] & (mask)) && \
322 !((&sb->s_feature_compat)[(type)] & (mask)))
323 #define FEATURE_CHANGED(type, mask) ((mask) & \
324 (old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
325
326 old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat;
327 old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat;
328 old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat;
329
330 if (e2p_edit_feature2(features, &sb->s_feature_compat,
331 ok_features, clear_ok_features,
332 &type_err, &mask_err)) {
333 if (!mask_err)
334 fprintf(stderr,
335 _("Invalid filesystem option set: %s\n"),
336 features);
337 else if (type_err & E2P_FEATURE_NEGATE_FLAG)
338 fprintf(stderr, _("Clearing filesystem feature '%s' "
339 "not supported.\n"),
340 e2p_feature2string(type_err &
341 E2P_FEATURE_TYPE_MASK,
342 mask_err));
343 else
344 fprintf(stderr, _("Setting filesystem feature '%s' "
345 "not supported.\n"),
346 e2p_feature2string(type_err, mask_err));
347 exit(1);
348 }
349
350 if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
351 if ((mount_flags & EXT2_MF_MOUNTED) &&
352 !(mount_flags & EXT2_MF_READONLY)) {
353 fputs(_("The has_journal flag may only be "
354 "cleared when the filesystem is\n"
355 "unmounted or mounted "
356 "read-only.\n"), stderr);
357 exit(1);
358 }
359 if (sb->s_feature_incompat &
360 EXT3_FEATURE_INCOMPAT_RECOVER) {
361 fputs(_("The needs_recovery flag is set. "
362 "Please run e2fsck before clearing\n"
363 "the has_journal flag.\n"), stderr);
364 exit(1);
365 }
366 if (sb->s_journal_inum) {
367 remove_journal_inode(fs);
368 }
369 if (sb->s_journal_dev) {
370 remove_journal_device(fs);
371 }
372 }
373
374 if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
375 /*
376 * If adding a journal flag, let the create journal
377 * code below handle creating setting the flag and
378 * creating the journal. We supply a default size if
379 * necessary.
380 */
381 if (!journal_size)
382 journal_size = -1;
383 sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL;
384 }
385
386 if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) {
387 if (!sb->s_def_hash_version)
388 sb->s_def_hash_version = EXT2_HASH_TEA;
389 if (uuid_is_null((unsigned char *) sb->s_hash_seed))
390 uuid_generate((unsigned char *) sb->s_hash_seed);
391 }
392
393 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
394 (sb->s_feature_compat || sb->s_feature_ro_compat ||
395 sb->s_feature_incompat))
396 ext2fs_update_dynamic_rev(fs);
397
398 if (FEATURE_CHANGED(E2P_FEATURE_RO_INCOMPAT,
399 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
400 FEATURE_CHANGED(E2P_FEATURE_INCOMPAT,
401 EXT2_FEATURE_INCOMPAT_FILETYPE) ||
402 FEATURE_CHANGED(E2P_FEATURE_COMPAT,
403 EXT2_FEATURE_COMPAT_RESIZE_INODE) ||
404 FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
405 EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) {
406 sb->s_state &= ~EXT2_VALID_FS;
407 printf("\n%s\n", _(please_fsck));
408 }
409
410 if ((old_features[E2P_FEATURE_COMPAT] != sb->s_feature_compat) ||
411 (old_features[E2P_FEATURE_INCOMPAT] != sb->s_feature_incompat) ||
412 (old_features[E2P_FEATURE_RO_INCOMPAT] != sb->s_feature_ro_compat))
413 ext2fs_mark_super_dirty(fs);
414 }
415
416 /*
417 * Add a journal to the filesystem.
418 */
add_journal(ext2_filsys fs)419 static void add_journal(ext2_filsys fs)
420 {
421 unsigned long journal_blocks;
422 errcode_t retval;
423 ext2_filsys jfs;
424 io_manager io_ptr;
425
426 if (fs->super->s_feature_compat &
427 EXT3_FEATURE_COMPAT_HAS_JOURNAL) {
428 fputs(_("The filesystem already has a journal.\n"), stderr);
429 goto err;
430 }
431 if (journal_device) {
432 check_plausibility(journal_device);
433 check_mount(journal_device, 0, _("journal"));
434 #ifdef CONFIG_TESTIO_DEBUG
435 io_ptr = test_io_manager;
436 test_io_backing_manager = unix_io_manager;
437 #else
438 io_ptr = unix_io_manager;
439 #endif
440 retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
441 EXT2_FLAG_JOURNAL_DEV_OK, 0,
442 fs->blocksize, io_ptr, &jfs);
443 if (retval) {
444 com_err(program_name, retval,
445 _("\n\twhile trying to open journal on %s\n"),
446 journal_device);
447 goto err;
448 }
449 printf(_("Creating journal on device %s: "),
450 journal_device);
451 fflush(stdout);
452
453 retval = ext2fs_add_journal_device(fs, jfs);
454 ext2fs_close(jfs);
455 if (retval) {
456 com_err (program_name, retval,
457 _("while adding filesystem to journal on %s"),
458 journal_device);
459 goto err;
460 }
461 fputs(_("done\n"), stdout);
462 } else if (journal_size) {
463 fputs(_("Creating journal inode: "), stdout);
464 fflush(stdout);
465 journal_blocks = figure_journal_size(journal_size, fs);
466
467 retval = ext2fs_add_journal_inode(fs, journal_blocks,
468 journal_flags);
469 if (retval) {
470 fprintf(stderr, "\n");
471 com_err(program_name, retval,
472 _("\n\twhile trying to create journal file"));
473 exit(1);
474 } else
475 fputs(_("done\n"), stdout);
476 /*
477 * If the filesystem wasn't mounted, we need to force
478 * the block group descriptors out.
479 */
480 if ((mount_flags & EXT2_MF_MOUNTED) == 0)
481 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
482 }
483 print_check_message(fs);
484 return;
485
486 err:
487 if (journal_device)
488 free(journal_device);
489 exit(1);
490 }
491
492
parse_e2label_options(int argc,char ** argv)493 static void parse_e2label_options(int argc, char ** argv)
494 {
495 if ((argc < 2) || (argc > 3)) {
496 fputs(_("Usage: e2label device [newlabel]\n"), stderr);
497 exit(1);
498 }
499 io_options = strchr(argv[1], '?');
500 if (io_options)
501 *io_options++ = 0;
502 device_name = blkid_get_devname(NULL, argv[1], NULL);
503 if (!device_name) {
504 com_err("e2label", 0, _("Unable to resolve '%s'"),
505 argv[1]);
506 exit(1);
507 }
508 open_flag = EXT2_FLAG_SOFTSUPP_FEATURES | EXT2_FLAG_JOURNAL_DEV_OK;
509 if (argc == 3) {
510 open_flag |= EXT2_FLAG_RW;
511 L_flag = 1;
512 new_label = argv[2];
513 } else
514 print_label++;
515 }
516
parse_time(char * str)517 static time_t parse_time(char *str)
518 {
519 struct tm ts;
520
521 if (strcmp(str, "now") == 0) {
522 return (time(0));
523 }
524 memset(&ts, 0, sizeof(ts));
525 #ifdef HAVE_STRPTIME
526 strptime(str, "%Y%m%d%H%M%S", &ts);
527 #else
528 sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
529 &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
530 ts.tm_year -= 1900;
531 ts.tm_mon -= 1;
532 if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
533 ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
534 ts.tm_min > 59 || ts.tm_sec > 61)
535 ts.tm_mday = 0;
536 #endif
537 if (ts.tm_mday == 0) {
538 com_err(program_name, 0,
539 _("Couldn't parse date/time specifier: %s"),
540 str);
541 usage();
542 }
543 return (mktime(&ts));
544 }
545
parse_tune2fs_options(int argc,char ** argv)546 static void parse_tune2fs_options(int argc, char **argv)
547 {
548 int c;
549 char * tmp;
550 struct group * gr;
551 struct passwd * pw;
552
553 open_flag = EXT2_FLAG_SOFTSUPP_FEATURES;
554
555 printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
556 while ((c = getopt(argc, argv, "c:e:fg:i:jlm:o:r:s:u:C:E:J:L:M:O:T:U:")) != EOF)
557 switch (c)
558 {
559 case 'c':
560 max_mount_count = strtol (optarg, &tmp, 0);
561 if (*tmp || max_mount_count > 16000) {
562 com_err (program_name, 0,
563 _("bad mounts count - %s"),
564 optarg);
565 usage();
566 }
567 if (max_mount_count == 0)
568 max_mount_count = -1;
569 c_flag = 1;
570 open_flag = EXT2_FLAG_RW;
571 break;
572 case 'C':
573 mount_count = strtoul (optarg, &tmp, 0);
574 if (*tmp || mount_count > 16000) {
575 com_err (program_name, 0,
576 _("bad mounts count - %s"),
577 optarg);
578 usage();
579 }
580 C_flag = 1;
581 open_flag = EXT2_FLAG_RW;
582 break;
583 case 'e':
584 if (strcmp (optarg, "continue") == 0)
585 errors = EXT2_ERRORS_CONTINUE;
586 else if (strcmp (optarg, "remount-ro") == 0)
587 errors = EXT2_ERRORS_RO;
588 else if (strcmp (optarg, "panic") == 0)
589 errors = EXT2_ERRORS_PANIC;
590 else {
591 com_err (program_name, 0,
592 _("bad error behavior - %s"),
593 optarg);
594 usage();
595 }
596 e_flag = 1;
597 open_flag = EXT2_FLAG_RW;
598 break;
599 case 'E':
600 extended_cmd = optarg;
601 open_flag |= EXT2_FLAG_RW;
602 break;
603 case 'f': /* Force */
604 f_flag = 1;
605 break;
606 case 'g':
607 resgid = strtoul (optarg, &tmp, 0);
608 if (*tmp) {
609 gr = getgrnam (optarg);
610 if (gr == NULL)
611 tmp = optarg;
612 else {
613 resgid = gr->gr_gid;
614 *tmp =0;
615 }
616 }
617 if (*tmp) {
618 com_err (program_name, 0,
619 _("bad gid/group name - %s"),
620 optarg);
621 usage();
622 }
623 g_flag = 1;
624 open_flag = EXT2_FLAG_RW;
625 break;
626 case 'i':
627 interval = strtoul (optarg, &tmp, 0);
628 switch (*tmp) {
629 case 's':
630 tmp++;
631 break;
632 case '\0':
633 case 'd':
634 case 'D': /* days */
635 interval *= 86400;
636 if (*tmp != '\0')
637 tmp++;
638 break;
639 case 'm':
640 case 'M': /* months! */
641 interval *= 86400 * 30;
642 tmp++;
643 break;
644 case 'w':
645 case 'W': /* weeks */
646 interval *= 86400 * 7;
647 tmp++;
648 break;
649 }
650 if (*tmp) {
651 com_err (program_name, 0,
652 _("bad interval - %s"), optarg);
653 usage();
654 }
655 i_flag = 1;
656 open_flag = EXT2_FLAG_RW;
657 break;
658 case 'j':
659 if (!journal_size)
660 journal_size = -1;
661 open_flag = EXT2_FLAG_RW;
662 break;
663 case 'J':
664 parse_journal_opts(optarg);
665 open_flag = EXT2_FLAG_RW;
666 break;
667 case 'l':
668 l_flag = 1;
669 break;
670 case 'L':
671 new_label = optarg;
672 L_flag = 1;
673 open_flag |= EXT2_FLAG_RW |
674 EXT2_FLAG_JOURNAL_DEV_OK;
675 break;
676 case 'm':
677 reserved_ratio = strtod(optarg, &tmp);
678 if (*tmp || reserved_ratio > 50) {
679 com_err (program_name, 0,
680 _("bad reserved block ratio - %s"),
681 optarg);
682 usage();
683 }
684 m_flag = 1;
685 open_flag = EXT2_FLAG_RW;
686 break;
687 case 'M':
688 new_last_mounted = optarg;
689 M_flag = 1;
690 open_flag = EXT2_FLAG_RW;
691 break;
692 case 'o':
693 if (mntopts_cmd) {
694 com_err (program_name, 0,
695 _("-o may only be specified once"));
696 usage();
697 }
698 mntopts_cmd = optarg;
699 open_flag = EXT2_FLAG_RW;
700 break;
701
702 case 'O':
703 if (features_cmd) {
704 com_err (program_name, 0,
705 _("-O may only be specified once"));
706 usage();
707 }
708 features_cmd = optarg;
709 open_flag = EXT2_FLAG_RW;
710 break;
711 case 'r':
712 reserved_blocks = strtoul (optarg, &tmp, 0);
713 if (*tmp) {
714 com_err (program_name, 0,
715 _("bad reserved blocks count - %s"),
716 optarg);
717 usage();
718 }
719 r_flag = 1;
720 open_flag = EXT2_FLAG_RW;
721 break;
722 case 's': /* Deprecated */
723 s_flag = atoi(optarg);
724 open_flag = EXT2_FLAG_RW;
725 break;
726 case 'T':
727 T_flag = 1;
728 last_check_time = parse_time(optarg);
729 open_flag = EXT2_FLAG_RW;
730 break;
731 case 'u':
732 resuid = strtoul (optarg, &tmp, 0);
733 if (*tmp) {
734 pw = getpwnam (optarg);
735 if (pw == NULL)
736 tmp = optarg;
737 else {
738 resuid = pw->pw_uid;
739 *tmp = 0;
740 }
741 }
742 if (*tmp) {
743 com_err (program_name, 0,
744 _("bad uid/user name - %s"),
745 optarg);
746 usage();
747 }
748 u_flag = 1;
749 open_flag = EXT2_FLAG_RW;
750 break;
751 case 'U':
752 new_UUID = optarg;
753 U_flag = 1;
754 open_flag = EXT2_FLAG_RW |
755 EXT2_FLAG_JOURNAL_DEV_OK;
756 break;
757 default:
758 usage();
759 }
760 if (optind < argc - 1 || optind == argc)
761 usage();
762 if (!open_flag && !l_flag)
763 usage();
764 io_options = strchr(argv[optind], '?');
765 if (io_options)
766 *io_options++ = 0;
767 device_name = blkid_get_devname(NULL, argv[optind], NULL);
768 if (!device_name) {
769 com_err("tune2fs", 0, _("Unable to resolve '%s'"),
770 argv[optind]);
771 exit(1);
772 }
773 }
774
do_findfs(int argc,char ** argv)775 void do_findfs(int argc, char **argv)
776 {
777 char *dev;
778
779 if ((argc != 2) ||
780 (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
781 fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
782 exit(2);
783 }
784 dev = blkid_get_devname(NULL, argv[1], NULL);
785 if (!dev) {
786 com_err("findfs", 0, _("Unable to resolve '%s'"),
787 argv[1]);
788 exit(1);
789 }
790 puts(dev);
791 exit(0);
792 }
793
794 /*
795 * Note! If any extended options are incompatible with the
796 * intersection of the SOFTSUPP features and those features explicitly
797 * enabled for tune2fs, there needs to be an explicit test for them
798 * here.
799 */
parse_extended_opts(ext2_filsys fs,const char * opts)800 static void parse_extended_opts(ext2_filsys fs, const char *opts)
801 {
802 char *buf, *token, *next, *p, *arg;
803 int len;
804 int r_usage = 0;
805
806 len = strlen(opts);
807 buf = malloc(len+1);
808 if (!buf) {
809 fprintf(stderr,
810 _("Couldn't allocate memory to parse options!\n"));
811 exit(1);
812 }
813 strcpy(buf, opts);
814 for (token = buf; token && *token; token = next) {
815 p = strchr(token, ',');
816 next = 0;
817 if (p) {
818 *p = 0;
819 next = p+1;
820 }
821 arg = strchr(token, '=');
822 if (arg) {
823 *arg = 0;
824 arg++;
825 }
826 if (!strcmp(token, "test_fs")) {
827 fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS;
828 printf("Setting test filesystem flag\n");
829 ext2fs_mark_super_dirty(fs);
830 } else if (!strcmp(token, "^test_fs")) {
831 fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
832 printf("Clearing test filesystem flag\n");
833 ext2fs_mark_super_dirty(fs);
834 } else if (strcmp(token, "stride") == 0) {
835 if (!arg) {
836 r_usage++;
837 continue;
838 }
839 stride = strtoul(arg, &p, 0);
840 if (*p || (stride == 0)) {
841 fprintf(stderr,
842 _("Invalid RAID stride: %s\n"),
843 arg);
844 r_usage++;
845 continue;
846 }
847 stride_set = 1;
848 } else if (strcmp(token, "stripe-width") == 0 ||
849 strcmp(token, "stripe_width") == 0) {
850 if (!arg) {
851 r_usage++;
852 continue;
853 }
854 stripe_width = strtoul(arg, &p, 0);
855 if (*p || (stripe_width == 0)) {
856 fprintf(stderr,
857 _("Invalid RAID stripe-width: %s\n"),
858 arg);
859 r_usage++;
860 continue;
861 }
862 stripe_width_set = 1;
863 } else
864 r_usage++;
865 }
866 if (r_usage) {
867 fprintf(stderr, _("\nBad options specified.\n\n"
868 "Extended options are separated by commas, "
869 "and may take an argument which\n"
870 "\tis set off by an equals ('=') sign.\n\n"
871 "Valid extended options are:\n"
872 "\tstride=<RAID per-disk chunk size in blocks>\n"
873 "\tstripe-width=<RAID stride*data disks in blocks>\n"
874 "\ttest_fs\n"
875 "\t^test_fs\n"));
876 free(buf);
877 exit(1);
878 }
879 free(buf);
880 }
881
882
main(int argc,char ** argv)883 int main (int argc, char ** argv)
884 {
885 errcode_t retval;
886 ext2_filsys fs;
887 struct ext2_super_block *sb;
888 io_manager io_ptr;
889
890 #ifdef ENABLE_NLS
891 setlocale(LC_MESSAGES, "");
892 setlocale(LC_CTYPE, "");
893 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
894 textdomain(NLS_CAT_NAME);
895 #endif
896 if (argc && *argv)
897 program_name = *argv;
898 add_error_table(&et_ext2_error_table);
899
900 if (strcmp(get_progname(argv[0]), "findfs") == 0)
901 do_findfs(argc, argv);
902 if (strcmp(get_progname(argv[0]), "e2label") == 0)
903 parse_e2label_options(argc, argv);
904 else
905 parse_tune2fs_options(argc, argv);
906
907 #ifdef CONFIG_TESTIO_DEBUG
908 io_ptr = test_io_manager;
909 test_io_backing_manager = unix_io_manager;
910 #else
911 io_ptr = unix_io_manager;
912 #endif
913 retval = ext2fs_open2(device_name, io_options, open_flag,
914 0, 0, io_ptr, &fs);
915 if (retval) {
916 com_err (program_name, retval, _("while trying to open %s"),
917 device_name);
918 fprintf(stderr,
919 _("Couldn't find valid filesystem superblock.\n"));
920 exit(1);
921 }
922 sb = fs->super;
923 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
924 if ((sb->s_feature_incompat & !EXT2_TUNE2FS_INCOMPAT) ||
925 (sb->s_feature_ro_compat & !EXT2_TUNE2FS_RO_COMPAT)) {
926 fprintf(stderr,
927 _("Filesystem %s has unsupported features enabled.\n"),
928 device_name);
929 exit(1);
930 }
931 if (print_label) {
932 /* For e2label emulation */
933 printf("%.*s\n", (int) sizeof(sb->s_volume_name),
934 sb->s_volume_name);
935 remove_error_table(&et_ext2_error_table);
936 exit(0);
937 }
938 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
939 if (retval) {
940 com_err("ext2fs_check_if_mount", retval,
941 _("while determining whether %s is mounted."),
942 device_name);
943 exit(1);
944 }
945 /* Normally we only need to write out the superblock */
946 fs->flags |= EXT2_FLAG_SUPER_ONLY;
947
948 if (c_flag) {
949 sb->s_max_mnt_count = max_mount_count;
950 ext2fs_mark_super_dirty(fs);
951 printf (_("Setting maximal mount count to %d\n"),
952 max_mount_count);
953 }
954 if (C_flag) {
955 sb->s_mnt_count = mount_count;
956 ext2fs_mark_super_dirty(fs);
957 printf (_("Setting current mount count to %d\n"), mount_count);
958 }
959 if (e_flag) {
960 sb->s_errors = errors;
961 ext2fs_mark_super_dirty(fs);
962 printf (_("Setting error behavior to %d\n"), errors);
963 }
964 if (g_flag) {
965 sb->s_def_resgid = resgid;
966 ext2fs_mark_super_dirty(fs);
967 printf (_("Setting reserved blocks gid to %lu\n"), resgid);
968 }
969 if (i_flag) {
970 sb->s_checkinterval = interval;
971 ext2fs_mark_super_dirty(fs);
972 printf (_("Setting interval between checks to %lu seconds\n"), interval);
973 }
974 if (m_flag) {
975 sb->s_r_blocks_count = e2p_percent(reserved_ratio,
976 sb->s_blocks_count);
977 ext2fs_mark_super_dirty(fs);
978 printf (_("Setting reserved blocks percentage to %g%% (%u blocks)\n"),
979 reserved_ratio, sb->s_r_blocks_count);
980 }
981 if (r_flag) {
982 if (reserved_blocks >= sb->s_blocks_count/2) {
983 com_err (program_name, 0,
984 _("reserved blocks count is too big (%lu)"),
985 reserved_blocks);
986 exit (1);
987 }
988 sb->s_r_blocks_count = reserved_blocks;
989 ext2fs_mark_super_dirty(fs);
990 printf (_("Setting reserved blocks count to %lu\n"),
991 reserved_blocks);
992 }
993 if (s_flag == 1) {
994 if (sb->s_feature_ro_compat &
995 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)
996 fputs(_("\nThe filesystem already has sparse "
997 "superblocks.\n"), stderr);
998 else {
999 sb->s_feature_ro_compat |=
1000 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER;
1001 sb->s_state &= ~EXT2_VALID_FS;
1002 ext2fs_mark_super_dirty(fs);
1003 printf(_("\nSparse superblock flag set. %s"),
1004 _(please_fsck));
1005 }
1006 }
1007 if (s_flag == 0) {
1008 fputs(_("\nClearing the sparse superflag not supported.\n"),
1009 stderr);
1010 exit(1);
1011 }
1012 if (T_flag) {
1013 sb->s_lastcheck = last_check_time;
1014 ext2fs_mark_super_dirty(fs);
1015 printf(_("Setting time filesystem last checked to %s\n"),
1016 ctime(&last_check_time));
1017 }
1018 if (u_flag) {
1019 sb->s_def_resuid = resuid;
1020 ext2fs_mark_super_dirty(fs);
1021 printf (_("Setting reserved blocks uid to %lu\n"), resuid);
1022 }
1023 if (L_flag) {
1024 if (strlen(new_label) > sizeof(sb->s_volume_name))
1025 fputs(_("Warning: label too long, truncating.\n"),
1026 stderr);
1027 memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
1028 strncpy(sb->s_volume_name, new_label,
1029 sizeof(sb->s_volume_name));
1030 ext2fs_mark_super_dirty(fs);
1031 }
1032 if (M_flag) {
1033 memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
1034 strncpy(sb->s_last_mounted, new_last_mounted,
1035 sizeof(sb->s_last_mounted));
1036 ext2fs_mark_super_dirty(fs);
1037 }
1038 if (mntopts_cmd)
1039 update_mntopts(fs, mntopts_cmd);
1040 if (features_cmd)
1041 update_feature_set(fs, features_cmd);
1042 if (extended_cmd)
1043 parse_extended_opts(fs, extended_cmd);
1044 if (journal_size || journal_device)
1045 add_journal(fs);
1046
1047 if (U_flag) {
1048 if ((strcasecmp(new_UUID, "null") == 0) ||
1049 (strcasecmp(new_UUID, "clear") == 0)) {
1050 uuid_clear(sb->s_uuid);
1051 } else if (strcasecmp(new_UUID, "time") == 0) {
1052 uuid_generate_time(sb->s_uuid);
1053 } else if (strcasecmp(new_UUID, "random") == 0) {
1054 uuid_generate(sb->s_uuid);
1055 } else if (uuid_parse(new_UUID, sb->s_uuid)) {
1056 com_err(program_name, 0, _("Invalid UUID format\n"));
1057 exit(1);
1058 }
1059 ext2fs_mark_super_dirty(fs);
1060 }
1061
1062 if (l_flag)
1063 list_super (sb);
1064 if (stride_set) {
1065 sb->s_raid_stride = stride;
1066 ext2fs_mark_super_dirty(fs);
1067 printf(_("Setting stride size to %d\n"), stride);
1068 }
1069 if (stripe_width_set) {
1070 sb->s_raid_stripe_width = stripe_width;
1071 ext2fs_mark_super_dirty(fs);
1072 printf(_("Setting stripe width to %d\n"), stripe_width);
1073 }
1074 remove_error_table(&et_ext2_error_table);
1075 return (ext2fs_close (fs) ? 1 : 0);
1076 }
1077