]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
2bd0ea18 | 2 | /* |
da23017d NS |
3 | * Copyright (c) 2000-2002,2005 Silicon Graphics, Inc. |
4 | * All Rights Reserved. | |
2bd0ea18 NS |
5 | */ |
6 | ||
6b803e5a CH |
7 | #include "libxfs.h" |
8 | #include "libxlog.h" | |
12be365e | 9 | #include <sys/resource.h> |
4a32b9e9 | 10 | #include "xfs_multidisk.h" |
2bd0ea18 | 11 | #include "avl.h" |
b4a09f89 | 12 | #include "libfrog/avl64.h" |
2bd0ea18 NS |
13 | #include "globals.h" |
14 | #include "versions.h" | |
15 | #include "agheader.h" | |
16 | #include "protos.h" | |
17 | #include "incore.h" | |
18 | #include "err_protos.h" | |
cb5b3ef4 | 19 | #include "prefetch.h" |
3b6ac903 | 20 | #include "threads.h" |
06fbdda9 | 21 | #include "progress.h" |
beed0dc8 | 22 | #include "dinode.h" |
9e0f480e DW |
23 | #include "slab.h" |
24 | #include "rmap.h" | |
fee68490 | 25 | #include "libfrog/fsgeom.h" |
b658de93 | 26 | #include "libfrog/platform.h" |
49031e66 | 27 | #include "bulkload.h" |
0a8d74d6 | 28 | #include "quotacheck.h" |
2bd0ea18 | 29 | |
2bd0ea18 NS |
30 | /* |
31 | * option tables for getsubopt calls | |
32 | */ | |
33 | ||
34 | /* | |
4af916f8 | 35 | * -o: user-supplied override options |
2bd0ea18 | 36 | */ |
98884b66 DW |
37 | enum o_opt_nums { |
38 | ASSUME_XFS = 0, | |
39 | IHASH_SIZE, | |
40 | BHASH_SIZE, | |
41 | AG_STRIDE, | |
42 | FORCE_GEO, | |
43 | PHASE2_THREADS, | |
49031e66 DW |
44 | BLOAD_LEAF_SLACK, |
45 | BLOAD_NODE_SLACK, | |
0a8d74d6 | 46 | NOQUOTA, |
98884b66 DW |
47 | O_MAX_OPTS, |
48 | }; | |
49 | ||
8b8a6b02 | 50 | static char *o_opts[] = { |
98884b66 DW |
51 | [ASSUME_XFS] = "assume_xfs", |
52 | [IHASH_SIZE] = "ihash", | |
53 | [BHASH_SIZE] = "bhash", | |
54 | [AG_STRIDE] = "ag_stride", | |
55 | [FORCE_GEO] = "force_geometry", | |
56 | [PHASE2_THREADS] = "phase2_threads", | |
49031e66 DW |
57 | [BLOAD_LEAF_SLACK] = "debug_bload_leaf_slack", |
58 | [BLOAD_NODE_SLACK] = "debug_bload_node_slack", | |
0a8d74d6 | 59 | [NOQUOTA] = "noquota", |
98884b66 | 60 | [O_MAX_OPTS] = NULL, |
2bd0ea18 NS |
61 | }; |
62 | ||
4af916f8 BN |
63 | /* |
64 | * -c: conversion options | |
65 | */ | |
98884b66 DW |
66 | enum c_opt_nums { |
67 | CONVERT_LAZY_COUNT = 0, | |
49c226a5 | 68 | CONVERT_INOBTCOUNT, |
63bbaacf | 69 | CONVERT_BIGTIME, |
1b3daa7d | 70 | CONVERT_NREXT64, |
98884b66 DW |
71 | C_MAX_OPTS, |
72 | }; | |
73 | ||
8b8a6b02 | 74 | static char *c_opts[] = { |
98884b66 | 75 | [CONVERT_LAZY_COUNT] = "lazycount", |
49c226a5 | 76 | [CONVERT_INOBTCOUNT] = "inobtcount", |
63bbaacf | 77 | [CONVERT_BIGTIME] = "bigtime", |
1b3daa7d | 78 | [CONVERT_NREXT64] = "nrext64", |
98884b66 | 79 | [C_MAX_OPTS] = NULL, |
4af916f8 BN |
80 | }; |
81 | ||
82 | ||
2556c98b | 83 | static int bhash_option_used; |
12be365e | 84 | static long max_mem_specified; /* in megabytes */ |
364a126c | 85 | static int phase2_threads = 32; |
7c3e94a3 | 86 | static bool report_corrected; |
2556c98b | 87 | |
2bd0ea18 NS |
88 | static void |
89 | usage(void) | |
90 | { | |
4af916f8 BN |
91 | do_warn(_( |
92 | "Usage: %s [options] device\n" | |
93 | "\n" | |
94 | "Options:\n" | |
95 | " -f The device is a file\n" | |
96 | " -L Force log zeroing. Do this as a last resort.\n" | |
97 | " -l logdev Specifies the device where the external log resides.\n" | |
98 | " -m maxmem Maximum amount of memory to be used in megabytes.\n" | |
99 | " -n No modify mode, just checks the filesystem for damage.\n" | |
7c3e94a3 | 100 | " (Cannot be used together with -e.)\n" |
4af916f8 BN |
101 | " -P Disables prefetching.\n" |
102 | " -r rtdev Specifies the device where the realtime section resides.\n" | |
103 | " -v Verbose output.\n" | |
104 | " -c subopts Change filesystem parameters - use xfs_admin.\n" | |
105 | " -o subopts Override default behaviour, refer to man page.\n" | |
79e106f0 | 106 | " -t interval Reporting interval in seconds.\n" |
4af916f8 | 107 | " -d Repair dangerously.\n" |
7c3e94a3 JT |
108 | " -e Exit with a non-zero code if any errors were repaired.\n" |
109 | " (Cannot be used together with -n.)\n" | |
4af916f8 | 110 | " -V Reports version and exits.\n"), progname); |
2bd0ea18 NS |
111 | exit(1); |
112 | } | |
113 | ||
2bd0ea18 NS |
114 | char * |
115 | err_string(int err_code) | |
116 | { | |
507f4e33 NS |
117 | static char *err_message[XR_BAD_ERR_CODE]; |
118 | static int done; | |
119 | ||
120 | if (!done) { | |
121 | err_message[XR_OK] = _("no error"); | |
122 | err_message[XR_BAD_MAGIC] = _("bad magic number"); | |
123 | err_message[XR_BAD_BLOCKSIZE] = _("bad blocksize field"); | |
124 | err_message[XR_BAD_BLOCKLOG] = _("bad blocksize log field"); | |
4af916f8 | 125 | err_message[XR_BAD_VERSION] = _("bad or unsupported version"); |
507f4e33 NS |
126 | err_message[XR_BAD_INPROGRESS] = |
127 | _("filesystem mkfs-in-progress bit set"); | |
128 | err_message[XR_BAD_FS_SIZE_DATA] = | |
129 | _("inconsistent filesystem geometry information"); | |
130 | err_message[XR_BAD_INO_SIZE_DATA] = | |
131 | _("bad inode size or inconsistent with number of inodes/block"), | |
132 | err_message[XR_BAD_SECT_SIZE_DATA] = _("bad sector size"); | |
133 | err_message[XR_AGF_GEO_MISMATCH] = | |
134 | _("AGF geometry info conflicts with filesystem geometry"); | |
135 | err_message[XR_AGI_GEO_MISMATCH] = | |
136 | _("AGI geometry info conflicts with filesystem geometry"); | |
137 | err_message[XR_SB_GEO_MISMATCH] = | |
138 | _("AG superblock geometry info conflicts with filesystem geometry"); | |
139 | err_message[XR_EOF] = _("attempted to perform I/O beyond EOF"); | |
140 | err_message[XR_BAD_RT_GEO_DATA] = | |
141 | _("inconsistent filesystem geometry in realtime filesystem component"); | |
142 | err_message[XR_BAD_INO_MAX_PCT] = | |
143 | _("maximum indicated percentage of inodes > 100%"); | |
144 | err_message[XR_BAD_INO_ALIGN] = | |
145 | _("inconsistent inode alignment value"); | |
146 | err_message[XR_INSUFF_SEC_SB] = | |
147 | _("not enough secondary superblocks with matching geometry"); | |
148 | err_message[XR_BAD_SB_UNIT] = | |
149 | _("bad stripe unit in superblock"); | |
150 | err_message[XR_BAD_SB_WIDTH] = | |
151 | _("bad stripe width in superblock"); | |
152 | err_message[XR_BAD_SVN] = | |
153 | _("bad shared version number in superblock"); | |
88f364a9 DC |
154 | err_message[XR_BAD_CRC] = |
155 | _("bad CRC in superblock"); | |
02b56f87 DW |
156 | err_message[XR_BAD_DIR_SIZE_DATA] = |
157 | _("inconsistent directory geometry information"); | |
eb9cee60 DW |
158 | err_message[XR_BAD_LOG_GEOMETRY] = |
159 | _("inconsistent log geometry information"); | |
507f4e33 NS |
160 | done = 1; |
161 | } | |
162 | ||
2bd0ea18 | 163 | if (err_code < XR_OK || err_code >= XR_BAD_ERR_CODE) |
507f4e33 | 164 | do_abort(_("bad error code - %d\n"), err_code); |
2bd0ea18 NS |
165 | |
166 | return(err_message[err_code]); | |
167 | } | |
168 | ||
169 | static void | |
170 | noval(char opt, char *tbl[], int idx) | |
171 | { | |
507f4e33 | 172 | do_warn(_("-%c %s option cannot have a value\n"), opt, tbl[idx]); |
2bd0ea18 NS |
173 | usage(); |
174 | } | |
175 | ||
176 | static void | |
177 | respec(char opt, char *tbl[], int idx) | |
178 | { | |
179 | do_warn("-%c ", opt); | |
180 | if (tbl) | |
181 | do_warn("%s ", tbl[idx]); | |
507f4e33 | 182 | do_warn(_("option respecified\n")); |
2bd0ea18 NS |
183 | usage(); |
184 | } | |
185 | ||
186 | static void | |
187 | unknown(char opt, char *s) | |
188 | { | |
507f4e33 | 189 | do_warn(_("unknown option -%c %s\n"), opt, s); |
2bd0ea18 NS |
190 | usage(); |
191 | } | |
192 | ||
193 | /* | |
194 | * sets only the global argument flags and variables | |
195 | */ | |
8b8a6b02 | 196 | static void |
2bd0ea18 NS |
197 | process_args(int argc, char **argv) |
198 | { | |
199 | char *p; | |
200 | int c; | |
201 | ||
202 | log_spec = 0; | |
203 | fs_is_dirty = 0; | |
204 | verbose = 0; | |
205 | no_modify = 0; | |
c781939c | 206 | dangerously = 0; |
2bd0ea18 | 207 | isa_file = 0; |
d321ceac | 208 | zap_log = 0; |
2bd0ea18 | 209 | dumpcore = 0; |
0f012a4c | 210 | full_ino_ex_data = 0; |
2bd0ea18 NS |
211 | force_geo = 0; |
212 | assume_xfs = 0; | |
6bf4721d | 213 | copied_sunit = 0; |
2bd0ea18 NS |
214 | sb_inoalignmt = 0; |
215 | sb_unit = 0; | |
216 | sb_width = 0; | |
add3cb90 | 217 | ag_stride = 0; |
2556c98b | 218 | thread_count = 1; |
06fbdda9 | 219 | report_interval = PROG_RPT_DEFAULT; |
7c3e94a3 | 220 | report_corrected = false; |
2bd0ea18 NS |
221 | |
222 | /* | |
223 | * XXX have to add suboption processing here | |
224 | * attributes, quotas, nlinks, aligned_inos, sb_fbits | |
225 | */ | |
7c3e94a3 | 226 | while ((c = getopt(argc, argv, "c:o:fl:m:r:LnDvVdPet:")) != EOF) { |
2bd0ea18 NS |
227 | switch (c) { |
228 | case 'D': | |
229 | dumpcore = 1; | |
230 | break; | |
231 | case 'o': | |
232 | p = optarg; | |
233 | while (*p != '\0') { | |
234 | char *val; | |
235 | ||
ab870d0e | 236 | switch (getsubopt(&p, o_opts, &val)) { |
2bd0ea18 NS |
237 | case ASSUME_XFS: |
238 | if (val) | |
239 | noval('o', o_opts, ASSUME_XFS); | |
240 | if (assume_xfs) | |
241 | respec('o', o_opts, ASSUME_XFS); | |
242 | assume_xfs = 1; | |
243 | break; | |
9f38f08d | 244 | case IHASH_SIZE: |
3a19fb7d CH |
245 | do_warn( |
246 | _("-o ihash option has been removed and will be ignored\n")); | |
9f38f08d MV |
247 | break; |
248 | case BHASH_SIZE: | |
12be365e BN |
249 | if (max_mem_specified) |
250 | do_abort( | |
3a19fb7d | 251 | _("-o bhash option cannot be used with -m option\n")); |
1f8480b6 DW |
252 | if (!val) |
253 | do_abort( | |
254 | _("-o bhash requires a parameter\n")); | |
d03b73d2 | 255 | errno = 0; |
5e656dbb | 256 | libxfs_bhash_size = (int)strtol(val, NULL, 0); |
d03b73d2 AA |
257 | if (errno) |
258 | do_abort( | |
259 | _("-o bhash invalid parameter: %s\n"), strerror(errno)); | |
2556c98b | 260 | bhash_option_used = 1; |
cb5b3ef4 | 261 | break; |
add3cb90 | 262 | case AG_STRIDE: |
1f8480b6 DW |
263 | if (!val) |
264 | do_abort( | |
265 | _("-o ag_stride requires a parameter\n")); | |
d03b73d2 | 266 | errno = 0; |
5e656dbb | 267 | ag_stride = (int)strtol(val, NULL, 0); |
d03b73d2 AA |
268 | if (errno) |
269 | do_abort( | |
270 | _("-o ag_stride invalid parameter: %s\n"), strerror(errno)); | |
3b6ac903 | 271 | break; |
d4dd6ab5 CH |
272 | case FORCE_GEO: |
273 | if (val) | |
274 | noval('o', o_opts, FORCE_GEO); | |
275 | if (force_geo) | |
276 | respec('o', o_opts, FORCE_GEO); | |
277 | force_geo = 1; | |
278 | break; | |
364a126c | 279 | case PHASE2_THREADS: |
1f8480b6 DW |
280 | if (!val) |
281 | do_abort( | |
282 | _("-o phase2_threads requires a parameter\n")); | |
d03b73d2 | 283 | errno = 0; |
364a126c | 284 | phase2_threads = (int)strtol(val, NULL, 0); |
d03b73d2 AA |
285 | if (errno) |
286 | do_abort( | |
287 | _("-o phase2_threads invalid parameter: %s\n"), strerror(errno)); | |
364a126c | 288 | break; |
49031e66 DW |
289 | case BLOAD_LEAF_SLACK: |
290 | if (!val) | |
291 | do_abort( | |
292 | _("-o debug_bload_leaf_slack requires a parameter\n")); | |
d03b73d2 | 293 | errno = 0; |
49031e66 | 294 | bload_leaf_slack = (int)strtol(val, NULL, 0); |
d03b73d2 AA |
295 | if (errno) |
296 | do_abort( | |
297 | _("-o debug_bload_leaf_slack invalid parameter: %s\n"), strerror(errno)); | |
49031e66 DW |
298 | break; |
299 | case BLOAD_NODE_SLACK: | |
300 | if (!val) | |
301 | do_abort( | |
302 | _("-o debug_bload_node_slack requires a parameter\n")); | |
d03b73d2 | 303 | errno = 0; |
49031e66 | 304 | bload_node_slack = (int)strtol(val, NULL, 0); |
d03b73d2 AA |
305 | if (errno) |
306 | do_abort( | |
307 | _("-o debug_bload_node_slack invalid parameter: %s\n"), strerror(errno)); | |
49031e66 | 308 | break; |
0a8d74d6 DW |
309 | case NOQUOTA: |
310 | quotacheck_skip(); | |
311 | break; | |
2bd0ea18 NS |
312 | default: |
313 | unknown('o', val); | |
314 | break; | |
315 | } | |
316 | } | |
317 | break; | |
4af916f8 BN |
318 | case 'c': |
319 | p = optarg; | |
320 | while (*p) { | |
321 | char *val; | |
322 | ||
ab870d0e | 323 | switch (getsubopt(&p, c_opts, &val)) { |
4af916f8 | 324 | case CONVERT_LAZY_COUNT: |
1f8480b6 DW |
325 | if (!val) |
326 | do_abort( | |
327 | _("-c lazycount requires a parameter\n")); | |
d03b73d2 | 328 | errno = 0; |
5e656dbb | 329 | lazy_count = (int)strtol(val, NULL, 0); |
d03b73d2 AA |
330 | if (errno) |
331 | do_abort( | |
332 | _("-o lazycount invalid parameter: %s\n"), strerror(errno)); | |
4af916f8 BN |
333 | convert_lazy_count = 1; |
334 | break; | |
49c226a5 DW |
335 | case CONVERT_INOBTCOUNT: |
336 | if (!val) | |
337 | do_abort( | |
338 | _("-c inobtcount requires a parameter\n")); | |
339 | if (strtol(val, NULL, 0) != 1) | |
340 | do_abort( | |
341 | _("-c inobtcount only supports upgrades\n")); | |
342 | add_inobtcount = true; | |
343 | break; | |
63bbaacf DW |
344 | case CONVERT_BIGTIME: |
345 | if (!val) | |
346 | do_abort( | |
347 | _("-c bigtime requires a parameter\n")); | |
348 | if (strtol(val, NULL, 0) != 1) | |
349 | do_abort( | |
350 | _("-c bigtime only supports upgrades\n")); | |
351 | add_bigtime = true; | |
352 | break; | |
1b3daa7d CB |
353 | case CONVERT_NREXT64: |
354 | if (!val) | |
355 | do_abort( | |
356 | _("-c nrext64 requires a parameter\n")); | |
357 | if (strtol(val, NULL, 0) != 1) | |
358 | do_abort( | |
359 | _("-c nrext64 only supports upgrades\n")); | |
360 | add_nrext64 = true; | |
361 | break; | |
4af916f8 BN |
362 | default: |
363 | unknown('c', val); | |
364 | break; | |
365 | } | |
366 | } | |
367 | break; | |
2bd0ea18 NS |
368 | case 'l': |
369 | log_name = optarg; | |
370 | log_spec = 1; | |
371 | break; | |
42a564ab ES |
372 | case 'r': |
373 | rt_name = optarg; | |
374 | rt_spec = 1; | |
375 | break; | |
2bd0ea18 NS |
376 | case 'f': |
377 | isa_file = 1; | |
378 | break; | |
12be365e BN |
379 | case 'm': |
380 | if (bhash_option_used) | |
381 | do_abort(_("-m option cannot be used with " | |
382 | "-o bhash option\n")); | |
d03b73d2 | 383 | errno = 0; |
5e656dbb | 384 | max_mem_specified = strtol(optarg, NULL, 0); |
d03b73d2 AA |
385 | if (errno) |
386 | do_abort( | |
387 | _("%s: invalid memory amount: %s\n"), optarg, strerror(errno)); | |
12be365e | 388 | break; |
d321ceac NS |
389 | case 'L': |
390 | zap_log = 1; | |
391 | break; | |
2bd0ea18 NS |
392 | case 'n': |
393 | no_modify = 1; | |
394 | break; | |
6089b6f0 NS |
395 | case 'd': |
396 | dangerously = 1; | |
397 | break; | |
2bd0ea18 | 398 | case 'v': |
3b6ac903 | 399 | verbose++; |
2bd0ea18 NS |
400 | break; |
401 | case 'V': | |
507f4e33 | 402 | printf(_("%s version %s\n"), progname, VERSION); |
3d98fe63 | 403 | exit(0); |
cb5b3ef4 | 404 | case 'P': |
2556c98b | 405 | do_prefetch = 0; |
3b6ac903 | 406 | break; |
06fbdda9 | 407 | case 't': |
d03b73d2 | 408 | errno = 0; |
fcac184c | 409 | report_interval = strtol(optarg, NULL, 0); |
d03b73d2 AA |
410 | if (errno) |
411 | do_abort( | |
412 | _("%s: invalid interval: %s\n"), optarg, strerror(errno)); | |
06fbdda9 | 413 | break; |
7c3e94a3 JT |
414 | case 'e': |
415 | report_corrected = true; | |
416 | break; | |
78aeaffd | 417 | default: |
2bd0ea18 NS |
418 | usage(); |
419 | } | |
420 | } | |
421 | ||
422 | if (argc - optind != 1) | |
423 | usage(); | |
424 | ||
425 | if ((fs_name = argv[optind]) == NULL) | |
426 | usage(); | |
7c3e94a3 JT |
427 | |
428 | if (report_corrected && no_modify) | |
429 | usage(); | |
7007b99f DW |
430 | |
431 | p = getenv("XFS_REPAIR_FAIL_AFTER_PHASE"); | |
d03b73d2 AA |
432 | if (p) { |
433 | errno = 0; | |
7007b99f | 434 | fail_after_phase = (int)strtol(p, NULL, 0); |
d03b73d2 AA |
435 | if (errno) |
436 | do_abort( | |
437 | _("%s: invalid phase in XFS_REPAIR_FAIL_AFTER_PHASE: %s\n"), | |
438 | p, strerror(errno)); | |
439 | } | |
2bd0ea18 NS |
440 | } |
441 | ||
b1559967 | 442 | void __attribute__((noreturn)) |
2bd0ea18 NS |
443 | do_error(char const *msg, ...) |
444 | { | |
445 | va_list args; | |
446 | ||
507f4e33 | 447 | fprintf(stderr, _("\nfatal error -- ")); |
2bd0ea18 NS |
448 | |
449 | va_start(args, msg); | |
079afa09 CH |
450 | vfprintf(stderr, msg, args); |
451 | if (dumpcore) | |
452 | abort(); | |
453 | exit(1); | |
2bd0ea18 NS |
454 | } |
455 | ||
456 | /* | |
457 | * like do_error, only the error is internal, no system | |
458 | * error so no oserror processing | |
459 | */ | |
b1559967 | 460 | void __attribute__((noreturn)) |
2bd0ea18 NS |
461 | do_abort(char const *msg, ...) |
462 | { | |
463 | va_list args; | |
464 | ||
465 | va_start(args, msg); | |
079afa09 CH |
466 | vfprintf(stderr, msg, args); |
467 | if (dumpcore) | |
468 | abort(); | |
469 | exit(1); | |
2bd0ea18 NS |
470 | } |
471 | ||
472 | void | |
473 | do_warn(char const *msg, ...) | |
474 | { | |
475 | va_list args; | |
476 | ||
477 | fs_is_dirty = 1; | |
478 | ||
479 | va_start(args, msg); | |
079afa09 | 480 | vfprintf(stderr, msg, args); |
2bd0ea18 NS |
481 | va_end(args); |
482 | } | |
483 | ||
484 | /* no formatting */ | |
485 | ||
486 | void | |
487 | do_log(char const *msg, ...) | |
488 | { | |
489 | va_list args; | |
490 | ||
491 | va_start(args, msg); | |
079afa09 | 492 | vfprintf(stderr, msg, args); |
2bd0ea18 NS |
493 | va_end(args); |
494 | } | |
495 | ||
a3e126aa DW |
496 | /* Make sure a fixed-location inode is where it should be. */ |
497 | static void | |
498 | validate_sb_ino( | |
499 | xfs_ino_t *ino, | |
500 | xfs_ino_t expected_ino, | |
501 | const char *tag) | |
502 | { | |
503 | if (*ino == expected_ino) | |
504 | return; | |
505 | ||
506 | do_warn( | |
507 | _("sb %s inode value %" PRIu64 " %sinconsistent with calculated value %"PRIu64"\n"), | |
508 | tag, *ino, *ino == NULLFSINO ? "(NULLFSINO) " : "", | |
509 | expected_ino); | |
510 | ||
511 | if (!no_modify) | |
512 | do_warn( | |
513 | _("resetting superblock %s inode pointer to %"PRIu64"\n"), | |
514 | tag, expected_ino); | |
515 | else | |
516 | do_warn( | |
517 | _("would reset superblock %s inode pointer to %"PRIu64"\n"), | |
518 | tag, expected_ino); | |
519 | ||
520 | /* | |
521 | * Just set the value -- safe since the superblock doesn't get flushed | |
522 | * out if no_modify is set. | |
523 | */ | |
524 | *ino = expected_ino; | |
525 | } | |
526 | ||
ded6b558 DW |
527 | /* Does the root directory inode look like a plausible root directory? */ |
528 | static bool | |
529 | has_plausible_rootdir( | |
530 | struct xfs_mount *mp) | |
531 | { | |
532 | struct xfs_inode *ip; | |
533 | xfs_ino_t ino; | |
534 | int error; | |
535 | bool ret = false; | |
536 | ||
1fecabf9 | 537 | error = -libxfs_iget(mp, NULL, mp->m_sb.sb_rootino, 0, &ip); |
ded6b558 DW |
538 | if (error) |
539 | goto out; | |
540 | if (!S_ISDIR(VFS_I(ip)->i_mode)) | |
541 | goto out_rele; | |
542 | ||
543 | error = -libxfs_dir_lookup(NULL, ip, &xfs_name_dotdot, &ino, NULL); | |
544 | if (error) | |
545 | goto out_rele; | |
546 | ||
547 | /* The root directory '..' entry points to the directory. */ | |
548 | if (ino == mp->m_sb.sb_rootino) | |
549 | ret = true; | |
550 | ||
551 | out_rele: | |
552 | libxfs_irele(ip); | |
553 | out: | |
554 | return ret; | |
555 | } | |
556 | ||
306b450b DW |
557 | /* |
558 | * If any of the secondary SBs contain a *correct* value for sunit, write that | |
559 | * back to the primary superblock. | |
560 | */ | |
561 | static void | |
562 | guess_correct_sunit( | |
563 | struct xfs_mount *mp) | |
564 | { | |
565 | struct xfs_sb sb; | |
566 | struct xfs_buf *bp; | |
567 | xfs_ino_t calc_rootino = NULLFSINO; | |
568 | xfs_agnumber_t agno; | |
569 | unsigned int new_sunit; | |
570 | unsigned int sunit_guess; | |
571 | int error; | |
572 | ||
573 | /* Try reading secondary supers to see if we find a good sb_unit. */ | |
574 | for (agno = 1; agno < mp->m_sb.sb_agcount; agno++) { | |
575 | error = -libxfs_sb_read_secondary(mp, NULL, agno, &bp); | |
576 | if (error) | |
577 | continue; | |
d6522f1d | 578 | libxfs_sb_from_disk(&sb, bp->b_addr); |
e02ba985 | 579 | libxfs_buf_relse(bp); |
306b450b DW |
580 | |
581 | calc_rootino = libxfs_ialloc_calc_rootino(mp, sb.sb_unit); | |
582 | if (calc_rootino == mp->m_sb.sb_rootino) | |
583 | break; | |
584 | } | |
585 | ||
586 | /* If we found a reasonable value, log where we found it. */ | |
587 | if (calc_rootino == mp->m_sb.sb_rootino) { | |
588 | do_warn(_("AG %u superblock contains plausible sb_unit value\n"), | |
589 | agno); | |
590 | new_sunit = sb.sb_unit; | |
591 | goto fix; | |
592 | } | |
593 | ||
594 | /* Try successive powers of two. */ | |
595 | for (sunit_guess = 1; | |
596 | sunit_guess <= XFS_AG_MAX_BLOCKS(mp->m_sb.sb_blocklog); | |
597 | sunit_guess *= 2) { | |
598 | calc_rootino = libxfs_ialloc_calc_rootino(mp, sunit_guess); | |
599 | if (calc_rootino == mp->m_sb.sb_rootino) | |
600 | break; | |
601 | } | |
602 | ||
603 | /* If we found a reasonable value, log where we found it. */ | |
604 | if (calc_rootino == mp->m_sb.sb_rootino) { | |
605 | do_warn(_("Found an sb_unit value that looks plausible\n")); | |
606 | new_sunit = sunit_guess; | |
607 | goto fix; | |
608 | } | |
609 | ||
610 | do_warn(_("Could not estimate a plausible sb_unit value\n")); | |
611 | return; | |
612 | ||
613 | fix: | |
614 | if (!no_modify) | |
615 | do_warn(_("Resetting sb_unit to %u\n"), new_sunit); | |
616 | else | |
617 | do_warn(_("Would reset sb_unit to %u\n"), new_sunit); | |
618 | ||
619 | /* | |
620 | * Just set the value -- safe since the superblock doesn't get flushed | |
621 | * out if no_modify is set. | |
622 | */ | |
623 | mp->m_sb.sb_unit = new_sunit; | |
624 | ||
625 | /* Make sure that swidth is still a multiple of sunit. */ | |
626 | if (mp->m_sb.sb_width % mp->m_sb.sb_unit == 0) | |
627 | return; | |
628 | ||
629 | if (!no_modify) | |
630 | do_warn(_("Resetting sb_width to %u\n"), new_sunit); | |
631 | else | |
632 | do_warn(_("Would reset sb_width to %u\n"), new_sunit); | |
633 | } | |
634 | ||
90b2397e DW |
635 | /* |
636 | * Make sure that the first 3 inodes in the filesystem are the root directory, | |
637 | * the realtime bitmap, and the realtime summary, in that order. | |
638 | */ | |
8b8a6b02 | 639 | static void |
90b2397e DW |
640 | calc_mkfs( |
641 | struct xfs_mount *mp) | |
2bd0ea18 | 642 | { |
90b2397e | 643 | xfs_ino_t rootino; |
649bfa9a | 644 | |
90b2397e | 645 | rootino = libxfs_ialloc_calc_rootino(mp, mp->m_sb.sb_unit); |
d4dd6ab5 | 646 | |
ded6b558 DW |
647 | /* |
648 | * If the root inode isn't where we think it is, check its plausibility | |
649 | * as a root directory. It's possible that somebody changed sunit | |
650 | * since the filesystem was created, which can change the value of the | |
651 | * above computation. Don't blow up the root directory if this is the | |
652 | * case. | |
653 | */ | |
654 | if (mp->m_sb.sb_rootino != rootino && has_plausible_rootdir(mp)) { | |
655 | do_warn( | |
656 | _("sb root inode value %" PRIu64 " valid but in unaligned location (expected %"PRIu64") possibly due to sunit change\n"), | |
657 | mp->m_sb.sb_rootino, rootino); | |
306b450b | 658 | guess_correct_sunit(mp); |
ded6b558 DW |
659 | rootino = mp->m_sb.sb_rootino; |
660 | } | |
661 | ||
90b2397e | 662 | validate_sb_ino(&mp->m_sb.sb_rootino, rootino, |
a3e126aa | 663 | _("root")); |
90b2397e | 664 | validate_sb_ino(&mp->m_sb.sb_rbmino, rootino + 1, |
a3e126aa | 665 | _("realtime bitmap")); |
90b2397e | 666 | validate_sb_ino(&mp->m_sb.sb_rsumino, rootino + 2, |
a3e126aa | 667 | _("realtime summary")); |
2bd0ea18 NS |
668 | } |
669 | ||
1926558d BF |
670 | /* |
671 | * v5 superblock metadata track the LSN of last modification and thus require | |
672 | * that the current LSN is always moving forward. The current LSN is reset if | |
673 | * the log has been cleared, which puts the log behind parts of the filesystem | |
674 | * on-disk and can disrupt log recovery. | |
675 | * | |
676 | * We have tracked the maximum LSN of every piece of metadata that has been read | |
677 | * in via the read verifiers. Compare the max LSN with the log and if the log is | |
678 | * behind, bump the cycle number and reformat the log. | |
679 | */ | |
680 | static void | |
681 | format_log_max_lsn( | |
682 | struct xfs_mount *mp) | |
683 | { | |
684 | struct xlog *log = mp->m_log; | |
685 | int max_cycle; | |
686 | int max_block; | |
687 | int new_cycle; | |
688 | xfs_daddr_t logstart; | |
689 | xfs_daddr_t logblocks; | |
690 | int logversion; | |
691 | ||
2660e653 | 692 | if (!xfs_has_crc(mp)) |
1926558d BF |
693 | return; |
694 | ||
695 | /* | |
696 | * If the log is ahead of the highest metadata LSN we've seen, we're | |
697 | * safe and there's nothing to do. | |
698 | */ | |
699 | max_cycle = CYCLE_LSN(libxfs_max_lsn); | |
700 | max_block = BLOCK_LSN(libxfs_max_lsn); | |
701 | if (max_cycle < log->l_curr_cycle || | |
702 | (max_cycle == log->l_curr_cycle && max_block < log->l_curr_block)) | |
703 | return; | |
704 | ||
705 | /* | |
706 | * Going to the next cycle should be sufficient but we bump by a few | |
707 | * counts to help cover any metadata LSNs we could have missed. | |
708 | */ | |
709 | new_cycle = max_cycle + 3; | |
710 | logstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart); | |
711 | logblocks = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); | |
2660e653 | 712 | logversion = xfs_has_logv2(mp) ? 2 : 1; |
1926558d BF |
713 | |
714 | do_warn(_("Maximum metadata LSN (%d:%d) is ahead of log (%d:%d).\n"), | |
715 | max_cycle, max_block, log->l_curr_cycle, log->l_curr_block); | |
716 | ||
717 | if (no_modify) { | |
718 | do_warn(_("Would format log to cycle %d.\n"), new_cycle); | |
719 | return; | |
720 | } | |
721 | ||
722 | do_warn(_("Format log to cycle %d.\n"), new_cycle); | |
1c12a814 BF |
723 | libxfs_log_clear(log->l_dev, NULL, logstart, logblocks, |
724 | &mp->m_sb.sb_uuid, logversion, mp->m_sb.sb_logsunit, | |
571a78a7 | 725 | XLOG_FMT, new_cycle, true); |
1926558d BF |
726 | } |
727 | ||
4a32b9e9 DC |
728 | /* |
729 | * mkfs increases the AG count for "multidisk" configurations, we want | |
730 | * to target these for an increase in thread count. Hence check the superlock | |
731 | * geometry information to determine if mkfs considered this a multidisk | |
732 | * configuration. | |
733 | */ | |
734 | static bool | |
735 | is_multidisk_filesystem( | |
736 | struct xfs_mount *mp) | |
737 | { | |
738 | struct xfs_sb *sbp = &mp->m_sb; | |
739 | ||
740 | /* High agcount filesystems are always considered "multidisk" */ | |
741 | if (sbp->sb_agcount >= XFS_MULTIDISK_AGCOUNT) | |
742 | return true; | |
743 | ||
744 | /* | |
745 | * If it doesn't have a sunit/swidth, mkfs didn't consider it a | |
746 | * multi-disk array, so we don't either. | |
747 | */ | |
748 | if (!sbp->sb_unit) | |
749 | return false; | |
750 | ||
751 | ASSERT(sbp->sb_width); | |
752 | return true; | |
753 | } | |
754 | ||
28a0a30f ZL |
755 | /* |
756 | * if the sector size of the filesystem we are trying to repair is | |
757 | * smaller than that of the underlying filesystem (i.e. we are repairing | |
758 | * an image), the we have to turn off direct IO because we cannot do IO | |
759 | * smaller than the host filesystem's sector size. | |
760 | */ | |
761 | static void | |
762 | check_fs_vs_host_sectsize( | |
763 | struct xfs_sb *sb) | |
764 | { | |
28cd682b | 765 | int ret; |
28a0a30f | 766 | long old_flags; |
9612817d | 767 | struct xfs_fsop_geom geom = { 0 }; |
28a0a30f | 768 | |
fc83c757 | 769 | ret = -xfrog_geometry(x.data.fd, &geom); |
9612817d | 770 | if (ret) { |
28a0a30f ZL |
771 | do_log(_("Cannot get host filesystem geometry.\n" |
772 | "Repair may fail if there is a sector size mismatch between\n" | |
773 | "the image and the host filesystem.\n")); | |
774 | geom.sectsize = BBSIZE; | |
775 | } | |
776 | ||
777 | if (sb->sb_sectsize < geom.sectsize) { | |
fc83c757 CH |
778 | old_flags = fcntl(x.data.fd, F_GETFL, 0); |
779 | if (fcntl(x.data.fd, F_SETFL, old_flags & ~O_DIRECT) < 0) { | |
28a0a30f ZL |
780 | do_warn(_( |
781 | "Sector size on host filesystem larger than image sector size.\n" | |
782 | "Cannot turn off direct IO, so exiting.\n")); | |
783 | exit(1); | |
784 | } | |
785 | } | |
786 | } | |
787 | ||
945c7341 DW |
788 | /* |
789 | * If we set up a writeback function to set NEEDSREPAIR while the filesystem is | |
790 | * dirty, there's a chance that calling libxfs_getsb could deadlock the buffer | |
791 | * cache while trying to get the primary sb buffer if the first non-sb write to | |
792 | * the filesystem is the result of a cache shake. Retain a reference to the | |
793 | * primary sb buffer to avoid all that. | |
794 | */ | |
795 | static struct xfs_buf *primary_sb_bp; /* buffer for superblock */ | |
796 | ||
797 | int | |
798 | retain_primary_sb( | |
799 | struct xfs_mount *mp) | |
800 | { | |
801 | int error; | |
802 | ||
803 | error = -libxfs_buf_read(mp->m_ddev_targp, XFS_SB_DADDR, | |
804 | XFS_FSS_TO_BB(mp, 1), 0, &primary_sb_bp, | |
805 | &xfs_sb_buf_ops); | |
806 | if (error) | |
807 | return error; | |
808 | ||
809 | libxfs_buf_unlock(primary_sb_bp); | |
810 | return 0; | |
811 | } | |
812 | ||
813 | static void | |
814 | drop_primary_sb(void) | |
815 | { | |
816 | if (!primary_sb_bp) | |
817 | return; | |
818 | ||
819 | libxfs_buf_lock(primary_sb_bp); | |
820 | libxfs_buf_relse(primary_sb_bp); | |
821 | primary_sb_bp = NULL; | |
822 | } | |
823 | ||
824 | static int | |
825 | get_primary_sb( | |
826 | struct xfs_mount *mp, | |
827 | struct xfs_buf **bpp) | |
828 | { | |
829 | int error; | |
830 | ||
831 | *bpp = NULL; | |
832 | ||
833 | if (!primary_sb_bp) { | |
834 | error = retain_primary_sb(mp); | |
835 | if (error) | |
836 | return error; | |
837 | } | |
838 | ||
839 | libxfs_buf_lock(primary_sb_bp); | |
840 | xfs_buf_hold(primary_sb_bp); | |
841 | *bpp = primary_sb_bp; | |
842 | return 0; | |
843 | } | |
844 | ||
a7348c58 | 845 | /* Clear needsrepair after a successful repair run. */ |
b6fef47a | 846 | static void |
a7348c58 DW |
847 | clear_needsrepair( |
848 | struct xfs_mount *mp) | |
849 | { | |
850 | struct xfs_buf *bp; | |
851 | int error; | |
852 | ||
853 | /* | |
854 | * If we're going to clear NEEDSREPAIR, we need to make absolutely sure | |
855 | * that everything is ok with the ondisk filesystem. Make sure any | |
856 | * dirty buffers are sent to disk and that the disks have persisted | |
857 | * writes to stable storage. If that fails, leave NEEDSREPAIR in | |
858 | * place. | |
859 | */ | |
860 | error = -libxfs_flush_mount(mp); | |
861 | if (error) { | |
862 | do_warn( | |
863 | _("Cannot clear needsrepair due to flush failure, err=%d.\n"), | |
864 | error); | |
945c7341 | 865 | goto drop; |
a7348c58 DW |
866 | } |
867 | ||
868 | /* Clear needsrepair from the superblock. */ | |
945c7341 DW |
869 | error = get_primary_sb(mp, &bp); |
870 | if (error) { | |
a7348c58 | 871 | do_warn( |
945c7341 | 872 | _("Cannot clear needsrepair from primary super, err=%d.\n"), error); |
a7348c58 DW |
873 | } else { |
874 | mp->m_sb.sb_features_incompat &= | |
875 | ~XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR; | |
876 | libxfs_sb_to_disk(bp->b_addr, &mp->m_sb); | |
877 | libxfs_buf_mark_dirty(bp); | |
878 | } | |
879 | if (bp) | |
880 | libxfs_buf_relse(bp); | |
945c7341 DW |
881 | drop: |
882 | drop_primary_sb(); | |
a7348c58 DW |
883 | } |
884 | ||
3b7667cb DW |
885 | static void |
886 | update_sb_crc_only( | |
887 | struct xfs_buf *bp) | |
888 | { | |
889 | xfs_buf_update_cksum(bp, XFS_SB_CRC_OFF); | |
890 | } | |
891 | ||
892 | /* Forcibly write the primary superblock with the NEEDSREPAIR flag set. */ | |
893 | static void | |
894 | force_needsrepair( | |
895 | struct xfs_mount *mp) | |
896 | { | |
897 | struct xfs_buf_ops fake_ops; | |
898 | struct xfs_buf *bp; | |
899 | int error; | |
900 | ||
2660e653 | 901 | if (!xfs_has_crc(mp) || |
3b7667cb DW |
902 | xfs_sb_version_needsrepair(&mp->m_sb)) |
903 | return; | |
904 | ||
945c7341 DW |
905 | error = get_primary_sb(mp, &bp); |
906 | if (error) { | |
3b7667cb | 907 | do_log( |
945c7341 | 908 | _("couldn't get superblock to set needsrepair, err=%d\n"), error); |
3b7667cb DW |
909 | } else { |
910 | /* | |
911 | * It's possible that we need to set NEEDSREPAIR before we've | |
912 | * had a chance to fix the summary counters in the primary sb. | |
913 | * With the exception of those counters, phase 1 already | |
914 | * ensured that the geometry makes sense. | |
915 | * | |
916 | * Bad summary counters in the primary super can cause the | |
917 | * write verifier to fail, so substitute a dummy that only sets | |
918 | * the CRC. In the event of a crash, NEEDSREPAIR will prevent | |
919 | * the kernel from mounting our potentially damaged filesystem | |
920 | * until repair is run again, so it's ok to bypass the usual | |
921 | * verification in this one case. | |
922 | */ | |
923 | fake_ops = xfs_sb_buf_ops; /* struct copy */ | |
924 | fake_ops.verify_write = update_sb_crc_only; | |
925 | ||
926 | mp->m_sb.sb_features_incompat |= | |
927 | XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR; | |
928 | libxfs_sb_to_disk(bp->b_addr, &mp->m_sb); | |
929 | ||
930 | /* Force the primary super to disk immediately. */ | |
931 | bp->b_ops = &fake_ops; | |
932 | error = -libxfs_bwrite(bp); | |
933 | bp->b_ops = &xfs_sb_buf_ops; | |
934 | if (error) | |
935 | do_log(_("couldn't force needsrepair, err=%d\n"), error); | |
936 | } | |
937 | if (bp) | |
938 | libxfs_buf_relse(bp); | |
939 | } | |
940 | ||
941 | /* | |
942 | * Intercept the first non-super write to the filesystem so we can set | |
943 | * NEEDSREPAIR to protect the filesystem from mount in case of a crash. | |
944 | */ | |
945 | static void | |
946 | repair_capture_writeback( | |
947 | struct xfs_buf *bp) | |
948 | { | |
949 | struct xfs_mount *mp = bp->b_mount; | |
950 | static pthread_mutex_t wb_mutex = PTHREAD_MUTEX_INITIALIZER; | |
951 | ||
952 | /* | |
953 | * This write hook ignores any buffer that looks like a superblock to | |
954 | * avoid hook recursion when setting NEEDSREPAIR. Higher level code | |
955 | * modifying an sb must control the flag manually. | |
956 | */ | |
f1208396 | 957 | if (bp->b_ops == &xfs_sb_buf_ops || xfs_buf_daddr(bp) == XFS_SB_DADDR) |
3b7667cb DW |
958 | return; |
959 | ||
960 | pthread_mutex_lock(&wb_mutex); | |
961 | ||
962 | /* | |
963 | * If someone else already dropped the hook, then needsrepair has | |
964 | * already been set on the filesystem and we can unlock. | |
965 | */ | |
966 | if (mp->m_buf_writeback_fn != repair_capture_writeback) | |
967 | goto unlock; | |
968 | ||
969 | /* | |
970 | * If we get here, the buffer being written is not a superblock, and | |
971 | * needsrepair needs to be set. The hook is kept in place to plug all | |
972 | * other writes until the sb write finishes. | |
973 | */ | |
974 | force_needsrepair(mp); | |
975 | ||
976 | /* We only set needsrepair once, so clear the hook now. */ | |
977 | mp->m_buf_writeback_fn = NULL; | |
978 | unlock: | |
979 | pthread_mutex_unlock(&wb_mutex); | |
980 | } | |
981 | ||
8e9f22d6 DW |
982 | static inline void |
983 | phase_end(int phase) | |
984 | { | |
985 | timestamp(PHASE_END, phase, NULL); | |
7007b99f DW |
986 | |
987 | /* Fail if someone injected an post-phase error. */ | |
988 | if (fail_after_phase && phase == fail_after_phase) | |
989 | platform_crash(); | |
8e9f22d6 DW |
990 | } |
991 | ||
2bd0ea18 NS |
992 | int |
993 | main(int argc, char **argv) | |
994 | { | |
2bd0ea18 NS |
995 | xfs_mount_t *temp_mp; |
996 | xfs_mount_t *mp; | |
167137fe | 997 | struct xfs_buf *sbp; |
2bd0ea18 | 998 | xfs_mount_t xfs_m; |
1d6cb115 | 999 | struct xlog log = {0}; |
06fbdda9 | 1000 | char *msgbuf; |
88f364a9 DC |
1001 | struct xfs_sb psb; |
1002 | int rval; | |
e7fd2b6f | 1003 | struct xfs_ino_geometry *igeo; |
14fb3613 | 1004 | int error; |
2bd0ea18 NS |
1005 | |
1006 | progname = basename(argv[0]); | |
507f4e33 NS |
1007 | setlocale(LC_ALL, ""); |
1008 | bindtextdomain(PACKAGE, LOCALEDIR); | |
1009 | textdomain(PACKAGE); | |
beed0dc8 | 1010 | dinode_bmbt_translation_init(); |
2bd0ea18 NS |
1011 | |
1012 | temp_mp = &xfs_m; | |
1013 | setbuf(stdout, NULL); | |
1014 | ||
1015 | process_args(argc, argv); | |
d321ceac | 1016 | xfs_init(&x); |
2bd0ea18 | 1017 | |
2556c98b BN |
1018 | msgbuf = malloc(DURATION_BUF_SIZE); |
1019 | ||
06fbdda9 | 1020 | timestamp(PHASE_START, 0, NULL); |
8e9f22d6 | 1021 | phase_end(0); |
06fbdda9 | 1022 | |
28a0a30f ZL |
1023 | /* -f forces this, but let's be nice and autodetect it, as well. */ |
1024 | if (!isa_file) { | |
28a0a30f ZL |
1025 | struct stat statbuf; |
1026 | ||
fc83c757 | 1027 | if (fstat(x.data.fd, &statbuf) < 0) |
28a0a30f ZL |
1028 | do_warn(_("%s: couldn't stat \"%s\"\n"), |
1029 | progname, fs_name); | |
1030 | else if (S_ISREG(statbuf.st_mode)) | |
1031 | isa_file = 1; | |
1032 | } | |
1033 | ||
1034 | if (isa_file) { | |
1035 | /* Best effort attempt to validate fs vs host sector size */ | |
1036 | rval = get_sb(&psb, 0, XFS_MAX_SECTORSIZE, 0); | |
1037 | if (rval == XR_OK) | |
1038 | check_fs_vs_host_sectsize(&psb); | |
1039 | } | |
1040 | ||
2bd0ea18 NS |
1041 | /* do phase1 to make sure we have a superblock */ |
1042 | phase1(temp_mp); | |
8e9f22d6 | 1043 | phase_end(1); |
2bd0ea18 NS |
1044 | |
1045 | if (no_modify && primary_sb_modified) { | |
507f4e33 NS |
1046 | do_warn(_("Primary superblock would have been modified.\n" |
1047 | "Cannot proceed further in no_modify mode.\n" | |
1048 | "Exiting now.\n")); | |
2bd0ea18 NS |
1049 | exit(1); |
1050 | } | |
1051 | ||
88f364a9 DC |
1052 | rval = get_sb(&psb, 0, XFS_MAX_SECTORSIZE, 0); |
1053 | if (rval != XR_OK) { | |
1054 | do_warn(_("Primary superblock bad after phase 1!\n" | |
1055 | "Exiting now.\n")); | |
1056 | exit(1); | |
1057 | } | |
2bd0ea18 | 1058 | |
f63fd268 | 1059 | /* |
28a0a30f ZL |
1060 | * Now that we have completely validated the superblock, geometry may |
1061 | * have changed; re-check geometry vs the host filesystem geometry | |
f63fd268 | 1062 | */ |
28a0a30f ZL |
1063 | if (isa_file) |
1064 | check_fs_vs_host_sectsize(&psb); | |
88f364a9 | 1065 | |
1d6cb115 BF |
1066 | /* |
1067 | * Prepare the mount structure. Point the log reference to our local | |
1068 | * copy so it's available to the various phases. The log bits are | |
1069 | * initialized in phase 2. | |
1070 | */ | |
88f364a9 | 1071 | memset(&xfs_m, 0, sizeof(xfs_mount_t)); |
ddd9942b | 1072 | mp = libxfs_mount(&xfs_m, &psb, &x, 0); |
2bd0ea18 NS |
1073 | |
1074 | if (!mp) { | |
507f4e33 NS |
1075 | fprintf(stderr, |
1076 | _("%s: cannot repair this filesystem. Sorry.\n"), | |
2bd0ea18 NS |
1077 | progname); |
1078 | exit(1); | |
1079 | } | |
1d6cb115 | 1080 | mp->m_log = &log; |
e7fd2b6f | 1081 | igeo = M_IGEO(mp); |
2bd0ea18 | 1082 | |
23639f77 ES |
1083 | /* Spit out function & line on these corruption macros */ |
1084 | if (verbose > 2) | |
2420d095 | 1085 | xfs_set_reporting_corruption(mp); |
23639f77 | 1086 | |
3b7667cb | 1087 | /* Capture the first writeback so that we can set needsrepair. */ |
2660e653 | 1088 | if (xfs_has_crc(mp)) |
3b7667cb DW |
1089 | mp->m_buf_writeback_fn = repair_capture_writeback; |
1090 | ||
2bd0ea18 NS |
1091 | /* |
1092 | * set XFS-independent status vars from the mount/sb structure | |
1093 | */ | |
1094 | glob_agcount = mp->m_sb.sb_agcount; | |
1095 | ||
1096 | chunks_pblock = mp->m_sb.sb_inopblock / XFS_INODES_PER_CHUNK; | |
5a707ca1 | 1097 | max_symlink_blocks = libxfs_symlink_blocks(mp, XFS_SYMLINK_MAXLEN); |
2bd0ea18 | 1098 | |
0cce4aa1 DC |
1099 | /* |
1100 | * Automatic striding for high agcount filesystems. | |
1101 | * | |
1102 | * More AGs indicates that the filesystem is either large or can handle | |
1103 | * more IO parallelism. Either way, we should try to process multiple | |
1104 | * AGs at a time in such a configuration to try to saturate the | |
1105 | * underlying storage and speed the repair process. Only do this if | |
1106 | * prefetching is enabled. | |
1107 | * | |
1108 | * Given mkfs defaults for 16AGs for "multidisk" configurations, we want | |
1109 | * to target these for an increase in thread count. Hence a stride value | |
1110 | * of 15 is chosen to ensure we get at least 2 AGs being scanned at once | |
1111 | * on such filesystems. | |
12b55baf DC |
1112 | * |
1113 | * Limit the maximum thread count based on the available CPU power that | |
1114 | * is available. If we use too many threads, we might run out of memory | |
1115 | * and CPU power before we run out of IO concurrency. We limit to 8 | |
1116 | * threads/CPU as this is enough threads to saturate a CPU on fast | |
1117 | * devices, yet few enough that it will saturate but won't overload slow | |
1118 | * devices. | |
4a32b9e9 DC |
1119 | * |
1120 | * Multidisk filesystems can handle more IO parallelism so we should try | |
1121 | * to process multiple AGs at a time in such a configuration to try to | |
1122 | * saturate the underlying storage and speed the repair process. Only do | |
1123 | * this if prefetching is enabled. | |
0cce4aa1 | 1124 | */ |
4a32b9e9 DC |
1125 | if (!ag_stride && do_prefetch && is_multidisk_filesystem(mp)) { |
1126 | /* | |
1127 | * For small agcount multidisk systems, just double the | |
1128 | * parallelism. For larger AG count filesystems (32 and above) | |
1129 | * use more parallelism, and linearly increase the parallelism | |
1130 | * with the number of AGs. | |
1131 | */ | |
1132 | ag_stride = min(glob_agcount, XFS_MULTIDISK_AGCOUNT / 2) - 1; | |
1133 | } | |
0cce4aa1 | 1134 | |
add3cb90 | 1135 | if (ag_stride) { |
12b55baf DC |
1136 | int max_threads = platform_nproc() * 8; |
1137 | ||
2556c98b | 1138 | thread_count = (glob_agcount + ag_stride - 1) / ag_stride; |
12b55baf DC |
1139 | while (thread_count > max_threads) { |
1140 | ag_stride *= 2; | |
1141 | thread_count = (glob_agcount + ag_stride - 1) / | |
1142 | ag_stride; | |
1143 | } | |
1144 | if (thread_count > 0) | |
1145 | thread_init(); | |
1146 | else { | |
1147 | thread_count = 1; | |
1148 | ag_stride = 0; | |
1149 | } | |
add3cb90 BN |
1150 | } |
1151 | ||
2556c98b | 1152 | if (ag_stride && report_interval) { |
06fbdda9 | 1153 | init_progress_rpt(); |
06fbdda9 MV |
1154 | if (msgbuf) { |
1155 | do_log(_(" - reporting progress in intervals of %s\n"), | |
1156 | duration(report_interval, msgbuf)); | |
06fbdda9 MV |
1157 | } |
1158 | } | |
1159 | ||
2556c98b BN |
1160 | /* |
1161 | * Adjust libxfs cache sizes based on system memory, | |
1162 | * filesystem size and inode count. | |
1163 | * | |
1164 | * We'll set the cache size based on 3/4s the memory minus | |
1165 | * space used by the inode AVL tree and block usage map. | |
1166 | * | |
1167 | * Inode AVL tree space is approximately 4 bytes per inode, | |
1168 | * block usage map is currently 1 byte for 2 blocks. | |
1169 | * | |
1170 | * We assume most blocks will be inode clusters. | |
1171 | * | |
1172 | * Calculations are done in kilobyte units. | |
1173 | */ | |
1174 | ||
12be365e | 1175 | if (!bhash_option_used || max_mem_specified) { |
2556c98b | 1176 | unsigned long mem_used; |
12be365e BN |
1177 | unsigned long max_mem; |
1178 | struct rlimit rlim; | |
2556c98b | 1179 | |
2556c98b | 1180 | libxfs_bcache_purge(); |
2556c98b BN |
1181 | cache_destroy(libxfs_bcache); |
1182 | ||
1183 | mem_used = (mp->m_sb.sb_icount >> (10 - 2)) + | |
12be365e BN |
1184 | (mp->m_sb.sb_dblocks >> (10 + 1)) + |
1185 | 50000; /* rough estimate of 50MB overhead */ | |
1186 | max_mem = max_mem_specified ? max_mem_specified * 1024 : | |
4e5fe123 | 1187 | platform_physmem() * 3 / 4; |
12be365e BN |
1188 | |
1189 | if (getrlimit(RLIMIT_AS, &rlim) != -1 && | |
1190 | rlim.rlim_cur != RLIM_INFINITY) { | |
1191 | rlim.rlim_cur = rlim.rlim_max; | |
1192 | setrlimit(RLIMIT_AS, &rlim); | |
1193 | /* use approximately 80% of rlimit to avoid overrun */ | |
68d16907 | 1194 | max_mem = min(max_mem, rlim.rlim_cur / 1280); |
12be365e | 1195 | } else |
68d16907 | 1196 | max_mem = min(max_mem, (LONG_MAX >> 10) + 1); |
2556c98b BN |
1197 | |
1198 | if (verbose > 1) | |
5d1b7f0f CH |
1199 | do_log( |
1200 | _(" - max_mem = %lu, icount = %" PRIu64 ", imem = %" PRIu64 ", dblock = %" PRIu64 ", dmem = %" PRIu64 "\n"), | |
12be365e BN |
1201 | max_mem, mp->m_sb.sb_icount, |
1202 | mp->m_sb.sb_icount >> (10 - 2), | |
1203 | mp->m_sb.sb_dblocks, | |
1204 | mp->m_sb.sb_dblocks >> (10 + 1)); | |
1205 | ||
1206 | if (max_mem <= mem_used) { | |
0335a835 DC |
1207 | if (max_mem_specified) { |
1208 | do_abort( | |
1209 | _("Required memory for repair is greater that the maximum specified\n" | |
1210 | "with the -m option. Please increase it to at least %lu.\n"), | |
12be365e | 1211 | mem_used / 1024); |
0335a835 | 1212 | } |
70a4820f | 1213 | do_log( |
61510437 DC |
1214 | _("Memory available for repair (%luMB) may not be sufficient.\n" |
1215 | "At least %luMB is needed to repair this filesystem efficiently\n" | |
1216 | "If repair fails due to lack of memory, please\n"), | |
1217 | max_mem / 1024, mem_used / 1024); | |
1218 | if (do_prefetch) | |
70a4820f | 1219 | do_log( |
61510437 DC |
1220 | _("turn prefetching off (-P) to reduce the memory footprint.\n")); |
1221 | else | |
70a4820f | 1222 | do_log( |
61510437 DC |
1223 | _("increase system RAM and/or swap space to at least %luMB.\n"), |
1224 | mem_used * 2 / 1024); | |
1225 | ||
1226 | max_mem = mem_used; | |
2556c98b BN |
1227 | } |
1228 | ||
61510437 DC |
1229 | max_mem -= mem_used; |
1230 | if (max_mem >= (1 << 30)) | |
1231 | max_mem = 1 << 30; | |
1232 | libxfs_bhash_size = max_mem / (HASH_CACHE_RATIO * | |
e7fd2b6f | 1233 | (igeo->inode_cluster_size >> 10)); |
61510437 DC |
1234 | if (libxfs_bhash_size < 512) |
1235 | libxfs_bhash_size = 512; | |
1236 | ||
2556c98b BN |
1237 | if (verbose) |
1238 | do_log(_(" - block cache size set to %d entries\n"), | |
1239 | libxfs_bhash_size * HASH_CACHE_RATIO); | |
1240 | ||
ba9ecd40 | 1241 | libxfs_bcache = cache_init(0, libxfs_bhash_size, |
2556c98b BN |
1242 | &libxfs_bcache_operations); |
1243 | } | |
1244 | ||
2bd0ea18 NS |
1245 | /* |
1246 | * calculate what mkfs would do to this filesystem | |
1247 | */ | |
1248 | calc_mkfs(mp); | |
1249 | ||
1250 | /* | |
c1f7a46c | 1251 | * initialize block alloc map |
2bd0ea18 | 1252 | */ |
c1f7a46c BN |
1253 | init_bmaps(mp); |
1254 | incore_ino_init(mp); | |
1255 | incore_ext_init(mp); | |
2d273771 | 1256 | rmaps_init(mp); |
c1f7a46c BN |
1257 | |
1258 | /* initialize random globals now that we know the fs geometry */ | |
1259 | inodes_per_block = mp->m_sb.sb_inopblock; | |
2bd0ea18 | 1260 | |
575f24e5 | 1261 | if (parse_sb_version(mp)) { |
2bd0ea18 | 1262 | do_warn( |
507f4e33 | 1263 | _("Found unsupported filesystem features. Exiting now.\n")); |
2bd0ea18 NS |
1264 | return(1); |
1265 | } | |
1266 | ||
1267 | /* make sure the per-ag freespace maps are ok so we can mount the fs */ | |
364a126c | 1268 | phase2(mp, phase2_threads); |
8e9f22d6 | 1269 | phase_end(2); |
2bd0ea18 | 1270 | |
2556c98b BN |
1271 | if (do_prefetch) |
1272 | init_prefetch(mp); | |
1273 | ||
8100dd79 | 1274 | phase3(mp, phase2_threads); |
8e9f22d6 | 1275 | phase_end(3); |
2bd0ea18 NS |
1276 | |
1277 | phase4(mp); | |
8e9f22d6 | 1278 | phase_end(4); |
2bd0ea18 | 1279 | |
f2e38861 | 1280 | if (no_modify) { |
507f4e33 | 1281 | printf(_("No modify flag set, skipping phase 5\n")); |
f2e38861 DW |
1282 | |
1283 | if (mp->m_sb.sb_rblocks > 0) | |
1284 | check_rtmetadata(mp); | |
1285 | } else { | |
2bd0ea18 | 1286 | phase5(mp); |
3b6ac903 | 1287 | } |
8e9f22d6 | 1288 | phase_end(5); |
2bd0ea18 | 1289 | |
c1f7a46c BN |
1290 | /* |
1291 | * Done with the block usage maps, toss them... | |
1292 | */ | |
2d273771 | 1293 | rmaps_free(mp); |
c1f7a46c BN |
1294 | free_bmaps(mp); |
1295 | ||
2bd0ea18 NS |
1296 | if (!bad_ino_btree) { |
1297 | phase6(mp); | |
8e9f22d6 | 1298 | phase_end(6); |
2bd0ea18 | 1299 | |
e161d4a8 | 1300 | phase7(mp, phase2_threads); |
8e9f22d6 | 1301 | phase_end(7); |
2bd0ea18 NS |
1302 | } else { |
1303 | do_warn( | |
507f4e33 | 1304 | _("Inode allocation btrees are too corrupted, skipping phases 6 and 7\n")); |
2bd0ea18 NS |
1305 | } |
1306 | ||
0340d706 | 1307 | if (lost_quotas && !have_uquotino && !have_gquotino && !have_pquotino) { |
2bd0ea18 NS |
1308 | if (!no_modify) { |
1309 | do_warn( | |
507f4e33 | 1310 | _("Warning: no quota inodes were found. Quotas disabled.\n")); |
2bd0ea18 NS |
1311 | } else { |
1312 | do_warn( | |
507f4e33 | 1313 | _("Warning: no quota inodes were found. Quotas would be disabled.\n")); |
2bd0ea18 NS |
1314 | } |
1315 | } else if (lost_quotas) { | |
1316 | if (!no_modify) { | |
1317 | do_warn( | |
507f4e33 | 1318 | _("Warning: quota inodes were cleared. Quotas disabled.\n")); |
2bd0ea18 NS |
1319 | } else { |
1320 | do_warn( | |
507f4e33 | 1321 | _("Warning: quota inodes would be cleared. Quotas would be disabled.\n")); |
2bd0ea18 NS |
1322 | } |
1323 | } else { | |
1324 | if (lost_uquotino) { | |
1325 | if (!no_modify) { | |
1326 | do_warn( | |
507f4e33 NS |
1327 | _("Warning: user quota information was cleared.\n" |
1328 | "User quotas can not be enforced until limit information is recreated.\n")); | |
2bd0ea18 NS |
1329 | } else { |
1330 | do_warn( | |
507f4e33 NS |
1331 | _("Warning: user quota information would be cleared.\n" |
1332 | "User quotas could not be enforced until limit information was recreated.\n")); | |
2bd0ea18 NS |
1333 | } |
1334 | } | |
1335 | ||
b36eef04 | 1336 | if (lost_gquotino) { |
2bd0ea18 NS |
1337 | if (!no_modify) { |
1338 | do_warn( | |
507f4e33 NS |
1339 | _("Warning: group quota information was cleared.\n" |
1340 | "Group quotas can not be enforced until limit information is recreated.\n")); | |
2bd0ea18 NS |
1341 | } else { |
1342 | do_warn( | |
507f4e33 NS |
1343 | _("Warning: group quota information would be cleared.\n" |
1344 | "Group quotas could not be enforced until limit information was recreated.\n")); | |
9b27bdbb NS |
1345 | } |
1346 | } | |
1347 | ||
1348 | if (lost_pquotino) { | |
1349 | if (!no_modify) { | |
1350 | do_warn( | |
1351 | _("Warning: project quota information was cleared.\n" | |
1352 | "Project quotas can not be enforced until limit information is recreated.\n")); | |
1353 | } else { | |
1354 | do_warn( | |
1355 | _("Warning: project quota information would be cleared.\n" | |
1356 | "Project quotas could not be enforced until limit information was recreated.\n")); | |
2bd0ea18 NS |
1357 | } |
1358 | } | |
1359 | } | |
1360 | ||
2556c98b | 1361 | if (ag_stride && report_interval) |
06fbdda9 | 1362 | stop_progress_rpt(); |
9f38f08d | 1363 | |
2bd0ea18 | 1364 | if (no_modify) { |
1926558d BF |
1365 | /* |
1366 | * Warn if the current LSN is problematic and the log requires a | |
1367 | * reformat. | |
1368 | */ | |
1369 | format_log_max_lsn(mp); | |
1370 | ||
2bd0ea18 | 1371 | do_log( |
507f4e33 | 1372 | _("No modify flag set, skipping filesystem flush and exiting.\n")); |
3b6ac903 | 1373 | if (verbose) |
06fbdda9 | 1374 | summary_report(); |
2bd0ea18 NS |
1375 | if (fs_is_dirty) |
1376 | return(1); | |
1377 | ||
1378 | return(0); | |
1379 | } | |
1380 | ||
1381 | /* | |
1382 | * Clear the quota flags if they're on. | |
1383 | */ | |
67c4a324 | 1384 | sbp = libxfs_getsb(mp); |
2bd0ea18 | 1385 | if (!sbp) |
507f4e33 | 1386 | do_error(_("couldn't get superblock\n")); |
2bd0ea18 | 1387 | |
91a52fbc | 1388 | if ((mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD) != quotacheck_results()) { |
5e656dbb BN |
1389 | do_warn(_("Note - quota info will be regenerated on next " |
1390 | "quota mount.\n")); | |
bbe6680f DW |
1391 | mp->m_sb.sb_qflags &= ~(XFS_UQUOTA_CHKD | XFS_GQUOTA_CHKD | |
1392 | XFS_PQUOTA_CHKD | XFS_OQUOTA_CHKD); | |
1393 | libxfs_sb_to_disk(sbp->b_addr, &mp->m_sb); | |
2bd0ea18 NS |
1394 | } |
1395 | ||
6bf4721d | 1396 | if (copied_sunit) { |
2bd0ea18 | 1397 | do_warn( |
6bf4721d ES |
1398 | _("Note - stripe unit (%d) and width (%d) were copied from a backup superblock.\n" |
1399 | "Please reset with mount -o sunit=<value>,swidth=<value> if necessary\n"), | |
bbe6680f | 1400 | mp->m_sb.sb_unit, mp->m_sb.sb_width); |
dfc130f3 | 1401 | } |
2bd0ea18 | 1402 | |
f524ae04 | 1403 | libxfs_buf_mark_dirty(sbp); |
18b4f688 | 1404 | libxfs_buf_relse(sbp); |
2bd0ea18 | 1405 | |
68fb1399 DW |
1406 | /* |
1407 | * If we upgraded V5 filesystem features, we need to update the | |
1408 | * secondary superblocks to include the new feature bits. Don't set | |
1409 | * NEEDSREPAIR on the secondaries. | |
1410 | */ | |
1411 | if (features_changed) { | |
1412 | mp->m_sb.sb_features_incompat &= | |
1413 | ~XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR; | |
1414 | error = -libxfs_update_secondary_sbs(mp); | |
1415 | if (error) | |
1416 | do_error(_("upgrading features of secondary supers")); | |
1417 | mp->m_sb.sb_features_incompat |= | |
1418 | XFS_SB_FEAT_INCOMPAT_NEEDSREPAIR; | |
1419 | } | |
1420 | ||
2556c98b | 1421 | /* |
1926558d BF |
1422 | * Done. Flush all cached buffers and inodes first to ensure all |
1423 | * verifiers are run (where we discover the max metadata LSN), reformat | |
1424 | * the log if necessary and unmount. | |
2556c98b BN |
1425 | */ |
1426 | libxfs_bcache_flush(); | |
1926558d | 1427 | format_log_max_lsn(mp); |
14fb3613 | 1428 | |
a7348c58 DW |
1429 | if (xfs_sb_version_needsrepair(&mp->m_sb)) |
1430 | clear_needsrepair(mp); | |
1431 | ||
14fb3613 DW |
1432 | /* Report failure if anything failed to get written to our fs. */ |
1433 | error = -libxfs_umount(mp); | |
1434 | if (error) | |
1435 | do_error( | |
1acabf90 | 1436 | _("File system metadata writeout failed, err=%d. Re-run xfs_repair.\n"), |
14fb3613 | 1437 | error); |
1926558d | 1438 | |
a9468486 | 1439 | libxfs_destroy(&x); |
2bd0ea18 | 1440 | |
06fbdda9 MV |
1441 | if (verbose) |
1442 | summary_report(); | |
507f4e33 | 1443 | do_log(_("done\n")); |
3ae81520 ES |
1444 | |
1445 | if (dangerously && !no_modify) | |
1446 | do_warn( | |
1447 | _("Repair of readonly mount complete. Immediate reboot encouraged.\n")); | |
1448 | ||
4c0a98ae BN |
1449 | pftrace_done(); |
1450 | ||
0a223eb8 ES |
1451 | free(msgbuf); |
1452 | ||
7c3e94a3 JT |
1453 | if (fs_is_dirty && report_corrected) |
1454 | return (4); | |
3b6ac903 MV |
1455 | return (0); |
1456 | } |