]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/blame - db/metadump.c
Update xfsprogs to version 2.9.5
[thirdparty/xfsprogs-dev.git] / db / metadump.c
CommitLineData
61983f67
BN
1/*
2 * Copyright (c) 2007 Silicon Graphics, Inc.
3 * All Rights Reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it would be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write the Free Software Foundation,
16 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18
19#include <libxfs.h>
20#include "bmap.h"
21#include "command.h"
22#include "metadump.h"
23#include "io.h"
24#include "output.h"
25#include "type.h"
26#include "init.h"
27#include "sig.h"
28#include "xfs_metadump.h"
29
30/* copy all metadata structures to/from a file */
31
32static int metadump_f(int argc, char **argv);
33static void metadump_help(void);
34
35/*
36 * metadump commands issue info/wornings/errors to standard error as
37 * metadump supports stdout as a destination.
38 *
39 * All static functions return zero on failure, while the public functions
40 * return zero on success.
41 */
42
43static const cmdinfo_t metadump_cmd =
44 { "metadump", NULL, metadump_f, 0, -1, 0,
88b8e1d6 45 "[-e] [-g] [-m max_extent] [-w] [-o] filename",
61983f67
BN
46 "dump metadata to a file", metadump_help };
47
48static FILE *outf; /* metadump file */
49
50static xfs_metablock_t *metablock; /* header + index + buffers */
51static __be64 *block_index;
52static char *block_buffer;
53
54static int num_indicies;
55static int cur_index;
56
57static xfs_ino_t cur_ino;
58
59static int show_progress = 0;
60static int stop_on_read_error = 0;
88b8e1d6 61static int max_extent_size = 20;
61983f67
BN
62static int dont_obfuscate = 0;
63static int show_warnings = 0;
64static int progress_since_warning = 0;
65
66void
67metadump_init(void)
68{
69 add_command(&metadump_cmd);
70}
71
72static void
73metadump_help(void)
74{
75 dbprintf(
76"\n"
77" The 'metadump' command dumps the known metadata to a compact file suitable\n"
78" for compressing and sending to an XFS maintainer for corruption analysis \n"
79" or xfs_repair failures.\n\n"
88b8e1d6 80" Options:\n"
61983f67
BN
81" -e -- Ignore read errors and keep going\n"
82" -g -- Display dump progress\n"
88b8e1d6 83" -m -- Specify max extent size in blocks to copy (default = 20 blocks)\n"
61983f67
BN
84" -o -- Don't obfuscate names and extended attributes\n"
85" -w -- Show warnings of bad metadata information\n"
86"\n");
87}
88
89static void
90print_warning(const char *fmt, ...)
91{
92 char buf[200];
93 va_list ap;
94
95 if (seenint())
96 return;
97
98 va_start(ap, fmt);
99 vsnprintf(buf, sizeof(buf), fmt, ap);
100 va_end(ap);
101 buf[sizeof(buf)-1] = '\0';
102
103 fprintf(stderr, "%s%s: %s\n", progress_since_warning ? "\n" : "",
104 progname, buf);
105 progress_since_warning = 0;
106}
107
108static void
109print_progress(const char *fmt, ...)
110{
111 char buf[60];
112 va_list ap;
113 FILE *f;
114
115 if (seenint())
116 return;
117
118 va_start(ap, fmt);
119 vsnprintf(buf, sizeof(buf), fmt, ap);
120 va_end(ap);
121 buf[sizeof(buf)-1] = '\0';
122
123 f = (outf == stdout) ? stderr : stdout;
124 fprintf(f, "\r%-59s", buf);
125 fflush(f);
126 progress_since_warning = 1;
127}
128
129/*
130 * A complete dump file will have a "zero" entry in the last index block,
131 * even if the dump is exactly aligned, the last index will be full of
132 * zeros. If the last index entry is non-zero, the dump is incomplete.
133 * Correspondingly, the last chunk will have a count < num_indicies.
134 */
135
136static int
137write_index(void)
138{
139 /*
140 * write index block and following data blocks (streaming)
141 */
142 metablock->mb_count = cpu_to_be16(cur_index);
143 if (fwrite(metablock, (cur_index + 1) << BBSHIFT, 1, outf) != 1) {
144 print_warning("error writing to file: %s", strerror(errno));
145 return 0;
146 }
147
148 memset(block_index, 0, num_indicies * sizeof(__be64));
149 cur_index = 0;
150 return 1;
151}
152
153static int
154write_buf(
155 iocur_t *buf)
156{
157 char *data;
158 __int64_t off;
159 int i;
160
161 for (i = 0, off = buf->bb, data = buf->data;
162 i < buf->blen;
163 i++, off++, data += BBSIZE) {
164 block_index[cur_index] = cpu_to_be64(off);
165 memcpy(&block_buffer[cur_index << BBSHIFT], data, BBSIZE);
166 if (++cur_index == num_indicies) {
167 if (!write_index())
168 return 0;
169 }
170 }
171 return !seenint();
172}
173
174
175static int
176scan_btree(
177 xfs_agnumber_t agno,
178 xfs_agblock_t agbno,
179 int level,
180 typnm_t btype,
181 void *arg,
182 int (*func)(xfs_btree_hdr_t *bthdr,
183 xfs_agnumber_t agno,
184 xfs_agblock_t agbno,
185 int level,
186 typnm_t btype,
187 void *arg))
188{
189 push_cur();
190 set_cur(&typtab[btype], XFS_AGB_TO_DADDR(mp, agno, agbno), blkbb,
191 DB_RING_IGN, NULL);
192 if (iocur_top->data == NULL) {
193 print_warning("cannot read %s block %u/%u", typtab[btype].name,
194 agno, agbno);
195 return !stop_on_read_error;
196 }
197 if (!write_buf(iocur_top))
198 return 0;
199
200 if (!(*func)(iocur_top->data, agno, agbno, level - 1, btype, arg))
201 return 0;
202
203 pop_cur();
204 return 1;
205}
206
207/* free space tree copy routines */
208
209static int
210valid_bno(
61983f67 211 xfs_agnumber_t agno,
88b8e1d6 212 xfs_agblock_t agbno)
61983f67 213{
88b8e1d6
BN
214 if (agno < (mp->m_sb.sb_agcount - 1) && agbno > 0 &&
215 agbno <= mp->m_sb.sb_agblocks)
216 return 1;
217 if (agno == (mp->m_sb.sb_agcount - 1) && agbno > 0 &&
218 agbno <= (mp->m_sb.sb_dblocks -
219 (mp->m_sb.sb_agcount - 1) * mp->m_sb.sb_agblocks))
61983f67
BN
220 return 1;
221
61983f67
BN
222 return 0;
223}
224
88b8e1d6 225
61983f67
BN
226static int
227scanfunc_freesp(
228 xfs_btree_hdr_t *bthdr,
229 xfs_agnumber_t agno,
230 xfs_agblock_t agbno,
231 int level,
232 typnm_t btype,
233 void *arg)
234{
235 xfs_alloc_ptr_t *pp;
236 int i;
88b8e1d6 237 int numrecs;
61983f67
BN
238
239 if (level == 0)
240 return 1;
241
88b8e1d6
BN
242 numrecs = be16_to_cpu(bthdr->bb_numrecs);
243 if (numrecs > mp->m_alloc_mxr[1]) {
61983f67 244 if (show_warnings)
88b8e1d6
BN
245 print_warning("invalid numrecs (%u) in %s block %u/%u",
246 numrecs, typtab[btype].name, agno, agbno);
61983f67
BN
247 return 1;
248 }
249
250 pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_alloc, bthdr, 1,
251 mp->m_alloc_mxr[1]);
88b8e1d6
BN
252 for (i = 0; i < numrecs; i++) {
253 if (!valid_bno(agno, be32_to_cpu(pp[i]))) {
254 if (show_warnings)
255 print_warning("invalid block number (%u/%u) "
256 "in %s block %u/%u",
257 agno, be32_to_cpu(pp[i]),
258 typtab[btype].name, agno, agbno);
61983f67 259 continue;
88b8e1d6 260 }
61983f67
BN
261 if (!scan_btree(agno, be32_to_cpu(pp[i]), level, btype, arg,
262 scanfunc_freesp))
263 return 0;
264 }
265 return 1;
266}
267
268static int
269copy_free_bno_btree(
270 xfs_agnumber_t agno,
271 xfs_agf_t *agf)
272{
273 xfs_agblock_t root;
274 int levels;
275
276 root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]);
277 levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]);
278
279 /* validate root and levels before processing the tree */
280 if (root == 0 || root > mp->m_sb.sb_agblocks) {
281 if (show_warnings)
282 print_warning("invalid block number (%u) in bnobt "
283 "root in agf %u", root, agno);
284 return 1;
285 }
286 if (levels >= XFS_BTREE_MAXLEVELS) {
287 if (show_warnings)
288 print_warning("invalid level (%u) in bnobt root "
289 "in agf %u", levels, agno);
290 return 1;
291 }
292
293 return scan_btree(agno, root, levels, TYP_BNOBT, agf, scanfunc_freesp);
294}
295
296static int
297copy_free_cnt_btree(
298 xfs_agnumber_t agno,
299 xfs_agf_t *agf)
300{
301 xfs_agblock_t root;
302 int levels;
303
304 root = be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]);
305 levels = be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNT]);
306
307 /* validate root and levels before processing the tree */
308 if (root == 0 || root > mp->m_sb.sb_agblocks) {
309 if (show_warnings)
310 print_warning("invalid block number (%u) in cntbt "
311 "root in agf %u", root, agno);
312 return 1;
313 }
314 if (levels >= XFS_BTREE_MAXLEVELS) {
315 if (show_warnings)
316 print_warning("invalid level (%u) in cntbt root "
317 "in agf %u", levels, agno);
318 return 1;
319 }
320
321 return scan_btree(agno, root, levels, TYP_CNTBT, agf, scanfunc_freesp);
322}
323
324/* filename and extended attribute obfuscation routines */
325
326typedef struct name_ent {
327 struct name_ent *next;
328 xfs_dahash_t hash;
329 int namelen;
330 uchar_t name[1];
331} name_ent_t;
332
333#define NAME_TABLE_SIZE 4096
334
335static name_ent_t **nametable;
336
337static int
338create_nametable(void)
339{
340 nametable = calloc(NAME_TABLE_SIZE, sizeof(name_ent_t));
341 return nametable != NULL;
342}
343
344static void
345clear_nametable(void)
346{
347 int i;
348 name_ent_t *p;
349
350 for (i = 0; i < NAME_TABLE_SIZE; i++) {
351 while (nametable[i]) {
352 p = nametable[i];
353 nametable[i] = p->next;
354 free(p);
355 }
356 }
357}
358
359
360#define is_invalid_char(c) ((c) == '/' || (c) == '\0')
361#define rol32(x,y) (((x) << (y)) | ((x) >> (32 - (y))))
362
363static inline uchar_t
364random_filename_char(void)
365{
366 uchar_t c;
367
368 do {
369 c = random() % 127 + 1;
370 } while (c == '/');
371 return c;
372}
373
374static int
375is_special_dirent(
376 xfs_ino_t ino,
377 int namelen,
378 uchar_t *name)
379{
380 static xfs_ino_t orphanage_ino = 0;
381 char s[32];
382 int slen;
383
384 /*
385 * due to the XFS name hashing algorithm, we cannot obfuscate
386 * names with 4 chars or less.
387 */
388 if (namelen <= 4)
389 return 1;
390
391 if (ino == 0)
392 return 0;
393
394 /*
395 * don't obfuscate lost+found nor any inodes within lost+found with
396 * the inode number
397 */
398 if (cur_ino == mp->m_sb.sb_rootino && namelen == 10 &&
399 memcmp(name, "lost+found", 10) == 0) {
400 orphanage_ino = ino;
401 return 1;
402 }
403 if (cur_ino != orphanage_ino)
404 return 0;
405
406 slen = sprintf(s, "%lld", (long long)ino);
407 return (slen == namelen && memcmp(name, s, namelen) == 0);
408}
409
410static void
411generate_obfuscated_name(
412 xfs_ino_t ino,
413 int namelen,
414 uchar_t *name)
415{
416 xfs_dahash_t hash;
417 name_ent_t *p;
418 int i;
419 int dup;
420 xfs_dahash_t newhash;
421 uchar_t newname[namelen];
422
423 if (is_special_dirent(ino, namelen, name))
424 return;
425
426 hash = libxfs_da_hashname(name, namelen);
427
428 /* create a random name with the same hash value */
429
430 do {
431 dup = 0;
432 newname[0] = '/';
433
434 for (;;) {
435 /* if the first char is a "/", preserve it */
436 i = (name[0] == '/');
437
438 for (newhash = 0; i < namelen - 5; i++) {
439 newname[i] = random_filename_char();
440 newhash = newname[i] ^ rol32(newhash, 7);
441 }
442 newhash = rol32(newhash, 3) ^ hash;
443 if (name[0] != '/' || namelen > 5) {
444 newname[namelen - 5] = (newhash >> 28) |
445 (random_filename_char() & 0xf0);
446 if (is_invalid_char(newname[namelen - 5]))
447 continue;
448 }
449 newname[namelen - 4] = (newhash >> 21) & 0x7f;
450 if (is_invalid_char(newname[namelen - 4]))
451 continue;
452 newname[namelen - 3] = (newhash >> 14) & 0x7f;
453 if (is_invalid_char(newname[namelen - 3]))
454 continue;
455 newname[namelen - 2] = (newhash >> 7) & 0x7f;
456 if (is_invalid_char(newname[namelen - 2]))
457 continue;
458 newname[namelen - 1] = ((newhash >> 0) ^
459 (newname[namelen - 5] >> 4)) & 0x7f;
460 if (is_invalid_char(newname[namelen - 1]))
461 continue;
462 break;
463 }
464
465 ASSERT(libxfs_da_hashname(newname, namelen) == hash);
466
467 for (p = nametable[hash % NAME_TABLE_SIZE]; p; p = p->next) {
468 if (p->hash == hash && p->namelen == namelen &&
469 memcmp(p->name, newname, namelen) == 0){
470 dup = 1;
471 break;
472 }
473 }
474 } while (dup);
475
476 memcpy(name, newname, namelen);
477
478 p = malloc(sizeof(name_ent_t) + namelen);
479 if (p == NULL)
480 return;
481
482 p->next = nametable[hash % NAME_TABLE_SIZE];
483 p->hash = hash;
484 p->namelen = namelen;
485 memcpy(p->name, name, namelen);
486
487 nametable[hash % NAME_TABLE_SIZE] = p;
488}
489
490static void
491obfuscate_sf_dir(
492 xfs_dinode_t *dip)
493{
494 xfs_dir2_sf_t *sfp;
495 xfs_dir2_sf_entry_t *sfep;
496 int ino_dir_size;
497 int i;
498
499 sfp = &dip->di_u.di_dir2sf;
500 ino_dir_size = dip->di_core.di_size;
501 if (ino_dir_size > XFS_DFORK_DSIZE(dip, mp)) {
502 ino_dir_size = XFS_DFORK_DSIZE(dip, mp);
503 if (show_warnings)
88b8e1d6 504 print_warning("invalid size in dir inode %llu",
61983f67
BN
505 (long long)cur_ino);
506 }
507
508 sfep = XFS_DIR2_SF_FIRSTENTRY(sfp);
509 for (i = 0; (i < sfp->hdr.count) &&
510 ((char *)sfep - (char *)sfp < ino_dir_size); i++) {
511
512 /*
513 * first check for bad name lengths. If they are bad, we
514 * have limitations to how much can be obfuscated.
515 */
516 int namelen = sfep->namelen;
517
518 if (namelen == 0) {
519 if (show_warnings)
520 print_warning("zero length entry in dir inode "
521 "%llu", (long long)cur_ino);
522 if (i != sfp->hdr.count - 1)
523 break;
524 namelen = ino_dir_size - ((char *)&sfep->name[0] -
525 (char *)sfp);
526 } else if ((char *)sfep - (char *)sfp +
527 XFS_DIR2_SF_ENTSIZE_BYENTRY(sfp, sfep) >
528 ino_dir_size) {
529 if (show_warnings)
530 print_warning("entry length in dir inode %llu "
531 "overflows space", (long long)cur_ino);
532 if (i != sfp->hdr.count - 1)
533 break;
534 namelen = ino_dir_size - ((char *)&sfep->name[0] -
535 (char *)sfp);
536 }
537
538 generate_obfuscated_name(XFS_DIR2_SF_GET_INUMBER(sfp,
539 XFS_DIR2_SF_INUMBERP(sfep)), namelen,
540 &sfep->name[0]);
541
542 sfep = (xfs_dir2_sf_entry_t *)((char *)sfep +
543 XFS_DIR2_SF_ENTSIZE_BYNAME(sfp, namelen));
544 }
545}
546
547static void
548obfuscate_sf_symlink(
549 xfs_dinode_t *dip)
550{
88b8e1d6
BN
551 int len;
552
553 len = dip->di_core.di_size;
554 if (len > XFS_DFORK_DSIZE(dip, mp)) {
555 if (show_warnings)
556 print_warning("invalid size (%d) in symlink inode %llu",
557 len, (long long)cur_ino);
558 len = XFS_DFORK_DSIZE(dip, mp);
559 }
61983f67 560
88b8e1d6
BN
561 while (len > 0)
562 dip->di_u.di_symlink[--len] = random() % 127 + 1;
61983f67
BN
563}
564
565static void
566obfuscate_sf_attr(
567 xfs_dinode_t *dip)
568{
569 /*
570 * with extended attributes, obfuscate the names and zero the actual
571 * values.
572 */
573
574 xfs_attr_shortform_t *asfp;
575 xfs_attr_sf_entry_t *asfep;
576 int ino_attr_size;
577 int i;
578
579 asfp = (xfs_attr_shortform_t *)XFS_DFORK_APTR(dip);
580 if (asfp->hdr.count == 0)
581 return;
582
583 ino_attr_size = be16_to_cpu(asfp->hdr.totsize);
584 if (ino_attr_size > XFS_DFORK_ASIZE(dip, mp)) {
585 ino_attr_size = XFS_DFORK_ASIZE(dip, mp);
586 if (show_warnings)
587 print_warning("invalid attr size in inode %llu",
588 (long long)cur_ino);
589 }
590
591 asfep = &asfp->list[0];
592 for (i = 0; (i < asfp->hdr.count) &&
593 ((char *)asfep - (char *)asfp < ino_attr_size); i++) {
594
595 int namelen = asfep->namelen;
596
597 if (namelen == 0) {
598 if (show_warnings)
599 print_warning("zero length attr entry in inode "
600 "%llu", (long long)cur_ino);
601 break;
602 } else if ((char *)asfep - (char *)asfp +
603 XFS_ATTR_SF_ENTSIZE(asfep) > ino_attr_size) {
604 if (show_warnings)
605 print_warning("attr entry length in inode %llu "
606 "overflows space", (long long)cur_ino);
607 break;
608 }
609
610 generate_obfuscated_name(0, asfep->namelen, &asfep->nameval[0]);
611 memset(&asfep->nameval[asfep->namelen], 0, asfep->valuelen);
612
613 asfep = (xfs_attr_sf_entry_t *)((char *)asfep +
614 XFS_ATTR_SF_ENTSIZE(asfep));
615 }
616}
617
618/*
619 * dir_data structure is used to track multi-fsblock dir2 blocks between extent
620 * processing calls.
621 */
622
623static struct dir_data_s {
624 int end_of_data;
625 int block_index;
626 int offset_to_entry;
627 int bad_block;
628} dir_data;
629
630static void
631obfuscate_dir_data_blocks(
632 char *block,
633 xfs_dfiloff_t offset,
634 xfs_dfilblks_t count,
635 int is_block_format)
636{
637 /*
638 * we have to rely on the fileoffset and signature of the block to
639 * handle it's contents. If it's invalid, leave it alone.
640 * for multi-fsblock dir blocks, if a name crosses an extent boundary,
641 * ignore it and continue.
642 */
643 int c;
644 int dir_offset;
645 char *ptr;
646 char *endptr;
647
648 if (is_block_format && count != mp->m_dirblkfsbs)
649 return; /* too complex to handle this rare case */
650
651 for (c = 0, endptr = block; c < count; c++) {
652
653 if (dir_data.block_index == 0) {
654 int wantmagic;
655
656 if (offset % mp->m_dirblkfsbs != 0)
657 return; /* corrupted, leave it alone */
658
659 dir_data.bad_block = 0;
660
661 if (is_block_format) {
662 xfs_dir2_leaf_entry_t *blp;
663 xfs_dir2_block_tail_t *btp;
664
665 btp = XFS_DIR2_BLOCK_TAIL_P(mp,
666 (xfs_dir2_block_t *)block);
667 blp = XFS_DIR2_BLOCK_LEAF_P(btp);
668 if ((char *)blp > (char *)btp)
669 blp = (xfs_dir2_leaf_entry_t *)btp;
670
671 dir_data.end_of_data = (char *)blp - block;
672 wantmagic = XFS_DIR2_BLOCK_MAGIC;
673 } else { /* leaf/node format */
674 dir_data.end_of_data = mp->m_dirblkfsbs <<
675 mp->m_sb.sb_blocklog;
676 wantmagic = XFS_DIR2_DATA_MAGIC;
677 }
678 dir_data.offset_to_entry = offsetof(xfs_dir2_data_t, u);
679
680 if (be32_to_cpu(((xfs_dir2_data_hdr_t*)block)->magic) !=
681 wantmagic) {
682 if (show_warnings)
683 print_warning("invalid magic in dir "
684 "inode %llu block %ld",
685 (long long)cur_ino,
686 (long)offset);
687 dir_data.bad_block = 1;
688 }
689 }
690 dir_data.block_index++;
691 if (dir_data.block_index == mp->m_dirblkfsbs)
692 dir_data.block_index = 0;
693
694 if (dir_data.bad_block)
695 continue;
696
697 dir_offset = (dir_data.block_index << mp->m_sb.sb_blocklog) +
698 dir_data.offset_to_entry;
699
700 ptr = endptr + dir_data.offset_to_entry;
701 endptr += mp->m_sb.sb_blocksize;
702
703 while (ptr < endptr && dir_offset < dir_data.end_of_data) {
704 xfs_dir2_data_entry_t *dep;
705 xfs_dir2_data_unused_t *dup;
706 int length;
707
708 dup = (xfs_dir2_data_unused_t *)ptr;
709
710 if (be16_to_cpu(dup->freetag) == XFS_DIR2_DATA_FREE_TAG) {
711 int length = be16_to_cpu(dup->length);
712 if (dir_offset + length > dir_data.end_of_data ||
713 length == 0 || (length &
714 (XFS_DIR2_DATA_ALIGN - 1))) {
715 if (show_warnings)
716 print_warning("invalid length "
717 "for dir free space in "
718 "inode %llu",
719 (long long)cur_ino);
720 dir_data.bad_block = 1;
721 break;
722 }
723 if (be16_to_cpu(*XFS_DIR2_DATA_UNUSED_TAG_P(dup)) !=
724 dir_offset) {
725 dir_data.bad_block = 1;
726 break;
727 }
728 dir_offset += length;
729 ptr += length;
730 if (dir_offset >= dir_data.end_of_data ||
731 ptr >= endptr)
732 break;
733 }
734
735 dep = (xfs_dir2_data_entry_t *)ptr;
736 length = XFS_DIR2_DATA_ENTSIZE(dep->namelen);
737
738 if (dir_offset + length > dir_data.end_of_data ||
739 ptr + length > endptr) {
740 if (show_warnings)
741 print_warning("invalid length for "
742 "dir entry name in inode %llu",
743 (long long)cur_ino);
744 break;
745 }
746 if (be16_to_cpu(*XFS_DIR2_DATA_ENTRY_TAG_P(dep)) !=
747 dir_offset) {
748 dir_data.bad_block = 1;
749 break;
750 }
751 generate_obfuscated_name(be64_to_cpu(dep->inumber),
752 dep->namelen, &dep->name[0]);
753 dir_offset += length;
754 ptr += length;
755 }
756 dir_data.offset_to_entry = dir_offset &
757 (mp->m_sb.sb_blocksize - 1);
758 }
759}
760
761static void
762obfuscate_symlink_blocks(
763 char *block,
764 xfs_dfilblks_t count)
765{
766 int i;
767
768 count <<= mp->m_sb.sb_blocklog;
769 for (i = 0; i < count; i++)
770 block[i] = random() % 127 + 1;
771}
772
773#define MAX_REMOTE_VALS 4095
774
775static struct attr_data_s {
776 int remote_val_count;
777 xfs_dablk_t remote_vals[MAX_REMOTE_VALS];
778} attr_data;
779
780static inline void
781add_remote_vals(
782 xfs_dablk_t blockidx,
783 int length)
784{
785 while (length > 0 && attr_data.remote_val_count < MAX_REMOTE_VALS) {
786 attr_data.remote_vals[attr_data.remote_val_count] = blockidx;
787 attr_data.remote_val_count++;
788 blockidx++;
789 length -= XFS_LBSIZE(mp);
790 }
791}
792
793static void
794obfuscate_attr_blocks(
795 char *block,
796 xfs_dfiloff_t offset,
797 xfs_dfilblks_t count)
798{
799 xfs_attr_leafblock_t *leaf;
800 int c;
801 int i;
802 int nentries;
803 xfs_attr_leaf_entry_t *entry;
804 xfs_attr_leaf_name_local_t *local;
805 xfs_attr_leaf_name_remote_t *remote;
806
807 for (c = 0; c < count; c++, offset++, block += XFS_LBSIZE(mp)) {
808
809 leaf = (xfs_attr_leafblock_t *)block;
810
811 if (be16_to_cpu(leaf->hdr.info.magic) != XFS_ATTR_LEAF_MAGIC) {
812 for (i = 0; i < attr_data.remote_val_count; i++) {
813 if (attr_data.remote_vals[i] == offset)
814 memset(block, 0, XFS_LBSIZE(mp));
815 }
816 continue;
817 }
818
819 nentries = be16_to_cpu(leaf->hdr.count);
820 if (nentries * sizeof(xfs_attr_leaf_entry_t) +
821 sizeof(xfs_attr_leaf_hdr_t) > XFS_LBSIZE(mp)) {
822 if (show_warnings)
823 print_warning("invalid attr count in inode %llu",
824 (long long)cur_ino);
825 continue;
826 }
827
828 for (i = 0, entry = &leaf->entries[0]; i < nentries;
829 i++, entry++) {
830 if (be16_to_cpu(entry->nameidx) > XFS_LBSIZE(mp)) {
831 if (show_warnings)
832 print_warning("invalid attr nameidx "
833 "in inode %llu",
834 (long long)cur_ino);
835 break;
836 }
837 if (entry->flags & XFS_ATTR_LOCAL) {
838 local = XFS_ATTR_LEAF_NAME_LOCAL(leaf, i);
839 if (local->namelen == 0) {
840 if (show_warnings)
841 print_warning("zero length for "
842 "attr name in inode %llu",
843 (long long)cur_ino);
844 break;
845 }
846 generate_obfuscated_name(0, local->namelen,
847 &local->nameval[0]);
848 memset(&local->nameval[local->namelen], 0,
849 be16_to_cpu(local->valuelen));
850 } else {
851 remote = XFS_ATTR_LEAF_NAME_REMOTE(leaf, i);
852 if (remote->namelen == 0 ||
853 remote->valueblk == 0) {
854 if (show_warnings)
855 print_warning("invalid attr "
856 "entry in inode %llu",
857 (long long)cur_ino);
858 break;
859 }
860 generate_obfuscated_name(0, remote->namelen,
861 &remote->name[0]);
862 add_remote_vals(be32_to_cpu(remote->valueblk),
863 be32_to_cpu(remote->valuelen));
864 }
865 }
866 }
867}
868
869/* inode copy routines */
870
871static int
872process_bmbt_reclist(
873 xfs_bmbt_rec_t *rp,
874 int numrecs,
875 typnm_t btype)
876{
877 int i;
88b8e1d6 878 xfs_dfiloff_t o, op;
61983f67 879 xfs_dfsbno_t s;
88b8e1d6 880 xfs_dfilblks_t c, cp;
61983f67
BN
881 int f;
882 xfs_dfiloff_t last;
88b8e1d6
BN
883 xfs_agnumber_t agno;
884 xfs_agblock_t agbno;
61983f67
BN
885
886 if (btype == TYP_DATA)
887 return 1;
888
889 convert_extent(&rp[numrecs - 1], &o, &s, &c, &f);
890 last = o + c;
891
892 for (i = 0; i < numrecs; i++, rp++) {
893 convert_extent(rp, &o, &s, &c, &f);
894
88b8e1d6
BN
895 /*
896 * ignore extents that are clearly bogus, and if a bogus
897 * one is found, stop processing remaining extents
898 */
899 if (i > 0 && op + cp > o) {
900 if (show_warnings)
901 print_warning("bmap extent %d in %s ino %llu "
902 "starts at %llu, previous extent "
903 "ended at %llu", i,
904 typtab[btype].name, (long long)cur_ino,
905 o, op + cp - 1);
906 break;
907 }
908
909 if (c > max_extent_size) {
910 /*
911 * since we are only processing non-data extents,
912 * large numbers of blocks in a metadata extent is
913 * extremely rare and more than likely to be corrupt.
914 */
915 if (show_warnings)
916 print_warning("suspicious count %u in bmap "
917 "extent %d in %s ino %llu", c, i,
918 typtab[btype].name, (long long)cur_ino);
919 break;
920 }
921
922 op = o;
923 cp = c;
924
925 agno = XFS_FSB_TO_AGNO(mp, s);
926 agbno = XFS_FSB_TO_AGBNO(mp, s);
927
928 if (!valid_bno(agno, agbno)) {
929 if (show_warnings)
930 print_warning("invalid block number %u/%u "
931 "(%llu) in bmap extent %d in %s ino "
932 "%llu", agno, agbno, s, i,
933 typtab[btype].name, (long long)cur_ino);
934 break;
935 }
936
937 if (!valid_bno(agno, agbno + c - 1)) {
938 if (show_warnings)
939 print_warning("bmap extent %i in %s inode %llu "
940 "overflows AG (end is %u/%u)", i,
941 typtab[btype].name, (long long)cur_ino,
942 agno, agbno + c - 1);
943 break;
944 }
945
61983f67
BN
946 push_cur();
947 set_cur(&typtab[btype], XFS_FSB_TO_DADDR(mp, s), c * blkbb,
948 DB_RING_IGN, NULL);
949 if (iocur_top->data == NULL) {
88b8e1d6
BN
950 print_warning("cannot read %s block %u/%u (%llu)",
951 typtab[btype].name, agno, agbno, s);
61983f67
BN
952 if (stop_on_read_error)
953 return 0;
954 } else {
955 if (!dont_obfuscate)
956 switch (btype) {
957 case TYP_DIR2:
958 if (o < mp->m_dirleafblk)
959 obfuscate_dir_data_blocks(
960 iocur_top->data, o, c,
961 last == mp->m_dirblkfsbs);
962 break;
963
964 case TYP_SYMLINK:
965 obfuscate_symlink_blocks(
966 iocur_top->data, c);
967 break;
968
969 case TYP_ATTR:
970 obfuscate_attr_blocks(iocur_top->data,
971 o, c);
972 break;
973
974 default: ;
975 }
976 if (!write_buf(iocur_top))
977 return 0;
978 }
979 pop_cur();
980 }
981
982 return 1;
983}
984
985static int
986scanfunc_bmap(
987 xfs_btree_hdr_t *bthdr,
988 xfs_agnumber_t agno,
989 xfs_agblock_t agbno,
990 int level,
991 typnm_t btype,
992 void *arg) /* ptr to itype */
993{
994 int i;
995 xfs_bmbt_ptr_t *pp;
996 xfs_bmbt_rec_t *rp;
997 int nrecs;
998
999 nrecs = be16_to_cpu(bthdr->bb_numrecs);
1000
1001 if (level == 0) {
1002 if (nrecs > mp->m_bmap_dmxr[0]) {
1003 if (show_warnings)
1004 print_warning("invalid numrecs (%u) in %s "
1005 "block %u/%u", nrecs,
1006 typtab[btype].name, agno, agbno);
1007 return 1;
1008 }
1009 rp = XFS_BTREE_REC_ADDR(mp->m_sb.sqb_blocksize, xfs_bmbt, bthdr,
1010 1, mp->m_bmap_dmxr[0]);
1011
1012 return process_bmbt_reclist(rp, nrecs, *(typnm_t*)arg);
1013 }
1014
1015 if (nrecs > mp->m_bmap_dmxr[1]) {
1016 if (show_warnings)
1017 print_warning("invalid numrecs (%u) in %s block %u/%u",
1018 nrecs, typtab[btype].name, agno, agbno);
1019 return 1;
1020 }
1021 pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_bmbt, bthdr, 1,
1022 mp->m_bmap_dmxr[1]);
1023 for (i = 0; i < nrecs; i++) {
1024 xfs_agnumber_t ag;
1025 xfs_agblock_t bno;
1026
1027 ag = XFS_FSB_TO_AGNO(mp, be64_to_cpu(pp[i]));
1028 bno = XFS_FSB_TO_AGBNO(mp, be64_to_cpu(pp[i]));
1029
1030 if (bno == 0 || bno > mp->m_sb.sb_agblocks ||
1031 ag > mp->m_sb.sb_agcount) {
1032 if (show_warnings)
1033 print_warning("invalid block number (%u/%u) "
1034 "in %s block %u/%u", ag, bno,
1035 typtab[btype].name, agno, agbno);
1036 continue;
1037 }
1038
1039 if (!scan_btree(ag, bno, level, btype, arg, scanfunc_bmap))
1040 return 0;
1041 }
1042 return 1;
1043}
1044
1045static int
1046process_btinode(
1047 xfs_dinode_t *dip,
1048 typnm_t itype)
1049{
1050 xfs_bmdr_block_t *dib;
1051 int i;
1052 xfs_bmbt_ptr_t *pp;
1053 xfs_bmbt_rec_t *rp;
1054 int level;
1055 int nrecs;
1056 int maxrecs;
1057 int whichfork;
1058 typnm_t btype;
1059
1060 whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK;
1061 btype = (itype == TYP_ATTR) ? TYP_BMAPBTA : TYP_BMAPBTD;
1062
1063 dib = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork);
1064 level = be16_to_cpu(dib->bb_level);
1065 nrecs = be16_to_cpu(dib->bb_numrecs);
1066
1067 if (level > XFS_BM_MAXLEVELS(mp, whichfork)) {
1068 if (show_warnings)
1069 print_warning("invalid level (%u) in inode %lld %s "
1070 "root", level, (long long)cur_ino,
1071 typtab[btype].name);
1072 return 1;
1073 }
1074
1075 if (level == 0) {
1076 rp = XFS_BTREE_REC_ADDR(XFS_DFORK_SIZE(dip, mp, whichfork),
1077 xfs_bmdr, dib, 1, XFS_BTREE_BLOCK_MAXRECS(
1078 XFS_DFORK_SIZE(dip, mp, whichfork),
1079 xfs_bmdr, 1));
1080
1081 return process_bmbt_reclist(rp, nrecs, itype);
1082 }
1083
1084 maxrecs = XFS_BTREE_BLOCK_MAXRECS(XFS_DFORK_SIZE(dip, mp, whichfork),
1085 xfs_bmdr, 0);
1086 if (nrecs > maxrecs) {
1087 if (show_warnings)
1088 print_warning("invalid numrecs (%u) in inode %lld %s "
1089 "root", nrecs, (long long)cur_ino,
1090 typtab[btype].name);
1091 return 1;
1092 }
1093
1094 pp = XFS_BTREE_PTR_ADDR(XFS_DFORK_SIZE(dip, mp, whichfork), xfs_bmdr,
1095 dib, 1, maxrecs);
1096 for (i = 0; i < nrecs; i++) {
1097 xfs_agnumber_t ag;
1098 xfs_agblock_t bno;
1099
1100 ag = XFS_FSB_TO_AGNO(mp, be64_to_cpu(pp[i]));
1101 bno = XFS_FSB_TO_AGBNO(mp, be64_to_cpu(pp[i]));
1102
1103 if (bno == 0 || bno > mp->m_sb.sb_agblocks ||
1104 ag > mp->m_sb.sb_agcount) {
1105 if (show_warnings)
1106 print_warning("invalid block number (%u/%u) "
1107 "in inode %llu %s root", ag,
1108 bno, (long long)cur_ino,
1109 typtab[btype].name);
1110 continue;
1111 }
1112
1113 if (!scan_btree(ag, bno, level, btype, &itype, scanfunc_bmap))
1114 return 0;
1115 }
1116 return 1;
1117}
1118
1119static int
1120process_exinode(
1121 xfs_dinode_t *dip,
1122 typnm_t itype)
1123{
1124 int whichfork;
88b8e1d6 1125 xfs_extnum_t nex;
61983f67
BN
1126
1127 whichfork = (itype == TYP_ATTR) ? XFS_ATTR_FORK : XFS_DATA_FORK;
1128
88b8e1d6
BN
1129 nex = XFS_DFORK_NEXTENTS_HOST(dip, whichfork);
1130 if (nex < 0 || nex > XFS_DFORK_SIZE_HOST(dip, mp, whichfork) /
1131 sizeof(xfs_bmbt_rec_t)) {
1132 if (show_warnings)
1133 print_warning("bad number of extents %d in inode %lld",
1134 nex, (long long)cur_ino);
1135 return 1;
1136 }
1137
1138 return process_bmbt_reclist((xfs_bmbt_rec_t *)XFS_DFORK_PTR(dip,
1139 whichfork), nex, itype);
61983f67
BN
1140}
1141
1142static int
1143process_inode_data(
1144 xfs_dinode_t *dip,
1145 typnm_t itype)
1146{
1147 switch (dip->di_core.di_format) {
1148 case XFS_DINODE_FMT_LOCAL:
1149 if (!dont_obfuscate)
1150 switch (itype) {
1151 case TYP_DIR2:
1152 obfuscate_sf_dir(dip);
1153 break;
1154
1155 case TYP_SYMLINK:
1156 obfuscate_sf_symlink(dip);
1157 break;
1158
1159 default: ;
1160 }
1161 break;
1162
1163 case XFS_DINODE_FMT_EXTENTS:
1164 return process_exinode(dip, itype);
1165
1166 case XFS_DINODE_FMT_BTREE:
1167 return process_btinode(dip, itype);
1168 }
1169 return 1;
1170}
1171
1172static int
1173process_inode(
1174 xfs_agnumber_t agno,
1175 xfs_agino_t agino,
1176 xfs_dinode_t *dip)
1177{
1178 xfs_dinode_core_t odic;
1179 int success;
1180
1181 /* convert the core */
1182 memcpy(&odic, &dip->di_core, sizeof(xfs_dinode_core_t));
1183 libxfs_xlate_dinode_core((xfs_caddr_t)&odic, &dip->di_core, 1);
1184
1185 success = 1;
1186 cur_ino = XFS_AGINO_TO_INO(mp, agno, agino);
1187
61983f67
BN
1188 /* copy appropriate data fork metadata */
1189 switch (dip->di_core.di_mode & S_IFMT) {
1190 case S_IFDIR:
1191 memset(&dir_data, 0, sizeof(dir_data));
1192 success = process_inode_data(dip, TYP_DIR2);
1193 break;
1194 case S_IFLNK:
1195 success = process_inode_data(dip, TYP_SYMLINK);
1196 break;
88b8e1d6 1197 case S_IFREG:
61983f67 1198 success = process_inode_data(dip, TYP_DATA);
88b8e1d6
BN
1199 break;
1200 default: ;
61983f67
BN
1201 }
1202 clear_nametable();
1203
88b8e1d6
BN
1204 /* copy extended attributes if they exist and forkoff is valid */
1205 if (success && XFS_CFORK_DSIZE(&dip->di_core, mp) < XFS_LITINO(mp)) {
61983f67
BN
1206 attr_data.remote_val_count = 0;
1207 switch (dip->di_core.di_aformat) {
1208 case XFS_DINODE_FMT_LOCAL:
1209 if (!dont_obfuscate)
1210 obfuscate_sf_attr(dip);
1211 break;
1212
1213 case XFS_DINODE_FMT_EXTENTS:
1214 success = process_exinode(dip, TYP_ATTR);
1215 break;
1216
1217 case XFS_DINODE_FMT_BTREE:
1218 success = process_btinode(dip, TYP_ATTR);
1219 break;
1220 }
1221 clear_nametable();
1222 }
1223
1224 /* restore the core back to it's original endianess */
1225 memcpy(&dip->di_core, &odic, sizeof(xfs_dinode_core_t));
1226
1227 return success;
1228}
1229
1230static __uint32_t inodes_copied = 0;
1231
1232static int
1233copy_inode_chunk(
1234 xfs_agnumber_t agno,
1235 xfs_inobt_rec_t *rp)
1236{
1237 xfs_agino_t agino;
1238 int off;
1239 xfs_agblock_t agbno;
1240 int i;
1241
1242 agino = be32_to_cpu(rp->ir_startino);
1243 agbno = XFS_AGINO_TO_AGBNO(mp, agino);
1244 off = XFS_INO_TO_OFFSET(mp, agino);
1245
88b8e1d6
BN
1246 if (agino == 0 || agino == NULLAGINO || !valid_bno(agno, agbno) ||
1247 !valid_bno(agno, XFS_AGINO_TO_AGBNO(mp,
1248 agino + XFS_INODES_PER_CHUNK - 1))) {
1249 if (show_warnings)
1250 print_warning("bad inode number %llu (%u/%u)",
1251 XFS_AGINO_TO_INO(mp, agno, agino), agno, agino);
1252 return 1;
1253 }
1254
61983f67
BN
1255 push_cur();
1256 set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno),
1257 XFS_FSB_TO_BB(mp, XFS_IALLOC_BLOCKS(mp)),
1258 DB_RING_IGN, NULL);
1259 if (iocur_top->data == NULL) {
1260 print_warning("cannot read inode block %u/%u", agno, agbno);
1261 return !stop_on_read_error;
1262 }
1263
88b8e1d6
BN
1264 /*
1265 * check for basic assumptions about inode chunks, and if any
1266 * assumptions fail, don't process the inode chunk.
1267 */
1268
1269 if ((mp->m_sb.sb_inopblock <= XFS_INODES_PER_CHUNK && off != 0) ||
1270 (mp->m_sb.sb_inopblock > XFS_INODES_PER_CHUNK &&
1271 off % XFS_INODES_PER_CHUNK != 0) ||
1272 (XFS_SB_VERSION_HASALIGN(&mp->m_sb) &&
1273 agbno % mp->m_sb.sb_inoalignmt != 0)) {
1274 if (show_warnings)
1275 print_warning("badly aligned inode (start = %llu)",
1276 XFS_AGINO_TO_INO(mp, agno, agino));
1277 goto skip_processing;
1278 }
1279
61983f67
BN
1280 /*
1281 * scan through inodes and copy any btree extent lists, directory
1282 * contents and extended attributes.
1283 */
61983f67
BN
1284 for (i = 0; i < XFS_INODES_PER_CHUNK; i++) {
1285 xfs_dinode_t *dip;
1286
1287 if (XFS_INOBT_IS_FREE_DISK(rp, i))
1288 continue;
1289
1290 dip = (xfs_dinode_t *)((char *)iocur_top->data +
1291 ((off + i) << mp->m_sb.sb_inodelog));
1292
1293 if (!process_inode(agno, agino + i, dip))
1294 return 0;
1295 }
88b8e1d6 1296skip_processing:
61983f67
BN
1297 if (!write_buf(iocur_top))
1298 return 0;
1299
1300 inodes_copied += XFS_INODES_PER_CHUNK;
1301
1302 if (show_progress)
1303 print_progress("Copied %u of %u inodes (%u of %u AGs)",
1304 inodes_copied, mp->m_sb.sb_icount, agno,
1305 mp->m_sb.sb_agcount);
1306
1307 pop_cur();
1308
1309 return 1;
1310}
1311
1312static int
1313scanfunc_ino(
1314 xfs_btree_hdr_t *bthdr,
1315 xfs_agnumber_t agno,
1316 xfs_agblock_t agbno,
1317 int level,
1318 typnm_t btype,
1319 void *arg)
1320{
1321 xfs_inobt_rec_t *rp;
1322 xfs_inobt_ptr_t *pp;
1323 int i;
88b8e1d6
BN
1324 int numrecs;
1325
1326 numrecs = be16_to_cpu(bthdr->bb_numrecs);
61983f67
BN
1327
1328 if (level == 0) {
88b8e1d6
BN
1329 if (numrecs > mp->m_inobt_mxr[0]) {
1330 if (show_warnings)
1331 print_warning("invalid numrecs %d in %s "
1332 "block %u/%u", numrecs,
1333 typtab[btype].name, agno, agbno);
1334 numrecs = mp->m_inobt_mxr[0];
1335 }
61983f67
BN
1336 rp = XFS_BTREE_REC_ADDR(mp->m_sb.sb_blocksize, xfs_inobt,
1337 bthdr, 1, mp->m_inobt_mxr[0]);
88b8e1d6 1338 for (i = 0; i < numrecs; i++, rp++) {
61983f67
BN
1339 if (!copy_inode_chunk(agno, rp))
1340 return 0;
1341 }
88b8e1d6
BN
1342 return 1;
1343 }
1344
1345 if (numrecs > mp->m_inobt_mxr[1]) {
1346 if (show_warnings)
1347 print_warning("invalid numrecs %d in %s block %u/%u",
1348 numrecs, typtab[btype].name, agno, agbno);
1349 numrecs = mp->m_inobt_mxr[1];
1350 }
1351
1352 pp = XFS_BTREE_PTR_ADDR(mp->m_sb.sb_blocksize, xfs_inobt,
1353 bthdr, 1, mp->m_inobt_mxr[1]);
1354 for (i = 0; i < numrecs; i++) {
1355 if (!valid_bno(agno, be32_to_cpu(pp[i]))) {
1356 if (show_warnings)
1357 print_warning("invalid block number (%u/%u) "
1358 "in %s block %u/%u",
1359 agno, be32_to_cpu(pp[i]),
1360 typtab[btype].name, agno, agbno);
1361 continue;
61983f67 1362 }
88b8e1d6
BN
1363 if (!scan_btree(agno, be32_to_cpu(pp[i]), level,
1364 btype, arg, scanfunc_ino))
1365 return 0;
61983f67
BN
1366 }
1367 return 1;
1368}
1369
1370static int
1371copy_inodes(
1372 xfs_agnumber_t agno,
1373 xfs_agi_t *agi)
1374{
1375 xfs_agblock_t root;
1376 int levels;
1377
1378 root = be32_to_cpu(agi->agi_root);
1379 levels = be32_to_cpu(agi->agi_level);
1380
1381 /* validate root and levels before processing the tree */
1382 if (root == 0 || root > mp->m_sb.sb_agblocks) {
1383 if (show_warnings)
1384 print_warning("invalid block number (%u) in inobt "
1385 "root in agi %u", root, agno);
1386 return 1;
1387 }
1388 if (levels >= XFS_BTREE_MAXLEVELS) {
1389 if (show_warnings)
1390 print_warning("invalid level (%u) in inobt root "
1391 "in agi %u", levels, agno);
1392 return 1;
1393 }
1394
1395 return scan_btree(agno, root, levels, TYP_INOBT, agi, scanfunc_ino);
1396}
1397
1398static int
1399scan_ag(
1400 xfs_agnumber_t agno)
1401{
1402 xfs_agf_t *agf;
1403 xfs_agi_t *agi;
1404
1405 /* copy the superblock of the AG */
1406 push_cur();
1407 set_cur(&typtab[TYP_SB], XFS_AG_DADDR(mp, agno, XFS_SB_DADDR),
1408 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
1409 if (!iocur_top->data) {
1410 print_warning("cannot read superblock for ag %u", agno);
1411 if (stop_on_read_error)
1412 return 0;
1413 } else {
1414 if (!write_buf(iocur_top))
1415 return 0;
1416 }
1417
1418 /* copy the AG free space btree root */
1419 push_cur();
1420 set_cur(&typtab[TYP_AGF], XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)),
1421 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
1422 agf = iocur_top->data;
1423 if (iocur_top->data == NULL) {
1424 print_warning("cannot read agf block for ag %u", agno);
1425 if (stop_on_read_error)
1426 return 0;
1427 } else {
1428 if (!write_buf(iocur_top))
1429 return 0;
1430 }
1431
1432 /* copy the AG inode btree root */
1433 push_cur();
1434 set_cur(&typtab[TYP_AGI], XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)),
1435 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
1436 agi = iocur_top->data;
1437 if (iocur_top->data == NULL) {
1438 print_warning("cannot read agi block for ag %u", agno);
1439 if (stop_on_read_error)
1440 return 0;
1441 } else {
1442 if (!write_buf(iocur_top))
1443 return 0;
1444 }
1445
1446 /* copy the AG free list header */
1447 push_cur();
1448 set_cur(&typtab[TYP_AGFL], XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)),
1449 XFS_FSS_TO_BB(mp, 1), DB_RING_IGN, NULL);
1450 if (iocur_top->data == NULL) {
1451 print_warning("cannot read agfl block for ag %u", agno);
1452 if (stop_on_read_error)
1453 return 0;
1454 } else {
1455 if (!write_buf(iocur_top))
1456 return 0;
1457 }
1458 pop_cur();
1459
1460 /* copy AG free space btrees */
1461 if (agf) {
1462 if (show_progress)
1463 print_progress("Copying free space trees of AG %u",
1464 agno);
1465 if (!copy_free_bno_btree(agno, agf))
1466 return 0;
1467 if (!copy_free_cnt_btree(agno, agf))
1468 return 0;
1469 }
1470
1471 /* copy inode btrees and the inodes and their associated metadata */
1472 if (agi) {
1473 if (!copy_inodes(agno, agi))
1474 return 0;
1475 }
1476
1477 pop_cur();
1478 pop_cur();
1479 pop_cur();
1480
1481 return 1;
1482}
1483
1484static int
1485copy_ino(
1486 xfs_ino_t ino,
1487 typnm_t itype)
1488{
1489 xfs_agnumber_t agno;
1490 xfs_agblock_t agbno;
1491 xfs_agino_t agino;
1492 xfs_dinode_t *dip;
1493 xfs_dinode_core_t tdic;
1494 int offset;
1495
1496 if (ino == 0)
1497 return 1;
1498
1499 agno = XFS_INO_TO_AGNO(mp, ino);
1500 agino = XFS_INO_TO_AGINO(mp, ino);
1501 agbno = XFS_AGINO_TO_AGBNO(mp, agino);
1502 offset = XFS_AGINO_TO_OFFSET(mp, agino);
1503
1504 if (agno >= mp->m_sb.sb_agcount || agbno >= mp->m_sb.sb_agblocks ||
1505 offset >= mp->m_sb.sb_inopblock) {
1506 if (show_warnings)
1507 print_warning("invalid %s inode number (%lld)",
1508 typtab[itype].name, (long long)ino);
1509 return 1;
1510 }
1511
1512 push_cur();
1513 set_cur(&typtab[TYP_INODE], XFS_AGB_TO_DADDR(mp, agno, agbno),
1514 blkbb, DB_RING_IGN, NULL);
1515 if (iocur_top->data == NULL) {
1516 print_warning("cannot read %s inode %lld",
1517 typtab[itype].name, (long long)ino);
1518 return !stop_on_read_error;
1519 }
1520 off_cur(offset << mp->m_sb.sb_inodelog, mp->m_sb.sb_inodesize);
1521
1522 dip = iocur_top->data;
1523 libxfs_xlate_dinode_core((xfs_caddr_t)&dip->di_core, &tdic, 1);
1524 memcpy(&dip->di_core, &tdic, sizeof(xfs_dinode_core_t));
1525
1526 cur_ino = ino;
1527 return process_inode_data(dip, itype);
1528}
1529
1530
1531static int
1532copy_sb_inodes(void)
1533{
1534 if (!copy_ino(mp->m_sb.sb_rbmino, TYP_RTBITMAP))
1535 return 0;
1536
1537 if (!copy_ino(mp->m_sb.sb_rsumino, TYP_RTSUMMARY))
1538 return 0;
1539
1540 if (!copy_ino(mp->m_sb.sb_uquotino, TYP_DQBLK))
1541 return 0;
1542
1543 return copy_ino(mp->m_sb.sb_gquotino, TYP_DQBLK);
1544}
1545
1546static int
1547copy_log(void)
1548{
1549 if (show_progress)
1550 print_progress("Copying log");
1551
1552 push_cur();
1553 set_cur(&typtab[TYP_LOG], XFS_FSB_TO_DADDR(mp, mp->m_sb.sb_logstart),
1554 mp->m_sb.sb_logblocks * blkbb, DB_RING_IGN, NULL);
1555 if (iocur_top->data == NULL) {
1556 print_warning("cannot read log");
1557 return !stop_on_read_error;
1558 }
1559 return write_buf(iocur_top);
1560}
1561
1562static int
1563metadump_f(
1564 int argc,
1565 char **argv)
1566{
1567 xfs_agnumber_t agno;
1568 int c;
1569 int start_iocur_sp;
88b8e1d6 1570 char *p;
61983f67
BN
1571
1572 exitcode = 1;
1573 show_progress = 0;
1574 show_warnings = 0;
1575 stop_on_read_error = 0;
1576
1577 if (mp->m_sb.sb_magicnum != XFS_SB_MAGIC) {
1578 print_warning("bad superblock magic number %x, giving up",
1579 mp->m_sb.sb_magicnum);
1580 return 0;
1581 }
1582
88b8e1d6 1583 while ((c = getopt(argc, argv, "egm:ow")) != EOF) {
61983f67
BN
1584 switch (c) {
1585 case 'e':
1586 stop_on_read_error = 1;
1587 break;
1588 case 'g':
1589 show_progress = 1;
1590 break;
88b8e1d6
BN
1591 case 'm':
1592 max_extent_size = (int)strtol(optarg, &p, 0);
1593 if (*p != '\0' || max_extent_size <= 0) {
1594 print_warning("bad max extent size %s",
1595 optarg);
1596 return 0;
1597 }
1598 break;
61983f67
BN
1599 case 'o':
1600 dont_obfuscate = 1;
1601 break;
1602 case 'w':
1603 show_warnings = 1;
1604 break;
1605 default:
1606 print_warning("bad option for metadump command");
1607 return 0;
1608 }
1609 }
1610
1611 if (optind != argc - 1) {
1612 print_warning("too few options for metadump (no filename given)");
1613 return 0;
1614 }
1615
1616 metablock = (xfs_metablock_t *)calloc(BBSIZE + 1, BBSIZE);
1617 if (metablock == NULL) {
1618 print_warning("memory allocation failure");
1619 return 0;
1620 }
1621 metablock->mb_blocklog = BBSHIFT;
1622 metablock->mb_magic = cpu_to_be32(XFS_MD_MAGIC);
1623
1624 if (!create_nametable()) {
1625 print_warning("memory allocation failure");
1626 free(metablock);
1627 return 0;
1628 }
1629
1630 block_index = (__be64 *)((char *)metablock + sizeof(xfs_metablock_t));
1631 block_buffer = (char *)metablock + BBSIZE;
1632 num_indicies = (BBSIZE - sizeof(xfs_metablock_t)) / sizeof(__be64);
1633 cur_index = 0;
1634 start_iocur_sp = iocur_sp;
1635
1636 if (strcmp(argv[optind], "-") == 0) {
1637 if (isatty(fileno(stdout))) {
1638 print_warning("cannot write to a terminal");
1639 free(nametable);
1640 free(metablock);
1641 return 0;
1642 }
1643 outf = stdout;
1644 } else {
1645 outf = fopen(argv[optind], "wb");
1646 if (outf == NULL) {
1647 print_warning("cannot create dump file");
1648 free(nametable);
1649 free(metablock);
1650 return 0;
1651 }
1652 }
1653
1654 exitcode = 0;
1655
1656 for (agno = 0; agno < mp->m_sb.sb_agcount; agno++) {
1657 if (!scan_ag(agno)) {
1658 exitcode = 1;
1659 break;
1660 }
1661 }
1662
1663 /* copy realtime and quota inode contents */
1664 if (!exitcode)
1665 exitcode = !copy_sb_inodes();
1666
1667 /* copy log if it's internal */
1668 if ((mp->m_sb.sb_logstart != 0) && !exitcode)
1669 exitcode = !copy_log();
1670
1671 /* write the remaining index */
1672 if (!exitcode)
1673 exitcode = !write_index();
1674
1675 if (progress_since_warning)
1676 fputc('\n', (outf == stdout) ? stderr : stdout);
1677
1678 if (outf != stdout)
1679 fclose(outf);
1680
1681 /* cleanup iocur stack */
1682 while (iocur_sp > start_iocur_sp)
1683 pop_cur();
1684
1685 free(nametable);
1686 free(metablock);
1687
1688 return 0;
1689}