]>
Commit | Line | Data |
---|---|---|
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 | ||
32 | static int metadump_f(int argc, char **argv); | |
33 | static 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 | ||
43 | static 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 | ||
48 | static FILE *outf; /* metadump file */ | |
49 | ||
50 | static xfs_metablock_t *metablock; /* header + index + buffers */ | |
51 | static __be64 *block_index; | |
52 | static char *block_buffer; | |
53 | ||
54 | static int num_indicies; | |
55 | static int cur_index; | |
56 | ||
57 | static xfs_ino_t cur_ino; | |
58 | ||
59 | static int show_progress = 0; | |
60 | static int stop_on_read_error = 0; | |
88b8e1d6 | 61 | static int max_extent_size = 20; |
61983f67 BN |
62 | static int dont_obfuscate = 0; |
63 | static int show_warnings = 0; | |
64 | static int progress_since_warning = 0; | |
65 | ||
66 | void | |
67 | metadump_init(void) | |
68 | { | |
69 | add_command(&metadump_cmd); | |
70 | } | |
71 | ||
72 | static void | |
73 | metadump_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 | ||
89 | static void | |
90 | print_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 | ||
108 | static void | |
109 | print_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 | ||
136 | static int | |
137 | write_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 | ||
153 | static int | |
154 | write_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 | ||
175 | static int | |
176 | scan_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 | ||
209 | static int | |
210 | valid_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 |
226 | static int |
227 | scanfunc_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 | ||
268 | static int | |
269 | copy_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 | ||
296 | static int | |
297 | copy_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 | ||
326 | typedef 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 | ||
335 | static name_ent_t **nametable; | |
336 | ||
337 | static int | |
338 | create_nametable(void) | |
339 | { | |
340 | nametable = calloc(NAME_TABLE_SIZE, sizeof(name_ent_t)); | |
341 | return nametable != NULL; | |
342 | } | |
343 | ||
344 | static void | |
345 | clear_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 | ||
363 | static inline uchar_t | |
364 | random_filename_char(void) | |
365 | { | |
366 | uchar_t c; | |
367 | ||
368 | do { | |
369 | c = random() % 127 + 1; | |
370 | } while (c == '/'); | |
371 | return c; | |
372 | } | |
373 | ||
374 | static int | |
375 | is_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 | ||
410 | static void | |
411 | generate_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 | ||
490 | static void | |
491 | obfuscate_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 | ||
547 | static void | |
548 | obfuscate_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 | ||
565 | static void | |
566 | obfuscate_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 | ||
623 | static 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 | ||
630 | static void | |
631 | obfuscate_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 | ||
761 | static void | |
762 | obfuscate_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 | ||
775 | static struct attr_data_s { | |
776 | int remote_val_count; | |
777 | xfs_dablk_t remote_vals[MAX_REMOTE_VALS]; | |
778 | } attr_data; | |
779 | ||
780 | static inline void | |
781 | add_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 | ||
793 | static void | |
794 | obfuscate_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 | ||
871 | static int | |
872 | process_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 | ||
985 | static int | |
986 | scanfunc_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 | ||
1045 | static int | |
1046 | process_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 | ||
1119 | static int | |
1120 | process_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 | ||
1142 | static int | |
1143 | process_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 | ||
1172 | static int | |
1173 | process_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 | ||
1230 | static __uint32_t inodes_copied = 0; | |
1231 | ||
1232 | static int | |
1233 | copy_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 | 1296 | skip_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 | ||
1312 | static int | |
1313 | scanfunc_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 | ||
1370 | static int | |
1371 | copy_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 | ||
1398 | static int | |
1399 | scan_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 | ||
1484 | static int | |
1485 | copy_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 | ||
1531 | static int | |
1532 | copy_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 | ||
1546 | static int | |
1547 | copy_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 | ||
1562 | static int | |
1563 | metadump_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 | } |