]> git.ipfire.org Git - thirdparty/e2fsprogs.git/blob - misc/tune2fs.c
chattr/lsattr: update obsolete attribute support
[thirdparty/e2fsprogs.git] / misc / tune2fs.c
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 600 /* for inclusion of strptime() */
29 #include "config.h"
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 #ifdef HAVE_STDLIB_H
41 #include <stdlib.h>
42 #endif
43 #ifdef HAVE_STRINGS_H
44 #include <strings.h> /* for strcasecmp() */
45 #else
46 #define _BSD_SOURCE /* for inclusion of strcasecmp() via <string.h> */
47 #define _DEFAULT_SOURCE /* since glibc 2.20 _BSD_SOURCE is deprecated */
48 #endif
49 #include <string.h>
50 #include <time.h>
51 #include <unistd.h>
52 #include <sys/types.h>
53 #include <libgen.h>
54 #include <limits.h>
55
56 #include "ext2fs/ext2_fs.h"
57 #include "ext2fs/ext2fs.h"
58 #include "ext2fs/kernel-jbd.h"
59 #include "et/com_err.h"
60 #include "support/plausible.h"
61 #include "support/quotaio.h"
62 #include "uuid/uuid.h"
63 #include "e2p/e2p.h"
64 #include "util.h"
65 #include "blkid/blkid.h"
66
67 #include "../version.h"
68 #include "support/nls-enable.h"
69
70 #define QOPT_ENABLE (1)
71 #define QOPT_DISABLE (-1)
72
73 extern int ask_yn(const char *string, int def);
74
75 const char *program_name = "tune2fs";
76 char *device_name;
77 char *new_label, *new_last_mounted, *new_UUID;
78 char *io_options;
79 static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag;
80 static int m_flag, M_flag, Q_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag;
81 static int I_flag;
82 static int clear_mmp;
83 static time_t last_check_time;
84 static int print_label;
85 static int max_mount_count, mount_count, mount_flags;
86 static unsigned long interval;
87 static blk64_t reserved_blocks;
88 static double reserved_ratio;
89 static unsigned long resgid, resuid;
90 static unsigned short errors;
91 static int open_flag;
92 static char *features_cmd;
93 static char *mntopts_cmd;
94 static int stride, stripe_width;
95 static int stride_set, stripe_width_set;
96 static char *extended_cmd;
97 static unsigned long new_inode_size;
98 static char *ext_mount_opts;
99 static int quota_enable[MAXQUOTAS];
100 static int rewrite_checksums;
101 static int feature_64bit;
102 static int fsck_requested;
103 static char *undo_file;
104
105 int journal_size, journal_flags;
106 char *journal_device;
107 static blk64_t journal_location = ~0LL;
108
109 static struct list_head blk_move_list;
110
111 struct blk_move {
112 struct list_head list;
113 blk64_t old_loc;
114 blk64_t new_loc;
115 };
116
117 errcode_t ext2fs_run_ext3_journal(ext2_filsys *fs);
118
119 static const char *fsck_explain = N_("\nThis operation requires a freshly checked filesystem.\n");
120
121 static const char *please_fsck = N_("Please run e2fsck -f on the filesystem.\n");
122 static const char *please_dir_fsck =
123 N_("Please run e2fsck -fD on the filesystem.\n");
124
125 #ifdef CONFIG_BUILD_FINDFS
126 void do_findfs(int argc, char **argv);
127 #endif
128
129 #ifdef CONFIG_JBD_DEBUG /* Enabled by configure --enable-jbd-debug */
130 int journal_enable_debug = -1;
131 #endif
132
133 static void usage(void)
134 {
135 fprintf(stderr,
136 _("Usage: %s [-c max_mounts_count] [-e errors_behavior] [-f] "
137 "[-g group]\n"
138 "\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n"
139 "\t[-m reserved_blocks_percent] [-o [^]mount_options[,...]]\n"
140 "\t[-r reserved_blocks_count] [-u user] [-C mount_count]\n"
141 "\t[-L volume_label] [-M last_mounted_dir]\n"
142 "\t[-O [^]feature[,...]] [-Q quota_options]\n"
143 "\t[-E extended-option[,...]] [-T last_check_time] "
144 "[-U UUID]\n\t[-I new_inode_size] [-z undo_file] device\n"),
145 program_name);
146 exit(1);
147 }
148
149 static __u32 ok_features[3] = {
150 /* Compat */
151 EXT3_FEATURE_COMPAT_HAS_JOURNAL |
152 EXT2_FEATURE_COMPAT_DIR_INDEX,
153 /* Incompat */
154 EXT2_FEATURE_INCOMPAT_FILETYPE |
155 EXT3_FEATURE_INCOMPAT_EXTENTS |
156 EXT4_FEATURE_INCOMPAT_FLEX_BG |
157 EXT4_FEATURE_INCOMPAT_MMP |
158 EXT4_FEATURE_INCOMPAT_64BIT |
159 EXT4_FEATURE_INCOMPAT_ENCRYPT |
160 EXT4_FEATURE_INCOMPAT_CSUM_SEED,
161 /* R/O compat */
162 EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
163 EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
164 EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
165 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
166 EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
167 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
168 EXT4_FEATURE_RO_COMPAT_QUOTA |
169 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM |
170 EXT4_FEATURE_RO_COMPAT_READONLY |
171 EXT4_FEATURE_RO_COMPAT_PROJECT
172 };
173
174 static __u32 clear_ok_features[3] = {
175 /* Compat */
176 EXT3_FEATURE_COMPAT_HAS_JOURNAL |
177 EXT2_FEATURE_COMPAT_RESIZE_INODE |
178 EXT2_FEATURE_COMPAT_DIR_INDEX,
179 /* Incompat */
180 EXT2_FEATURE_INCOMPAT_FILETYPE |
181 EXT4_FEATURE_INCOMPAT_FLEX_BG |
182 EXT4_FEATURE_INCOMPAT_MMP |
183 EXT4_FEATURE_INCOMPAT_64BIT |
184 EXT4_FEATURE_INCOMPAT_CSUM_SEED,
185 /* R/O compat */
186 EXT2_FEATURE_RO_COMPAT_LARGE_FILE |
187 EXT4_FEATURE_RO_COMPAT_HUGE_FILE|
188 EXT4_FEATURE_RO_COMPAT_DIR_NLINK|
189 EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE|
190 EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
191 EXT4_FEATURE_RO_COMPAT_QUOTA |
192 EXT4_FEATURE_RO_COMPAT_PROJECT |
193 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM |
194 EXT4_FEATURE_RO_COMPAT_READONLY
195 };
196
197 /**
198 * Try to get journal super block if any
199 */
200 static int get_journal_sb(ext2_filsys jfs, char buf[SUPERBLOCK_SIZE])
201 {
202 int retval;
203 journal_superblock_t *jsb;
204
205 if (!ext2fs_has_feature_journal_dev(jfs->super)) {
206 return EXT2_ET_UNSUPP_FEATURE;
207 }
208
209 /* Get the journal superblock */
210 if ((retval = io_channel_read_blk64(jfs->io,
211 ext2fs_journal_sb_start(jfs->blocksize), -SUPERBLOCK_SIZE, buf))) {
212 com_err(program_name, retval, "%s",
213 _("while reading journal superblock"));
214 return retval;
215 }
216
217 jsb = (journal_superblock_t *) buf;
218 if ((jsb->s_header.h_magic != (unsigned)ntohl(JFS_MAGIC_NUMBER)) ||
219 (jsb->s_header.h_blocktype != (unsigned)ntohl(JFS_SUPERBLOCK_V2))) {
220 fputs(_("Journal superblock not found!\n"), stderr);
221 return EXT2_ET_BAD_MAGIC;
222 }
223
224 return 0;
225 }
226
227 static __u8 *journal_user(__u8 uuid[UUID_SIZE], __u8 s_users[JFS_USERS_SIZE],
228 int nr_users)
229 {
230 int i;
231 for (i = 0; i < nr_users; i++) {
232 if (memcmp(uuid, &s_users[i * UUID_SIZE], UUID_SIZE) == 0)
233 return &s_users[i * UUID_SIZE];
234 }
235
236 return NULL;
237 }
238
239 /*
240 * Remove an external journal from the filesystem
241 */
242 static int remove_journal_device(ext2_filsys fs)
243 {
244 char *journal_path;
245 ext2_filsys jfs;
246 char buf[SUPERBLOCK_SIZE] __attribute__ ((aligned(8)));
247 journal_superblock_t *jsb;
248 int i, nr_users;
249 errcode_t retval;
250 int commit_remove_journal = 0;
251 io_manager io_ptr;
252
253 if (f_flag)
254 commit_remove_journal = 1; /* force removal even if error */
255
256 uuid_unparse(fs->super->s_journal_uuid, buf);
257 journal_path = blkid_get_devname(NULL, "UUID", buf);
258
259 if (!journal_path) {
260 journal_path =
261 ext2fs_find_block_device(fs->super->s_journal_dev);
262 if (!journal_path)
263 goto no_valid_journal;
264 }
265
266 #ifdef CONFIG_TESTIO_DEBUG
267 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
268 io_ptr = test_io_manager;
269 test_io_backing_manager = unix_io_manager;
270 } else
271 #endif
272 io_ptr = unix_io_manager;
273 retval = ext2fs_open(journal_path, EXT2_FLAG_RW|
274 EXT2_FLAG_JOURNAL_DEV_OK, 0,
275 fs->blocksize, io_ptr, &jfs);
276 if (retval) {
277 com_err(program_name, retval, "%s",
278 _("while trying to open external journal"));
279 goto no_valid_journal;
280 }
281
282 if ((retval = get_journal_sb(jfs, buf))) {
283 if (retval == EXT2_ET_UNSUPP_FEATURE)
284 fprintf(stderr, _("%s is not a journal device.\n"),
285 journal_path);
286 goto no_valid_journal;
287 }
288
289 jsb = (journal_superblock_t *) buf;
290 /* Find the filesystem UUID */
291 nr_users = ntohl(jsb->s_nr_users);
292
293 if (!journal_user(fs->super->s_uuid, jsb->s_users, nr_users)) {
294 fputs(_("Filesystem's UUID not found on journal device.\n"),
295 stderr);
296 commit_remove_journal = 1;
297 goto no_valid_journal;
298 }
299 nr_users--;
300 for (i = 0; i < nr_users; i++)
301 memcpy(&jsb->s_users[i * 16], &jsb->s_users[(i + 1) * 16], 16);
302 jsb->s_nr_users = htonl(nr_users);
303
304 /* Write back the journal superblock */
305 retval = io_channel_write_blk64(jfs->io,
306 ext2fs_journal_sb_start(fs->blocksize),
307 -SUPERBLOCK_SIZE, buf);
308 if (retval) {
309 com_err(program_name, retval,
310 "while writing journal superblock.");
311 goto no_valid_journal;
312 }
313
314 commit_remove_journal = 1;
315
316 no_valid_journal:
317 if (commit_remove_journal == 0) {
318 fputs(_("Cannot locate journal device. It was NOT removed\n"
319 "Use -f option to remove missing journal device.\n"),
320 stderr);
321 return 1;
322 }
323 fs->super->s_journal_dev = 0;
324 memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks));
325 uuid_clear(fs->super->s_journal_uuid);
326 ext2fs_mark_super_dirty(fs);
327 fputs(_("Journal removed\n"), stdout);
328 free(journal_path);
329
330 return 0;
331 }
332
333 /* Helper function for remove_journal_inode */
334 static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr,
335 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
336 blk64_t ref_block EXT2FS_ATTR((unused)),
337 int ref_offset EXT2FS_ATTR((unused)),
338 void *private EXT2FS_ATTR((unused)))
339 {
340 blk64_t block;
341 int group;
342
343 block = *blocknr;
344 ext2fs_unmark_block_bitmap2(fs->block_map, block);
345 group = ext2fs_group_of_blk2(fs, block);
346 ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1);
347 ext2fs_group_desc_csum_set(fs, group);
348 ext2fs_free_blocks_count_add(fs->super, EXT2FS_CLUSTER_RATIO(fs));
349 return 0;
350 }
351
352 /*
353 * Remove the journal inode from the filesystem
354 */
355 static errcode_t remove_journal_inode(ext2_filsys fs)
356 {
357 struct ext2_inode inode;
358 errcode_t retval;
359 ino_t ino = fs->super->s_journal_inum;
360
361 retval = ext2fs_read_inode(fs, ino, &inode);
362 if (retval) {
363 com_err(program_name, retval, "%s",
364 _("while reading journal inode"));
365 return retval;
366 }
367 if (ino == EXT2_JOURNAL_INO) {
368 retval = ext2fs_read_bitmaps(fs);
369 if (retval) {
370 com_err(program_name, retval, "%s",
371 _("while reading bitmaps"));
372 return retval;
373 }
374 retval = ext2fs_block_iterate3(fs, ino,
375 BLOCK_FLAG_READ_ONLY, NULL,
376 release_blocks_proc, NULL);
377 if (retval) {
378 com_err(program_name, retval, "%s",
379 _("while clearing journal inode"));
380 return retval;
381 }
382 memset(&inode, 0, sizeof(inode));
383 ext2fs_mark_bb_dirty(fs);
384 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
385 } else
386 inode.i_flags &= ~EXT2_IMMUTABLE_FL;
387 retval = ext2fs_write_inode(fs, ino, &inode);
388 if (retval) {
389 com_err(program_name, retval, "%s",
390 _("while writing journal inode"));
391 return retval;
392 }
393 fs->super->s_journal_inum = 0;
394 memset(fs->super->s_jnl_blocks, 0, sizeof(fs->super->s_jnl_blocks));
395 ext2fs_mark_super_dirty(fs);
396
397 return 0;
398 }
399
400 /*
401 * Update the default mount options
402 */
403 static int update_mntopts(ext2_filsys fs, char *mntopts)
404 {
405 struct ext2_super_block *sb = fs->super;
406
407 if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) {
408 fprintf(stderr, _("Invalid mount option set: %s\n"),
409 mntopts);
410 return 1;
411 }
412 ext2fs_mark_super_dirty(fs);
413
414 return 0;
415 }
416
417 static void check_fsck_needed(ext2_filsys fs, const char *prompt)
418 {
419 /* Refuse to modify anything but a freshly checked valid filesystem. */
420 if (!(fs->super->s_state & EXT2_VALID_FS) ||
421 (fs->super->s_state & EXT2_ERROR_FS) ||
422 (fs->super->s_lastcheck < fs->super->s_mtime)) {
423 puts(_(fsck_explain));
424 puts(_(please_fsck));
425 if (mount_flags & EXT2_MF_READONLY)
426 printf("%s", _("(and reboot afterwards!)\n"));
427 exit(1);
428 }
429
430 /* Give the admin a few seconds to bail out of a dangerous op. */
431 if (!getenv("TUNE2FS_FORCE_PROMPT") && (!isatty(0) || !isatty(1)))
432 return;
433
434 puts(prompt);
435 proceed_question(5);
436 }
437
438 static void request_dir_fsck_afterwards(ext2_filsys fs)
439 {
440 static int requested;
441
442 if (requested++)
443 return;
444 fsck_requested++;
445 fs->super->s_state &= ~EXT2_VALID_FS;
446 puts(_(fsck_explain));
447 puts(_(please_dir_fsck));
448 if (mount_flags & EXT2_MF_READONLY)
449 printf("%s", _("(and reboot afterwards!)\n"));
450 }
451
452 static void request_fsck_afterwards(ext2_filsys fs)
453 {
454 static int requested = 0;
455
456 if (requested++)
457 return;
458 fsck_requested++;
459 fs->super->s_state &= ~EXT2_VALID_FS;
460 printf("\n%s\n", _(please_fsck));
461 if (mount_flags & EXT2_MF_READONLY)
462 printf("%s", _("(and reboot afterwards!)\n"));
463 }
464
465 static void convert_64bit(ext2_filsys fs, int direction)
466 {
467 /*
468 * Is resize2fs going to demand a fsck run? Might as well tell the
469 * user now.
470 */
471 if (!fsck_requested &&
472 ((fs->super->s_state & EXT2_ERROR_FS) ||
473 !(fs->super->s_state & EXT2_VALID_FS) ||
474 fs->super->s_lastcheck < fs->super->s_mtime))
475 request_fsck_afterwards(fs);
476 if (fsck_requested)
477 fprintf(stderr, _("After running e2fsck, please run `resize2fs %s %s"),
478 direction > 0 ? "-b" : "-s", fs->device_name);
479 else
480 fprintf(stderr, _("Please run `resize2fs %s %s"),
481 direction > 0 ? "-b" : "-s", fs->device_name);
482
483 if (undo_file)
484 fprintf(stderr, _(" -z \"%s\""), undo_file);
485 if (direction > 0)
486 fprintf(stderr, _("' to enable 64-bit mode.\n"));
487 else
488 fprintf(stderr, _("' to disable 64-bit mode.\n"));
489 }
490
491 /* Rewrite extents */
492 static errcode_t rewrite_extents(ext2_filsys fs, ext2_ino_t ino,
493 struct ext2_inode *inode)
494 {
495 ext2_extent_handle_t handle;
496 struct ext2fs_extent extent;
497 errcode_t errcode;
498 struct ext2_extent_info info;
499
500 if (!(inode->i_flags & EXT4_EXTENTS_FL) ||
501 !ext2fs_has_feature_metadata_csum(fs->super))
502 return 0;
503
504 errcode = ext2fs_extent_open(fs, ino, &handle);
505 if (errcode)
506 return errcode;
507
508 errcode = ext2fs_extent_get(handle, EXT2_EXTENT_ROOT, &extent);
509 if (errcode)
510 goto out;
511
512 do {
513 errcode = ext2fs_extent_get_info(handle, &info);
514 if (errcode)
515 break;
516
517 /*
518 * If this is the first extent in an extent block that we
519 * haven't visited, rewrite the extent to force the ETB
520 * checksum to be rewritten.
521 */
522 if (info.curr_entry == 1 && info.curr_level != 0 &&
523 !(extent.e_flags & EXT2_EXTENT_FLAGS_SECOND_VISIT)) {
524 errcode = ext2fs_extent_replace(handle, 0, &extent);
525 if (errcode)
526 break;
527 }
528
529 /* Skip to the end of a block of leaf nodes */
530 if (extent.e_flags & EXT2_EXTENT_FLAGS_LEAF) {
531 errcode = ext2fs_extent_get(handle,
532 EXT2_EXTENT_LAST_SIB,
533 &extent);
534 if (errcode)
535 break;
536 }
537
538 errcode = ext2fs_extent_get(handle, EXT2_EXTENT_NEXT, &extent);
539 } while (errcode == 0);
540
541 out:
542 /* Ok if we run off the end */
543 if (errcode == EXT2_ET_EXTENT_NO_NEXT)
544 errcode = 0;
545 ext2fs_extent_free(handle);
546 return errcode;
547 }
548
549 /*
550 * Rewrite directory blocks with checksums
551 */
552 struct rewrite_dir_context {
553 char *buf;
554 errcode_t errcode;
555 ext2_ino_t dir;
556 int is_htree;
557 };
558
559 static int rewrite_dir_block(ext2_filsys fs,
560 blk64_t *blocknr,
561 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
562 blk64_t ref_block EXT2FS_ATTR((unused)),
563 int ref_offset EXT2FS_ATTR((unused)),
564 void *priv_data)
565 {
566 struct ext2_dx_countlimit *dcl = NULL;
567 struct rewrite_dir_context *ctx = priv_data;
568 int dcl_offset, changed = 0;
569
570 ctx->errcode = ext2fs_read_dir_block4(fs, *blocknr, ctx->buf, 0,
571 ctx->dir);
572 if (ctx->errcode)
573 return BLOCK_ABORT;
574
575 /* if htree node... */
576 if (ctx->is_htree)
577 ext2fs_get_dx_countlimit(fs, (struct ext2_dir_entry *)ctx->buf,
578 &dcl, &dcl_offset);
579 if (dcl) {
580 if (!ext2fs_has_feature_metadata_csum(fs->super)) {
581 /* Ensure limit is the max size */
582 int max_entries = (fs->blocksize - dcl_offset) /
583 sizeof(struct ext2_dx_entry);
584 if (ext2fs_le16_to_cpu(dcl->limit) != max_entries) {
585 changed = 1;
586 dcl->limit = ext2fs_cpu_to_le16(max_entries);
587 }
588 } else {
589 /* If htree block is full then rebuild the dir */
590 if (ext2fs_le16_to_cpu(dcl->count) ==
591 ext2fs_le16_to_cpu(dcl->limit)) {
592 request_dir_fsck_afterwards(fs);
593 return 0;
594 }
595 /*
596 * Ensure dcl->limit is small enough to leave room for
597 * the checksum tail.
598 */
599 int max_entries = (fs->blocksize - (dcl_offset +
600 sizeof(struct ext2_dx_tail))) /
601 sizeof(struct ext2_dx_entry);
602 if (ext2fs_le16_to_cpu(dcl->limit) != max_entries)
603 dcl->limit = ext2fs_cpu_to_le16(max_entries);
604 /* Always rewrite checksum */
605 changed = 1;
606 }
607 } else {
608 unsigned int rec_len, name_size;
609 char *top = ctx->buf + fs->blocksize;
610 struct ext2_dir_entry *de = (struct ext2_dir_entry *)ctx->buf;
611 struct ext2_dir_entry *last_de = NULL, *penultimate_de = NULL;
612
613 /* Find last and penultimate dirent */
614 while ((char *)de < top) {
615 penultimate_de = last_de;
616 last_de = de;
617 ctx->errcode = ext2fs_get_rec_len(fs, de, &rec_len);
618 if (!ctx->errcode && !rec_len)
619 ctx->errcode = EXT2_ET_DIR_CORRUPTED;
620 if (ctx->errcode)
621 return BLOCK_ABORT;
622 de = (struct ext2_dir_entry *)(((char *)de) + rec_len);
623 }
624 ctx->errcode = ext2fs_get_rec_len(fs, last_de, &rec_len);
625 if (ctx->errcode)
626 return BLOCK_ABORT;
627 name_size = ext2fs_dirent_name_len(last_de);
628
629 if (!ext2fs_has_feature_metadata_csum(fs->super)) {
630 if (!penultimate_de)
631 return 0;
632 if (last_de->inode ||
633 name_size ||
634 rec_len != sizeof(struct ext2_dir_entry_tail))
635 return 0;
636 /*
637 * The last dirent is unused and the right length to
638 * have stored a checksum. Erase it.
639 */
640 ctx->errcode = ext2fs_get_rec_len(fs, penultimate_de,
641 &rec_len);
642 if (!rec_len)
643 ctx->errcode = EXT2_ET_DIR_CORRUPTED;
644 if (ctx->errcode)
645 return BLOCK_ABORT;
646 ext2fs_set_rec_len(fs, rec_len +
647 sizeof(struct ext2_dir_entry_tail),
648 penultimate_de);
649 changed = 1;
650 } else {
651 unsigned csum_size = sizeof(struct ext2_dir_entry_tail);
652 struct ext2_dir_entry_tail *t;
653
654 /*
655 * If the last dirent looks like the tail, just update
656 * the checksum.
657 */
658 if (!last_de->inode &&
659 rec_len == csum_size) {
660 t = (struct ext2_dir_entry_tail *)last_de;
661 t->det_reserved_name_len =
662 EXT2_DIR_NAME_LEN_CSUM;
663 changed = 1;
664 goto out;
665 }
666 if (name_size & 3)
667 name_size = (name_size & ~3) + 4;
668 /* If there's not enough space for the tail, e2fsck */
669 if (rec_len <= (8 + name_size + csum_size)) {
670 request_dir_fsck_afterwards(fs);
671 return 0;
672 }
673 /* Shorten that last de and insert the tail */
674 ext2fs_set_rec_len(fs, rec_len - csum_size, last_de);
675 t = EXT2_DIRENT_TAIL(ctx->buf, fs->blocksize);
676 ext2fs_initialize_dirent_tail(fs, t);
677
678 /* Always update checksum */
679 changed = 1;
680 }
681 }
682
683 out:
684 if (!changed)
685 return 0;
686
687 ctx->errcode = ext2fs_write_dir_block4(fs, *blocknr, ctx->buf,
688 0, ctx->dir);
689 if (ctx->errcode)
690 return BLOCK_ABORT;
691
692 return 0;
693 }
694
695 static errcode_t rewrite_directory(ext2_filsys fs, ext2_ino_t dir,
696 struct ext2_inode *inode)
697 {
698 errcode_t retval;
699 struct rewrite_dir_context ctx;
700
701 retval = ext2fs_get_mem(fs->blocksize, &ctx.buf);
702 if (retval)
703 return retval;
704
705 ctx.is_htree = (inode->i_flags & EXT2_INDEX_FL);
706 ctx.dir = dir;
707 ctx.errcode = 0;
708 retval = ext2fs_block_iterate3(fs, dir, BLOCK_FLAG_READ_ONLY |
709 BLOCK_FLAG_DATA_ONLY,
710 0, rewrite_dir_block, &ctx);
711
712 ext2fs_free_mem(&ctx.buf);
713 if (retval)
714 return retval;
715
716 return ctx.errcode;
717 }
718
719 /*
720 * Forcibly set checksums in all inodes.
721 */
722 static void rewrite_inodes(ext2_filsys fs)
723 {
724 int length = EXT2_INODE_SIZE(fs->super);
725 struct ext2_inode *inode, *zero;
726 char *ea_buf;
727 ext2_inode_scan scan;
728 errcode_t retval;
729 ext2_ino_t ino;
730 blk64_t file_acl_block;
731 int inode_dirty;
732
733 if (fs->super->s_creator_os == EXT2_OS_HURD)
734 return;
735
736 retval = ext2fs_open_inode_scan(fs, 0, &scan);
737 if (retval) {
738 com_err("set_csum", retval, "while opening inode scan");
739 exit(1);
740 }
741
742 retval = ext2fs_get_mem(length, &inode);
743 if (retval) {
744 com_err("set_csum", retval, "while allocating memory");
745 exit(1);
746 }
747
748 retval = ext2fs_get_memzero(length, &zero);
749 if (retval) {
750 com_err("set_csum", retval, "while allocating memory");
751 exit(1);
752 }
753
754 retval = ext2fs_get_mem(fs->blocksize, &ea_buf);
755 if (retval) {
756 com_err("set_csum", retval, "while allocating memory");
757 exit(1);
758 }
759
760 do {
761 retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
762 if (retval) {
763 com_err("set_csum", retval, "while getting next inode");
764 exit(1);
765 }
766 if (!ino)
767 break;
768 if (ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
769 inode_dirty = 1;
770 } else {
771 if (memcmp(inode, zero, length) != 0) {
772 memset(inode, 0, length);
773 inode_dirty = 1;
774 } else {
775 inode_dirty = 0;
776 }
777 }
778
779 if (inode_dirty) {
780 retval = ext2fs_write_inode_full(fs, ino, inode,
781 length);
782 if (retval) {
783 com_err("set_csum", retval, "while writing "
784 "inode");
785 exit(1);
786 }
787 }
788
789 retval = rewrite_extents(fs, ino, inode);
790 if (retval) {
791 com_err("rewrite_extents", retval,
792 "while rewriting extents");
793 exit(1);
794 }
795
796 if (LINUX_S_ISDIR(inode->i_mode) &&
797 ext2fs_inode_has_valid_blocks2(fs, inode)) {
798 retval = rewrite_directory(fs, ino, inode);
799 if (retval) {
800 com_err("rewrite_directory", retval,
801 "while rewriting directories");
802 exit(1);
803 }
804 }
805
806 file_acl_block = ext2fs_file_acl_block(fs, inode);
807 if (!file_acl_block)
808 continue;
809 retval = ext2fs_read_ext_attr3(fs, file_acl_block, ea_buf, ino);
810 if (retval) {
811 com_err("rewrite_eablock", retval,
812 "while rewriting extended attribute");
813 exit(1);
814 }
815 retval = ext2fs_write_ext_attr3(fs, file_acl_block, ea_buf,
816 ino);
817 if (retval) {
818 com_err("rewrite_eablock", retval,
819 "while rewriting extended attribute");
820 exit(1);
821 }
822 } while (ino);
823
824 ext2fs_free_mem(&zero);
825 ext2fs_free_mem(&inode);
826 ext2fs_free_mem(&ea_buf);
827 ext2fs_close_inode_scan(scan);
828 }
829
830 static void rewrite_metadata_checksums(ext2_filsys fs)
831 {
832 errcode_t retval;
833 dgrp_t i;
834
835 fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
836 ext2fs_init_csum_seed(fs);
837 for (i = 0; i < fs->group_desc_count; i++)
838 ext2fs_group_desc_csum_set(fs, i);
839 retval = ext2fs_read_bitmaps(fs);
840 if (retval) {
841 com_err("rewrite_metadata_checksums", retval,
842 "while reading bitmaps");
843 exit(1);
844 }
845 rewrite_inodes(fs);
846 ext2fs_mark_ib_dirty(fs);
847 ext2fs_mark_bb_dirty(fs);
848 ext2fs_mmp_update2(fs, 1);
849 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
850 fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
851 if (ext2fs_has_feature_metadata_csum(fs->super))
852 fs->super->s_checksum_type = EXT2_CRC32C_CHKSUM;
853 else
854 fs->super->s_checksum_type = 0;
855 ext2fs_mark_super_dirty(fs);
856 }
857
858 static void enable_uninit_bg(ext2_filsys fs)
859 {
860 struct ext2_group_desc *gd;
861 dgrp_t i;
862
863 for (i = 0; i < fs->group_desc_count; i++) {
864 gd = ext2fs_group_desc(fs, fs->group_desc, i);
865 gd->bg_itable_unused = 0;
866 gd->bg_flags = EXT2_BG_INODE_ZEROED;
867 ext2fs_group_desc_csum_set(fs, i);
868 }
869 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
870 }
871
872 static errcode_t zero_empty_inodes(ext2_filsys fs)
873 {
874 int length = EXT2_INODE_SIZE(fs->super);
875 struct ext2_inode *inode = NULL;
876 ext2_inode_scan scan;
877 errcode_t retval;
878 ext2_ino_t ino;
879
880 retval = ext2fs_open_inode_scan(fs, 0, &scan);
881 if (retval)
882 goto out;
883
884 retval = ext2fs_get_mem(length, &inode);
885 if (retval)
886 goto out;
887
888 do {
889 retval = ext2fs_get_next_inode_full(scan, &ino, inode, length);
890 if (retval)
891 goto out;
892 if (!ino)
893 break;
894 if (!ext2fs_test_inode_bitmap2(fs->inode_map, ino)) {
895 memset(inode, 0, length);
896 retval = ext2fs_write_inode_full(fs, ino, inode,
897 length);
898 if (retval)
899 goto out;
900 }
901 } while (1);
902
903 out:
904 ext2fs_free_mem(&inode);
905 ext2fs_close_inode_scan(scan);
906 return retval;
907 }
908
909 static errcode_t disable_uninit_bg(ext2_filsys fs, __u32 csum_feature_flag)
910 {
911 struct ext2_group_desc *gd;
912 dgrp_t i;
913 errcode_t retval;
914 blk64_t b, c, d;
915
916 /* Load bitmaps to ensure that the uninit ones get written out */
917 fs->super->s_feature_ro_compat |= csum_feature_flag;
918 retval = ext2fs_read_bitmaps(fs);
919 fs->super->s_feature_ro_compat &= ~csum_feature_flag;
920 if (retval) {
921 com_err("disable_uninit_bg", retval,
922 "while reading bitmaps");
923 request_fsck_afterwards(fs);
924 return retval;
925 }
926 ext2fs_mark_ib_dirty(fs);
927 ext2fs_mark_bb_dirty(fs);
928
929 /* If we're only turning off uninit_bg, zero the inodes */
930 if (csum_feature_flag == EXT4_FEATURE_RO_COMPAT_GDT_CSUM) {
931 retval = zero_empty_inodes(fs);
932 if (retval) {
933 com_err("disable_uninit_bg", retval,
934 "while zeroing unused inodes");
935 request_fsck_afterwards(fs);
936 return retval;
937 }
938 }
939
940 /* The bbitmap is zeroed; we must mark group metadata blocks in use */
941 for (i = 0; i < fs->group_desc_count; i++) {
942 b = ext2fs_block_bitmap_loc(fs, i);
943 ext2fs_mark_block_bitmap2(fs->block_map, b);
944 b = ext2fs_inode_bitmap_loc(fs, i);
945 ext2fs_mark_block_bitmap2(fs->block_map, b);
946
947 retval = ext2fs_super_and_bgd_loc2(fs, i, &b, &c, &d, NULL);
948 if (retval == 0 && b)
949 ext2fs_mark_block_bitmap2(fs->block_map, b);
950 if (retval == 0 && c)
951 ext2fs_mark_block_bitmap2(fs->block_map, c);
952 if (retval == 0 && d)
953 ext2fs_mark_block_bitmap2(fs->block_map, d);
954 if (retval) {
955 com_err("disable_uninit_bg", retval,
956 "while initializing block bitmaps");
957 request_fsck_afterwards(fs);
958 }
959
960 gd = ext2fs_group_desc(fs, fs->group_desc, i);
961 gd->bg_itable_unused = 0;
962 gd->bg_flags = 0;
963 ext2fs_group_desc_csum_set(fs, i);
964 }
965 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
966 ext2fs_mark_super_dirty(fs);
967
968 return 0;
969 }
970
971 static void
972 try_confirm_csum_seed_support(void)
973 {
974 if (access("/sys/fs/ext4/features/metadata_csum_seed", R_OK))
975 fputs(_("WARNING: Could not confirm kernel support for "
976 "metadata_csum_seed.\n This requires Linux >= "
977 "v4.4.\n"), stderr);
978 }
979
980 /*
981 * Update the feature set as provided by the user.
982 */
983 static int update_feature_set(ext2_filsys fs, char *features)
984 {
985 struct ext2_super_block *sb = fs->super;
986 __u32 old_features[3];
987 int type_err;
988 unsigned int mask_err;
989 errcode_t err;
990 enum quota_type qtype;
991
992 #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \
993 ((&sb->s_feature_compat)[(type)] & (mask)))
994 #define FEATURE_OFF(type, mask) ((old_features[(type)] & (mask)) && \
995 !((&sb->s_feature_compat)[(type)] & (mask)))
996 #define FEATURE_CHANGED(type, mask) ((mask) & \
997 (old_features[(type)] ^ (&sb->s_feature_compat)[(type)]))
998
999 old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat;
1000 old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat;
1001 old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat;
1002
1003 if (e2p_edit_feature2(features, &sb->s_feature_compat,
1004 ok_features, clear_ok_features,
1005 &type_err, &mask_err)) {
1006 if (!mask_err)
1007 fprintf(stderr,
1008 _("Invalid filesystem option set: %s\n"),
1009 features);
1010 else if (type_err & E2P_FEATURE_NEGATE_FLAG)
1011 fprintf(stderr, _("Clearing filesystem feature '%s' "
1012 "not supported.\n"),
1013 e2p_feature2string(type_err &
1014 E2P_FEATURE_TYPE_MASK,
1015 mask_err));
1016 else
1017 fprintf(stderr, _("Setting filesystem feature '%s' "
1018 "not supported.\n"),
1019 e2p_feature2string(type_err, mask_err));
1020 return 1;
1021 }
1022
1023 if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
1024 if ((mount_flags & EXT2_MF_MOUNTED) &&
1025 !(mount_flags & EXT2_MF_READONLY)) {
1026 fputs(_("The has_journal feature may only be "
1027 "cleared when the filesystem is\n"
1028 "unmounted or mounted "
1029 "read-only.\n"), stderr);
1030 return 1;
1031 }
1032 if (ext2fs_has_feature_journal_needs_recovery(sb) &&
1033 f_flag < 2) {
1034 fputs(_("The needs_recovery flag is set. "
1035 "Please run e2fsck before clearing\n"
1036 "the has_journal flag.\n"), stderr);
1037 return 1;
1038 }
1039 if (sb->s_journal_inum) {
1040 if (remove_journal_inode(fs))
1041 return 1;
1042 }
1043 if (sb->s_journal_dev) {
1044 if (remove_journal_device(fs))
1045 return 1;
1046 }
1047 }
1048
1049 if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
1050 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) {
1051 if (ext2fs_has_feature_meta_bg(sb)) {
1052 fputs(_("Setting filesystem feature 'sparse_super' "
1053 "not supported\nfor filesystems with "
1054 "the meta_bg feature enabled.\n"),
1055 stderr);
1056 return 1;
1057 }
1058 }
1059
1060 if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) {
1061 int error;
1062
1063 if ((mount_flags & EXT2_MF_MOUNTED) ||
1064 (mount_flags & EXT2_MF_READONLY)) {
1065 fputs(_("The multiple mount protection feature can't\n"
1066 "be set if the filesystem is mounted or\n"
1067 "read-only.\n"), stderr);
1068 return 1;
1069 }
1070
1071 error = ext2fs_mmp_init(fs);
1072 if (error) {
1073 fputs(_("\nError while enabling multiple mount "
1074 "protection feature."), stderr);
1075 return 1;
1076 }
1077
1078 /*
1079 * We want to update group desc with the new free blocks count
1080 */
1081 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
1082
1083 printf(_("Multiple mount protection has been enabled "
1084 "with update interval %ds.\n"),
1085 sb->s_mmp_update_interval);
1086 }
1087
1088 if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) {
1089 int error;
1090
1091 if (mount_flags & EXT2_MF_READONLY) {
1092 fputs(_("The multiple mount protection feature cannot\n"
1093 "be disabled if the filesystem is readonly.\n"),
1094 stderr);
1095 return 1;
1096 }
1097
1098 error = ext2fs_read_bitmaps(fs);
1099 if (error) {
1100 fputs(_("Error while reading bitmaps\n"), stderr);
1101 return 1;
1102 }
1103
1104 error = ext2fs_mmp_read(fs, sb->s_mmp_block, NULL);
1105 if (error) {
1106 struct mmp_struct *mmp_cmp = fs->mmp_cmp;
1107
1108 if (error == EXT2_ET_MMP_MAGIC_INVALID)
1109 printf(_("Magic number in MMP block does not "
1110 "match. expected: %x, actual: %x\n"),
1111 EXT4_MMP_MAGIC, mmp_cmp->mmp_magic);
1112 else
1113 com_err(program_name, error, "%s",
1114 _("while reading MMP block."));
1115 goto mmp_error;
1116 }
1117
1118 /* We need to force out the group descriptors as well */
1119 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
1120 ext2fs_block_alloc_stats2(fs, sb->s_mmp_block, -1);
1121 mmp_error:
1122 sb->s_mmp_block = 0;
1123 sb->s_mmp_update_interval = 0;
1124 }
1125
1126 if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) {
1127 /*
1128 * If adding a journal flag, let the create journal
1129 * code below handle setting the flag and creating the
1130 * journal. We supply a default size if necessary.
1131 */
1132 if (!journal_size)
1133 journal_size = -1;
1134 ext2fs_clear_feature_journal(sb);
1135 }
1136
1137 if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) {
1138 if (!sb->s_def_hash_version)
1139 sb->s_def_hash_version = EXT2_HASH_HALF_MD4;
1140 if (uuid_is_null((unsigned char *) sb->s_hash_seed))
1141 uuid_generate((unsigned char *) sb->s_hash_seed);
1142 }
1143
1144 if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG)) {
1145 if (ext2fs_check_desc(fs)) {
1146 fputs(_("Clearing the flex_bg flag would "
1147 "cause the the filesystem to be\n"
1148 "inconsistent.\n"), stderr);
1149 return 1;
1150 }
1151 }
1152
1153 if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
1154 EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) {
1155 if ((mount_flags & EXT2_MF_MOUNTED) &&
1156 !(mount_flags & EXT2_MF_READONLY)) {
1157 fputs(_("The huge_file feature may only be "
1158 "cleared when the filesystem is\n"
1159 "unmounted or mounted "
1160 "read-only.\n"), stderr);
1161 return 1;
1162 }
1163 }
1164
1165 if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
1166 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
1167 check_fsck_needed(fs,
1168 _("Enabling checksums could take some time."));
1169 if (mount_flags & EXT2_MF_MOUNTED) {
1170 fputs(_("Cannot enable metadata_csum on a mounted "
1171 "filesystem!\n"), stderr);
1172 exit(1);
1173 }
1174 if (!ext2fs_has_feature_extents(fs->super))
1175 printf("%s",
1176 _("Extents are not enabled. The file extent "
1177 "tree can be checksummed, whereas block maps "
1178 "cannot. Not enabling extents reduces the "
1179 "coverage of metadata checksumming. "
1180 "Re-run with -O extent to rectify.\n"));
1181 if (!ext2fs_has_feature_64bit(fs->super))
1182 printf("%s",
1183 _("64-bit filesystem support is not enabled. "
1184 "The larger fields afforded by this feature "
1185 "enable full-strength checksumming. "
1186 "Run resize2fs -b to rectify.\n"));
1187 rewrite_checksums = 1;
1188 /* metadata_csum supersedes uninit_bg */
1189 ext2fs_clear_feature_gdt_csum(fs->super);
1190
1191 /* if uninit_bg was previously off, rewrite group desc */
1192 if (!(old_features[E2P_FEATURE_RO_INCOMPAT] &
1193 EXT4_FEATURE_RO_COMPAT_GDT_CSUM))
1194 enable_uninit_bg(fs);
1195
1196 /*
1197 * Since metadata_csum supersedes uninit_bg, pretend like
1198 * uninit_bg has been off all along.
1199 */
1200 old_features[E2P_FEATURE_RO_INCOMPAT] &=
1201 ~EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
1202 }
1203
1204 if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
1205 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM)) {
1206 __u32 test_features[3];
1207
1208 check_fsck_needed(fs,
1209 _("Disabling checksums could take some time."));
1210 if (mount_flags & EXT2_MF_MOUNTED) {
1211 fputs(_("Cannot disable metadata_csum on a mounted "
1212 "filesystem!\n"), stderr);
1213 exit(1);
1214 }
1215 rewrite_checksums = 1;
1216
1217 /* Enable uninit_bg unless the user expressly turned it off */
1218 memcpy(test_features, old_features, sizeof(test_features));
1219 test_features[E2P_FEATURE_RO_INCOMPAT] |=
1220 EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
1221 e2p_edit_feature2(features, test_features, ok_features,
1222 clear_ok_features, NULL, NULL);
1223 if (test_features[E2P_FEATURE_RO_INCOMPAT] &
1224 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)
1225 ext2fs_set_feature_gdt_csum(fs->super);
1226
1227 /*
1228 * If we're turning off metadata_csum and not turning on
1229 * uninit_bg, rewrite group desc.
1230 */
1231 if (!ext2fs_has_feature_gdt_csum(fs->super)) {
1232 err = disable_uninit_bg(fs,
1233 EXT4_FEATURE_RO_COMPAT_METADATA_CSUM);
1234 if (err)
1235 return 1;
1236 } else
1237 /*
1238 * metadata_csum previously provided uninit_bg, so if
1239 * we're also setting the uninit_bg feature bit,
1240 * pretend like it was previously enabled. Checksums
1241 * will be rewritten with crc16 later.
1242 */
1243 old_features[E2P_FEATURE_RO_INCOMPAT] |=
1244 EXT4_FEATURE_RO_COMPAT_GDT_CSUM;
1245 fs->super->s_checksum_seed = 0;
1246 ext2fs_clear_feature_csum_seed(fs->super);
1247 }
1248
1249 if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
1250 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
1251 /* Do not enable uninit_bg when metadata_csum enabled */
1252 if (ext2fs_has_feature_metadata_csum(fs->super))
1253 ext2fs_clear_feature_gdt_csum(fs->super);
1254 else
1255 enable_uninit_bg(fs);
1256 }
1257
1258 if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
1259 EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) {
1260 err = disable_uninit_bg(fs,
1261 EXT4_FEATURE_RO_COMPAT_GDT_CSUM);
1262 if (err)
1263 return 1;
1264 }
1265
1266 /*
1267 * We don't actually toggle 64bit; resize2fs does that. But this
1268 * must come after the metadata_csum feature_on so that it won't
1269 * complain about the lack of 64bit.
1270 */
1271 if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
1272 EXT4_FEATURE_INCOMPAT_64BIT)) {
1273 if (mount_flags & EXT2_MF_MOUNTED) {
1274 fprintf(stderr, _("Cannot enable 64-bit mode "
1275 "while mounted!\n"));
1276 exit(1);
1277 }
1278 ext2fs_clear_feature_64bit(sb);
1279 feature_64bit = 1;
1280 }
1281 if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
1282 EXT4_FEATURE_INCOMPAT_64BIT)) {
1283 if (mount_flags & EXT2_MF_MOUNTED) {
1284 fprintf(stderr, _("Cannot disable 64-bit mode "
1285 "while mounted!\n"));
1286 exit(1);
1287 }
1288 ext2fs_set_feature_64bit(sb);
1289 feature_64bit = -1;
1290 }
1291
1292 if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
1293 EXT4_FEATURE_RO_COMPAT_QUOTA)) {
1294 /*
1295 * Set the Q_flag here and handle the quota options in the code
1296 * below.
1297 */
1298 if (!Q_flag) {
1299 Q_flag = 1;
1300 /* Enable usr/grp quota by default */
1301 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
1302 if (qtype != PRJQUOTA)
1303 quota_enable[qtype] = QOPT_ENABLE;
1304 else
1305 quota_enable[qtype] = QOPT_DISABLE;
1306 }
1307 }
1308 ext2fs_clear_feature_quota(sb);
1309 }
1310
1311 if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT,
1312 EXT4_FEATURE_RO_COMPAT_PROJECT)) {
1313 if (fs->super->s_inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
1314 fprintf(stderr, _("Cannot enable project feature; "
1315 "inode size too small.\n"));
1316 exit(1);
1317 }
1318 Q_flag = 1;
1319 quota_enable[PRJQUOTA] = QOPT_ENABLE;
1320 }
1321
1322 if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
1323 EXT4_FEATURE_RO_COMPAT_PROJECT)) {
1324 Q_flag = 1;
1325 quota_enable[PRJQUOTA] = QOPT_DISABLE;
1326 }
1327
1328 if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
1329 EXT4_FEATURE_RO_COMPAT_QUOTA)) {
1330 /*
1331 * Set the Q_flag here and handle the quota options in the code
1332 * below.
1333 */
1334 if (Q_flag)
1335 fputs(_("\nWarning: '^quota' option overrides '-Q'"
1336 "arguments.\n"), stderr);
1337 Q_flag = 1;
1338 /* Disable all quota by default */
1339 for (qtype = 0; qtype < MAXQUOTAS; qtype++)
1340 quota_enable[qtype] = QOPT_DISABLE;
1341 }
1342
1343 if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_ENCRYPT)) {
1344 fs->super->s_encrypt_algos[0] =
1345 EXT4_ENCRYPTION_MODE_AES_256_XTS;
1346 fs->super->s_encrypt_algos[1] =
1347 EXT4_ENCRYPTION_MODE_AES_256_CTS;
1348 }
1349
1350 if (FEATURE_ON(E2P_FEATURE_INCOMPAT,
1351 EXT4_FEATURE_INCOMPAT_CSUM_SEED)) {
1352 if (!ext2fs_has_feature_metadata_csum(sb)) {
1353 fputs(_("Setting feature 'metadata_csum_seed' "
1354 "is only supported\non filesystems with "
1355 "the metadata_csum feature enabled.\n"),
1356 stderr);
1357 return 1;
1358 }
1359 try_confirm_csum_seed_support();
1360 fs->super->s_checksum_seed = fs->csum_seed;
1361 }
1362
1363 if (FEATURE_OFF(E2P_FEATURE_INCOMPAT,
1364 EXT4_FEATURE_INCOMPAT_CSUM_SEED)) {
1365 __le32 uuid_seed;
1366
1367 uuid_seed = ext2fs_crc32c_le(~0, fs->super->s_uuid,
1368 sizeof(fs->super->s_uuid));
1369 if (fs->super->s_checksum_seed != uuid_seed &&
1370 (mount_flags & EXT2_MF_MOUNTED)) {
1371 fputs(_("UUID has changed since enabling "
1372 "metadata_csum. Filesystem must be unmounted "
1373 "\nto safely rewrite all metadata to "
1374 "match the new UUID.\n"), stderr);
1375 return 1;
1376 }
1377
1378 rewrite_checksums = 1;
1379 }
1380
1381 if (sb->s_rev_level == EXT2_GOOD_OLD_REV &&
1382 (sb->s_feature_compat || sb->s_feature_ro_compat ||
1383 sb->s_feature_incompat))
1384 ext2fs_update_dynamic_rev(fs);
1385
1386 if (FEATURE_CHANGED(E2P_FEATURE_RO_INCOMPAT,
1387 EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) ||
1388 FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
1389 EXT4_FEATURE_RO_COMPAT_HUGE_FILE) ||
1390 FEATURE_CHANGED(E2P_FEATURE_INCOMPAT,
1391 EXT2_FEATURE_INCOMPAT_FILETYPE) ||
1392 FEATURE_CHANGED(E2P_FEATURE_COMPAT,
1393 EXT2_FEATURE_COMPAT_RESIZE_INODE) ||
1394 FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT,
1395 EXT2_FEATURE_RO_COMPAT_LARGE_FILE))
1396 request_fsck_afterwards(fs);
1397
1398 if ((old_features[E2P_FEATURE_COMPAT] != sb->s_feature_compat) ||
1399 (old_features[E2P_FEATURE_INCOMPAT] != sb->s_feature_incompat) ||
1400 (old_features[E2P_FEATURE_RO_INCOMPAT] != sb->s_feature_ro_compat))
1401 ext2fs_mark_super_dirty(fs);
1402
1403 return 0;
1404 }
1405
1406 /*
1407 * Add a journal to the filesystem.
1408 */
1409 static int add_journal(ext2_filsys fs)
1410 {
1411 unsigned long journal_blocks;
1412 errcode_t retval;
1413 ext2_filsys jfs;
1414 io_manager io_ptr;
1415
1416 if (ext2fs_has_feature_journal(fs->super)) {
1417 fputs(_("The filesystem already has a journal.\n"), stderr);
1418 goto err;
1419 }
1420 if (journal_device) {
1421 if (!check_plausibility(journal_device, CHECK_BLOCK_DEV,
1422 NULL))
1423 proceed_question(-1);
1424 check_mount(journal_device, 0, _("journal"));
1425 #ifdef CONFIG_TESTIO_DEBUG
1426 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) {
1427 io_ptr = test_io_manager;
1428 test_io_backing_manager = unix_io_manager;
1429 } else
1430 #endif
1431 io_ptr = unix_io_manager;
1432 retval = ext2fs_open(journal_device, EXT2_FLAG_RW|
1433 EXT2_FLAG_JOURNAL_DEV_OK, 0,
1434 fs->blocksize, io_ptr, &jfs);
1435 if (retval) {
1436 com_err(program_name, retval,
1437 _("\n\twhile trying to open journal on %s\n"),
1438 journal_device);
1439 goto err;
1440 }
1441 printf(_("Creating journal on device %s: "),
1442 journal_device);
1443 fflush(stdout);
1444
1445 retval = ext2fs_add_journal_device(fs, jfs);
1446 ext2fs_close_free(&jfs);
1447 if (retval) {
1448 com_err(program_name, retval,
1449 _("while adding filesystem to journal on %s"),
1450 journal_device);
1451 goto err;
1452 }
1453 fputs(_("done\n"), stdout);
1454 } else if (journal_size) {
1455 fputs(_("Creating journal inode: "), stdout);
1456 fflush(stdout);
1457 journal_blocks = figure_journal_size(journal_size, fs);
1458
1459 if (journal_location_string)
1460 journal_location =
1461 parse_num_blocks2(journal_location_string,
1462 fs->super->s_log_block_size);
1463 retval = ext2fs_add_journal_inode2(fs, journal_blocks,
1464 journal_location,
1465 journal_flags);
1466 if (retval) {
1467 fprintf(stderr, "\n");
1468 com_err(program_name, retval, "%s",
1469 _("\n\twhile trying to create journal file"));
1470 return retval;
1471 } else
1472 fputs(_("done\n"), stdout);
1473 /*
1474 * If the filesystem wasn't mounted, we need to force
1475 * the block group descriptors out.
1476 */
1477 if ((mount_flags & EXT2_MF_MOUNTED) == 0)
1478 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
1479 }
1480 print_check_message(fs->super->s_max_mnt_count,
1481 fs->super->s_checkinterval);
1482 return 0;
1483
1484 err:
1485 free(journal_device);
1486 return 1;
1487 }
1488
1489 static void handle_quota_options(ext2_filsys fs)
1490 {
1491 errcode_t retval;
1492 quota_ctx_t qctx;
1493 ext2_ino_t qf_ino;
1494 enum quota_type qtype;
1495 unsigned int qtype_bits = 0;
1496 int need_dirty = 0;
1497
1498 for (qtype = 0 ; qtype < MAXQUOTAS; qtype++)
1499 if (quota_enable[qtype] != 0)
1500 break;
1501 if (qtype == MAXQUOTAS)
1502 /* Nothing to do. */
1503 return;
1504
1505 if (quota_enable[PRJQUOTA] == QOPT_ENABLE &&
1506 fs->super->s_inode_size == EXT2_GOOD_OLD_INODE_SIZE) {
1507 fprintf(stderr, _("Cannot enable project quota; "
1508 "inode size too small.\n"));
1509 exit(1);
1510 }
1511
1512 for (qtype = 0; qtype < MAXQUOTAS; qtype++) {
1513 if (quota_enable[qtype] == QOPT_ENABLE)
1514 qtype_bits |= 1 << qtype;
1515 }
1516
1517 retval = quota_init_context(&qctx, fs, qtype_bits);
1518 if (retval) {
1519 com_err(program_name, retval,
1520 _("while initializing quota context in support library"));
1521 exit(1);
1522 }
1523
1524 if (qtype_bits)
1525 quota_compute_usage(qctx);
1526
1527 for (qtype = 0 ; qtype < MAXQUOTAS; qtype++) {
1528 if (quota_enable[qtype] == QOPT_ENABLE &&
1529 *quota_sb_inump(fs->super, qtype) == 0) {
1530 if ((qf_ino = quota_file_exists(fs, qtype)) > 0) {
1531 retval = quota_update_limits(qctx, qf_ino,
1532 qtype);
1533 if (retval) {
1534 com_err(program_name, retval,
1535 _("while updating quota limits (%d)"),
1536 qtype);
1537 exit(1);
1538 }
1539 }
1540 retval = quota_write_inode(qctx, 1 << qtype);
1541 if (retval) {
1542 com_err(program_name, retval,
1543 _("while writing quota file (%d)"),
1544 qtype);
1545 exit(1);
1546 }
1547 /* Enable Quota feature if one of quota enabled */
1548 if (!ext2fs_has_feature_quota(fs->super)) {
1549 ext2fs_set_feature_quota(fs->super);
1550 need_dirty = 1;
1551 }
1552 if (qtype == PRJQUOTA &&
1553 !ext2fs_has_feature_project(fs->super)) {
1554 ext2fs_set_feature_project(fs->super);
1555 need_dirty = 1;
1556 }
1557 } else if (quota_enable[qtype] == QOPT_DISABLE) {
1558 retval = quota_remove_inode(fs, qtype);
1559 if (retval) {
1560 com_err(program_name, retval,
1561 _("while removing quota file (%d)"),
1562 qtype);
1563 exit(1);
1564 }
1565 if (qtype == PRJQUOTA) {
1566 ext2fs_clear_feature_project(fs->super);
1567 need_dirty = 1;
1568 }
1569 }
1570 }
1571
1572 quota_release_context(&qctx);
1573 /* Clear Quota feature if all quota types disabled. */
1574 if (!qtype_bits) {
1575 for (qtype = 0 ; qtype < MAXQUOTAS; qtype++)
1576 if (*quota_sb_inump(fs->super, qtype))
1577 break;
1578 if (qtype == MAXQUOTAS) {
1579 ext2fs_clear_feature_quota(fs->super);
1580 need_dirty = 1;
1581 }
1582
1583 }
1584 if (need_dirty)
1585 ext2fs_mark_super_dirty(fs);
1586 return;
1587 }
1588
1589 static int option_handle_function(char *token)
1590 {
1591 if (strncmp(token, "usr", 3) == 0) {
1592 quota_enable[USRQUOTA] = QOPT_ENABLE;
1593 } else if (strncmp(token, "^usr", 4) == 0) {
1594 quota_enable[USRQUOTA] = QOPT_DISABLE;
1595 } else if (strncmp(token, "grp", 3) == 0) {
1596 quota_enable[GRPQUOTA] = QOPT_ENABLE;
1597 } else if (strncmp(token, "^grp", 4) == 0) {
1598 quota_enable[GRPQUOTA] = QOPT_DISABLE;
1599 } else if (strncmp(token, "prj", 3) == 0) {
1600 quota_enable[PRJQUOTA] = QOPT_ENABLE;
1601 } else if (strncmp(token, "^prj", 4) == 0) {
1602 quota_enable[PRJQUOTA] = QOPT_DISABLE;
1603 } else {
1604 fputs(_("\nBad quota options specified.\n\n"
1605 "Following valid quota options are available "
1606 "(pass by separating with comma):\n"
1607 "\t[^]usr[quota]\n"
1608 "\t[^]grp[quota]\n"
1609 "\t[^]prj[quota]\n"
1610 "\n\n"), stderr);
1611 return 1;
1612 }
1613 return 0;
1614 }
1615
1616 static void parse_e2label_options(int argc, char ** argv)
1617 {
1618 if ((argc < 2) || (argc > 3)) {
1619 fputs(_("Usage: e2label device [newlabel]\n"), stderr);
1620 exit(1);
1621 }
1622 io_options = strchr(argv[1], '?');
1623 if (io_options)
1624 *io_options++ = 0;
1625 device_name = blkid_get_devname(NULL, argv[1], NULL);
1626 if (!device_name) {
1627 com_err("e2label", 0, _("Unable to resolve '%s'"),
1628 argv[1]);
1629 exit(1);
1630 }
1631 open_flag = EXT2_FLAG_JOURNAL_DEV_OK;
1632 if (argc == 3) {
1633 open_flag |= EXT2_FLAG_RW;
1634 L_flag = 1;
1635 new_label = argv[2];
1636 } else
1637 print_label++;
1638 }
1639
1640 static time_t parse_time(char *str)
1641 {
1642 struct tm ts;
1643
1644 if (strcmp(str, "now") == 0) {
1645 return (time(0));
1646 }
1647 memset(&ts, 0, sizeof(ts));
1648 #ifdef HAVE_STRPTIME
1649 strptime(str, "%Y%m%d%H%M%S", &ts);
1650 #else
1651 sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon,
1652 &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec);
1653 ts.tm_year -= 1900;
1654 ts.tm_mon -= 1;
1655 if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 ||
1656 ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 ||
1657 ts.tm_min > 59 || ts.tm_sec > 61)
1658 ts.tm_mday = 0;
1659 #endif
1660 if (ts.tm_mday == 0) {
1661 com_err(program_name, 0,
1662 _("Couldn't parse date/time specifier: %s"),
1663 str);
1664 usage();
1665 }
1666 ts.tm_isdst = -1;
1667 return (mktime(&ts));
1668 }
1669
1670 static void parse_tune2fs_options(int argc, char **argv)
1671 {
1672 int c;
1673 char *tmp;
1674 struct group *gr;
1675 struct passwd *pw;
1676 int ret;
1677 char optstring[100] = "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:T:U:z:Q:";
1678
1679 open_flag = 0;
1680 printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE);
1681 while ((c = getopt(argc, argv, optstring)) != EOF)
1682 switch (c) {
1683 case 'c':
1684 max_mount_count = strtol(optarg, &tmp, 0);
1685 if (*tmp || max_mount_count > 16000) {
1686 com_err(program_name, 0,
1687 _("bad mounts count - %s"),
1688 optarg);
1689 usage();
1690 }
1691 if (max_mount_count == 0)
1692 max_mount_count = -1;
1693 c_flag = 1;
1694 open_flag = EXT2_FLAG_RW;
1695 break;
1696 case 'C':
1697 mount_count = strtoul(optarg, &tmp, 0);
1698 if (*tmp || mount_count > 16000) {
1699 com_err(program_name, 0,
1700 _("bad mounts count - %s"),
1701 optarg);
1702 usage();
1703 }
1704 C_flag = 1;
1705 open_flag = EXT2_FLAG_RW;
1706 break;
1707 case 'e':
1708 if (strcmp(optarg, "continue") == 0)
1709 errors = EXT2_ERRORS_CONTINUE;
1710 else if (strcmp(optarg, "remount-ro") == 0)
1711 errors = EXT2_ERRORS_RO;
1712 else if (strcmp(optarg, "panic") == 0)
1713 errors = EXT2_ERRORS_PANIC;
1714 else {
1715 com_err(program_name, 0,
1716 _("bad error behavior - %s"),
1717 optarg);
1718 usage();
1719 }
1720 e_flag = 1;
1721 open_flag = EXT2_FLAG_RW;
1722 break;
1723 case 'E':
1724 extended_cmd = optarg;
1725 open_flag |= EXT2_FLAG_RW;
1726 break;
1727 case 'f': /* Force */
1728 f_flag++;
1729 break;
1730 case 'g':
1731 resgid = strtoul(optarg, &tmp, 0);
1732 if (*tmp) {
1733 gr = getgrnam(optarg);
1734 if (gr == NULL)
1735 tmp = optarg;
1736 else {
1737 resgid = gr->gr_gid;
1738 *tmp = 0;
1739 }
1740 }
1741 if (*tmp) {
1742 com_err(program_name, 0,
1743 _("bad gid/group name - %s"),
1744 optarg);
1745 usage();
1746 }
1747 g_flag = 1;
1748 open_flag = EXT2_FLAG_RW;
1749 break;
1750 case 'i':
1751 interval = strtoul(optarg, &tmp, 0);
1752 switch (*tmp) {
1753 case 's':
1754 tmp++;
1755 break;
1756 case '\0':
1757 case 'd':
1758 case 'D': /* days */
1759 interval *= 86400;
1760 if (*tmp != '\0')
1761 tmp++;
1762 break;
1763 case 'm':
1764 case 'M': /* months! */
1765 interval *= 86400 * 30;
1766 tmp++;
1767 break;
1768 case 'w':
1769 case 'W': /* weeks */
1770 interval *= 86400 * 7;
1771 tmp++;
1772 break;
1773 }
1774 if (*tmp) {
1775 com_err(program_name, 0,
1776 _("bad interval - %s"), optarg);
1777 usage();
1778 }
1779 i_flag = 1;
1780 open_flag = EXT2_FLAG_RW;
1781 break;
1782 case 'j':
1783 if (!journal_size)
1784 journal_size = -1;
1785 open_flag = EXT2_FLAG_RW;
1786 break;
1787 case 'J':
1788 parse_journal_opts(optarg);
1789 open_flag = EXT2_FLAG_RW;
1790 break;
1791 case 'l':
1792 l_flag = 1;
1793 break;
1794 case 'L':
1795 new_label = optarg;
1796 L_flag = 1;
1797 open_flag |= EXT2_FLAG_RW |
1798 EXT2_FLAG_JOURNAL_DEV_OK;
1799 break;
1800 case 'm':
1801 reserved_ratio = strtod(optarg, &tmp);
1802 if (*tmp || reserved_ratio > 50 ||
1803 reserved_ratio < 0) {
1804 com_err(program_name, 0,
1805 _("bad reserved block ratio - %s"),
1806 optarg);
1807 usage();
1808 }
1809 m_flag = 1;
1810 open_flag = EXT2_FLAG_RW;
1811 break;
1812 case 'M':
1813 new_last_mounted = optarg;
1814 M_flag = 1;
1815 open_flag = EXT2_FLAG_RW;
1816 break;
1817 case 'o':
1818 if (mntopts_cmd) {
1819 com_err(program_name, 0, "%s",
1820 _("-o may only be specified once"));
1821 usage();
1822 }
1823 mntopts_cmd = optarg;
1824 open_flag = EXT2_FLAG_RW;
1825 break;
1826 case 'O':
1827 if (features_cmd) {
1828 com_err(program_name, 0, "%s",
1829 _("-O may only be specified once"));
1830 usage();
1831 }
1832 features_cmd = optarg;
1833 open_flag = EXT2_FLAG_RW;
1834 break;
1835 case 'Q':
1836 Q_flag = 1;
1837 ret = parse_quota_opts(optarg, option_handle_function);
1838 if (ret)
1839 exit(1);
1840 open_flag = EXT2_FLAG_RW;
1841 break;
1842 case 'r':
1843 reserved_blocks = strtoul(optarg, &tmp, 0);
1844 if (*tmp) {
1845 com_err(program_name, 0,
1846 _("bad reserved blocks count - %s"),
1847 optarg);
1848 usage();
1849 }
1850 r_flag = 1;
1851 open_flag = EXT2_FLAG_RW;
1852 break;
1853 case 's': /* Deprecated */
1854 s_flag = atoi(optarg);
1855 open_flag = EXT2_FLAG_RW;
1856 break;
1857 case 'T':
1858 T_flag = 1;
1859 last_check_time = parse_time(optarg);
1860 open_flag = EXT2_FLAG_RW;
1861 break;
1862 case 'u':
1863 resuid = strtoul(optarg, &tmp, 0);
1864 if (*tmp) {
1865 pw = getpwnam(optarg);
1866 if (pw == NULL)
1867 tmp = optarg;
1868 else {
1869 resuid = pw->pw_uid;
1870 *tmp = 0;
1871 }
1872 }
1873 if (*tmp) {
1874 com_err(program_name, 0,
1875 _("bad uid/user name - %s"),
1876 optarg);
1877 usage();
1878 }
1879 u_flag = 1;
1880 open_flag = EXT2_FLAG_RW;
1881 break;
1882 case 'U':
1883 new_UUID = optarg;
1884 U_flag = 1;
1885 open_flag = EXT2_FLAG_RW |
1886 EXT2_FLAG_JOURNAL_DEV_OK;
1887 break;
1888 case 'I':
1889 new_inode_size = strtoul(optarg, &tmp, 0);
1890 if (*tmp) {
1891 com_err(program_name, 0,
1892 _("bad inode size - %s"),
1893 optarg);
1894 usage();
1895 }
1896 if (!((new_inode_size &
1897 (new_inode_size - 1)) == 0)) {
1898 com_err(program_name, 0,
1899 _("Inode size must be a "
1900 "power of two- %s"),
1901 optarg);
1902 usage();
1903 }
1904 open_flag = EXT2_FLAG_RW;
1905 I_flag = 1;
1906 break;
1907 case 'z':
1908 undo_file = optarg;
1909 break;
1910 default:
1911 usage();
1912 }
1913 if (optind < argc - 1 || optind == argc)
1914 usage();
1915 if (!open_flag && !l_flag)
1916 usage();
1917 io_options = strchr(argv[optind], '?');
1918 if (io_options)
1919 *io_options++ = 0;
1920 device_name = blkid_get_devname(NULL, argv[optind], NULL);
1921 if (!device_name) {
1922 com_err(program_name, 0, _("Unable to resolve '%s'"),
1923 argv[optind]);
1924 exit(1);
1925 }
1926 }
1927
1928 #ifdef CONFIG_BUILD_FINDFS
1929 void do_findfs(int argc, char **argv)
1930 {
1931 char *dev;
1932
1933 if ((argc != 2) ||
1934 (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) {
1935 fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n");
1936 exit(2);
1937 }
1938 dev = blkid_get_devname(NULL, argv[1], NULL);
1939 if (!dev) {
1940 com_err("findfs", 0, _("Unable to resolve '%s'"),
1941 argv[1]);
1942 exit(1);
1943 }
1944 puts(dev);
1945 exit(0);
1946 }
1947 #endif
1948
1949 static int parse_extended_opts(ext2_filsys fs, const char *opts)
1950 {
1951 char *buf, *token, *next, *p, *arg;
1952 int len, hash_alg;
1953 int r_usage = 0;
1954
1955 len = strlen(opts);
1956 buf = malloc(len+1);
1957 if (!buf) {
1958 fprintf(stderr, "%s",
1959 _("Couldn't allocate memory to parse options!\n"));
1960 return 1;
1961 }
1962 strcpy(buf, opts);
1963 for (token = buf; token && *token; token = next) {
1964 p = strchr(token, ',');
1965 next = 0;
1966 if (p) {
1967 *p = 0;
1968 next = p+1;
1969 }
1970 arg = strchr(token, '=');
1971 if (arg) {
1972 *arg = 0;
1973 arg++;
1974 }
1975 if (strcmp(token, "clear-mmp") == 0 ||
1976 strcmp(token, "clear_mmp") == 0) {
1977 clear_mmp = 1;
1978 } else if (strcmp(token, "mmp_update_interval") == 0) {
1979 unsigned long intv;
1980 if (!arg) {
1981 r_usage++;
1982 continue;
1983 }
1984 intv = strtoul(arg, &p, 0);
1985 if (*p) {
1986 fprintf(stderr,
1987 _("Invalid mmp_update_interval: %s\n"),
1988 arg);
1989 r_usage++;
1990 continue;
1991 }
1992 if (intv == 0) {
1993 intv = EXT4_MMP_UPDATE_INTERVAL;
1994 } else if (intv > EXT4_MMP_MAX_UPDATE_INTERVAL) {
1995 fprintf(stderr,
1996 _("mmp_update_interval too big: %lu\n"),
1997 intv);
1998 r_usage++;
1999 continue;
2000 }
2001 printf(P_("Setting multiple mount protection update "
2002 "interval to %lu second\n",
2003 "Setting multiple mount protection update "
2004 "interval to %lu seconds\n", intv),
2005 intv);
2006 fs->super->s_mmp_update_interval = intv;
2007 ext2fs_mark_super_dirty(fs);
2008 } else if (!strcmp(token, "test_fs")) {
2009 fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS;
2010 printf("Setting test filesystem flag\n");
2011 ext2fs_mark_super_dirty(fs);
2012 } else if (!strcmp(token, "^test_fs")) {
2013 fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS;
2014 printf("Clearing test filesystem flag\n");
2015 ext2fs_mark_super_dirty(fs);
2016 } else if (strcmp(token, "stride") == 0) {
2017 if (!arg) {
2018 r_usage++;
2019 continue;
2020 }
2021 stride = strtoul(arg, &p, 0);
2022 if (*p) {
2023 fprintf(stderr,
2024 _("Invalid RAID stride: %s\n"),
2025 arg);
2026 r_usage++;
2027 continue;
2028 }
2029 stride_set = 1;
2030 } else if (strcmp(token, "stripe-width") == 0 ||
2031 strcmp(token, "stripe_width") == 0) {
2032 if (!arg) {
2033 r_usage++;
2034 continue;
2035 }
2036 stripe_width = strtoul(arg, &p, 0);
2037 if (*p) {
2038 fprintf(stderr,
2039 _("Invalid RAID stripe-width: %s\n"),
2040 arg);
2041 r_usage++;
2042 continue;
2043 }
2044 stripe_width_set = 1;
2045 } else if (strcmp(token, "hash_alg") == 0 ||
2046 strcmp(token, "hash-alg") == 0) {
2047 if (!arg) {
2048 r_usage++;
2049 continue;
2050 }
2051 hash_alg = e2p_string2hash(arg);
2052 if (hash_alg < 0) {
2053 fprintf(stderr,
2054 _("Invalid hash algorithm: %s\n"),
2055 arg);
2056 r_usage++;
2057 continue;
2058 }
2059 fs->super->s_def_hash_version = hash_alg;
2060 printf(_("Setting default hash algorithm "
2061 "to %s (%d)\n"),
2062 arg, hash_alg);
2063 ext2fs_mark_super_dirty(fs);
2064 } else if (!strcmp(token, "mount_opts")) {
2065 if (!arg) {
2066 r_usage++;
2067 continue;
2068 }
2069 if (strlen(arg) >= sizeof(fs->super->s_mount_opts)) {
2070 fprintf(stderr,
2071 "Extended mount options too long\n");
2072 continue;
2073 }
2074 ext_mount_opts = strdup(arg);
2075 } else
2076 r_usage++;
2077 }
2078 if (r_usage) {
2079 fprintf(stderr, "%s", _("\nBad options specified.\n\n"
2080 "Extended options are separated by commas, "
2081 "and may take an argument which\n"
2082 "\tis set off by an equals ('=') sign.\n\n"
2083 "Valid extended options are:\n"
2084 "\tclear_mmp\n"
2085 "\thash_alg=<hash algorithm>\n"
2086 "\tmount_opts=<extended default mount options>\n"
2087 "\tmmp_update_interval=<mmp update interval in seconds>\n"
2088 "\tstride=<RAID per-disk chunk size in blocks>\n"
2089 "\tstripe_width=<RAID stride*data disks in blocks>\n"
2090 "\ttest_fs\n"
2091 "\t^test_fs\n"));
2092 free(buf);
2093 return 1;
2094 }
2095 free(buf);
2096
2097 return 0;
2098 }
2099
2100 /*
2101 * Fill in the block bitmap bmap with the information regarding the
2102 * blocks to be moved
2103 */
2104 static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp,
2105 ext2fs_block_bitmap bmap)
2106 {
2107 dgrp_t i;
2108 int retval;
2109 ext2_badblocks_list bb_list = 0;
2110 blk64_t j, needed_blocks = 0;
2111 blk64_t start_blk, end_blk;
2112
2113 retval = ext2fs_read_bb_inode(fs, &bb_list);
2114 if (retval)
2115 return retval;
2116
2117 for (i = 0; i < fs->group_desc_count; i++) {
2118 start_blk = ext2fs_inode_table_loc(fs, i) +
2119 fs->inode_blocks_per_group;
2120
2121 end_blk = ext2fs_inode_table_loc(fs, i) +
2122 new_ino_blks_per_grp;
2123
2124 for (j = start_blk; j < end_blk; j++) {
2125 if (ext2fs_test_block_bitmap2(fs->block_map, j)) {
2126 /*
2127 * IF the block is a bad block we fail
2128 */
2129 if (ext2fs_badblocks_list_test(bb_list, j)) {
2130 ext2fs_badblocks_list_free(bb_list);
2131 return ENOSPC;
2132 }
2133
2134 ext2fs_mark_block_bitmap2(bmap, j);
2135 } else {
2136 /*
2137 * We are going to use this block for
2138 * inode table. So mark them used.
2139 */
2140 ext2fs_mark_block_bitmap2(fs->block_map, j);
2141 }
2142 }
2143 needed_blocks += end_blk - start_blk;
2144 }
2145
2146 ext2fs_badblocks_list_free(bb_list);
2147 if (needed_blocks > ext2fs_free_blocks_count(fs->super))
2148 return ENOSPC;
2149
2150 return 0;
2151 }
2152
2153 static int ext2fs_is_meta_block(ext2_filsys fs, blk64_t blk)
2154 {
2155 dgrp_t group;
2156 group = ext2fs_group_of_blk2(fs, blk);
2157 if (ext2fs_block_bitmap_loc(fs, group) == blk)
2158 return 1;
2159 if (ext2fs_inode_bitmap_loc(fs, group) == blk)
2160 return 1;
2161 return 0;
2162 }
2163
2164 static int ext2fs_is_block_in_group(ext2_filsys fs, dgrp_t group, blk64_t blk)
2165 {
2166 blk64_t start_blk, end_blk;
2167 start_blk = fs->super->s_first_data_block +
2168 EXT2_GROUPS_TO_BLOCKS(fs->super, group);
2169 /*
2170 * We cannot get new block beyond end_blk for for the last block group
2171 * so we can check with EXT2_BLOCKS_PER_GROUP even for last block group
2172 */
2173 end_blk = start_blk + EXT2_BLOCKS_PER_GROUP(fs->super);
2174 if (blk >= start_blk && blk <= end_blk)
2175 return 1;
2176 return 0;
2177 }
2178
2179 static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap)
2180 {
2181
2182 char *buf;
2183 dgrp_t group = 0;
2184 errcode_t retval;
2185 int meta_data = 0;
2186 blk64_t blk, new_blk, goal;
2187 struct blk_move *bmv;
2188
2189 retval = ext2fs_get_mem(fs->blocksize, &buf);
2190 if (retval)
2191 return retval;
2192
2193 for (new_blk = blk = fs->super->s_first_data_block;
2194 blk < ext2fs_blocks_count(fs->super); blk++) {
2195 if (!ext2fs_test_block_bitmap2(bmap, blk))
2196 continue;
2197
2198 if (ext2fs_is_meta_block(fs, blk)) {
2199 /*
2200 * If the block is mapping a fs meta data block
2201 * like group desc/block bitmap/inode bitmap. We
2202 * should find a block in the same group and fix
2203 * the respective fs metadata pointers. Otherwise
2204 * fail
2205 */
2206 group = ext2fs_group_of_blk2(fs, blk);
2207 goal = ext2fs_group_first_block2(fs, group);
2208 meta_data = 1;
2209
2210 } else {
2211 goal = new_blk;
2212 }
2213 retval = ext2fs_new_block2(fs, goal, NULL, &new_blk);
2214 if (retval)
2215 goto err_out;
2216
2217 /* new fs meta data block should be in the same group */
2218 if (meta_data && !ext2fs_is_block_in_group(fs, group, new_blk)) {
2219 retval = ENOSPC;
2220 goto err_out;
2221 }
2222
2223 /* Mark this block as allocated */
2224 ext2fs_mark_block_bitmap2(fs->block_map, new_blk);
2225
2226 /* Add it to block move list */
2227 retval = ext2fs_get_mem(sizeof(struct blk_move), &bmv);
2228 if (retval)
2229 goto err_out;
2230
2231 bmv->old_loc = blk;
2232 bmv->new_loc = new_blk;
2233
2234 list_add(&(bmv->list), &blk_move_list);
2235
2236 retval = io_channel_read_blk64(fs->io, blk, 1, buf);
2237 if (retval)
2238 goto err_out;
2239
2240 retval = io_channel_write_blk64(fs->io, new_blk, 1, buf);
2241 if (retval)
2242 goto err_out;
2243 }
2244
2245 err_out:
2246 ext2fs_free_mem(&buf);
2247 return retval;
2248 }
2249
2250 static blk64_t translate_block(blk64_t blk)
2251 {
2252 struct list_head *entry;
2253 struct blk_move *bmv;
2254
2255 list_for_each(entry, &blk_move_list) {
2256 bmv = list_entry(entry, struct blk_move, list);
2257 if (bmv->old_loc == blk)
2258 return bmv->new_loc;
2259 }
2260
2261 return 0;
2262 }
2263
2264 static int process_block(ext2_filsys fs EXT2FS_ATTR((unused)),
2265 blk64_t *block_nr,
2266 e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)),
2267 blk64_t ref_block EXT2FS_ATTR((unused)),
2268 int ref_offset EXT2FS_ATTR((unused)),
2269 void *priv_data)
2270 {
2271 int ret = 0;
2272 blk64_t new_blk;
2273 ext2fs_block_bitmap bmap = (ext2fs_block_bitmap) priv_data;
2274
2275 if (!ext2fs_test_block_bitmap2(bmap, *block_nr))
2276 return 0;
2277 new_blk = translate_block(*block_nr);
2278 if (new_blk) {
2279 *block_nr = new_blk;
2280 /*
2281 * This will force the ext2fs_write_inode in the iterator
2282 */
2283 ret |= BLOCK_CHANGED;
2284 }
2285
2286 return ret;
2287 }
2288
2289 static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
2290 {
2291 errcode_t retval = 0;
2292 ext2_ino_t ino;
2293 blk64_t blk;
2294 char *block_buf = 0;
2295 struct ext2_inode inode;
2296 ext2_inode_scan scan = NULL;
2297
2298 retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf);
2299 if (retval)
2300 return retval;
2301
2302 retval = ext2fs_open_inode_scan(fs, 0, &scan);
2303 if (retval)
2304 goto err_out;
2305
2306 while (1) {
2307 retval = ext2fs_get_next_inode(scan, &ino, &inode);
2308 if (retval)
2309 goto err_out;
2310
2311 if (!ino)
2312 break;
2313
2314 if (inode.i_links_count == 0)
2315 continue; /* inode not in use */
2316
2317 /* FIXME!!
2318 * If we end up modifying the journal inode
2319 * the sb->s_jnl_blocks will differ. But a
2320 * subsequent e2fsck fixes that.
2321 * Do we need to fix this ??
2322 */
2323
2324 if (ext2fs_file_acl_block(fs, &inode) &&
2325 ext2fs_test_block_bitmap2(bmap,
2326 ext2fs_file_acl_block(fs, &inode))) {
2327 blk = translate_block(ext2fs_file_acl_block(fs,
2328 &inode));
2329 if (!blk)
2330 continue;
2331
2332 ext2fs_file_acl_block_set(fs, &inode, blk);
2333
2334 /*
2335 * Write the inode to disk so that inode table
2336 * resizing can work
2337 */
2338 retval = ext2fs_write_inode(fs, ino, &inode);
2339 if (retval)
2340 goto err_out;
2341 }
2342
2343 if (!ext2fs_inode_has_valid_blocks2(fs, &inode))
2344 continue;
2345
2346 retval = ext2fs_block_iterate3(fs, ino, 0, block_buf,
2347 process_block, bmap);
2348 if (retval)
2349 goto err_out;
2350
2351 }
2352
2353 err_out:
2354 ext2fs_free_mem(&block_buf);
2355 ext2fs_close_inode_scan(scan);
2356
2357 return retval;
2358 }
2359
2360 /*
2361 * We need to scan for inode and block bitmaps that may need to be
2362 * moved. This can take place if the filesystem was formatted for
2363 * RAID arrays using the mke2fs's extended option "stride".
2364 */
2365 static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap)
2366 {
2367 dgrp_t i;
2368 blk64_t blk, new_blk;
2369
2370 for (i = 0; i < fs->group_desc_count; i++) {
2371 blk = ext2fs_block_bitmap_loc(fs, i);
2372 if (ext2fs_test_block_bitmap2(bmap, blk)) {
2373 new_blk = translate_block(blk);
2374 if (!new_blk)
2375 continue;
2376 ext2fs_block_bitmap_loc_set(fs, i, new_blk);
2377 }
2378
2379 blk = ext2fs_inode_bitmap_loc(fs, i);
2380 if (ext2fs_test_block_bitmap2(bmap, blk)) {
2381 new_blk = translate_block(blk);
2382 if (!new_blk)
2383 continue;
2384 ext2fs_inode_bitmap_loc_set(fs, i, new_blk);
2385 }
2386 }
2387 return 0;
2388 }
2389
2390 static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size)
2391 {
2392 dgrp_t i;
2393 blk64_t blk;
2394 errcode_t retval;
2395 int new_ino_blks_per_grp;
2396 unsigned int j;
2397 char *old_itable = NULL, *new_itable = NULL;
2398 char *tmp_old_itable = NULL, *tmp_new_itable = NULL;
2399 unsigned long old_ino_size;
2400 int old_itable_size, new_itable_size;
2401
2402 old_itable_size = fs->inode_blocks_per_group * fs->blocksize;
2403 old_ino_size = EXT2_INODE_SIZE(fs->super);
2404
2405 new_ino_blks_per_grp = ext2fs_div_ceil(
2406 EXT2_INODES_PER_GROUP(fs->super) *
2407 new_ino_size,
2408 fs->blocksize);
2409
2410 new_itable_size = new_ino_blks_per_grp * fs->blocksize;
2411
2412 retval = ext2fs_get_mem(old_itable_size, &old_itable);
2413 if (retval)
2414 return retval;
2415
2416 retval = ext2fs_get_mem(new_itable_size, &new_itable);
2417 if (retval)
2418 goto err_out;
2419
2420 tmp_old_itable = old_itable;
2421 tmp_new_itable = new_itable;
2422
2423 for (i = 0; i < fs->group_desc_count; i++) {
2424 blk = ext2fs_inode_table_loc(fs, i);
2425 retval = io_channel_read_blk64(fs->io, blk,
2426 fs->inode_blocks_per_group, old_itable);
2427 if (retval)
2428 goto err_out;
2429
2430 for (j = 0; j < EXT2_INODES_PER_GROUP(fs->super); j++) {
2431 memcpy(new_itable, old_itable, old_ino_size);
2432
2433 memset(new_itable+old_ino_size, 0,
2434 new_ino_size - old_ino_size);
2435
2436 new_itable += new_ino_size;
2437 old_itable += old_ino_size;
2438 }
2439
2440 /* reset the pointer */
2441 old_itable = tmp_old_itable;
2442 new_itable = tmp_new_itable;
2443
2444 retval = io_channel_write_blk64(fs->io, blk,
2445 new_ino_blks_per_grp, new_itable);
2446 if (retval)
2447 goto err_out;
2448 }
2449
2450 /* Update the meta data */
2451 fs->inode_blocks_per_group = new_ino_blks_per_grp;
2452 ext2fs_free_inode_cache(fs->icache);
2453 fs->icache = 0;
2454 fs->super->s_inode_size = new_ino_size;
2455
2456 err_out:
2457 if (old_itable)
2458 ext2fs_free_mem(&old_itable);
2459
2460 if (new_itable)
2461 ext2fs_free_mem(&new_itable);
2462
2463 return retval;
2464 }
2465
2466 static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs)
2467 {
2468 blk64_t blk;
2469 ext2_ino_t ino;
2470 unsigned int group = 0;
2471 unsigned int count = 0;
2472 int total_free = 0;
2473 int group_free = 0;
2474
2475 /*
2476 * First calculate the block statistics
2477 */
2478 for (blk = fs->super->s_first_data_block;
2479 blk < ext2fs_blocks_count(fs->super); blk++) {
2480 if (!ext2fs_fast_test_block_bitmap2(fs->block_map, blk)) {
2481 group_free++;
2482 total_free++;
2483 }
2484 count++;
2485 if ((count == fs->super->s_blocks_per_group) ||
2486 (blk == ext2fs_blocks_count(fs->super)-1)) {
2487 ext2fs_bg_free_blocks_count_set(fs, group++,
2488 group_free);
2489 count = 0;
2490 group_free = 0;
2491 }
2492 }
2493 total_free = EXT2FS_C2B(fs, total_free);
2494 ext2fs_free_blocks_count_set(fs->super, total_free);
2495
2496 /*
2497 * Next, calculate the inode statistics
2498 */
2499 group_free = 0;
2500 total_free = 0;
2501 count = 0;
2502 group = 0;
2503
2504 /* Protect loop from wrap-around if s_inodes_count maxed */
2505 for (ino = 1; ino <= fs->super->s_inodes_count && ino > 0; ino++) {
2506 if (!ext2fs_fast_test_inode_bitmap2(fs->inode_map, ino)) {
2507 group_free++;
2508 total_free++;
2509 }
2510 count++;
2511 if ((count == fs->super->s_inodes_per_group) ||
2512 (ino == fs->super->s_inodes_count)) {
2513 ext2fs_bg_free_inodes_count_set(fs, group++,
2514 group_free);
2515 count = 0;
2516 group_free = 0;
2517 }
2518 }
2519 fs->super->s_free_inodes_count = total_free;
2520 ext2fs_mark_super_dirty(fs);
2521 return 0;
2522 }
2523
2524 #define list_for_each_safe(pos, pnext, head) \
2525 for (pos = (head)->next, pnext = pos->next; pos != (head); \
2526 pos = pnext, pnext = pos->next)
2527
2528 static void free_blk_move_list(void)
2529 {
2530 struct list_head *entry, *tmp;
2531 struct blk_move *bmv;
2532
2533 list_for_each_safe(entry, tmp, &blk_move_list) {
2534 bmv = list_entry(entry, struct blk_move, list);
2535 list_del(entry);
2536 ext2fs_free_mem(&bmv);
2537 }
2538 return;
2539 }
2540
2541 static int resize_inode(ext2_filsys fs, unsigned long new_size)
2542 {
2543 errcode_t retval;
2544 int new_ino_blks_per_grp;
2545 ext2fs_block_bitmap bmap;
2546
2547 retval = ext2fs_read_inode_bitmap(fs);
2548 if (retval) {
2549 fputs(_("Failed to read inode bitmap\n"), stderr);
2550 return retval;
2551 }
2552 retval = ext2fs_read_block_bitmap(fs);
2553 if (retval) {
2554 fputs(_("Failed to read block bitmap\n"), stderr);
2555 return retval;
2556 }
2557 INIT_LIST_HEAD(&blk_move_list);
2558
2559
2560 new_ino_blks_per_grp = ext2fs_div_ceil(
2561 EXT2_INODES_PER_GROUP(fs->super)*
2562 new_size,
2563 fs->blocksize);
2564
2565 /* We may change the file system.
2566 * Mark the file system as invalid so that
2567 * the user is prompted to run fsck.
2568 */
2569 fs->super->s_state &= ~EXT2_VALID_FS;
2570
2571 retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"),
2572 &bmap);
2573 if (retval) {
2574 fputs(_("Failed to allocate block bitmap when "
2575 "increasing inode size\n"), stderr);
2576 return retval;
2577 }
2578 retval = get_move_bitmaps(fs, new_ino_blks_per_grp, bmap);
2579 if (retval) {
2580 fputs(_("Not enough space to increase inode size \n"), stderr);
2581 goto err_out;
2582 }
2583 retval = move_block(fs, bmap);
2584 if (retval) {
2585 fputs(_("Failed to relocate blocks during inode resize \n"),
2586 stderr);
2587 goto err_out;
2588 }
2589 retval = inode_scan_and_fix(fs, bmap);
2590 if (retval)
2591 goto err_out_undo;
2592
2593 retval = group_desc_scan_and_fix(fs, bmap);
2594 if (retval)
2595 goto err_out_undo;
2596
2597 retval = expand_inode_table(fs, new_size);
2598 if (retval)
2599 goto err_out_undo;
2600
2601 ext2fs_calculate_summary_stats(fs);
2602
2603 fs->super->s_state |= EXT2_VALID_FS;
2604 /* mark super block and block bitmap as dirty */
2605 ext2fs_mark_super_dirty(fs);
2606 ext2fs_mark_bb_dirty(fs);
2607
2608 err_out:
2609 free_blk_move_list();
2610 ext2fs_free_block_bitmap(bmap);
2611
2612 return retval;
2613
2614 err_out_undo:
2615 free_blk_move_list();
2616 ext2fs_free_block_bitmap(bmap);
2617 fputs(_("Error in resizing the inode size.\n"
2618 "Run e2undo to undo the "
2619 "file system changes. \n"), stderr);
2620
2621 return retval;
2622 }
2623
2624 static int tune2fs_setup_tdb(const char *name, io_manager *io_ptr)
2625 {
2626 errcode_t retval = 0;
2627 const char *tdb_dir;
2628 char *tdb_file = NULL;
2629 char *dev_name, *tmp_name;
2630
2631 /* (re)open a specific undo file */
2632 if (undo_file && undo_file[0] != 0) {
2633 retval = set_undo_io_backing_manager(*io_ptr);
2634 if (retval)
2635 goto err;
2636 *io_ptr = undo_io_manager;
2637 retval = set_undo_io_backup_file(undo_file);
2638 if (retval)
2639 goto err;
2640 printf(_("Overwriting existing filesystem; this can be undone "
2641 "using the command:\n"
2642 " e2undo %s %s\n\n"),
2643 undo_file, name);
2644 return retval;
2645 }
2646
2647 /*
2648 * Configuration via a conf file would be
2649 * nice
2650 */
2651 tdb_dir = getenv("E2FSPROGS_UNDO_DIR");
2652 if (!tdb_dir)
2653 tdb_dir = "/var/lib/e2fsprogs";
2654
2655 if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0) ||
2656 access(tdb_dir, W_OK))
2657 return 0;
2658
2659 tmp_name = strdup(name);
2660 if (!tmp_name)
2661 goto errout;
2662 dev_name = basename(tmp_name);
2663 tdb_file = malloc(strlen(tdb_dir) + 9 + strlen(dev_name) + 7 + 1);
2664 if (!tdb_file) {
2665 free(tmp_name);
2666 goto errout;
2667 }
2668 sprintf(tdb_file, "%s/tune2fs-%s.e2undo", tdb_dir, dev_name);
2669 free(tmp_name);
2670
2671 if ((unlink(tdb_file) < 0) && (errno != ENOENT)) {
2672 retval = errno;
2673 com_err(program_name, retval,
2674 _("while trying to delete %s"), tdb_file);
2675 goto errout;
2676 }
2677
2678 retval = set_undo_io_backing_manager(*io_ptr);
2679 if (retval)
2680 goto errout;
2681 *io_ptr = undo_io_manager;
2682 retval = set_undo_io_backup_file(tdb_file);
2683 if (retval)
2684 goto errout;
2685 printf(_("Overwriting existing filesystem; this can be undone "
2686 "using the command:\n"
2687 " e2undo %s %s\n\n"),
2688 tdb_file, name);
2689
2690 free(tdb_file);
2691 return 0;
2692 errout:
2693 free(tdb_file);
2694 err:
2695 com_err("tune2fs", retval, "while trying to setup undo file\n");
2696 return retval;
2697 }
2698
2699 static int
2700 fs_update_journal_user(struct ext2_super_block *sb, __u8 old_uuid[UUID_SIZE])
2701 {
2702 int retval, nr_users, start;
2703 journal_superblock_t *jsb;
2704 ext2_filsys jfs;
2705 __u8 *j_uuid;
2706 char *journal_path;
2707 char uuid[UUID_STR_SIZE];
2708 char buf[SUPERBLOCK_SIZE] __attribute__ ((aligned(8)));
2709
2710 if (!ext2fs_has_feature_journal(sb) || uuid_is_null(sb->s_journal_uuid))
2711 return 0;
2712
2713 uuid_unparse(sb->s_journal_uuid, uuid);
2714 journal_path = blkid_get_devname(NULL, "UUID", uuid);
2715 if (!journal_path)
2716 return 0;
2717
2718 retval = ext2fs_open2(journal_path, io_options,
2719 EXT2_FLAG_JOURNAL_DEV_OK | EXT2_FLAG_RW,
2720 0, 0, unix_io_manager, &jfs);
2721 if (retval) {
2722 com_err(program_name, retval,
2723 _("while trying to open %s"),
2724 journal_path);
2725 return retval;
2726 }
2727
2728 retval = get_journal_sb(jfs, buf);
2729 if (retval != 0) {
2730 if (retval == EXT2_ET_UNSUPP_FEATURE)
2731 fprintf(stderr, _("%s is not a journal device.\n"),
2732 journal_path);
2733 return retval;
2734 }
2735
2736 jsb = (journal_superblock_t *) buf;
2737 /* Find the filesystem UUID */
2738 nr_users = ntohl(jsb->s_nr_users);
2739
2740 j_uuid = journal_user(old_uuid, jsb->s_users, nr_users);
2741 if (j_uuid == NULL) {
2742 fputs(_("Filesystem's UUID not found on journal device.\n"),
2743 stderr);
2744 return EXT2_ET_LOAD_EXT_JOURNAL;
2745 }
2746
2747 memcpy(j_uuid, sb->s_uuid, UUID_SIZE);
2748
2749 start = ext2fs_journal_sb_start(jfs->blocksize);
2750 /* Write back the journal superblock */
2751 retval = io_channel_write_blk64(jfs->io, start, -SUPERBLOCK_SIZE, buf);
2752 if (retval != 0) {
2753 com_err(program_name, retval,
2754 "while writing journal superblock.");
2755 return retval;
2756 }
2757
2758 ext2fs_close(jfs);
2759
2760 return 0;
2761 }
2762
2763 #ifndef BUILD_AS_LIB
2764 int main(int argc, char **argv)
2765 #else
2766 int tune2fs_main(int argc, char **argv)
2767 #endif /* BUILD_AS_LIB */
2768 {
2769 errcode_t retval;
2770 ext2_filsys fs;
2771 struct ext2_super_block *sb;
2772 io_manager io_ptr, io_ptr_orig = NULL;
2773 int rc = 0;
2774 char default_undo_file[1] = { 0 };
2775
2776 #ifdef ENABLE_NLS
2777 setlocale(LC_MESSAGES, "");
2778 setlocale(LC_CTYPE, "");
2779 bindtextdomain(NLS_CAT_NAME, LOCALEDIR);
2780 textdomain(NLS_CAT_NAME);
2781 set_com_err_gettext(gettext);
2782 #endif
2783 if (argc && *argv)
2784 program_name = *argv;
2785 add_error_table(&et_ext2_error_table);
2786
2787 #ifdef CONFIG_BUILD_FINDFS
2788 if (strcmp(get_progname(argv[0]), "findfs") == 0)
2789 do_findfs(argc, argv);
2790 #endif
2791 if (strcmp(get_progname(argv[0]), "e2label") == 0)
2792 parse_e2label_options(argc, argv);
2793 else
2794 parse_tune2fs_options(argc, argv);
2795
2796 #ifdef CONFIG_TESTIO_DEBUG
2797 if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_DEBUG")) {
2798 io_ptr = test_io_manager;
2799 test_io_backing_manager = unix_io_manager;
2800 } else
2801 #endif
2802 io_ptr = unix_io_manager;
2803
2804 retry_open:
2805 if ((open_flag & EXT2_FLAG_RW) == 0 || f_flag)
2806 open_flag |= EXT2_FLAG_SKIP_MMP;
2807
2808 open_flag |= EXT2_FLAG_64BITS | EXT2_FLAG_JOURNAL_DEV_OK;
2809
2810 /* keep the filesystem struct around to dump MMP data */
2811 open_flag |= EXT2_FLAG_NOFREE_ON_ERROR;
2812
2813 retval = ext2fs_open2(device_name, io_options, open_flag,
2814 0, 0, io_ptr, &fs);
2815 if (retval) {
2816 com_err(program_name, retval,
2817 _("while trying to open %s"),
2818 device_name);
2819 if (retval == EXT2_ET_MMP_FSCK_ON ||
2820 retval == EXT2_ET_MMP_UNKNOWN_SEQ)
2821 dump_mmp_msg(fs->mmp_buf,
2822 _("If you are sure the filesystem "
2823 "is not in use on any node, run:\n"
2824 "'tune2fs -f -E clear_mmp {device}'\n"));
2825 else if (retval == EXT2_ET_MMP_FAILED)
2826 dump_mmp_msg(fs->mmp_buf, NULL);
2827 else if (retval == EXT2_ET_MMP_MAGIC_INVALID)
2828 fprintf(stderr,
2829 _("MMP block magic is bad. Try to fix it by "
2830 "running:\n'e2fsck -f %s'\n"), device_name);
2831 else if (retval == EXT2_ET_BAD_MAGIC)
2832 check_plausibility(device_name, CHECK_FS_EXIST, NULL);
2833 else if (retval != EXT2_ET_MMP_FAILED)
2834 fprintf(stderr, "%s",
2835 _("Couldn't find valid filesystem superblock.\n"));
2836
2837 ext2fs_free(fs);
2838 exit(1);
2839 }
2840 if (ext2fs_has_feature_journal_dev(fs->super)) {
2841 fprintf(stderr, "%s", _("Cannot modify a journal device.\n"));
2842 ext2fs_free(fs);
2843 exit(1);
2844 }
2845 fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE;
2846
2847 if (I_flag) {
2848 /*
2849 * Check the inode size is right so we can issue an
2850 * error message and bail before setting up the tdb
2851 * file.
2852 */
2853 if (new_inode_size == EXT2_INODE_SIZE(fs->super)) {
2854 fprintf(stderr, _("The inode size is already %lu\n"),
2855 new_inode_size);
2856 rc = 1;
2857 goto closefs;
2858 }
2859 if (new_inode_size < EXT2_INODE_SIZE(fs->super)) {
2860 fprintf(stderr, "%s",
2861 _("Shrinking inode size is not supported\n"));
2862 rc = 1;
2863 goto closefs;
2864 }
2865 if (new_inode_size > fs->blocksize) {
2866 fprintf(stderr, _("Invalid inode size %lu (max %d)\n"),
2867 new_inode_size, fs->blocksize);
2868 rc = 1;
2869 goto closefs;
2870 }
2871 check_fsck_needed(fs,
2872 _("Resizing inodes could take some time."));
2873 /*
2874 * If inode resize is requested use the
2875 * Undo I/O manager
2876 */
2877 undo_file = default_undo_file;
2878 }
2879
2880 /* Set up an undo file */
2881 if (undo_file && io_ptr_orig == NULL) {
2882 io_ptr_orig = io_ptr;
2883 retval = tune2fs_setup_tdb(device_name, &io_ptr);
2884 if (retval) {
2885 rc = 1;
2886 goto closefs;
2887 }
2888 if (io_ptr != io_ptr_orig) {
2889 ext2fs_close_free(&fs);
2890 goto retry_open;
2891 }
2892 }
2893
2894 sb = fs->super;
2895 fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY;
2896
2897 if (print_label) {
2898 /* For e2label emulation */
2899 printf("%.*s\n", (int) sizeof(sb->s_volume_name),
2900 sb->s_volume_name);
2901 remove_error_table(&et_ext2_error_table);
2902 goto closefs;
2903 }
2904
2905 retval = ext2fs_check_if_mounted(device_name, &mount_flags);
2906 if (retval) {
2907 com_err("ext2fs_check_if_mount", retval,
2908 _("while determining whether %s is mounted."),
2909 device_name);
2910 rc = 1;
2911 goto closefs;
2912 }
2913 /* Normally we only need to write out the superblock */
2914 fs->flags |= EXT2_FLAG_SUPER_ONLY;
2915
2916 if (c_flag) {
2917 sb->s_max_mnt_count = max_mount_count;
2918 ext2fs_mark_super_dirty(fs);
2919 printf(_("Setting maximal mount count to %d\n"),
2920 max_mount_count);
2921 }
2922 if (C_flag) {
2923 sb->s_mnt_count = mount_count;
2924 ext2fs_mark_super_dirty(fs);
2925 printf(_("Setting current mount count to %d\n"), mount_count);
2926 }
2927 if (e_flag) {
2928 sb->s_errors = errors;
2929 ext2fs_mark_super_dirty(fs);
2930 printf(_("Setting error behavior to %d\n"), errors);
2931 }
2932 if (g_flag) {
2933 sb->s_def_resgid = resgid;
2934 ext2fs_mark_super_dirty(fs);
2935 printf(_("Setting reserved blocks gid to %lu\n"), resgid);
2936 }
2937 if (i_flag) {
2938 if ((unsigned long long)interval >= (1ULL << 32)) {
2939 com_err(program_name, 0,
2940 _("interval between checks is too big (%lu)"),
2941 interval);
2942 rc = 1;
2943 goto closefs;
2944 }
2945 sb->s_checkinterval = interval;
2946 ext2fs_mark_super_dirty(fs);
2947 printf(_("Setting interval between checks to %lu seconds\n"),
2948 interval);
2949 }
2950 if (m_flag) {
2951 ext2fs_r_blocks_count_set(sb, reserved_ratio *
2952 ext2fs_blocks_count(sb) / 100.0);
2953 ext2fs_mark_super_dirty(fs);
2954 printf (_("Setting reserved blocks percentage to %g%% (%llu blocks)\n"),
2955 reserved_ratio, ext2fs_r_blocks_count(sb));
2956 }
2957 if (r_flag) {
2958 if (reserved_blocks > ext2fs_blocks_count(sb)/2) {
2959 com_err(program_name, 0,
2960 _("reserved blocks count is too big (%llu)"),
2961 reserved_blocks);
2962 rc = 1;
2963 goto closefs;
2964 }
2965 ext2fs_r_blocks_count_set(sb, reserved_blocks);
2966 ext2fs_mark_super_dirty(fs);
2967 printf(_("Setting reserved blocks count to %llu\n"),
2968 reserved_blocks);
2969 }
2970 if (s_flag == 1) {
2971 if (ext2fs_has_feature_sparse_super(sb)) {
2972 fputs(_("\nThe filesystem already has sparse "
2973 "superblocks.\n"), stderr);
2974 } else if (ext2fs_has_feature_meta_bg(sb)) {
2975 fputs(_("\nSetting the sparse superblock flag not "
2976 "supported\nfor filesystems with "
2977 "the meta_bg feature enabled.\n"),
2978 stderr);
2979 rc = 1;
2980 goto closefs;
2981 } else {
2982 ext2fs_set_feature_sparse_super(sb);
2983 sb->s_state &= ~EXT2_VALID_FS;
2984 ext2fs_mark_super_dirty(fs);
2985 printf(_("\nSparse superblock flag set. %s"),
2986 _(please_fsck));
2987 }
2988 }
2989 if (s_flag == 0) {
2990 fputs(_("\nClearing the sparse superblock flag not supported.\n"),
2991 stderr);
2992 rc = 1;
2993 goto closefs;
2994 }
2995 if (T_flag) {
2996 sb->s_lastcheck = last_check_time;
2997 ext2fs_mark_super_dirty(fs);
2998 printf(_("Setting time filesystem last checked to %s\n"),
2999 ctime(&last_check_time));
3000 }
3001 if (u_flag) {
3002 sb->s_def_resuid = resuid;
3003 ext2fs_mark_super_dirty(fs);
3004 printf(_("Setting reserved blocks uid to %lu\n"), resuid);
3005 }
3006 if (L_flag) {
3007 if (strlen(new_label) > sizeof(sb->s_volume_name))
3008 fputs(_("Warning: label too long, truncating.\n"),
3009 stderr);
3010 memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name));
3011 strncpy(sb->s_volume_name, new_label,
3012 sizeof(sb->s_volume_name));
3013 ext2fs_mark_super_dirty(fs);
3014 }
3015 if (M_flag) {
3016 memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted));
3017 strncpy(sb->s_last_mounted, new_last_mounted,
3018 sizeof(sb->s_last_mounted));
3019 ext2fs_mark_super_dirty(fs);
3020 }
3021 if (mntopts_cmd) {
3022 rc = update_mntopts(fs, mntopts_cmd);
3023 if (rc)
3024 goto closefs;
3025 }
3026 if (features_cmd) {
3027 rc = update_feature_set(fs, features_cmd);
3028 if (rc)
3029 goto closefs;
3030 }
3031 if (extended_cmd) {
3032 rc = parse_extended_opts(fs, extended_cmd);
3033 if (rc)
3034 goto closefs;
3035 if (clear_mmp && !f_flag) {
3036 fputs(_("Error in using clear_mmp. "
3037 "It must be used with -f\n"),
3038 stderr);
3039 goto closefs;
3040 }
3041 }
3042 if (clear_mmp) {
3043 rc = ext2fs_mmp_clear(fs);
3044 goto closefs;
3045 }
3046 if (journal_size || journal_device) {
3047 rc = add_journal(fs);
3048 if (rc)
3049 goto closefs;
3050 }
3051
3052 if (Q_flag) {
3053 if (mount_flags & EXT2_MF_MOUNTED) {
3054 fputs(_("The quota feature may only be changed when "
3055 "the filesystem is unmounted.\n"), stderr);
3056 rc = 1;
3057 goto closefs;
3058 }
3059 handle_quota_options(fs);
3060 }
3061
3062 if (U_flag) {
3063 int set_csum = 0;
3064 dgrp_t i;
3065 char buf[SUPERBLOCK_SIZE] __attribute__ ((aligned(8)));
3066 __u8 old_uuid[UUID_SIZE];
3067
3068 if (ext2fs_has_group_desc_csum(fs)) {
3069 /*
3070 * Changing the UUID on a metadata_csum FS requires
3071 * rewriting all metadata, which can race with a
3072 * mounted fs. Don't allow that unless we're saving
3073 * the checksum seed.
3074 */
3075 if ((mount_flags & EXT2_MF_MOUNTED) &&
3076 !ext2fs_has_feature_csum_seed(fs->super) &&
3077 ext2fs_has_feature_metadata_csum(fs->super)) {
3078 fputs(_("The UUID may only be "
3079 "changed when the filesystem is "
3080 "unmounted.\n"), stderr);
3081 fputs(_("If you only use kernels newer than "
3082 "v4.4, run 'tune2fs -O "
3083 "metadata_csum_seed' and re-run this "
3084 "command.\n"), stderr);
3085 try_confirm_csum_seed_support();
3086 exit(1);
3087 }
3088 if (!ext2fs_has_feature_csum_seed(fs->super))
3089 check_fsck_needed(fs,
3090 _("Setting UUID on a checksummed "
3091 "filesystem could take some time."));
3092
3093 /*
3094 * Determine if the block group checksums are
3095 * correct so we know whether or not to set
3096 * them later on.
3097 */
3098 for (i = 0; i < fs->group_desc_count; i++)
3099 if (!ext2fs_group_desc_csum_verify(fs, i))
3100 break;
3101 if (i >= fs->group_desc_count)
3102 set_csum = 1;
3103 }
3104
3105 memcpy(old_uuid, sb->s_uuid, UUID_SIZE);
3106 if ((strcasecmp(new_UUID, "null") == 0) ||
3107 (strcasecmp(new_UUID, "clear") == 0)) {
3108 uuid_clear(sb->s_uuid);
3109 } else if (strcasecmp(new_UUID, "time") == 0) {
3110 uuid_generate_time(sb->s_uuid);
3111 } else if (strcasecmp(new_UUID, "random") == 0) {
3112 uuid_generate(sb->s_uuid);
3113 } else if (uuid_parse(new_UUID, sb->s_uuid)) {
3114 com_err(program_name, 0, "%s",
3115 _("Invalid UUID format\n"));
3116 rc = 1;
3117 goto closefs;
3118 }
3119 ext2fs_init_csum_seed(fs);
3120 if (set_csum) {
3121 for (i = 0; i < fs->group_desc_count; i++)
3122 ext2fs_group_desc_csum_set(fs, i);
3123 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
3124 }
3125
3126 /* If this is a journal dev, we need to copy UUID into jsb */
3127 if (!(rc = get_journal_sb(fs, buf))) {
3128 journal_superblock_t *jsb;
3129
3130 jsb = (journal_superblock_t *) buf;
3131 fputs(_("Need to update journal superblock.\n"), stdout);
3132 memcpy(jsb->s_uuid, sb->s_uuid, sizeof(sb->s_uuid));
3133
3134 /* Writeback the journal superblock */
3135 if ((rc = io_channel_write_blk64(fs->io,
3136 ext2fs_journal_sb_start(fs->blocksize),
3137 -SUPERBLOCK_SIZE, buf)))
3138 goto closefs;
3139 } else if (rc != EXT2_ET_UNSUPP_FEATURE)
3140 goto closefs;
3141 else {
3142 rc = 0; /** Reset rc to avoid ext2fs_mmp_stop() */
3143
3144 if ((rc = fs_update_journal_user(sb, old_uuid)))
3145 goto closefs;
3146 }
3147
3148 ext2fs_mark_super_dirty(fs);
3149 if (ext2fs_has_feature_metadata_csum(fs->super) &&
3150 !ext2fs_has_feature_csum_seed(fs->super))
3151 rewrite_checksums = 1;
3152 }
3153
3154 if (I_flag) {
3155 if (mount_flags & EXT2_MF_MOUNTED) {
3156 fputs(_("The inode size may only be "
3157 "changed when the filesystem is "
3158 "unmounted.\n"), stderr);
3159 rc = 1;
3160 goto closefs;
3161 }
3162 if (ext2fs_has_feature_flex_bg(fs->super)) {
3163 fputs(_("Changing the inode size not supported for "
3164 "filesystems with the flex_bg\n"
3165 "feature enabled.\n"),
3166 stderr);
3167 rc = 1;
3168 goto closefs;
3169 }
3170 /*
3171 * We want to update group descriptor also
3172 * with the new free inode count
3173 */
3174 if (rewrite_checksums)
3175 fs->flags |= EXT2_FLAG_IGNORE_CSUM_ERRORS;
3176 fs->flags &= ~EXT2_FLAG_SUPER_ONLY;
3177 retval = resize_inode(fs, new_inode_size);
3178 if (rewrite_checksums)
3179 fs->flags &= ~EXT2_FLAG_IGNORE_CSUM_ERRORS;
3180 if (retval == 0) {
3181 printf(_("Setting inode size %lu\n"),
3182 new_inode_size);
3183 rewrite_checksums = 1;
3184 } else {
3185 printf("%s", _("Failed to change inode size\n"));
3186 rc = 1;
3187 goto closefs;
3188 }
3189 }
3190
3191 if (rewrite_checksums)
3192 rewrite_metadata_checksums(fs);
3193
3194 if (l_flag)
3195 list_super(sb);
3196 if (stride_set) {
3197 sb->s_raid_stride = stride;
3198 ext2fs_mark_super_dirty(fs);
3199 printf(_("Setting stride size to %d\n"), stride);
3200 }
3201 if (stripe_width_set) {
3202 sb->s_raid_stripe_width = stripe_width;
3203 ext2fs_mark_super_dirty(fs);
3204 printf(_("Setting stripe width to %d\n"), stripe_width);
3205 }
3206 if (ext_mount_opts) {
3207 strncpy((char *)(fs->super->s_mount_opts), ext_mount_opts,
3208 sizeof(fs->super->s_mount_opts));
3209 fs->super->s_mount_opts[sizeof(fs->super->s_mount_opts)-1] = 0;
3210 ext2fs_mark_super_dirty(fs);
3211 printf(_("Setting extended default mount options to '%s'\n"),
3212 ext_mount_opts);
3213 free(ext_mount_opts);
3214 }
3215
3216 #ifdef NO_RECOVERY
3217 /* Warn if file system needs recovery and it is opened for writing. */
3218 if ((open_flag & EXT2_FLAG_RW) && !(mount_flags & EXT2_MF_MOUNTED) &&
3219 (sb->s_feature_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) &&
3220 (sb->s_feature_incompat & EXT3_FEATURE_INCOMPAT_RECOVER)) {
3221 fprintf(stderr,
3222 _("Warning: The journal is dirty. You may wish to replay the journal like:\n\n"
3223 "\te2fsck -E journal_only %s\n\n"
3224 "then rerun this command. Otherwise, any changes made may be overwritten\n"
3225 "by journal recovery.\n"), device_name);
3226 }
3227 #else
3228 /* Recover the journal if possible. */
3229 if ((open_flag & EXT2_FLAG_RW) && !(mount_flags & EXT2_MF_MOUNTED) &&
3230 ext2fs_has_feature_journal_needs_recovery(fs->super)) {
3231 errcode_t err;
3232
3233 printf(_("Recovering journal.\n"));
3234 err = ext2fs_run_ext3_journal(&fs);
3235 if (err) {
3236 com_err("tune2fs", err, "while recovering journal.\n");
3237 printf(_("Please run e2fsck -fy %s.\n"), argv[1]);
3238 if (fs)
3239 ext2fs_close_free(&fs);
3240 exit(1);
3241 }
3242 ext2fs_clear_feature_journal_needs_recovery(fs->super);
3243 ext2fs_mark_super_dirty(fs);
3244 }
3245 #endif
3246
3247 free(device_name);
3248 remove_error_table(&et_ext2_error_table);
3249
3250 closefs:
3251 if (rc) {
3252 ext2fs_mmp_stop(fs);
3253 #ifndef BUILD_AS_LIB
3254 exit(1);
3255 #endif
3256 }
3257
3258 if (feature_64bit)
3259 convert_64bit(fs, feature_64bit);
3260 return (ext2fs_close_free(&fs) ? 1 : 0);
3261 }