]>
Commit | Line | Data |
---|---|---|
959ef981 | 1 | // SPDX-License-Identifier: GPL-2.0 |
61983f67 | 2 | /* |
56281ed4 | 3 | * Copyright (c) 2007, 2011 SGI |
61983f67 | 4 | * All Rights Reserved. |
61983f67 BN |
5 | */ |
6 | ||
6b803e5a CH |
7 | #include "libxfs.h" |
8 | #include "libxlog.h" | |
61983f67 BN |
9 | #include "bmap.h" |
10 | #include "command.h" | |
11 | #include "metadump.h" | |
12 | #include "io.h" | |
13 | #include "output.h" | |
14 | #include "type.h" | |
15 | #include "init.h" | |
16 | #include "sig.h" | |
17 | #include "xfs_metadump.h" | |
a2ceac1f DC |
18 | #include "fprint.h" |
19 | #include "faddr.h" | |
20 | #include "field.h" | |
21 | #include "dir2.h" | |
2b686ab3 | 22 | #include "obfuscate.h" |
61983f67 | 23 | |
d3e0c71f | 24 | #define DEFAULT_MAX_EXT_SIZE XFS_MAX_BMBT_EXTLEN |
7431d134 | 25 | |
61983f67 BN |
26 | /* copy all metadata structures to/from a file */ |
27 | ||
28 | static int metadump_f(int argc, char **argv); | |
29 | static void metadump_help(void); | |
30 | ||
31 | /* | |
32 | * metadump commands issue info/wornings/errors to standard error as | |
33 | * metadump supports stdout as a destination. | |
34 | * | |
35 | * All static functions return zero on failure, while the public functions | |
36 | * return zero on success. | |
37 | */ | |
38 | ||
39 | static const cmdinfo_t metadump_cmd = | |
40 | { "metadump", NULL, metadump_f, 0, -1, 0, | |
b09e839e | 41 | N_("[-a] [-e] [-g] [-m max_extent] [-w] [-o] filename"), |
9ee7055c | 42 | N_("dump metadata to a file"), metadump_help }; |
61983f67 | 43 | |
be75f7d7 CB |
44 | struct metadump_ops { |
45 | /* | |
46 | * Initialize Metadump. This may perform actions such as | |
47 | * 1. Allocating memory for structures required for dumping the | |
48 | * metadata. | |
49 | * 2. Writing a header to the beginning of the metadump file. | |
50 | */ | |
51 | int (*init)(void); | |
52 | /* | |
53 | * Write metadata to the metadump file along with the required ancillary | |
54 | * data. @off and @len are in units of 512 byte blocks. | |
55 | */ | |
56 | int (*write)(enum typnm type, const char *data, xfs_daddr_t off, | |
57 | int len); | |
58 | /* | |
59 | * Flush any in-memory remanents of metadata to the metadump file. | |
60 | */ | |
61 | int (*finish_dump)(void); | |
62 | /* | |
63 | * Free resources allocated during metadump process. | |
64 | */ | |
65 | void (*release)(void); | |
66 | }; | |
67 | ||
fb4697dd CB |
68 | static struct metadump { |
69 | int version; | |
70 | bool show_progress; | |
71 | bool stop_on_read_error; | |
72 | int max_extent_size; | |
73 | bool show_warnings; | |
74 | bool obfuscate; | |
75 | bool zero_stale_data; | |
76 | bool progress_since_warning; | |
77 | bool dirty_log; | |
78 | bool stdout_metadump; | |
79 | xfs_ino_t cur_ino; | |
80 | /* Metadump file */ | |
81 | FILE *outf; | |
be75f7d7 | 82 | struct metadump_ops *mdops; |
fb4697dd CB |
83 | /* header + index + buffers */ |
84 | struct xfs_metablock *metablock; | |
85 | __be64 *block_index; | |
86 | char *block_buffer; | |
87 | int num_indices; | |
88 | int cur_index; | |
89 | } metadump; | |
61983f67 BN |
90 | |
91 | void | |
92 | metadump_init(void) | |
93 | { | |
94 | add_command(&metadump_cmd); | |
95 | } | |
96 | ||
97 | static void | |
98 | metadump_help(void) | |
99 | { | |
9ee7055c | 100 | dbprintf(_( |
61983f67 BN |
101 | "\n" |
102 | " The 'metadump' command dumps the known metadata to a compact file suitable\n" | |
103 | " for compressing and sending to an XFS maintainer for corruption analysis \n" | |
104 | " or xfs_repair failures.\n\n" | |
88b8e1d6 | 105 | " Options:\n" |
b09e839e | 106 | " -a -- Copy full metadata blocks without zeroing unused space\n" |
61983f67 BN |
107 | " -e -- Ignore read errors and keep going\n" |
108 | " -g -- Display dump progress\n" | |
7431d134 | 109 | " -m -- Specify max extent size in blocks to copy (default = %d blocks)\n" |
61983f67 BN |
110 | " -o -- Don't obfuscate names and extended attributes\n" |
111 | " -w -- Show warnings of bad metadata information\n" | |
9ee7055c | 112 | "\n"), DEFAULT_MAX_EXT_SIZE); |
61983f67 BN |
113 | } |
114 | ||
115 | static void | |
116 | print_warning(const char *fmt, ...) | |
117 | { | |
118 | char buf[200]; | |
119 | va_list ap; | |
120 | ||
121 | if (seenint()) | |
122 | return; | |
123 | ||
124 | va_start(ap, fmt); | |
125 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
126 | va_end(ap); | |
127 | buf[sizeof(buf)-1] = '\0'; | |
128 | ||
fb4697dd CB |
129 | fprintf(stderr, "%s%s: %s\n", |
130 | metadump.progress_since_warning ? "\n" : "", | |
61983f67 | 131 | progname, buf); |
fb4697dd | 132 | metadump.progress_since_warning = false; |
61983f67 BN |
133 | } |
134 | ||
135 | static void | |
136 | print_progress(const char *fmt, ...) | |
137 | { | |
138 | char buf[60]; | |
139 | va_list ap; | |
140 | FILE *f; | |
141 | ||
142 | if (seenint()) | |
143 | return; | |
144 | ||
145 | va_start(ap, fmt); | |
146 | vsnprintf(buf, sizeof(buf), fmt, ap); | |
147 | va_end(ap); | |
148 | buf[sizeof(buf)-1] = '\0'; | |
149 | ||
fb4697dd | 150 | f = metadump.stdout_metadump ? stderr : stdout; |
61983f67 BN |
151 | fprintf(f, "\r%-59s", buf); |
152 | fflush(f); | |
fb4697dd | 153 | metadump.progress_since_warning = true; |
61983f67 BN |
154 | } |
155 | ||
fd491857 DC |
156 | /* |
157 | * we want to preserve the state of the metadata in the dump - whether it is | |
158 | * intact or corrupt, so even if the buffer has a verifier attached to it we | |
159 | * don't want to run it prior to writing the buffer to the metadump image. | |
160 | * | |
161 | * The only reason for running the verifier is to recalculate the CRCs on a | |
162 | * buffer that has been obfuscated. i.e. a buffer than metadump modified itself. | |
163 | * In this case, we only run the verifier if the buffer was not corrupt to begin | |
164 | * with so that we don't accidentally correct buffers with CRC or errors in them | |
165 | * when we are obfuscating them. | |
166 | */ | |
61983f67 BN |
167 | static int |
168 | write_buf( | |
169 | iocur_t *buf) | |
170 | { | |
fd491857 | 171 | struct xfs_buf *bp = buf->bp; |
61983f67 | 172 | int i; |
878afc65 | 173 | int ret; |
61983f67 | 174 | |
8ab75c4d DC |
175 | /* |
176 | * Run the write verifier to recalculate the buffer CRCs and check | |
fd491857 DC |
177 | * metadump didn't introduce a new corruption. Warn if the verifier |
178 | * failed, but still continue to dump it into the output file. | |
8ab75c4d | 179 | */ |
fd491857 DC |
180 | if (buf->need_crc && bp && bp->b_ops && !bp->b_error) { |
181 | bp->b_ops->verify_write(bp); | |
182 | if (bp->b_error) { | |
183 | print_warning( | |
a3fac935 ES |
184 | "obfuscation corrupted block at %s bno 0x%llx/0x%x", |
185 | bp->b_ops->name, | |
f1208396 | 186 | (long long)xfs_buf_daddr(bp), BBTOB(bp->b_length)); |
8ab75c4d DC |
187 | } |
188 | } | |
189 | ||
1516a5b5 DC |
190 | /* handle discontiguous buffers */ |
191 | if (!buf->bbmap) { | |
1a5a88ec CB |
192 | ret = metadump.mdops->write(buf->typ->typnm, buf->data, buf->bb, |
193 | buf->blen); | |
1516a5b5 DC |
194 | if (ret) |
195 | return ret; | |
196 | } else { | |
197 | int len = 0; | |
198 | for (i = 0; i < buf->bbmap->nmaps; i++) { | |
1a5a88ec CB |
199 | ret = metadump.mdops->write(buf->typ->typnm, |
200 | buf->data + BBTOB(len), | |
201 | buf->bbmap->b[i].bm_bn, | |
202 | buf->bbmap->b[i].bm_len); | |
878afc65 DC |
203 | if (ret) |
204 | return ret; | |
1516a5b5 | 205 | len += buf->bbmap->b[i].bm_len; |
61983f67 BN |
206 | } |
207 | } | |
878afc65 | 208 | return seenint() ? -EINTR : 0; |
61983f67 BN |
209 | } |
210 | ||
6058426f DC |
211 | /* |
212 | * We could be processing a corrupt block, so we can't trust any of | |
213 | * the offsets or lengths to be within the buffer range. Hence check | |
214 | * carefully! | |
215 | */ | |
20f35ef4 ES |
216 | static void |
217 | zero_btree_node( | |
218 | struct xfs_btree_block *block, | |
219 | typnm_t btype) | |
220 | { | |
221 | int nrecs; | |
222 | xfs_bmbt_ptr_t *bpp; | |
223 | xfs_bmbt_key_t *bkp; | |
224 | xfs_inobt_ptr_t *ipp; | |
225 | xfs_inobt_key_t *ikp; | |
226 | xfs_alloc_ptr_t *app; | |
227 | xfs_alloc_key_t *akp; | |
a7302f83 DC |
228 | char *zp1, *zp2; |
229 | char *key_end; | |
e7fd2b6f | 230 | struct xfs_ino_geometry *igeo = M_IGEO(mp); |
20f35ef4 ES |
231 | |
232 | nrecs = be16_to_cpu(block->bb_numrecs); | |
6058426f DC |
233 | if (nrecs < 0) |
234 | return; | |
20f35ef4 ES |
235 | |
236 | switch (btype) { | |
237 | case TYP_BMAPBTA: | |
238 | case TYP_BMAPBTD: | |
6058426f DC |
239 | if (nrecs > mp->m_bmap_dmxr[1]) |
240 | return; | |
241 | ||
20f35ef4 ES |
242 | bkp = XFS_BMBT_KEY_ADDR(mp, block, 1); |
243 | bpp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]); | |
a7302f83 DC |
244 | zp1 = (char *)&bkp[nrecs]; |
245 | zp2 = (char *)&bpp[nrecs]; | |
246 | key_end = (char *)bpp; | |
20f35ef4 ES |
247 | break; |
248 | case TYP_INOBT: | |
249 | case TYP_FINOBT: | |
e7fd2b6f | 250 | if (nrecs > igeo->inobt_mxr[1]) |
6058426f DC |
251 | return; |
252 | ||
20f35ef4 | 253 | ikp = XFS_INOBT_KEY_ADDR(mp, block, 1); |
e7fd2b6f | 254 | ipp = XFS_INOBT_PTR_ADDR(mp, block, 1, igeo->inobt_mxr[1]); |
a7302f83 DC |
255 | zp1 = (char *)&ikp[nrecs]; |
256 | zp2 = (char *)&ipp[nrecs]; | |
257 | key_end = (char *)ipp; | |
20f35ef4 ES |
258 | break; |
259 | case TYP_BNOBT: | |
260 | case TYP_CNTBT: | |
6058426f DC |
261 | if (nrecs > mp->m_alloc_mxr[1]) |
262 | return; | |
263 | ||
20f35ef4 ES |
264 | akp = XFS_ALLOC_KEY_ADDR(mp, block, 1); |
265 | app = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]); | |
a7302f83 DC |
266 | zp1 = (char *)&akp[nrecs]; |
267 | zp2 = (char *)&app[nrecs]; | |
268 | key_end = (char *)app; | |
20f35ef4 ES |
269 | break; |
270 | default: | |
a7302f83 | 271 | return; |
20f35ef4 ES |
272 | } |
273 | ||
a7302f83 DC |
274 | |
275 | /* Zero from end of keys to beginning of pointers */ | |
276 | memset(zp1, 0, key_end - zp1); | |
277 | ||
278 | /* Zero from end of pointers to end of block */ | |
279 | memset(zp2, 0, (char *)block + mp->m_sb.sb_blocksize - zp2); | |
20f35ef4 ES |
280 | } |
281 | ||
6058426f DC |
282 | /* |
283 | * We could be processing a corrupt block, so we can't trust any of | |
284 | * the offsets or lengths to be within the buffer range. Hence check | |
285 | * carefully! | |
286 | */ | |
20f35ef4 ES |
287 | static void |
288 | zero_btree_leaf( | |
289 | struct xfs_btree_block *block, | |
290 | typnm_t btype) | |
291 | { | |
292 | int nrecs; | |
293 | struct xfs_bmbt_rec *brp; | |
294 | struct xfs_inobt_rec *irp; | |
295 | struct xfs_alloc_rec *arp; | |
a7302f83 | 296 | char *zp; |
20f35ef4 ES |
297 | |
298 | nrecs = be16_to_cpu(block->bb_numrecs); | |
6058426f DC |
299 | if (nrecs < 0) |
300 | return; | |
20f35ef4 ES |
301 | |
302 | switch (btype) { | |
303 | case TYP_BMAPBTA: | |
304 | case TYP_BMAPBTD: | |
6058426f DC |
305 | if (nrecs > mp->m_bmap_dmxr[0]) |
306 | return; | |
307 | ||
20f35ef4 | 308 | brp = XFS_BMBT_REC_ADDR(mp, block, 1); |
a7302f83 | 309 | zp = (char *)&brp[nrecs]; |
20f35ef4 ES |
310 | break; |
311 | case TYP_INOBT: | |
312 | case TYP_FINOBT: | |
e7fd2b6f | 313 | if (nrecs > M_IGEO(mp)->inobt_mxr[0]) |
6058426f DC |
314 | return; |
315 | ||
20f35ef4 | 316 | irp = XFS_INOBT_REC_ADDR(mp, block, 1); |
a7302f83 | 317 | zp = (char *)&irp[nrecs]; |
20f35ef4 ES |
318 | break; |
319 | case TYP_BNOBT: | |
320 | case TYP_CNTBT: | |
6058426f DC |
321 | if (nrecs > mp->m_alloc_mxr[0]) |
322 | return; | |
323 | ||
20f35ef4 | 324 | arp = XFS_ALLOC_REC_ADDR(mp, block, 1); |
a7302f83 | 325 | zp = (char *)&arp[nrecs]; |
20f35ef4 ES |
326 | break; |
327 | default: | |
a7302f83 | 328 | return; |
20f35ef4 ES |
329 | } |
330 | ||
331 | /* Zero from end of records to end of block */ | |
a7302f83 | 332 | memset(zp, 0, (char *)block + mp->m_sb.sb_blocksize - zp); |
20f35ef4 ES |
333 | } |
334 | ||
335 | static void | |
336 | zero_btree_block( | |
337 | struct xfs_btree_block *block, | |
338 | typnm_t btype) | |
339 | { | |
340 | int level; | |
341 | ||
342 | level = be16_to_cpu(block->bb_level); | |
343 | ||
344 | if (level > 0) | |
345 | zero_btree_node(block, btype); | |
346 | else | |
347 | zero_btree_leaf(block, btype); | |
348 | } | |
61983f67 BN |
349 | |
350 | static int | |
351 | scan_btree( | |
352 | xfs_agnumber_t agno, | |
353 | xfs_agblock_t agbno, | |
354 | int level, | |
355 | typnm_t btype, | |
356 | void *arg, | |
b194c7d8 | 357 | int (*func)(struct xfs_btree_block *block, |
61983f67 BN |
358 | xfs_agnumber_t agno, |
359 | xfs_agblock_t agbno, | |
360 | int level, | |
361 | typnm_t btype, | |
362 | void *arg)) | |
363 | { | |
d24c0a90 BN |
364 | int rval = 0; |
365 | ||
61983f67 BN |
366 | push_cur(); |
367 | set_cur(&typtab[btype], XFS_AGB_TO_DADDR(mp, agno, agbno), blkbb, | |
368 | DB_RING_IGN, NULL); | |
369 | if (iocur_top->data == NULL) { | |
370 | print_warning("cannot read %s block %u/%u", typtab[btype].name, | |
371 | agno, agbno); | |
fb4697dd | 372 | rval = !metadump.stop_on_read_error; |
d24c0a90 | 373 | goto pop_out; |
61983f67 | 374 | } |
20f35ef4 | 375 | |
fb4697dd | 376 | if (metadump.zero_stale_data) { |
20f35ef4 ES |
377 | zero_btree_block(iocur_top->data, btype); |
378 | iocur_top->need_crc = 1; | |
379 | } | |
380 | ||
878afc65 | 381 | if (write_buf(iocur_top)) |
d24c0a90 | 382 | goto pop_out; |
61983f67 BN |
383 | |
384 | if (!(*func)(iocur_top->data, agno, agbno, level - 1, btype, arg)) | |
d24c0a90 BN |
385 | goto pop_out; |
386 | rval = 1; | |
387 | pop_out: | |
61983f67 | 388 | pop_cur(); |
d24c0a90 | 389 | return rval; |
61983f67 BN |
390 | } |
391 | ||
392 | /* free space tree copy routines */ | |
393 | ||
394 | static int | |
395 | valid_bno( | |
61983f67 | 396 | xfs_agnumber_t agno, |
88b8e1d6 | 397 | xfs_agblock_t agbno) |
61983f67 | 398 | { |
88b8e1d6 BN |
399 | if (agno < (mp->m_sb.sb_agcount - 1) && agbno > 0 && |
400 | agbno <= mp->m_sb.sb_agblocks) | |
401 | return 1; | |
402 | if (agno == (mp->m_sb.sb_agcount - 1) && agbno > 0 && | |
403 | agbno <= (mp->m_sb.sb_dblocks - | |
5a35bf2c | 404 | (xfs_rfsblock_t)(mp->m_sb.sb_agcount - 1) * |
66be354e | 405 | mp->m_sb.sb_agblocks)) |
61983f67 BN |
406 | return 1; |
407 | ||
61983f67 BN |
408 | return 0; |
409 | } | |
410 | ||
88b8e1d6 | 411 | |
61983f67 BN |
412 | static int |
413 | scanfunc_freesp( | |
b194c7d8 | 414 | struct xfs_btree_block *block, |
61983f67 BN |
415 | xfs_agnumber_t agno, |
416 | xfs_agblock_t agbno, | |
417 | int level, | |
418 | typnm_t btype, | |
419 | void *arg) | |
420 | { | |
421 | xfs_alloc_ptr_t *pp; | |
422 | int i; | |
88b8e1d6 | 423 | int numrecs; |
61983f67 BN |
424 | |
425 | if (level == 0) | |
426 | return 1; | |
427 | ||
b194c7d8 | 428 | numrecs = be16_to_cpu(block->bb_numrecs); |
88b8e1d6 | 429 | if (numrecs > mp->m_alloc_mxr[1]) { |
fb4697dd | 430 | if (metadump.show_warnings) |
88b8e1d6 BN |
431 | print_warning("invalid numrecs (%u) in %s block %u/%u", |
432 | numrecs, typtab[btype].name, agno, agbno); | |
61983f67 BN |
433 | return 1; |
434 | } | |
435 | ||
b3563c19 | 436 | pp = XFS_ALLOC_PTR_ADDR(mp, block, 1, mp->m_alloc_mxr[1]); |
88b8e1d6 BN |
437 | for (i = 0; i < numrecs; i++) { |
438 | if (!valid_bno(agno, be32_to_cpu(pp[i]))) { | |
fb4697dd | 439 | if (metadump.show_warnings) |
88b8e1d6 BN |
440 | print_warning("invalid block number (%u/%u) " |
441 | "in %s block %u/%u", | |
442 | agno, be32_to_cpu(pp[i]), | |
443 | typtab[btype].name, agno, agbno); | |
61983f67 | 444 | continue; |
88b8e1d6 | 445 | } |
61983f67 BN |
446 | if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg, |
447 | scanfunc_freesp)) | |
448 | return 0; | |
449 | } | |
450 | return 1; | |
451 | } | |
452 | ||
453 | static int | |
454 | copy_free_bno_btree( | |
455 | xfs_agnumber_t agno, | |
456 | xfs_agf_t *agf) | |
457 | { | |
458 | xfs_agblock_t root; | |
459 | int levels; | |
460 | ||
461 | root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]); | |
462 | levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]); | |
463 | ||
464 | /* validate root and levels before processing the tree */ | |
465 | if (root == 0 || root > mp->m_sb.sb_agblocks) { | |
fb4697dd | 466 | if (metadump.show_warnings) |
61983f67 BN |
467 | print_warning("invalid block number (%u) in bnobt " |
468 | "root in agf %u", root, agno); | |
469 | return 1; | |
470 | } | |
716b497a | 471 | if (levels > mp->m_alloc_maxlevels) { |
fb4697dd | 472 | if (metadump.show_warnings) |
61983f67 BN |
473 | print_warning("invalid level (%u) in bnobt root " |
474 | "in agf %u", levels, agno); | |
475 | return 1; | |
476 | } | |
477 | ||
478 | return scan_btree(agno, root, levels, TYP_BNOBT, agf, scanfunc_freesp); | |
479 | } | |
480 | ||
481 | static int | |
482 | copy_free_cnt_btree( | |
483 | xfs_agnumber_t agno, | |
484 | xfs_agf_t *agf) | |
485 | { | |
486 | xfs_agblock_t root; | |
487 | int levels; | |
488 | ||
489 | root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]); | |
490 | levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]); | |
491 | ||
492 | /* validate root and levels before processing the tree */ | |
493 | if (root == 0 || root > mp->m_sb.sb_agblocks) { | |
fb4697dd | 494 | if (metadump.show_warnings) |
61983f67 BN |
495 | print_warning("invalid block number (%u) in cntbt " |
496 | "root in agf %u", root, agno); | |
497 | return 1; | |
498 | } | |
716b497a | 499 | if (levels > mp->m_alloc_maxlevels) { |
fb4697dd | 500 | if (metadump.show_warnings) |
61983f67 BN |
501 | print_warning("invalid level (%u) in cntbt root " |
502 | "in agf %u", levels, agno); | |
503 | return 1; | |
504 | } | |
505 | ||
506 | return scan_btree(agno, root, levels, TYP_CNTBT, agf, scanfunc_freesp); | |
507 | } | |
508 | ||
e434854e DW |
509 | static int |
510 | scanfunc_rmapbt( | |
511 | struct xfs_btree_block *block, | |
512 | xfs_agnumber_t agno, | |
513 | xfs_agblock_t agbno, | |
514 | int level, | |
515 | typnm_t btype, | |
516 | void *arg) | |
517 | { | |
518 | xfs_rmap_ptr_t *pp; | |
519 | int i; | |
520 | int numrecs; | |
521 | ||
522 | if (level == 0) | |
523 | return 1; | |
524 | ||
525 | numrecs = be16_to_cpu(block->bb_numrecs); | |
526 | if (numrecs > mp->m_rmap_mxr[1]) { | |
fb4697dd | 527 | if (metadump.show_warnings) |
e434854e DW |
528 | print_warning("invalid numrecs (%u) in %s block %u/%u", |
529 | numrecs, typtab[btype].name, agno, agbno); | |
530 | return 1; | |
531 | } | |
532 | ||
533 | pp = XFS_RMAP_PTR_ADDR(block, 1, mp->m_rmap_mxr[1]); | |
534 | for (i = 0; i < numrecs; i++) { | |
535 | if (!valid_bno(agno, be32_to_cpu(pp[i]))) { | |
fb4697dd | 536 | if (metadump.show_warnings) |
e434854e DW |
537 | print_warning("invalid block number (%u/%u) " |
538 | "in %s block %u/%u", | |
539 | agno, be32_to_cpu(pp[i]), | |
540 | typtab[btype].name, agno, agbno); | |
541 | continue; | |
542 | } | |
543 | if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg, | |
544 | scanfunc_rmapbt)) | |
545 | return 0; | |
546 | } | |
547 | return 1; | |
548 | } | |
549 | ||
550 | static int | |
551 | copy_rmap_btree( | |
552 | xfs_agnumber_t agno, | |
553 | struct xfs_agf *agf) | |
554 | { | |
555 | xfs_agblock_t root; | |
556 | int levels; | |
557 | ||
2660e653 | 558 | if (!xfs_has_rmapbt(mp)) |
e434854e DW |
559 | return 1; |
560 | ||
561 | root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]); | |
562 | levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]); | |
563 | ||
564 | /* validate root and levels before processing the tree */ | |
565 | if (root == 0 || root > mp->m_sb.sb_agblocks) { | |
fb4697dd | 566 | if (metadump.show_warnings) |
e434854e DW |
567 | print_warning("invalid block number (%u) in rmapbt " |
568 | "root in agf %u", root, agno); | |
569 | return 1; | |
570 | } | |
716b497a | 571 | if (levels > mp->m_rmap_maxlevels) { |
fb4697dd | 572 | if (metadump.show_warnings) |
e434854e DW |
573 | print_warning("invalid level (%u) in rmapbt root " |
574 | "in agf %u", levels, agno); | |
575 | return 1; | |
576 | } | |
577 | ||
578 | return scan_btree(agno, root, levels, TYP_RMAPBT, agf, scanfunc_rmapbt); | |
579 | } | |
580 | ||
e2756db3 DW |
581 | static int |
582 | scanfunc_refcntbt( | |
583 | struct xfs_btree_block *block, | |
584 | xfs_agnumber_t agno, | |
585 | xfs_agblock_t agbno, | |
586 | int level, | |
587 | typnm_t btype, | |
588 | void *arg) | |
589 | { | |
590 | xfs_refcount_ptr_t *pp; | |
591 | int i; | |
592 | int numrecs; | |
593 | ||
594 | if (level == 0) | |
595 | return 1; | |
596 | ||
597 | numrecs = be16_to_cpu(block->bb_numrecs); | |
598 | if (numrecs > mp->m_refc_mxr[1]) { | |
fb4697dd | 599 | if (metadump.show_warnings) |
e2756db3 DW |
600 | print_warning("invalid numrecs (%u) in %s block %u/%u", |
601 | numrecs, typtab[btype].name, agno, agbno); | |
602 | return 1; | |
603 | } | |
604 | ||
605 | pp = XFS_REFCOUNT_PTR_ADDR(block, 1, mp->m_refc_mxr[1]); | |
606 | for (i = 0; i < numrecs; i++) { | |
607 | if (!valid_bno(agno, be32_to_cpu(pp[i]))) { | |
fb4697dd | 608 | if (metadump.show_warnings) |
e2756db3 DW |
609 | print_warning("invalid block number (%u/%u) " |
610 | "in %s block %u/%u", | |
611 | agno, be32_to_cpu(pp[i]), | |
612 | typtab[btype].name, agno, agbno); | |
613 | continue; | |
614 | } | |
615 | if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg, | |
616 | scanfunc_refcntbt)) | |
617 | return 0; | |
618 | } | |
619 | return 1; | |
620 | } | |
621 | ||
622 | static int | |
623 | copy_refcount_btree( | |
624 | xfs_agnumber_t agno, | |
625 | struct xfs_agf *agf) | |
626 | { | |
627 | xfs_agblock_t root; | |
628 | int levels; | |
629 | ||
2660e653 | 630 | if (!xfs_has_reflink(mp)) |
e2756db3 DW |
631 | return 1; |
632 | ||
633 | root = be32_to_cpu(agf->agf_refcount_root); | |
634 | levels = be32_to_cpu(agf->agf_refcount_level); | |
635 | ||
636 | /* validate root and levels before processing the tree */ | |
637 | if (root == 0 || root > mp->m_sb.sb_agblocks) { | |
fb4697dd | 638 | if (metadump.show_warnings) |
e2756db3 DW |
639 | print_warning("invalid block number (%u) in refcntbt " |
640 | "root in agf %u", root, agno); | |
641 | return 1; | |
642 | } | |
716b497a | 643 | if (levels > mp->m_refc_maxlevels) { |
fb4697dd | 644 | if (metadump.show_warnings) |
e2756db3 DW |
645 | print_warning("invalid level (%u) in refcntbt root " |
646 | "in agf %u", levels, agno); | |
647 | return 1; | |
648 | } | |
649 | ||
650 | return scan_btree(agno, root, levels, TYP_REFCBT, agf, scanfunc_refcntbt); | |
651 | } | |
652 | ||
61983f67 BN |
653 | /* filename and extended attribute obfuscation routines */ |
654 | ||
78027d48 | 655 | struct name_ent { |
61983f67 BN |
656 | struct name_ent *next; |
657 | xfs_dahash_t hash; | |
78027d48 | 658 | int namelen; |
02211695 | 659 | unsigned char name[1]; |
78027d48 | 660 | }; |
61983f67 BN |
661 | |
662 | #define NAME_TABLE_SIZE 4096 | |
663 | ||
a85f8b0a | 664 | static struct name_ent *nametable[NAME_TABLE_SIZE]; |
61983f67 BN |
665 | |
666 | static void | |
a85f8b0a | 667 | nametable_clear(void) |
61983f67 | 668 | { |
a85f8b0a | 669 | int i; |
78027d48 | 670 | struct name_ent *ent; |
61983f67 BN |
671 | |
672 | for (i = 0; i < NAME_TABLE_SIZE; i++) { | |
a85f8b0a AE |
673 | while ((ent = nametable[i])) { |
674 | nametable[i] = ent->next; | |
675 | free(ent); | |
61983f67 BN |
676 | } |
677 | } | |
678 | } | |
679 | ||
a85f8b0a AE |
680 | /* |
681 | * See if the given name is already in the name table. If so, | |
682 | * return a pointer to its entry, otherwise return a null pointer. | |
683 | */ | |
684 | static struct name_ent * | |
02211695 | 685 | nametable_find(xfs_dahash_t hash, int namelen, unsigned char *name) |
a85f8b0a AE |
686 | { |
687 | struct name_ent *ent; | |
688 | ||
689 | for (ent = nametable[hash % NAME_TABLE_SIZE]; ent; ent = ent->next) { | |
690 | if (ent->hash == hash && ent->namelen == namelen && | |
691 | !memcmp(ent->name, name, namelen)) | |
692 | return ent; | |
693 | } | |
694 | return NULL; | |
695 | } | |
696 | ||
697 | /* | |
698 | * Add the given name to the name table. Returns a pointer to the | |
699 | * name's new entry, or a null pointer if an error occurs. | |
700 | */ | |
701 | static struct name_ent * | |
02211695 | 702 | nametable_add(xfs_dahash_t hash, int namelen, unsigned char *name) |
a85f8b0a AE |
703 | { |
704 | struct name_ent *ent; | |
705 | ||
706 | ent = malloc(sizeof *ent + namelen); | |
707 | if (!ent) | |
708 | return NULL; | |
709 | ||
710 | ent->namelen = namelen; | |
711 | memcpy(ent->name, name, namelen); | |
712 | ent->hash = hash; | |
713 | ent->next = nametable[hash % NAME_TABLE_SIZE]; | |
714 | ||
715 | nametable[hash % NAME_TABLE_SIZE] = ent; | |
716 | ||
717 | return ent; | |
718 | } | |
61983f67 | 719 | |
56281ed4 AE |
720 | #define ORPHANAGE "lost+found" |
721 | #define ORPHANAGE_LEN (sizeof (ORPHANAGE) - 1) | |
722 | ||
723 | static inline int | |
724 | is_orphanage_dir( | |
725 | struct xfs_mount *mp, | |
726 | xfs_ino_t dir_ino, | |
727 | size_t name_len, | |
02211695 | 728 | unsigned char *name) |
56281ed4 AE |
729 | { |
730 | return dir_ino == mp->m_sb.sb_rootino && | |
731 | name_len == ORPHANAGE_LEN && | |
732 | !memcmp(name, ORPHANAGE, ORPHANAGE_LEN); | |
733 | } | |
734 | ||
735 | /* | |
736 | * Determine whether a name is one we shouldn't obfuscate because | |
737 | * it's an orphan (or the "lost+found" directory itself). Note | |
738 | * "cur_ino" is the inode for the directory currently being | |
739 | * processed. | |
740 | * | |
741 | * Returns 1 if the name should NOT be obfuscated or 0 otherwise. | |
742 | */ | |
61983f67 | 743 | static int |
56281ed4 | 744 | in_lost_found( |
61983f67 BN |
745 | xfs_ino_t ino, |
746 | int namelen, | |
02211695 | 747 | unsigned char *name) |
61983f67 BN |
748 | { |
749 | static xfs_ino_t orphanage_ino = 0; | |
56281ed4 | 750 | char s[24]; /* 21 is enough (64 bits in decimal) */ |
61983f67 BN |
751 | int slen; |
752 | ||
56281ed4 AE |
753 | /* Record the "lost+found" inode if we haven't done so already */ |
754 | ||
755 | ASSERT(ino != 0); | |
fb4697dd CB |
756 | if (!orphanage_ino && is_orphanage_dir(mp, metadump.cur_ino, namelen, |
757 | name)) | |
56281ed4 AE |
758 | orphanage_ino = ino; |
759 | ||
760 | /* We don't obfuscate the "lost+found" directory itself */ | |
761 | ||
762 | if (ino == orphanage_ino) | |
61983f67 BN |
763 | return 1; |
764 | ||
56281ed4 AE |
765 | /* Most files aren't in "lost+found" at all */ |
766 | ||
fb4697dd | 767 | if (metadump.cur_ino != orphanage_ino) |
61983f67 BN |
768 | return 0; |
769 | ||
770 | /* | |
56281ed4 AE |
771 | * Within "lost+found", we don't obfuscate any file whose |
772 | * name is the same as its inode number. Any others are | |
773 | * stray files and can be obfuscated. | |
61983f67 | 774 | */ |
56281ed4 | 775 | slen = snprintf(s, sizeof (s), "%llu", (unsigned long long) ino); |
61983f67 | 776 | |
56281ed4 | 777 | return slen == namelen && !memcmp(name, s, namelen); |
61983f67 BN |
778 | } |
779 | ||
fcb63670 AE |
780 | /* |
781 | * Look up the given name in the name table. If it is already | |
1167ddc4 AE |
782 | * present, iterate through a well-defined sequence of alternate |
783 | * names and attempt to use an alternate name instead. | |
fcb63670 AE |
784 | * |
785 | * Returns 1 if the (possibly modified) name is not present in the | |
1167ddc4 AE |
786 | * name table. Returns 0 if the name and all possible alternates |
787 | * are already in the table. | |
fcb63670 AE |
788 | */ |
789 | static int | |
02211695 | 790 | handle_duplicate_name(xfs_dahash_t hash, size_t name_len, unsigned char *name) |
fcb63670 | 791 | { |
02211695 | 792 | unsigned char new_name[name_len + 1]; |
1167ddc4 | 793 | uint32_t seq = 1; |
fcb63670 AE |
794 | |
795 | if (!nametable_find(hash, name_len, name)) | |
1167ddc4 | 796 | return 1; /* No duplicate */ |
fcb63670 AE |
797 | |
798 | /* Name is already in use. Need to find an alternate. */ | |
799 | ||
800 | do { | |
1167ddc4 | 801 | int found; |
fcb63670 | 802 | |
1167ddc4 AE |
803 | /* Only change incoming name if we find an alternate */ |
804 | do { | |
805 | memcpy(new_name, name, name_len); | |
806 | found = find_alternate(name_len, new_name, seq++); | |
807 | if (found < 0) | |
808 | return 0; /* No more to check */ | |
809 | } while (!found); | |
810 | } while (nametable_find(hash, name_len, new_name)); | |
fcb63670 | 811 | |
1167ddc4 AE |
812 | /* |
813 | * The alternate wasn't in the table already. Pass it back | |
814 | * to the caller. | |
815 | */ | |
816 | memcpy(name, new_name, name_len); | |
817 | ||
818 | return 1; | |
fcb63670 AE |
819 | } |
820 | ||
10a01bcd DW |
821 | static inline xfs_dahash_t |
822 | dirattr_hashname( | |
823 | bool is_dirent, | |
824 | const uint8_t *name, | |
825 | int namelen) | |
826 | { | |
827 | if (is_dirent) { | |
828 | struct xfs_name xname = { | |
829 | .name = name, | |
830 | .len = namelen, | |
831 | }; | |
832 | ||
833 | return libxfs_dir2_hashname(mp, &xname); | |
834 | } | |
835 | ||
836 | return libxfs_da_hashname(name, namelen); | |
837 | } | |
838 | ||
da7daaf2 AE |
839 | static void |
840 | generate_obfuscated_name( | |
841 | xfs_ino_t ino, | |
842 | int namelen, | |
02211695 | 843 | unsigned char *name) |
da7daaf2 AE |
844 | { |
845 | xfs_dahash_t hash; | |
da7daaf2 | 846 | |
56281ed4 AE |
847 | /* |
848 | * We don't obfuscate "lost+found" or any orphan files | |
849 | * therein. When the name table is used for extended | |
850 | * attributes, the inode number provided is 0, in which | |
851 | * case we don't need to make this check. | |
852 | */ | |
853 | if (ino && in_lost_found(ino, namelen, name)) | |
854 | return; | |
61983f67 | 855 | |
ad6bb839 | 856 | /* |
fcb63670 AE |
857 | * If the name starts with a slash, just skip over it. It |
858 | * isn't included in the hash and we don't record it in the | |
859 | * name table. Note that the namelen value passed in does | |
860 | * not count the leading slash (if one is present). | |
ad6bb839 AE |
861 | */ |
862 | if (*name == '/') | |
863 | name++; | |
61983f67 | 864 | |
fcb63670 | 865 | /* Obfuscate the name (if possible) */ |
61983f67 | 866 | |
10a01bcd DW |
867 | hash = dirattr_hashname(ino != 0, name, namelen); |
868 | obfuscate_name(hash, namelen, name, ino != 0); | |
869 | ASSERT(hash == dirattr_hashname(ino != 0, name, namelen)); | |
88b1fe2a AE |
870 | |
871 | /* | |
fcb63670 AE |
872 | * Make sure the name is not something already seen. If we |
873 | * fail to find a suitable alternate, we're dealing with a | |
874 | * very pathological situation, and we may end up creating | |
875 | * a duplicate name in the metadump, so issue a warning. | |
88b1fe2a | 876 | */ |
fcb63670 | 877 | if (!handle_duplicate_name(hash, namelen, name)) { |
88b1fe2a AE |
878 | print_warning("duplicate name for inode %llu " |
879 | "in dir inode %llu\n", | |
880 | (unsigned long long) ino, | |
fb4697dd | 881 | (unsigned long long) metadump.cur_ino); |
fcb63670 AE |
882 | return; |
883 | } | |
884 | ||
885 | /* Create an entry for the new name in the name table. */ | |
61983f67 | 886 | |
a85f8b0a AE |
887 | if (!nametable_add(hash, namelen, name)) |
888 | print_warning("unable to record name for inode %llu " | |
889 | "in dir inode %llu\n", | |
890 | (unsigned long long) ino, | |
fb4697dd | 891 | (unsigned long long) metadump.cur_ino); |
61983f67 BN |
892 | } |
893 | ||
894 | static void | |
87c955c3 | 895 | process_sf_dir( |
7328ea6e | 896 | struct xfs_dinode *dip) |
61983f67 | 897 | { |
eb0cb950 | 898 | struct xfs_dir2_sf_hdr *sfp; |
61983f67 | 899 | xfs_dir2_sf_entry_t *sfep; |
14f8b681 | 900 | uint64_t ino_dir_size; |
61983f67 BN |
901 | int i; |
902 | ||
eb0cb950 | 903 | sfp = (struct xfs_dir2_sf_hdr *)XFS_DFORK_DPTR(dip); |
56b2de80 | 904 | ino_dir_size = be64_to_cpu(dip->di_size); |
61983f67 BN |
905 | if (ino_dir_size > XFS_DFORK_DSIZE(dip, mp)) { |
906 | ino_dir_size = XFS_DFORK_DSIZE(dip, mp); | |
fb4697dd | 907 | if (metadump.show_warnings) |
88b8e1d6 | 908 | print_warning("invalid size in dir inode %llu", |
fb4697dd | 909 | (long long)metadump.cur_ino); |
61983f67 BN |
910 | } |
911 | ||
eb0cb950 DC |
912 | sfep = xfs_dir2_sf_firstentry(sfp); |
913 | for (i = 0; (i < sfp->count) && | |
61983f67 BN |
914 | ((char *)sfep - (char *)sfp < ino_dir_size); i++) { |
915 | ||
916 | /* | |
917 | * first check for bad name lengths. If they are bad, we | |
918 | * have limitations to how much can be obfuscated. | |
919 | */ | |
920 | int namelen = sfep->namelen; | |
921 | ||
922 | if (namelen == 0) { | |
fb4697dd | 923 | if (metadump.show_warnings) |
61983f67 | 924 | print_warning("zero length entry in dir inode " |
fb4697dd | 925 | "%llu", (long long)metadump.cur_ino); |
eb0cb950 | 926 | if (i != sfp->count - 1) |
61983f67 BN |
927 | break; |
928 | namelen = ino_dir_size - ((char *)&sfep->name[0] - | |
929 | (char *)sfp); | |
930 | } else if ((char *)sfep - (char *)sfp + | |
660836c9 | 931 | libxfs_dir2_sf_entsize(mp, sfp, sfep->namelen) > |
61983f67 | 932 | ino_dir_size) { |
fb4697dd | 933 | if (metadump.show_warnings) |
61983f67 | 934 | print_warning("entry length in dir inode %llu " |
fb4697dd CB |
935 | "overflows space", |
936 | (long long)metadump.cur_ino); | |
eb0cb950 | 937 | if (i != sfp->count - 1) |
61983f67 BN |
938 | break; |
939 | namelen = ino_dir_size - ((char *)&sfep->name[0] - | |
940 | (char *)sfp); | |
941 | } | |
942 | ||
fb4697dd | 943 | if (metadump.obfuscate) |
87c955c3 | 944 | generate_obfuscated_name( |
e96bd2d3 | 945 | libxfs_dir2_sf_get_ino(mp, sfp, sfep), |
a2ceac1f | 946 | namelen, &sfep->name[0]); |
61983f67 BN |
947 | |
948 | sfep = (xfs_dir2_sf_entry_t *)((char *)sfep + | |
660836c9 | 949 | libxfs_dir2_sf_entsize(mp, sfp, namelen)); |
61983f67 | 950 | } |
87c955c3 ES |
951 | |
952 | /* zero stale data in rest of space in data fork, if any */ | |
fb4697dd CB |
953 | if (metadump.zero_stale_data && |
954 | (ino_dir_size < XFS_DFORK_DSIZE(dip, mp))) | |
87c955c3 | 955 | memset(sfep, 0, XFS_DFORK_DSIZE(dip, mp) - ino_dir_size); |
61983f67 BN |
956 | } |
957 | ||
f63c7540 DC |
958 | /* |
959 | * The pathname may not be null terminated. It may be terminated by the end of | |
960 | * a buffer or inode literal area, and the start of the next region contains | |
961 | * unknown data. Therefore, when we get to the last component of the symlink, we | |
962 | * cannot assume that strlen() will give us the right result. Hence we need to | |
963 | * track the remaining pathname length and use that instead. | |
964 | */ | |
b249a9f0 ES |
965 | static void |
966 | obfuscate_path_components( | |
967 | char *buf, | |
14f8b681 | 968 | uint64_t len) |
b249a9f0 | 969 | { |
02211695 CH |
970 | unsigned char *comp = (unsigned char *)buf; |
971 | unsigned char *end = comp + len; | |
b249a9f0 ES |
972 | xfs_dahash_t hash; |
973 | ||
f63c7540 | 974 | while (comp < end) { |
b249a9f0 ES |
975 | char *slash; |
976 | int namelen; | |
977 | ||
978 | /* find slash at end of this component */ | |
979 | slash = strchr((char *)comp, '/'); | |
980 | if (!slash) { | |
981 | /* last (or single) component */ | |
f63c7540 | 982 | namelen = strnlen((char *)comp, len); |
b249a9f0 | 983 | hash = libxfs_da_hashname(comp, namelen); |
10a01bcd | 984 | obfuscate_name(hash, namelen, comp, false); |
cb8c70b0 | 985 | ASSERT(hash == libxfs_da_hashname(comp, namelen)); |
b249a9f0 ES |
986 | break; |
987 | } | |
988 | namelen = slash - (char *)comp; | |
989 | /* handle leading or consecutive slashes */ | |
990 | if (!namelen) { | |
991 | comp++; | |
f63c7540 | 992 | len--; |
b249a9f0 ES |
993 | continue; |
994 | } | |
995 | hash = libxfs_da_hashname(comp, namelen); | |
10a01bcd | 996 | obfuscate_name(hash, namelen, comp, false); |
cb8c70b0 | 997 | ASSERT(hash == libxfs_da_hashname(comp, namelen)); |
b249a9f0 | 998 | comp += namelen + 1; |
f63c7540 | 999 | len -= namelen + 1; |
b249a9f0 ES |
1000 | } |
1001 | } | |
1002 | ||
61983f67 | 1003 | static void |
87c955c3 | 1004 | process_sf_symlink( |
7328ea6e | 1005 | struct xfs_dinode *dip) |
61983f67 | 1006 | { |
14f8b681 | 1007 | uint64_t len; |
56b2de80 | 1008 | char *buf; |
88b8e1d6 | 1009 | |
56b2de80 | 1010 | len = be64_to_cpu(dip->di_size); |
88b8e1d6 | 1011 | if (len > XFS_DFORK_DSIZE(dip, mp)) { |
fb4697dd | 1012 | if (metadump.show_warnings) |
88b8e1d6 | 1013 | print_warning("invalid size (%d) in symlink inode %llu", |
fb4697dd | 1014 | len, (long long)metadump.cur_ino); |
88b8e1d6 BN |
1015 | len = XFS_DFORK_DSIZE(dip, mp); |
1016 | } | |
61983f67 | 1017 | |
56b2de80 | 1018 | buf = (char *)XFS_DFORK_DPTR(dip); |
fb4697dd | 1019 | if (metadump.obfuscate) |
87c955c3 ES |
1020 | obfuscate_path_components(buf, len); |
1021 | ||
1022 | /* zero stale data in rest of space in data fork, if any */ | |
fb4697dd | 1023 | if (metadump.zero_stale_data && len < XFS_DFORK_DSIZE(dip, mp)) |
87c955c3 | 1024 | memset(&buf[len], 0, XFS_DFORK_DSIZE(dip, mp) - len); |
61983f67 BN |
1025 | } |
1026 | ||
1027 | static void | |
87c955c3 | 1028 | process_sf_attr( |
7328ea6e | 1029 | struct xfs_dinode *dip) |
61983f67 BN |
1030 | { |
1031 | /* | |
1941482c ES |
1032 | * with extended attributes, obfuscate the names and fill the actual |
1033 | * values with 'v' (to see a valid string length, as opposed to NULLs) | |
61983f67 BN |
1034 | */ |
1035 | ||
2fd09353 | 1036 | struct xfs_attr_shortform *asfp; |
cc3650f7 CM |
1037 | struct xfs_attr_sf_entry *asfep; |
1038 | int ino_attr_size; | |
1039 | int i; | |
61983f67 | 1040 | |
2fd09353 | 1041 | asfp = (struct xfs_attr_shortform *)XFS_DFORK_APTR(dip); |
61983f67 BN |
1042 | if (asfp->hdr.count == 0) |
1043 | return; | |
1044 | ||
1045 | ino_attr_size = be16_to_cpu(asfp->hdr.totsize); | |
1046 | if (ino_attr_size > XFS_DFORK_ASIZE(dip, mp)) { | |
1047 | ino_attr_size = XFS_DFORK_ASIZE(dip, mp); | |
fb4697dd | 1048 | if (metadump.show_warnings) |
61983f67 | 1049 | print_warning("invalid attr size in inode %llu", |
fb4697dd | 1050 | (long long)metadump.cur_ino); |
61983f67 BN |
1051 | } |
1052 | ||
1053 | asfep = &asfp->list[0]; | |
1054 | for (i = 0; (i < asfp->hdr.count) && | |
1055 | ((char *)asfep - (char *)asfp < ino_attr_size); i++) { | |
1056 | ||
1057 | int namelen = asfep->namelen; | |
1058 | ||
1059 | if (namelen == 0) { | |
fb4697dd | 1060 | if (metadump.show_warnings) |
61983f67 | 1061 | print_warning("zero length attr entry in inode " |
fb4697dd | 1062 | "%llu", (long long)metadump.cur_ino); |
61983f67 BN |
1063 | break; |
1064 | } else if ((char *)asfep - (char *)asfp + | |
24b24fad | 1065 | xfs_attr_sf_entsize(asfep) > ino_attr_size) { |
fb4697dd | 1066 | if (metadump.show_warnings) |
61983f67 | 1067 | print_warning("attr entry length in inode %llu " |
fb4697dd CB |
1068 | "overflows space", |
1069 | (long long)metadump.cur_ino); | |
61983f67 BN |
1070 | break; |
1071 | } | |
1072 | ||
fb4697dd | 1073 | if (metadump.obfuscate) { |
87c955c3 ES |
1074 | generate_obfuscated_name(0, asfep->namelen, |
1075 | &asfep->nameval[0]); | |
1076 | memset(&asfep->nameval[asfep->namelen], 'v', | |
1077 | asfep->valuelen); | |
1078 | } | |
61983f67 | 1079 | |
cc3650f7 | 1080 | asfep = (struct xfs_attr_sf_entry *)((char *)asfep + |
24b24fad | 1081 | xfs_attr_sf_entsize(asfep)); |
61983f67 | 1082 | } |
87c955c3 ES |
1083 | |
1084 | /* zero stale data in rest of space in attr fork, if any */ | |
fb4697dd CB |
1085 | if (metadump.zero_stale_data && |
1086 | (ino_attr_size < XFS_DFORK_ASIZE(dip, mp))) | |
87c955c3 | 1087 | memset(asfep, 0, XFS_DFORK_ASIZE(dip, mp) - ino_attr_size); |
61983f67 BN |
1088 | } |
1089 | ||
c3387ef7 SR |
1090 | static void |
1091 | process_dir_free_block( | |
1092 | char *block) | |
1093 | { | |
1094 | struct xfs_dir2_free *free; | |
1095 | struct xfs_dir3_icfree_hdr freehdr; | |
1096 | ||
fb4697dd | 1097 | if (!metadump.zero_stale_data) |
c3387ef7 SR |
1098 | return; |
1099 | ||
1100 | free = (struct xfs_dir2_free *)block; | |
61e2142e | 1101 | libxfs_dir2_free_hdr_from_disk(mp, &freehdr, free); |
c3387ef7 SR |
1102 | |
1103 | switch (freehdr.magic) { | |
1104 | case XFS_DIR2_FREE_MAGIC: | |
1105 | case XFS_DIR3_FREE_MAGIC: { | |
1106 | __be16 *bests; | |
1107 | char *high; | |
1108 | int used; | |
1109 | ||
1110 | /* Zero out space from end of bests[] to end of block */ | |
cb5d1930 | 1111 | bests = freehdr.bests; |
c3387ef7 SR |
1112 | high = (char *)&bests[freehdr.nvalid]; |
1113 | used = high - (char*)free; | |
1114 | memset(high, 0, mp->m_dir_geo->blksize - used); | |
1115 | iocur_top->need_crc = 1; | |
1116 | break; | |
1117 | } | |
1118 | default: | |
fb4697dd | 1119 | if (metadump.show_warnings) |
c3387ef7 SR |
1120 | print_warning("invalid magic in dir inode %llu " |
1121 | "free block", | |
fb4697dd | 1122 | (unsigned long long)metadump.cur_ino); |
c3387ef7 SR |
1123 | break; |
1124 | } | |
1125 | } | |
1126 | ||
a56173b5 ES |
1127 | static void |
1128 | process_dir_leaf_block( | |
1129 | char *block) | |
1130 | { | |
1131 | struct xfs_dir2_leaf *leaf; | |
11f3d9ff | 1132 | struct xfs_dir3_icleaf_hdr leafhdr; |
a56173b5 | 1133 | |
fb4697dd | 1134 | if (!metadump.zero_stale_data) |
a56173b5 ES |
1135 | return; |
1136 | ||
1137 | /* Yes, this works for dir2 & dir3. Difference is padding. */ | |
1138 | leaf = (struct xfs_dir2_leaf *)block; | |
9db68faf | 1139 | libxfs_dir2_leaf_hdr_from_disk(mp, &leafhdr, leaf); |
a56173b5 | 1140 | |
11f3d9ff SR |
1141 | switch (leafhdr.magic) { |
1142 | case XFS_DIR2_LEAF1_MAGIC: | |
1143 | case XFS_DIR3_LEAF1_MAGIC: { | |
a56173b5 ES |
1144 | struct xfs_dir2_leaf_tail *ltp; |
1145 | __be16 *lbp; | |
a56173b5 ES |
1146 | char *free; /* end of ents */ |
1147 | ||
11f3d9ff | 1148 | /* Zero out space from end of ents[] to bests */ |
a2279497 | 1149 | free = (char *)&leafhdr.ents[leafhdr.count]; |
a56173b5 ES |
1150 | ltp = xfs_dir2_leaf_tail_p(mp->m_dir_geo, leaf); |
1151 | lbp = xfs_dir2_leaf_bests_p(ltp); | |
1152 | memset(free, 0, (char *)lbp - free); | |
1153 | iocur_top->need_crc = 1; | |
11f3d9ff SR |
1154 | break; |
1155 | } | |
1156 | case XFS_DIR2_LEAFN_MAGIC: | |
1157 | case XFS_DIR3_LEAFN_MAGIC: { | |
11f3d9ff SR |
1158 | char *free; |
1159 | int used; | |
1160 | ||
1161 | /* Zero out space from end of ents[] to end of block */ | |
a2279497 | 1162 | free = (char *)&leafhdr.ents[leafhdr.count]; |
11f3d9ff SR |
1163 | used = free - (char*)leaf; |
1164 | memset(free, 0, mp->m_dir_geo->blksize - used); | |
1165 | iocur_top->need_crc = 1; | |
1166 | break; | |
1167 | } | |
1168 | default: | |
1169 | break; | |
a56173b5 ES |
1170 | } |
1171 | } | |
1172 | ||
61983f67 | 1173 | static void |
6d34e8b3 | 1174 | process_dir_data_block( |
6e79202b | 1175 | char *block, |
5a35bf2c | 1176 | xfs_fileoff_t offset, |
6e79202b | 1177 | int is_block_format) |
61983f67 BN |
1178 | { |
1179 | /* | |
1180 | * we have to rely on the fileoffset and signature of the block to | |
1181 | * handle it's contents. If it's invalid, leave it alone. | |
1182 | * for multi-fsblock dir blocks, if a name crosses an extent boundary, | |
1183 | * ignore it and continue. | |
1184 | */ | |
6e79202b DC |
1185 | int dir_offset; |
1186 | char *ptr; | |
1187 | char *endptr; | |
1188 | int end_of_data; | |
1189 | int wantmagic; | |
1190 | struct xfs_dir2_data_hdr *datahdr; | |
1191 | ||
1192 | datahdr = (struct xfs_dir2_data_hdr *)block; | |
1193 | ||
6e79202b DC |
1194 | if (is_block_format) { |
1195 | xfs_dir2_leaf_entry_t *blp; | |
1196 | xfs_dir2_block_tail_t *btp; | |
1197 | ||
ff105f75 | 1198 | btp = xfs_dir2_block_tail_p(mp->m_dir_geo, datahdr); |
6e79202b DC |
1199 | blp = xfs_dir2_block_leaf_p(btp); |
1200 | if ((char *)blp > (char *)btp) | |
1201 | blp = (xfs_dir2_leaf_entry_t *)btp; | |
1202 | ||
1203 | end_of_data = (char *)blp - block; | |
2660e653 | 1204 | if (xfs_has_crc(mp)) |
6e79202b DC |
1205 | wantmagic = XFS_DIR3_BLOCK_MAGIC; |
1206 | else | |
1207 | wantmagic = XFS_DIR2_BLOCK_MAGIC; | |
1208 | } else { /* leaf/node format */ | |
ff105f75 | 1209 | end_of_data = mp->m_dir_geo->fsbcount << mp->m_sb.sb_blocklog; |
2660e653 | 1210 | if (xfs_has_crc(mp)) |
6e79202b DC |
1211 | wantmagic = XFS_DIR3_DATA_MAGIC; |
1212 | else | |
1213 | wantmagic = XFS_DIR2_DATA_MAGIC; | |
1214 | } | |
61983f67 | 1215 | |
6e79202b | 1216 | if (be32_to_cpu(datahdr->magic) != wantmagic) { |
fb4697dd | 1217 | if (metadump.show_warnings) |
6e79202b DC |
1218 | print_warning( |
1219 | "invalid magic in dir inode %llu block %ld", | |
fb4697dd | 1220 | (unsigned long long)metadump.cur_ino, (long)offset); |
6e79202b DC |
1221 | return; |
1222 | } | |
61983f67 | 1223 | |
58a1d356 | 1224 | dir_offset = mp->m_dir_geo->data_entry_offset; |
6e79202b | 1225 | ptr = block + dir_offset; |
ff105f75 | 1226 | endptr = block + mp->m_dir_geo->blksize; |
61983f67 | 1227 | |
6e79202b DC |
1228 | while (ptr < endptr && dir_offset < end_of_data) { |
1229 | xfs_dir2_data_entry_t *dep; | |
1230 | xfs_dir2_data_unused_t *dup; | |
1231 | int length; | |
61983f67 | 1232 | |
6e79202b | 1233 | dup = (xfs_dir2_data_unused_t *)ptr; |
61983f67 | 1234 | |
6e79202b | 1235 | if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) { |
8ab6f7e0 ES |
1236 | int free_length = be16_to_cpu(dup->length); |
1237 | if (dir_offset + free_length > end_of_data || | |
1238 | !free_length || | |
1239 | (free_length & (XFS_DIR2_DATA_ALIGN - 1))) { | |
fb4697dd | 1240 | if (metadump.show_warnings) |
6e79202b DC |
1241 | print_warning( |
1242 | "invalid length for dir free space in inode %llu", | |
fb4697dd | 1243 | (long long)metadump.cur_ino); |
6e79202b | 1244 | return; |
61983f67 | 1245 | } |
6e79202b DC |
1246 | if (be16_to_cpu(*xfs_dir2_data_unused_tag_p(dup)) != |
1247 | dir_offset) | |
1248 | return; | |
8ab6f7e0 ES |
1249 | dir_offset += free_length; |
1250 | ptr += free_length; | |
6d34e8b3 ES |
1251 | /* |
1252 | * Zero the unused space up to the tag - the tag is | |
1253 | * actually at a variable offset, so zeroing &dup->tag | |
1254 | * is zeroing the free space in between | |
1255 | */ | |
fb4697dd | 1256 | if (metadump.zero_stale_data) { |
8ab6f7e0 | 1257 | int zlen = free_length - |
6d34e8b3 ES |
1258 | sizeof(xfs_dir2_data_unused_t); |
1259 | ||
1260 | if (zlen > 0) { | |
1261 | memset(&dup->tag, 0, zlen); | |
1262 | iocur_top->need_crc = 1; | |
1263 | } | |
1264 | } | |
6e79202b DC |
1265 | if (dir_offset >= end_of_data || ptr >= endptr) |
1266 | return; | |
1267 | } | |
1268 | ||
1269 | dep = (xfs_dir2_data_entry_t *)ptr; | |
271a654f | 1270 | length = libxfs_dir2_data_entsize(mp, dep->namelen); |
6e79202b DC |
1271 | |
1272 | if (dir_offset + length > end_of_data || | |
1273 | ptr + length > endptr) { | |
fb4697dd | 1274 | if (metadump.show_warnings) |
6e79202b DC |
1275 | print_warning( |
1276 | "invalid length for dir entry name in inode %llu", | |
fb4697dd | 1277 | (long long)metadump.cur_ino); |
6e79202b | 1278 | return; |
61983f67 | 1279 | } |
823711f2 | 1280 | if (be16_to_cpu(*libxfs_dir2_data_entry_tag_p(mp, dep)) != |
6e79202b DC |
1281 | dir_offset) |
1282 | return; | |
6d34e8b3 | 1283 | |
fb4697dd | 1284 | if (metadump.obfuscate) |
6d34e8b3 | 1285 | generate_obfuscated_name(be64_to_cpu(dep->inumber), |
6e79202b DC |
1286 | dep->namelen, &dep->name[0]); |
1287 | dir_offset += length; | |
1288 | ptr += length; | |
6d34e8b3 | 1289 | /* Zero the unused space after name, up to the tag */ |
fb4697dd | 1290 | if (metadump.zero_stale_data) { |
6d34e8b3 ES |
1291 | /* 1 byte for ftype; don't bother with conditional */ |
1292 | int zlen = | |
823711f2 | 1293 | (char *)libxfs_dir2_data_entry_tag_p(mp, dep) - |
6d34e8b3 ES |
1294 | (char *)&dep->name[dep->namelen] - 1; |
1295 | if (zlen > 0) { | |
1296 | memset(&dep->name[dep->namelen] + 1, 0, zlen); | |
1297 | iocur_top->need_crc = 1; | |
1298 | } | |
1299 | } | |
61983f67 BN |
1300 | } |
1301 | } | |
1302 | ||
0e81250e | 1303 | static int |
23b2ae23 | 1304 | process_symlink_block( |
0e81250e DW |
1305 | xfs_fileoff_t o, |
1306 | xfs_fsblock_t s, | |
1307 | xfs_filblks_t c, | |
1308 | typnm_t btype, | |
1309 | xfs_fileoff_t last) | |
61983f67 | 1310 | { |
0e81250e DW |
1311 | struct bbmap map; |
1312 | char *link; | |
a196ca65 | 1313 | int rval = 1; |
0e81250e DW |
1314 | |
1315 | push_cur(); | |
1316 | map.nmaps = 1; | |
1317 | map.b[0].bm_bn = XFS_FSB_TO_DADDR(mp, s); | |
1318 | map.b[0].bm_len = XFS_FSB_TO_BB(mp, c); | |
1319 | set_cur(&typtab[btype], 0, 0, DB_RING_IGN, &map); | |
1320 | if (!iocur_top->data) { | |
1321 | xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp, s); | |
1322 | xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(mp, s); | |
1323 | ||
1324 | print_warning("cannot read %s block %u/%u (%llu)", | |
1325 | typtab[btype].name, agno, agbno, s); | |
fb4697dd | 1326 | rval = !metadump.stop_on_read_error; |
0e81250e DW |
1327 | goto out_pop; |
1328 | } | |
1329 | link = iocur_top->data; | |
23b2ae23 | 1330 | |
2660e653 | 1331 | if (xfs_has_crc((mp))) |
23b2ae23 ES |
1332 | link += sizeof(struct xfs_dsymlink_hdr); |
1333 | ||
fb4697dd | 1334 | if (metadump.obfuscate) |
23b2ae23 ES |
1335 | obfuscate_path_components(link, XFS_SYMLINK_BUF_SPACE(mp, |
1336 | mp->m_sb.sb_blocksize)); | |
fb4697dd | 1337 | if (metadump.zero_stale_data) { |
23b2ae23 | 1338 | size_t linklen, zlen; |
80917c1a | 1339 | |
23b2ae23 ES |
1340 | linklen = strlen(link); |
1341 | zlen = mp->m_sb.sb_blocksize - linklen; | |
2660e653 | 1342 | if (xfs_has_crc(mp)) |
23b2ae23 ES |
1343 | zlen -= sizeof(struct xfs_dsymlink_hdr); |
1344 | if (zlen < mp->m_sb.sb_blocksize) | |
1345 | memset(link + linklen, 0, zlen); | |
1346 | } | |
0e81250e DW |
1347 | |
1348 | iocur_top->need_crc = 1; | |
a196ca65 DC |
1349 | if (write_buf(iocur_top)) |
1350 | rval = 0; | |
0e81250e DW |
1351 | out_pop: |
1352 | pop_cur(); | |
a196ca65 | 1353 | return rval; |
61983f67 BN |
1354 | } |
1355 | ||
1356 | #define MAX_REMOTE_VALS 4095 | |
1357 | ||
1358 | static struct attr_data_s { | |
1359 | int remote_val_count; | |
1360 | xfs_dablk_t remote_vals[MAX_REMOTE_VALS]; | |
1361 | } attr_data; | |
1362 | ||
1363 | static inline void | |
1364 | add_remote_vals( | |
1365 | xfs_dablk_t blockidx, | |
1366 | int length) | |
1367 | { | |
1368 | while (length > 0 && attr_data.remote_val_count < MAX_REMOTE_VALS) { | |
1369 | attr_data.remote_vals[attr_data.remote_val_count] = blockidx; | |
1370 | attr_data.remote_val_count++; | |
1371 | blockidx++; | |
e66eae01 | 1372 | length -= XFS_ATTR3_RMT_BUF_SPACE(mp, mp->m_sb.sb_blocksize); |
61983f67 | 1373 | } |
ed737480 DC |
1374 | |
1375 | if (attr_data.remote_val_count >= MAX_REMOTE_VALS) { | |
1376 | print_warning( | |
1377 | "Overflowed attr obfuscation array. No longer obfuscating remote attrs."); | |
1378 | } | |
61983f67 BN |
1379 | } |
1380 | ||
80853366 | 1381 | /* Handle remote and leaf attributes */ |
61983f67 | 1382 | static void |
70099c89 | 1383 | process_attr_block( |
80853366 ES |
1384 | char *block, |
1385 | xfs_fileoff_t offset) | |
61983f67 | 1386 | { |
80853366 ES |
1387 | struct xfs_attr_leafblock *leaf; |
1388 | struct xfs_attr3_icleaf_hdr hdr; | |
1389 | int i; | |
1390 | int nentries; | |
1391 | xfs_attr_leaf_entry_t *entry; | |
1392 | xfs_attr_leaf_name_local_t *local; | |
1393 | xfs_attr_leaf_name_remote_t *remote; | |
14f8b681 | 1394 | uint32_t bs = mp->m_sb.sb_blocksize; |
70099c89 | 1395 | char *first_name; |
80853366 | 1396 | |
61983f67 | 1397 | |
ed737480 | 1398 | leaf = (xfs_attr_leafblock_t *)block; |
61983f67 | 1399 | |
80853366 ES |
1400 | /* Remote attributes - attr3 has XFS_ATTR3_RMT_MAGIC, attr has none */ |
1401 | if ((be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR_LEAF_MAGIC) && | |
1402 | (be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR3_LEAF_MAGIC)) { | |
ed737480 | 1403 | for (i = 0; i < attr_data.remote_val_count; i++) { |
fb4697dd CB |
1404 | if (metadump.obfuscate && |
1405 | attr_data.remote_vals[i] == offset) | |
80853366 ES |
1406 | /* Macros to handle both attr and attr3 */ |
1407 | memset(block + | |
1408 | (bs - XFS_ATTR3_RMT_BUF_SPACE(mp, bs)), | |
1941482c | 1409 | 'v', XFS_ATTR3_RMT_BUF_SPACE(mp, bs)); |
61983f67 | 1410 | } |
ed737480 DC |
1411 | return; |
1412 | } | |
61983f67 | 1413 | |
80853366 | 1414 | /* Ok, it's a leaf - get header; accounts for crc & non-crc */ |
19ebedcf | 1415 | xfs_attr3_leaf_hdr_from_disk(mp->m_attr_geo, &hdr, leaf); |
80853366 ES |
1416 | |
1417 | nentries = hdr.count; | |
59562913 ES |
1418 | if (nentries == 0 || |
1419 | nentries * sizeof(xfs_attr_leaf_entry_t) + | |
80853366 ES |
1420 | xfs_attr3_leaf_hdr_size(leaf) > |
1421 | XFS_ATTR3_RMT_BUF_SPACE(mp, bs)) { | |
fb4697dd | 1422 | if (metadump.show_warnings) |
ed737480 | 1423 | print_warning("invalid attr count in inode %llu", |
fb4697dd | 1424 | (long long)metadump.cur_ino); |
ed737480 DC |
1425 | return; |
1426 | } | |
1427 | ||
80853366 | 1428 | entry = xfs_attr3_leaf_entryp(leaf); |
70099c89 ES |
1429 | /* We will move this as we parse */ |
1430 | first_name = NULL; | |
80853366 | 1431 | for (i = 0; i < nentries; i++, entry++) { |
70099c89 ES |
1432 | int nlen, vlen, zlen; |
1433 | ||
1434 | /* Grows up; if this name is topmost, move first_name */ | |
1435 | if (!first_name || xfs_attr3_leaf_name(leaf, i) < first_name) | |
1436 | first_name = xfs_attr3_leaf_name(leaf, i); | |
1437 | ||
ff105f75 | 1438 | if (be16_to_cpu(entry->nameidx) > mp->m_sb.sb_blocksize) { |
fb4697dd | 1439 | if (metadump.show_warnings) |
ed737480 DC |
1440 | print_warning( |
1441 | "invalid attr nameidx in inode %llu", | |
fb4697dd | 1442 | (long long)metadump.cur_ino); |
ed737480 | 1443 | break; |
61983f67 | 1444 | } |
ed737480 DC |
1445 | if (entry->flags & XFS_ATTR_LOCAL) { |
1446 | local = xfs_attr3_leaf_name_local(leaf, i); | |
1447 | if (local->namelen == 0) { | |
fb4697dd | 1448 | if (metadump.show_warnings) |
ed737480 DC |
1449 | print_warning( |
1450 | "zero length for attr name in inode %llu", | |
fb4697dd | 1451 | (long long)metadump.cur_ino); |
61983f67 BN |
1452 | break; |
1453 | } | |
fb4697dd | 1454 | if (metadump.obfuscate) { |
70099c89 ES |
1455 | generate_obfuscated_name(0, local->namelen, |
1456 | &local->nameval[0]); | |
1457 | memset(&local->nameval[local->namelen], 'v', | |
1458 | be16_to_cpu(local->valuelen)); | |
1459 | } | |
1460 | /* zero from end of nameval[] to next name start */ | |
1461 | nlen = local->namelen; | |
1462 | vlen = be16_to_cpu(local->valuelen); | |
1463 | zlen = xfs_attr_leaf_entsize_local(nlen, vlen) - | |
39e9f4c2 | 1464 | (offsetof(struct xfs_attr_leaf_name_local, nameval) + |
70099c89 | 1465 | nlen + vlen); |
fb4697dd | 1466 | if (metadump.zero_stale_data) |
70099c89 | 1467 | memset(&local->nameval[nlen + vlen], 0, zlen); |
ed737480 DC |
1468 | } else { |
1469 | remote = xfs_attr3_leaf_name_remote(leaf, i); | |
1470 | if (remote->namelen == 0 || remote->valueblk == 0) { | |
fb4697dd | 1471 | if (metadump.show_warnings) |
ed737480 DC |
1472 | print_warning( |
1473 | "invalid attr entry in inode %llu", | |
fb4697dd | 1474 | (long long)metadump.cur_ino); |
ed737480 | 1475 | break; |
61983f67 | 1476 | } |
fb4697dd | 1477 | if (metadump.obfuscate) { |
70099c89 ES |
1478 | generate_obfuscated_name(0, remote->namelen, |
1479 | &remote->name[0]); | |
1480 | add_remote_vals(be32_to_cpu(remote->valueblk), | |
1481 | be32_to_cpu(remote->valuelen)); | |
1482 | } | |
1483 | /* zero from end of name[] to next name start */ | |
1484 | nlen = remote->namelen; | |
1485 | zlen = xfs_attr_leaf_entsize_remote(nlen) - | |
39e9f4c2 | 1486 | (offsetof(struct xfs_attr_leaf_name_remote, name) + |
70099c89 | 1487 | nlen); |
fb4697dd | 1488 | if (metadump.zero_stale_data) |
70099c89 | 1489 | memset(&remote->name[nlen], 0, zlen); |
61983f67 BN |
1490 | } |
1491 | } | |
70099c89 ES |
1492 | |
1493 | /* Zero from end of entries array to the first name/val */ | |
fb4697dd | 1494 | if (metadump.zero_stale_data) { |
70099c89 ES |
1495 | struct xfs_attr_leaf_entry *entries; |
1496 | ||
1497 | entries = xfs_attr3_leaf_entryp(leaf); | |
1498 | memset(&entries[nentries], 0, | |
1499 | first_name - (char *)&entries[nentries]); | |
1500 | } | |
61983f67 BN |
1501 | } |
1502 | ||
70099c89 | 1503 | /* Processes symlinks, attrs, directories ... */ |
d452ae4d DC |
1504 | static int |
1505 | process_single_fsb_objects( | |
5a35bf2c DC |
1506 | xfs_fileoff_t o, |
1507 | xfs_fsblock_t s, | |
1508 | xfs_filblks_t c, | |
d452ae4d | 1509 | typnm_t btype, |
5a35bf2c | 1510 | xfs_fileoff_t last) |
d452ae4d | 1511 | { |
a196ca65 | 1512 | int rval = 1; |
ed737480 | 1513 | char *dp; |
ed737480 | 1514 | int i; |
d452ae4d | 1515 | |
0c6b1caf DC |
1516 | for (i = 0; i < c; i++) { |
1517 | push_cur(); | |
1518 | set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, s), blkbb, | |
1519 | DB_RING_IGN, NULL); | |
d452ae4d | 1520 | |
0c6b1caf DC |
1521 | if (!iocur_top->data) { |
1522 | xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp, s); | |
1523 | xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(mp, s); | |
61983f67 | 1524 | |
0c6b1caf DC |
1525 | print_warning("cannot read %s block %u/%u (%llu)", |
1526 | typtab[btype].name, agno, agbno, s); | |
fb4697dd | 1527 | rval = !metadump.stop_on_read_error; |
0c6b1caf | 1528 | goto out_pop; |
d452ae4d | 1529 | |
0c6b1caf | 1530 | } |
d452ae4d | 1531 | |
fb4697dd | 1532 | if (!metadump.obfuscate && !metadump.zero_stale_data) |
0c6b1caf | 1533 | goto write; |
d452ae4d | 1534 | |
c83c169e | 1535 | /* Zero unused part of interior nodes */ |
fb4697dd | 1536 | if (metadump.zero_stale_data) { |
c83c169e ES |
1537 | xfs_da_intnode_t *node = iocur_top->data; |
1538 | int magic = be16_to_cpu(node->hdr.info.magic); | |
1539 | ||
1540 | if (magic == XFS_DA_NODE_MAGIC || | |
1541 | magic == XFS_DA3_NODE_MAGIC) { | |
1542 | struct xfs_da3_icnode_hdr hdr; | |
1543 | int used; | |
1544 | ||
08c16786 | 1545 | libxfs_da3_node_hdr_from_disk(mp, &hdr, node); |
52be9b6a CH |
1546 | switch (btype) { |
1547 | case TYP_DIR2: | |
1548 | used = mp->m_dir_geo->node_hdr_size; | |
1549 | break; | |
1550 | case TYP_ATTR: | |
1551 | used = mp->m_attr_geo->node_hdr_size; | |
1552 | break; | |
1553 | default: | |
1554 | /* unknown type, don't zero anything */ | |
1555 | used = mp->m_sb.sb_blocksize; | |
1556 | break; | |
1557 | } | |
c83c169e ES |
1558 | |
1559 | used += hdr.count | |
1560 | * sizeof(struct xfs_da_node_entry); | |
1561 | ||
1562 | if (used < mp->m_sb.sb_blocksize) { | |
1563 | memset((char *)node + used, 0, | |
1564 | mp->m_sb.sb_blocksize - used); | |
1565 | iocur_top->need_crc = 1; | |
1566 | } | |
1567 | } | |
1568 | } | |
1569 | ||
1570 | /* Handle leaf nodes */ | |
0c6b1caf | 1571 | dp = iocur_top->data; |
ed737480 DC |
1572 | switch (btype) { |
1573 | case TYP_DIR2: | |
a56173b5 | 1574 | if (o >= mp->m_dir_geo->freeblk) { |
c3387ef7 | 1575 | process_dir_free_block(dp); |
a56173b5 ES |
1576 | } else if (o >= mp->m_dir_geo->leafblk) { |
1577 | process_dir_leaf_block(dp); | |
1578 | } else { | |
1579 | process_dir_data_block(dp, o, | |
ff105f75 | 1580 | last == mp->m_dir_geo->fsbcount); |
a56173b5 | 1581 | } |
fd491857 | 1582 | iocur_top->need_crc = 1; |
ed737480 | 1583 | break; |
ed737480 | 1584 | case TYP_ATTR: |
70099c89 ES |
1585 | process_attr_block(dp, o); |
1586 | iocur_top->need_crc = 1; | |
ed737480 DC |
1587 | break; |
1588 | default: | |
1589 | break; | |
1590 | } | |
0c6b1caf DC |
1591 | |
1592 | write: | |
a196ca65 DC |
1593 | if (write_buf(iocur_top)) |
1594 | rval = 0; | |
0c6b1caf DC |
1595 | out_pop: |
1596 | pop_cur(); | |
a196ca65 | 1597 | if (!rval) |
0c6b1caf | 1598 | break; |
ed737480 | 1599 | o++; |
0c6b1caf | 1600 | s++; |
d452ae4d | 1601 | } |
d452ae4d | 1602 | |
a196ca65 | 1603 | return rval; |
d452ae4d DC |
1604 | } |
1605 | ||
6e79202b DC |
1606 | /* |
1607 | * Static map to aggregate multiple extents into a single directory block. | |
1608 | */ | |
1609 | static struct bbmap mfsb_map; | |
1610 | static int mfsb_length; | |
1611 | ||
d452ae4d | 1612 | static int |
9e43c345 | 1613 | process_multi_fsb_dir( |
5a35bf2c DC |
1614 | xfs_fileoff_t o, |
1615 | xfs_fsblock_t s, | |
1616 | xfs_filblks_t c, | |
d452ae4d | 1617 | typnm_t btype, |
5a35bf2c | 1618 | xfs_fileoff_t last) |
d452ae4d | 1619 | { |
b04e7e92 | 1620 | char *dp; |
a196ca65 | 1621 | int rval = 1; |
ed737480 | 1622 | |
6e79202b DC |
1623 | while (c > 0) { |
1624 | unsigned int bm_len; | |
ed737480 | 1625 | |
ff105f75 DC |
1626 | if (mfsb_length + c >= mp->m_dir_geo->fsbcount) { |
1627 | bm_len = mp->m_dir_geo->fsbcount - mfsb_length; | |
6e79202b DC |
1628 | mfsb_length = 0; |
1629 | } else { | |
1630 | mfsb_length += c; | |
1631 | bm_len = c; | |
1632 | } | |
ed737480 | 1633 | |
6e79202b DC |
1634 | mfsb_map.b[mfsb_map.nmaps].bm_bn = XFS_FSB_TO_DADDR(mp, s); |
1635 | mfsb_map.b[mfsb_map.nmaps].bm_len = XFS_FSB_TO_BB(mp, bm_len); | |
1636 | mfsb_map.nmaps++; | |
ed737480 | 1637 | |
6e79202b DC |
1638 | if (mfsb_length == 0) { |
1639 | push_cur(); | |
1640 | set_cur(&typtab[btype], 0, 0, DB_RING_IGN, &mfsb_map); | |
1641 | if (!iocur_top->data) { | |
1642 | xfs_agnumber_t agno = XFS_FSB_TO_AGNO(mp, s); | |
1643 | xfs_agblock_t agbno = XFS_FSB_TO_AGBNO(mp, s); | |
ed737480 | 1644 | |
6e79202b DC |
1645 | print_warning("cannot read %s block %u/%u (%llu)", |
1646 | typtab[btype].name, agno, agbno, s); | |
fb4697dd | 1647 | rval = !metadump.stop_on_read_error; |
6e79202b | 1648 | goto out_pop; |
ed737480 | 1649 | |
6e79202b | 1650 | } |
ed737480 | 1651 | |
fb4697dd | 1652 | if (!metadump.obfuscate && !metadump.zero_stale_data) |
b04e7e92 | 1653 | goto write; |
6e79202b | 1654 | |
b04e7e92 SR |
1655 | dp = iocur_top->data; |
1656 | if (o >= mp->m_dir_geo->freeblk) { | |
c3387ef7 | 1657 | process_dir_free_block(dp); |
b04e7e92 SR |
1658 | } else if (o >= mp->m_dir_geo->leafblk) { |
1659 | process_dir_leaf_block(dp); | |
1660 | } else { | |
1661 | process_dir_data_block(dp, o, | |
1662 | last == mp->m_dir_geo->fsbcount); | |
1663 | } | |
fd491857 | 1664 | iocur_top->need_crc = 1; |
b04e7e92 | 1665 | write: |
a196ca65 DC |
1666 | if (write_buf(iocur_top)) |
1667 | rval = 0; | |
ed737480 | 1668 | out_pop: |
6e79202b DC |
1669 | pop_cur(); |
1670 | mfsb_map.nmaps = 0; | |
a196ca65 | 1671 | if (!rval) |
6e79202b DC |
1672 | break; |
1673 | } | |
1674 | c -= bm_len; | |
1675 | s += bm_len; | |
1676 | } | |
1677 | ||
a196ca65 | 1678 | return rval; |
d452ae4d DC |
1679 | } |
1680 | ||
001df390 DW |
1681 | static bool |
1682 | is_multi_fsb_object( | |
1683 | struct xfs_mount *mp, | |
1684 | typnm_t btype) | |
1685 | { | |
1686 | if (btype == TYP_DIR2 && mp->m_dir_geo->fsbcount > 1) | |
1687 | return true; | |
0e81250e DW |
1688 | if (btype == TYP_SYMLINK) |
1689 | return true; | |
001df390 DW |
1690 | return false; |
1691 | } | |
1692 | ||
9e43c345 DW |
1693 | static int |
1694 | process_multi_fsb_objects( | |
1695 | xfs_fileoff_t o, | |
1696 | xfs_fsblock_t s, | |
1697 | xfs_filblks_t c, | |
1698 | typnm_t btype, | |
1699 | xfs_fileoff_t last) | |
1700 | { | |
1701 | switch (btype) { | |
1702 | case TYP_DIR2: | |
1703 | return process_multi_fsb_dir(o, s, c, btype, last); | |
0e81250e DW |
1704 | case TYP_SYMLINK: |
1705 | return process_symlink_block(o, s, c, btype, last); | |
9e43c345 DW |
1706 | default: |
1707 | print_warning("bad type for multi-fsb object %d", btype); | |
a196ca65 | 1708 | return 1; |
9e43c345 DW |
1709 | } |
1710 | } | |
1711 | ||
d452ae4d | 1712 | /* inode copy routines */ |
61983f67 BN |
1713 | static int |
1714 | process_bmbt_reclist( | |
a196ca65 DC |
1715 | xfs_bmbt_rec_t *rp, |
1716 | int numrecs, | |
61983f67 BN |
1717 | typnm_t btype) |
1718 | { | |
1719 | int i; | |
5a35bf2c DC |
1720 | xfs_fileoff_t o, op = NULLFILEOFF; |
1721 | xfs_fsblock_t s; | |
1722 | xfs_filblks_t c, cp = NULLFILEOFF; | |
61983f67 | 1723 | int f; |
5a35bf2c | 1724 | xfs_fileoff_t last; |
88b8e1d6 BN |
1725 | xfs_agnumber_t agno; |
1726 | xfs_agblock_t agbno; | |
001df390 | 1727 | bool is_multi_fsb = is_multi_fsb_object(mp, btype); |
a196ca65 | 1728 | int rval = 1; |
61983f67 BN |
1729 | |
1730 | if (btype == TYP_DATA) | |
1731 | return 1; | |
1732 | ||
1733 | convert_extent(&rp[numrecs - 1], &o, &s, &c, &f); | |
1734 | last = o + c; | |
1735 | ||
1736 | for (i = 0; i < numrecs; i++, rp++) { | |
1737 | convert_extent(rp, &o, &s, &c, &f); | |
1738 | ||
88b8e1d6 BN |
1739 | /* |
1740 | * ignore extents that are clearly bogus, and if a bogus | |
1741 | * one is found, stop processing remaining extents | |
1742 | */ | |
1743 | if (i > 0 && op + cp > o) { | |
fb4697dd | 1744 | if (metadump.show_warnings) |
88b8e1d6 BN |
1745 | print_warning("bmap extent %d in %s ino %llu " |
1746 | "starts at %llu, previous extent " | |
1747 | "ended at %llu", i, | |
fb4697dd CB |
1748 | typtab[btype].name, |
1749 | (long long)metadump.cur_ino, | |
88b8e1d6 BN |
1750 | o, op + cp - 1); |
1751 | break; | |
1752 | } | |
1753 | ||
fb4697dd | 1754 | if (c > metadump.max_extent_size) { |
88b8e1d6 BN |
1755 | /* |
1756 | * since we are only processing non-data extents, | |
1757 | * large numbers of blocks in a metadata extent is | |
1758 | * extremely rare and more than likely to be corrupt. | |
1759 | */ | |
fb4697dd | 1760 | if (metadump.show_warnings) |
88b8e1d6 BN |
1761 | print_warning("suspicious count %u in bmap " |
1762 | "extent %d in %s ino %llu", c, i, | |
fb4697dd CB |
1763 | typtab[btype].name, |
1764 | (long long)metadump.cur_ino); | |
88b8e1d6 BN |
1765 | break; |
1766 | } | |
1767 | ||
1768 | op = o; | |
1769 | cp = c; | |
1770 | ||
1771 | agno = XFS_FSB_TO_AGNO(mp, s); | |
1772 | agbno = XFS_FSB_TO_AGBNO(mp, s); | |
1773 | ||
1774 | if (!valid_bno(agno, agbno)) { | |
fb4697dd | 1775 | if (metadump.show_warnings) |
88b8e1d6 BN |
1776 | print_warning("invalid block number %u/%u " |
1777 | "(%llu) in bmap extent %d in %s ino " | |
1778 | "%llu", agno, agbno, s, i, | |
fb4697dd CB |
1779 | typtab[btype].name, |
1780 | (long long)metadump.cur_ino); | |
88b8e1d6 BN |
1781 | break; |
1782 | } | |
1783 | ||
1784 | if (!valid_bno(agno, agbno + c - 1)) { | |
fb4697dd | 1785 | if (metadump.show_warnings) |
88b8e1d6 BN |
1786 | print_warning("bmap extent %i in %s inode %llu " |
1787 | "overflows AG (end is %u/%u)", i, | |
fb4697dd CB |
1788 | typtab[btype].name, |
1789 | (long long)metadump.cur_ino, | |
88b8e1d6 BN |
1790 | agno, agbno + c - 1); |
1791 | break; | |
1792 | } | |
1793 | ||
d452ae4d | 1794 | /* multi-extent blocks require special handling */ |
001df390 | 1795 | if (is_multi_fsb) |
a196ca65 | 1796 | rval = process_multi_fsb_objects(o, s, c, btype, |
001df390 DW |
1797 | last); |
1798 | else | |
a196ca65 | 1799 | rval = process_single_fsb_objects(o, s, c, btype, |
001df390 | 1800 | last); |
a196ca65 DC |
1801 | if (!rval) |
1802 | break; | |
61983f67 BN |
1803 | } |
1804 | ||
a196ca65 | 1805 | return rval; |
61983f67 BN |
1806 | } |
1807 | ||
1808 | static int | |
1809 | scanfunc_bmap( | |
b194c7d8 | 1810 | struct xfs_btree_block *block, |
61983f67 BN |
1811 | xfs_agnumber_t agno, |
1812 | xfs_agblock_t agbno, | |
1813 | int level, | |
1814 | typnm_t btype, | |
1815 | void *arg) /* ptr to itype */ | |
1816 | { | |
1817 | int i; | |
1818 | xfs_bmbt_ptr_t *pp; | |
61983f67 BN |
1819 | int nrecs; |
1820 | ||
b194c7d8 | 1821 | nrecs = be16_to_cpu(block->bb_numrecs); |
61983f67 BN |
1822 | |
1823 | if (level == 0) { | |
1824 | if (nrecs > mp->m_bmap_dmxr[0]) { | |
fb4697dd | 1825 | if (metadump.show_warnings) |
61983f67 BN |
1826 | print_warning("invalid numrecs (%u) in %s " |
1827 | "block %u/%u", nrecs, | |
1828 | typtab[btype].name, agno, agbno); | |
1829 | return 1; | |
1830 | } | |
b3563c19 BN |
1831 | return process_bmbt_reclist(XFS_BMBT_REC_ADDR(mp, block, 1), |
1832 | nrecs, *(typnm_t*)arg); | |
61983f67 BN |
1833 | } |
1834 | ||
1835 | if (nrecs > mp->m_bmap_dmxr[1]) { | |
fb4697dd | 1836 | if (metadump.show_warnings) |
61983f67 BN |
1837 | print_warning("invalid numrecs (%u) in %s block %u/%u", |
1838 | nrecs, typtab[btype].name, agno, agbno); | |
1839 | return 1; | |
1840 | } | |
b3563c19 | 1841 | pp = XFS_BMBT_PTR_ADDR(mp, block, 1, mp->m_bmap_dmxr[1]); |
61983f67 BN |
1842 | for (i = 0; i < nrecs; i++) { |
1843 | xfs_agnumber_t ag; | |
1844 | xfs_agblock_t bno; | |
1845 | ||
1bec3a62 ES |
1846 | ag = XFS_FSB_TO_AGNO(mp, get_unaligned_be64(&pp[i])); |
1847 | bno = XFS_FSB_TO_AGBNO(mp, get_unaligned_be64(&pp[i])); | |
61983f67 BN |
1848 | |
1849 | if (bno == 0 || bno > mp->m_sb.sb_agblocks || | |
1850 | ag > mp->m_sb.sb_agcount) { | |
fb4697dd | 1851 | if (metadump.show_warnings) |
61983f67 BN |
1852 | print_warning("invalid block number (%u/%u) " |
1853 | "in %s block %u/%u", ag, bno, | |
1854 | typtab[btype].name, agno, agbno); | |
1855 | continue; | |
1856 | } | |
1857 | ||
1858 | if (!scan_btree(ag, bno, level, btype, arg, scanfunc_bmap)) | |
1859 | return 0; | |
1860 | } | |
1861 | return 1; | |
1862 | } | |
1863 | ||
1864 | static int | |
1865 | process_btinode( | |
7328ea6e | 1866 | struct xfs_dinode *dip, |
61983f67 BN |
1867 | typnm_t itype) |
1868 | { | |
1869 | xfs_bmdr_block_t *dib; | |
1870 | int i; | |
1871 | xfs_bmbt_ptr_t *pp; | |
61983f67 BN |
1872 | int level; |
1873 | int nrecs; | |
1874 | int maxrecs; | |
1875 | int whichfork; | |
1876 | typnm_t btype; | |
1877 | ||
1878 | whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK; | |
1879 | btype = (itype == TYP_ATTR) ? TYP_BMAPBTA : TYP_BMAPBTD; | |
1880 | ||
1881 | dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork); | |
1882 | level = be16_to_cpu(dib->bb_level); | |
1883 | nrecs = be16_to_cpu(dib->bb_numrecs); | |
1884 | ||
1885 | if (level > XFS_BM_MAXLEVELS(mp, whichfork)) { | |
fb4697dd | 1886 | if (metadump.show_warnings) |
61983f67 | 1887 | print_warning("invalid level (%u) in inode %lld %s " |
fb4697dd CB |
1888 | "root", level, (long long)metadump.cur_ino, |
1889 | typtab[btype].name); | |
61983f67 BN |
1890 | return 1; |
1891 | } | |
1892 | ||
b3563c19 BN |
1893 | if (level == 0) { |
1894 | return process_bmbt_reclist(XFS_BMDR_REC_ADDR(dib, 1), | |
1895 | nrecs, itype); | |
1896 | } | |
61983f67 | 1897 | |
e2f60652 | 1898 | maxrecs = libxfs_bmdr_maxrecs(XFS_DFORK_SIZE(dip, mp, whichfork), 0); |
61983f67 | 1899 | if (nrecs > maxrecs) { |
fb4697dd | 1900 | if (metadump.show_warnings) |
61983f67 | 1901 | print_warning("invalid numrecs (%u) in inode %lld %s " |
fb4697dd CB |
1902 | "root", nrecs, (long long)metadump.cur_ino, |
1903 | typtab[btype].name); | |
61983f67 BN |
1904 | return 1; |
1905 | } | |
1906 | ||
b3563c19 | 1907 | pp = XFS_BMDR_PTR_ADDR(dib, 1, maxrecs); |
07b75e31 | 1908 | |
fb4697dd | 1909 | if (metadump.zero_stale_data) { |
07b75e31 SR |
1910 | char *top; |
1911 | ||
1912 | /* Unused btree key space */ | |
1913 | top = (char*)XFS_BMDR_KEY_ADDR(dib, nrecs + 1); | |
1914 | memset(top, 0, (char*)pp - top); | |
1915 | ||
1916 | /* Unused btree ptr space */ | |
1917 | top = (char*)&pp[nrecs]; | |
1918 | memset(top, 0, (char*)dib + XFS_DFORK_SIZE(dip, mp, whichfork) - top); | |
1919 | } | |
1920 | ||
61983f67 BN |
1921 | for (i = 0; i < nrecs; i++) { |
1922 | xfs_agnumber_t ag; | |
1923 | xfs_agblock_t bno; | |
1924 | ||
1bec3a62 ES |
1925 | ag = XFS_FSB_TO_AGNO(mp, get_unaligned_be64(&pp[i])); |
1926 | bno = XFS_FSB_TO_AGBNO(mp, get_unaligned_be64(&pp[i])); | |
61983f67 BN |
1927 | |
1928 | if (bno == 0 || bno > mp->m_sb.sb_agblocks || | |
1929 | ag > mp->m_sb.sb_agcount) { | |
fb4697dd | 1930 | if (metadump.show_warnings) |
61983f67 | 1931 | print_warning("invalid block number (%u/%u) " |
fb4697dd CB |
1932 | "in inode %llu %s root", ag, bno, |
1933 | (long long)metadump.cur_ino, | |
1934 | typtab[btype].name); | |
61983f67 BN |
1935 | continue; |
1936 | } | |
1937 | ||
1938 | if (!scan_btree(ag, bno, level, btype, &itype, scanfunc_bmap)) | |
1939 | return 0; | |
1940 | } | |
1941 | return 1; | |
1942 | } | |
1943 | ||
1944 | static int | |
1945 | process_exinode( | |
7328ea6e | 1946 | struct xfs_dinode *dip, |
61983f67 BN |
1947 | typnm_t itype) |
1948 | { | |
1949 | int whichfork; | |
87c955c3 | 1950 | int used; |
95e3fc7f | 1951 | xfs_extnum_t nex, max_nex; |
61983f67 BN |
1952 | |
1953 | whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK; | |
1954 | ||
5f70c91b | 1955 | nex = xfs_dfork_nextents(dip, whichfork); |
95e3fc7f DW |
1956 | max_nex = xfs_iext_max_nextents( |
1957 | xfs_dinode_has_large_extent_counts(dip), | |
1958 | whichfork); | |
87c955c3 | 1959 | used = nex * sizeof(xfs_bmbt_rec_t); |
95e3fc7f | 1960 | if (nex > max_nex || used > XFS_DFORK_SIZE(dip, mp, whichfork)) { |
fb4697dd | 1961 | if (metadump.show_warnings) |
95e3fc7f | 1962 | print_warning("bad number of extents %llu in inode %lld", |
fb4697dd CB |
1963 | (unsigned long long)nex, |
1964 | (long long)metadump.cur_ino); | |
88b8e1d6 BN |
1965 | return 1; |
1966 | } | |
1967 | ||
87c955c3 | 1968 | /* Zero unused data fork past used extents */ |
fb4697dd CB |
1969 | if (metadump.zero_stale_data && |
1970 | (used < XFS_DFORK_SIZE(dip, mp, whichfork))) | |
87c955c3 ES |
1971 | memset(XFS_DFORK_PTR(dip, whichfork) + used, 0, |
1972 | XFS_DFORK_SIZE(dip, mp, whichfork) - used); | |
1973 | ||
1974 | ||
88b8e1d6 BN |
1975 | return process_bmbt_reclist((xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip, |
1976 | whichfork), nex, itype); | |
61983f67 BN |
1977 | } |
1978 | ||
1979 | static int | |
1980 | process_inode_data( | |
7328ea6e | 1981 | struct xfs_dinode *dip, |
61983f67 BN |
1982 | typnm_t itype) |
1983 | { | |
56b2de80 | 1984 | switch (dip->di_format) { |
61983f67 | 1985 | case XFS_DINODE_FMT_LOCAL: |
fb4697dd | 1986 | if (!(metadump.obfuscate || metadump.zero_stale_data)) |
38feb6e5 DC |
1987 | break; |
1988 | ||
1989 | /* | |
1990 | * If the fork size is invalid, we can't safely do | |
1991 | * anything with this fork. Leave it alone to preserve | |
1992 | * the information for diagnostic purposes. | |
1993 | */ | |
1994 | if (XFS_DFORK_DSIZE(dip, mp) > XFS_LITINO(mp)) { | |
1995 | print_warning( | |
1996 | "Invalid data fork size (%d) in inode %llu, preserving contents!", | |
1997 | XFS_DFORK_DSIZE(dip, mp), | |
fb4697dd | 1998 | (long long)metadump.cur_ino); |
38feb6e5 DC |
1999 | break; |
2000 | } | |
61983f67 | 2001 | |
38feb6e5 DC |
2002 | switch (itype) { |
2003 | case TYP_DIR2: | |
2004 | process_sf_dir(dip); | |
2005 | break; | |
61983f67 | 2006 | |
38feb6e5 DC |
2007 | case TYP_SYMLINK: |
2008 | process_sf_symlink(dip); | |
2009 | break; | |
2010 | ||
2011 | default: | |
2012 | break; | |
2013 | } | |
61983f67 BN |
2014 | break; |
2015 | ||
2016 | case XFS_DINODE_FMT_EXTENTS: | |
2017 | return process_exinode(dip, itype); | |
2018 | ||
2019 | case XFS_DINODE_FMT_BTREE: | |
2020 | return process_btinode(dip, itype); | |
2021 | } | |
2022 | return 1; | |
2023 | } | |
2024 | ||
a196ca65 | 2025 | static void |
b32d0eb6 | 2026 | process_dev_inode( |
7328ea6e | 2027 | struct xfs_dinode *dip) |
b32d0eb6 | 2028 | { |
5f70c91b | 2029 | if (xfs_dfork_data_extents(dip)) { |
fb4697dd | 2030 | if (metadump.show_warnings) |
b32d0eb6 | 2031 | print_warning("inode %llu has unexpected extents", |
fb4697dd | 2032 | (unsigned long long)metadump.cur_ino); |
a196ca65 DC |
2033 | return; |
2034 | } | |
38feb6e5 DC |
2035 | |
2036 | /* | |
2037 | * If the fork size is invalid, we can't safely do anything with | |
2038 | * this fork. Leave it alone to preserve the information for diagnostic | |
2039 | * purposes. | |
2040 | */ | |
2041 | if (XFS_DFORK_DSIZE(dip, mp) > XFS_LITINO(mp)) { | |
2042 | print_warning( | |
2043 | "Invalid data fork size (%d) in inode %llu, preserving contents!", | |
fb4697dd | 2044 | XFS_DFORK_DSIZE(dip, mp), (long long)metadump.cur_ino); |
38feb6e5 DC |
2045 | return; |
2046 | } | |
2047 | ||
fb4697dd | 2048 | if (metadump.zero_stale_data) { |
a196ca65 | 2049 | unsigned int size = sizeof(xfs_dev_t); |
b32d0eb6 | 2050 | |
a196ca65 DC |
2051 | memset(XFS_DFORK_DPTR(dip) + size, 0, |
2052 | XFS_DFORK_DSIZE(dip, mp) - size); | |
b32d0eb6 SR |
2053 | } |
2054 | } | |
2055 | ||
fd491857 DC |
2056 | /* |
2057 | * when we process the inode, we may change the data in the data and/or | |
2058 | * attribute fork if they are in short form and we are obfuscating names. | |
2059 | * In this case we need to recalculate the CRC of the inode, but we should | |
2060 | * only do that if the CRC in the inode is good to begin with. If the crc | |
2061 | * is not ok, we just leave it alone. | |
2062 | */ | |
61983f67 BN |
2063 | static int |
2064 | process_inode( | |
2065 | xfs_agnumber_t agno, | |
2066 | xfs_agino_t agino, | |
7328ea6e | 2067 | struct xfs_dinode *dip, |
27499a0a | 2068 | bool free_inode) |
61983f67 | 2069 | { |
a196ca65 | 2070 | int rval = 1; |
fd491857 DC |
2071 | bool crc_was_ok = false; /* no recalc by default */ |
2072 | bool need_new_crc = false; | |
61983f67 | 2073 | |
fb4697dd | 2074 | metadump.cur_ino = XFS_AGINO_TO_INO(mp, agno, agino); |
61983f67 | 2075 | |
27499a0a | 2076 | /* we only care about crc recalculation if we will modify the inode. */ |
fb4697dd | 2077 | if (metadump.obfuscate || metadump.zero_stale_data) { |
e2f60652 | 2078 | crc_was_ok = libxfs_verify_cksum((char *)dip, |
fd491857 DC |
2079 | mp->m_sb.sb_inodesize, |
2080 | offsetof(struct xfs_dinode, di_crc)); | |
2081 | } | |
2082 | ||
27499a0a | 2083 | if (free_inode) { |
fb4697dd | 2084 | if (metadump.zero_stale_data) { |
27499a0a | 2085 | /* Zero all of the inode literal area */ |
4de63245 | 2086 | memset(XFS_DFORK_DPTR(dip), 0, XFS_LITINO(mp)); |
27499a0a ES |
2087 | } |
2088 | goto done; | |
2089 | } | |
2090 | ||
61983f67 | 2091 | /* copy appropriate data fork metadata */ |
56b2de80 | 2092 | switch (be16_to_cpu(dip->di_mode) & S_IFMT) { |
61983f67 | 2093 | case S_IFDIR: |
a196ca65 | 2094 | rval = process_inode_data(dip, TYP_DIR2); |
fd491857 | 2095 | if (dip->di_format == XFS_DINODE_FMT_LOCAL) |
3ea31010 | 2096 | need_new_crc = true; |
61983f67 BN |
2097 | break; |
2098 | case S_IFLNK: | |
a196ca65 | 2099 | rval = process_inode_data(dip, TYP_SYMLINK); |
fd491857 | 2100 | if (dip->di_format == XFS_DINODE_FMT_LOCAL) |
3ea31010 | 2101 | need_new_crc = true; |
61983f67 | 2102 | break; |
88b8e1d6 | 2103 | case S_IFREG: |
a196ca65 | 2104 | rval = process_inode_data(dip, TYP_DATA); |
88b8e1d6 | 2105 | break; |
b32d0eb6 SR |
2106 | case S_IFIFO: |
2107 | case S_IFCHR: | |
2108 | case S_IFBLK: | |
2109 | case S_IFSOCK: | |
a196ca65 | 2110 | process_dev_inode(dip); |
3ea31010 | 2111 | need_new_crc = true; |
b32d0eb6 SR |
2112 | break; |
2113 | default: | |
2114 | break; | |
61983f67 | 2115 | } |
a85f8b0a | 2116 | nametable_clear(); |
a196ca65 DC |
2117 | if (!rval) |
2118 | goto done; | |
61983f67 | 2119 | |
88b8e1d6 | 2120 | /* copy extended attributes if they exist and forkoff is valid */ |
a196ca65 | 2121 | if (XFS_DFORK_DSIZE(dip, mp) < XFS_LITINO(mp)) { |
61983f67 | 2122 | attr_data.remote_val_count = 0; |
56b2de80 | 2123 | switch (dip->di_aformat) { |
61983f67 | 2124 | case XFS_DINODE_FMT_LOCAL: |
3ea31010 | 2125 | need_new_crc = true; |
fb4697dd CB |
2126 | if (metadump.obfuscate || |
2127 | metadump.zero_stale_data) | |
87c955c3 | 2128 | process_sf_attr(dip); |
61983f67 BN |
2129 | break; |
2130 | ||
2131 | case XFS_DINODE_FMT_EXTENTS: | |
a196ca65 | 2132 | rval = process_exinode(dip, TYP_ATTR); |
61983f67 BN |
2133 | break; |
2134 | ||
2135 | case XFS_DINODE_FMT_BTREE: | |
a196ca65 | 2136 | rval = process_btinode(dip, TYP_ATTR); |
61983f67 BN |
2137 | break; |
2138 | } | |
a85f8b0a | 2139 | nametable_clear(); |
61983f67 | 2140 | } |
fd491857 | 2141 | |
27499a0a ES |
2142 | done: |
2143 | /* Heavy handed but low cost; just do it as a catch-all. */ | |
fb4697dd | 2144 | if (metadump.zero_stale_data) |
3ea31010 | 2145 | need_new_crc = true; |
27499a0a | 2146 | |
fd491857 | 2147 | if (crc_was_ok && need_new_crc) |
f616e2bf | 2148 | libxfs_dinode_calc_crc(mp, dip); |
a196ca65 DC |
2149 | |
2150 | return rval; | |
61983f67 BN |
2151 | } |
2152 | ||
14f8b681 | 2153 | static uint32_t inodes_copied; |
61983f67 BN |
2154 | |
2155 | static int | |
2156 | copy_inode_chunk( | |
2157 | xfs_agnumber_t agno, | |
2158 | xfs_inobt_rec_t *rp) | |
2159 | { | |
2160 | xfs_agino_t agino; | |
2161 | int off; | |
2162 | xfs_agblock_t agbno; | |
04b21e41 | 2163 | xfs_agblock_t end_agbno; |
61983f67 | 2164 | int i; |
d24c0a90 | 2165 | int rval = 0; |
04b21e41 BF |
2166 | int blks_per_buf; |
2167 | int inodes_per_buf; | |
2168 | int ioff; | |
e7fd2b6f | 2169 | struct xfs_ino_geometry *igeo = M_IGEO(mp); |
61983f67 BN |
2170 | |
2171 | agino = be32_to_cpu(rp->ir_startino); | |
2172 | agbno = XFS_AGINO_TO_AGBNO(mp, agino); | |
e7fd2b6f | 2173 | end_agbno = agbno + igeo->ialloc_blks; |
61983f67 BN |
2174 | off = XFS_INO_TO_OFFSET(mp, agino); |
2175 | ||
04b21e41 BF |
2176 | /* |
2177 | * If the fs supports sparse inode records, we must process inodes a | |
2178 | * cluster at a time because that is the sparse allocation granularity. | |
2179 | * Otherwise, we risk CRC corruption errors on reads of inode chunks. | |
2180 | * | |
2181 | * Also make sure that that we don't process more than the single record | |
2182 | * we've been passed (large block sizes can hold multiple inode chunks). | |
2183 | */ | |
2660e653 | 2184 | if (xfs_has_sparseinodes(mp)) |
e7fd2b6f | 2185 | blks_per_buf = igeo->blocks_per_cluster; |
04b21e41 | 2186 | else |
e7fd2b6f | 2187 | blks_per_buf = igeo->ialloc_blks; |
7516da71 | 2188 | inodes_per_buf = min(XFS_FSB_TO_INO(mp, blks_per_buf), |
04b21e41 BF |
2189 | XFS_INODES_PER_CHUNK); |
2190 | ||
2191 | /* | |
2192 | * Sanity check that we only process a single buffer if ir_startino has | |
2193 | * a buffer offset. A non-zero offset implies that the entire chunk lies | |
2194 | * within a block. | |
2195 | */ | |
2196 | if (off && inodes_per_buf != XFS_INODES_PER_CHUNK) { | |
2197 | print_warning("bad starting inode offset %d", off); | |
2198 | return 0; | |
2199 | } | |
2200 | ||
88b8e1d6 BN |
2201 | if (agino == 0 || agino == NULLAGINO || !valid_bno(agno, agbno) || |
2202 | !valid_bno(agno, XFS_AGINO_TO_AGBNO(mp, | |
2203 | agino + XFS_INODES_PER_CHUNK - 1))) { | |
fb4697dd | 2204 | if (metadump.show_warnings) |
88b8e1d6 BN |
2205 | print_warning("bad inode number %llu (%u/%u)", |
2206 | XFS_AGINO_TO_INO(mp, agno, agino), agno, agino); | |
2207 | return 1; | |
2208 | } | |
2209 | ||
88b8e1d6 BN |
2210 | /* |
2211 | * check for basic assumptions about inode chunks, and if any | |
2212 | * assumptions fail, don't process the inode chunk. | |
2213 | */ | |
88b8e1d6 BN |
2214 | if ((mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK && off != 0) || |
2215 | (mp->m_sb.sb_inopblock > XFS_INODES_PER_CHUNK && | |
2216 | off % XFS_INODES_PER_CHUNK != 0) || | |
2660e653 | 2217 | (xfs_has_align(mp) && |
0ab7cbc8 | 2218 | mp->m_sb.sb_inoalignmt != 0 && |
88b8e1d6 | 2219 | agbno % mp->m_sb.sb_inoalignmt != 0)) { |
fb4697dd | 2220 | if (metadump.show_warnings) |
88b8e1d6 BN |
2221 | print_warning("badly aligned inode (start = %llu)", |
2222 | XFS_AGINO_TO_INO(mp, agno, agino)); | |
9180183e BF |
2223 | return 1; |
2224 | } | |
2225 | ||
2226 | push_cur(); | |
04b21e41 BF |
2227 | ioff = 0; |
2228 | while (agbno < end_agbno && ioff < XFS_INODES_PER_CHUNK) { | |
2229 | if (xfs_inobt_is_sparse_disk(rp, ioff)) | |
2230 | goto next_bp; | |
2231 | ||
2232 | set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno), | |
2233 | XFS_FSB_TO_BB(mp, blks_per_buf), DB_RING_IGN, NULL); | |
2234 | if (iocur_top->data == NULL) { | |
2235 | print_warning("cannot read inode block %u/%u", | |
2236 | agno, agbno); | |
fb4697dd | 2237 | rval = !metadump.stop_on_read_error; |
04b21e41 BF |
2238 | goto pop_out; |
2239 | } | |
88b8e1d6 | 2240 | |
04b21e41 | 2241 | for (i = 0; i < inodes_per_buf; i++) { |
7328ea6e | 2242 | struct xfs_dinode *dip; |
61983f67 | 2243 | |
7328ea6e | 2244 | dip = (struct xfs_dinode *)((char *)iocur_top->data + |
04b21e41 | 2245 | ((off + i) << mp->m_sb.sb_inodelog)); |
61983f67 | 2246 | |
04b21e41 BF |
2247 | /* process_inode handles free inodes, too */ |
2248 | if (!process_inode(agno, agino + ioff + i, dip, | |
a196ca65 | 2249 | XFS_INOBT_IS_FREE_DISK(rp, ioff + i))) |
04b21e41 | 2250 | goto pop_out; |
9180183e | 2251 | |
04b21e41 BF |
2252 | inodes_copied++; |
2253 | } | |
61983f67 | 2254 | |
04b21e41 BF |
2255 | if (write_buf(iocur_top)) |
2256 | goto pop_out; | |
2257 | ||
2258 | next_bp: | |
2259 | agbno += blks_per_buf; | |
2260 | ioff += inodes_per_buf; | |
2261 | } | |
61983f67 | 2262 | |
fb4697dd | 2263 | if (metadump.show_progress) |
61983f67 BN |
2264 | print_progress("Copied %u of %u inodes (%u of %u AGs)", |
2265 | inodes_copied, mp->m_sb.sb_icount, agno, | |
2266 | mp->m_sb.sb_agcount); | |
d24c0a90 BN |
2267 | rval = 1; |
2268 | pop_out: | |
61983f67 | 2269 | pop_cur(); |
d24c0a90 | 2270 | return rval; |
61983f67 BN |
2271 | } |
2272 | ||
2273 | static int | |
2274 | scanfunc_ino( | |
b194c7d8 | 2275 | struct xfs_btree_block *block, |
61983f67 BN |
2276 | xfs_agnumber_t agno, |
2277 | xfs_agblock_t agbno, | |
2278 | int level, | |
2279 | typnm_t btype, | |
2280 | void *arg) | |
2281 | { | |
2282 | xfs_inobt_rec_t *rp; | |
2283 | xfs_inobt_ptr_t *pp; | |
2284 | int i; | |
88b8e1d6 | 2285 | int numrecs; |
03e956b2 | 2286 | int finobt = *(int *) arg; |
e7fd2b6f | 2287 | struct xfs_ino_geometry *igeo = M_IGEO(mp); |
88b8e1d6 | 2288 | |
b194c7d8 | 2289 | numrecs = be16_to_cpu(block->bb_numrecs); |
61983f67 BN |
2290 | |
2291 | if (level == 0) { | |
e7fd2b6f | 2292 | if (numrecs > igeo->inobt_mxr[0]) { |
fb4697dd | 2293 | if (metadump.show_warnings) |
88b8e1d6 BN |
2294 | print_warning("invalid numrecs %d in %s " |
2295 | "block %u/%u", numrecs, | |
2296 | typtab[btype].name, agno, agbno); | |
e7fd2b6f | 2297 | numrecs = igeo->inobt_mxr[0]; |
88b8e1d6 | 2298 | } |
03e956b2 BF |
2299 | |
2300 | /* | |
2301 | * Only copy the btree blocks for the finobt. The inobt scan | |
2302 | * copies the inode chunks. | |
2303 | */ | |
2304 | if (finobt) | |
2305 | return 1; | |
2306 | ||
b3563c19 | 2307 | rp = XFS_INOBT_REC_ADDR(mp, block, 1); |
88b8e1d6 | 2308 | for (i = 0; i < numrecs; i++, rp++) { |
61983f67 BN |
2309 | if (!copy_inode_chunk(agno, rp)) |
2310 | return 0; | |
2311 | } | |
88b8e1d6 BN |
2312 | return 1; |
2313 | } | |
2314 | ||
e7fd2b6f | 2315 | if (numrecs > igeo->inobt_mxr[1]) { |
fb4697dd | 2316 | if (metadump.show_warnings) |
88b8e1d6 BN |
2317 | print_warning("invalid numrecs %d in %s block %u/%u", |
2318 | numrecs, typtab[btype].name, agno, agbno); | |
e7fd2b6f | 2319 | numrecs = igeo->inobt_mxr[1]; |
88b8e1d6 BN |
2320 | } |
2321 | ||
e7fd2b6f | 2322 | pp = XFS_INOBT_PTR_ADDR(mp, block, 1, igeo->inobt_mxr[1]); |
88b8e1d6 BN |
2323 | for (i = 0; i < numrecs; i++) { |
2324 | if (!valid_bno(agno, be32_to_cpu(pp[i]))) { | |
fb4697dd | 2325 | if (metadump.show_warnings) |
88b8e1d6 BN |
2326 | print_warning("invalid block number (%u/%u) " |
2327 | "in %s block %u/%u", | |
2328 | agno, be32_to_cpu(pp[i]), | |
2329 | typtab[btype].name, agno, agbno); | |
2330 | continue; | |
61983f67 | 2331 | } |
88b8e1d6 BN |
2332 | if (!scan_btree(agno, be32_to_cpu(pp[i]), level, |
2333 | btype, arg, scanfunc_ino)) | |
2334 | return 0; | |
61983f67 BN |
2335 | } |
2336 | return 1; | |
2337 | } | |
2338 | ||
2339 | static int | |
2340 | copy_inodes( | |
2341 | xfs_agnumber_t agno, | |
2342 | xfs_agi_t *agi) | |
2343 | { | |
2344 | xfs_agblock_t root; | |
2345 | int levels; | |
03e956b2 | 2346 | int finobt = 0; |
61983f67 BN |
2347 | |
2348 | root = be32_to_cpu(agi->agi_root); | |
2349 | levels = be32_to_cpu(agi->agi_level); | |
2350 | ||
2351 | /* validate root and levels before processing the tree */ | |
2352 | if (root == 0 || root > mp->m_sb.sb_agblocks) { | |
fb4697dd | 2353 | if (metadump.show_warnings) |
61983f67 BN |
2354 | print_warning("invalid block number (%u) in inobt " |
2355 | "root in agi %u", root, agno); | |
2356 | return 1; | |
2357 | } | |
716b497a | 2358 | if (levels > M_IGEO(mp)->inobt_maxlevels) { |
fb4697dd | 2359 | if (metadump.show_warnings) |
61983f67 BN |
2360 | print_warning("invalid level (%u) in inobt root " |
2361 | "in agi %u", levels, agno); | |
2362 | return 1; | |
2363 | } | |
2364 | ||
03e956b2 BF |
2365 | if (!scan_btree(agno, root, levels, TYP_INOBT, &finobt, scanfunc_ino)) |
2366 | return 0; | |
2367 | ||
2660e653 | 2368 | if (xfs_has_finobt(mp)) { |
03e956b2 BF |
2369 | root = be32_to_cpu(agi->agi_free_root); |
2370 | levels = be32_to_cpu(agi->agi_free_level); | |
2371 | ||
9b9c121a | 2372 | if (root == 0 || root > mp->m_sb.sb_agblocks) { |
fb4697dd | 2373 | if (metadump.show_warnings) |
9b9c121a DW |
2374 | print_warning("invalid block number (%u) in " |
2375 | "finobt root in agi %u", root, | |
2376 | agno); | |
2377 | return 1; | |
2378 | } | |
2379 | ||
2380 | if (levels > M_IGEO(mp)->inobt_maxlevels) { | |
fb4697dd | 2381 | if (metadump.show_warnings) |
9b9c121a DW |
2382 | print_warning("invalid level (%u) in finobt " |
2383 | "root in agi %u", levels, agno); | |
2384 | return 1; | |
2385 | } | |
2386 | ||
03e956b2 | 2387 | finobt = 1; |
28bbc15a | 2388 | if (!scan_btree(agno, root, levels, TYP_FINOBT, &finobt, |
03e956b2 BF |
2389 | scanfunc_ino)) |
2390 | return 0; | |
2391 | } | |
2392 | ||
2393 | return 1; | |
61983f67 BN |
2394 | } |
2395 | ||
2396 | static int | |
2397 | scan_ag( | |
2398 | xfs_agnumber_t agno) | |
2399 | { | |
2400 | xfs_agf_t *agf; | |
2401 | xfs_agi_t *agi; | |
d24c0a90 BN |
2402 | int stack_count = 0; |
2403 | int rval = 0; | |
61983f67 BN |
2404 | |
2405 | /* copy the superblock of the AG */ | |
2406 | push_cur(); | |
d24c0a90 | 2407 | stack_count++; |
61983f67 BN |
2408 | set_cur(&typtab[TYP_SB], XFS_AG_DADDR(mp, agno, XFS_SB_DADDR), |
2409 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
2410 | if (!iocur_top->data) { | |
2411 | print_warning("cannot read superblock for ag %u", agno); | |
fb4697dd | 2412 | if (metadump.stop_on_read_error) |
d24c0a90 | 2413 | goto pop_out; |
61983f67 | 2414 | } else { |
8927d44b | 2415 | /* Replace any filesystem label with "L's" */ |
fb4697dd | 2416 | if (metadump.obfuscate) { |
8927d44b ES |
2417 | struct xfs_sb *sb = iocur_top->data; |
2418 | memset(sb->sb_fname, 'L', | |
2419 | min(strlen(sb->sb_fname), sizeof(sb->sb_fname))); | |
2420 | iocur_top->need_crc = 1; | |
2421 | } | |
878afc65 | 2422 | if (write_buf(iocur_top)) |
d24c0a90 | 2423 | goto pop_out; |
61983f67 BN |
2424 | } |
2425 | ||
2426 | /* copy the AG free space btree root */ | |
2427 | push_cur(); | |
d24c0a90 | 2428 | stack_count++; |
61983f67 BN |
2429 | set_cur(&typtab[TYP_AGF], XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), |
2430 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
2431 | agf = iocur_top->data; | |
2432 | if (iocur_top->data == NULL) { | |
2433 | print_warning("cannot read agf block for ag %u", agno); | |
fb4697dd | 2434 | if (metadump.stop_on_read_error) |
d24c0a90 | 2435 | goto pop_out; |
61983f67 | 2436 | } else { |
878afc65 | 2437 | if (write_buf(iocur_top)) |
d24c0a90 | 2438 | goto pop_out; |
61983f67 BN |
2439 | } |
2440 | ||
2441 | /* copy the AG inode btree root */ | |
2442 | push_cur(); | |
d24c0a90 | 2443 | stack_count++; |
61983f67 BN |
2444 | set_cur(&typtab[TYP_AGI], XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), |
2445 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
2446 | agi = iocur_top->data; | |
2447 | if (iocur_top->data == NULL) { | |
2448 | print_warning("cannot read agi block for ag %u", agno); | |
fb4697dd | 2449 | if (metadump.stop_on_read_error) |
d24c0a90 | 2450 | goto pop_out; |
61983f67 | 2451 | } else { |
878afc65 | 2452 | if (write_buf(iocur_top)) |
d24c0a90 | 2453 | goto pop_out; |
61983f67 BN |
2454 | } |
2455 | ||
2456 | /* copy the AG free list header */ | |
2457 | push_cur(); | |
d24c0a90 | 2458 | stack_count++; |
61983f67 BN |
2459 | set_cur(&typtab[TYP_AGFL], XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)), |
2460 | XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL); | |
2461 | if (iocur_top->data == NULL) { | |
2462 | print_warning("cannot read agfl block for ag %u", agno); | |
fb4697dd | 2463 | if (metadump.stop_on_read_error) |
d24c0a90 | 2464 | goto pop_out; |
61983f67 | 2465 | } else { |
fb4697dd | 2466 | if (agf && metadump.zero_stale_data) { |
18cdb614 ES |
2467 | /* Zero out unused bits of agfl */ |
2468 | int i; | |
2469 | __be32 *agfl_bno; | |
2470 | ||
b134e771 | 2471 | agfl_bno = xfs_buf_to_agfl_bno(iocur_top->bp); |
18cdb614 ES |
2472 | i = be32_to_cpu(agf->agf_fllast); |
2473 | ||
2474 | for (;;) { | |
b8165508 | 2475 | if (++i == libxfs_agfl_size(mp)) |
18cdb614 ES |
2476 | i = 0; |
2477 | if (i == be32_to_cpu(agf->agf_flfirst)) | |
2478 | break; | |
2479 | agfl_bno[i] = cpu_to_be32(NULLAGBLOCK); | |
2480 | } | |
2481 | iocur_top->need_crc = 1; | |
2482 | } | |
878afc65 | 2483 | if (write_buf(iocur_top)) |
d24c0a90 | 2484 | goto pop_out; |
61983f67 | 2485 | } |
61983f67 BN |
2486 | |
2487 | /* copy AG free space btrees */ | |
2488 | if (agf) { | |
fb4697dd | 2489 | if (metadump.show_progress) |
61983f67 BN |
2490 | print_progress("Copying free space trees of AG %u", |
2491 | agno); | |
2492 | if (!copy_free_bno_btree(agno, agf)) | |
d24c0a90 | 2493 | goto pop_out; |
61983f67 | 2494 | if (!copy_free_cnt_btree(agno, agf)) |
d24c0a90 | 2495 | goto pop_out; |
e434854e DW |
2496 | if (!copy_rmap_btree(agno, agf)) |
2497 | goto pop_out; | |
e2756db3 DW |
2498 | if (!copy_refcount_btree(agno, agf)) |
2499 | goto pop_out; | |
61983f67 BN |
2500 | } |
2501 | ||
2502 | /* copy inode btrees and the inodes and their associated metadata */ | |
2503 | if (agi) { | |
2504 | if (!copy_inodes(agno, agi)) | |
d24c0a90 | 2505 | goto pop_out; |
61983f67 | 2506 | } |
d24c0a90 BN |
2507 | rval = 1; |
2508 | pop_out: | |
2509 | while (stack_count--) | |
2510 | pop_cur(); | |
2511 | return rval; | |
61983f67 BN |
2512 | } |
2513 | ||
2514 | static int | |
2515 | copy_ino( | |
2516 | xfs_ino_t ino, | |
2517 | typnm_t itype) | |
2518 | { | |
2519 | xfs_agnumber_t agno; | |
2520 | xfs_agblock_t agbno; | |
2521 | xfs_agino_t agino; | |
61983f67 | 2522 | int offset; |
a196ca65 | 2523 | int rval = 1; |
61983f67 | 2524 | |
39fe84af | 2525 | if (ino == 0 || ino == NULLFSINO) |
61983f67 BN |
2526 | return 1; |
2527 | ||
2528 | agno = XFS_INO_TO_AGNO(mp, ino); | |
2529 | agino = XFS_INO_TO_AGINO(mp, ino); | |
2530 | agbno = XFS_AGINO_TO_AGBNO(mp, agino); | |
2531 | offset = XFS_AGINO_TO_OFFSET(mp, agino); | |
2532 | ||
2533 | if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks || | |
2534 | offset >= mp->m_sb.sb_inopblock) { | |
fb4697dd | 2535 | if (metadump.show_warnings) |
61983f67 BN |
2536 | print_warning("invalid %s inode number (%lld)", |
2537 | typtab[itype].name, (long long)ino); | |
2538 | return 1; | |
2539 | } | |
2540 | ||
2541 | push_cur(); | |
2542 | set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno), | |
2543 | blkbb, DB_RING_IGN, NULL); | |
2544 | if (iocur_top->data == NULL) { | |
2545 | print_warning("cannot read %s inode %lld", | |
2546 | typtab[itype].name, (long long)ino); | |
fb4697dd | 2547 | rval = !metadump.stop_on_read_error; |
d24c0a90 | 2548 | goto pop_out; |
61983f67 BN |
2549 | } |
2550 | off_cur(offset << mp->m_sb.sb_inodelog, mp->m_sb.sb_inodesize); | |
2551 | ||
fb4697dd | 2552 | metadump.cur_ino = ino; |
5e656dbb | 2553 | rval = process_inode_data(iocur_top->data, itype); |
d24c0a90 BN |
2554 | pop_out: |
2555 | pop_cur(); | |
2556 | return rval; | |
61983f67 BN |
2557 | } |
2558 | ||
2559 | ||
2560 | static int | |
2561 | copy_sb_inodes(void) | |
2562 | { | |
2563 | if (!copy_ino(mp->m_sb.sb_rbmino, TYP_RTBITMAP)) | |
2564 | return 0; | |
2565 | ||
2566 | if (!copy_ino(mp->m_sb.sb_rsumino, TYP_RTSUMMARY)) | |
2567 | return 0; | |
2568 | ||
2569 | if (!copy_ino(mp->m_sb.sb_uquotino, TYP_DQBLK)) | |
2570 | return 0; | |
2571 | ||
0340d706 CS |
2572 | if (!copy_ino(mp->m_sb.sb_gquotino, TYP_DQBLK)) |
2573 | return 0; | |
2574 | ||
2575 | return copy_ino(mp->m_sb.sb_pquotino, TYP_DQBLK); | |
61983f67 BN |
2576 | } |
2577 | ||
2578 | static int | |
2579 | copy_log(void) | |
2580 | { | |
0ab627db BF |
2581 | struct xlog log; |
2582 | int dirty; | |
1c12a814 BF |
2583 | xfs_daddr_t logstart; |
2584 | int logblocks; | |
2585 | int logversion; | |
2586 | int cycle = XLOG_INIT_CYCLE; | |
190df617 | 2587 | |
fb4697dd | 2588 | if (metadump.show_progress) |
61983f67 BN |
2589 | print_progress("Copying log"); |
2590 | ||
2591 | push_cur(); | |
2592 | set_cur(&typtab[TYP_LOG], XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart), | |
2593 | mp->m_sb.sb_logblocks * blkbb, DB_RING_IGN, NULL); | |
2594 | if (iocur_top->data == NULL) { | |
d24c0a90 | 2595 | pop_cur(); |
61983f67 | 2596 | print_warning("cannot read log"); |
fb4697dd | 2597 | return !metadump.stop_on_read_error; |
61983f67 | 2598 | } |
190df617 | 2599 | |
75333d29 | 2600 | /* If not obfuscating or zeroing, just copy the log as it is */ |
fb4697dd | 2601 | if (!metadump.obfuscate && !metadump.zero_stale_data) |
37a78181 ES |
2602 | goto done; |
2603 | ||
0ab627db | 2604 | dirty = xlog_is_dirty(mp, &log, &x, 0); |
190df617 ES |
2605 | |
2606 | switch (dirty) { | |
2607 | case 0: | |
2608 | /* clear out a clean log */ | |
fb4697dd | 2609 | if (metadump.show_progress) |
190df617 | 2610 | print_progress("Zeroing clean log"); |
1c12a814 BF |
2611 | |
2612 | logstart = XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart); | |
2613 | logblocks = XFS_FSB_TO_BB(mp, mp->m_sb.sb_logblocks); | |
2660e653 DW |
2614 | logversion = xfs_has_logv2(mp) ? 2 : 1; |
2615 | if (xfs_has_crc(mp)) | |
1c12a814 BF |
2616 | cycle = log.l_curr_cycle + 1; |
2617 | ||
2618 | libxfs_log_clear(NULL, iocur_top->data, logstart, logblocks, | |
2619 | &mp->m_sb.sb_uuid, logversion, | |
571a78a7 | 2620 | mp->m_sb.sb_logsunit, XLOG_FMT, cycle, true); |
190df617 ES |
2621 | break; |
2622 | case 1: | |
2623 | /* keep the dirty log */ | |
fb4697dd | 2624 | if (metadump.obfuscate) |
74642d8e | 2625 | print_warning( |
3b3751ab JT |
2626 | _("Warning: log recovery of an obfuscated metadata image can leak " |
2627 | "unobfuscated metadata and/or cause image corruption. If possible, " | |
2628 | "please mount the filesystem to clean the log, or disable obfuscation.")); | |
190df617 ES |
2629 | break; |
2630 | case -1: | |
2631 | /* log detection error */ | |
fb4697dd | 2632 | if (metadump.obfuscate) |
74642d8e | 2633 | print_warning( |
190df617 ES |
2634 | _("Could not discern log; image will contain unobfuscated metadata in log.")); |
2635 | break; | |
2636 | } | |
2637 | ||
37a78181 | 2638 | done: |
878afc65 | 2639 | return !write_buf(iocur_top); |
61983f67 BN |
2640 | } |
2641 | ||
eba3f43e | 2642 | static int |
1a5a88ec | 2643 | init_metadump_v1(void) |
eba3f43e CB |
2644 | { |
2645 | metadump.metablock = (xfs_metablock_t *)calloc(BBSIZE + 1, BBSIZE); | |
2646 | if (metadump.metablock == NULL) { | |
2647 | print_warning("memory allocation failure"); | |
2648 | return -1; | |
2649 | } | |
2650 | metadump.metablock->mb_blocklog = BBSHIFT; | |
2651 | metadump.metablock->mb_magic = cpu_to_be32(XFS_MD_MAGIC); | |
2652 | ||
2653 | /* Set flags about state of metadump */ | |
2654 | metadump.metablock->mb_info = XFS_METADUMP_INFO_FLAGS; | |
2655 | if (metadump.obfuscate) | |
2656 | metadump.metablock->mb_info |= XFS_METADUMP_OBFUSCATED; | |
2657 | if (!metadump.zero_stale_data) | |
2658 | metadump.metablock->mb_info |= XFS_METADUMP_FULLBLOCKS; | |
2659 | if (metadump.dirty_log) | |
2660 | metadump.metablock->mb_info |= XFS_METADUMP_DIRTYLOG; | |
2661 | ||
2662 | metadump.block_index = (__be64 *)((char *)metadump.metablock + | |
2663 | sizeof(xfs_metablock_t)); | |
2664 | metadump.block_buffer = (char *)(metadump.metablock) + BBSIZE; | |
2665 | metadump.num_indices = (BBSIZE - sizeof(xfs_metablock_t)) / sizeof(__be64); | |
2666 | ||
2667 | /* | |
2668 | * A metadump block can hold at most num_indices of BBSIZE sectors; | |
2669 | * do not try to dump a filesystem with a sector size which does not | |
2670 | * fit within num_indices (i.e. within a single metablock). | |
2671 | */ | |
2672 | if (mp->m_sb.sb_sectsize > metadump.num_indices * BBSIZE) { | |
2673 | print_warning("Cannot dump filesystem with sector size %u", | |
2674 | mp->m_sb.sb_sectsize); | |
2675 | free(metadump.metablock); | |
2676 | return -1; | |
2677 | } | |
2678 | ||
2679 | metadump.cur_index = 0; | |
2680 | ||
2681 | return 0; | |
2682 | } | |
2683 | ||
1a5a88ec CB |
2684 | static int |
2685 | finish_dump_metadump_v1(void) | |
2686 | { | |
2687 | /* | |
2688 | * write index block and following data blocks (streaming) | |
2689 | */ | |
2690 | metadump.metablock->mb_count = cpu_to_be16(metadump.cur_index); | |
2691 | if (fwrite(metadump.metablock, (metadump.cur_index + 1) << BBSHIFT, 1, | |
2692 | metadump.outf) != 1) { | |
2693 | print_warning("error writing to target file"); | |
2694 | return -1; | |
2695 | } | |
2696 | ||
2697 | memset(metadump.block_index, 0, metadump.num_indices * sizeof(__be64)); | |
2698 | metadump.cur_index = 0; | |
2699 | return 0; | |
2700 | } | |
2701 | ||
2702 | static int | |
2703 | write_metadump_v1( | |
2704 | enum typnm type, | |
2705 | const char *data, | |
2706 | xfs_daddr_t off, | |
2707 | int len) | |
2708 | { | |
2709 | int i; | |
2710 | int ret; | |
2711 | ||
2712 | for (i = 0; i < len; i++, off++, data += BBSIZE) { | |
2713 | metadump.block_index[metadump.cur_index] = cpu_to_be64(off); | |
2714 | memcpy(&metadump.block_buffer[metadump.cur_index << BBSHIFT], | |
2715 | data, BBSIZE); | |
2716 | if (++metadump.cur_index == metadump.num_indices) { | |
2717 | ret = finish_dump_metadump_v1(); | |
2718 | if (ret) | |
2719 | return -EIO; | |
2720 | } | |
2721 | } | |
2722 | ||
2723 | return 0; | |
2724 | } | |
2725 | ||
eba3f43e | 2726 | static void |
1a5a88ec | 2727 | release_metadump_v1(void) |
eba3f43e CB |
2728 | { |
2729 | free(metadump.metablock); | |
2730 | } | |
2731 | ||
1a5a88ec CB |
2732 | static struct metadump_ops metadump1_ops = { |
2733 | .init = init_metadump_v1, | |
2734 | .write = write_metadump_v1, | |
2735 | .finish_dump = finish_dump_metadump_v1, | |
2736 | .release = release_metadump_v1, | |
2737 | }; | |
2738 | ||
61983f67 BN |
2739 | static int |
2740 | metadump_f( | |
2741 | int argc, | |
2742 | char **argv) | |
2743 | { | |
2744 | xfs_agnumber_t agno; | |
2745 | int c; | |
2746 | int start_iocur_sp; | |
449df236 DW |
2747 | int outfd = -1; |
2748 | int ret; | |
88b8e1d6 | 2749 | char *p; |
61983f67 BN |
2750 | |
2751 | exitcode = 1; | |
fb4697dd CB |
2752 | |
2753 | metadump.version = 1; | |
2754 | metadump.show_progress = false; | |
2755 | metadump.stop_on_read_error = false; | |
2756 | metadump.max_extent_size = DEFAULT_MAX_EXT_SIZE; | |
2757 | metadump.show_warnings = false; | |
2758 | metadump.obfuscate = true; | |
2759 | metadump.zero_stale_data = true; | |
2760 | metadump.dirty_log = false; | |
61983f67 BN |
2761 | |
2762 | if (mp->m_sb.sb_magicnum != XFS_SB_MAGIC) { | |
2763 | print_warning("bad superblock magic number %x, giving up", | |
2764 | mp->m_sb.sb_magicnum); | |
2765 | return 0; | |
2766 | } | |
2767 | ||
a547152d ES |
2768 | /* |
2769 | * on load, we sanity-checked agcount and possibly set to 1 | |
2770 | * if it was corrupted and large. | |
2771 | */ | |
2772 | if (mp->m_sb.sb_agcount == 1 && | |
2773 | XFS_MAX_DBLOCKS(&mp->m_sb) < mp->m_sb.sb_dblocks) { | |
2774 | print_warning("truncated agcount, giving up"); | |
2775 | return 0; | |
2776 | } | |
2777 | ||
b09e839e | 2778 | while ((c = getopt(argc, argv, "aegm:ow")) != EOF) { |
61983f67 | 2779 | switch (c) { |
b09e839e | 2780 | case 'a': |
fb4697dd | 2781 | metadump.zero_stale_data = false; |
b09e839e | 2782 | break; |
61983f67 | 2783 | case 'e': |
fb4697dd | 2784 | metadump.stop_on_read_error = true; |
61983f67 BN |
2785 | break; |
2786 | case 'g': | |
fb4697dd | 2787 | metadump.show_progress = true; |
61983f67 | 2788 | break; |
88b8e1d6 | 2789 | case 'm': |
fb4697dd CB |
2790 | metadump.max_extent_size = |
2791 | (int)strtol(optarg, &p, 0); | |
2792 | if (*p != '\0' || | |
2793 | metadump.max_extent_size <= 0) { | |
88b8e1d6 BN |
2794 | print_warning("bad max extent size %s", |
2795 | optarg); | |
2796 | return 0; | |
2797 | } | |
2798 | break; | |
61983f67 | 2799 | case 'o': |
fb4697dd | 2800 | metadump.obfuscate = false; |
61983f67 BN |
2801 | break; |
2802 | case 'w': | |
fb4697dd | 2803 | metadump.show_warnings = true; |
61983f67 BN |
2804 | break; |
2805 | default: | |
2806 | print_warning("bad option for metadump command"); | |
2807 | return 0; | |
2808 | } | |
2809 | } | |
2810 | ||
2811 | if (optind != argc - 1) { | |
2812 | print_warning("too few options for metadump (no filename given)"); | |
2813 | return 0; | |
2814 | } | |
2815 | ||
2291c68b ES |
2816 | /* If we'll copy the log, see if the log is dirty */ |
2817 | if (mp->m_sb.sb_logstart) { | |
2818 | push_cur(); | |
2819 | set_cur(&typtab[TYP_LOG], | |
2820 | XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart), | |
2821 | mp->m_sb.sb_logblocks * blkbb, DB_RING_IGN, NULL); | |
2822 | if (iocur_top->data) { /* best effort */ | |
2823 | struct xlog log; | |
2824 | ||
2825 | if (xlog_is_dirty(mp, &log, &x, 0)) | |
fb4697dd | 2826 | metadump.dirty_log = true; |
2291c68b ES |
2827 | } |
2828 | pop_cur(); | |
2829 | } | |
2830 | ||
61983f67 BN |
2831 | start_iocur_sp = iocur_sp; |
2832 | ||
2833 | if (strcmp(argv[optind], "-") == 0) { | |
2834 | if (isatty(fileno(stdout))) { | |
2835 | print_warning("cannot write to a terminal"); | |
eba3f43e | 2836 | goto out; |
61983f67 | 2837 | } |
4944defa DW |
2838 | /* |
2839 | * Redirect stdout to stderr for the duration of the | |
2840 | * metadump operation so that dbprintf and other messages | |
2841 | * are sent to the console instead of polluting the | |
2842 | * metadump stream. | |
449df236 DW |
2843 | * |
2844 | * We get to do this the hard way because musl doesn't | |
2845 | * allow reassignment of stdout. | |
4944defa | 2846 | */ |
449df236 DW |
2847 | fflush(stdout); |
2848 | outfd = dup(STDOUT_FILENO); | |
2849 | if (outfd < 0) { | |
2850 | perror("opening dump stream"); | |
2851 | goto out; | |
2852 | } | |
2853 | ret = dup2(STDERR_FILENO, STDOUT_FILENO); | |
2854 | if (ret < 0) { | |
2855 | perror("redirecting stdout"); | |
2856 | close(outfd); | |
2857 | goto out; | |
2858 | } | |
fb4697dd CB |
2859 | metadump.outf = fdopen(outfd, "a"); |
2860 | if (metadump.outf == NULL) { | |
449df236 DW |
2861 | fprintf(stderr, "cannot create dump stream\n"); |
2862 | dup2(outfd, STDOUT_FILENO); | |
2863 | close(outfd); | |
2864 | goto out; | |
2865 | } | |
fb4697dd | 2866 | metadump.stdout_metadump = true; |
61983f67 | 2867 | } else { |
fb4697dd CB |
2868 | metadump.outf = fopen(argv[optind], "wb"); |
2869 | if (metadump.outf == NULL) { | |
61983f67 | 2870 | print_warning("cannot create dump file"); |
449df236 | 2871 | goto out; |
61983f67 BN |
2872 | } |
2873 | } | |
2874 | ||
1a5a88ec CB |
2875 | metadump.mdops = &metadump1_ops; |
2876 | ||
2877 | ret = metadump.mdops->init(); | |
1e470277 CB |
2878 | if (ret) |
2879 | goto out; | |
2880 | ||
61983f67 BN |
2881 | exitcode = 0; |
2882 | ||
2883 | for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) { | |
2884 | if (!scan_ag(agno)) { | |
2885 | exitcode = 1; | |
2886 | break; | |
2887 | } | |
2888 | } | |
2889 | ||
2890 | /* copy realtime and quota inode contents */ | |
2891 | if (!exitcode) | |
2892 | exitcode = !copy_sb_inodes(); | |
2893 | ||
2894 | /* copy log if it's internal */ | |
2895 | if ((mp->m_sb.sb_logstart != 0) && !exitcode) | |
2896 | exitcode = !copy_log(); | |
2897 | ||
2898 | /* write the remaining index */ | |
2899 | if (!exitcode) | |
1a5a88ec | 2900 | exitcode = metadump.mdops->finish_dump() < 0; |
61983f67 | 2901 | |
fb4697dd CB |
2902 | if (metadump.progress_since_warning) |
2903 | fputc('\n', metadump.stdout_metadump ? stderr : stdout); | |
61983f67 | 2904 | |
fb4697dd CB |
2905 | if (metadump.stdout_metadump) { |
2906 | fflush(metadump.outf); | |
449df236 DW |
2907 | fflush(stdout); |
2908 | ret = dup2(outfd, STDOUT_FILENO); | |
2909 | if (ret < 0) | |
2910 | perror("un-redirecting stdout"); | |
fb4697dd | 2911 | metadump.stdout_metadump = false; |
449df236 | 2912 | } |
fb4697dd | 2913 | fclose(metadump.outf); |
61983f67 BN |
2914 | |
2915 | /* cleanup iocur stack */ | |
2916 | while (iocur_sp > start_iocur_sp) | |
2917 | pop_cur(); | |
1e470277 | 2918 | |
1a5a88ec | 2919 | metadump.mdops->release(); |
61983f67 | 2920 | |
1e470277 | 2921 | out: |
61983f67 BN |
2922 | return 0; |
2923 | } |