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