]>
Commit | Line | Data |
---|---|---|
2bd0ea18 | 1 | /* |
da23017d NS |
2 | * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. |
3 | * All Rights Reserved. | |
dfc130f3 | 4 | * |
da23017d NS |
5 | * This program is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU General Public License as | |
2bd0ea18 | 7 | * published by the Free Software Foundation. |
dfc130f3 | 8 | * |
da23017d NS |
9 | * This program is distributed in the hope that it would be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | * GNU General Public License for more details. | |
dfc130f3 | 13 | * |
da23017d NS |
14 | * You should have received a copy of the GNU General Public License |
15 | * along with this program; if not, write the Free Software Foundation, | |
16 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
2bd0ea18 NS |
17 | */ |
18 | ||
1d7e80ee | 19 | #include <xfs/libxlog.h> |
12be365e | 20 | #include <sys/resource.h> |
2bd0ea18 NS |
21 | #include "avl.h" |
22 | #include "avl64.h" | |
23 | #include "globals.h" | |
24 | #include "versions.h" | |
25 | #include "agheader.h" | |
26 | #include "protos.h" | |
27 | #include "incore.h" | |
28 | #include "err_protos.h" | |
cb5b3ef4 | 29 | #include "prefetch.h" |
3b6ac903 | 30 | #include "threads.h" |
06fbdda9 | 31 | #include "progress.h" |
2bd0ea18 NS |
32 | |
33 | #define rounddown(x, y) (((x)/(y))*(y)) | |
34 | ||
35 | extern void phase1(xfs_mount_t *); | |
364a126c | 36 | extern void phase2(xfs_mount_t *, int); |
2bd0ea18 NS |
37 | extern void phase3(xfs_mount_t *); |
38 | extern void phase4(xfs_mount_t *); | |
39 | extern void phase5(xfs_mount_t *); | |
40 | extern void phase6(xfs_mount_t *); | |
41 | extern void phase7(xfs_mount_t *); | |
2bd0ea18 NS |
42 | |
43 | #define XR_MAX_SECT_SIZE (64 * 1024) | |
44 | ||
45 | /* | |
46 | * option tables for getsubopt calls | |
47 | */ | |
48 | ||
49 | /* | |
4af916f8 | 50 | * -o: user-supplied override options |
2bd0ea18 NS |
51 | */ |
52 | ||
53 | char *o_opts[] = { | |
54 | #define ASSUME_XFS 0 | |
55 | "assume_xfs", | |
56 | #define PRE_65_BETA 1 | |
57 | "fs_is_pre_65_beta", | |
9f38f08d MV |
58 | #define IHASH_SIZE 2 |
59 | "ihash", | |
60 | #define BHASH_SIZE 3 | |
61 | "bhash", | |
2556c98b | 62 | #define AG_STRIDE 4 |
add3cb90 | 63 | "ag_stride", |
d4dd6ab5 CH |
64 | #define FORCE_GEO 5 |
65 | "force_geometry", | |
364a126c DC |
66 | #define PHASE2_THREADS 6 |
67 | "phase2_threads", | |
2bd0ea18 NS |
68 | NULL |
69 | }; | |
70 | ||
4af916f8 BN |
71 | /* |
72 | * -c: conversion options | |
73 | */ | |
74 | ||
75 | char *c_opts[] = { | |
76 | #define CONVERT_LAZY_COUNT 0 | |
77 | "lazycount", | |
78 | NULL | |
79 | }; | |
80 | ||
81 | ||
2556c98b BN |
82 | static int ihash_option_used; |
83 | static int bhash_option_used; | |
12be365e | 84 | static long max_mem_specified; /* in megabytes */ |
364a126c | 85 | static int phase2_threads = 32; |
2556c98b | 86 | |
2bd0ea18 NS |
87 | static void |
88 | usage(void) | |
89 | { | |
4af916f8 BN |
90 | do_warn(_( |
91 | "Usage: %s [options] device\n" | |
92 | "\n" | |
93 | "Options:\n" | |
94 | " -f The device is a file\n" | |
95 | " -L Force log zeroing. Do this as a last resort.\n" | |
96 | " -l logdev Specifies the device where the external log resides.\n" | |
97 | " -m maxmem Maximum amount of memory to be used in megabytes.\n" | |
98 | " -n No modify mode, just checks the filesystem for damage.\n" | |
99 | " -P Disables prefetching.\n" | |
100 | " -r rtdev Specifies the device where the realtime section resides.\n" | |
101 | " -v Verbose output.\n" | |
102 | " -c subopts Change filesystem parameters - use xfs_admin.\n" | |
103 | " -o subopts Override default behaviour, refer to man page.\n" | |
104 | " -t interval Reporting interval in minutes.\n" | |
105 | " -d Repair dangerously.\n" | |
106 | " -V Reports version and exits.\n"), progname); | |
2bd0ea18 NS |
107 | exit(1); |
108 | } | |
109 | ||
2bd0ea18 NS |
110 | char * |
111 | err_string(int err_code) | |
112 | { | |
507f4e33 NS |
113 | static char *err_message[XR_BAD_ERR_CODE]; |
114 | static int done; | |
115 | ||
116 | if (!done) { | |
117 | err_message[XR_OK] = _("no error"); | |
118 | err_message[XR_BAD_MAGIC] = _("bad magic number"); | |
119 | err_message[XR_BAD_BLOCKSIZE] = _("bad blocksize field"); | |
120 | err_message[XR_BAD_BLOCKLOG] = _("bad blocksize log field"); | |
4af916f8 | 121 | err_message[XR_BAD_VERSION] = _("bad or unsupported version"); |
507f4e33 NS |
122 | err_message[XR_BAD_INPROGRESS] = |
123 | _("filesystem mkfs-in-progress bit set"); | |
124 | err_message[XR_BAD_FS_SIZE_DATA] = | |
125 | _("inconsistent filesystem geometry information"); | |
126 | err_message[XR_BAD_INO_SIZE_DATA] = | |
127 | _("bad inode size or inconsistent with number of inodes/block"), | |
128 | err_message[XR_BAD_SECT_SIZE_DATA] = _("bad sector size"); | |
129 | err_message[XR_AGF_GEO_MISMATCH] = | |
130 | _("AGF geometry info conflicts with filesystem geometry"); | |
131 | err_message[XR_AGI_GEO_MISMATCH] = | |
132 | _("AGI geometry info conflicts with filesystem geometry"); | |
133 | err_message[XR_SB_GEO_MISMATCH] = | |
134 | _("AG superblock geometry info conflicts with filesystem geometry"); | |
135 | err_message[XR_EOF] = _("attempted to perform I/O beyond EOF"); | |
136 | err_message[XR_BAD_RT_GEO_DATA] = | |
137 | _("inconsistent filesystem geometry in realtime filesystem component"); | |
138 | err_message[XR_BAD_INO_MAX_PCT] = | |
139 | _("maximum indicated percentage of inodes > 100%"); | |
140 | err_message[XR_BAD_INO_ALIGN] = | |
141 | _("inconsistent inode alignment value"); | |
142 | err_message[XR_INSUFF_SEC_SB] = | |
143 | _("not enough secondary superblocks with matching geometry"); | |
144 | err_message[XR_BAD_SB_UNIT] = | |
145 | _("bad stripe unit in superblock"); | |
146 | err_message[XR_BAD_SB_WIDTH] = | |
147 | _("bad stripe width in superblock"); | |
148 | err_message[XR_BAD_SVN] = | |
149 | _("bad shared version number in superblock"); | |
150 | done = 1; | |
151 | } | |
152 | ||
2bd0ea18 | 153 | if (err_code < XR_OK || err_code >= XR_BAD_ERR_CODE) |
507f4e33 | 154 | do_abort(_("bad error code - %d\n"), err_code); |
2bd0ea18 NS |
155 | |
156 | return(err_message[err_code]); | |
157 | } | |
158 | ||
159 | static void | |
160 | noval(char opt, char *tbl[], int idx) | |
161 | { | |
507f4e33 | 162 | do_warn(_("-%c %s option cannot have a value\n"), opt, tbl[idx]); |
2bd0ea18 NS |
163 | usage(); |
164 | } | |
165 | ||
166 | static void | |
167 | respec(char opt, char *tbl[], int idx) | |
168 | { | |
169 | do_warn("-%c ", opt); | |
170 | if (tbl) | |
171 | do_warn("%s ", tbl[idx]); | |
507f4e33 | 172 | do_warn(_("option respecified\n")); |
2bd0ea18 NS |
173 | usage(); |
174 | } | |
175 | ||
176 | static void | |
177 | unknown(char opt, char *s) | |
178 | { | |
507f4e33 | 179 | do_warn(_("unknown option -%c %s\n"), opt, s); |
2bd0ea18 NS |
180 | usage(); |
181 | } | |
182 | ||
183 | /* | |
184 | * sets only the global argument flags and variables | |
185 | */ | |
186 | void | |
187 | process_args(int argc, char **argv) | |
188 | { | |
189 | char *p; | |
190 | int c; | |
191 | ||
192 | log_spec = 0; | |
193 | fs_is_dirty = 0; | |
194 | verbose = 0; | |
195 | no_modify = 0; | |
c781939c | 196 | dangerously = 0; |
2bd0ea18 | 197 | isa_file = 0; |
d321ceac | 198 | zap_log = 0; |
2bd0ea18 | 199 | dumpcore = 0; |
0f012a4c | 200 | full_ino_ex_data = 0; |
2bd0ea18 NS |
201 | delete_attr_ok = 1; |
202 | force_geo = 0; | |
203 | assume_xfs = 0; | |
204 | clear_sunit = 0; | |
205 | sb_inoalignmt = 0; | |
206 | sb_unit = 0; | |
207 | sb_width = 0; | |
208 | fs_attributes_allowed = 1; | |
9b1d68ec | 209 | fs_attributes2_allowed = 1; |
2bd0ea18 NS |
210 | fs_inode_nlink_allowed = 1; |
211 | fs_quotas_allowed = 1; | |
212 | fs_aligned_inodes_allowed = 1; | |
213 | fs_sb_feature_bits_allowed = 1; | |
214 | fs_has_extflgbit_allowed = 1; | |
215 | pre_65_beta = 0; | |
216 | fs_shared_allowed = 1; | |
add3cb90 | 217 | ag_stride = 0; |
2556c98b | 218 | thread_count = 1; |
06fbdda9 | 219 | report_interval = PROG_RPT_DEFAULT; |
2bd0ea18 NS |
220 | |
221 | /* | |
222 | * XXX have to add suboption processing here | |
223 | * attributes, quotas, nlinks, aligned_inos, sb_fbits | |
224 | */ | |
4af916f8 | 225 | while ((c = getopt(argc, argv, "c:o:fl:m:r:LnDvVdPt:")) != EOF) { |
2bd0ea18 NS |
226 | switch (c) { |
227 | case 'D': | |
228 | dumpcore = 1; | |
229 | break; | |
230 | case 'o': | |
231 | p = optarg; | |
232 | while (*p != '\0') { | |
233 | char *val; | |
234 | ||
235 | switch (getsubopt(&p, (constpp)o_opts, &val)) { | |
236 | case ASSUME_XFS: | |
237 | if (val) | |
238 | noval('o', o_opts, ASSUME_XFS); | |
239 | if (assume_xfs) | |
240 | respec('o', o_opts, ASSUME_XFS); | |
241 | assume_xfs = 1; | |
242 | break; | |
243 | case PRE_65_BETA: | |
244 | if (val) | |
245 | noval('o', o_opts, PRE_65_BETA); | |
246 | if (pre_65_beta) | |
247 | respec('o', o_opts, | |
248 | PRE_65_BETA); | |
249 | pre_65_beta = 1; | |
250 | break; | |
9f38f08d | 251 | case IHASH_SIZE: |
5e656dbb | 252 | libxfs_ihash_size = (int)strtol(val, NULL, 0); |
2556c98b | 253 | ihash_option_used = 1; |
9f38f08d MV |
254 | break; |
255 | case BHASH_SIZE: | |
12be365e BN |
256 | if (max_mem_specified) |
257 | do_abort( | |
258 | _("-o bhash option cannot be used with -m option\n")); | |
5e656dbb | 259 | libxfs_bhash_size = (int)strtol(val, NULL, 0); |
2556c98b | 260 | bhash_option_used = 1; |
cb5b3ef4 | 261 | break; |
add3cb90 | 262 | case AG_STRIDE: |
5e656dbb | 263 | ag_stride = (int)strtol(val, NULL, 0); |
3b6ac903 | 264 | break; |
d4dd6ab5 CH |
265 | case FORCE_GEO: |
266 | if (val) | |
267 | noval('o', o_opts, FORCE_GEO); | |
268 | if (force_geo) | |
269 | respec('o', o_opts, FORCE_GEO); | |
270 | force_geo = 1; | |
271 | break; | |
364a126c DC |
272 | case PHASE2_THREADS: |
273 | phase2_threads = (int)strtol(val, NULL, 0); | |
274 | break; | |
2bd0ea18 NS |
275 | default: |
276 | unknown('o', val); | |
277 | break; | |
278 | } | |
279 | } | |
280 | break; | |
4af916f8 BN |
281 | case 'c': |
282 | p = optarg; | |
283 | while (*p) { | |
284 | char *val; | |
285 | ||
286 | switch (getsubopt(&p, (constpp)c_opts, &val)) { | |
287 | case CONVERT_LAZY_COUNT: | |
5e656dbb | 288 | lazy_count = (int)strtol(val, NULL, 0); |
4af916f8 BN |
289 | convert_lazy_count = 1; |
290 | break; | |
291 | default: | |
292 | unknown('c', val); | |
293 | break; | |
294 | } | |
295 | } | |
296 | break; | |
2bd0ea18 NS |
297 | case 'l': |
298 | log_name = optarg; | |
299 | log_spec = 1; | |
300 | break; | |
42a564ab ES |
301 | case 'r': |
302 | rt_name = optarg; | |
303 | rt_spec = 1; | |
304 | break; | |
2bd0ea18 NS |
305 | case 'f': |
306 | isa_file = 1; | |
307 | break; | |
12be365e BN |
308 | case 'm': |
309 | if (bhash_option_used) | |
310 | do_abort(_("-m option cannot be used with " | |
311 | "-o bhash option\n")); | |
5e656dbb | 312 | max_mem_specified = strtol(optarg, NULL, 0); |
12be365e | 313 | break; |
d321ceac NS |
314 | case 'L': |
315 | zap_log = 1; | |
316 | break; | |
2bd0ea18 NS |
317 | case 'n': |
318 | no_modify = 1; | |
319 | break; | |
6089b6f0 NS |
320 | case 'd': |
321 | dangerously = 1; | |
322 | break; | |
2bd0ea18 | 323 | case 'v': |
3b6ac903 | 324 | verbose++; |
2bd0ea18 NS |
325 | break; |
326 | case 'V': | |
507f4e33 | 327 | printf(_("%s version %s\n"), progname, VERSION); |
3d98fe63 | 328 | exit(0); |
cb5b3ef4 | 329 | case 'P': |
2556c98b | 330 | do_prefetch = 0; |
3b6ac903 | 331 | break; |
06fbdda9 | 332 | case 't': |
5e656dbb | 333 | report_interval = (int)strtol(optarg, NULL, 0); |
06fbdda9 | 334 | break; |
2bd0ea18 NS |
335 | case '?': |
336 | usage(); | |
337 | } | |
338 | } | |
339 | ||
340 | if (argc - optind != 1) | |
341 | usage(); | |
342 | ||
343 | if ((fs_name = argv[optind]) == NULL) | |
344 | usage(); | |
345 | } | |
346 | ||
b1559967 | 347 | void __attribute__((noreturn)) |
2bd0ea18 NS |
348 | do_error(char const *msg, ...) |
349 | { | |
350 | va_list args; | |
351 | ||
507f4e33 | 352 | fprintf(stderr, _("\nfatal error -- ")); |
2bd0ea18 NS |
353 | |
354 | va_start(args, msg); | |
079afa09 CH |
355 | vfprintf(stderr, msg, args); |
356 | if (dumpcore) | |
357 | abort(); | |
358 | exit(1); | |
2bd0ea18 NS |
359 | } |
360 | ||
361 | /* | |
362 | * like do_error, only the error is internal, no system | |
363 | * error so no oserror processing | |
364 | */ | |
b1559967 | 365 | void __attribute__((noreturn)) |
2bd0ea18 NS |
366 | do_abort(char const *msg, ...) |
367 | { | |
368 | va_list args; | |
369 | ||
370 | va_start(args, msg); | |
079afa09 CH |
371 | vfprintf(stderr, msg, args); |
372 | if (dumpcore) | |
373 | abort(); | |
374 | exit(1); | |
2bd0ea18 NS |
375 | } |
376 | ||
377 | void | |
378 | do_warn(char const *msg, ...) | |
379 | { | |
380 | va_list args; | |
381 | ||
382 | fs_is_dirty = 1; | |
383 | ||
384 | va_start(args, msg); | |
079afa09 | 385 | vfprintf(stderr, msg, args); |
2bd0ea18 NS |
386 | va_end(args); |
387 | } | |
388 | ||
389 | /* no formatting */ | |
390 | ||
391 | void | |
392 | do_log(char const *msg, ...) | |
393 | { | |
394 | va_list args; | |
395 | ||
396 | va_start(args, msg); | |
079afa09 | 397 | vfprintf(stderr, msg, args); |
2bd0ea18 NS |
398 | va_end(args); |
399 | } | |
400 | ||
401 | void | |
402 | calc_mkfs(xfs_mount_t *mp) | |
403 | { | |
404 | xfs_agblock_t fino_bno; | |
405 | int do_inoalign; | |
406 | ||
407 | do_inoalign = mp->m_sinoalign; | |
408 | ||
409 | /* | |
410 | * pre-calculate geometry of ag 0. We know what it looks | |
411 | * like because we know what mkfs does -- 3 btree roots, | |
412 | * and some number of blocks to prefill the agfl. | |
413 | */ | |
414 | bnobt_root = howmany(4 * mp->m_sb.sb_sectsize, mp->m_sb.sb_blocksize); | |
415 | bcntbt_root = bnobt_root + 1; | |
416 | inobt_root = bnobt_root + 2; | |
417 | fino_bno = inobt_root + XFS_MIN_FREELIST_RAW(1, 1, mp) + 1; | |
418 | ||
d4dd6ab5 CH |
419 | /* |
420 | * If we only have a single allocation group the log is also allocated | |
421 | * in the first allocation group and we need to add the number of | |
422 | * blocks used by the log to the above calculation. | |
423 | * All this of course doesn't apply if we have an external log. | |
424 | */ | |
425 | if (mp->m_sb.sb_agcount == 1 && mp->m_sb.sb_logstart) { | |
426 | /* | |
427 | * XXX(hch): verify that sb_logstart makes sense? | |
428 | */ | |
429 | fino_bno += mp->m_sb.sb_logblocks; | |
430 | } | |
431 | ||
2bd0ea18 NS |
432 | /* |
433 | * ditto the location of the first inode chunks in the fs ('/') | |
434 | */ | |
5e656dbb | 435 | if (xfs_sb_version_hasdalign(&mp->m_sb) && do_inoalign) { |
2bd0ea18 NS |
436 | first_prealloc_ino = XFS_OFFBNO_TO_AGINO(mp, roundup(fino_bno, |
437 | mp->m_sb.sb_unit), 0); | |
5e656dbb | 438 | } else if (xfs_sb_version_hasalign(&mp->m_sb) && |
2bd0ea18 NS |
439 | mp->m_sb.sb_inoalignmt > 1) { |
440 | first_prealloc_ino = XFS_OFFBNO_TO_AGINO(mp, | |
441 | roundup(fino_bno, | |
442 | mp->m_sb.sb_inoalignmt), | |
443 | 0); | |
444 | } else { | |
445 | first_prealloc_ino = XFS_OFFBNO_TO_AGINO(mp, fino_bno, 0); | |
446 | } | |
447 | ||
448 | ASSERT(XFS_IALLOC_BLOCKS(mp) > 0); | |
449 | ||
450 | if (XFS_IALLOC_BLOCKS(mp) > 1) | |
451 | last_prealloc_ino = first_prealloc_ino + XFS_INODES_PER_CHUNK; | |
452 | else | |
453 | last_prealloc_ino = XFS_OFFBNO_TO_AGINO(mp, fino_bno + 1, 0); | |
454 | ||
455 | /* | |
456 | * now the first 3 inodes in the system | |
457 | */ | |
458 | if (mp->m_sb.sb_rootino != first_prealloc_ino) { | |
459 | do_warn( | |
5d1b7f0f | 460 | _("sb root inode value %" PRIu64 " %sinconsistent with calculated value %u\n"), |
507f4e33 NS |
461 | mp->m_sb.sb_rootino, |
462 | (mp->m_sb.sb_rootino == NULLFSINO ? "(NULLFSINO) ":""), | |
463 | first_prealloc_ino); | |
2bd0ea18 NS |
464 | |
465 | if (!no_modify) | |
466 | do_warn( | |
5d1b7f0f | 467 | _("resetting superblock root inode pointer to %u\n"), |
2bd0ea18 NS |
468 | first_prealloc_ino); |
469 | else | |
470 | do_warn( | |
5d1b7f0f | 471 | _("would reset superblock root inode pointer to %u\n"), |
2bd0ea18 NS |
472 | first_prealloc_ino); |
473 | ||
474 | /* | |
475 | * just set the value -- safe since the superblock | |
476 | * doesn't get flushed out if no_modify is set | |
477 | */ | |
478 | mp->m_sb.sb_rootino = first_prealloc_ino; | |
479 | } | |
480 | ||
481 | if (mp->m_sb.sb_rbmino != first_prealloc_ino + 1) { | |
482 | do_warn( | |
5d1b7f0f | 483 | _("sb realtime bitmap inode %" PRIu64 " %sinconsistent with calculated value %u\n"), |
507f4e33 NS |
484 | mp->m_sb.sb_rbmino, |
485 | (mp->m_sb.sb_rbmino == NULLFSINO ? "(NULLFSINO) ":""), | |
486 | first_prealloc_ino + 1); | |
2bd0ea18 NS |
487 | |
488 | if (!no_modify) | |
489 | do_warn( | |
5d1b7f0f | 490 | _("resetting superblock realtime bitmap ino pointer to %u\n"), |
2bd0ea18 NS |
491 | first_prealloc_ino + 1); |
492 | else | |
493 | do_warn( | |
5d1b7f0f | 494 | _("would reset superblock realtime bitmap ino pointer to %u\n"), |
2bd0ea18 NS |
495 | first_prealloc_ino + 1); |
496 | ||
497 | /* | |
498 | * just set the value -- safe since the superblock | |
499 | * doesn't get flushed out if no_modify is set | |
500 | */ | |
501 | mp->m_sb.sb_rbmino = first_prealloc_ino + 1; | |
502 | } | |
503 | ||
504 | if (mp->m_sb.sb_rsumino != first_prealloc_ino + 2) { | |
505 | do_warn( | |
5d1b7f0f CH |
506 | _("sb realtime summary inode %" PRIu64 " %sinconsistent with calculated value %u\n"), |
507 | mp->m_sb.sb_rsumino, | |
508 | (mp->m_sb.sb_rsumino == NULLFSINO ? "(NULLFSINO) ":""), | |
509 | first_prealloc_ino + 2); | |
2bd0ea18 NS |
510 | |
511 | if (!no_modify) | |
512 | do_warn( | |
5d1b7f0f | 513 | _("resetting superblock realtime summary ino pointer to %u\n"), |
2bd0ea18 NS |
514 | first_prealloc_ino + 2); |
515 | else | |
516 | do_warn( | |
5d1b7f0f | 517 | _("would reset superblock realtime summary ino pointer to %u\n"), |
2bd0ea18 NS |
518 | first_prealloc_ino + 2); |
519 | ||
520 | /* | |
521 | * just set the value -- safe since the superblock | |
522 | * doesn't get flushed out if no_modify is set | |
523 | */ | |
524 | mp->m_sb.sb_rsumino = first_prealloc_ino + 2; | |
525 | } | |
526 | ||
527 | } | |
528 | ||
529 | int | |
530 | main(int argc, char **argv) | |
531 | { | |
2bd0ea18 NS |
532 | xfs_mount_t *temp_mp; |
533 | xfs_mount_t *mp; | |
5e656dbb | 534 | xfs_dsb_t *dsb; |
2bd0ea18 NS |
535 | xfs_buf_t *sbp; |
536 | xfs_mount_t xfs_m; | |
06fbdda9 | 537 | char *msgbuf; |
2bd0ea18 NS |
538 | |
539 | progname = basename(argv[0]); | |
507f4e33 NS |
540 | setlocale(LC_ALL, ""); |
541 | bindtextdomain(PACKAGE, LOCALEDIR); | |
542 | textdomain(PACKAGE); | |
2bd0ea18 NS |
543 | |
544 | temp_mp = &xfs_m; | |
545 | setbuf(stdout, NULL); | |
546 | ||
547 | process_args(argc, argv); | |
d321ceac | 548 | xfs_init(&x); |
2bd0ea18 | 549 | |
2556c98b BN |
550 | msgbuf = malloc(DURATION_BUF_SIZE); |
551 | ||
06fbdda9 MV |
552 | timestamp(PHASE_START, 0, NULL); |
553 | timestamp(PHASE_END, 0, NULL); | |
554 | ||
2bd0ea18 NS |
555 | /* do phase1 to make sure we have a superblock */ |
556 | phase1(temp_mp); | |
06fbdda9 | 557 | timestamp(PHASE_END, 1, NULL); |
2bd0ea18 NS |
558 | |
559 | if (no_modify && primary_sb_modified) { | |
507f4e33 NS |
560 | do_warn(_("Primary superblock would have been modified.\n" |
561 | "Cannot proceed further in no_modify mode.\n" | |
562 | "Exiting now.\n")); | |
2bd0ea18 NS |
563 | exit(1); |
564 | } | |
565 | ||
566 | /* prepare the mount structure */ | |
2aa2e7b9 BN |
567 | sbp = libxfs_readbuf(x.ddev, XFS_SB_DADDR, |
568 | 1 << (XFS_MAX_SECTORSIZE_LOG - BBSHIFT), 0); | |
2bd0ea18 | 569 | memset(&xfs_m, 0, sizeof(xfs_mount_t)); |
5e656dbb | 570 | libxfs_sb_from_disk(&xfs_m.m_sb, XFS_BUF_TO_SBP(sbp)); |
2bd0ea18 | 571 | |
f63fd268 DC |
572 | /* |
573 | * if the sector size of the filesystem we are trying to repair is | |
574 | * smaller than that of the underlying filesystem (i.e. we are repairing | |
575 | * an image), the we have to turn off direct IO because we cannot do IO | |
576 | * smaller than the host filesystem's sector size. | |
577 | */ | |
578 | if (isa_file) { | |
579 | int fd = libxfs_device_to_fd(x.ddev); | |
580 | struct xfs_fsop_geom_v1 geom = { 0 }; | |
581 | ||
582 | if (ioctl(fd, XFS_IOC_FSGEOMETRY_V1, &geom) < 0) { | |
583 | do_warn(_("Cannot get host filesystem geometry.\n" | |
584 | "Repair may fail if there is a sector size mismatch between\n" | |
585 | "the image and the host filesystem.\n")); | |
586 | geom.sectsize = BBSIZE; | |
587 | } | |
588 | ||
589 | if (xfs_m.m_sb.sb_sectsize < geom.sectsize) { | |
590 | long old_flags; | |
591 | ||
592 | old_flags = fcntl(fd, F_GETFL, 0); | |
593 | if (fcntl(fd, F_SETFL, old_flags & ~O_DIRECT) < 0) { | |
594 | do_warn(_( | |
595 | "Sector size on host filesystem larger than image sector size.\n" | |
596 | "Cannot turn off direct IO, so exiting.\n")); | |
597 | exit(1); | |
598 | } | |
599 | } | |
600 | } | |
5e656dbb | 601 | mp = libxfs_mount(&xfs_m, &xfs_m.m_sb, x.ddev, x.logdev, x.rtdev, 0); |
2bd0ea18 NS |
602 | |
603 | if (!mp) { | |
507f4e33 NS |
604 | fprintf(stderr, |
605 | _("%s: cannot repair this filesystem. Sorry.\n"), | |
2bd0ea18 NS |
606 | progname); |
607 | exit(1); | |
608 | } | |
609 | libxfs_putbuf(sbp); | |
2aa2e7b9 | 610 | libxfs_purgebuf(sbp); |
2bd0ea18 NS |
611 | |
612 | /* | |
613 | * set XFS-independent status vars from the mount/sb structure | |
614 | */ | |
615 | glob_agcount = mp->m_sb.sb_agcount; | |
616 | ||
617 | chunks_pblock = mp->m_sb.sb_inopblock / XFS_INODES_PER_CHUNK; | |
618 | max_symlink_blocks = howmany(MAXPATHLEN - 1, mp->m_sb.sb_blocksize); | |
edf3f9d0 BN |
619 | inodes_per_cluster = MAX(mp->m_sb.sb_inopblock, |
620 | XFS_INODE_CLUSTER_SIZE(mp) >> mp->m_sb.sb_inodelog); | |
2bd0ea18 | 621 | |
add3cb90 | 622 | if (ag_stride) { |
2556c98b | 623 | thread_count = (glob_agcount + ag_stride - 1) / ag_stride; |
add3cb90 BN |
624 | thread_init(); |
625 | } | |
626 | ||
2556c98b | 627 | if (ag_stride && report_interval) { |
06fbdda9 | 628 | init_progress_rpt(); |
06fbdda9 MV |
629 | if (msgbuf) { |
630 | do_log(_(" - reporting progress in intervals of %s\n"), | |
631 | duration(report_interval, msgbuf)); | |
06fbdda9 MV |
632 | } |
633 | } | |
634 | ||
2556c98b BN |
635 | /* |
636 | * Adjust libxfs cache sizes based on system memory, | |
637 | * filesystem size and inode count. | |
638 | * | |
639 | * We'll set the cache size based on 3/4s the memory minus | |
640 | * space used by the inode AVL tree and block usage map. | |
641 | * | |
642 | * Inode AVL tree space is approximately 4 bytes per inode, | |
643 | * block usage map is currently 1 byte for 2 blocks. | |
644 | * | |
645 | * We assume most blocks will be inode clusters. | |
646 | * | |
647 | * Calculations are done in kilobyte units. | |
648 | */ | |
649 | ||
12be365e | 650 | if (!bhash_option_used || max_mem_specified) { |
2556c98b | 651 | unsigned long mem_used; |
12be365e BN |
652 | unsigned long max_mem; |
653 | struct rlimit rlim; | |
2556c98b BN |
654 | |
655 | libxfs_icache_purge(); | |
656 | libxfs_bcache_purge(); | |
657 | cache_destroy(libxfs_icache); | |
658 | cache_destroy(libxfs_bcache); | |
659 | ||
660 | mem_used = (mp->m_sb.sb_icount >> (10 - 2)) + | |
12be365e BN |
661 | (mp->m_sb.sb_dblocks >> (10 + 1)) + |
662 | 50000; /* rough estimate of 50MB overhead */ | |
663 | max_mem = max_mem_specified ? max_mem_specified * 1024 : | |
664 | libxfs_physmem() * 3 / 4; | |
665 | ||
666 | if (getrlimit(RLIMIT_AS, &rlim) != -1 && | |
667 | rlim.rlim_cur != RLIM_INFINITY) { | |
668 | rlim.rlim_cur = rlim.rlim_max; | |
669 | setrlimit(RLIMIT_AS, &rlim); | |
670 | /* use approximately 80% of rlimit to avoid overrun */ | |
671 | max_mem = MIN(max_mem, rlim.rlim_cur / 1280); | |
672 | } else | |
673 | max_mem = MIN(max_mem, (LONG_MAX >> 10) + 1); | |
2556c98b BN |
674 | |
675 | if (verbose > 1) | |
5d1b7f0f CH |
676 | do_log( |
677 | _(" - max_mem = %lu, icount = %" PRIu64 ", imem = %" PRIu64 ", dblock = %" PRIu64 ", dmem = %" PRIu64 "\n"), | |
12be365e BN |
678 | max_mem, mp->m_sb.sb_icount, |
679 | mp->m_sb.sb_icount >> (10 - 2), | |
680 | mp->m_sb.sb_dblocks, | |
681 | mp->m_sb.sb_dblocks >> (10 + 1)); | |
682 | ||
683 | if (max_mem <= mem_used) { | |
2556c98b BN |
684 | /* |
685 | * Turn off prefetch and minimise libxfs cache if | |
686 | * physical memory is deemed insufficient | |
687 | */ | |
0335a835 DC |
688 | if (max_mem_specified) { |
689 | do_abort( | |
690 | _("Required memory for repair is greater that the maximum specified\n" | |
691 | "with the -m option. Please increase it to at least %lu.\n"), | |
12be365e | 692 | mem_used / 1024); |
0335a835 DC |
693 | } else { |
694 | do_warn( | |
695 | _("Not enough RAM available for repair to enable prefetching.\n" | |
696 | "This will be _slow_.\n" | |
697 | "You need at least %luMB RAM to run with prefetching enabled.\n"), | |
698 | mem_used * 1280 / (1024 * 1024)); | |
699 | } | |
2556c98b BN |
700 | do_prefetch = 0; |
701 | libxfs_bhash_size = 64; | |
702 | } else { | |
12be365e BN |
703 | max_mem -= mem_used; |
704 | if (max_mem >= (1 << 30)) | |
705 | max_mem = 1 << 30; | |
706 | libxfs_bhash_size = max_mem / (HASH_CACHE_RATIO * | |
2556c98b BN |
707 | (mp->m_inode_cluster_size >> 10)); |
708 | if (libxfs_bhash_size < 512) | |
709 | libxfs_bhash_size = 512; | |
710 | } | |
711 | ||
712 | if (verbose) | |
713 | do_log(_(" - block cache size set to %d entries\n"), | |
714 | libxfs_bhash_size * HASH_CACHE_RATIO); | |
715 | ||
716 | if (!ihash_option_used) | |
717 | libxfs_ihash_size = libxfs_bhash_size; | |
718 | ||
719 | libxfs_icache = cache_init(libxfs_ihash_size, | |
720 | &libxfs_icache_operations); | |
721 | libxfs_bcache = cache_init(libxfs_bhash_size, | |
722 | &libxfs_bcache_operations); | |
723 | } | |
724 | ||
2bd0ea18 NS |
725 | /* |
726 | * calculate what mkfs would do to this filesystem | |
727 | */ | |
728 | calc_mkfs(mp); | |
729 | ||
730 | /* | |
c1f7a46c | 731 | * initialize block alloc map |
2bd0ea18 | 732 | */ |
c1f7a46c BN |
733 | init_bmaps(mp); |
734 | incore_ino_init(mp); | |
735 | incore_ext_init(mp); | |
736 | ||
737 | /* initialize random globals now that we know the fs geometry */ | |
738 | inodes_per_block = mp->m_sb.sb_inopblock; | |
2bd0ea18 NS |
739 | |
740 | if (parse_sb_version(&mp->m_sb)) { | |
741 | do_warn( | |
507f4e33 | 742 | _("Found unsupported filesystem features. Exiting now.\n")); |
2bd0ea18 NS |
743 | return(1); |
744 | } | |
745 | ||
746 | /* make sure the per-ag freespace maps are ok so we can mount the fs */ | |
364a126c | 747 | phase2(mp, phase2_threads); |
06fbdda9 | 748 | timestamp(PHASE_END, 2, NULL); |
2bd0ea18 | 749 | |
2556c98b BN |
750 | if (do_prefetch) |
751 | init_prefetch(mp); | |
752 | ||
2bd0ea18 | 753 | phase3(mp); |
06fbdda9 | 754 | timestamp(PHASE_END, 3, NULL); |
2bd0ea18 NS |
755 | |
756 | phase4(mp); | |
06fbdda9 | 757 | timestamp(PHASE_END, 4, NULL); |
2bd0ea18 NS |
758 | |
759 | if (no_modify) | |
507f4e33 | 760 | printf(_("No modify flag set, skipping phase 5\n")); |
3b6ac903 | 761 | else { |
2bd0ea18 | 762 | phase5(mp); |
3b6ac903 | 763 | } |
06fbdda9 | 764 | timestamp(PHASE_END, 5, NULL); |
2bd0ea18 | 765 | |
c1f7a46c BN |
766 | /* |
767 | * Done with the block usage maps, toss them... | |
768 | */ | |
769 | free_bmaps(mp); | |
770 | ||
2bd0ea18 NS |
771 | if (!bad_ino_btree) { |
772 | phase6(mp); | |
06fbdda9 | 773 | timestamp(PHASE_END, 6, NULL); |
2bd0ea18 NS |
774 | |
775 | phase7(mp); | |
06fbdda9 | 776 | timestamp(PHASE_END, 7, NULL); |
2bd0ea18 NS |
777 | } else { |
778 | do_warn( | |
507f4e33 | 779 | _("Inode allocation btrees are too corrupted, skipping phases 6 and 7\n")); |
2bd0ea18 NS |
780 | } |
781 | ||
b36eef04 | 782 | if (lost_quotas && !have_uquotino && !have_gquotino) { |
2bd0ea18 NS |
783 | if (!no_modify) { |
784 | do_warn( | |
507f4e33 | 785 | _("Warning: no quota inodes were found. Quotas disabled.\n")); |
2bd0ea18 NS |
786 | } else { |
787 | do_warn( | |
507f4e33 | 788 | _("Warning: no quota inodes were found. Quotas would be disabled.\n")); |
2bd0ea18 NS |
789 | } |
790 | } else if (lost_quotas) { | |
791 | if (!no_modify) { | |
792 | do_warn( | |
507f4e33 | 793 | _("Warning: quota inodes were cleared. Quotas disabled.\n")); |
2bd0ea18 NS |
794 | } else { |
795 | do_warn( | |
507f4e33 | 796 | _("Warning: quota inodes would be cleared. Quotas would be disabled.\n")); |
2bd0ea18 NS |
797 | } |
798 | } else { | |
799 | if (lost_uquotino) { | |
800 | if (!no_modify) { | |
801 | do_warn( | |
507f4e33 NS |
802 | _("Warning: user quota information was cleared.\n" |
803 | "User quotas can not be enforced until limit information is recreated.\n")); | |
2bd0ea18 NS |
804 | } else { |
805 | do_warn( | |
507f4e33 NS |
806 | _("Warning: user quota information would be cleared.\n" |
807 | "User quotas could not be enforced until limit information was recreated.\n")); | |
2bd0ea18 NS |
808 | } |
809 | } | |
810 | ||
b36eef04 | 811 | if (lost_gquotino) { |
2bd0ea18 NS |
812 | if (!no_modify) { |
813 | do_warn( | |
507f4e33 NS |
814 | _("Warning: group quota information was cleared.\n" |
815 | "Group quotas can not be enforced until limit information is recreated.\n")); | |
2bd0ea18 NS |
816 | } else { |
817 | do_warn( | |
507f4e33 NS |
818 | _("Warning: group quota information would be cleared.\n" |
819 | "Group quotas could not be enforced until limit information was recreated.\n")); | |
9b27bdbb NS |
820 | } |
821 | } | |
822 | ||
823 | if (lost_pquotino) { | |
824 | if (!no_modify) { | |
825 | do_warn( | |
826 | _("Warning: project quota information was cleared.\n" | |
827 | "Project quotas can not be enforced until limit information is recreated.\n")); | |
828 | } else { | |
829 | do_warn( | |
830 | _("Warning: project quota information would be cleared.\n" | |
831 | "Project quotas could not be enforced until limit information was recreated.\n")); | |
2bd0ea18 NS |
832 | } |
833 | } | |
834 | } | |
835 | ||
2556c98b | 836 | if (ag_stride && report_interval) |
06fbdda9 | 837 | stop_progress_rpt(); |
9f38f08d | 838 | |
2bd0ea18 NS |
839 | if (no_modify) { |
840 | do_log( | |
507f4e33 | 841 | _("No modify flag set, skipping filesystem flush and exiting.\n")); |
3b6ac903 | 842 | if (verbose) |
06fbdda9 | 843 | summary_report(); |
2bd0ea18 NS |
844 | if (fs_is_dirty) |
845 | return(1); | |
846 | ||
847 | return(0); | |
848 | } | |
849 | ||
850 | /* | |
851 | * Clear the quota flags if they're on. | |
852 | */ | |
853 | sbp = libxfs_getsb(mp, 0); | |
854 | if (!sbp) | |
507f4e33 | 855 | do_error(_("couldn't get superblock\n")); |
2bd0ea18 | 856 | |
5e656dbb | 857 | dsb = XFS_BUF_TO_SBP(sbp); |
2bd0ea18 | 858 | |
5e656dbb BN |
859 | if (be16_to_cpu(dsb->sb_qflags) & (XFS_UQUOTA_CHKD | XFS_OQUOTA_CHKD)) { |
860 | do_warn(_("Note - quota info will be regenerated on next " | |
861 | "quota mount.\n")); | |
862 | dsb->sb_qflags &= cpu_to_be16(~(XFS_UQUOTA_CHKD | | |
863 | XFS_OQUOTA_CHKD)); | |
2bd0ea18 NS |
864 | } |
865 | ||
866 | if (clear_sunit) { | |
867 | do_warn( | |
507f4e33 NS |
868 | _("Note - stripe unit (%d) and width (%d) fields have been reset.\n" |
869 | "Please set with mount -o sunit=<value>,swidth=<value>\n"), | |
5e656dbb BN |
870 | be32_to_cpu(dsb->sb_unit), be32_to_cpu(dsb->sb_width)); |
871 | dsb->sb_unit = 0; | |
872 | dsb->sb_width = 0; | |
dfc130f3 | 873 | } |
2bd0ea18 NS |
874 | |
875 | libxfs_writebuf(sbp, 0); | |
876 | ||
2556c98b BN |
877 | /* |
878 | * Done, flush all cached buffers and inodes. | |
879 | */ | |
880 | libxfs_bcache_flush(); | |
881 | ||
2bd0ea18 | 882 | libxfs_umount(mp); |
d321ceac NS |
883 | if (x.rtdev) |
884 | libxfs_device_close(x.rtdev); | |
885 | if (x.logdev && x.logdev != x.ddev) | |
886 | libxfs_device_close(x.logdev); | |
887 | libxfs_device_close(x.ddev); | |
2bd0ea18 | 888 | |
06fbdda9 MV |
889 | if (verbose) |
890 | summary_report(); | |
507f4e33 | 891 | do_log(_("done\n")); |
4c0a98ae BN |
892 | pftrace_done(); |
893 | ||
3b6ac903 MV |
894 | return (0); |
895 | } |