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