]>
Commit | Line | Data |
---|---|---|
3839e657 | 1 | /* |
4d0f2283 | 2 | * tune2fs.c - Change the file system parameters on an ext2 file system |
3839e657 TT |
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 | * | |
4d0f2283 | 8 | * Copyright 1995, 1996, 1997, 1998, 1999, 2000 by Theodore Ts'o. |
19c78dc0 TT |
9 | * |
10 | * %Begin-Header% | |
11 | * This file may be redistributed under the terms of the GNU Public | |
12 | * License. | |
13 | * %End-Header% | |
3839e657 TT |
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 | ||
ebabf2ad | 28 | #define _XOPEN_SOURCE 600 /* for inclusion of strptime() */ |
d1154eb4 | 29 | #include "config.h" |
3839e657 | 30 | #include <fcntl.h> |
f3db3566 | 31 | #include <grp.h> |
a418d3ad | 32 | #ifdef HAVE_GETOPT_H |
3839e657 | 33 | #include <getopt.h> |
373b8337 TT |
34 | #else |
35 | extern char *optarg; | |
36 | extern int optind; | |
a418d3ad | 37 | #endif |
f3db3566 | 38 | #include <pwd.h> |
3839e657 | 39 | #include <stdio.h> |
f38cf3cb | 40 | #ifdef HAVE_STDLIB_H |
3839e657 | 41 | #include <stdlib.h> |
f38cf3cb | 42 | #endif |
269da3b8 AD |
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 | #endif | |
3839e657 TT |
48 | #include <string.h> |
49 | #include <time.h> | |
50 | #include <unistd.h> | |
f3db3566 | 51 | #include <sys/types.h> |
64d588cf | 52 | #include <libgen.h> |
36585791 | 53 | #include <limits.h> |
3839e657 | 54 | |
54c637d4 | 55 | #include "ext2fs/ext2_fs.h" |
3839e657 TT |
56 | #include "ext2fs/ext2fs.h" |
57 | #include "et/com_err.h" | |
1e3472c5 | 58 | #include "uuid/uuid.h" |
3839e657 | 59 | #include "e2p/e2p.h" |
dc2ec525 | 60 | #include "jfs_user.h" |
63985320 | 61 | #include "util.h" |
ed1b33e8 | 62 | #include "blkid/blkid.h" |
d37178bb | 63 | #include "quota/quotaio.h" |
3839e657 TT |
64 | |
65 | #include "../version.h" | |
d9c56d3c | 66 | #include "nls-enable.h" |
3839e657 | 67 | |
771e8db9 AK |
68 | #define QOPT_ENABLE (1) |
69 | #define QOPT_DISABLE (-1) | |
70 | ||
71 | extern int ask_yn(const char *string, int def); | |
72 | ||
ec43da2f TT |
73 | const char *program_name = "tune2fs"; |
74 | char *device_name; | |
75 | char *new_label, *new_last_mounted, *new_UUID; | |
76 | char *io_options; | |
4d0f2283 | 77 | static int c_flag, C_flag, e_flag, f_flag, g_flag, i_flag, l_flag, L_flag; |
771e8db9 | 78 | static int m_flag, M_flag, Q_flag, r_flag, s_flag = -1, u_flag, U_flag, T_flag; |
64d588cf | 79 | static int I_flag; |
0f5eba75 | 80 | static int clear_mmp; |
d4de4aa9 | 81 | static time_t last_check_time; |
83238153 | 82 | static int print_label; |
63985320 | 83 | static int max_mount_count, mount_count, mount_flags; |
4efbac6f VAH |
84 | static unsigned long interval; |
85 | static blk64_t reserved_blocks; | |
ce911145 | 86 | static double reserved_ratio; |
63985320 TT |
87 | static unsigned long resgid, resuid; |
88 | static unsigned short errors; | |
83238153 TT |
89 | static int open_flag; |
90 | static char *features_cmd; | |
a0c3fd5e | 91 | static char *mntopts_cmd; |
0c17cb25 TT |
92 | static int stride, stripe_width; |
93 | static int stride_set, stripe_width_set; | |
6cb27404 | 94 | static char *extended_cmd; |
721b367a | 95 | static unsigned long new_inode_size; |
9a976ac7 | 96 | static char *ext_mount_opts; |
771e8db9 | 97 | static int usrquota, grpquota; |
3839e657 | 98 | |
63985320 TT |
99 | int journal_size, journal_flags; |
100 | char *journal_device; | |
b818205f | 101 | static blk64_t journal_location = ~0LL; |
63985320 | 102 | |
64d588cf AK |
103 | static struct list_head blk_move_list; |
104 | ||
105 | struct blk_move { | |
106 | struct list_head list; | |
7117f8d6 VAH |
107 | blk64_t old_loc; |
108 | blk64_t new_loc; | |
64d588cf AK |
109 | }; |
110 | ||
111 | ||
63985320 | 112 | static const char *please_fsck = N_("Please run e2fsck on the filesystem.\n"); |
1e3472c5 | 113 | |
14b596d4 | 114 | #ifdef CONFIG_BUILD_FINDFS |
3e699064 | 115 | void do_findfs(int argc, char **argv); |
14b596d4 | 116 | #endif |
3e699064 | 117 | |
818180cd | 118 | static void usage(void) |
3839e657 | 119 | { |
b21e38a0 | 120 | fprintf(stderr, |
bb145b01 | 121 | _("Usage: %s [-c max_mounts_count] [-e errors_behavior] " |
b21e38a0 | 122 | "[-g group]\n" |
ff662d5d TT |
123 | "\t[-i interval[d|m|w]] [-j] [-J journal_options] [-l]\n" |
124 | "\t[-m reserved_blocks_percent] " | |
0f5eba75 | 125 | "[-o [^]mount_options[,...]] [-p mmp_update_interval]\n" |
ff662d5d TT |
126 | "\t[-r reserved_blocks_count] [-u user] [-C mount_count] " |
127 | "[-L volume_label]\n" | |
6cb27404 | 128 | "\t[-M last_mounted_dir] [-O [^]feature[,...]]\n" |
d82445e9 ES |
129 | #ifdef CONFIG_QUOTA |
130 | "\t[-Q quota_options]\n" | |
131 | #endif | |
6cb27404 | 132 | "\t[-E extended-option[,...]] [-T last_check_time] " |
64d588cf | 133 | "[-U UUID]\n\t[ -I new_inode_size ] device\n"), program_name); |
ec43da2f | 134 | exit(1); |
3839e657 TT |
135 | } |
136 | ||
896938d5 | 137 | static __u32 ok_features[3] = { |
558df544 | 138 | /* Compat */ |
843049c4 | 139 | EXT3_FEATURE_COMPAT_HAS_JOURNAL | |
558df544 TT |
140 | EXT2_FEATURE_COMPAT_DIR_INDEX, |
141 | /* Incompat */ | |
a49670e6 TT |
142 | EXT2_FEATURE_INCOMPAT_FILETYPE | |
143 | EXT3_FEATURE_INCOMPAT_EXTENTS | | |
0f5eba75 AD |
144 | EXT4_FEATURE_INCOMPAT_FLEX_BG | |
145 | EXT4_FEATURE_INCOMPAT_MMP, | |
558df544 TT |
146 | /* R/O compat */ |
147 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE | | |
2be8fe43 TT |
148 | EXT4_FEATURE_RO_COMPAT_HUGE_FILE| |
149 | EXT4_FEATURE_RO_COMPAT_DIR_NLINK| | |
150 | EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| | |
4e988cb4 | 151 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM | |
44a2cca3 | 152 | #ifdef CONFIG_QUOTA |
0f818659 | 153 | EXT4_FEATURE_RO_COMPAT_QUOTA | |
44a2cca3 | 154 | #endif |
0f818659 | 155 | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER |
896938d5 TT |
156 | }; |
157 | ||
7c4a2ef5 | 158 | static __u32 clear_ok_features[3] = { |
558df544 | 159 | /* Compat */ |
7c4a2ef5 | 160 | EXT3_FEATURE_COMPAT_HAS_JOURNAL | |
037914e2 | 161 | EXT2_FEATURE_COMPAT_RESIZE_INODE | |
558df544 TT |
162 | EXT2_FEATURE_COMPAT_DIR_INDEX, |
163 | /* Incompat */ | |
a49670e6 | 164 | EXT2_FEATURE_INCOMPAT_FILETYPE | |
0f5eba75 AD |
165 | EXT4_FEATURE_INCOMPAT_FLEX_BG | |
166 | EXT4_FEATURE_INCOMPAT_MMP, | |
558df544 | 167 | /* R/O compat */ |
4e988cb4 | 168 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE | |
2be8fe43 TT |
169 | EXT4_FEATURE_RO_COMPAT_HUGE_FILE| |
170 | EXT4_FEATURE_RO_COMPAT_DIR_NLINK| | |
171 | EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE| | |
44a2cca3 | 172 | #ifdef CONFIG_QUOTA |
0f818659 | 173 | EXT4_FEATURE_RO_COMPAT_QUOTA | |
44a2cca3 | 174 | #endif |
0f818659 | 175 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM |
896938d5 TT |
176 | }; |
177 | ||
dc2ec525 TT |
178 | /* |
179 | * Remove an external journal from the filesystem | |
180 | */ | |
0f5eba75 | 181 | static int remove_journal_device(ext2_filsys fs) |
dc2ec525 | 182 | { |
4ea7bd04 | 183 | char *journal_path; |
dc2ec525 TT |
184 | ext2_filsys jfs; |
185 | char buf[1024]; | |
186 | journal_superblock_t *jsb; | |
187 | int i, nr_users; | |
188 | errcode_t retval; | |
4d0f2283 | 189 | int commit_remove_journal = 0; |
2a29f135 | 190 | io_manager io_ptr; |
4d0f2283 TT |
191 | |
192 | if (f_flag) | |
193 | commit_remove_journal = 1; /* force removal even if error */ | |
dc2ec525 | 194 | |
2d15576d | 195 | uuid_unparse(fs->super->s_journal_uuid, buf); |
ed1b33e8 | 196 | journal_path = blkid_get_devname(NULL, "UUID", buf); |
2d15576d | 197 | |
4ea7bd04 TT |
198 | if (!journal_path) { |
199 | journal_path = | |
2d15576d | 200 | ext2fs_find_block_device(fs->super->s_journal_dev); |
4ea7bd04 | 201 | if (!journal_path) |
d90d6a71 | 202 | goto no_valid_journal; |
2d15576d | 203 | } |
dc2ec525 | 204 | |
2a29f135 | 205 | #ifdef CONFIG_TESTIO_DEBUG |
f38cf3cb TT |
206 | if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { |
207 | io_ptr = test_io_manager; | |
208 | test_io_backing_manager = unix_io_manager; | |
209 | } else | |
2a29f135 | 210 | #endif |
f38cf3cb | 211 | io_ptr = unix_io_manager; |
4ea7bd04 | 212 | retval = ext2fs_open(journal_path, EXT2_FLAG_RW| |
dc2ec525 | 213 | EXT2_FLAG_JOURNAL_DEV_OK, 0, |
2a29f135 | 214 | fs->blocksize, io_ptr, &jfs); |
dc2ec525 | 215 | if (retval) { |
45ff69ff | 216 | com_err(program_name, retval, "%s", |
dc2ec525 | 217 | _("while trying to open external journal")); |
4d0f2283 | 218 | goto no_valid_journal; |
dc2ec525 | 219 | } |
6747ac86 AD |
220 | if (!(jfs->super->s_feature_incompat & |
221 | EXT3_FEATURE_INCOMPAT_JOURNAL_DEV)) { | |
7141b54b | 222 | fprintf(stderr, _("%s is not a journal device.\n"), |
4ea7bd04 | 223 | journal_path); |
4d0f2283 | 224 | goto no_valid_journal; |
dc2ec525 TT |
225 | } |
226 | ||
227 | /* Get the journal superblock */ | |
24a117ab | 228 | if ((retval = io_channel_read_blk64(jfs->io, 1, -1024, buf))) { |
45ff69ff | 229 | com_err(program_name, retval, "%s", |
dc2ec525 | 230 | _("while reading journal superblock")); |
4d0f2283 | 231 | goto no_valid_journal; |
dc2ec525 TT |
232 | } |
233 | ||
234 | jsb = (journal_superblock_t *) buf; | |
6747ac86 AD |
235 | if ((jsb->s_header.h_magic != (unsigned)ntohl(JFS_MAGIC_NUMBER)) || |
236 | (jsb->s_header.h_blocktype != (unsigned)ntohl(JFS_SUPERBLOCK_V2))) { | |
54434927 | 237 | fputs(_("Journal superblock not found!\n"), stderr); |
4d0f2283 | 238 | goto no_valid_journal; |
dc2ec525 TT |
239 | } |
240 | ||
241 | /* Find the filesystem UUID */ | |
242 | nr_users = ntohl(jsb->s_nr_users); | |
ec43da2f | 243 | for (i = 0; i < nr_users; i++) { |
6747ac86 | 244 | if (memcmp(fs->super->s_uuid, &jsb->s_users[i * 16], 16) == 0) |
dc2ec525 TT |
245 | break; |
246 | } | |
247 | if (i >= nr_users) { | |
efc6f628 | 248 | fputs(_("Filesystem's UUID not found on journal device.\n"), |
54434927 | 249 | stderr); |
4d0f2283 TT |
250 | commit_remove_journal = 1; |
251 | goto no_valid_journal; | |
dc2ec525 TT |
252 | } |
253 | nr_users--; | |
ec43da2f | 254 | for (i = 0; i < nr_users; i++) |
6747ac86 | 255 | memcpy(&jsb->s_users[i * 16], &jsb->s_users[(i + 1) * 16], 16); |
dc2ec525 TT |
256 | jsb->s_nr_users = htonl(nr_users); |
257 | ||
258 | /* Write back the journal superblock */ | |
24a117ab | 259 | if ((retval = io_channel_write_blk64(jfs->io, 1, -1024, buf))) { |
dc2ec525 TT |
260 | com_err(program_name, retval, |
261 | "while writing journal superblock."); | |
4d0f2283 | 262 | goto no_valid_journal; |
dc2ec525 TT |
263 | } |
264 | ||
4d0f2283 TT |
265 | commit_remove_journal = 1; |
266 | ||
267 | no_valid_journal: | |
268 | if (commit_remove_journal == 0) { | |
d90d6a71 AD |
269 | fputs(_("Cannot locate journal device. It was NOT removed\n" |
270 | "Use -f option to remove missing journal device.\n"), | |
271 | stderr); | |
0f5eba75 | 272 | return 1; |
4d0f2283 | 273 | } |
dc2ec525 | 274 | fs->super->s_journal_dev = 0; |
ed1b33e8 | 275 | uuid_clear(fs->super->s_journal_uuid); |
dc2ec525 | 276 | ext2fs_mark_super_dirty(fs); |
54434927 | 277 | fputs(_("Journal removed\n"), stdout); |
4ea7bd04 | 278 | free(journal_path); |
0f5eba75 AD |
279 | |
280 | return 0; | |
dc2ec525 TT |
281 | } |
282 | ||
194686bb | 283 | /* Helper function for remove_journal_inode */ |
7117f8d6 VAH |
284 | static int release_blocks_proc(ext2_filsys fs, blk64_t *blocknr, |
285 | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), | |
286 | blk64_t ref_block EXT2FS_ATTR((unused)), | |
287 | int ref_offset EXT2FS_ATTR((unused)), | |
54434927 | 288 | void *private EXT2FS_ATTR((unused))) |
194686bb | 289 | { |
7117f8d6 | 290 | blk64_t block; |
194686bb TT |
291 | int group; |
292 | ||
293 | block = *blocknr; | |
3c041a51 | 294 | ext2fs_unmark_block_bitmap2(fs->block_map, block); |
6493f8e8 | 295 | group = ext2fs_group_of_blk2(fs, block); |
d7cca6b0 | 296 | ext2fs_bg_free_blocks_count_set(fs, group, ext2fs_bg_free_blocks_count(fs, group) + 1); |
4e988cb4 | 297 | ext2fs_group_desc_csum_set(fs, group); |
fe75afbf | 298 | ext2fs_free_blocks_count_add(fs->super, EXT2FS_CLUSTER_RATIO(fs)); |
194686bb TT |
299 | return 0; |
300 | } | |
301 | ||
302 | /* | |
303 | * Remove the journal inode from the filesystem | |
304 | */ | |
0f5eba75 | 305 | static errcode_t remove_journal_inode(ext2_filsys fs) |
194686bb TT |
306 | { |
307 | struct ext2_inode inode; | |
308 | errcode_t retval; | |
309 | ino_t ino = fs->super->s_journal_inum; | |
efc6f628 | 310 | |
194686bb TT |
311 | retval = ext2fs_read_inode(fs, ino, &inode); |
312 | if (retval) { | |
45ff69ff | 313 | com_err(program_name, retval, "%s", |
194686bb | 314 | _("while reading journal inode")); |
0f5eba75 | 315 | return retval; |
194686bb TT |
316 | } |
317 | if (ino == EXT2_JOURNAL_INO) { | |
318 | retval = ext2fs_read_bitmaps(fs); | |
319 | if (retval) { | |
45ff69ff | 320 | com_err(program_name, retval, "%s", |
194686bb | 321 | _("while reading bitmaps")); |
0f5eba75 | 322 | return retval; |
194686bb | 323 | } |
7117f8d6 VAH |
324 | retval = ext2fs_block_iterate3(fs, ino, |
325 | BLOCK_FLAG_READ_ONLY, NULL, | |
326 | release_blocks_proc, NULL); | |
194686bb | 327 | if (retval) { |
45ff69ff | 328 | com_err(program_name, retval, "%s", |
194686bb | 329 | _("while clearing journal inode")); |
0f5eba75 | 330 | return retval; |
194686bb TT |
331 | } |
332 | memset(&inode, 0, sizeof(inode)); | |
333 | ext2fs_mark_bb_dirty(fs); | |
194686bb TT |
334 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; |
335 | } else | |
336 | inode.i_flags &= ~EXT2_IMMUTABLE_FL; | |
337 | retval = ext2fs_write_inode(fs, ino, &inode); | |
338 | if (retval) { | |
45ff69ff | 339 | com_err(program_name, retval, "%s", |
194686bb | 340 | _("while writing journal inode")); |
0f5eba75 | 341 | return retval; |
194686bb TT |
342 | } |
343 | fs->super->s_journal_inum = 0; | |
344 | ext2fs_mark_super_dirty(fs); | |
0f5eba75 AD |
345 | |
346 | return 0; | |
194686bb | 347 | } |
dc2ec525 | 348 | |
a0c3fd5e TT |
349 | /* |
350 | * Update the default mount options | |
351 | */ | |
0f5eba75 | 352 | static int update_mntopts(ext2_filsys fs, char *mntopts) |
a0c3fd5e | 353 | { |
ec43da2f | 354 | struct ext2_super_block *sb = fs->super; |
a0c3fd5e TT |
355 | |
356 | if (e2p_edit_mntopts(mntopts, &sb->s_default_mount_opts, ~0)) { | |
357 | fprintf(stderr, _("Invalid mount option set: %s\n"), | |
358 | mntopts); | |
0f5eba75 | 359 | return 1; |
a0c3fd5e TT |
360 | } |
361 | ext2fs_mark_super_dirty(fs); | |
0f5eba75 AD |
362 | |
363 | return 0; | |
a0c3fd5e TT |
364 | } |
365 | ||
66457fcb DW |
366 | static int check_fsck_needed(ext2_filsys fs) |
367 | { | |
368 | if (fs->super->s_state & EXT2_VALID_FS) | |
369 | return 0; | |
370 | printf("\n%s\n", _(please_fsck)); | |
371 | if (mount_flags & EXT2_MF_READONLY) | |
4d46e6c7 | 372 | printf("%s", _("(and reboot afterwards!)\n")); |
66457fcb DW |
373 | return 1; |
374 | } | |
375 | ||
079ad63d TT |
376 | static void request_fsck_afterwards(ext2_filsys fs) |
377 | { | |
378 | static int requested = 0; | |
379 | ||
380 | if (requested++) | |
381 | return; | |
382 | fs->super->s_state &= ~EXT2_VALID_FS; | |
383 | printf("\n%s\n", _(please_fsck)); | |
384 | if (mount_flags & EXT2_MF_READONLY) | |
45ff69ff | 385 | printf("%s", _("(and reboot afterwards!)\n")); |
079ad63d TT |
386 | } |
387 | ||
63985320 TT |
388 | /* |
389 | * Update the feature set as provided by the user. | |
390 | */ | |
0f5eba75 | 391 | static int update_feature_set(ext2_filsys fs, char *features) |
63985320 | 392 | { |
ec43da2f | 393 | struct ext2_super_block *sb = fs->super; |
079ad63d | 394 | struct ext2_group_desc *gd; |
885bf6b8 | 395 | __u32 old_features[3]; |
577c773a TT |
396 | dgrp_t i; |
397 | int type_err; | |
7c4a2ef5 | 398 | unsigned int mask_err; |
2eb3b20e | 399 | |
885bf6b8 TT |
400 | #define FEATURE_ON(type, mask) (!(old_features[(type)] & (mask)) && \ |
401 | ((&sb->s_feature_compat)[(type)] & (mask))) | |
402 | #define FEATURE_OFF(type, mask) ((old_features[(type)] & (mask)) && \ | |
403 | !((&sb->s_feature_compat)[(type)] & (mask))) | |
404 | #define FEATURE_CHANGED(type, mask) ((mask) & \ | |
405 | (old_features[(type)] ^ (&sb->s_feature_compat)[(type)])) | |
406 | ||
407 | old_features[E2P_FEATURE_COMPAT] = sb->s_feature_compat; | |
408 | old_features[E2P_FEATURE_INCOMPAT] = sb->s_feature_incompat; | |
409 | old_features[E2P_FEATURE_RO_INCOMPAT] = sb->s_feature_ro_compat; | |
410 | ||
7c4a2ef5 TT |
411 | if (e2p_edit_feature2(features, &sb->s_feature_compat, |
412 | ok_features, clear_ok_features, | |
413 | &type_err, &mask_err)) { | |
414 | if (!mask_err) | |
415 | fprintf(stderr, | |
416 | _("Invalid filesystem option set: %s\n"), | |
417 | features); | |
418 | else if (type_err & E2P_FEATURE_NEGATE_FLAG) | |
419 | fprintf(stderr, _("Clearing filesystem feature '%s' " | |
420 | "not supported.\n"), | |
421 | e2p_feature2string(type_err & | |
422 | E2P_FEATURE_TYPE_MASK, | |
423 | mask_err)); | |
424 | else | |
425 | fprintf(stderr, _("Setting filesystem feature '%s' " | |
426 | "not supported.\n"), | |
427 | e2p_feature2string(type_err, mask_err)); | |
0f5eba75 | 428 | return 1; |
63985320 | 429 | } |
885bf6b8 TT |
430 | |
431 | if (FEATURE_OFF(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { | |
63985320 TT |
432 | if ((mount_flags & EXT2_MF_MOUNTED) && |
433 | !(mount_flags & EXT2_MF_READONLY)) { | |
2be8fe43 | 434 | fputs(_("The has_journal feature may only be " |
54434927 TT |
435 | "cleared when the filesystem is\n" |
436 | "unmounted or mounted " | |
437 | "read-only.\n"), stderr); | |
0f5eba75 | 438 | return 1; |
63985320 | 439 | } |
5fe2bd60 ES |
440 | if ((sb->s_feature_incompat & |
441 | EXT3_FEATURE_INCOMPAT_RECOVER) && | |
442 | f_flag < 2) { | |
54434927 TT |
443 | fputs(_("The needs_recovery flag is set. " |
444 | "Please run e2fsck before clearing\n" | |
445 | "the has_journal flag.\n"), stderr); | |
0f5eba75 | 446 | return 1; |
63985320 | 447 | } |
63985320 | 448 | if (sb->s_journal_inum) { |
0f5eba75 AD |
449 | if (remove_journal_inode(fs)) |
450 | return 1; | |
63985320 | 451 | } |
de49f015 | 452 | if (sb->s_journal_dev) { |
0f5eba75 AD |
453 | if (remove_journal_device(fs)) |
454 | return 1; | |
de49f015 | 455 | } |
63985320 | 456 | } |
9e7294d3 AF |
457 | |
458 | if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, | |
459 | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER)) { | |
460 | if (sb->s_feature_incompat & | |
461 | EXT2_FEATURE_INCOMPAT_META_BG) { | |
462 | fputs(_("Setting filesystem feature 'sparse_super' " | |
463 | "not supported\nfor filesystems with " | |
464 | "the meta_bg feature enabled.\n"), | |
465 | stderr); | |
466 | return 1; | |
467 | } | |
468 | } | |
469 | ||
0f5eba75 AD |
470 | if (FEATURE_ON(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) { |
471 | int error; | |
472 | ||
473 | if ((mount_flags & EXT2_MF_MOUNTED) || | |
474 | (mount_flags & EXT2_MF_READONLY)) { | |
475 | fputs(_("The multiple mount protection feature can't\n" | |
476 | "be set if the filesystem is mounted or\n" | |
477 | "read-only.\n"), stderr); | |
478 | return 1; | |
479 | } | |
480 | ||
481 | error = ext2fs_mmp_init(fs); | |
482 | if (error) { | |
483 | fputs(_("\nError while enabling multiple mount " | |
484 | "protection feature."), stderr); | |
485 | return 1; | |
486 | } | |
487 | ||
488 | /* | |
489 | * We want to update group desc with the new free blocks count | |
490 | */ | |
491 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
492 | ||
493 | printf(_("Multiple mount protection has been enabled " | |
494 | "with update interval %ds.\n"), | |
495 | sb->s_mmp_update_interval); | |
496 | } | |
497 | ||
498 | if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_MMP)) { | |
499 | int error; | |
500 | ||
501 | if (mount_flags & EXT2_MF_READONLY) { | |
502 | fputs(_("The multiple mount protection feature cannot\n" | |
503 | "be disabled if the filesystem is readonly.\n"), | |
504 | stderr); | |
505 | return 1; | |
506 | } | |
507 | ||
508 | error = ext2fs_read_bitmaps(fs); | |
509 | if (error) { | |
510 | fputs(_("Error while reading bitmaps\n"), stderr); | |
511 | return 1; | |
512 | } | |
513 | ||
514 | error = ext2fs_mmp_read(fs, sb->s_mmp_block, NULL); | |
515 | if (error) { | |
516 | struct mmp_struct *mmp_cmp = fs->mmp_cmp; | |
517 | ||
518 | if (error == EXT2_ET_MMP_MAGIC_INVALID) | |
519 | printf(_("Magic number in MMP block does not " | |
520 | "match. expected: %x, actual: %x\n"), | |
521 | EXT4_MMP_MAGIC, mmp_cmp->mmp_magic); | |
522 | else | |
45ff69ff | 523 | com_err(program_name, error, "%s", |
0f5eba75 AD |
524 | _("while reading MMP block.")); |
525 | goto mmp_error; | |
526 | } | |
527 | ||
528 | /* We need to force out the group descriptors as well */ | |
529 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
4dbfd79d | 530 | ext2fs_block_alloc_stats2(fs, sb->s_mmp_block, -1); |
0f5eba75 AD |
531 | mmp_error: |
532 | sb->s_mmp_block = 0; | |
533 | sb->s_mmp_update_interval = 0; | |
534 | } | |
885bf6b8 TT |
535 | |
536 | if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT3_FEATURE_COMPAT_HAS_JOURNAL)) { | |
63985320 TT |
537 | /* |
538 | * If adding a journal flag, let the create journal | |
7a0516a3 BS |
539 | * code below handle setting the flag and creating the |
540 | * journal. We supply a default size if necessary. | |
63985320 | 541 | */ |
dc2ec525 TT |
542 | if (!journal_size) |
543 | journal_size = -1; | |
08dd830d | 544 | sb->s_feature_compat &= ~EXT3_FEATURE_COMPAT_HAS_JOURNAL; |
63985320 | 545 | } |
885bf6b8 TT |
546 | |
547 | if (FEATURE_ON(E2P_FEATURE_COMPAT, EXT2_FEATURE_COMPAT_DIR_INDEX)) { | |
843049c4 | 548 | if (!sb->s_def_hash_version) |
d1070d91 | 549 | sb->s_def_hash_version = EXT2_HASH_HALF_MD4; |
843049c4 TT |
550 | if (uuid_is_null((unsigned char *) sb->s_hash_seed)) |
551 | uuid_generate((unsigned char *) sb->s_hash_seed); | |
552 | } | |
dc2ec525 | 553 | |
a49670e6 | 554 | if (FEATURE_OFF(E2P_FEATURE_INCOMPAT, EXT4_FEATURE_INCOMPAT_FLEX_BG)) { |
c2d4300b JS |
555 | if (ext2fs_check_desc(fs)) { |
556 | fputs(_("Clearing the flex_bg flag would " | |
557 | "cause the the filesystem to be\n" | |
558 | "inconsistent.\n"), stderr); | |
0f5eba75 | 559 | return 1; |
c2d4300b JS |
560 | } |
561 | } | |
dc2ec525 | 562 | |
2be8fe43 TT |
563 | if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, |
564 | EXT4_FEATURE_RO_COMPAT_HUGE_FILE)) { | |
565 | if ((mount_flags & EXT2_MF_MOUNTED) && | |
566 | !(mount_flags & EXT2_MF_READONLY)) { | |
567 | fputs(_("The huge_file feature may only be " | |
568 | "cleared when the filesystem is\n" | |
569 | "unmounted or mounted " | |
570 | "read-only.\n"), stderr); | |
0f5eba75 | 571 | return 1; |
2be8fe43 TT |
572 | } |
573 | } | |
574 | ||
079ad63d TT |
575 | if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, |
576 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { | |
7117f8d6 VAH |
577 | for (i = 0; i < fs->group_desc_count; i++) { |
578 | gd = ext2fs_group_desc(fs, fs->group_desc, i); | |
079ad63d TT |
579 | gd->bg_itable_unused = 0; |
580 | gd->bg_flags = EXT2_BG_INODE_ZEROED; | |
581 | ext2fs_group_desc_csum_set(fs, i); | |
582 | } | |
583 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
584 | } | |
585 | ||
586 | if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, | |
587 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM)) { | |
7117f8d6 VAH |
588 | for (i = 0; i < fs->group_desc_count; i++) { |
589 | gd = ext2fs_group_desc(fs, fs->group_desc, i); | |
079ad63d TT |
590 | if ((gd->bg_flags & EXT2_BG_INODE_ZEROED) == 0) { |
591 | /* | |
592 | * XXX what we really should do is zap | |
593 | * uninitialized inode tables instead. | |
594 | */ | |
595 | request_fsck_afterwards(fs); | |
596 | break; | |
597 | } | |
598 | gd->bg_itable_unused = 0; | |
599 | gd->bg_flags = 0; | |
600 | gd->bg_checksum = 0; | |
601 | } | |
602 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
603 | } | |
604 | ||
771e8db9 AK |
605 | if (FEATURE_ON(E2P_FEATURE_RO_INCOMPAT, |
606 | EXT4_FEATURE_RO_COMPAT_QUOTA)) { | |
607 | /* | |
608 | * Set the Q_flag here and handle the quota options in the code | |
609 | * below. | |
610 | */ | |
611 | if (!Q_flag) { | |
612 | Q_flag = 1; | |
613 | /* Enable both user quota and group quota by default */ | |
614 | usrquota = QOPT_ENABLE; | |
615 | grpquota = QOPT_ENABLE; | |
616 | } | |
617 | sb->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA; | |
618 | } | |
619 | ||
620 | if (FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, | |
621 | EXT4_FEATURE_RO_COMPAT_QUOTA)) { | |
622 | /* | |
623 | * Set the Q_flag here and handle the quota options in the code | |
624 | * below. | |
625 | */ | |
626 | if (Q_flag) | |
627 | fputs(_("\nWarning: '^quota' option overrides '-Q'" | |
628 | "arguments.\n"), stderr); | |
629 | Q_flag = 1; | |
630 | /* Disable both user quota and group quota by default */ | |
631 | usrquota = QOPT_DISABLE; | |
632 | grpquota = QOPT_DISABLE; | |
633 | } | |
634 | ||
63985320 TT |
635 | if (sb->s_rev_level == EXT2_GOOD_OLD_REV && |
636 | (sb->s_feature_compat || sb->s_feature_ro_compat || | |
637 | sb->s_feature_incompat)) | |
638 | ext2fs_update_dynamic_rev(fs); | |
885bf6b8 TT |
639 | |
640 | if (FEATURE_CHANGED(E2P_FEATURE_RO_INCOMPAT, | |
641 | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) || | |
2be8fe43 TT |
642 | FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, |
643 | EXT4_FEATURE_RO_COMPAT_HUGE_FILE) || | |
885bf6b8 | 644 | FEATURE_CHANGED(E2P_FEATURE_INCOMPAT, |
037914e2 TT |
645 | EXT2_FEATURE_INCOMPAT_FILETYPE) || |
646 | FEATURE_CHANGED(E2P_FEATURE_COMPAT, | |
558df544 TT |
647 | EXT2_FEATURE_COMPAT_RESIZE_INODE) || |
648 | FEATURE_OFF(E2P_FEATURE_RO_INCOMPAT, | |
079ad63d TT |
649 | EXT2_FEATURE_RO_COMPAT_LARGE_FILE)) |
650 | request_fsck_afterwards(fs); | |
885bf6b8 TT |
651 | |
652 | if ((old_features[E2P_FEATURE_COMPAT] != sb->s_feature_compat) || | |
653 | (old_features[E2P_FEATURE_INCOMPAT] != sb->s_feature_incompat) || | |
654 | (old_features[E2P_FEATURE_RO_INCOMPAT] != sb->s_feature_ro_compat)) | |
2eb3b20e | 655 | ext2fs_mark_super_dirty(fs); |
0f5eba75 AD |
656 | |
657 | return 0; | |
63985320 TT |
658 | } |
659 | ||
660 | /* | |
661 | * Add a journal to the filesystem. | |
662 | */ | |
0f5eba75 | 663 | static int add_journal(ext2_filsys fs) |
63985320 TT |
664 | { |
665 | unsigned long journal_blocks; | |
666 | errcode_t retval; | |
16ed5b3a | 667 | ext2_filsys jfs; |
2a29f135 | 668 | io_manager io_ptr; |
63985320 TT |
669 | |
670 | if (fs->super->s_feature_compat & | |
671 | EXT3_FEATURE_COMPAT_HAS_JOURNAL) { | |
54434927 | 672 | fputs(_("The filesystem already has a journal.\n"), stderr); |
2d15576d | 673 | goto err; |
63985320 | 674 | } |
63985320 | 675 | if (journal_device) { |
d69f43f5 TT |
676 | if (!check_plausibility(journal_device, CHECK_BLOCK_DEV, |
677 | NULL)) | |
f83f4132 | 678 | proceed_question(-1); |
63985320 | 679 | check_mount(journal_device, 0, _("journal")); |
2a29f135 | 680 | #ifdef CONFIG_TESTIO_DEBUG |
f38cf3cb TT |
681 | if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_BLOCK")) { |
682 | io_ptr = test_io_manager; | |
683 | test_io_backing_manager = unix_io_manager; | |
684 | } else | |
2a29f135 | 685 | #endif |
f38cf3cb | 686 | io_ptr = unix_io_manager; |
16ed5b3a TT |
687 | retval = ext2fs_open(journal_device, EXT2_FLAG_RW| |
688 | EXT2_FLAG_JOURNAL_DEV_OK, 0, | |
2a29f135 | 689 | fs->blocksize, io_ptr, &jfs); |
16ed5b3a TT |
690 | if (retval) { |
691 | com_err(program_name, retval, | |
1d08d9bf | 692 | _("\n\twhile trying to open journal on %s\n"), |
16ed5b3a | 693 | journal_device); |
2d15576d | 694 | goto err; |
16ed5b3a | 695 | } |
63985320 TT |
696 | printf(_("Creating journal on device %s: "), |
697 | journal_device); | |
4055ef73 | 698 | fflush(stdout); |
2d15576d | 699 | |
16ed5b3a | 700 | retval = ext2fs_add_journal_device(fs, jfs); |
2d15576d | 701 | ext2fs_close(jfs); |
63985320 | 702 | if (retval) { |
ec43da2f TT |
703 | com_err(program_name, retval, |
704 | _("while adding filesystem to journal on %s"), | |
705 | journal_device); | |
2d15576d | 706 | goto err; |
63985320 | 707 | } |
54434927 | 708 | fputs(_("done\n"), stdout); |
63985320 | 709 | } else if (journal_size) { |
54434927 | 710 | fputs(_("Creating journal inode: "), stdout); |
63985320 | 711 | fflush(stdout); |
2537b6d0 TT |
712 | journal_blocks = figure_journal_size(journal_size, fs); |
713 | ||
b818205f TT |
714 | if (journal_location_string) |
715 | journal_location = | |
716 | parse_num_blocks2(journal_location_string, | |
717 | fs->super->s_log_block_size); | |
718 | retval = ext2fs_add_journal_inode2(fs, journal_blocks, | |
719 | journal_location, | |
720 | journal_flags); | |
63985320 | 721 | if (retval) { |
7141b54b | 722 | fprintf(stderr, "\n"); |
45ff69ff | 723 | com_err(program_name, retval, "%s", |
1d08d9bf | 724 | _("\n\twhile trying to create journal file")); |
0f5eba75 | 725 | return retval; |
1d08d9bf | 726 | } else |
54434927 | 727 | fputs(_("done\n"), stdout); |
63985320 TT |
728 | /* |
729 | * If the filesystem wasn't mounted, we need to force | |
730 | * the block group descriptors out. | |
731 | */ | |
732 | if ((mount_flags & EXT2_MF_MOUNTED) == 0) | |
733 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
734 | } | |
faa2dcda LC |
735 | print_check_message(fs->super->s_max_mnt_count, |
736 | fs->super->s_checkinterval); | |
0f5eba75 | 737 | return 0; |
2d15576d AD |
738 | |
739 | err: | |
45e338f5 | 740 | free(journal_device); |
0f5eba75 | 741 | return 1; |
63985320 TT |
742 | } |
743 | ||
577c773a | 744 | static void handle_quota_options(ext2_filsys fs) |
771e8db9 AK |
745 | { |
746 | quota_ctx_t qctx; | |
771e8db9 AK |
747 | ext2_ino_t qf_ino; |
748 | ||
749 | if (!usrquota && !grpquota) | |
750 | /* Nothing to do. */ | |
751 | return; | |
752 | ||
a86d55da | 753 | quota_init_context(&qctx, fs, -1); |
771e8db9 | 754 | |
d7c6f4e6 AK |
755 | if (usrquota == QOPT_ENABLE || grpquota == QOPT_ENABLE) |
756 | quota_compute_usage(qctx); | |
757 | ||
cdfaa759 N |
758 | if (usrquota == QOPT_ENABLE && !fs->super->s_usr_quota_inum) { |
759 | if ((qf_ino = quota_file_exists(fs, USRQUOTA, | |
50277515 AK |
760 | QFMT_VFS_V1)) > 0) |
761 | quota_update_limits(qctx, qf_ino, USRQUOTA); | |
762 | quota_write_inode(qctx, USRQUOTA); | |
cdfaa759 | 763 | } else if (usrquota == QOPT_DISABLE) { |
a86d55da | 764 | quota_remove_inode(fs, USRQUOTA); |
cdfaa759 | 765 | } |
771e8db9 | 766 | |
cdfaa759 N |
767 | if (grpquota == QOPT_ENABLE && !fs->super->s_grp_quota_inum) { |
768 | if ((qf_ino = quota_file_exists(fs, GRPQUOTA, | |
50277515 AK |
769 | QFMT_VFS_V1)) > 0) |
770 | quota_update_limits(qctx, qf_ino, GRPQUOTA); | |
771 | quota_write_inode(qctx, GRPQUOTA); | |
cdfaa759 | 772 | } else if (grpquota == QOPT_DISABLE) { |
a86d55da | 773 | quota_remove_inode(fs, GRPQUOTA); |
cdfaa759 | 774 | } |
771e8db9 | 775 | |
a86d55da | 776 | quota_release_context(&qctx); |
771e8db9 AK |
777 | |
778 | if ((usrquota == QOPT_ENABLE) || (grpquota == QOPT_ENABLE)) { | |
779 | fs->super->s_feature_ro_compat |= EXT4_FEATURE_RO_COMPAT_QUOTA; | |
780 | ext2fs_mark_super_dirty(fs); | |
89dd15db AK |
781 | } else if (!fs->super->s_usr_quota_inum && |
782 | !fs->super->s_grp_quota_inum) { | |
771e8db9 AK |
783 | fs->super->s_feature_ro_compat &= ~EXT4_FEATURE_RO_COMPAT_QUOTA; |
784 | ext2fs_mark_super_dirty(fs); | |
785 | } | |
786 | ||
787 | return; | |
788 | } | |
789 | ||
269da3b8 | 790 | #ifdef CONFIG_QUOTA |
577c773a | 791 | static void parse_quota_opts(const char *opts) |
771e8db9 | 792 | { |
e64e6761 | 793 | char *buf, *token, *next, *p; |
771e8db9 AK |
794 | int len; |
795 | ||
796 | len = strlen(opts); | |
797 | buf = malloc(len+1); | |
798 | if (!buf) { | |
799 | fputs(_("Couldn't allocate memory to parse quota " | |
800 | "options!\n"), stderr); | |
801 | exit(1); | |
802 | } | |
803 | strcpy(buf, opts); | |
804 | for (token = buf; token && *token; token = next) { | |
805 | p = strchr(token, ','); | |
806 | next = 0; | |
807 | if (p) { | |
808 | *p = 0; | |
809 | next = p+1; | |
810 | } | |
811 | ||
812 | if (strcmp(token, "usrquota") == 0) { | |
813 | usrquota = QOPT_ENABLE; | |
814 | } else if (strcmp(token, "^usrquota") == 0) { | |
815 | usrquota = QOPT_DISABLE; | |
816 | } else if (strcmp(token, "grpquota") == 0) { | |
817 | grpquota = QOPT_ENABLE; | |
818 | } else if (strcmp(token, "^grpquota") == 0) { | |
819 | grpquota = QOPT_DISABLE; | |
820 | } else { | |
821 | fputs(_("\nBad quota options specified.\n\n" | |
822 | "Following valid quota options are available " | |
823 | "(pass by separating with comma):\n" | |
824 | "\t[^]usrquota\n" | |
825 | "\t[^]grpquota\n" | |
826 | "\n\n"), stderr); | |
827 | free(buf); | |
828 | exit(1); | |
829 | } | |
830 | } | |
831 | free(buf); | |
832 | } | |
269da3b8 | 833 | #endif |
83238153 | 834 | |
c8c071a0 | 835 | static void parse_e2label_options(int argc, char ** argv) |
83238153 TT |
836 | { |
837 | if ((argc < 2) || (argc > 3)) { | |
54434927 | 838 | fputs(_("Usage: e2label device [newlabel]\n"), stderr); |
83238153 TT |
839 | exit(1); |
840 | } | |
2e8ca9a2 TT |
841 | io_options = strchr(argv[1], '?'); |
842 | if (io_options) | |
843 | *io_options++ = 0; | |
332f2c23 | 844 | device_name = blkid_get_devname(NULL, argv[1], NULL); |
817e49e3 | 845 | if (!device_name) { |
efc6f628 | 846 | com_err("e2label", 0, _("Unable to resolve '%s'"), |
817e49e3 TT |
847 | argv[1]); |
848 | exit(1); | |
849 | } | |
2be8fe43 | 850 | open_flag = EXT2_FLAG_JOURNAL_DEV_OK; |
83238153 | 851 | if (argc == 3) { |
0ddfd9a5 | 852 | open_flag |= EXT2_FLAG_RW; |
83238153 TT |
853 | L_flag = 1; |
854 | new_label = argv[2]; | |
efc6f628 | 855 | } else |
83238153 TT |
856 | print_label++; |
857 | } | |
858 | ||
d4de4aa9 TT |
859 | static time_t parse_time(char *str) |
860 | { | |
861 | struct tm ts; | |
862 | ||
863 | if (strcmp(str, "now") == 0) { | |
864 | return (time(0)); | |
865 | } | |
866 | memset(&ts, 0, sizeof(ts)); | |
bc7c14e0 | 867 | #ifdef HAVE_STRPTIME |
690e693c | 868 | strptime(str, "%Y%m%d%H%M%S", &ts); |
bc7c14e0 | 869 | #else |
690e693c | 870 | sscanf(str, "%4d%2d%2d%2d%2d%2d", &ts.tm_year, &ts.tm_mon, |
bc7c14e0 TT |
871 | &ts.tm_mday, &ts.tm_hour, &ts.tm_min, &ts.tm_sec); |
872 | ts.tm_year -= 1900; | |
873 | ts.tm_mon -= 1; | |
874 | if (ts.tm_year < 0 || ts.tm_mon < 0 || ts.tm_mon > 11 || | |
875 | ts.tm_mday < 0 || ts.tm_mday > 31 || ts.tm_hour > 23 || | |
876 | ts.tm_min > 59 || ts.tm_sec > 61) | |
877 | ts.tm_mday = 0; | |
878 | #endif | |
d4de4aa9 TT |
879 | if (ts.tm_mday == 0) { |
880 | com_err(program_name, 0, | |
881 | _("Couldn't parse date/time specifier: %s"), | |
882 | str); | |
883 | usage(); | |
884 | } | |
a2ff0f31 | 885 | ts.tm_isdst = -1; |
d4de4aa9 TT |
886 | return (mktime(&ts)); |
887 | } | |
83238153 | 888 | |
c8c071a0 | 889 | static void parse_tune2fs_options(int argc, char **argv) |
3839e657 | 890 | { |
519149fb | 891 | int c; |
ec43da2f TT |
892 | char *tmp; |
893 | struct group *gr; | |
894 | struct passwd *pw; | |
d82445e9 | 895 | char optstring[100] = "c:e:fg:i:jlm:o:r:s:u:C:E:I:J:L:M:O:T:U:"; |
3839e657 | 896 | |
d82445e9 ES |
897 | #ifdef CONFIG_QUOTA |
898 | strcat(optstring, "Q:"); | |
899 | #endif | |
2be8fe43 | 900 | open_flag = 0; |
0ddfd9a5 | 901 | |
0f8973fb | 902 | printf("tune2fs %s (%s)\n", E2FSPROGS_VERSION, E2FSPROGS_DATE); |
d82445e9 | 903 | while ((c = getopt(argc, argv, optstring)) != EOF) |
ec43da2f TT |
904 | switch (c) { |
905 | case 'c': | |
906 | max_mount_count = strtol(optarg, &tmp, 0); | |
907 | if (*tmp || max_mount_count > 16000) { | |
908 | com_err(program_name, 0, | |
909 | _("bad mounts count - %s"), | |
910 | optarg); | |
911 | usage(); | |
912 | } | |
913 | if (max_mount_count == 0) | |
914 | max_mount_count = -1; | |
915 | c_flag = 1; | |
916 | open_flag = EXT2_FLAG_RW; | |
917 | break; | |
918 | case 'C': | |
919 | mount_count = strtoul(optarg, &tmp, 0); | |
920 | if (*tmp || mount_count > 16000) { | |
921 | com_err(program_name, 0, | |
922 | _("bad mounts count - %s"), | |
923 | optarg); | |
924 | usage(); | |
925 | } | |
926 | C_flag = 1; | |
927 | open_flag = EXT2_FLAG_RW; | |
928 | break; | |
929 | case 'e': | |
930 | if (strcmp(optarg, "continue") == 0) | |
931 | errors = EXT2_ERRORS_CONTINUE; | |
932 | else if (strcmp(optarg, "remount-ro") == 0) | |
933 | errors = EXT2_ERRORS_RO; | |
934 | else if (strcmp(optarg, "panic") == 0) | |
935 | errors = EXT2_ERRORS_PANIC; | |
936 | else { | |
937 | com_err(program_name, 0, | |
938 | _("bad error behavior - %s"), | |
939 | optarg); | |
940 | usage(); | |
941 | } | |
942 | e_flag = 1; | |
943 | open_flag = EXT2_FLAG_RW; | |
944 | break; | |
945 | case 'E': | |
946 | extended_cmd = optarg; | |
947 | open_flag |= EXT2_FLAG_RW; | |
948 | break; | |
949 | case 'f': /* Force */ | |
5fe2bd60 | 950 | f_flag++; |
ec43da2f TT |
951 | break; |
952 | case 'g': | |
953 | resgid = strtoul(optarg, &tmp, 0); | |
954 | if (*tmp) { | |
955 | gr = getgrnam(optarg); | |
956 | if (gr == NULL) | |
957 | tmp = optarg; | |
818180cd | 958 | else { |
ec43da2f TT |
959 | resgid = gr->gr_gid; |
960 | *tmp = 0; | |
f3db3566 | 961 | } |
ec43da2f TT |
962 | } |
963 | if (*tmp) { | |
964 | com_err(program_name, 0, | |
965 | _("bad gid/group name - %s"), | |
966 | optarg); | |
967 | usage(); | |
968 | } | |
969 | g_flag = 1; | |
970 | open_flag = EXT2_FLAG_RW; | |
971 | break; | |
972 | case 'i': | |
973 | interval = strtoul(optarg, &tmp, 0); | |
974 | switch (*tmp) { | |
975 | case 's': | |
976 | tmp++; | |
f3db3566 | 977 | break; |
ec43da2f TT |
978 | case '\0': |
979 | case 'd': | |
980 | case 'D': /* days */ | |
981 | interval *= 86400; | |
982 | if (*tmp != '\0') | |
1e3472c5 | 983 | tmp++; |
1e3472c5 | 984 | break; |
3839e657 | 985 | case 'm': |
ec43da2f TT |
986 | case 'M': /* months! */ |
987 | interval *= 86400 * 30; | |
988 | tmp++; | |
3839e657 | 989 | break; |
ec43da2f TT |
990 | case 'w': |
991 | case 'W': /* weeks */ | |
992 | interval *= 86400 * 7; | |
993 | tmp++; | |
a0c3fd5e | 994 | break; |
ec43da2f TT |
995 | } |
996 | if (*tmp) { | |
997 | com_err(program_name, 0, | |
998 | _("bad interval - %s"), optarg); | |
999 | usage(); | |
1000 | } | |
1001 | i_flag = 1; | |
1002 | open_flag = EXT2_FLAG_RW; | |
1003 | break; | |
1004 | case 'j': | |
1005 | if (!journal_size) | |
1006 | journal_size = -1; | |
1007 | open_flag = EXT2_FLAG_RW; | |
1008 | break; | |
1009 | case 'J': | |
1010 | parse_journal_opts(optarg); | |
1011 | open_flag = EXT2_FLAG_RW; | |
1012 | break; | |
1013 | case 'l': | |
1014 | l_flag = 1; | |
1015 | break; | |
1016 | case 'L': | |
1017 | new_label = optarg; | |
1018 | L_flag = 1; | |
1019 | open_flag |= EXT2_FLAG_RW | | |
1020 | EXT2_FLAG_JOURNAL_DEV_OK; | |
1021 | break; | |
1022 | case 'm': | |
1023 | reserved_ratio = strtod(optarg, &tmp); | |
8d822455 TT |
1024 | if (*tmp || reserved_ratio > 50 || |
1025 | reserved_ratio < 0) { | |
ec43da2f TT |
1026 | com_err(program_name, 0, |
1027 | _("bad reserved block ratio - %s"), | |
1028 | optarg); | |
1029 | usage(); | |
1030 | } | |
1031 | m_flag = 1; | |
1032 | open_flag = EXT2_FLAG_RW; | |
1033 | break; | |
1034 | case 'M': | |
1035 | new_last_mounted = optarg; | |
1036 | M_flag = 1; | |
1037 | open_flag = EXT2_FLAG_RW; | |
1038 | break; | |
1039 | case 'o': | |
1040 | if (mntopts_cmd) { | |
45ff69ff | 1041 | com_err(program_name, 0, "%s", |
ec43da2f TT |
1042 | _("-o may only be specified once")); |
1043 | usage(); | |
1044 | } | |
1045 | mntopts_cmd = optarg; | |
1046 | open_flag = EXT2_FLAG_RW; | |
1047 | break; | |
ec43da2f TT |
1048 | case 'O': |
1049 | if (features_cmd) { | |
45ff69ff | 1050 | com_err(program_name, 0, "%s", |
ec43da2f TT |
1051 | _("-O may only be specified once")); |
1052 | usage(); | |
1053 | } | |
1054 | features_cmd = optarg; | |
1055 | open_flag = EXT2_FLAG_RW; | |
1056 | break; | |
d82445e9 | 1057 | #ifdef CONFIG_QUOTA |
771e8db9 AK |
1058 | case 'Q': |
1059 | Q_flag = 1; | |
1060 | parse_quota_opts(optarg); | |
1061 | open_flag = EXT2_FLAG_RW; | |
1062 | break; | |
d82445e9 | 1063 | #endif |
ec43da2f TT |
1064 | case 'r': |
1065 | reserved_blocks = strtoul(optarg, &tmp, 0); | |
1066 | if (*tmp) { | |
1067 | com_err(program_name, 0, | |
1068 | _("bad reserved blocks count - %s"), | |
1069 | optarg); | |
1070 | usage(); | |
1071 | } | |
1072 | r_flag = 1; | |
1073 | open_flag = EXT2_FLAG_RW; | |
1074 | break; | |
1075 | case 's': /* Deprecated */ | |
1076 | s_flag = atoi(optarg); | |
1077 | open_flag = EXT2_FLAG_RW; | |
1078 | break; | |
1079 | case 'T': | |
1080 | T_flag = 1; | |
1081 | last_check_time = parse_time(optarg); | |
1082 | open_flag = EXT2_FLAG_RW; | |
1083 | break; | |
1084 | case 'u': | |
1085 | resuid = strtoul(optarg, &tmp, 0); | |
818180cd | 1086 | if (*tmp) { |
ec43da2f | 1087 | pw = getpwnam(optarg); |
f3db3566 TT |
1088 | if (pw == NULL) |
1089 | tmp = optarg; | |
a418d3ad | 1090 | else { |
f3db3566 | 1091 | resuid = pw->pw_uid; |
a418d3ad TT |
1092 | *tmp = 0; |
1093 | } | |
f3db3566 | 1094 | } |
818180cd | 1095 | if (*tmp) { |
ec43da2f TT |
1096 | com_err(program_name, 0, |
1097 | _("bad uid/user name - %s"), | |
1098 | optarg); | |
818180cd | 1099 | usage(); |
f3db3566 TT |
1100 | } |
1101 | u_flag = 1; | |
1e3472c5 TT |
1102 | open_flag = EXT2_FLAG_RW; |
1103 | break; | |
ec43da2f TT |
1104 | case 'U': |
1105 | new_UUID = optarg; | |
1106 | U_flag = 1; | |
1107 | open_flag = EXT2_FLAG_RW | | |
1108 | EXT2_FLAG_JOURNAL_DEV_OK; | |
1109 | break; | |
1110 | case 'I': | |
1111 | new_inode_size = strtoul(optarg, &tmp, 0); | |
1112 | if (*tmp) { | |
1113 | com_err(program_name, 0, | |
1114 | _("bad inode size - %s"), | |
1115 | optarg); | |
818180cd | 1116 | usage(); |
ec43da2f TT |
1117 | } |
1118 | if (!((new_inode_size & | |
1119 | (new_inode_size - 1)) == 0)) { | |
1120 | com_err(program_name, 0, | |
1121 | _("Inode size must be a " | |
1122 | "power of two- %s"), | |
1123 | optarg); | |
1124 | usage(); | |
1125 | } | |
1126 | open_flag = EXT2_FLAG_RW; | |
1127 | I_flag = 1; | |
1128 | break; | |
1129 | default: | |
1130 | usage(); | |
3839e657 TT |
1131 | } |
1132 | if (optind < argc - 1 || optind == argc) | |
818180cd | 1133 | usage(); |
1e3472c5 TT |
1134 | if (!open_flag && !l_flag) |
1135 | usage(); | |
2e8ca9a2 TT |
1136 | io_options = strchr(argv[optind], '?'); |
1137 | if (io_options) | |
1138 | *io_options++ = 0; | |
332f2c23 | 1139 | device_name = blkid_get_devname(NULL, argv[optind], NULL); |
817e49e3 | 1140 | if (!device_name) { |
e3507739 | 1141 | com_err(program_name, 0, _("Unable to resolve '%s'"), |
817e49e3 TT |
1142 | argv[optind]); |
1143 | exit(1); | |
1144 | } | |
118d7dac TT |
1145 | } |
1146 | ||
14b596d4 | 1147 | #ifdef CONFIG_BUILD_FINDFS |
3e699064 | 1148 | void do_findfs(int argc, char **argv) |
118d7dac TT |
1149 | { |
1150 | char *dev; | |
83238153 | 1151 | |
118d7dac TT |
1152 | if ((argc != 2) || |
1153 | (strncmp(argv[1], "LABEL=", 6) && strncmp(argv[1], "UUID=", 5))) { | |
1154 | fprintf(stderr, "Usage: findfs LABEL=<label>|UUID=<uuid>\n"); | |
1155 | exit(2); | |
1156 | } | |
ed1b33e8 | 1157 | dev = blkid_get_devname(NULL, argv[1], NULL); |
118d7dac | 1158 | if (!dev) { |
efc6f628 | 1159 | com_err("findfs", 0, _("Unable to resolve '%s'"), |
118d7dac TT |
1160 | argv[1]); |
1161 | exit(1); | |
1162 | } | |
1163 | puts(dev); | |
1164 | exit(0); | |
1165 | } | |
14b596d4 | 1166 | #endif |
83238153 | 1167 | |
0f5eba75 | 1168 | static int parse_extended_opts(ext2_filsys fs, const char *opts) |
6cb27404 TT |
1169 | { |
1170 | char *buf, *token, *next, *p, *arg; | |
10ff68d4 | 1171 | int len, hash_alg; |
6cb27404 TT |
1172 | int r_usage = 0; |
1173 | ||
1174 | len = strlen(opts); | |
1175 | buf = malloc(len+1); | |
1176 | if (!buf) { | |
45ff69ff | 1177 | fprintf(stderr, "%s", |
6cb27404 | 1178 | _("Couldn't allocate memory to parse options!\n")); |
0f5eba75 | 1179 | return 1; |
6cb27404 TT |
1180 | } |
1181 | strcpy(buf, opts); | |
1182 | for (token = buf; token && *token; token = next) { | |
1183 | p = strchr(token, ','); | |
1184 | next = 0; | |
1185 | if (p) { | |
1186 | *p = 0; | |
1187 | next = p+1; | |
1188 | } | |
1189 | arg = strchr(token, '='); | |
1190 | if (arg) { | |
1191 | *arg = 0; | |
1192 | arg++; | |
1193 | } | |
0f5eba75 AD |
1194 | if (strcmp(token, "clear-mmp") == 0 || |
1195 | strcmp(token, "clear_mmp") == 0) { | |
1196 | clear_mmp = 1; | |
1197 | } else if (strcmp(token, "mmp_update_interval") == 0) { | |
577c773a | 1198 | unsigned long intv; |
0f5eba75 AD |
1199 | if (!arg) { |
1200 | r_usage++; | |
1201 | continue; | |
1202 | } | |
577c773a | 1203 | intv = strtoul(arg, &p, 0); |
0f5eba75 AD |
1204 | if (*p) { |
1205 | fprintf(stderr, | |
1206 | _("Invalid mmp_update_interval: %s\n"), | |
1207 | arg); | |
1208 | r_usage++; | |
1209 | continue; | |
1210 | } | |
577c773a TT |
1211 | if (intv == 0) { |
1212 | intv = EXT4_MMP_UPDATE_INTERVAL; | |
1213 | } else if (intv > EXT4_MMP_MAX_UPDATE_INTERVAL) { | |
0f5eba75 AD |
1214 | fprintf(stderr, |
1215 | _("mmp_update_interval too big: %lu\n"), | |
577c773a | 1216 | intv); |
0f5eba75 AD |
1217 | r_usage++; |
1218 | continue; | |
1219 | } | |
3fcd8fe8 TT |
1220 | printf(P_("Setting multiple mount protection update " |
1221 | "interval to %lu second\n", | |
1222 | "Setting multiple mount protection update " | |
577c773a TT |
1223 | "interval to %lu seconds\n", intv), |
1224 | intv); | |
1225 | fs->super->s_mmp_update_interval = intv; | |
0f5eba75 AD |
1226 | ext2fs_mark_super_dirty(fs); |
1227 | } else if (!strcmp(token, "test_fs")) { | |
6cb27404 TT |
1228 | fs->super->s_flags |= EXT2_FLAGS_TEST_FILESYS; |
1229 | printf("Setting test filesystem flag\n"); | |
1230 | ext2fs_mark_super_dirty(fs); | |
1231 | } else if (!strcmp(token, "^test_fs")) { | |
1232 | fs->super->s_flags &= ~EXT2_FLAGS_TEST_FILESYS; | |
1233 | printf("Clearing test filesystem flag\n"); | |
1234 | ext2fs_mark_super_dirty(fs); | |
0c17cb25 TT |
1235 | } else if (strcmp(token, "stride") == 0) { |
1236 | if (!arg) { | |
1237 | r_usage++; | |
1238 | continue; | |
1239 | } | |
1240 | stride = strtoul(arg, &p, 0); | |
035f32ab | 1241 | if (*p) { |
0c17cb25 | 1242 | fprintf(stderr, |
0f5eba75 | 1243 | _("Invalid RAID stride: %s\n"), |
0c17cb25 TT |
1244 | arg); |
1245 | r_usage++; | |
1246 | continue; | |
1247 | } | |
1248 | stride_set = 1; | |
1249 | } else if (strcmp(token, "stripe-width") == 0 || | |
1250 | strcmp(token, "stripe_width") == 0) { | |
1251 | if (!arg) { | |
1252 | r_usage++; | |
1253 | continue; | |
1254 | } | |
1255 | stripe_width = strtoul(arg, &p, 0); | |
035f32ab | 1256 | if (*p) { |
0c17cb25 TT |
1257 | fprintf(stderr, |
1258 | _("Invalid RAID stripe-width: %s\n"), | |
1259 | arg); | |
1260 | r_usage++; | |
1261 | continue; | |
1262 | } | |
1263 | stripe_width_set = 1; | |
10ff68d4 TT |
1264 | } else if (strcmp(token, "hash_alg") == 0 || |
1265 | strcmp(token, "hash-alg") == 0) { | |
1266 | if (!arg) { | |
1267 | r_usage++; | |
1268 | continue; | |
1269 | } | |
1270 | hash_alg = e2p_string2hash(arg); | |
1271 | if (hash_alg < 0) { | |
ec43da2f | 1272 | fprintf(stderr, |
10ff68d4 TT |
1273 | _("Invalid hash algorithm: %s\n"), |
1274 | arg); | |
1275 | r_usage++; | |
1276 | continue; | |
1277 | } | |
1278 | fs->super->s_def_hash_version = hash_alg; | |
1279 | printf(_("Setting default hash algorithm " | |
ec43da2f | 1280 | "to %s (%d)\n"), |
10ff68d4 TT |
1281 | arg, hash_alg); |
1282 | ext2fs_mark_super_dirty(fs); | |
9a976ac7 | 1283 | } else if (!strcmp(token, "mount_opts")) { |
9345f026 TT |
1284 | if (!arg) { |
1285 | r_usage++; | |
1286 | continue; | |
1287 | } | |
1288 | if (strlen(arg) >= sizeof(fs->super->s_mount_opts)) { | |
1289 | fprintf(stderr, | |
1290 | "Extended mount options too long\n"); | |
1291 | continue; | |
1292 | } | |
9a976ac7 | 1293 | ext_mount_opts = strdup(arg); |
efc6f628 | 1294 | } else |
6cb27404 TT |
1295 | r_usage++; |
1296 | } | |
1297 | if (r_usage) { | |
45ff69ff | 1298 | fprintf(stderr, "%s", _("\nBad options specified.\n\n" |
6cb27404 TT |
1299 | "Extended options are separated by commas, " |
1300 | "and may take an argument which\n" | |
1301 | "\tis set off by an equals ('=') sign.\n\n" | |
1302 | "Valid extended options are:\n" | |
0f5eba75 | 1303 | "\tclear_mmp\n" |
9a976ac7 TT |
1304 | "\thash_alg=<hash algorithm>\n" |
1305 | "\tmount_opts=<extended default mount options>\n" | |
0c17cb25 | 1306 | "\tstride=<RAID per-disk chunk size in blocks>\n" |
10ff68d4 | 1307 | "\tstripe_width=<RAID stride*data disks in blocks>\n" |
6cb27404 TT |
1308 | "\ttest_fs\n" |
1309 | "\t^test_fs\n")); | |
1310 | free(buf); | |
0f5eba75 | 1311 | return 1; |
6cb27404 TT |
1312 | } |
1313 | free(buf); | |
0f5eba75 AD |
1314 | |
1315 | return 0; | |
efc6f628 | 1316 | } |
6cb27404 | 1317 | |
31f1815f | 1318 | /* |
a9e5177b AK |
1319 | * Fill in the block bitmap bmap with the information regarding the |
1320 | * blocks to be moved | |
31f1815f TT |
1321 | */ |
1322 | static int get_move_bitmaps(ext2_filsys fs, int new_ino_blks_per_grp, | |
a9e5177b | 1323 | ext2fs_block_bitmap bmap) |
64d588cf AK |
1324 | { |
1325 | dgrp_t i; | |
154a5d75 AK |
1326 | int retval; |
1327 | ext2_badblocks_list bb_list = 0; | |
7117f8d6 VAH |
1328 | blk64_t j, needed_blocks = 0; |
1329 | blk64_t start_blk, end_blk; | |
64d588cf | 1330 | |
154a5d75 AK |
1331 | retval = ext2fs_read_bb_inode(fs, &bb_list); |
1332 | if (retval) | |
1333 | return retval; | |
1334 | ||
64d588cf | 1335 | for (i = 0; i < fs->group_desc_count; i++) { |
d7cca6b0 | 1336 | start_blk = ext2fs_inode_table_loc(fs, i) + |
64d588cf AK |
1337 | fs->inode_blocks_per_group; |
1338 | ||
d7cca6b0 | 1339 | end_blk = ext2fs_inode_table_loc(fs, i) + |
64d588cf AK |
1340 | new_ino_blks_per_grp; |
1341 | ||
1342 | for (j = start_blk; j < end_blk; j++) { | |
3c041a51 | 1343 | if (ext2fs_test_block_bitmap2(fs->block_map, j)) { |
154a5d75 AK |
1344 | /* |
1345 | * IF the block is a bad block we fail | |
64d588cf | 1346 | */ |
154a5d75 AK |
1347 | if (ext2fs_badblocks_list_test(bb_list, j)) { |
1348 | ext2fs_badblocks_list_free(bb_list); | |
1349 | return ENOSPC; | |
1350 | } | |
1351 | ||
3c041a51 | 1352 | ext2fs_mark_block_bitmap2(bmap, j); |
64d588cf AK |
1353 | } else { |
1354 | /* | |
1355 | * We are going to use this block for | |
1356 | * inode table. So mark them used. | |
1357 | */ | |
3c041a51 | 1358 | ext2fs_mark_block_bitmap2(fs->block_map, j); |
64d588cf AK |
1359 | } |
1360 | } | |
a9e5177b | 1361 | needed_blocks += end_blk - start_blk; |
64d588cf AK |
1362 | } |
1363 | ||
154a5d75 | 1364 | ext2fs_badblocks_list_free(bb_list); |
4efbac6f | 1365 | if (needed_blocks > ext2fs_free_blocks_count(fs->super)) |
64d588cf | 1366 | return ENOSPC; |
64d588cf AK |
1367 | |
1368 | return 0; | |
1369 | } | |
1370 | ||
4dbfd79d | 1371 | static int ext2fs_is_meta_block(ext2_filsys fs, blk64_t blk) |
91fac979 AK |
1372 | { |
1373 | dgrp_t group; | |
4dbfd79d | 1374 | group = ext2fs_group_of_blk2(fs, blk); |
d7cca6b0 | 1375 | if (ext2fs_block_bitmap_loc(fs, group) == blk) |
91fac979 | 1376 | return 1; |
d7cca6b0 | 1377 | if (ext2fs_inode_bitmap_loc(fs, group) == blk) |
91fac979 AK |
1378 | return 1; |
1379 | return 0; | |
1380 | } | |
1381 | ||
4dbfd79d | 1382 | static int ext2fs_is_block_in_group(ext2_filsys fs, dgrp_t group, blk64_t blk) |
91fac979 | 1383 | { |
4dbfd79d | 1384 | blk64_t start_blk, end_blk; |
91fac979 AK |
1385 | start_blk = fs->super->s_first_data_block + |
1386 | EXT2_BLOCKS_PER_GROUP(fs->super) * group; | |
1387 | /* | |
1388 | * We cannot get new block beyond end_blk for for the last block group | |
1389 | * so we can check with EXT2_BLOCKS_PER_GROUP even for last block group | |
1390 | */ | |
1391 | end_blk = start_blk + EXT2_BLOCKS_PER_GROUP(fs->super); | |
1392 | if (blk >= start_blk && blk <= end_blk) | |
1393 | return 1; | |
1394 | return 0; | |
1395 | } | |
1396 | ||
a9e5177b | 1397 | static int move_block(ext2_filsys fs, ext2fs_block_bitmap bmap) |
64d588cf | 1398 | { |
91fac979 | 1399 | |
64d588cf | 1400 | char *buf; |
cf5301d7 | 1401 | dgrp_t group = 0; |
64d588cf | 1402 | errcode_t retval; |
91fac979 | 1403 | int meta_data = 0; |
7117f8d6 | 1404 | blk64_t blk, new_blk, goal; |
64d588cf AK |
1405 | struct blk_move *bmv; |
1406 | ||
64d588cf AK |
1407 | retval = ext2fs_get_mem(fs->blocksize, &buf); |
1408 | if (retval) | |
1409 | return retval; | |
1410 | ||
27c6de45 | 1411 | for (new_blk = blk = fs->super->s_first_data_block; |
4efbac6f | 1412 | blk < ext2fs_blocks_count(fs->super); blk++) { |
3c041a51 | 1413 | if (!ext2fs_test_block_bitmap2(bmap, blk)) |
64d588cf AK |
1414 | continue; |
1415 | ||
91fac979 AK |
1416 | if (ext2fs_is_meta_block(fs, blk)) { |
1417 | /* | |
1418 | * If the block is mapping a fs meta data block | |
1419 | * like group desc/block bitmap/inode bitmap. We | |
1420 | * should find a block in the same group and fix | |
1421 | * the respective fs metadata pointers. Otherwise | |
1422 | * fail | |
1423 | */ | |
4dbfd79d | 1424 | group = ext2fs_group_of_blk2(fs, blk); |
b49f78fe | 1425 | goal = ext2fs_group_first_block2(fs, group); |
91fac979 AK |
1426 | meta_data = 1; |
1427 | ||
1428 | } else { | |
1429 | goal = new_blk; | |
1430 | } | |
7117f8d6 | 1431 | retval = ext2fs_new_block2(fs, goal, NULL, &new_blk); |
64d588cf AK |
1432 | if (retval) |
1433 | goto err_out; | |
1434 | ||
91fac979 AK |
1435 | /* new fs meta data block should be in the same group */ |
1436 | if (meta_data && !ext2fs_is_block_in_group(fs, group, new_blk)) { | |
1437 | retval = ENOSPC; | |
1438 | goto err_out; | |
1439 | } | |
1440 | ||
64d588cf | 1441 | /* Mark this block as allocated */ |
3c041a51 | 1442 | ext2fs_mark_block_bitmap2(fs->block_map, new_blk); |
64d588cf AK |
1443 | |
1444 | /* Add it to block move list */ | |
1445 | retval = ext2fs_get_mem(sizeof(struct blk_move), &bmv); | |
1446 | if (retval) | |
1447 | goto err_out; | |
1448 | ||
1449 | bmv->old_loc = blk; | |
1450 | bmv->new_loc = new_blk; | |
1451 | ||
1452 | list_add(&(bmv->list), &blk_move_list); | |
1453 | ||
24a117ab | 1454 | retval = io_channel_read_blk64(fs->io, blk, 1, buf); |
64d588cf AK |
1455 | if (retval) |
1456 | goto err_out; | |
1457 | ||
24a117ab | 1458 | retval = io_channel_write_blk64(fs->io, new_blk, 1, buf); |
64d588cf AK |
1459 | if (retval) |
1460 | goto err_out; | |
1461 | } | |
1462 | ||
1463 | err_out: | |
1464 | ext2fs_free_mem(&buf); | |
1465 | return retval; | |
1466 | } | |
1467 | ||
7117f8d6 | 1468 | static blk64_t translate_block(blk64_t blk) |
64d588cf AK |
1469 | { |
1470 | struct list_head *entry; | |
1471 | struct blk_move *bmv; | |
1472 | ||
1473 | list_for_each(entry, &blk_move_list) { | |
64d588cf AK |
1474 | bmv = list_entry(entry, struct blk_move, list); |
1475 | if (bmv->old_loc == blk) | |
1476 | return bmv->new_loc; | |
1477 | } | |
1478 | ||
1479 | return 0; | |
1480 | } | |
1481 | ||
721b367a | 1482 | static int process_block(ext2_filsys fs EXT2FS_ATTR((unused)), |
7117f8d6 | 1483 | blk64_t *block_nr, |
721b367a | 1484 | e2_blkcnt_t blockcnt EXT2FS_ATTR((unused)), |
7117f8d6 | 1485 | blk64_t ref_block EXT2FS_ATTR((unused)), |
64d588cf | 1486 | int ref_offset EXT2FS_ATTR((unused)), |
27c6de45 | 1487 | void *priv_data) |
64d588cf AK |
1488 | { |
1489 | int ret = 0; | |
7117f8d6 | 1490 | blk64_t new_blk; |
27c6de45 | 1491 | ext2fs_block_bitmap bmap = (ext2fs_block_bitmap) priv_data; |
64d588cf | 1492 | |
3c041a51 | 1493 | if (!ext2fs_test_block_bitmap2(bmap, *block_nr)) |
27c6de45 | 1494 | return 0; |
ec43da2f | 1495 | new_blk = translate_block(*block_nr); |
64d588cf AK |
1496 | if (new_blk) { |
1497 | *block_nr = new_blk; | |
1498 | /* | |
1499 | * This will force the ext2fs_write_inode in the iterator | |
1500 | */ | |
1501 | ret |= BLOCK_CHANGED; | |
1502 | } | |
1503 | ||
1504 | return ret; | |
1505 | } | |
1506 | ||
27c6de45 | 1507 | static int inode_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) |
64d588cf AK |
1508 | { |
1509 | errcode_t retval = 0; | |
1510 | ext2_ino_t ino; | |
7117f8d6 | 1511 | blk64_t blk; |
64d588cf AK |
1512 | char *block_buf = 0; |
1513 | struct ext2_inode inode; | |
1514 | ext2_inode_scan scan = NULL; | |
1515 | ||
1516 | retval = ext2fs_get_mem(fs->blocksize * 3, &block_buf); | |
1517 | if (retval) | |
1518 | return retval; | |
1519 | ||
1520 | retval = ext2fs_open_inode_scan(fs, 0, &scan); | |
1521 | if (retval) | |
1522 | goto err_out; | |
1523 | ||
1524 | while (1) { | |
64d588cf AK |
1525 | retval = ext2fs_get_next_inode(scan, &ino, &inode); |
1526 | if (retval) | |
1527 | goto err_out; | |
1528 | ||
1529 | if (!ino) | |
1530 | break; | |
1531 | ||
1532 | if (inode.i_links_count == 0) | |
1533 | continue; /* inode not in use */ | |
1534 | ||
1535 | /* FIXME!! | |
1536 | * If we end up modifying the journal inode | |
1537 | * the sb->s_jnl_blocks will differ. But a | |
1538 | * subsequent e2fsck fixes that. | |
1539 | * Do we need to fix this ?? | |
1540 | */ | |
1541 | ||
0c80c44b | 1542 | if (ext2fs_file_acl_block(fs, &inode) && |
a63745e8 | 1543 | ext2fs_test_block_bitmap2(bmap, |
0c80c44b TT |
1544 | ext2fs_file_acl_block(fs, &inode))) { |
1545 | blk = translate_block(ext2fs_file_acl_block(fs, | |
1546 | &inode)); | |
64d588cf AK |
1547 | if (!blk) |
1548 | continue; | |
1549 | ||
0c80c44b | 1550 | ext2fs_file_acl_block_set(fs, &inode, blk); |
64d588cf AK |
1551 | |
1552 | /* | |
1553 | * Write the inode to disk so that inode table | |
1554 | * resizing can work | |
1555 | */ | |
1556 | retval = ext2fs_write_inode(fs, ino, &inode); | |
1557 | if (retval) | |
1558 | goto err_out; | |
1559 | } | |
1560 | ||
0c80c44b | 1561 | if (!ext2fs_inode_has_valid_blocks2(fs, &inode)) |
64d588cf AK |
1562 | continue; |
1563 | ||
7117f8d6 | 1564 | retval = ext2fs_block_iterate3(fs, ino, 0, block_buf, |
27c6de45 | 1565 | process_block, bmap); |
64d588cf AK |
1566 | if (retval) |
1567 | goto err_out; | |
1568 | ||
1569 | } | |
1570 | ||
1571 | err_out: | |
1572 | ext2fs_free_mem(&block_buf); | |
1573 | ||
1574 | return retval; | |
31f1815f TT |
1575 | } |
1576 | ||
91fac979 AK |
1577 | /* |
1578 | * We need to scan for inode and block bitmaps that may need to be | |
1579 | * moved. This can take place if the filesystem was formatted for | |
1580 | * RAID arrays using the mke2fs's extended option "stride". | |
1581 | */ | |
1582 | static int group_desc_scan_and_fix(ext2_filsys fs, ext2fs_block_bitmap bmap) | |
1583 | { | |
1584 | dgrp_t i; | |
4dbfd79d | 1585 | blk64_t blk, new_blk; |
91fac979 AK |
1586 | |
1587 | for (i = 0; i < fs->group_desc_count; i++) { | |
d7cca6b0 | 1588 | blk = ext2fs_block_bitmap_loc(fs, i); |
3c041a51 | 1589 | if (ext2fs_test_block_bitmap2(bmap, blk)) { |
91fac979 AK |
1590 | new_blk = translate_block(blk); |
1591 | if (!new_blk) | |
1592 | continue; | |
d7cca6b0 | 1593 | ext2fs_block_bitmap_loc_set(fs, i, new_blk); |
91fac979 AK |
1594 | } |
1595 | ||
d7cca6b0 | 1596 | blk = ext2fs_inode_bitmap_loc(fs, i); |
3c041a51 | 1597 | if (ext2fs_test_block_bitmap2(bmap, blk)) { |
91fac979 AK |
1598 | new_blk = translate_block(blk); |
1599 | if (!new_blk) | |
1600 | continue; | |
d7cca6b0 | 1601 | ext2fs_inode_bitmap_loc_set(fs, i, new_blk); |
91fac979 AK |
1602 | } |
1603 | } | |
1604 | return 0; | |
1605 | } | |
1606 | ||
721b367a | 1607 | static int expand_inode_table(ext2_filsys fs, unsigned long new_ino_size) |
64d588cf AK |
1608 | { |
1609 | dgrp_t i; | |
7117f8d6 | 1610 | blk64_t blk; |
64d588cf | 1611 | errcode_t retval; |
721b367a TT |
1612 | int new_ino_blks_per_grp; |
1613 | unsigned int j; | |
64d588cf AK |
1614 | char *old_itable = NULL, *new_itable = NULL; |
1615 | char *tmp_old_itable = NULL, *tmp_new_itable = NULL; | |
721b367a | 1616 | unsigned long old_ino_size; |
64d588cf AK |
1617 | int old_itable_size, new_itable_size; |
1618 | ||
1619 | old_itable_size = fs->inode_blocks_per_group * fs->blocksize; | |
721b367a | 1620 | old_ino_size = EXT2_INODE_SIZE(fs->super); |
64d588cf AK |
1621 | |
1622 | new_ino_blks_per_grp = ext2fs_div_ceil( | |
1623 | EXT2_INODES_PER_GROUP(fs->super) * | |
721b367a | 1624 | new_ino_size, |
64d588cf AK |
1625 | fs->blocksize); |
1626 | ||
1627 | new_itable_size = new_ino_blks_per_grp * fs->blocksize; | |
1628 | ||
1629 | retval = ext2fs_get_mem(old_itable_size, &old_itable); | |
1630 | if (retval) | |
1631 | return retval; | |
1632 | ||
1633 | retval = ext2fs_get_mem(new_itable_size, &new_itable); | |
1634 | if (retval) | |
1635 | goto err_out; | |
1636 | ||
1637 | tmp_old_itable = old_itable; | |
1638 | tmp_new_itable = new_itable; | |
1639 | ||
1640 | for (i = 0; i < fs->group_desc_count; i++) { | |
d7cca6b0 | 1641 | blk = ext2fs_inode_table_loc(fs, i); |
24a117ab | 1642 | retval = io_channel_read_blk64(fs->io, blk, |
64d588cf AK |
1643 | fs->inode_blocks_per_group, old_itable); |
1644 | if (retval) | |
1645 | goto err_out; | |
1646 | ||
1647 | for (j = 0; j < EXT2_INODES_PER_GROUP(fs->super); j++) { | |
721b367a | 1648 | memcpy(new_itable, old_itable, old_ino_size); |
64d588cf | 1649 | |
721b367a TT |
1650 | memset(new_itable+old_ino_size, 0, |
1651 | new_ino_size - old_ino_size); | |
64d588cf | 1652 | |
721b367a TT |
1653 | new_itable += new_ino_size; |
1654 | old_itable += old_ino_size; | |
64d588cf AK |
1655 | } |
1656 | ||
1657 | /* reset the pointer */ | |
1658 | old_itable = tmp_old_itable; | |
1659 | new_itable = tmp_new_itable; | |
1660 | ||
24a117ab | 1661 | retval = io_channel_write_blk64(fs->io, blk, |
64d588cf AK |
1662 | new_ino_blks_per_grp, new_itable); |
1663 | if (retval) | |
1664 | goto err_out; | |
1665 | } | |
1666 | ||
1667 | /* Update the meta data */ | |
1668 | fs->inode_blocks_per_group = new_ino_blks_per_grp; | |
721b367a | 1669 | fs->super->s_inode_size = new_ino_size; |
64d588cf AK |
1670 | |
1671 | err_out: | |
1672 | if (old_itable) | |
1673 | ext2fs_free_mem(&old_itable); | |
1674 | ||
1675 | if (new_itable) | |
1676 | ext2fs_free_mem(&new_itable); | |
1677 | ||
1678 | return retval; | |
64d588cf AK |
1679 | } |
1680 | ||
1681 | static errcode_t ext2fs_calculate_summary_stats(ext2_filsys fs) | |
1682 | { | |
7117f8d6 | 1683 | blk64_t blk; |
64d588cf AK |
1684 | ext2_ino_t ino; |
1685 | unsigned int group = 0; | |
1686 | unsigned int count = 0; | |
1687 | int total_free = 0; | |
1688 | int group_free = 0; | |
1689 | ||
1690 | /* | |
1691 | * First calculate the block statistics | |
1692 | */ | |
1693 | for (blk = fs->super->s_first_data_block; | |
4efbac6f | 1694 | blk < ext2fs_blocks_count(fs->super); blk++) { |
3c041a51 | 1695 | if (!ext2fs_fast_test_block_bitmap2(fs->block_map, blk)) { |
64d588cf AK |
1696 | group_free++; |
1697 | total_free++; | |
1698 | } | |
1699 | count++; | |
1700 | if ((count == fs->super->s_blocks_per_group) || | |
4efbac6f | 1701 | (blk == ext2fs_blocks_count(fs->super)-1)) { |
d7cca6b0 VAH |
1702 | ext2fs_bg_free_blocks_count_set(fs, group++, |
1703 | group_free); | |
64d588cf AK |
1704 | count = 0; |
1705 | group_free = 0; | |
1706 | } | |
1707 | } | |
fe75afbf | 1708 | total_free = EXT2FS_C2B(fs, total_free); |
4efbac6f | 1709 | ext2fs_free_blocks_count_set(fs->super, total_free); |
64d588cf AK |
1710 | |
1711 | /* | |
1712 | * Next, calculate the inode statistics | |
1713 | */ | |
1714 | group_free = 0; | |
1715 | total_free = 0; | |
1716 | count = 0; | |
1717 | group = 0; | |
1718 | ||
1719 | /* Protect loop from wrap-around if s_inodes_count maxed */ | |
1720 | for (ino = 1; ino <= fs->super->s_inodes_count && ino > 0; ino++) { | |
3c041a51 | 1721 | if (!ext2fs_fast_test_inode_bitmap2(fs->inode_map, ino)) { |
64d588cf AK |
1722 | group_free++; |
1723 | total_free++; | |
1724 | } | |
1725 | count++; | |
1726 | if ((count == fs->super->s_inodes_per_group) || | |
1727 | (ino == fs->super->s_inodes_count)) { | |
d7cca6b0 VAH |
1728 | ext2fs_bg_free_inodes_count_set(fs, group++, |
1729 | group_free); | |
64d588cf AK |
1730 | count = 0; |
1731 | group_free = 0; | |
1732 | } | |
1733 | } | |
1734 | fs->super->s_free_inodes_count = total_free; | |
1735 | ext2fs_mark_super_dirty(fs); | |
1736 | return 0; | |
1737 | } | |
1738 | ||
1739 | #define list_for_each_safe(pos, pnext, head) \ | |
1740 | for (pos = (head)->next, pnext = pos->next; pos != (head); \ | |
1741 | pos = pnext, pnext = pos->next) | |
1742 | ||
721b367a | 1743 | static void free_blk_move_list(void) |
64d588cf AK |
1744 | { |
1745 | struct list_head *entry, *tmp; | |
1746 | struct blk_move *bmv; | |
1747 | ||
1748 | list_for_each_safe(entry, tmp, &blk_move_list) { | |
64d588cf AK |
1749 | bmv = list_entry(entry, struct blk_move, list); |
1750 | list_del(entry); | |
1751 | ext2fs_free_mem(&bmv); | |
1752 | } | |
31f1815f | 1753 | return; |
64d588cf | 1754 | } |
721b367a TT |
1755 | |
1756 | static int resize_inode(ext2_filsys fs, unsigned long new_size) | |
64d588cf AK |
1757 | { |
1758 | errcode_t retval; | |
1759 | int new_ino_blks_per_grp; | |
a9e5177b | 1760 | ext2fs_block_bitmap bmap; |
64d588cf | 1761 | |
c3ecabe6 ES |
1762 | retval = ext2fs_read_inode_bitmap(fs); |
1763 | if (retval) { | |
1764 | fputs(_("Failed to read inode bitmap\n"), stderr); | |
1765 | return retval; | |
1766 | } | |
1767 | retval = ext2fs_read_block_bitmap(fs); | |
1768 | if (retval) { | |
3fcd8fe8 | 1769 | fputs(_("Failed to read block bitmap\n"), stderr); |
c3ecabe6 ES |
1770 | return retval; |
1771 | } | |
64d588cf AK |
1772 | INIT_LIST_HEAD(&blk_move_list); |
1773 | ||
1774 | ||
1775 | new_ino_blks_per_grp = ext2fs_div_ceil( | |
1776 | EXT2_INODES_PER_GROUP(fs->super)* | |
721b367a | 1777 | new_size, |
64d588cf AK |
1778 | fs->blocksize); |
1779 | ||
1780 | /* We may change the file system. | |
1781 | * Mark the file system as invalid so that | |
1782 | * the user is prompted to run fsck. | |
1783 | */ | |
1784 | fs->super->s_state &= ~EXT2_VALID_FS; | |
1785 | ||
1786 | retval = ext2fs_allocate_block_bitmap(fs, _("blocks to be moved"), | |
1787 | &bmap); | |
a9e5177b AK |
1788 | if (retval) { |
1789 | fputs(_("Failed to allocate block bitmap when " | |
1790 | "increasing inode size\n"), stderr); | |
31f1815f | 1791 | return retval; |
a9e5177b AK |
1792 | } |
1793 | retval = get_move_bitmaps(fs, new_ino_blks_per_grp, bmap); | |
1794 | if (retval) { | |
1795 | fputs(_("Not enough space to increase inode size \n"), stderr); | |
64d588cf | 1796 | goto err_out; |
a9e5177b AK |
1797 | } |
1798 | retval = move_block(fs, bmap); | |
1799 | if (retval) { | |
1800 | fputs(_("Failed to relocate blocks during inode resize \n"), | |
1801 | stderr); | |
64d588cf | 1802 | goto err_out; |
a9e5177b | 1803 | } |
27c6de45 | 1804 | retval = inode_scan_and_fix(fs, bmap); |
64d588cf | 1805 | if (retval) |
a9e5177b | 1806 | goto err_out_undo; |
31f1815f | 1807 | |
91fac979 AK |
1808 | retval = group_desc_scan_and_fix(fs, bmap); |
1809 | if (retval) | |
1810 | goto err_out_undo; | |
1811 | ||
721b367a | 1812 | retval = expand_inode_table(fs, new_size); |
64d588cf | 1813 | if (retval) |
a9e5177b | 1814 | goto err_out_undo; |
64d588cf AK |
1815 | |
1816 | ext2fs_calculate_summary_stats(fs); | |
1817 | ||
1818 | fs->super->s_state |= EXT2_VALID_FS; | |
1819 | /* mark super block and block bitmap as dirty */ | |
1820 | ext2fs_mark_super_dirty(fs); | |
1821 | ext2fs_mark_bb_dirty(fs); | |
1822 | ||
1823 | err_out: | |
1824 | free_blk_move_list(); | |
1825 | ext2fs_free_block_bitmap(bmap); | |
1826 | ||
1827 | return retval; | |
a9e5177b AK |
1828 | |
1829 | err_out_undo: | |
1830 | free_blk_move_list(); | |
1831 | ext2fs_free_block_bitmap(bmap); | |
1832 | fputs(_("Error in resizing the inode size.\n" | |
1833 | "Run e2undo to undo the " | |
1834 | "file system changes. \n"), stderr); | |
1835 | ||
1836 | return retval; | |
64d588cf AK |
1837 | } |
1838 | ||
1839 | static int tune2fs_setup_tdb(const char *name, io_manager *io_ptr) | |
1840 | { | |
1841 | errcode_t retval = 0; | |
721b367a | 1842 | const char *tdb_dir; |
f203bbdb | 1843 | char *tdb_file; |
721b367a | 1844 | char *dev_name, *tmp_name; |
64d588cf AK |
1845 | |
1846 | #if 0 /* FIXME!! */ | |
1847 | /* | |
1848 | * Configuration via a conf file would be | |
1849 | * nice | |
1850 | */ | |
1851 | profile_get_string(profile, "scratch_files", | |
1852 | "directory", 0, 0, | |
1853 | &tdb_dir); | |
1854 | #endif | |
1855 | tmp_name = strdup(name); | |
f203bbdb TT |
1856 | if (!tmp_name) { |
1857 | alloc_fn_fail: | |
45ff69ff | 1858 | com_err(program_name, ENOMEM, "%s", |
f203bbdb TT |
1859 | _("Couldn't allocate memory for tdb filename\n")); |
1860 | return ENOMEM; | |
1861 | } | |
721b367a | 1862 | dev_name = basename(tmp_name); |
64d588cf AK |
1863 | |
1864 | tdb_dir = getenv("E2FSPROGS_UNDO_DIR"); | |
1865 | if (!tdb_dir) | |
ec43da2f | 1866 | tdb_dir = "/var/lib/e2fsprogs"; |
64d588cf AK |
1867 | |
1868 | if (!strcmp(tdb_dir, "none") || (tdb_dir[0] == 0) || | |
1869 | access(tdb_dir, W_OK)) | |
1870 | return 0; | |
1871 | ||
f203bbdb TT |
1872 | tdb_file = malloc(strlen(tdb_dir) + 9 + strlen(dev_name) + 7 + 1); |
1873 | if (!tdb_file) | |
1874 | goto alloc_fn_fail; | |
721b367a | 1875 | sprintf(tdb_file, "%s/tune2fs-%s.e2undo", tdb_dir, dev_name); |
64d588cf | 1876 | |
250bf9a7 TT |
1877 | if ((unlink(tdb_file) < 0) && (errno != ENOENT)) { |
1878 | retval = errno; | |
1879 | com_err(program_name, retval, | |
1880 | _("while trying to delete %s"), tdb_file); | |
1881 | free(tdb_file); | |
1882 | return retval; | |
64d588cf AK |
1883 | } |
1884 | ||
1885 | set_undo_io_backing_manager(*io_ptr); | |
1886 | *io_ptr = undo_io_manager; | |
1887 | set_undo_io_backup_file(tdb_file); | |
5bf81bae | 1888 | printf(_("To undo the tune2fs operation please run " |
577b5c43 | 1889 | "the command\n e2undo %s %s\n\n"), |
64d588cf | 1890 | tdb_file, name); |
f203bbdb | 1891 | free(tdb_file); |
64d588cf AK |
1892 | free(tmp_name); |
1893 | return retval; | |
1894 | } | |
83238153 | 1895 | |
ec43da2f | 1896 | int main(int argc, char **argv) |
83238153 TT |
1897 | { |
1898 | errcode_t retval; | |
1899 | ext2_filsys fs; | |
1900 | struct ext2_super_block *sb; | |
d887f88d | 1901 | io_manager io_ptr, io_ptr_orig = NULL; |
0f5eba75 | 1902 | int rc = 0; |
83238153 TT |
1903 | |
1904 | #ifdef ENABLE_NLS | |
1905 | setlocale(LC_MESSAGES, ""); | |
14308a53 | 1906 | setlocale(LC_CTYPE, ""); |
83238153 TT |
1907 | bindtextdomain(NLS_CAT_NAME, LOCALEDIR); |
1908 | textdomain(NLS_CAT_NAME); | |
9d4507c5 | 1909 | set_com_err_gettext(gettext); |
83238153 TT |
1910 | #endif |
1911 | if (argc && *argv) | |
1912 | program_name = *argv; | |
a6d8302b | 1913 | add_error_table(&et_ext2_error_table); |
83238153 | 1914 | |
14b596d4 | 1915 | #ifdef CONFIG_BUILD_FINDFS |
118d7dac TT |
1916 | if (strcmp(get_progname(argv[0]), "findfs") == 0) |
1917 | do_findfs(argc, argv); | |
14b596d4 | 1918 | #endif |
83238153 TT |
1919 | if (strcmp(get_progname(argv[0]), "e2label") == 0) |
1920 | parse_e2label_options(argc, argv); | |
1921 | else | |
1922 | parse_tune2fs_options(argc, argv); | |
efc6f628 | 1923 | |
2a29f135 | 1924 | #ifdef CONFIG_TESTIO_DEBUG |
f38cf3cb TT |
1925 | if (getenv("TEST_IO_FLAGS") || getenv("TEST_IO_DEBUG")) { |
1926 | io_ptr = test_io_manager; | |
1927 | test_io_backing_manager = unix_io_manager; | |
1928 | } else | |
2a29f135 | 1929 | #endif |
f38cf3cb | 1930 | io_ptr = unix_io_manager; |
64d588cf | 1931 | |
d887f88d | 1932 | retry_open: |
0f5eba75 AD |
1933 | if ((open_flag & EXT2_FLAG_RW) == 0 || f_flag) |
1934 | open_flag |= EXT2_FLAG_SKIP_MMP; | |
1935 | ||
1936 | open_flag |= EXT2_FLAG_64BITS; | |
1937 | ||
1938 | /* keep the filesystem struct around to dump MMP data */ | |
1939 | open_flag |= EXT2_FLAG_NOFREE_ON_ERROR; | |
1940 | ||
efc6f628 | 1941 | retval = ext2fs_open2(device_name, io_options, open_flag, |
2e8ca9a2 | 1942 | 0, 0, io_ptr, &fs); |
ec43da2f | 1943 | if (retval) { |
0f5eba75 AD |
1944 | com_err(program_name, retval, |
1945 | _("while trying to open %s"), | |
ec43da2f | 1946 | device_name); |
0f5eba75 AD |
1947 | if (retval == EXT2_ET_MMP_FSCK_ON || |
1948 | retval == EXT2_ET_MMP_UNKNOWN_SEQ) | |
1949 | dump_mmp_msg(fs->mmp_buf, | |
1950 | _("If you are sure the filesystem " | |
1951 | "is not in use on any node, run:\n" | |
1952 | "'tune2fs -f -E clear_mmp {device}'\n")); | |
1953 | else if (retval == EXT2_ET_MMP_FAILED) | |
1954 | dump_mmp_msg(fs->mmp_buf, NULL); | |
1955 | else if (retval == EXT2_ET_MMP_MAGIC_INVALID) | |
1956 | fprintf(stderr, | |
1957 | _("MMP block magic is bad. Try to fix it by " | |
1958 | "running:\n'e2fsck -f %s'\n"), device_name); | |
1959 | else if (retval != EXT2_ET_MMP_FAILED) | |
45ff69ff | 1960 | fprintf(stderr, "%s", |
0f5eba75 AD |
1961 | _("Couldn't find valid filesystem superblock.\n")); |
1962 | ||
1963 | ext2fs_free(fs); | |
3839e657 TT |
1964 | exit(1); |
1965 | } | |
24dea554 | 1966 | fs->default_bitmap_type = EXT2FS_BMAP64_RBTREE; |
d887f88d TT |
1967 | |
1968 | if (I_flag && !io_ptr_orig) { | |
1969 | /* | |
1970 | * Check the inode size is right so we can issue an | |
1971 | * error message and bail before setting up the tdb | |
1972 | * file. | |
1973 | */ | |
1974 | if (new_inode_size == EXT2_INODE_SIZE(fs->super)) { | |
9266fc7a | 1975 | fprintf(stderr, _("The inode size is already %lu\n"), |
d887f88d | 1976 | new_inode_size); |
0f5eba75 AD |
1977 | rc = 1; |
1978 | goto closefs; | |
d887f88d TT |
1979 | } |
1980 | if (new_inode_size < EXT2_INODE_SIZE(fs->super)) { | |
45ff69ff AD |
1981 | fprintf(stderr, "%s", |
1982 | _("Shrinking inode size is not supported\n")); | |
0f5eba75 AD |
1983 | rc = 1; |
1984 | goto closefs; | |
fab0d286 AF |
1985 | } |
1986 | if (new_inode_size > fs->blocksize) { | |
1987 | fprintf(stderr, _("Invalid inode size %lu (max %d)\n"), | |
1988 | new_inode_size, fs->blocksize); | |
1989 | rc = 1; | |
1990 | goto closefs; | |
d887f88d TT |
1991 | } |
1992 | ||
1993 | /* | |
1994 | * If inode resize is requested use the | |
1995 | * Undo I/O manager | |
1996 | */ | |
1997 | io_ptr_orig = io_ptr; | |
1998 | retval = tune2fs_setup_tdb(device_name, &io_ptr); | |
0f5eba75 AD |
1999 | if (retval) { |
2000 | rc = 1; | |
2001 | goto closefs; | |
2002 | } | |
d887f88d TT |
2003 | if (io_ptr != io_ptr_orig) { |
2004 | ext2fs_close(fs); | |
2005 | goto retry_open; | |
2006 | } | |
2007 | } | |
2008 | ||
83238153 | 2009 | sb = fs->super; |
058ad1c7 | 2010 | fs->flags &= ~EXT2_FLAG_MASTER_SB_ONLY; |
2be8fe43 | 2011 | |
83238153 TT |
2012 | if (print_label) { |
2013 | /* For e2label emulation */ | |
c8c071a0 TT |
2014 | printf("%.*s\n", (int) sizeof(sb->s_volume_name), |
2015 | sb->s_volume_name); | |
a6d8302b | 2016 | remove_error_table(&et_ext2_error_table); |
0f5eba75 | 2017 | goto closefs; |
83238153 | 2018 | } |
2be8fe43 | 2019 | |
d06863f0 TT |
2020 | retval = ext2fs_check_if_mounted(device_name, &mount_flags); |
2021 | if (retval) { | |
2022 | com_err("ext2fs_check_if_mount", retval, | |
2023 | _("while determining whether %s is mounted."), | |
2024 | device_name); | |
0f5eba75 AD |
2025 | rc = 1; |
2026 | goto closefs; | |
b21e38a0 | 2027 | } |
63985320 TT |
2028 | /* Normally we only need to write out the superblock */ |
2029 | fs->flags |= EXT2_FLAG_SUPER_ONLY; | |
3839e657 | 2030 | |
1e3472c5 | 2031 | if (c_flag) { |
b21e38a0 | 2032 | sb->s_max_mnt_count = max_mount_count; |
3839e657 | 2033 | ext2fs_mark_super_dirty(fs); |
ec43da2f TT |
2034 | printf(_("Setting maximal mount count to %d\n"), |
2035 | max_mount_count); | |
3839e657 | 2036 | } |
1e3472c5 | 2037 | if (C_flag) { |
b21e38a0 | 2038 | sb->s_mnt_count = mount_count; |
1e3472c5 | 2039 | ext2fs_mark_super_dirty(fs); |
ec43da2f | 2040 | printf(_("Setting current mount count to %d\n"), mount_count); |
1e3472c5 TT |
2041 | } |
2042 | if (e_flag) { | |
b21e38a0 | 2043 | sb->s_errors = errors; |
3839e657 | 2044 | ext2fs_mark_super_dirty(fs); |
ec43da2f | 2045 | printf(_("Setting error behavior to %d\n"), errors); |
3839e657 | 2046 | } |
b21e38a0 TT |
2047 | if (g_flag) { |
2048 | sb->s_def_resgid = resgid; | |
f3db3566 | 2049 | ext2fs_mark_super_dirty(fs); |
ec43da2f | 2050 | printf(_("Setting reserved blocks gid to %lu\n"), resgid); |
f3db3566 | 2051 | } |
818180cd | 2052 | if (i_flag) { |
d8f401b1 | 2053 | if ((unsigned long long)interval >= (1ULL << 32)) { |
2972b163 KM |
2054 | com_err(program_name, 0, |
2055 | _("interval between checks is too big (%lu)"), | |
2056 | interval); | |
0f5eba75 AD |
2057 | rc = 1; |
2058 | goto closefs; | |
2972b163 | 2059 | } |
b21e38a0 | 2060 | sb->s_checkinterval = interval; |
3839e657 | 2061 | ext2fs_mark_super_dirty(fs); |
ec43da2f TT |
2062 | printf(_("Setting interval between checks to %lu seconds\n"), |
2063 | interval); | |
3839e657 | 2064 | } |
818180cd | 2065 | if (m_flag) { |
4efbac6f VAH |
2066 | ext2fs_r_blocks_count_set(sb, reserved_ratio * |
2067 | ext2fs_blocks_count(sb) / 100.0); | |
3839e657 | 2068 | ext2fs_mark_super_dirty(fs); |
4efbac6f VAH |
2069 | printf (_("Setting reserved blocks percentage to %g%% (%llu blocks)\n"), |
2070 | reserved_ratio, ext2fs_r_blocks_count(sb)); | |
3839e657 | 2071 | } |
818180cd | 2072 | if (r_flag) { |
3977a4ff | 2073 | if (reserved_blocks > ext2fs_blocks_count(sb)/2) { |
ec43da2f | 2074 | com_err(program_name, 0, |
4efbac6f | 2075 | _("reserved blocks count is too big (%llu)"), |
ec43da2f | 2076 | reserved_blocks); |
0f5eba75 AD |
2077 | rc = 1; |
2078 | goto closefs; | |
f3db3566 | 2079 | } |
4efbac6f | 2080 | ext2fs_r_blocks_count_set(sb, reserved_blocks); |
f3db3566 | 2081 | ext2fs_mark_super_dirty(fs); |
4efbac6f | 2082 | printf(_("Setting reserved blocks count to %llu\n"), |
ec43da2f | 2083 | reserved_blocks); |
f3db3566 | 2084 | } |
521e3685 | 2085 | if (s_flag == 1) { |
521e3685 | 2086 | if (sb->s_feature_ro_compat & |
9e7294d3 | 2087 | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) { |
54434927 TT |
2088 | fputs(_("\nThe filesystem already has sparse " |
2089 | "superblocks.\n"), stderr); | |
9e7294d3 AF |
2090 | } else if (sb->s_feature_incompat & |
2091 | EXT2_FEATURE_INCOMPAT_META_BG) { | |
2092 | fputs(_("\nSetting the sparse superblock flag not " | |
2093 | "supported\nfor filesystems with " | |
2094 | "the meta_bg feature enabled.\n"), | |
2095 | stderr); | |
2096 | rc = 1; | |
2097 | goto closefs; | |
2098 | } else { | |
521e3685 TT |
2099 | sb->s_feature_ro_compat |= |
2100 | EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER; | |
b21e38a0 | 2101 | sb->s_state &= ~EXT2_VALID_FS; |
521e3685 | 2102 | ext2fs_mark_super_dirty(fs); |
d9c56d3c | 2103 | printf(_("\nSparse superblock flag set. %s"), |
a4fa100c | 2104 | _(please_fsck)); |
521e3685 | 2105 | } |
521e3685 TT |
2106 | } |
2107 | if (s_flag == 0) { | |
9e7294d3 | 2108 | fputs(_("\nClearing the sparse superblock flag not supported.\n"), |
ff662d5d | 2109 | stderr); |
0f5eba75 AD |
2110 | rc = 1; |
2111 | goto closefs; | |
521e3685 | 2112 | } |
d4de4aa9 TT |
2113 | if (T_flag) { |
2114 | sb->s_lastcheck = last_check_time; | |
2115 | ext2fs_mark_super_dirty(fs); | |
2116 | printf(_("Setting time filesystem last checked to %s\n"), | |
2117 | ctime(&last_check_time)); | |
2118 | } | |
b21e38a0 TT |
2119 | if (u_flag) { |
2120 | sb->s_def_resuid = resuid; | |
f3db3566 | 2121 | ext2fs_mark_super_dirty(fs); |
ec43da2f | 2122 | printf(_("Setting reserved blocks uid to %lu\n"), resuid); |
f3db3566 | 2123 | } |
1e3472c5 | 2124 | if (L_flag) { |
a789d840 | 2125 | if (strlen(new_label) > sizeof(sb->s_volume_name)) |
efc6f628 | 2126 | fputs(_("Warning: label too long, truncating.\n"), |
54434927 | 2127 | stderr); |
1e3472c5 TT |
2128 | memset(sb->s_volume_name, 0, sizeof(sb->s_volume_name)); |
2129 | strncpy(sb->s_volume_name, new_label, | |
2130 | sizeof(sb->s_volume_name)); | |
2131 | ext2fs_mark_super_dirty(fs); | |
2132 | } | |
2133 | if (M_flag) { | |
2134 | memset(sb->s_last_mounted, 0, sizeof(sb->s_last_mounted)); | |
2135 | strncpy(sb->s_last_mounted, new_last_mounted, | |
2136 | sizeof(sb->s_last_mounted)); | |
2137 | ext2fs_mark_super_dirty(fs); | |
2138 | } | |
0f5eba75 AD |
2139 | if (mntopts_cmd) { |
2140 | rc = update_mntopts(fs, mntopts_cmd); | |
2141 | if (rc) | |
2142 | goto closefs; | |
2143 | } | |
2144 | if (features_cmd) { | |
2145 | rc = update_feature_set(fs, features_cmd); | |
2146 | if (rc) | |
2147 | goto closefs; | |
2148 | } | |
2149 | if (extended_cmd) { | |
2150 | rc = parse_extended_opts(fs, extended_cmd); | |
2151 | if (rc) | |
2152 | goto closefs; | |
2153 | if (clear_mmp && !f_flag) { | |
2154 | fputs(_("Error in using clear_mmp. " | |
2155 | "It must be used with -f\n"), | |
2156 | stderr); | |
2157 | goto closefs; | |
2158 | } | |
2159 | } | |
2160 | if (clear_mmp) { | |
2161 | rc = ext2fs_mmp_clear(fs); | |
2162 | goto closefs; | |
2163 | } | |
2164 | if (journal_size || journal_device) { | |
2165 | rc = add_journal(fs); | |
2166 | if (rc) | |
2167 | goto closefs; | |
2168 | } | |
efc6f628 | 2169 | |
771e8db9 AK |
2170 | if (Q_flag) { |
2171 | if (mount_flags & EXT2_MF_MOUNTED) { | |
2172 | fputs(_("The quota feature may only be changed when " | |
2173 | "the filesystem is unmounted.\n"), stderr); | |
0f5eba75 AD |
2174 | rc = 1; |
2175 | goto closefs; | |
771e8db9 AK |
2176 | } |
2177 | handle_quota_options(fs); | |
2178 | } | |
2179 | ||
1e3472c5 | 2180 | if (U_flag) { |
9d4a4dc2 TT |
2181 | int set_csum = 0; |
2182 | dgrp_t i; | |
2183 | ||
2184 | if (sb->s_feature_ro_compat & | |
2185 | EXT4_FEATURE_RO_COMPAT_GDT_CSUM) { | |
66457fcb DW |
2186 | /* |
2187 | * Changing the UUID requires rewriting all metadata, | |
2188 | * which can race with a mounted fs. Don't allow that. | |
2189 | */ | |
2190 | if (mount_flags & EXT2_MF_MOUNTED) { | |
2191 | fputs(_("The UUID may only be " | |
2192 | "changed when the filesystem is " | |
2193 | "unmounted.\n"), stderr); | |
2194 | exit(1); | |
2195 | } | |
2196 | if (check_fsck_needed(fs)) | |
2197 | exit(1); | |
2198 | ||
ec43da2f | 2199 | /* |
9d4a4dc2 TT |
2200 | * Determine if the block group checksums are |
2201 | * correct so we know whether or not to set | |
2202 | * them later on. | |
2203 | */ | |
2204 | for (i = 0; i < fs->group_desc_count; i++) | |
2205 | if (!ext2fs_group_desc_csum_verify(fs, i)) | |
2206 | break; | |
2207 | if (i >= fs->group_desc_count) | |
2208 | set_csum = 1; | |
2209 | } | |
4d0f2283 TT |
2210 | if ((strcasecmp(new_UUID, "null") == 0) || |
2211 | (strcasecmp(new_UUID, "clear") == 0)) { | |
1e3472c5 | 2212 | uuid_clear(sb->s_uuid); |
63985320 TT |
2213 | } else if (strcasecmp(new_UUID, "time") == 0) { |
2214 | uuid_generate_time(sb->s_uuid); | |
1e3472c5 TT |
2215 | } else if (strcasecmp(new_UUID, "random") == 0) { |
2216 | uuid_generate(sb->s_uuid); | |
2217 | } else if (uuid_parse(new_UUID, sb->s_uuid)) { | |
45ff69ff AD |
2218 | com_err(program_name, 0, "%s", |
2219 | _("Invalid UUID format\n")); | |
0f5eba75 AD |
2220 | rc = 1; |
2221 | goto closefs; | |
1e3472c5 | 2222 | } |
9d4a4dc2 TT |
2223 | if (set_csum) { |
2224 | for (i = 0; i < fs->group_desc_count; i++) | |
2225 | ext2fs_group_desc_csum_set(fs, i); | |
2226 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
2227 | } | |
1e3472c5 TT |
2228 | ext2fs_mark_super_dirty(fs); |
2229 | } | |
64d588cf AK |
2230 | if (I_flag) { |
2231 | if (mount_flags & EXT2_MF_MOUNTED) { | |
2232 | fputs(_("The inode size may only be " | |
2233 | "changed when the filesystem is " | |
2234 | "unmounted.\n"), stderr); | |
0f5eba75 AD |
2235 | rc = 1; |
2236 | goto closefs; | |
64d588cf | 2237 | } |
5bf81bae TT |
2238 | if (fs->super->s_feature_incompat & |
2239 | EXT4_FEATURE_INCOMPAT_FLEX_BG) { | |
ec43da2f TT |
2240 | fputs(_("Changing the inode size not supported for " |
2241 | "filesystems with the flex_bg\n" | |
2242 | "feature enabled.\n"), | |
5bf81bae | 2243 | stderr); |
0f5eba75 AD |
2244 | rc = 1; |
2245 | goto closefs; | |
5bf81bae | 2246 | } |
64d588cf AK |
2247 | /* |
2248 | * We want to update group descriptor also | |
2249 | * with the new free inode count | |
2250 | */ | |
2251 | fs->flags &= ~EXT2_FLAG_SUPER_ONLY; | |
a9e5177b | 2252 | if (resize_inode(fs, new_inode_size) == 0) { |
ec43da2f | 2253 | printf(_("Setting inode size %lu\n"), |
64d588cf | 2254 | new_inode_size); |
c3ecabe6 | 2255 | } else { |
45ff69ff | 2256 | printf("%s", _("Failed to change inode size\n")); |
0f5eba75 AD |
2257 | rc = 1; |
2258 | goto closefs; | |
64d588cf AK |
2259 | } |
2260 | } | |
1e3472c5 | 2261 | |
3839e657 | 2262 | if (l_flag) |
ec43da2f | 2263 | list_super(sb); |
0c17cb25 TT |
2264 | if (stride_set) { |
2265 | sb->s_raid_stride = stride; | |
2266 | ext2fs_mark_super_dirty(fs); | |
2267 | printf(_("Setting stride size to %d\n"), stride); | |
2268 | } | |
2269 | if (stripe_width_set) { | |
2270 | sb->s_raid_stripe_width = stripe_width; | |
2271 | ext2fs_mark_super_dirty(fs); | |
2272 | printf(_("Setting stripe width to %d\n"), stripe_width); | |
2273 | } | |
9a976ac7 | 2274 | if (ext_mount_opts) { |
6b56f3d9 | 2275 | strncpy((char *)(fs->super->s_mount_opts), ext_mount_opts, |
9a976ac7 TT |
2276 | sizeof(fs->super->s_mount_opts)); |
2277 | fs->super->s_mount_opts[sizeof(fs->super->s_mount_opts)-1] = 0; | |
2278 | ext2fs_mark_super_dirty(fs); | |
2279 | printf(_("Setting extended default mount options to '%s'\n"), | |
2280 | ext_mount_opts); | |
2281 | free(ext_mount_opts); | |
2282 | } | |
1acde2b2 | 2283 | free(device_name); |
a6d8302b | 2284 | remove_error_table(&et_ext2_error_table); |
0f5eba75 AD |
2285 | |
2286 | closefs: | |
2287 | if (rc) { | |
2288 | ext2fs_mmp_stop(fs); | |
2289 | exit(1); | |
2290 | } | |
2291 | ||
ec43da2f | 2292 | return (ext2fs_close(fs) ? 1 : 0); |
3839e657 | 2293 | } |