xref: /dflybsd-src/sbin/hammer2/cmd_recover.c (revision eefc659d345ccfd93a9146d3aa9637bee107afbd)
1ca5510acSMatthew Dillon /*
2ca5510acSMatthew Dillon  * Copyright (c) 2023 The DragonFly Project.  All rights reserved.
3ca5510acSMatthew Dillon  *
4ca5510acSMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
5ca5510acSMatthew Dillon  * by Matthew Dillon <dillon@dragonflybsd.org>
6ca5510acSMatthew Dillon  * by Venkatesh Srinivas <vsrinivas@dragonflybsd.org>
7ca5510acSMatthew Dillon  *
8ca5510acSMatthew Dillon  * Redistribution and use in source and binary forms, with or without
9ca5510acSMatthew Dillon  * modification, are permitted provided that the following conditions
10ca5510acSMatthew Dillon  * are met:
11ca5510acSMatthew Dillon  *
12ca5510acSMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
13ca5510acSMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
14ca5510acSMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
15ca5510acSMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
16ca5510acSMatthew Dillon  *    the documentation and/or other materials provided with the
17ca5510acSMatthew Dillon  *    distribution.
18ca5510acSMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
19ca5510acSMatthew Dillon  *    contributors may be used to endorse or promote products derived
20ca5510acSMatthew Dillon  *    from this software without specific, prior written permission.
21ca5510acSMatthew Dillon  *
22ca5510acSMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23ca5510acSMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24ca5510acSMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25ca5510acSMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
26ca5510acSMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27ca5510acSMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
28ca5510acSMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29ca5510acSMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
30ca5510acSMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
31ca5510acSMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
32ca5510acSMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33ca5510acSMatthew Dillon  * SUCH DAMAGE.
34ca5510acSMatthew Dillon  */
355627cde5SMatthew Dillon /*
365627cde5SMatthew Dillon  * hammer2 recover <devpath> <path> <destdir>
375627cde5SMatthew Dillon  *
385627cde5SMatthew Dillon  * Recover files from corrupted media, recover deleted files from good
395627cde5SMatthew Dillon  * media, generally ignoring the data structure topology outside of the
405627cde5SMatthew Dillon  * structures that hang off of inodes and directory entries.  Files are
415627cde5SMatthew Dillon  * validated during recovery and renamed to .corrupted when a version of
425627cde5SMatthew Dillon  * a file cannot be completely recovered.
435627cde5SMatthew Dillon  *
445627cde5SMatthew Dillon  * This is a "try to get everything we can out of the filesystem"
455627cde5SMatthew Dillon  * directive when you've done something terrible to the filesystem.
465627cde5SMatthew Dillon  * The resulting <destdir> tree cannot directly replace the lost topology
475627cde5SMatthew Dillon  * as many versions of the same file will be present, so filenames are
485627cde5SMatthew Dillon  * suffixed.
495627cde5SMatthew Dillon  *
505627cde5SMatthew Dillon  * <path> may be a relative file, directory, directory path, or absolute
515627cde5SMatthew Dillon  * (absolute is relative to the mount) file or directory path.
525627cde5SMatthew Dillon  *
535627cde5SMatthew Dillon  * For example, "hammer2 recover /dev/da0s1d /home/charlie /tmp/" will
545627cde5SMatthew Dillon  * recover all possible versions of the /home/charlie sub-tree.  If
555627cde5SMatthew Dillon  * you said "home/charlie" instead, then it would recover the same but
565627cde5SMatthew Dillon  * also include any sub-directory paths that match home/charlie, not
575627cde5SMatthew Dillon  * just root paths.  If you want a specific file, then e.g. ".cshrc"
585627cde5SMatthew Dillon  * would recover every single .cshrc that can be found on the media.
595627cde5SMatthew Dillon  *
605627cde5SMatthew Dillon  * The command checks ALL PFSs and snapshots.  Redundant files with the same
615627cde5SMatthew Dillon  * path are ignored.  You basically get everything that can possibly be
625627cde5SMatthew Dillon  * recovered from the media.
635627cde5SMatthew Dillon  */
64ca5510acSMatthew Dillon #include "hammer2.h"
65ca5510acSMatthew Dillon 
66*eefc659dSMatthew Dillon #define HTABLE_SIZE	(4*1024*1024)
67*eefc659dSMatthew Dillon #define HTABLE_MASK	(HTABLE_SIZE - 1)
68*eefc659dSMatthew Dillon #define DISPMODULO	(HTABLE_SIZE / 32768)
69ca5510acSMatthew Dillon 
70ca5510acSMatthew Dillon #include <openssl/sha.h>
71ca5510acSMatthew Dillon 
725627cde5SMatthew Dillon typedef struct dirent_entry {
735627cde5SMatthew Dillon 	struct dirent_entry *next;
745627cde5SMatthew Dillon 	char		*filename;
755627cde5SMatthew Dillon 	size_t		len;
76ca5510acSMatthew Dillon 	hammer2_key_t	inum;
775627cde5SMatthew Dillon 	hammer2_off_t	bref_off;
785627cde5SMatthew Dillon } dirent_entry_t;
79ca5510acSMatthew Dillon 
805627cde5SMatthew Dillon typedef struct inode_entry {
815627cde5SMatthew Dillon 	struct inode_entry *next;
82c84b3a36SMatthew Dillon 	struct inode_entry *next2;	/* secondary (ino ^ data_off) hash */
835627cde5SMatthew Dillon 	hammer2_off_t	data_off;
84*eefc659dSMatthew Dillon 	hammer2_key_t	inum;		/* from bref or inode meta */
85*eefc659dSMatthew Dillon 	//hammer2_inode_data_t inode;	/* (removed, too expensive) */
865627cde5SMatthew Dillon 	char		*link_file_path;
87*eefc659dSMatthew Dillon 	uint32_t	inode_crc;
88*eefc659dSMatthew Dillon 	uint8_t		type;		/* from inode meta */
89*eefc659dSMatthew Dillon 	uint8_t		encountered;	/* copies limit w/REPINODEDEPTH */
90*eefc659dSMatthew Dillon 	uint8_t		loopcheck;	/* recursion loop check */
91*eefc659dSMatthew Dillon 	uint8_t		unused03;
925627cde5SMatthew Dillon } inode_entry_t;
93ca5510acSMatthew Dillon 
945627cde5SMatthew Dillon typedef struct topology_entry {
955627cde5SMatthew Dillon 	struct topology_entry *next;
965627cde5SMatthew Dillon 	char		*path;
975627cde5SMatthew Dillon 	long		iterator;
985627cde5SMatthew Dillon } topology_entry_t;
995627cde5SMatthew Dillon 
100c255364fSMatthew Dillon typedef struct neg_entry {
101c255364fSMatthew Dillon 	struct neg_entry *next;
102c255364fSMatthew Dillon 	hammer2_blockref_t bref;
103c255364fSMatthew Dillon } neg_entry_t;
104c255364fSMatthew Dillon 
105f26c2b1fSMatthew Dillon typedef struct topo_bref_entry {
106f26c2b1fSMatthew Dillon 	struct topo_bref_entry *next;
107f26c2b1fSMatthew Dillon 	topology_entry_t  *topo;
108f26c2b1fSMatthew Dillon 	hammer2_off_t	data_off;
109f26c2b1fSMatthew Dillon } topo_bref_entry_t;
110f26c2b1fSMatthew Dillon 
111f26c2b1fSMatthew Dillon typedef struct topo_inode_entry {
112f26c2b1fSMatthew Dillon 	struct topo_inode_entry *next;
113f26c2b1fSMatthew Dillon 	topology_entry_t  *topo;
114*eefc659dSMatthew Dillon 	inode_entry_t *iscan;
115f26c2b1fSMatthew Dillon } topo_inode_entry_t;
116f26c2b1fSMatthew Dillon 
1175627cde5SMatthew Dillon static dirent_entry_t **DirHash;
1185627cde5SMatthew Dillon static inode_entry_t **InodeHash;
119c84b3a36SMatthew Dillon static inode_entry_t **InodeHash2;	/* secondary multi-variable hash */
1205627cde5SMatthew Dillon static topology_entry_t **TopologyHash;
121c255364fSMatthew Dillon static neg_entry_t **NegativeHash;
122f26c2b1fSMatthew Dillon static topo_bref_entry_t **TopoBRefHash;
123f26c2b1fSMatthew Dillon static topo_inode_entry_t **TopoInodeHash;
1245627cde5SMatthew Dillon 
1255627cde5SMatthew Dillon /*static void resolve_topology(void);*/
1265627cde5SMatthew Dillon static int check_filename(hammer2_blockref_t *bref,
1275627cde5SMatthew Dillon 			const char *filename, char *buf, size_t flen);
1285627cde5SMatthew Dillon //static void enter_dirent(hammer2_blockref_t *bref, hammer2_off_t loff,
1295627cde5SMatthew Dillon //			const char *filename, size_t flen);
1305627cde5SMatthew Dillon static topology_entry_t *enter_topology(const char *path);
1315627cde5SMatthew Dillon static int topology_check_duplicate_inode(topology_entry_t *topo,
1325627cde5SMatthew Dillon 			inode_entry_t *iscan);
133b601e4dfSMatthew Dillon static int topology_check_duplicate_indirect(topology_entry_t *topo,
134b601e4dfSMatthew Dillon 			hammer2_blockref_t *bref);
1355627cde5SMatthew Dillon static void enter_inode(hammer2_blockref_t *bref);
1365627cde5SMatthew Dillon static void enter_inode_untested(hammer2_inode_data_t *ip, hammer2_off_t loff);
1375627cde5SMatthew Dillon static inode_entry_t *find_first_inode(hammer2_key_t inum);
1385627cde5SMatthew Dillon /*static dirent_entry_t *find_first_dirent(hammer2_key_t inum);*/
139c255364fSMatthew Dillon static int find_neg(hammer2_blockref_t *bref);
140c255364fSMatthew Dillon static void enter_neg(hammer2_blockref_t *bref);
1415627cde5SMatthew Dillon static void dump_tree(inode_entry_t *iscan, const char *dest,
142b601e4dfSMatthew Dillon 			const char *remain, int depth, int path_depth,
143b601e4dfSMatthew Dillon 			int isafile);
144*eefc659dSMatthew Dillon static int dump_inum_file(inode_entry_t *iscan, hammer2_inode_data_t *inode,
145*eefc659dSMatthew Dillon 			const char *path);
1465627cde5SMatthew Dillon static int dump_inum_softlink(hammer2_inode_data_t *inode, const char *path);
1475627cde5SMatthew Dillon static int dump_dir_data(const char *dest, const char *remain,
148ddc249f6SMatthew Dillon 			hammer2_blockref_t *base, int count,
149b601e4dfSMatthew Dillon 			int depth, int path_depth, int isafile);
150ca5510acSMatthew Dillon static int dump_file_data(int wfd, hammer2_off_t fsize,
151ca5510acSMatthew Dillon 			hammer2_blockref_t *bref, int count);
152ca5510acSMatthew Dillon static int validate_crc(hammer2_blockref_t *bref, void *data, size_t bytes);
153ca5510acSMatthew Dillon static uint32_t hammer2_to_unix_xid(const uuid_t *uuid);
154*eefc659dSMatthew Dillon static void *hammer2_cache_read(hammer2_off_t data_off, size_t *bytesp);
155ca5510acSMatthew Dillon 
1565627cde5SMatthew Dillon static long InodeCount;
157f26c2b1fSMatthew Dillon static long TopoBRefCount;
158f26c2b1fSMatthew Dillon static long TopoBRefDupCount;
159f26c2b1fSMatthew Dillon static long TopoInodeCount;
160f26c2b1fSMatthew Dillon static long TopoInodeDupCount;
161c255364fSMatthew Dillon static long NegativeCount;
162c255364fSMatthew Dillon static long NegativeHits;
163ddc249f6SMatthew Dillon static long MediaBytes;
164ddc249f6SMatthew Dillon static int StrictMode = 1;
1655627cde5SMatthew Dillon 
1665627cde5SMatthew Dillon #define INODES_PER_BLOCK	\
1675627cde5SMatthew Dillon 	(sizeof(union hammer2_media_data) / sizeof(hammer2_inode_data_t))
1685627cde5SMatthew Dillon #define REPINODEDEPTH		256
1695627cde5SMatthew Dillon 
170ca5510acSMatthew Dillon /*
171ca5510acSMatthew Dillon  * Recover the specified file.
172ca5510acSMatthew Dillon  *
173ca5510acSMatthew Dillon  * Basically do a raw scan of the drive image looking for directory entries
174ca5510acSMatthew Dillon  * and inodes.  Index all inodes found, including copies, and filter
175ca5510acSMatthew Dillon  * directory entries for the requested filename to locate inode numbers.
176ca5510acSMatthew Dillon  *
177ca5510acSMatthew Dillon  * All copies that are located are written to destdir with a suffix .00001,
178ca5510acSMatthew Dillon  * .00002, etc.
179ca5510acSMatthew Dillon  */
180ca5510acSMatthew Dillon int
cmd_recover(const char * devpath,const char * pathname,const char * destdir,int strict,int isafile)1815627cde5SMatthew Dillon cmd_recover(const char *devpath, const char *pathname,
182ddc249f6SMatthew Dillon 	    const char *destdir, int strict, int isafile)
183ca5510acSMatthew Dillon {
184ca5510acSMatthew Dillon 	hammer2_media_data_t data;
185ca5510acSMatthew Dillon 	hammer2_volume_t *vol;
186ca5510acSMatthew Dillon 	hammer2_off_t loff;
187ca5510acSMatthew Dillon 	hammer2_off_t poff;
188ca5510acSMatthew Dillon 	size_t i;
189ca5510acSMatthew Dillon 
190ddc249f6SMatthew Dillon 	StrictMode = strict;
191ca5510acSMatthew Dillon 	hammer2_init_volumes(devpath, 1);
192*eefc659dSMatthew Dillon 	DirHash = calloc(HTABLE_SIZE, sizeof(dirent_entry_t *));
193*eefc659dSMatthew Dillon 	InodeHash = calloc(HTABLE_SIZE, sizeof(inode_entry_t *));
194*eefc659dSMatthew Dillon 	InodeHash2 = calloc(HTABLE_SIZE, sizeof(inode_entry_t *));
195*eefc659dSMatthew Dillon 	TopologyHash = calloc(HTABLE_SIZE, sizeof(topology_entry_t *));
196*eefc659dSMatthew Dillon 	NegativeHash = calloc(HTABLE_SIZE, sizeof(neg_entry_t *));
197*eefc659dSMatthew Dillon 	TopoBRefHash = calloc(HTABLE_SIZE, sizeof(topo_bref_entry_t *));
198*eefc659dSMatthew Dillon 	TopoInodeHash = calloc(HTABLE_SIZE, sizeof(topo_inode_entry_t *));
199ca5510acSMatthew Dillon 
2005627cde5SMatthew Dillon 	/*
2015627cde5SMatthew Dillon 	 * Media Pass
2025627cde5SMatthew Dillon 	 *
2035627cde5SMatthew Dillon 	 * Look for blockrefs that point to inodes.  The blockrefs could
2045627cde5SMatthew Dillon 	 * be bogus since we aren't validating them, but the combination
2055627cde5SMatthew Dillon 	 * of a CRC that matches the inode content is fairly robust in
2065627cde5SMatthew Dillon 	 * finding actual inodes.
2075627cde5SMatthew Dillon 	 *
2085627cde5SMatthew Dillon 	 * We also enter unvalidated inodes for inode #1 (PFS roots),
2095627cde5SMatthew Dillon 	 * because there might not be any blockrefs pointing to some of
2105627cde5SMatthew Dillon 	 * them.  We need these to be able to locate directory entries
2115627cde5SMatthew Dillon 	 * under the roots.
2125627cde5SMatthew Dillon 	 *
2135627cde5SMatthew Dillon 	 * At the moment we do not try to enter unvalidated directory
2145627cde5SMatthew Dillon 	 * entries, since this will result in a massive number of false
2155627cde5SMatthew Dillon 	 * hits
2165627cde5SMatthew Dillon 	 */
2175627cde5SMatthew Dillon 	printf("MEDIA PASS\n");
218ca5510acSMatthew Dillon 
219ca5510acSMatthew Dillon 	loff = 0;
220ca5510acSMatthew Dillon 	while ((vol = hammer2_get_volume(loff)) != NULL) {
221ddc249f6SMatthew Dillon 		int fd;
222ddc249f6SMatthew Dillon 		int xdisp;
223ddc249f6SMatthew Dillon 		hammer2_off_t vol_size;
224ddc249f6SMatthew Dillon 
225ca5510acSMatthew Dillon 		fd = vol->fd;
226ca5510acSMatthew Dillon 		poff = loff - vol->offset;
227ddc249f6SMatthew Dillon 		vol_size = lseek(fd, 0L, SEEK_END);
228ddc249f6SMatthew Dillon 		xdisp = 0;
229ddc249f6SMatthew Dillon 
230ca5510acSMatthew Dillon 		while (poff < vol->size) {
231ca5510acSMatthew Dillon 			if (pread(fd, &data, sizeof(data), poff) !=
232ca5510acSMatthew Dillon 			    sizeof(data))
233ca5510acSMatthew Dillon 			{
234ca5510acSMatthew Dillon 				/* try to skip possible I/O error */
235ca5510acSMatthew Dillon 				poff += sizeof(data);
236ca5510acSMatthew Dillon 				continue;
237ca5510acSMatthew Dillon 			}
238ca5510acSMatthew Dillon 			for (i = 0; i < HAMMER2_IND_COUNT_MAX; ++i) {
239ca5510acSMatthew Dillon 				hammer2_blockref_t *bref;
2405627cde5SMatthew Dillon 				// char filename_buf[1024+1];
241ca5510acSMatthew Dillon 
242ca5510acSMatthew Dillon 				bref = &data.npdata[i];
243ca5510acSMatthew Dillon 
244ca5510acSMatthew Dillon 				/*
2455627cde5SMatthew Dillon 				 * Found a possible inode
246ca5510acSMatthew Dillon 				 */
2475627cde5SMatthew Dillon 				switch(bref->type) {
2485627cde5SMatthew Dillon 				case HAMMER2_BREF_TYPE_INODE:
249a5eec3d1SMatthew Dillon 					/*
250a5eec3d1SMatthew Dillon 					 * Note: preliminary bref filter
251a5eec3d1SMatthew Dillon 					 * is inside enter_inode().
252a5eec3d1SMatthew Dillon 					 */
2535627cde5SMatthew Dillon 					enter_inode(bref);
2545627cde5SMatthew Dillon 					break;
2555627cde5SMatthew Dillon 				case HAMMER2_BREF_TYPE_DIRENT:
2565627cde5SMatthew Dillon 					/*
2575627cde5SMatthew Dillon 					 * Go overboard and try to index
2585627cde5SMatthew Dillon 					 * anything that looks like a
2595627cde5SMatthew Dillon 					 * directory entry.  This might find
2605627cde5SMatthew Dillon 					 * entries whos inodes are no longer
2615627cde5SMatthew Dillon 					 * available, but will also generate
2625627cde5SMatthew Dillon 					 * a lot of false files.
2635627cde5SMatthew Dillon 					 */
2645627cde5SMatthew Dillon #if 0
2655627cde5SMatthew Dillon 					if (filename &&
2665627cde5SMatthew Dillon 					    flen != bref->embed.dirent.namlen)
267ca5510acSMatthew Dillon 					{
2685627cde5SMatthew Dillon 						/* single-file shortcut */
2695627cde5SMatthew Dillon 						break;
2705627cde5SMatthew Dillon 					}
2715627cde5SMatthew Dillon 					if (check_filename(bref, filename,
2725627cde5SMatthew Dillon 						   filename_buf,
2735627cde5SMatthew Dillon 						   bref->embed.dirent.namlen))
2745627cde5SMatthew Dillon 					{
2755627cde5SMatthew Dillon 						enter_dirent(bref,
2765627cde5SMatthew Dillon 						    poff + vol->offset +
2775627cde5SMatthew Dillon 						    (i *
2785627cde5SMatthew Dillon 						    sizeof(hammer2_blockref_t)),
2795627cde5SMatthew Dillon 						    filename_buf,
2805627cde5SMatthew Dillon 						    bref->embed.dirent.namlen);
2815627cde5SMatthew Dillon 					}
2825627cde5SMatthew Dillon #endif
2835627cde5SMatthew Dillon 					break;
2845627cde5SMatthew Dillon 				default:
2855627cde5SMatthew Dillon 					break;
2865627cde5SMatthew Dillon 				}
2875627cde5SMatthew Dillon 			}
2885627cde5SMatthew Dillon 
2895627cde5SMatthew Dillon 			/*
2905627cde5SMatthew Dillon 			 * Look for possible root inodes.  We generally can't
2915627cde5SMatthew Dillon 			 * find these by finding BREFs pointing to them because
2925627cde5SMatthew Dillon 			 * the BREFs often hang off the volume header.
2935627cde5SMatthew Dillon 			 *
2945627cde5SMatthew Dillon 			 * These "inodes" could be seriously corrupt, but if
2955627cde5SMatthew Dillon 			 * the bref tree is intact that is what we need to
2965627cde5SMatthew Dillon 			 * get top-level directory entries.
2975627cde5SMatthew Dillon 			 */
2985627cde5SMatthew Dillon 			for (i = 0; i < INODES_PER_BLOCK; ++i) {
2995627cde5SMatthew Dillon 				hammer2_inode_data_t *ip;
3005627cde5SMatthew Dillon 
3015627cde5SMatthew Dillon 				ip = (void *)(data.buf + i * sizeof(*ip));
3025627cde5SMatthew Dillon 				if (ip->meta.inum == 1 &&
3035627cde5SMatthew Dillon 				    ip->meta.iparent == 0 &&
3045627cde5SMatthew Dillon 				    ip->meta.type ==
3055627cde5SMatthew Dillon 					HAMMER2_OBJTYPE_DIRECTORY &&
3065627cde5SMatthew Dillon 				    ip->meta.op_flags & HAMMER2_OPFLAG_PFSROOT)
3075627cde5SMatthew Dillon 				{
3085627cde5SMatthew Dillon 					enter_inode_untested(ip,
3095627cde5SMatthew Dillon 						poff + vol->offset +
3105627cde5SMatthew Dillon 						(i * sizeof(*ip)));
311ca5510acSMatthew Dillon 				}
312ca5510acSMatthew Dillon 			}
313ca5510acSMatthew Dillon 			poff += sizeof(data);
314ddc249f6SMatthew Dillon 
315ddc249f6SMatthew Dillon 			MediaBytes += sizeof(data);
316ddc249f6SMatthew Dillon 
317ddc249f6SMatthew Dillon 			/*
318ddc249f6SMatthew Dillon 			 * Update progress
319ddc249f6SMatthew Dillon 			 */
320ddc249f6SMatthew Dillon 			if (QuietOpt == 0 &&
321*eefc659dSMatthew Dillon 			    (++xdisp == DISPMODULO ||
322*eefc659dSMatthew Dillon 			     poff == vol->size - sizeof(data)))
323ddc249f6SMatthew Dillon 			{
324ddc249f6SMatthew Dillon 				xdisp = 0;
325ddc249f6SMatthew Dillon 				printf("%ld inodes scanned, "
326ddc249f6SMatthew Dillon 				       "media %6.2f/%-3.2fG\r",
327ddc249f6SMatthew Dillon 					InodeCount,
328ddc249f6SMatthew Dillon 					MediaBytes / 1e9,
329ddc249f6SMatthew Dillon 					vol_size / 1e9);
330ddc249f6SMatthew Dillon 				fflush(stdout);
331ddc249f6SMatthew Dillon 			}
332ca5510acSMatthew Dillon 		}
333ca5510acSMatthew Dillon 		loff = vol->offset + vol->size;
334ca5510acSMatthew Dillon 	}
335ca5510acSMatthew Dillon 
336ca5510acSMatthew Dillon 	/*
3375627cde5SMatthew Dillon 	 * Restoration Pass
3385627cde5SMatthew Dillon 	 *
3395627cde5SMatthew Dillon 	 * Run through available directory inodes, which allows us to locate
3405627cde5SMatthew Dillon 	 * and validate (crc check) their directory entry blockrefs and
3415627cde5SMatthew Dillon 	 * construct absolute or relative paths through a recursion.
3425627cde5SMatthew Dillon 	 *
3435627cde5SMatthew Dillon 	 * When an absolute path is obtained the search is anchored on a
3445627cde5SMatthew Dillon 	 * root inode.  When a relative path is obtained the search is
3455627cde5SMatthew Dillon 	 * unanchored and will find all matching sub-paths.  For example,
3465627cde5SMatthew Dillon 	 * if you look for ".cshrc" it will find ALL .cshrc's.  If you
3475627cde5SMatthew Dillon 	 * look for "fubar/.cshsrc" it will find ALL .cshrc's residing
3485627cde5SMatthew Dillon 	 * in a directory called fubar, however many there are.  But if
3495627cde5SMatthew Dillon 	 * you look for "/fubar/srcs" it will only find the sub-tree
3505627cde5SMatthew Dillon 	 * "/fubar/srcs" relative to PFS roots.
3515627cde5SMatthew Dillon 	 *
3525627cde5SMatthew Dillon 	 * We may not have indexed the PFS roots themselves, because they
3535627cde5SMatthew Dillon 	 * often hang off of the volume header and might not have COW'd
3545627cde5SMatthew Dillon 	 * references to them, so we use the "iparent" field in the inode
3555627cde5SMatthew Dillon 	 * to detect top-level directories under those roots.
3565627cde5SMatthew Dillon 	 */
357c255364fSMatthew Dillon 	printf("\nInodes=%ld, Invalid_brefs=%ld, Invalid_hits=%ld\n",
358c255364fSMatthew Dillon 		InodeCount, NegativeCount, NegativeHits);
3595627cde5SMatthew Dillon 	printf("RESTORATION PASS\n");
3605627cde5SMatthew Dillon 
3615627cde5SMatthew Dillon 	{
3625627cde5SMatthew Dillon 		int abspath = 0;
363b601e4dfSMatthew Dillon 		long root_count = 0;
364b601e4dfSMatthew Dillon 		long root_max = 0;
3655627cde5SMatthew Dillon 
3665627cde5SMatthew Dillon 		/*
3675627cde5SMatthew Dillon 		 * Check for absolute path, else relative
3685627cde5SMatthew Dillon 		 */
3695627cde5SMatthew Dillon 		if (pathname[0] == '/') {
3705627cde5SMatthew Dillon 			abspath = 1;
3715627cde5SMatthew Dillon 			while (*pathname == '/')
3725627cde5SMatthew Dillon 				++pathname;
3735627cde5SMatthew Dillon 		}
3745627cde5SMatthew Dillon 
3755627cde5SMatthew Dillon 		/*
376b601e4dfSMatthew Dillon 		 * Count root inodes
377b601e4dfSMatthew Dillon 		 */
378b601e4dfSMatthew Dillon 		{
379b601e4dfSMatthew Dillon 			inode_entry_t *iscan;
380b601e4dfSMatthew Dillon 
381b601e4dfSMatthew Dillon 			for (iscan = InodeHash[1];
382b601e4dfSMatthew Dillon 			     iscan;
383b601e4dfSMatthew Dillon 			     iscan = iscan->next)
384b601e4dfSMatthew Dillon 			{
385b601e4dfSMatthew Dillon 				if (iscan->inum == 1)
386b601e4dfSMatthew Dillon 					++root_max;
387b601e4dfSMatthew Dillon 			}
388b601e4dfSMatthew Dillon 		}
389b601e4dfSMatthew Dillon 
390b601e4dfSMatthew Dillon 		/*
3915627cde5SMatthew Dillon 		 * Run through all directory inodes to locate validated
3925627cde5SMatthew Dillon 		 * directory entries.  If an absolute path was specified
3935627cde5SMatthew Dillon 		 * we start at root inodes.
3945627cde5SMatthew Dillon 		 */
395*eefc659dSMatthew Dillon 		for (i = 0; i < HTABLE_SIZE; ++i) {
3965627cde5SMatthew Dillon 			inode_entry_t *iscan;
3975627cde5SMatthew Dillon 
3985627cde5SMatthew Dillon 			for (iscan = InodeHash[i];
3995627cde5SMatthew Dillon 			     iscan;
4005627cde5SMatthew Dillon 			     iscan = iscan->next)
4015627cde5SMatthew Dillon 			{
402b601e4dfSMatthew Dillon 				/*
403b601e4dfSMatthew Dillon 				 * Absolute paths always start at root inodes,
404b601e4dfSMatthew Dillon 				 * otherwise we can start at any directory
405b601e4dfSMatthew Dillon 				 * inode.
406b601e4dfSMatthew Dillon 				 */
407*eefc659dSMatthew Dillon 				if (abspath && iscan->inum != 1)
4085627cde5SMatthew Dillon 					continue;
409*eefc659dSMatthew Dillon 				if (iscan->type != HAMMER2_OBJTYPE_DIRECTORY)
4105627cde5SMatthew Dillon 					continue;
411b601e4dfSMatthew Dillon 
412b601e4dfSMatthew Dillon 				/*
413b601e4dfSMatthew Dillon 				 * Progress down root inodes can be slow,
414b601e4dfSMatthew Dillon 				 * so print progress for each root inode.
415b601e4dfSMatthew Dillon 				 */
416b601e4dfSMatthew Dillon 				if (i == 1 && iscan->inum == 1 &&
417b601e4dfSMatthew Dillon 				    QuietOpt == 0)
418b601e4dfSMatthew Dillon 				{
419f26c2b1fSMatthew Dillon 					printf("scan roots %p 0x%016jx "
420b601e4dfSMatthew Dillon 					       "(count %ld/%ld)\r",
421b601e4dfSMatthew Dillon 						iscan,
422b601e4dfSMatthew Dillon 						iscan->data_off,
423b601e4dfSMatthew Dillon 						++root_count, root_max);
424b601e4dfSMatthew Dillon 					fflush(stdout);
425b601e4dfSMatthew Dillon 				}
426b601e4dfSMatthew Dillon 
427b601e4dfSMatthew Dillon 				/*
428b601e4dfSMatthew Dillon 				 * Primary match/recover recursion
429b601e4dfSMatthew Dillon 				 */
430ddc249f6SMatthew Dillon 				dump_tree(iscan, destdir, pathname,
431b601e4dfSMatthew Dillon 					  1, 1, isafile);
432ddc249f6SMatthew Dillon 			}
433*eefc659dSMatthew Dillon 			if (QuietOpt == 0 &&
434*eefc659dSMatthew Dillon 			    (i & (DISPMODULO - 1)) == DISPMODULO - 1)
435*eefc659dSMatthew Dillon 			{
436*eefc659dSMatthew Dillon 				if (i == DISPMODULO - 1)
437b601e4dfSMatthew Dillon 					printf("\n");
438*eefc659dSMatthew Dillon 				printf("Progress %zd/%d\r", i, HTABLE_SIZE);
439ddc249f6SMatthew Dillon 				fflush(stdout);
4405627cde5SMatthew Dillon 			}
4415627cde5SMatthew Dillon 		}
442ddc249f6SMatthew Dillon 		printf("\n");
4435627cde5SMatthew Dillon 	}
4445627cde5SMatthew Dillon 
4455627cde5SMatthew Dillon 	printf("CLEANUP\n");
446f26c2b1fSMatthew Dillon 	printf("TopoBRef stats: count=%ld dups=%ld\n",
447f26c2b1fSMatthew Dillon 	       TopoBRefCount, TopoBRefDupCount);
448f26c2b1fSMatthew Dillon 	printf("TopoInode stats: count=%ld dups=%ld\n",
449f26c2b1fSMatthew Dillon 	       TopoInodeCount, TopoInodeDupCount);
4505627cde5SMatthew Dillon 
4515627cde5SMatthew Dillon 	/*
452ca5510acSMatthew Dillon 	 * Cleanup
453ca5510acSMatthew Dillon 	 */
454ca5510acSMatthew Dillon 	hammer2_cleanup_volumes();
455ca5510acSMatthew Dillon 
456*eefc659dSMatthew Dillon 	for (i = 0; i < HTABLE_SIZE; ++i) {
4575627cde5SMatthew Dillon 		dirent_entry_t *dscan;
4585627cde5SMatthew Dillon 		inode_entry_t *iscan;
4595627cde5SMatthew Dillon 		topology_entry_t *top_scan;
460c255364fSMatthew Dillon 		neg_entry_t *negscan;
461f26c2b1fSMatthew Dillon 		topo_bref_entry_t *topo_bref;
462f26c2b1fSMatthew Dillon 		topo_inode_entry_t *topo_inode;
463ca5510acSMatthew Dillon 
4645627cde5SMatthew Dillon 		while ((dscan = DirHash[i]) != NULL) {
4655627cde5SMatthew Dillon 			DirHash[i] = dscan->next;
4665627cde5SMatthew Dillon 			free(dscan->filename);
4675627cde5SMatthew Dillon 			free(dscan);
468ca5510acSMatthew Dillon 		}
469c84b3a36SMatthew Dillon 
4705627cde5SMatthew Dillon 		while ((iscan = InodeHash[i]) != NULL) {
4715627cde5SMatthew Dillon 			InodeHash[i] = iscan->next;
4725627cde5SMatthew Dillon 			free(iscan);
4735627cde5SMatthew Dillon 		}
474c84b3a36SMatthew Dillon 		/* InodeHash2[] indexes the same structures */
475c84b3a36SMatthew Dillon 
4765627cde5SMatthew Dillon 		while ((top_scan = TopologyHash[i]) != NULL) {
4775627cde5SMatthew Dillon 			TopologyHash[i] = top_scan->next;
4785627cde5SMatthew Dillon 			free(top_scan->path);
4795627cde5SMatthew Dillon 			free(top_scan);
480ca5510acSMatthew Dillon 		}
481c84b3a36SMatthew Dillon 
482c255364fSMatthew Dillon 		while ((negscan = NegativeHash[i]) != NULL) {
483c255364fSMatthew Dillon 			NegativeHash[i] = negscan->next;
484c255364fSMatthew Dillon 			free(negscan);
485ca5510acSMatthew Dillon 		}
486c84b3a36SMatthew Dillon 
487f26c2b1fSMatthew Dillon 		while ((topo_bref = TopoBRefHash[i]) != NULL) {
488f26c2b1fSMatthew Dillon 			TopoBRefHash[i] = topo_bref->next;
489f26c2b1fSMatthew Dillon 			free(topo_bref);
490c255364fSMatthew Dillon 		}
491c84b3a36SMatthew Dillon 
492f26c2b1fSMatthew Dillon 		while ((topo_inode = TopoInodeHash[i]) != NULL) {
493f26c2b1fSMatthew Dillon 			TopoInodeHash[i] = topo_inode->next;
494f26c2b1fSMatthew Dillon 			free(topo_inode);
495f26c2b1fSMatthew Dillon 		}
496f26c2b1fSMatthew Dillon 	}
497f26c2b1fSMatthew Dillon 	free(TopoInodeHash);
498f26c2b1fSMatthew Dillon 	free(TopoBRefHash);
499c255364fSMatthew Dillon 	free(NegativeHash);
5005627cde5SMatthew Dillon 	free(TopologyHash);
5015627cde5SMatthew Dillon 	free(DirHash);
5025627cde5SMatthew Dillon 	free(InodeHash);
503c84b3a36SMatthew Dillon 	free(InodeHash2);
504ca5510acSMatthew Dillon 
505ca5510acSMatthew Dillon 	return 0;
506ca5510acSMatthew Dillon }
507ca5510acSMatthew Dillon 
508ca5510acSMatthew Dillon /*
5095627cde5SMatthew Dillon  * Check for a matching filename, Directory entries can directly-embed
5105627cde5SMatthew Dillon  * filenames <= 64 bytes.  Otherwise the directory entry has a data
5115627cde5SMatthew Dillon  * reference to the location of the filename.
5125627cde5SMatthew Dillon  *
5135627cde5SMatthew Dillon  * If filename is NULL, check for a valid filename, and copy it into buf.
514ca5510acSMatthew Dillon  */
515ca5510acSMatthew Dillon static int
check_filename(hammer2_blockref_t * bref,const char * filename,char * buf,size_t flen)5165627cde5SMatthew Dillon check_filename(hammer2_blockref_t *bref, const char *filename, char *buf,
5175627cde5SMatthew Dillon 	       size_t flen)
518ca5510acSMatthew Dillon {
5195627cde5SMatthew Dillon 	/* filename too long */
5205627cde5SMatthew Dillon 	if (flen > 1024)
5215627cde5SMatthew Dillon 		return 0;
5225627cde5SMatthew Dillon 
5235627cde5SMatthew Dillon 	if (flen <= 64) {
5245627cde5SMatthew Dillon 		/*
5255627cde5SMatthew Dillon 		 * Filename is embedded in bref
5265627cde5SMatthew Dillon 		 */
5275627cde5SMatthew Dillon 		if (buf)
5285627cde5SMatthew Dillon 			bcopy(bref->check.buf, buf, flen);
5295627cde5SMatthew Dillon 		buf[flen] = 0;
5305627cde5SMatthew Dillon 		if (filename == NULL)
5315627cde5SMatthew Dillon 			return 1;
5325627cde5SMatthew Dillon 		if (bcmp(filename, bref->check.buf, flen) == 0)
5335627cde5SMatthew Dillon 			return 1;
5345627cde5SMatthew Dillon 	} else {
5355627cde5SMatthew Dillon 		/*
5365627cde5SMatthew Dillon 		 * Filename requires media access
5375627cde5SMatthew Dillon 		 */
5385627cde5SMatthew Dillon 		hammer2_media_data_t data;
539ca5510acSMatthew Dillon 		hammer2_volume_t *vol;
540ca5510acSMatthew Dillon 		hammer2_off_t poff;
5415627cde5SMatthew Dillon 		hammer2_off_t psize;
542ca5510acSMatthew Dillon 		int vfd;
543ca5510acSMatthew Dillon 
544ca5510acSMatthew Dillon 		/*
5455627cde5SMatthew Dillon 		 * bref must represent a data reference to a 1KB block or
5465627cde5SMatthew Dillon 		 * smaller.
547ca5510acSMatthew Dillon 		 */
5485627cde5SMatthew Dillon 		if ((bref->data_off & 0x1F) == 0 ||
5495627cde5SMatthew Dillon 		    (bref->data_off & 0x1F) > 10)
5505627cde5SMatthew Dillon 		{
5515627cde5SMatthew Dillon 			return 0;
552ca5510acSMatthew Dillon 		}
553ca5510acSMatthew Dillon 
554ca5510acSMatthew Dillon 		/*
5555627cde5SMatthew Dillon 		 * Indirect block containing filename must be large enough
5565627cde5SMatthew Dillon 		 * to contain the filename.
557ca5510acSMatthew Dillon 		 */
5585627cde5SMatthew Dillon 		psize = 1 << (bref->data_off & 0x1F);
5595627cde5SMatthew Dillon 		if (flen > psize)
560ca5510acSMatthew Dillon 			return 0;
5615627cde5SMatthew Dillon 
5625627cde5SMatthew Dillon 		/*
563ddc249f6SMatthew Dillon 		 * In strict mode we disallow bref's set to HAMMER2_CHECK_NONE
564ddc249f6SMatthew Dillon 		 * or HAMMER2_CHECK_DISABLED.  Do this check before burning
565ddc249f6SMatthew Dillon 		 * time on an I/O.
566ddc249f6SMatthew Dillon 		 */
567ddc249f6SMatthew Dillon 		if (StrictMode) {
568ddc249f6SMatthew Dillon 			if (HAMMER2_DEC_CHECK(bref->methods) ==
569ddc249f6SMatthew Dillon 				HAMMER2_CHECK_NONE ||
570ddc249f6SMatthew Dillon 			    HAMMER2_DEC_CHECK(bref->methods) ==
571ddc249f6SMatthew Dillon 				HAMMER2_CHECK_DISABLED)
572ddc249f6SMatthew Dillon 			{
573ddc249f6SMatthew Dillon 				return 0;
574ddc249f6SMatthew Dillon 			}
575ddc249f6SMatthew Dillon 		}
576ddc249f6SMatthew Dillon 
577ddc249f6SMatthew Dillon 		/*
5785627cde5SMatthew Dillon 		 * Read the data, check CRC and such
5795627cde5SMatthew Dillon 		 */
580ca5510acSMatthew Dillon 		vol = hammer2_get_volume(bref->data_off);
581ca5510acSMatthew Dillon 		if (vol == NULL)
582ca5510acSMatthew Dillon 			return 0;
583ca5510acSMatthew Dillon 
584ca5510acSMatthew Dillon 		vfd = vol->fd;
585ca5510acSMatthew Dillon 		poff = (bref->data_off - vol->offset) & ~0x1FL;
5865627cde5SMatthew Dillon 		if (pread(vfd, &data, psize, poff) != (ssize_t)psize)
587ca5510acSMatthew Dillon 			return 0;
588ca5510acSMatthew Dillon 
5895627cde5SMatthew Dillon 		if (validate_crc(bref, &data, psize) == 0)
5905627cde5SMatthew Dillon 			return 0;
5915627cde5SMatthew Dillon 
5925627cde5SMatthew Dillon 		if (buf)
5935627cde5SMatthew Dillon 			bcopy(data.buf, buf, flen);
5945627cde5SMatthew Dillon 		buf[flen] = 0;
5955627cde5SMatthew Dillon 		if (filename == NULL)
5965627cde5SMatthew Dillon 			return 1;
5975627cde5SMatthew Dillon 		if (bcmp(filename, data.buf, flen) == 0)
5985627cde5SMatthew Dillon 			return 1;
5995627cde5SMatthew Dillon 	}
6005627cde5SMatthew Dillon 	return 0;
6015627cde5SMatthew Dillon }
6025627cde5SMatthew Dillon 
6035627cde5SMatthew Dillon #if 0
6045627cde5SMatthew Dillon static void
6055627cde5SMatthew Dillon enter_dirent(hammer2_blockref_t *bref, hammer2_off_t loff,
6065627cde5SMatthew Dillon 	     const char *filename, size_t flen)
6075627cde5SMatthew Dillon {
6085627cde5SMatthew Dillon 	hammer2_key_t inum = bref->embed.dirent.inum;
6095627cde5SMatthew Dillon 	dirent_entry_t *entry;
610*eefc659dSMatthew Dillon 	uint32_t hv = (inum ^ (inum >> 16)) & HTABLE_MASK;
6115627cde5SMatthew Dillon 
6125627cde5SMatthew Dillon 	for (entry = DirHash[hv]; entry; entry = entry->next) {
6135627cde5SMatthew Dillon 		if (entry->inum == inum &&
6145627cde5SMatthew Dillon 		    entry->len == flen &&
6155627cde5SMatthew Dillon 		    bcmp(entry->filename, filename, flen) == 0)
6165627cde5SMatthew Dillon 		{
6175627cde5SMatthew Dillon 			return;
6185627cde5SMatthew Dillon 		}
6195627cde5SMatthew Dillon 	}
6205627cde5SMatthew Dillon 	entry = malloc(sizeof(*entry));
6215627cde5SMatthew Dillon 	bzero(entry, sizeof(*entry));
6225627cde5SMatthew Dillon 	entry->inum = inum;
6235627cde5SMatthew Dillon 	entry->next = DirHash[hv];
6245627cde5SMatthew Dillon 	entry->filename = malloc(flen + 1);
6255627cde5SMatthew Dillon 	entry->len = flen;
6265627cde5SMatthew Dillon 	entry->bref_off = loff;
6275627cde5SMatthew Dillon 	bcopy(filename, entry->filename, flen);
6285627cde5SMatthew Dillon 	entry->filename[flen] = 0;
6295627cde5SMatthew Dillon 	DirHash[hv] = entry;
6305627cde5SMatthew Dillon }
6315627cde5SMatthew Dillon #endif
6325627cde5SMatthew Dillon 
633b601e4dfSMatthew Dillon /*
634b601e4dfSMatthew Dillon  * Topology duplicate scan avoidance helpers.  We associate inodes and
635b601e4dfSMatthew Dillon  * indirect block data offsets, allowing us to avoid re-scanning any
636b601e4dfSMatthew Dillon  * duplicates that we see.  And there will be many due to how the COW
637b601e4dfSMatthew Dillon  * process occurs.
638b601e4dfSMatthew Dillon  *
639b601e4dfSMatthew Dillon  * For example, when a large directory is modified the content update to
640b601e4dfSMatthew Dillon  * the directory entries will cause the directory inode to be COWd, along
641b601e4dfSMatthew Dillon  * with whatever is holding the bref(s) blocks that have undergone
642b601e4dfSMatthew Dillon  * adjustment.  More likely than not, there will be MANY shared indirect
643b601e4dfSMatthew Dillon  * blocks.
644b601e4dfSMatthew Dillon  */
6455627cde5SMatthew Dillon static topology_entry_t *
enter_topology(const char * path)6465627cde5SMatthew Dillon enter_topology(const char *path)
6475627cde5SMatthew Dillon {
6485627cde5SMatthew Dillon 	topology_entry_t *topo;
6495627cde5SMatthew Dillon 	uint32_t hv = 0;
6505627cde5SMatthew Dillon 	size_t i;
6515627cde5SMatthew Dillon 
6525627cde5SMatthew Dillon 	for (i = 0; path[i]; ++i)
6535627cde5SMatthew Dillon 		hv = (hv << 5) ^ path[i] ^ (hv >> 24);
654*eefc659dSMatthew Dillon 	hv = (hv ^ (hv >> 16)) & HTABLE_MASK;
6555627cde5SMatthew Dillon 	for (topo = TopologyHash[hv]; topo; topo = topo->next) {
6565627cde5SMatthew Dillon 		if (strcmp(path, topo->path) == 0)
6575627cde5SMatthew Dillon 			return topo;
6585627cde5SMatthew Dillon 	}
6595627cde5SMatthew Dillon 	topo = malloc(sizeof(*topo));
6605627cde5SMatthew Dillon 	bzero(topo, sizeof(*topo));
6615627cde5SMatthew Dillon 
6625627cde5SMatthew Dillon 	topo->next = TopologyHash[hv];
6635627cde5SMatthew Dillon 	TopologyHash[hv] = topo;
6645627cde5SMatthew Dillon 	topo->path = strdup(path);
6655627cde5SMatthew Dillon 	topo->iterator = 1;
6665627cde5SMatthew Dillon 
6675627cde5SMatthew Dillon 	return topo;
6685627cde5SMatthew Dillon }
6695627cde5SMatthew Dillon 
670f26c2b1fSMatthew Dillon /*
671f26c2b1fSMatthew Dillon  * Determine if an inode at the current topology location is one that we
672f26c2b1fSMatthew Dillon  * have already dealt with.
673f26c2b1fSMatthew Dillon  */
6745627cde5SMatthew Dillon static int
topology_check_duplicate_inode(topology_entry_t * topo,inode_entry_t * iscan)675*eefc659dSMatthew Dillon topology_check_duplicate_inode(topology_entry_t *topo, inode_entry_t *iscan)
6765627cde5SMatthew Dillon {
677*eefc659dSMatthew Dillon 	int hv = (((intptr_t)topo ^ (intptr_t)iscan) >> 6) & HTABLE_MASK;
678f26c2b1fSMatthew Dillon 	topo_inode_entry_t *scan;
6795627cde5SMatthew Dillon 
680f26c2b1fSMatthew Dillon 	for (scan = TopoInodeHash[hv]; scan; scan = scan->next) {
681f26c2b1fSMatthew Dillon 		if (scan->topo == topo &&
682*eefc659dSMatthew Dillon 		    scan->iscan == iscan)
683f26c2b1fSMatthew Dillon 		{
684f26c2b1fSMatthew Dillon 			++TopoInodeDupCount;
6855627cde5SMatthew Dillon 			return 1;
6865627cde5SMatthew Dillon 		}
687f26c2b1fSMatthew Dillon 	}
6885627cde5SMatthew Dillon 	scan = malloc(sizeof(*scan));
6895627cde5SMatthew Dillon 	bzero(scan, sizeof(*scan));
690*eefc659dSMatthew Dillon 	scan->iscan = iscan;
691f26c2b1fSMatthew Dillon 	scan->topo = topo;
692f26c2b1fSMatthew Dillon 	scan->next = TopoInodeHash[hv];
693f26c2b1fSMatthew Dillon 	TopoInodeHash[hv] = scan;
694f26c2b1fSMatthew Dillon 	++TopoInodeCount;
6955627cde5SMatthew Dillon 
6965627cde5SMatthew Dillon 	return 0;
6975627cde5SMatthew Dillon }
6985627cde5SMatthew Dillon 
699f26c2b1fSMatthew Dillon /*
700f26c2b1fSMatthew Dillon  * Determine if an indirect block (represented by the bref) at the current
701f26c2b1fSMatthew Dillon  * topology level is one that we have already dealt with.
702f26c2b1fSMatthew Dillon  */
703b601e4dfSMatthew Dillon static int
topology_check_duplicate_indirect(topology_entry_t * topo,hammer2_blockref_t * bref)704b601e4dfSMatthew Dillon topology_check_duplicate_indirect(topology_entry_t *topo,
705b601e4dfSMatthew Dillon 				  hammer2_blockref_t *bref)
706b601e4dfSMatthew Dillon {
707*eefc659dSMatthew Dillon 	int hv = ((intptr_t)topo ^ (bref->data_off >> 8)) & HTABLE_MASK;
708f26c2b1fSMatthew Dillon 	topo_bref_entry_t *scan;
709b601e4dfSMatthew Dillon 
710f26c2b1fSMatthew Dillon 	for (scan = TopoBRefHash[hv]; scan; scan = scan->next) {
711f26c2b1fSMatthew Dillon 		if (scan->topo == topo &&
712f26c2b1fSMatthew Dillon 		    scan->data_off == bref->data_off)
713f26c2b1fSMatthew Dillon 		{
714f26c2b1fSMatthew Dillon 			++TopoBRefDupCount;
715b601e4dfSMatthew Dillon 			return 1;
716b601e4dfSMatthew Dillon 		}
717b601e4dfSMatthew Dillon 	}
718b601e4dfSMatthew Dillon 	scan = malloc(sizeof(*scan));
719b601e4dfSMatthew Dillon 	bzero(scan, sizeof(*scan));
720b601e4dfSMatthew Dillon 	scan->data_off = bref->data_off;
721f26c2b1fSMatthew Dillon 	scan->topo = topo;
722f26c2b1fSMatthew Dillon 	scan->next = TopoBRefHash[hv];
723f26c2b1fSMatthew Dillon 	TopoBRefHash[hv] = scan;
724f26c2b1fSMatthew Dillon 	++TopoBRefCount;
725b601e4dfSMatthew Dillon 
726b601e4dfSMatthew Dillon 	return 0;
727b601e4dfSMatthew Dillon }
728b601e4dfSMatthew Dillon 
7295627cde5SMatthew Dillon /*
730b601e4dfSMatthew Dillon  * Valid and record an inode found on media.  There can be many versions
731b601e4dfSMatthew Dillon  * of the same inode number present on the media.
7325627cde5SMatthew Dillon  */
7335627cde5SMatthew Dillon static void
enter_inode(hammer2_blockref_t * bref)7345627cde5SMatthew Dillon enter_inode(hammer2_blockref_t *bref)
7355627cde5SMatthew Dillon {
736c84b3a36SMatthew Dillon 	uint32_t hv;
737c84b3a36SMatthew Dillon 	uint32_t hv2;
7385627cde5SMatthew Dillon 	inode_entry_t *scan;
739a5eec3d1SMatthew Dillon 	hammer2_inode_data_t *inode;
740*eefc659dSMatthew Dillon 	size_t psize;
7415627cde5SMatthew Dillon 
742*eefc659dSMatthew Dillon 	hv = (bref->key ^ (bref->key >> 16)) & HTABLE_MASK;
743c84b3a36SMatthew Dillon 	hv2 = (bref->key ^ (bref->key >> 16) ^ (bref->data_off >> 10)) &
744*eefc659dSMatthew Dillon 	      HTABLE_MASK;
745c84b3a36SMatthew Dillon 
746ddc249f6SMatthew Dillon 	/*
747c84b3a36SMatthew Dillon 	 * Ignore duplicate inodes, use the secondary inode hash table's
748c84b3a36SMatthew Dillon 	 * better spread to reduce cpu consumption (there can be many
749c84b3a36SMatthew Dillon 	 * copies of the same inode so the primary hash table can have
750c84b3a36SMatthew Dillon 	 * very long chains in it).
751ddc249f6SMatthew Dillon 	 */
752c84b3a36SMatthew Dillon 	for (scan = InodeHash2[hv2]; scan; scan = scan->next2) {
7535627cde5SMatthew Dillon 		if (bref->key == scan->inum &&
7545627cde5SMatthew Dillon 		    bref->data_off == scan->data_off)
7555627cde5SMatthew Dillon 		{
7565627cde5SMatthew Dillon 			return;
7575627cde5SMatthew Dillon 		}
7585627cde5SMatthew Dillon 	}
7595627cde5SMatthew Dillon 
7605627cde5SMatthew Dillon 	/*
761c255364fSMatthew Dillon 	 * Ignore brefs which we have already determined to be bad
762c255364fSMatthew Dillon 	 */
763c255364fSMatthew Dillon 	if (find_neg(bref))
764c255364fSMatthew Dillon 		return;
765c255364fSMatthew Dillon 
766c255364fSMatthew Dillon 	/*
7675627cde5SMatthew Dillon 	 * Validate the potential blockref.  Note that this might not be a
7685627cde5SMatthew Dillon 	 * real blockref.  Don't trust anything, really.
769a5eec3d1SMatthew Dillon 	 *
770a5eec3d1SMatthew Dillon 	 * - Must be sized for an inode block
771a5eec3d1SMatthew Dillon 	 * - Must be properly aligned for an inode block
772a5eec3d1SMatthew Dillon 	 * - Keyspace is 1 (keybits == 0), i.e. a single inode number
7735627cde5SMatthew Dillon 	 */
774a5eec3d1SMatthew Dillon 	if ((1 << (bref->data_off & 0x1F)) != sizeof(*inode))
7755627cde5SMatthew Dillon 		return;
776a5eec3d1SMatthew Dillon 	if ((bref->data_off & ~0x1FL & (sizeof(*inode) - 1)) != 0)
777a5eec3d1SMatthew Dillon 		return;
778a5eec3d1SMatthew Dillon 	if (bref->keybits != 0)
779a5eec3d1SMatthew Dillon 		return;
780a5eec3d1SMatthew Dillon 	if (bref->key == 0)
7815627cde5SMatthew Dillon 		return;
782*eefc659dSMatthew Dillon 
783*eefc659dSMatthew Dillon 	inode = hammer2_cache_read(bref->data_off, &psize);
784*eefc659dSMatthew Dillon 
785*eefc659dSMatthew Dillon 	/*
786*eefc659dSMatthew Dillon 	 * Failure prior to I/O being performed.
787*eefc659dSMatthew Dillon 	 */
788*eefc659dSMatthew Dillon 	if (psize == 0)
7895627cde5SMatthew Dillon 		return;
7905627cde5SMatthew Dillon 
791c255364fSMatthew Dillon 	/*
792c255364fSMatthew Dillon 	 * Any failures which occur after the I/O has been performed
793c255364fSMatthew Dillon 	 * should enter the bref in the negative cache to avoid unnecessary
794c255364fSMatthew Dillon 	 * guaranteed-to-fil reissuances of the same (bref, data_off) combo.
795c255364fSMatthew Dillon 	 */
796c255364fSMatthew Dillon 	if (inode == NULL) {
797c255364fSMatthew Dillon fail:
798c255364fSMatthew Dillon 		enter_neg(bref);
7995627cde5SMatthew Dillon 		return;
800c255364fSMatthew Dillon 	}
8015627cde5SMatthew Dillon 
802ca5510acSMatthew Dillon 	/*
803ca5510acSMatthew Dillon 	 * The blockref looks ok but the real test is whether the
804ca5510acSMatthew Dillon 	 * inode data it references passes the CRC check.  If it
805ca5510acSMatthew Dillon 	 * does, it is highly likely that we have a valid inode.
806ca5510acSMatthew Dillon 	 */
807a5eec3d1SMatthew Dillon 	if (validate_crc(bref, inode, sizeof(*inode)) == 0)
808c255364fSMatthew Dillon 		goto fail;
809a5eec3d1SMatthew Dillon 	if (inode->meta.inum != bref->key)
810c255364fSMatthew Dillon 		goto fail;
8115627cde5SMatthew Dillon 
812*eefc659dSMatthew Dillon 	/*
813*eefc659dSMatthew Dillon 	 * Record the inode.  For now we do not record the actual content
814*eefc659dSMatthew Dillon 	 * of the inode because if there are more than few million of them
815*eefc659dSMatthew Dillon 	 * the memory consumption can get into the dozens of gigabytes.
816*eefc659dSMatthew Dillon 	 *
817*eefc659dSMatthew Dillon 	 * Instead, the inode will be re-read from media in the recovery
818*eefc659dSMatthew Dillon 	 * pass.
819*eefc659dSMatthew Dillon 	 */
8205627cde5SMatthew Dillon 	scan = malloc(sizeof(*scan));
8215627cde5SMatthew Dillon 	bzero(scan, sizeof(*scan));
8225627cde5SMatthew Dillon 
8235627cde5SMatthew Dillon 	scan->inum = bref->key;
824*eefc659dSMatthew Dillon 	scan->type = inode->meta.type;
8255627cde5SMatthew Dillon 	scan->data_off = bref->data_off;
826*eefc659dSMatthew Dillon 	scan->inode_crc = hammer2_icrc32(inode, sizeof(*inode));
827*eefc659dSMatthew Dillon 	//scan->inode = *inode;		/* removed, too expensive */
828c84b3a36SMatthew Dillon 
8295627cde5SMatthew Dillon 	scan->next = InodeHash[hv];
8305627cde5SMatthew Dillon 	InodeHash[hv] = scan;
831c84b3a36SMatthew Dillon 	scan->next2 = InodeHash2[hv2];
832c84b3a36SMatthew Dillon 	InodeHash2[hv2] = scan;
8335627cde5SMatthew Dillon 
8345627cde5SMatthew Dillon 	++InodeCount;
835ca5510acSMatthew Dillon }
836ca5510acSMatthew Dillon 
837b601e4dfSMatthew Dillon /*
838b601e4dfSMatthew Dillon  * This is used to enter possible root inodes.  Root inodes typically hang
839b601e4dfSMatthew Dillon  * off the volume root and thus there might not be a bref reference to the
840b601e4dfSMatthew Dillon  * many old copies of root inodes sitting around on the media.  Without a
841b601e4dfSMatthew Dillon  * bref we can't really validate that the content is ok.  But we need
842b601e4dfSMatthew Dillon  * these inodes as part of our path searches.
843b601e4dfSMatthew Dillon  */
8445627cde5SMatthew Dillon static void
enter_inode_untested(hammer2_inode_data_t * ip,hammer2_off_t loff)8455627cde5SMatthew Dillon enter_inode_untested(hammer2_inode_data_t *ip, hammer2_off_t loff)
8465627cde5SMatthew Dillon {
8476f05fbf6SMatthew Dillon 	uint32_t hv;
8486f05fbf6SMatthew Dillon 	uint32_t hv2;
8495627cde5SMatthew Dillon 	inode_entry_t *scan;
8505627cde5SMatthew Dillon 
851*eefc659dSMatthew Dillon 	hv = (ip->meta.inum ^ (ip->meta.inum >> 16)) & HTABLE_MASK;
8526f05fbf6SMatthew Dillon 	hv2 = (ip->meta.inum ^ (ip->meta.inum >> 16) ^ (loff >> 10)) &
853*eefc659dSMatthew Dillon 	      HTABLE_MASK;
8546f05fbf6SMatthew Dillon 
8556f05fbf6SMatthew Dillon 	for (scan = InodeHash2[hv2]; scan; scan = scan->next2) {
8565627cde5SMatthew Dillon 		if (ip->meta.inum == scan->inum &&
8575627cde5SMatthew Dillon 		    loff == scan->data_off)
8585627cde5SMatthew Dillon 		{
8595627cde5SMatthew Dillon 			return;
8605627cde5SMatthew Dillon 		}
8615627cde5SMatthew Dillon 	}
8625627cde5SMatthew Dillon 
863*eefc659dSMatthew Dillon 	/*
864*eefc659dSMatthew Dillon 	 * Record the inode.  For now we do not record the actual content
865*eefc659dSMatthew Dillon 	 * of the inode because if there are more than few million of them
866*eefc659dSMatthew Dillon 	 * the memory consumption can get into the dozens of gigabytes.
867*eefc659dSMatthew Dillon 	 *
868*eefc659dSMatthew Dillon 	 * Instead, the inode will be re-read from media in the recovery
869*eefc659dSMatthew Dillon 	 * pass.
870*eefc659dSMatthew Dillon 	 */
8715627cde5SMatthew Dillon 	scan = malloc(sizeof(*scan));
8725627cde5SMatthew Dillon 	bzero(scan, sizeof(*scan));
8735627cde5SMatthew Dillon 
8745627cde5SMatthew Dillon 	scan->inum = ip->meta.inum;
875*eefc659dSMatthew Dillon 	scan->type = ip->meta.type;
8765627cde5SMatthew Dillon 	scan->data_off = loff;
877*eefc659dSMatthew Dillon 	scan->inode_crc = hammer2_icrc32(ip, sizeof(*ip));
878*eefc659dSMatthew Dillon 	//scan->inode = *ip;		/* removed, too expensive */
8796f05fbf6SMatthew Dillon 
8805627cde5SMatthew Dillon 	scan->next = InodeHash[hv];
8815627cde5SMatthew Dillon 	InodeHash[hv] = scan;
8826f05fbf6SMatthew Dillon 	scan->next2 = InodeHash2[hv2];
8836f05fbf6SMatthew Dillon 	InodeHash2[hv2] = scan;
8845627cde5SMatthew Dillon 
8855627cde5SMatthew Dillon 	++InodeCount;
8865627cde5SMatthew Dillon }
8875627cde5SMatthew Dillon 
8885627cde5SMatthew Dillon static inode_entry_t *
find_first_inode(hammer2_key_t inum)8895627cde5SMatthew Dillon find_first_inode(hammer2_key_t inum)
8905627cde5SMatthew Dillon {
8915627cde5SMatthew Dillon 	inode_entry_t *entry;
892*eefc659dSMatthew Dillon 	uint32_t hv = (inum ^ (inum >> 16)) & HTABLE_MASK;
8935627cde5SMatthew Dillon 
8945627cde5SMatthew Dillon 	for (entry = InodeHash[hv]; entry; entry = entry->next) {
8955627cde5SMatthew Dillon 		if (entry->inum == inum)
8965627cde5SMatthew Dillon 			return entry;
8975627cde5SMatthew Dillon 	}
8985627cde5SMatthew Dillon 	return NULL;
8995627cde5SMatthew Dillon }
900ca5510acSMatthew Dillon 
901ca5510acSMatthew Dillon /*
902c255364fSMatthew Dillon  * Negative bref cache.  A cache of brefs that we have determined
903f26c2b1fSMatthew Dillon  * to be invalid.  Used to reduce unnecessary disk I/O.
904c255364fSMatthew Dillon  *
905c255364fSMatthew Dillon  * NOTE: Checks must be reasonable and at least encompass checks
906c255364fSMatthew Dillon  *	 done in enter_inode() after it has decided to read the
907c255364fSMatthew Dillon  *	 block at data_off.
908c255364fSMatthew Dillon  *
909c255364fSMatthew Dillon  *	 Adding a few more match fields in addition won't hurt either.
910c255364fSMatthew Dillon  */
911c255364fSMatthew Dillon static int
find_neg(hammer2_blockref_t * bref)912c255364fSMatthew Dillon find_neg(hammer2_blockref_t *bref)
913c255364fSMatthew Dillon {
914*eefc659dSMatthew Dillon 	int hv = (bref->data_off >> 10) & HTABLE_MASK;
915c255364fSMatthew Dillon 	neg_entry_t *neg;
916c255364fSMatthew Dillon 
917c255364fSMatthew Dillon 	for (neg = NegativeHash[hv]; neg; neg = neg->next) {
918c255364fSMatthew Dillon 		if (bref->data_off == neg->bref.data_off &&
919c255364fSMatthew Dillon 		    bref->type == neg->bref.type &&
920c255364fSMatthew Dillon 		    bref->methods == neg->bref.methods &&
921c255364fSMatthew Dillon 		    bref->key == neg->bref.key &&
922c255364fSMatthew Dillon 		    bcmp(&bref->check, &neg->bref.check,
923c255364fSMatthew Dillon 			 sizeof(bref->check)) == 0)
924c255364fSMatthew Dillon 		{
925c255364fSMatthew Dillon 			++NegativeHits;
926c255364fSMatthew Dillon 			return 1;
927c255364fSMatthew Dillon 		}
928c255364fSMatthew Dillon 	}
929c255364fSMatthew Dillon 	return 0;
930c255364fSMatthew Dillon }
931c255364fSMatthew Dillon 
932c255364fSMatthew Dillon static void
enter_neg(hammer2_blockref_t * bref)933c255364fSMatthew Dillon enter_neg(hammer2_blockref_t *bref)
934c255364fSMatthew Dillon {
935*eefc659dSMatthew Dillon 	int hv = (bref->data_off >> 10) & HTABLE_MASK;
936c255364fSMatthew Dillon 	neg_entry_t *neg;
937c255364fSMatthew Dillon 
938c255364fSMatthew Dillon 	neg = malloc(sizeof(*neg));
939c255364fSMatthew Dillon 	bzero(neg, sizeof(*neg));
940c255364fSMatthew Dillon 	neg->next = NegativeHash[hv];
941c255364fSMatthew Dillon 	neg->bref = *bref;
942c255364fSMatthew Dillon 	NegativeHash[hv] = neg;
943c255364fSMatthew Dillon 	++NegativeCount;
944c255364fSMatthew Dillon }
945c255364fSMatthew Dillon 
946c255364fSMatthew Dillon /*
947b601e4dfSMatthew Dillon  * Dump the specified inode (file or directory)
948b601e4dfSMatthew Dillon  *
949b601e4dfSMatthew Dillon  * This function recurses via dump_dir_data().
9505627cde5SMatthew Dillon  */
9515627cde5SMatthew Dillon static void
dump_tree(inode_entry_t * iscan,const char * dest,const char * remain,int depth,int path_depth,int isafile)952ddc249f6SMatthew Dillon dump_tree(inode_entry_t *iscan, const char *dest, const char *remain,
953b601e4dfSMatthew Dillon 	  int depth, int path_depth, int isafile)
9545627cde5SMatthew Dillon {
9555627cde5SMatthew Dillon 	topology_entry_t *topo;
956*eefc659dSMatthew Dillon 	hammer2_inode_data_t *inode;
957*eefc659dSMatthew Dillon 	hammer2_inode_data_t inode_copy;
9585627cde5SMatthew Dillon 	struct stat st;
959*eefc659dSMatthew Dillon 	size_t psize;
9605627cde5SMatthew Dillon 	char *path;
9615627cde5SMatthew Dillon 
9625627cde5SMatthew Dillon 	/*
963*eefc659dSMatthew Dillon 	 * Re-read the already-validated inode instead of saving it in
964*eefc659dSMatthew Dillon 	 * memory from the media pass.  Even though we already validated
965*eefc659dSMatthew Dillon 	 * it, the content may have changed if scanning live media, so
966*eefc659dSMatthew Dillon 	 * check against a simple crc we recorded earlier.
967b601e4dfSMatthew Dillon 	 */
968*eefc659dSMatthew Dillon 	inode = hammer2_cache_read(iscan->data_off, &psize);
969*eefc659dSMatthew Dillon 	if (psize == 0)
970*eefc659dSMatthew Dillon 		return;
971*eefc659dSMatthew Dillon 	if (inode == NULL || psize != sizeof(*inode))
972*eefc659dSMatthew Dillon 		return;
973*eefc659dSMatthew Dillon 	if (iscan->inode_crc != hammer2_icrc32(inode, sizeof(*inode)))
974*eefc659dSMatthew Dillon 		return;
975*eefc659dSMatthew Dillon 	inode_copy = *inode;
976*eefc659dSMatthew Dillon 	inode = &inode_copy;
977b601e4dfSMatthew Dillon 
978b601e4dfSMatthew Dillon 	/*
9795627cde5SMatthew Dillon 	 * Try to limit potential infinite loops
9805627cde5SMatthew Dillon 	 */
981*eefc659dSMatthew Dillon 	if (depth > REPINODEDEPTH && iscan->encountered)
9825627cde5SMatthew Dillon 		return;
9835627cde5SMatthew Dillon 
9845627cde5SMatthew Dillon 	/*
9855627cde5SMatthew Dillon 	 * Get rid of any dividing slashes
9865627cde5SMatthew Dillon 	 */
9875627cde5SMatthew Dillon 	while (*remain == '/')
9885627cde5SMatthew Dillon 		++remain;
9895627cde5SMatthew Dillon 
9905627cde5SMatthew Dillon 	/*
9915627cde5SMatthew Dillon 	 * Create/lookup destination path (without the iterator), to acquire
9925627cde5SMatthew Dillon 	 * an iterator for different versions of the same file.
9935627cde5SMatthew Dillon 	 *
9945627cde5SMatthew Dillon 	 * Due to the COW mechanism, a lot of repeated snapshot-like
9955627cde5SMatthew Dillon 	 * directories may be encountered so we use the topology tree
9965627cde5SMatthew Dillon 	 * to weed out duplicates that attempt to use the same pathname.
9975627cde5SMatthew Dillon 	 *
9985627cde5SMatthew Dillon 	 * We also don't iterate copies of directories since this would
9995627cde5SMatthew Dillon 	 * create a major mess due to the many versions that might be
10005627cde5SMatthew Dillon 	 * laying around.  Directories use unextended names.
10015627cde5SMatthew Dillon 	 */
10025627cde5SMatthew Dillon 	topo = enter_topology(dest);
1003b601e4dfSMatthew Dillon 	if (topology_check_duplicate_inode(topo, iscan))
10045627cde5SMatthew Dillon 		return;
10055627cde5SMatthew Dillon 
1006*eefc659dSMatthew Dillon 	switch(iscan->type) {
10075627cde5SMatthew Dillon 	case HAMMER2_OBJTYPE_DIRECTORY:
10085627cde5SMatthew Dillon 		/*
1009ddc249f6SMatthew Dillon 		 * If we have exhausted the path and isafile is TRUE,
1010ddc249f6SMatthew Dillon 		 * stop.
1011ddc249f6SMatthew Dillon 		 */
1012ddc249f6SMatthew Dillon 		if (isafile && *remain == 0)
1013ddc249f6SMatthew Dillon 			return;
1014ddc249f6SMatthew Dillon 
1015ddc249f6SMatthew Dillon 		/*
10165627cde5SMatthew Dillon 		 * Create / make usable the target directory.  Note that
10175627cde5SMatthew Dillon 		 * it might already exist.
1018d5a9a602SMatthew Dillon 		 *
1019d5a9a602SMatthew Dillon 		 * Do not do this for the destination base directory
1020d5a9a602SMatthew Dillon 		 * (depth 1).
10215627cde5SMatthew Dillon 		 */
1022d5a9a602SMatthew Dillon 		if (depth != 1) {
10235627cde5SMatthew Dillon 			if (mkdir(dest, 0755) == 0)
1024*eefc659dSMatthew Dillon 				iscan->encountered = 1;
10255627cde5SMatthew Dillon 			if (stat(dest, &st) == 0) {
10265627cde5SMatthew Dillon 				if (st.st_flags)
10275627cde5SMatthew Dillon 					chflags(dest, 0);
10285627cde5SMatthew Dillon 				if ((st.st_mode & 0700) != 0700)
10295627cde5SMatthew Dillon 					chmod(dest, 0755);
10305627cde5SMatthew Dillon 			}
1031d5a9a602SMatthew Dillon 		}
10325627cde5SMatthew Dillon 
10335627cde5SMatthew Dillon 		/*
10345627cde5SMatthew Dillon 		 * Dump directory contents (scan the directory)
10355627cde5SMatthew Dillon 		 */
10365627cde5SMatthew Dillon 		(void)dump_dir_data(dest, remain,
1037*eefc659dSMatthew Dillon 				    &inode->u.blockset.blockref[0],
1038b601e4dfSMatthew Dillon 				    HAMMER2_SET_COUNT, depth, path_depth + 1,
1039b601e4dfSMatthew Dillon 				    isafile);
10405627cde5SMatthew Dillon 
10415627cde5SMatthew Dillon 		/*
10425627cde5SMatthew Dillon 		 * Final adjustment to directory inode
10435627cde5SMatthew Dillon 		 */
1044d5a9a602SMatthew Dillon 		if (depth != 1) {
10455627cde5SMatthew Dillon 			struct timeval tvs[2];
10465627cde5SMatthew Dillon 
1047*eefc659dSMatthew Dillon 			tvs[0].tv_sec = inode->meta.atime / 1000000;
1048*eefc659dSMatthew Dillon 			tvs[0].tv_usec = inode->meta.atime % 1000000;
1049*eefc659dSMatthew Dillon 			tvs[1].tv_sec = inode->meta.mtime / 1000000;
1050*eefc659dSMatthew Dillon 			tvs[1].tv_usec = inode->meta.mtime % 1000000;
10515627cde5SMatthew Dillon 
10525627cde5SMatthew Dillon 			if (lutimes(dest, tvs) < 0)
10535627cde5SMatthew Dillon 				perror("futimes");
10545627cde5SMatthew Dillon 			lchown(dest,
1055*eefc659dSMatthew Dillon 			       hammer2_to_unix_xid(&inode->meta.uid),
1056*eefc659dSMatthew Dillon 			       hammer2_to_unix_xid(&inode->meta.gid));
10575627cde5SMatthew Dillon 
1058*eefc659dSMatthew Dillon 			lchmod(dest, inode->meta.mode);
1059*eefc659dSMatthew Dillon 			lchflags(dest, inode->meta.uflags);
10605627cde5SMatthew Dillon 		}
10615627cde5SMatthew Dillon 		break;
10625627cde5SMatthew Dillon 	case HAMMER2_OBJTYPE_REGFILE:
10635627cde5SMatthew Dillon 		/*
10645627cde5SMatthew Dillon 		 * If no more path to match, dump the file contents
10655627cde5SMatthew Dillon 		 */
10665627cde5SMatthew Dillon 		if (*remain == 0) {
10675627cde5SMatthew Dillon 			asprintf(&path, "%s.%05ld", dest, topo->iterator);
10685627cde5SMatthew Dillon 			++topo->iterator;
10695627cde5SMatthew Dillon 
10705627cde5SMatthew Dillon 			if (stat(path, &st) == 0) {
10715627cde5SMatthew Dillon 				if (st.st_flags)
10725627cde5SMatthew Dillon 					chflags(path, 0);
10735627cde5SMatthew Dillon 				if ((st.st_mode & 0600) != 0600)
10745627cde5SMatthew Dillon 					chmod(path, 0644);
10755627cde5SMatthew Dillon 			}
1076*eefc659dSMatthew Dillon 			iscan->encountered = 1;
1077*eefc659dSMatthew Dillon 			(void)dump_inum_file(iscan, inode, path);
10785627cde5SMatthew Dillon 			free(path);
10795627cde5SMatthew Dillon 		}
10805627cde5SMatthew Dillon 		break;
10815627cde5SMatthew Dillon 	case HAMMER2_OBJTYPE_SOFTLINK:
10825627cde5SMatthew Dillon 		/*
10835627cde5SMatthew Dillon 		 * If no more path to match, dump the file contents
10845627cde5SMatthew Dillon 		 */
10855627cde5SMatthew Dillon 		if (*remain == 0) {
10865627cde5SMatthew Dillon 			asprintf(&path, "%s.%05ld", dest, topo->iterator);
10875627cde5SMatthew Dillon 			++topo->iterator;
10885627cde5SMatthew Dillon 
10895627cde5SMatthew Dillon 			if (stat(path, &st) == 0) {
10905627cde5SMatthew Dillon 				if (st.st_flags)
10915627cde5SMatthew Dillon 					chflags(path, 0);
10925627cde5SMatthew Dillon 				if ((st.st_mode & 0600) != 0600)
10935627cde5SMatthew Dillon 					chmod(path, 0644);
10945627cde5SMatthew Dillon 			}
1095*eefc659dSMatthew Dillon 			(void)dump_inum_softlink(inode, path);
10965627cde5SMatthew Dillon 			free(path);
10975627cde5SMatthew Dillon 		}
10985627cde5SMatthew Dillon 		break;
10995627cde5SMatthew Dillon 	}
11005627cde5SMatthew Dillon }
11015627cde5SMatthew Dillon 
11025627cde5SMatthew Dillon /*
11035627cde5SMatthew Dillon  * [re]create a regular file and attempt to restore the originanl perms,
11045627cde5SMatthew Dillon  * modes, flags, times, uid, and gid if successful.
1105ca5510acSMatthew Dillon  *
1106ca5510acSMatthew Dillon  * If the data block recursion fails the file will be renamed
11075627cde5SMatthew Dillon  * .corrupted.
11085627cde5SMatthew Dillon  */
11095627cde5SMatthew Dillon static int
dump_inum_file(inode_entry_t * iscan,hammer2_inode_data_t * inode,const char * path1)1110*eefc659dSMatthew Dillon dump_inum_file(inode_entry_t *iscan, hammer2_inode_data_t *inode,
1111*eefc659dSMatthew Dillon 	       const char *path1)
11125627cde5SMatthew Dillon {
11135627cde5SMatthew Dillon 	char *path2;
11145627cde5SMatthew Dillon 	int wfd;
11155627cde5SMatthew Dillon 	int res;
11165627cde5SMatthew Dillon 
11175627cde5SMatthew Dillon 	/*
11185627cde5SMatthew Dillon 	 * If this specific inode has already been generated, try to
11195627cde5SMatthew Dillon 	 * hardlink it instead of regenerating the same file again.
11205627cde5SMatthew Dillon 	 */
11215627cde5SMatthew Dillon 	if (iscan->link_file_path) {
11225627cde5SMatthew Dillon 		if (link(iscan->link_file_path, path1) == 0)
11235627cde5SMatthew Dillon 			return 1;
11245627cde5SMatthew Dillon 		chflags(iscan->link_file_path, 0);
11255627cde5SMatthew Dillon 		chmod(iscan->link_file_path, 0600);
11265627cde5SMatthew Dillon 		if (link(iscan->link_file_path, path1) == 0) {
11275627cde5SMatthew Dillon 			chmod(iscan->link_file_path, inode->meta.mode);
11285627cde5SMatthew Dillon 			chflags(iscan->link_file_path, inode->meta.uflags);
11295627cde5SMatthew Dillon 			return 1;
11305627cde5SMatthew Dillon 		}
11315627cde5SMatthew Dillon 	}
11325627cde5SMatthew Dillon 
11335627cde5SMatthew Dillon 	/*
11345627cde5SMatthew Dillon 	 * Cleanup potential flags and modes to allow us to write out a
11355627cde5SMatthew Dillon 	 * new file.
1136ca5510acSMatthew Dillon 	 */
1137ca5510acSMatthew Dillon 	chflags(path1, 0);
1138ca5510acSMatthew Dillon 	chmod(path1, 0600);
1139ca5510acSMatthew Dillon 	wfd = open(path1, O_RDWR|O_CREAT|O_TRUNC, 0600);
1140ca5510acSMatthew Dillon 
11415627cde5SMatthew Dillon 	if (inode->meta.op_flags & HAMMER2_OPFLAG_DIRECTDATA) {
1142ca5510acSMatthew Dillon 		/*
1143ca5510acSMatthew Dillon 		 * Direct data case
1144ca5510acSMatthew Dillon 		 */
11455627cde5SMatthew Dillon 		if (inode->meta.size > 0 &&
11465627cde5SMatthew Dillon 		    inode->meta.size <= sizeof(inode->u.data))
1147ca5510acSMatthew Dillon 		{
11485627cde5SMatthew Dillon 			write(wfd, inode->u.data, inode->meta.size);
1149ca5510acSMatthew Dillon 		}
1150ca5510acSMatthew Dillon 		res = 1;
1151ca5510acSMatthew Dillon 	} else {
1152ca5510acSMatthew Dillon 		/*
1153ca5510acSMatthew Dillon 		 * file content, indirect blockrefs
1154ca5510acSMatthew Dillon 		 */
11555627cde5SMatthew Dillon 		res = dump_file_data(wfd, inode->meta.size,
11565627cde5SMatthew Dillon 				     &inode->u.blockset.blockref[0],
1157ca5510acSMatthew Dillon 				     HAMMER2_SET_COUNT);
1158ca5510acSMatthew Dillon 	}
1159ca5510acSMatthew Dillon 
1160ca5510acSMatthew Dillon 	/*
1161ca5510acSMatthew Dillon 	 * On success, set perms, mtime, flags, etc
1162ca5510acSMatthew Dillon 	 * On failure, rename file to .corrupted
1163ca5510acSMatthew Dillon 	 */
1164ca5510acSMatthew Dillon 	if (res) {
1165ca5510acSMatthew Dillon 		struct timeval tvs[2];
1166ca5510acSMatthew Dillon 
11675627cde5SMatthew Dillon 		tvs[0].tv_sec = inode->meta.atime / 1000000;
11685627cde5SMatthew Dillon 		tvs[0].tv_usec = inode->meta.atime % 1000000;
11695627cde5SMatthew Dillon 		tvs[1].tv_sec = inode->meta.mtime / 1000000;
11705627cde5SMatthew Dillon 		tvs[1].tv_usec = inode->meta.mtime % 1000000;
1171ca5510acSMatthew Dillon 
11725627cde5SMatthew Dillon 		ftruncate(wfd, inode->meta.size);
1173ca5510acSMatthew Dillon 		if (futimes(wfd, tvs) < 0)
1174ca5510acSMatthew Dillon 			perror("futimes");
1175ca5510acSMatthew Dillon 		fchown(wfd,
11765627cde5SMatthew Dillon 		       hammer2_to_unix_xid(&inode->meta.uid),
11775627cde5SMatthew Dillon 		       hammer2_to_unix_xid(&inode->meta.gid));
1178ca5510acSMatthew Dillon 
11795627cde5SMatthew Dillon 		fchmod(wfd, inode->meta.mode);
11805627cde5SMatthew Dillon 		fchflags(wfd, inode->meta.uflags);
11815627cde5SMatthew Dillon 		iscan->link_file_path = strdup(path1);
1182ca5510acSMatthew Dillon 	} else {
1183ca5510acSMatthew Dillon 		struct timeval tvs[2];
1184ca5510acSMatthew Dillon 
11855627cde5SMatthew Dillon 		tvs[0].tv_sec = inode->meta.atime / 1000000;
11865627cde5SMatthew Dillon 		tvs[0].tv_usec = inode->meta.atime % 1000000;
11875627cde5SMatthew Dillon 		tvs[1].tv_sec = inode->meta.mtime / 1000000;
11885627cde5SMatthew Dillon 		tvs[1].tv_usec = inode->meta.mtime % 1000000;
1189ca5510acSMatthew Dillon 
11905627cde5SMatthew Dillon 		ftruncate(wfd, inode->meta.size);
1191ca5510acSMatthew Dillon 		if (futimes(wfd, tvs) < 0)
1192ca5510acSMatthew Dillon 			perror("futimes");
1193ca5510acSMatthew Dillon 		fchown(wfd,
11945627cde5SMatthew Dillon 		       hammer2_to_unix_xid(&inode->meta.uid),
11955627cde5SMatthew Dillon 		       hammer2_to_unix_xid(&inode->meta.gid));
1196ca5510acSMatthew Dillon 
1197ca5510acSMatthew Dillon 		asprintf(&path2, "%s.corrupted", path1);
1198ca5510acSMatthew Dillon 		rename(path1, path2);
11995627cde5SMatthew Dillon 		iscan->link_file_path = path2;
1200ca5510acSMatthew Dillon 	}
1201ca5510acSMatthew Dillon 
1202ca5510acSMatthew Dillon 	/*
1203ca5510acSMatthew Dillon 	 * Cleanup
1204ca5510acSMatthew Dillon 	 */
12055627cde5SMatthew Dillon 	close(wfd);
1206ca5510acSMatthew Dillon 
1207ca5510acSMatthew Dillon 	return res;
1208ca5510acSMatthew Dillon }
1209ca5510acSMatthew Dillon 
1210b601e4dfSMatthew Dillon /*
1211b601e4dfSMatthew Dillon  * TODO XXX
1212b601e4dfSMatthew Dillon  */
1213ca5510acSMatthew Dillon static int
dump_inum_softlink(hammer2_inode_data_t * inode __unused,const char * path __unused)12145627cde5SMatthew Dillon dump_inum_softlink(hammer2_inode_data_t *inode __unused,
12155627cde5SMatthew Dillon 		   const char *path __unused)
12165627cde5SMatthew Dillon {
12175627cde5SMatthew Dillon 	return 0;
12185627cde5SMatthew Dillon }
12195627cde5SMatthew Dillon 
12205627cde5SMatthew Dillon /*
12215627cde5SMatthew Dillon  * Scan the directory for a match against the next component in
12225627cde5SMatthew Dillon  * the (remain) path.
1223b601e4dfSMatthew Dillon  *
1224b601e4dfSMatthew Dillon  * This function is part of the dump_tree() recursion mechanism.
12255627cde5SMatthew Dillon  */
12265627cde5SMatthew Dillon static int
dump_dir_data(const char * dest,const char * remain,hammer2_blockref_t * base,int count,int depth,int path_depth,int isafile)12275627cde5SMatthew Dillon dump_dir_data(const char *dest, const char *remain,
1228ddc249f6SMatthew Dillon 	      hammer2_blockref_t *base, int count,
1229b601e4dfSMatthew Dillon 	      int depth, int path_depth, int isafile)
12305627cde5SMatthew Dillon {
12315627cde5SMatthew Dillon 	hammer2_media_data_t data;
12325627cde5SMatthew Dillon 	hammer2_volume_t *vol;
12335627cde5SMatthew Dillon 	hammer2_off_t poff;
1234*eefc659dSMatthew Dillon 	size_t psize;
12355627cde5SMatthew Dillon 	int res = 1;
12365627cde5SMatthew Dillon 	int rtmp;
12375627cde5SMatthew Dillon 	int n;
12385627cde5SMatthew Dillon 	size_t flen;
12395627cde5SMatthew Dillon 
12405627cde5SMatthew Dillon 	/*
12415627cde5SMatthew Dillon 	 * Calculate length of next path component to match against.
12425627cde5SMatthew Dillon 	 * A length of zero matches against the entire remaining
12435627cde5SMatthew Dillon 	 * directory sub-tree.
12445627cde5SMatthew Dillon 	 */
12455627cde5SMatthew Dillon 	for (flen = 0; remain[flen] && remain[flen] != '/'; ++flen)
12465627cde5SMatthew Dillon 		;
12475627cde5SMatthew Dillon 
12485627cde5SMatthew Dillon 	/*
12495627cde5SMatthew Dillon 	 * Scan the brefs associated with the directory
12505627cde5SMatthew Dillon 	 */
12515627cde5SMatthew Dillon 	for (n = 0; n < count; ++n) {
12525627cde5SMatthew Dillon 		hammer2_blockref_t *bref;
12535627cde5SMatthew Dillon 		char filename_buf[1024+1];
1254b601e4dfSMatthew Dillon 		topology_entry_t *topo;
12555627cde5SMatthew Dillon 
12565627cde5SMatthew Dillon 		bref = &base[n];
12575627cde5SMatthew Dillon 
12585627cde5SMatthew Dillon 		if (bref->type == HAMMER2_BREF_TYPE_EMPTY)
12595627cde5SMatthew Dillon 			continue;
12605627cde5SMatthew Dillon 
12615627cde5SMatthew Dillon 		vol = NULL;
12625627cde5SMatthew Dillon 		poff = 0;
12635627cde5SMatthew Dillon 		psize = 0;
12645627cde5SMatthew Dillon 		if (bref->data_off & 0x1F) {
12655627cde5SMatthew Dillon 			vol = hammer2_get_volume(bref->data_off);
12665627cde5SMatthew Dillon 			if (vol == NULL) {
12675627cde5SMatthew Dillon 				res = 0;
12685627cde5SMatthew Dillon 				continue;
12695627cde5SMatthew Dillon 			}
12705627cde5SMatthew Dillon 			poff = (bref->data_off - vol->offset) & ~0x1FL;
12715627cde5SMatthew Dillon 			psize = 1 << (bref->data_off & 0x1F);
12725627cde5SMatthew Dillon 			if (psize > sizeof(data)) {
12735627cde5SMatthew Dillon 				res = 0;
12745627cde5SMatthew Dillon 				continue;
12755627cde5SMatthew Dillon 			}
12765627cde5SMatthew Dillon 		}
12775627cde5SMatthew Dillon 
12785627cde5SMatthew Dillon 		switch(bref->type) {
12795627cde5SMatthew Dillon 		case HAMMER2_BREF_TYPE_DIRENT:
12805627cde5SMatthew Dillon 			/*
12815627cde5SMatthew Dillon 			 * Match the path element or match all directory
12825627cde5SMatthew Dillon 			 * entries if we have exhausted (remain).
12835627cde5SMatthew Dillon 			 *
12845627cde5SMatthew Dillon 			 * Locate all matching inodes and continue the
12855627cde5SMatthew Dillon 			 * traversal.
1286b601e4dfSMatthew Dillon 			 *
1287b601e4dfSMatthew Dillon 			 * Avoid traversal loops by recording path_depth
1288b601e4dfSMatthew Dillon 			 * on the way down and clearing it on the way back
1289b601e4dfSMatthew Dillon 			 * up.
12905627cde5SMatthew Dillon 			 */
12915627cde5SMatthew Dillon 			if ((flen == 0 &&
12925627cde5SMatthew Dillon 			    check_filename(bref, NULL, filename_buf,
12935627cde5SMatthew Dillon 					   bref->embed.dirent.namlen)) ||
12945627cde5SMatthew Dillon 			    (flen &&
12955627cde5SMatthew Dillon 			     flen == bref->embed.dirent.namlen &&
12965627cde5SMatthew Dillon 			     check_filename(bref, remain, filename_buf, flen)))
12975627cde5SMatthew Dillon 			{
12985627cde5SMatthew Dillon 				inode_entry_t *iscan;
12995627cde5SMatthew Dillon 				hammer2_key_t inum;
13005627cde5SMatthew Dillon 				char *path;
13015627cde5SMatthew Dillon 
13025627cde5SMatthew Dillon 				inum = bref->embed.dirent.inum;
13035627cde5SMatthew Dillon 
13045627cde5SMatthew Dillon 				asprintf(&path, "%s/%s", dest, filename_buf);
13055627cde5SMatthew Dillon 				iscan = find_first_inode(inum);
13065627cde5SMatthew Dillon 				while (iscan) {
1307ddc249f6SMatthew Dillon 					if (iscan->inum == inum &&
1308*eefc659dSMatthew Dillon 					    iscan->loopcheck == 0 &&
1309*eefc659dSMatthew Dillon 					    iscan->type ==
1310ddc249f6SMatthew Dillon 					     bref->embed.dirent.type)
1311ddc249f6SMatthew Dillon 					{
1312*eefc659dSMatthew Dillon 						iscan->loopcheck = 1;
13135627cde5SMatthew Dillon 						dump_tree(iscan, path,
13145627cde5SMatthew Dillon 							  remain + flen,
1315ddc249f6SMatthew Dillon 							  depth + 1,
1316b601e4dfSMatthew Dillon 							  path_depth,
1317ddc249f6SMatthew Dillon 							  isafile);
1318b601e4dfSMatthew Dillon 
1319b601e4dfSMatthew Dillon 						/*
1320b601e4dfSMatthew Dillon 						 * Clear loop check
1321b601e4dfSMatthew Dillon 						 */
1322*eefc659dSMatthew Dillon 						iscan->loopcheck = 0;
13235627cde5SMatthew Dillon 					}
13245627cde5SMatthew Dillon 					iscan = iscan->next;
13255627cde5SMatthew Dillon 				}
13265627cde5SMatthew Dillon 				free(path);
13275627cde5SMatthew Dillon 			}
13285627cde5SMatthew Dillon 			break;
13295627cde5SMatthew Dillon 		case HAMMER2_BREF_TYPE_INDIRECT:
13305627cde5SMatthew Dillon 			if (psize == 0) {
13315627cde5SMatthew Dillon 				res = 0;
13325627cde5SMatthew Dillon 				break;
13335627cde5SMatthew Dillon 			}
1334b601e4dfSMatthew Dillon 
1335b601e4dfSMatthew Dillon 			/*
1336b601e4dfSMatthew Dillon 			 * Due to COW operations, even if the inode is
1337b601e4dfSMatthew Dillon 			 * replicated, some of the indirect brefs might
1338b601e4dfSMatthew Dillon 			 * still be shared and allow us to reject duplicate
1339b601e4dfSMatthew Dillon 			 * scans.
1340b601e4dfSMatthew Dillon 			 */
1341b601e4dfSMatthew Dillon 			topo = enter_topology(dest);
1342b601e4dfSMatthew Dillon 			if (topology_check_duplicate_indirect(topo, bref))
1343b601e4dfSMatthew Dillon 				break;
1344b601e4dfSMatthew Dillon 
13455627cde5SMatthew Dillon 			if (pread(vol->fd, &data, psize, poff) !=
13465627cde5SMatthew Dillon 			    (ssize_t)psize)
13475627cde5SMatthew Dillon 			{
13485627cde5SMatthew Dillon 				res = 0;
13495627cde5SMatthew Dillon 				break;
13505627cde5SMatthew Dillon 			}
13515627cde5SMatthew Dillon 
13525627cde5SMatthew Dillon 			if (validate_crc(bref, &data, psize) == 0) {
13535627cde5SMatthew Dillon 				res = 0;
13545627cde5SMatthew Dillon 				break;
13555627cde5SMatthew Dillon 			}
13565627cde5SMatthew Dillon 
13575627cde5SMatthew Dillon 			rtmp = dump_dir_data(dest, remain,
13585627cde5SMatthew Dillon 					  &data.npdata[0],
13595627cde5SMatthew Dillon 					  psize / sizeof(hammer2_blockref_t),
1360b601e4dfSMatthew Dillon 					  depth, path_depth, isafile);
13615627cde5SMatthew Dillon 			if (res)
13625627cde5SMatthew Dillon 				res = rtmp;
13635627cde5SMatthew Dillon 			break;
13645627cde5SMatthew Dillon 		}
13655627cde5SMatthew Dillon 	}
13665627cde5SMatthew Dillon 	return res;
13675627cde5SMatthew Dillon }
13685627cde5SMatthew Dillon 
13695627cde5SMatthew Dillon /*
13705627cde5SMatthew Dillon  * Dumps the data records for an inode to the target file (wfd), returns
13715627cde5SMatthew Dillon  * TRUE on success, FALSE if corruption was detected.
13725627cde5SMatthew Dillon  */
13735627cde5SMatthew Dillon static int
dump_file_data(int wfd,hammer2_off_t fsize,hammer2_blockref_t * base,int count)1374ca5510acSMatthew Dillon dump_file_data(int wfd, hammer2_off_t fsize,
1375ca5510acSMatthew Dillon 	       hammer2_blockref_t *base, int count)
1376ca5510acSMatthew Dillon {
1377ca5510acSMatthew Dillon 	hammer2_media_data_t data;
1378ca5510acSMatthew Dillon 	hammer2_blockref_t *bref;
1379ca5510acSMatthew Dillon 	hammer2_volume_t *vol;
1380ca5510acSMatthew Dillon 	hammer2_off_t poff;
1381ca5510acSMatthew Dillon 	hammer2_off_t psize;
13823aa7d58aSMatthew Dillon 	hammer2_off_t nsize;
1383ca5510acSMatthew Dillon 	int res = 1;
1384ca5510acSMatthew Dillon 	int rtmp;
1385ca5510acSMatthew Dillon 	int n;
1386ca5510acSMatthew Dillon 
1387ca5510acSMatthew Dillon 	for (n = 0; n < count; ++n) {
13883aa7d58aSMatthew Dillon 		char *dptr;
13893aa7d58aSMatthew Dillon 		int res2;
13903aa7d58aSMatthew Dillon 
1391ca5510acSMatthew Dillon 		bref = &base[n];
1392ca5510acSMatthew Dillon 
1393ca5510acSMatthew Dillon 		if (bref->type == HAMMER2_BREF_TYPE_EMPTY ||
1394ca5510acSMatthew Dillon 		    bref->data_off == 0)
1395ca5510acSMatthew Dillon 		{
1396ca5510acSMatthew Dillon 			continue;
1397ca5510acSMatthew Dillon 		}
1398ca5510acSMatthew Dillon 		vol = hammer2_get_volume(bref->data_off);
1399ca5510acSMatthew Dillon 		if (vol == NULL)
1400ca5510acSMatthew Dillon 			continue;
1401ca5510acSMatthew Dillon 		if (bref->type == HAMMER2_BREF_TYPE_EMPTY ||
1402ca5510acSMatthew Dillon 		    bref->data_off == 0)
1403ca5510acSMatthew Dillon 		{
1404ca5510acSMatthew Dillon 			continue;
1405ca5510acSMatthew Dillon 		}
1406ca5510acSMatthew Dillon 
1407ca5510acSMatthew Dillon 		poff = (bref->data_off - vol->offset) & ~0x1FL;
1408ca5510acSMatthew Dillon 		psize = 1 << (bref->data_off & 0x1F);
1409ca5510acSMatthew Dillon 		if (psize > sizeof(data)) {
1410ca5510acSMatthew Dillon 			res = 0;
1411ca5510acSMatthew Dillon 			continue;
1412ca5510acSMatthew Dillon 		}
1413ca5510acSMatthew Dillon 
1414ca5510acSMatthew Dillon 		if (pread(vol->fd, &data, psize, poff) != (ssize_t)psize) {
1415ca5510acSMatthew Dillon 			res = 0;
1416ca5510acSMatthew Dillon 			continue;
1417ca5510acSMatthew Dillon 		}
1418ca5510acSMatthew Dillon 
1419ca5510acSMatthew Dillon 		if (validate_crc(bref, &data, psize) == 0) {
1420ca5510acSMatthew Dillon 			res = 0;
1421ca5510acSMatthew Dillon 			continue;
1422ca5510acSMatthew Dillon 		}
1423ca5510acSMatthew Dillon 
1424ca5510acSMatthew Dillon 		switch(bref->type) {
1425ca5510acSMatthew Dillon 		case HAMMER2_BREF_TYPE_DATA:
14263aa7d58aSMatthew Dillon 			dptr = (void *)&data;
14273aa7d58aSMatthew Dillon 
14283aa7d58aSMatthew Dillon 			nsize = 1L << bref->keybits;
14293aa7d58aSMatthew Dillon 			if (nsize > sizeof(data)) {
14303aa7d58aSMatthew Dillon 				res = 0;
14313aa7d58aSMatthew Dillon 				break;
14323aa7d58aSMatthew Dillon 			}
14333aa7d58aSMatthew Dillon 
14343aa7d58aSMatthew Dillon 			switch (HAMMER2_DEC_COMP(bref->methods)) {
14353aa7d58aSMatthew Dillon 			case HAMMER2_COMP_LZ4:
14363aa7d58aSMatthew Dillon 				dptr = hammer2_decompress_LZ4(dptr, psize,
14373aa7d58aSMatthew Dillon 							      nsize, &res2);
14383aa7d58aSMatthew Dillon 				if (res)
14393aa7d58aSMatthew Dillon 					res = res2;
14403aa7d58aSMatthew Dillon 				psize = nsize;
14413aa7d58aSMatthew Dillon 				break;
14423aa7d58aSMatthew Dillon 			case HAMMER2_COMP_ZLIB:
14433aa7d58aSMatthew Dillon 				dptr = hammer2_decompress_ZLIB(dptr, psize,
14443aa7d58aSMatthew Dillon 							       nsize, &res2);
14453aa7d58aSMatthew Dillon 				if (res)
14463aa7d58aSMatthew Dillon 					res = res2;
14473aa7d58aSMatthew Dillon 				psize = nsize;
14483aa7d58aSMatthew Dillon 				break;
14493aa7d58aSMatthew Dillon 			case HAMMER2_COMP_NONE:
14503aa7d58aSMatthew Dillon 			default:
14513aa7d58aSMatthew Dillon 				/* leave in current form */
14523aa7d58aSMatthew Dillon 				break;
14533aa7d58aSMatthew Dillon 			}
14543aa7d58aSMatthew Dillon 
1455ca5510acSMatthew Dillon 			if (bref->key + psize > fsize)
14563aa7d58aSMatthew Dillon 				pwrite(wfd, dptr, fsize - bref->key,
1457ca5510acSMatthew Dillon 				       bref->key);
1458ca5510acSMatthew Dillon 			else
14593aa7d58aSMatthew Dillon 				pwrite(wfd, dptr, psize, bref->key);
1460ca5510acSMatthew Dillon 			break;
1461ca5510acSMatthew Dillon 		case HAMMER2_BREF_TYPE_INDIRECT:
1462ca5510acSMatthew Dillon 			rtmp = dump_file_data(wfd, fsize,
1463ca5510acSMatthew Dillon 					  &data.npdata[0],
1464ca5510acSMatthew Dillon 					  psize / sizeof(hammer2_blockref_t));
1465ca5510acSMatthew Dillon 			if (res)
1466ca5510acSMatthew Dillon 				res = rtmp;
1467ca5510acSMatthew Dillon 			break;
1468ca5510acSMatthew Dillon 		}
1469ca5510acSMatthew Dillon 	}
1470ca5510acSMatthew Dillon 	return res;
1471ca5510acSMatthew Dillon }
1472ca5510acSMatthew Dillon 
14735627cde5SMatthew Dillon /*
14745627cde5SMatthew Dillon  * Validate the bref data target.  The recovery scan will often attempt to
14755627cde5SMatthew Dillon  * validate invalid elements, so don't spew errors to stderr on failure.
14765627cde5SMatthew Dillon  */
1477ca5510acSMatthew Dillon static int
validate_crc(hammer2_blockref_t * bref,void * data,size_t bytes)1478ca5510acSMatthew Dillon validate_crc(hammer2_blockref_t *bref, void *data, size_t bytes)
1479ca5510acSMatthew Dillon {
1480ca5510acSMatthew Dillon 	uint32_t cv;
1481ca5510acSMatthew Dillon 	uint64_t cv64;
1482ca5510acSMatthew Dillon 	SHA256_CTX hash_ctx;
1483ca5510acSMatthew Dillon 	int success = 0;
1484ca5510acSMatthew Dillon 	union {
1485ca5510acSMatthew Dillon 		uint8_t digest[SHA256_DIGEST_LENGTH];
1486ca5510acSMatthew Dillon 		uint64_t digest64[SHA256_DIGEST_LENGTH/8];
1487ca5510acSMatthew Dillon 	} u;
1488ca5510acSMatthew Dillon 
1489ca5510acSMatthew Dillon 	switch(HAMMER2_DEC_CHECK(bref->methods)) {
1490ca5510acSMatthew Dillon 	case HAMMER2_CHECK_NONE:
1491ddc249f6SMatthew Dillon 		if (StrictMode == 0)
1492ca5510acSMatthew Dillon 			success = 1;
1493ca5510acSMatthew Dillon 		break;
1494ca5510acSMatthew Dillon 	case HAMMER2_CHECK_DISABLED:
1495ddc249f6SMatthew Dillon 		if (StrictMode == 0)
1496ca5510acSMatthew Dillon 			success = 1;
1497ca5510acSMatthew Dillon 		break;
1498ca5510acSMatthew Dillon 	case HAMMER2_CHECK_ISCSI32:
1499ca5510acSMatthew Dillon 		cv = hammer2_icrc32(data, bytes);
15005627cde5SMatthew Dillon 		if (bref->check.iscsi32.value == cv) {
1501ca5510acSMatthew Dillon 			success = 1;
1502ca5510acSMatthew Dillon 		}
1503ca5510acSMatthew Dillon 		break;
1504ca5510acSMatthew Dillon 	case HAMMER2_CHECK_XXHASH64:
1505ca5510acSMatthew Dillon 		cv64 = XXH64(data, bytes, XXH_HAMMER2_SEED);
15065627cde5SMatthew Dillon 		if (bref->check.xxhash64.value == cv64) {
1507ca5510acSMatthew Dillon 			success = 1;
1508ca5510acSMatthew Dillon 		}
1509ca5510acSMatthew Dillon 		break;
1510ca5510acSMatthew Dillon 	case HAMMER2_CHECK_SHA192:
1511ca5510acSMatthew Dillon 		SHA256_Init(&hash_ctx);
1512ca5510acSMatthew Dillon 		SHA256_Update(&hash_ctx, data, bytes);
1513ca5510acSMatthew Dillon 		SHA256_Final(u.digest, &hash_ctx);
1514ca5510acSMatthew Dillon 		u.digest64[2] ^= u.digest64[3];
1515ca5510acSMatthew Dillon 		if (memcmp(u.digest, bref->check.sha192.data,
15165627cde5SMatthew Dillon 			   sizeof(bref->check.sha192.data)) == 0)
15175627cde5SMatthew Dillon 		{
1518ca5510acSMatthew Dillon 			success = 1;
1519ca5510acSMatthew Dillon 		}
1520ca5510acSMatthew Dillon 		break;
1521ca5510acSMatthew Dillon 	case HAMMER2_CHECK_FREEMAP:
1522ca5510acSMatthew Dillon 		cv = hammer2_icrc32(data, bytes);
15235627cde5SMatthew Dillon 		if (bref->check.freemap.icrc32 == cv) {
1524ca5510acSMatthew Dillon 			success = 1;
1525ca5510acSMatthew Dillon 		}
1526ca5510acSMatthew Dillon 		break;
1527ca5510acSMatthew Dillon 	}
1528ca5510acSMatthew Dillon 	return success;
1529ca5510acSMatthew Dillon }
1530ca5510acSMatthew Dillon 
15315627cde5SMatthew Dillon /*
15325627cde5SMatthew Dillon  * Convert a hammer2 uuid to a uid or gid.
15335627cde5SMatthew Dillon  */
1534ca5510acSMatthew Dillon static uint32_t
hammer2_to_unix_xid(const uuid_t * uuid)1535ca5510acSMatthew Dillon hammer2_to_unix_xid(const uuid_t *uuid)
1536ca5510acSMatthew Dillon {
1537ca5510acSMatthew Dillon         return(*(const uint32_t *)&uuid->node[2]);
1538ca5510acSMatthew Dillon }
1539a5eec3d1SMatthew Dillon 
1540a5eec3d1SMatthew Dillon /*
1541a5eec3d1SMatthew Dillon  * Read from disk image, with caching to improve performance.
1542a5eec3d1SMatthew Dillon  *
1543a5eec3d1SMatthew Dillon  * Use a very simple LRU algo with 16 entries, linearly checked.
1544a5eec3d1SMatthew Dillon  */
1545a5eec3d1SMatthew Dillon #define SDCCOUNT	16
1546a5eec3d1SMatthew Dillon #define SDCMASK		(SDCCOUNT - 1)
1547a5eec3d1SMatthew Dillon 
1548a5eec3d1SMatthew Dillon typedef struct sdccache {
1549a5eec3d1SMatthew Dillon 	char		buf[HAMMER2_PBUFSIZE];
1550a5eec3d1SMatthew Dillon 	hammer2_off_t	offset;
1551a5eec3d1SMatthew Dillon 	hammer2_volume_t *vol;
1552a5eec3d1SMatthew Dillon 	int64_t		last;
1553a5eec3d1SMatthew Dillon } sdccache_t;
1554a5eec3d1SMatthew Dillon 
1555a5eec3d1SMatthew Dillon static sdccache_t SDCCache[SDCCOUNT];
1556a5eec3d1SMatthew Dillon static int64_t SDCLast;
1557a5eec3d1SMatthew Dillon 
1558a5eec3d1SMatthew Dillon static void *
hammer2_cache_read(hammer2_off_t data_off,size_t * bytesp)1559*eefc659dSMatthew Dillon hammer2_cache_read(hammer2_off_t data_off, size_t *bytesp)
1560a5eec3d1SMatthew Dillon {
1561*eefc659dSMatthew Dillon 	hammer2_off_t poff;
1562*eefc659dSMatthew Dillon 	hammer2_off_t pbase;
1563*eefc659dSMatthew Dillon 	hammer2_volume_t *vol;
1564a5eec3d1SMatthew Dillon 	sdccache_t *sdc;
1565*eefc659dSMatthew Dillon 	sdccache_t *sdc_worst;
1566a5eec3d1SMatthew Dillon 	int i;
1567a5eec3d1SMatthew Dillon 
1568*eefc659dSMatthew Dillon 	*bytesp = 0;
1569*eefc659dSMatthew Dillon 
1570*eefc659dSMatthew Dillon 	/*
1571*eefc659dSMatthew Dillon 	 * Translate logical offset to volume and physical offset.
1572*eefc659dSMatthew Dillon 	 * Return NULL with *bytesp set to 0 to indicate pre-I/O
1573*eefc659dSMatthew Dillon 	 * sanity check failure.
1574*eefc659dSMatthew Dillon 	 */
1575*eefc659dSMatthew Dillon 	vol = hammer2_get_volume(data_off);
1576*eefc659dSMatthew Dillon         if (vol == NULL)
1577*eefc659dSMatthew Dillon                 return NULL;
1578*eefc659dSMatthew Dillon         poff = (data_off - vol->offset) & ~0x1FL;
1579*eefc659dSMatthew Dillon 	*bytesp = 1 << (data_off & 0x1F);
1580*eefc659dSMatthew Dillon 
1581*eefc659dSMatthew Dillon 	pbase = poff & ~HAMMER2_PBUFMASK64;
1582*eefc659dSMatthew Dillon 
1583*eefc659dSMatthew Dillon 	/*
1584*eefc659dSMatthew Dillon 	 * Must not straddle two full-sized hammer2 blocks
1585*eefc659dSMatthew Dillon 	 */
1586*eefc659dSMatthew Dillon 	if ((poff ^ (poff + *bytesp - 1)) & ~HAMMER2_PBUFMASK64) {
1587*eefc659dSMatthew Dillon 		*bytesp = 0;
1588*eefc659dSMatthew Dillon 		return NULL;
1589*eefc659dSMatthew Dillon 	}
1590*eefc659dSMatthew Dillon 
1591c255364fSMatthew Dillon 	/*
1592c255364fSMatthew Dillon 	 * I/Os are powers of 2 in size and must be aligned to at least
1593c255364fSMatthew Dillon 	 * the I/O size.
1594c255364fSMatthew Dillon 	 */
1595*eefc659dSMatthew Dillon 	if (poff & ~0x1FL & (*bytesp - 1)) {
1596*eefc659dSMatthew Dillon 		*bytesp = 0;
1597c255364fSMatthew Dillon 		return NULL;
1598*eefc659dSMatthew Dillon 	}
1599c255364fSMatthew Dillon 
1600*eefc659dSMatthew Dillon 	/*
1601*eefc659dSMatthew Dillon 	 * LRU match lookup
1602*eefc659dSMatthew Dillon 	 */
1603*eefc659dSMatthew Dillon 	sdc_worst = NULL;
1604a5eec3d1SMatthew Dillon 	for (i = 0; i < SDCCOUNT; ++i) {
1605a5eec3d1SMatthew Dillon 		sdc = &SDCCache[i];
1606a5eec3d1SMatthew Dillon 
1607a5eec3d1SMatthew Dillon 		if (sdc->vol == vol && sdc->offset == pbase) {
1608a5eec3d1SMatthew Dillon 			sdc->last = ++SDCLast;
1609a5eec3d1SMatthew Dillon 			return (&sdc->buf[poff - pbase]);
1610a5eec3d1SMatthew Dillon 		}
1611a5eec3d1SMatthew Dillon 		if (sdc_worst == NULL || sdc_worst->last > sdc->last)
1612a5eec3d1SMatthew Dillon 			sdc_worst = sdc;
1613a5eec3d1SMatthew Dillon 	}
1614a5eec3d1SMatthew Dillon 
1615*eefc659dSMatthew Dillon 	/*
1616*eefc659dSMatthew Dillon 	 * Fallback to I/O if not found, using oldest entry
1617*eefc659dSMatthew Dillon 	 *
1618*eefc659dSMatthew Dillon 	 * On failure we leave (*bytesp) intact to indicate that an I/O
1619*eefc659dSMatthew Dillon 	 * was attempted.
1620*eefc659dSMatthew Dillon 	 */
1621a5eec3d1SMatthew Dillon 	sdc = sdc_worst;
1622a5eec3d1SMatthew Dillon 	sdc->vol = vol;
1623a5eec3d1SMatthew Dillon 	sdc->offset = pbase;
1624a5eec3d1SMatthew Dillon 	sdc->last = ++SDCLast;
1625a5eec3d1SMatthew Dillon 
1626a5eec3d1SMatthew Dillon 	if (pread(vol->fd, sdc->buf, HAMMER2_PBUFSIZE, pbase) !=
1627a5eec3d1SMatthew Dillon 	    HAMMER2_PBUFSIZE)
1628a5eec3d1SMatthew Dillon 	{
1629a5eec3d1SMatthew Dillon 		sdc->offset = (hammer2_off_t)-1;
1630a5eec3d1SMatthew Dillon 		sdc->last = 0;
1631a5eec3d1SMatthew Dillon 		return NULL;
1632a5eec3d1SMatthew Dillon 	}
1633a5eec3d1SMatthew Dillon 	return (&sdc->buf[poff - pbase]);
1634a5eec3d1SMatthew Dillon }
1635