]>
Commit | Line | Data |
---|---|---|
4d3c95f5 JL |
1 | /* |
2 | * | |
3 | * ZFS filesystem ported to u-boot by | |
4 | * Jorgen Lundman <lundman at lundman.net> | |
5 | * | |
6 | * GRUB -- GRand Unified Bootloader | |
7 | * Copyright (C) 1999,2000,2001,2002,2003,2004 | |
8 | * Free Software Foundation, Inc. | |
9 | * Copyright 2004 Sun Microsystems, Inc. | |
10 | * | |
1a459660 | 11 | * SPDX-License-Identifier: GPL-2.0+ |
4d3c95f5 JL |
12 | */ |
13 | ||
14 | #include <common.h> | |
15 | #include <malloc.h> | |
16 | #include <linux/stat.h> | |
17 | #include <linux/time.h> | |
18 | #include <linux/ctype.h> | |
19 | #include <asm/byteorder.h> | |
20 | #include "zfs_common.h" | |
624c721f | 21 | #include "div64.h" |
4d3c95f5 JL |
22 | |
23 | block_dev_desc_t *zfs_dev_desc; | |
24 | ||
25 | /* | |
26 | * The zfs plug-in routines for GRUB are: | |
27 | * | |
28 | * zfs_mount() - locates a valid uberblock of the root pool and reads | |
29 | * in its MOS at the memory address MOS. | |
30 | * | |
31 | * zfs_open() - locates a plain file object by following the MOS | |
32 | * and places its dnode at the memory address DNODE. | |
33 | * | |
34 | * zfs_read() - read in the data blocks pointed by the DNODE. | |
35 | * | |
36 | */ | |
37 | ||
38 | #include <zfs/zfs.h> | |
39 | #include <zfs/zio.h> | |
40 | #include <zfs/dnode.h> | |
41 | #include <zfs/uberblock_impl.h> | |
42 | #include <zfs/vdev_impl.h> | |
43 | #include <zfs/zio_checksum.h> | |
44 | #include <zfs/zap_impl.h> | |
45 | #include <zfs/zap_leaf.h> | |
46 | #include <zfs/zfs_znode.h> | |
47 | #include <zfs/dmu.h> | |
48 | #include <zfs/dmu_objset.h> | |
49 | #include <zfs/sa_impl.h> | |
50 | #include <zfs/dsl_dir.h> | |
51 | #include <zfs/dsl_dataset.h> | |
52 | ||
53 | ||
54 | #define ZPOOL_PROP_BOOTFS "bootfs" | |
55 | ||
56 | ||
57 | /* | |
58 | * For nvlist manipulation. (from nvpair.h) | |
59 | */ | |
60 | #define NV_ENCODE_NATIVE 0 | |
61 | #define NV_ENCODE_XDR 1 | |
62 | #define NV_BIG_ENDIAN 0 | |
63 | #define NV_LITTLE_ENDIAN 1 | |
64 | #define DATA_TYPE_UINT64 8 | |
65 | #define DATA_TYPE_STRING 9 | |
66 | #define DATA_TYPE_NVLIST 19 | |
67 | #define DATA_TYPE_NVLIST_ARRAY 20 | |
68 | ||
69 | ||
70 | /* | |
71 | * Macros to get fields in a bp or DVA. | |
72 | */ | |
73 | #define P2PHASE(x, align) ((x) & ((align) - 1)) | |
74 | #define DVA_OFFSET_TO_PHYS_SECTOR(offset) \ | |
75 | ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT) | |
76 | ||
77 | /* | |
78 | * return x rounded down to an align boundary | |
79 | * eg, P2ALIGN(1200, 1024) == 1024 (1*align) | |
80 | * eg, P2ALIGN(1024, 1024) == 1024 (1*align) | |
81 | * eg, P2ALIGN(0x1234, 0x100) == 0x1200 (0x12*align) | |
82 | * eg, P2ALIGN(0x5600, 0x100) == 0x5600 (0x56*align) | |
83 | */ | |
84 | #define P2ALIGN(x, align) ((x) & -(align)) | |
85 | ||
86 | /* | |
87 | * FAT ZAP data structures | |
88 | */ | |
89 | #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */ | |
90 | #define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n)))) | |
91 | #define CHAIN_END 0xffff /* end of the chunk chain */ | |
92 | ||
93 | /* | |
94 | * The amount of space within the chunk available for the array is: | |
95 | * chunk size - space for type (1) - space for next pointer (2) | |
96 | */ | |
97 | #define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3) | |
98 | ||
99 | #define ZAP_LEAF_HASH_SHIFT(bs) (bs - 5) | |
100 | #define ZAP_LEAF_HASH_NUMENTRIES(bs) (1 << ZAP_LEAF_HASH_SHIFT(bs)) | |
101 | #define LEAF_HASH(bs, h) \ | |
102 | ((ZAP_LEAF_HASH_NUMENTRIES(bs)-1) & \ | |
103 | ((h) >> (64 - ZAP_LEAF_HASH_SHIFT(bs)-l->l_hdr.lh_prefix_len))) | |
104 | ||
105 | /* | |
106 | * The amount of space available for chunks is: | |
107 | * block size shift - hash entry size (2) * number of hash | |
108 | * entries - header space (2*chunksize) | |
109 | */ | |
110 | #define ZAP_LEAF_NUMCHUNKS(bs) \ | |
111 | (((1<<bs) - 2*ZAP_LEAF_HASH_NUMENTRIES(bs)) / \ | |
112 | ZAP_LEAF_CHUNKSIZE - 2) | |
113 | ||
114 | /* | |
115 | * The chunks start immediately after the hash table. The end of the | |
116 | * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a | |
117 | * chunk_t. | |
118 | */ | |
119 | #define ZAP_LEAF_CHUNK(l, bs, idx) \ | |
120 | ((zap_leaf_chunk_t *)(l->l_hash + ZAP_LEAF_HASH_NUMENTRIES(bs)))[idx] | |
121 | #define ZAP_LEAF_ENTRY(l, bs, idx) (&ZAP_LEAF_CHUNK(l, bs, idx).l_entry) | |
122 | ||
123 | ||
124 | /* | |
125 | * Decompression Entry - lzjb | |
126 | */ | |
127 | #ifndef NBBY | |
128 | #define NBBY 8 | |
129 | #endif | |
130 | ||
131 | ||
132 | ||
133 | typedef int zfs_decomp_func_t(void *s_start, void *d_start, | |
134 | uint32_t s_len, uint32_t d_len); | |
135 | typedef struct decomp_entry { | |
136 | char *name; | |
137 | zfs_decomp_func_t *decomp_func; | |
138 | } decomp_entry_t; | |
139 | ||
140 | typedef struct dnode_end { | |
141 | dnode_phys_t dn; | |
142 | zfs_endian_t endian; | |
143 | } dnode_end_t; | |
144 | ||
145 | struct zfs_data { | |
146 | /* cache for a file block of the currently zfs_open()-ed file */ | |
147 | char *file_buf; | |
148 | uint64_t file_start; | |
149 | uint64_t file_end; | |
150 | ||
151 | /* XXX: ashift is per vdev, not per pool. We currently only ever touch | |
152 | * a single vdev, but when/if raid-z or stripes are supported, this | |
153 | * may need revision. | |
154 | */ | |
155 | uint64_t vdev_ashift; | |
156 | uint64_t label_txg; | |
157 | uint64_t pool_guid; | |
158 | ||
159 | /* cache for a dnode block */ | |
160 | dnode_phys_t *dnode_buf; | |
161 | dnode_phys_t *dnode_mdn; | |
162 | uint64_t dnode_start; | |
163 | uint64_t dnode_end; | |
164 | zfs_endian_t dnode_endian; | |
165 | ||
166 | uberblock_t current_uberblock; | |
167 | ||
168 | dnode_end_t mos; | |
169 | dnode_end_t mdn; | |
170 | dnode_end_t dnode; | |
171 | ||
172 | uint64_t vdev_phys_sector; | |
173 | ||
174 | int (*userhook)(const char *, const struct zfs_dirhook_info *); | |
175 | struct zfs_dirhook_info *dirinfo; | |
176 | ||
177 | }; | |
178 | ||
179 | ||
180 | ||
181 | ||
182 | static int | |
183 | zlib_decompress(void *s, void *d, | |
184 | uint32_t slen, uint32_t dlen) | |
185 | { | |
186 | if (zlib_decompress(s, d, slen, dlen) < 0) | |
187 | return ZFS_ERR_BAD_FS; | |
188 | return ZFS_ERR_NONE; | |
189 | } | |
190 | ||
191 | static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { | |
192 | {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */ | |
193 | {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ | |
194 | {"off", NULL}, /* ZIO_COMPRESS_OFF */ | |
195 | {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */ | |
196 | {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */ | |
197 | {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */ | |
198 | {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */ | |
199 | {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */ | |
200 | {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */ | |
201 | {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */ | |
202 | {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */ | |
203 | {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */ | |
204 | {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */ | |
205 | {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ | |
206 | }; | |
207 | ||
208 | ||
209 | ||
210 | static int zio_read_data(blkptr_t *bp, zfs_endian_t endian, | |
211 | void *buf, struct zfs_data *data); | |
212 | ||
213 | static int | |
214 | zio_read(blkptr_t *bp, zfs_endian_t endian, void **buf, | |
215 | size_t *size, struct zfs_data *data); | |
216 | ||
217 | /* | |
218 | * Our own version of log2(). Same thing as highbit()-1. | |
219 | */ | |
220 | static int | |
221 | zfs_log2(uint64_t num) | |
222 | { | |
223 | int i = 0; | |
224 | ||
225 | while (num > 1) { | |
226 | i++; | |
227 | num = num >> 1; | |
228 | } | |
229 | ||
230 | return i; | |
231 | } | |
232 | ||
233 | ||
234 | /* Checksum Functions */ | |
235 | static void | |
236 | zio_checksum_off(const void *buf __attribute__ ((unused)), | |
237 | uint64_t size __attribute__ ((unused)), | |
238 | zfs_endian_t endian __attribute__ ((unused)), | |
239 | zio_cksum_t *zcp) | |
240 | { | |
241 | ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0); | |
242 | } | |
243 | ||
244 | /* Checksum Table and Values */ | |
245 | static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { | |
246 | {NULL, 0, 0, "inherit"}, | |
247 | {NULL, 0, 0, "on"}, | |
248 | {zio_checksum_off, 0, 0, "off"}, | |
249 | {zio_checksum_SHA256, 1, 1, "label"}, | |
250 | {zio_checksum_SHA256, 1, 1, "gang_header"}, | |
251 | {NULL, 0, 0, "zilog"}, | |
252 | {fletcher_2_endian, 0, 0, "fletcher2"}, | |
253 | {fletcher_4_endian, 1, 0, "fletcher4"}, | |
254 | {zio_checksum_SHA256, 1, 0, "SHA256"}, | |
255 | {NULL, 0, 0, "zilog2"}, | |
256 | }; | |
257 | ||
258 | /* | |
259 | * zio_checksum_verify: Provides support for checksum verification. | |
260 | * | |
261 | * Fletcher2, Fletcher4, and SHA256 are supported. | |
262 | * | |
263 | */ | |
264 | static int | |
265 | zio_checksum_verify(zio_cksum_t zc, uint32_t checksum, | |
266 | zfs_endian_t endian, char *buf, int size) | |
267 | { | |
268 | zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1; | |
269 | zio_checksum_info_t *ci = &zio_checksum_table[checksum]; | |
270 | zio_cksum_t actual_cksum, expected_cksum; | |
271 | ||
272 | if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL) { | |
273 | printf("zfs unknown checksum function %d\n", checksum); | |
274 | return ZFS_ERR_NOT_IMPLEMENTED_YET; | |
275 | } | |
276 | ||
277 | if (ci->ci_eck) { | |
278 | expected_cksum = zec->zec_cksum; | |
279 | zec->zec_cksum = zc; | |
280 | ci->ci_func(buf, size, endian, &actual_cksum); | |
281 | zec->zec_cksum = expected_cksum; | |
282 | zc = expected_cksum; | |
283 | } else { | |
284 | ci->ci_func(buf, size, endian, &actual_cksum); | |
285 | } | |
286 | ||
287 | if ((actual_cksum.zc_word[0] != zc.zc_word[0]) | |
288 | || (actual_cksum.zc_word[1] != zc.zc_word[1]) | |
289 | || (actual_cksum.zc_word[2] != zc.zc_word[2]) | |
290 | || (actual_cksum.zc_word[3] != zc.zc_word[3])) { | |
291 | return ZFS_ERR_BAD_FS; | |
292 | } | |
293 | ||
294 | return ZFS_ERR_NONE; | |
295 | } | |
296 | ||
297 | /* | |
298 | * vdev_uberblock_compare takes two uberblock structures and returns an integer | |
299 | * indicating the more recent of the two. | |
300 | * Return Value = 1 if ub2 is more recent | |
301 | * Return Value = -1 if ub1 is more recent | |
302 | * The most recent uberblock is determined using its transaction number and | |
303 | * timestamp. The uberblock with the highest transaction number is | |
304 | * considered "newer". If the transaction numbers of the two blocks match, the | |
305 | * timestamps are compared to determine the "newer" of the two. | |
306 | */ | |
307 | static int | |
308 | vdev_uberblock_compare(uberblock_t *ub1, uberblock_t *ub2) | |
309 | { | |
310 | zfs_endian_t ub1_endian, ub2_endian; | |
311 | if (zfs_to_cpu64(ub1->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC) | |
312 | ub1_endian = LITTLE_ENDIAN; | |
313 | else | |
314 | ub1_endian = BIG_ENDIAN; | |
315 | if (zfs_to_cpu64(ub2->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC) | |
316 | ub2_endian = LITTLE_ENDIAN; | |
317 | else | |
318 | ub2_endian = BIG_ENDIAN; | |
319 | ||
320 | if (zfs_to_cpu64(ub1->ub_txg, ub1_endian) | |
321 | < zfs_to_cpu64(ub2->ub_txg, ub2_endian)) | |
322 | return -1; | |
323 | if (zfs_to_cpu64(ub1->ub_txg, ub1_endian) | |
324 | > zfs_to_cpu64(ub2->ub_txg, ub2_endian)) | |
325 | return 1; | |
326 | ||
327 | if (zfs_to_cpu64(ub1->ub_timestamp, ub1_endian) | |
328 | < zfs_to_cpu64(ub2->ub_timestamp, ub2_endian)) | |
329 | return -1; | |
330 | if (zfs_to_cpu64(ub1->ub_timestamp, ub1_endian) | |
331 | > zfs_to_cpu64(ub2->ub_timestamp, ub2_endian)) | |
332 | return 1; | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | /* | |
338 | * Three pieces of information are needed to verify an uberblock: the magic | |
339 | * number, the version number, and the checksum. | |
340 | * | |
341 | * Currently Implemented: version number, magic number, label txg | |
342 | * Need to Implement: checksum | |
343 | * | |
344 | */ | |
345 | static int | |
346 | uberblock_verify(uberblock_t *uber, int offset, struct zfs_data *data) | |
347 | { | |
348 | int err; | |
349 | zfs_endian_t endian = UNKNOWN_ENDIAN; | |
350 | zio_cksum_t zc; | |
351 | ||
352 | if (uber->ub_txg < data->label_txg) { | |
353 | debug("ignoring partially written label: uber_txg < label_txg %llu %llu\n", | |
354 | uber->ub_txg, data->label_txg); | |
355 | return ZFS_ERR_BAD_FS; | |
356 | } | |
357 | ||
358 | if (zfs_to_cpu64(uber->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC | |
359 | && zfs_to_cpu64(uber->ub_version, LITTLE_ENDIAN) > 0 | |
360 | && zfs_to_cpu64(uber->ub_version, LITTLE_ENDIAN) <= SPA_VERSION) | |
361 | endian = LITTLE_ENDIAN; | |
362 | ||
363 | if (zfs_to_cpu64(uber->ub_magic, BIG_ENDIAN) == UBERBLOCK_MAGIC | |
364 | && zfs_to_cpu64(uber->ub_version, BIG_ENDIAN) > 0 | |
365 | && zfs_to_cpu64(uber->ub_version, BIG_ENDIAN) <= SPA_VERSION) | |
366 | endian = BIG_ENDIAN; | |
367 | ||
368 | if (endian == UNKNOWN_ENDIAN) { | |
369 | printf("invalid uberblock magic\n"); | |
370 | return ZFS_ERR_BAD_FS; | |
371 | } | |
372 | ||
373 | memset(&zc, 0, sizeof(zc)); | |
374 | zc.zc_word[0] = cpu_to_zfs64(offset, endian); | |
375 | err = zio_checksum_verify(zc, ZIO_CHECKSUM_LABEL, endian, | |
376 | (char *) uber, UBERBLOCK_SIZE(data->vdev_ashift)); | |
377 | ||
378 | if (!err) { | |
379 | /* Check that the data pointed by the rootbp is usable. */ | |
380 | void *osp = NULL; | |
381 | size_t ospsize; | |
382 | err = zio_read(&uber->ub_rootbp, endian, &osp, &ospsize, data); | |
383 | free(osp); | |
384 | ||
385 | if (!err && ospsize < OBJSET_PHYS_SIZE_V14) { | |
386 | printf("uberblock rootbp points to invalid data\n"); | |
387 | return ZFS_ERR_BAD_FS; | |
388 | } | |
389 | } | |
390 | ||
391 | return err; | |
392 | } | |
393 | ||
394 | /* | |
395 | * Find the best uberblock. | |
396 | * Return: | |
397 | * Success - Pointer to the best uberblock. | |
398 | * Failure - NULL | |
399 | */ | |
400 | static uberblock_t *find_bestub(char *ub_array, struct zfs_data *data) | |
401 | { | |
402 | const uint64_t sector = data->vdev_phys_sector; | |
403 | uberblock_t *ubbest = NULL; | |
404 | uberblock_t *ubnext; | |
405 | unsigned int i, offset, pickedub = 0; | |
406 | int err = ZFS_ERR_NONE; | |
407 | ||
408 | const unsigned int UBCOUNT = UBERBLOCK_COUNT(data->vdev_ashift); | |
409 | const uint64_t UBBYTES = UBERBLOCK_SIZE(data->vdev_ashift); | |
410 | ||
411 | for (i = 0; i < UBCOUNT; i++) { | |
412 | ubnext = (uberblock_t *) (i * UBBYTES + ub_array); | |
413 | offset = (sector << SPA_MINBLOCKSHIFT) + VDEV_PHYS_SIZE + (i * UBBYTES); | |
414 | ||
415 | err = uberblock_verify(ubnext, offset, data); | |
416 | if (err) | |
417 | continue; | |
418 | ||
419 | if (ubbest == NULL || vdev_uberblock_compare(ubnext, ubbest) > 0) { | |
420 | ubbest = ubnext; | |
421 | pickedub = i; | |
422 | } | |
423 | } | |
424 | ||
425 | if (ubbest) | |
426 | debug("zfs Found best uberblock at idx %d, txg %llu\n", | |
427 | pickedub, (unsigned long long) ubbest->ub_txg); | |
428 | ||
429 | return ubbest; | |
430 | } | |
431 | ||
432 | static inline size_t | |
433 | get_psize(blkptr_t *bp, zfs_endian_t endian) | |
434 | { | |
435 | return (((zfs_to_cpu64((bp)->blk_prop, endian) >> 16) & 0xffff) + 1) | |
436 | << SPA_MINBLOCKSHIFT; | |
437 | } | |
438 | ||
439 | static uint64_t | |
440 | dva_get_offset(dva_t *dva, zfs_endian_t endian) | |
441 | { | |
442 | return zfs_to_cpu64((dva)->dva_word[1], | |
443 | endian) << SPA_MINBLOCKSHIFT; | |
444 | } | |
445 | ||
446 | /* | |
447 | * Read a block of data based on the gang block address dva, | |
448 | * and put its data in buf. | |
449 | * | |
450 | */ | |
451 | static int | |
452 | zio_read_gang(blkptr_t *bp, zfs_endian_t endian, dva_t *dva, void *buf, | |
453 | struct zfs_data *data) | |
454 | { | |
455 | zio_gbh_phys_t *zio_gb; | |
456 | uint64_t offset, sector; | |
457 | unsigned i; | |
458 | int err; | |
459 | zio_cksum_t zc; | |
460 | ||
461 | memset(&zc, 0, sizeof(zc)); | |
462 | ||
463 | zio_gb = malloc(SPA_GANGBLOCKSIZE); | |
464 | if (!zio_gb) | |
465 | return ZFS_ERR_OUT_OF_MEMORY; | |
466 | ||
467 | offset = dva_get_offset(dva, endian); | |
468 | sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); | |
469 | ||
470 | /* read in the gang block header */ | |
471 | err = zfs_devread(sector, 0, SPA_GANGBLOCKSIZE, (char *) zio_gb); | |
472 | ||
473 | if (err) { | |
474 | free(zio_gb); | |
475 | return err; | |
476 | } | |
477 | ||
478 | /* XXX */ | |
479 | /* self checksuming the gang block header */ | |
480 | ZIO_SET_CHECKSUM(&zc, DVA_GET_VDEV(dva), | |
481 | dva_get_offset(dva, endian), bp->blk_birth, 0); | |
482 | err = zio_checksum_verify(zc, ZIO_CHECKSUM_GANG_HEADER, endian, | |
483 | (char *) zio_gb, SPA_GANGBLOCKSIZE); | |
484 | if (err) { | |
485 | free(zio_gb); | |
486 | return err; | |
487 | } | |
488 | ||
489 | endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; | |
490 | ||
491 | for (i = 0; i < SPA_GBH_NBLKPTRS; i++) { | |
492 | if (zio_gb->zg_blkptr[i].blk_birth == 0) | |
493 | continue; | |
494 | ||
495 | err = zio_read_data(&zio_gb->zg_blkptr[i], endian, buf, data); | |
496 | if (err) { | |
497 | free(zio_gb); | |
498 | return err; | |
499 | } | |
500 | buf = (char *) buf + get_psize(&zio_gb->zg_blkptr[i], endian); | |
501 | } | |
502 | free(zio_gb); | |
503 | return ZFS_ERR_NONE; | |
504 | } | |
505 | ||
506 | /* | |
507 | * Read in a block of raw data to buf. | |
508 | */ | |
509 | static int | |
510 | zio_read_data(blkptr_t *bp, zfs_endian_t endian, void *buf, | |
511 | struct zfs_data *data) | |
512 | { | |
513 | int i, psize; | |
514 | int err = ZFS_ERR_NONE; | |
515 | ||
516 | psize = get_psize(bp, endian); | |
517 | ||
518 | /* pick a good dva from the block pointer */ | |
519 | for (i = 0; i < SPA_DVAS_PER_BP; i++) { | |
520 | uint64_t offset, sector; | |
521 | ||
522 | if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0) | |
523 | continue; | |
524 | ||
525 | if ((zfs_to_cpu64(bp->blk_dva[i].dva_word[1], endian)>>63) & 1) { | |
526 | err = zio_read_gang(bp, endian, &bp->blk_dva[i], buf, data); | |
527 | } else { | |
528 | /* read in a data block */ | |
529 | offset = dva_get_offset(&bp->blk_dva[i], endian); | |
530 | sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); | |
531 | ||
532 | err = zfs_devread(sector, 0, psize, buf); | |
533 | } | |
534 | ||
535 | if (!err) { | |
536 | /*Check the underlying checksum before we rule this DVA as "good"*/ | |
537 | uint32_t checkalgo = (zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff; | |
538 | ||
539 | err = zio_checksum_verify(bp->blk_cksum, checkalgo, endian, buf, psize); | |
540 | if (!err) | |
541 | return ZFS_ERR_NONE; | |
542 | } | |
543 | ||
544 | /* If read failed or checksum bad, reset the error. Hopefully we've got some more DVA's to try.*/ | |
545 | } | |
546 | ||
547 | if (!err) { | |
548 | printf("couldn't find a valid DVA\n"); | |
549 | err = ZFS_ERR_BAD_FS; | |
550 | } | |
551 | ||
552 | return err; | |
553 | } | |
554 | ||
555 | /* | |
556 | * Read in a block of data, verify its checksum, decompress if needed, | |
557 | * and put the uncompressed data in buf. | |
558 | */ | |
559 | static int | |
560 | zio_read(blkptr_t *bp, zfs_endian_t endian, void **buf, | |
561 | size_t *size, struct zfs_data *data) | |
562 | { | |
563 | size_t lsize, psize; | |
564 | unsigned int comp; | |
565 | char *compbuf = NULL; | |
566 | int err; | |
567 | ||
568 | *buf = NULL; | |
569 | ||
570 | comp = (zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff; | |
571 | lsize = (BP_IS_HOLE(bp) ? 0 : | |
572 | (((zfs_to_cpu64((bp)->blk_prop, endian) & 0xffff) + 1) | |
573 | << SPA_MINBLOCKSHIFT)); | |
574 | psize = get_psize(bp, endian); | |
575 | ||
576 | if (size) | |
577 | *size = lsize; | |
578 | ||
579 | if (comp >= ZIO_COMPRESS_FUNCTIONS) { | |
580 | printf("compression algorithm %u not supported\n", (unsigned int) comp); | |
581 | return ZFS_ERR_NOT_IMPLEMENTED_YET; | |
582 | } | |
583 | ||
584 | if (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL) { | |
585 | printf("compression algorithm %s not supported\n", decomp_table[comp].name); | |
586 | return ZFS_ERR_NOT_IMPLEMENTED_YET; | |
587 | } | |
588 | ||
589 | if (comp != ZIO_COMPRESS_OFF) { | |
590 | compbuf = malloc(psize); | |
591 | if (!compbuf) | |
592 | return ZFS_ERR_OUT_OF_MEMORY; | |
593 | } else { | |
594 | compbuf = *buf = malloc(lsize); | |
595 | } | |
596 | ||
597 | err = zio_read_data(bp, endian, compbuf, data); | |
598 | if (err) { | |
599 | free(compbuf); | |
600 | *buf = NULL; | |
601 | return err; | |
602 | } | |
603 | ||
604 | if (comp != ZIO_COMPRESS_OFF) { | |
605 | *buf = malloc(lsize); | |
606 | if (!*buf) { | |
607 | free(compbuf); | |
608 | return ZFS_ERR_OUT_OF_MEMORY; | |
609 | } | |
610 | ||
611 | err = decomp_table[comp].decomp_func(compbuf, *buf, psize, lsize); | |
612 | free(compbuf); | |
613 | if (err) { | |
614 | free(*buf); | |
615 | *buf = NULL; | |
616 | return err; | |
617 | } | |
618 | } | |
619 | ||
620 | return ZFS_ERR_NONE; | |
621 | } | |
622 | ||
623 | /* | |
624 | * Get the block from a block id. | |
625 | * push the block onto the stack. | |
626 | * | |
627 | */ | |
628 | static int | |
629 | dmu_read(dnode_end_t *dn, uint64_t blkid, void **buf, | |
630 | zfs_endian_t *endian_out, struct zfs_data *data) | |
631 | { | |
632 | int idx, level; | |
633 | blkptr_t *bp_array = dn->dn.dn_blkptr; | |
634 | int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT; | |
635 | blkptr_t *bp; | |
636 | void *tmpbuf = 0; | |
637 | zfs_endian_t endian; | |
638 | int err = ZFS_ERR_NONE; | |
639 | ||
640 | bp = malloc(sizeof(blkptr_t)); | |
641 | if (!bp) | |
642 | return ZFS_ERR_OUT_OF_MEMORY; | |
643 | ||
644 | endian = dn->endian; | |
645 | for (level = dn->dn.dn_nlevels - 1; level >= 0; level--) { | |
646 | idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1); | |
647 | *bp = bp_array[idx]; | |
648 | if (bp_array != dn->dn.dn_blkptr) { | |
649 | free(bp_array); | |
650 | bp_array = 0; | |
651 | } | |
652 | ||
653 | if (BP_IS_HOLE(bp)) { | |
654 | size_t size = zfs_to_cpu16(dn->dn.dn_datablkszsec, | |
655 | dn->endian) | |
656 | << SPA_MINBLOCKSHIFT; | |
657 | *buf = malloc(size); | |
658 | if (*buf) { | |
659 | err = ZFS_ERR_OUT_OF_MEMORY; | |
660 | break; | |
661 | } | |
662 | memset(*buf, 0, size); | |
663 | endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; | |
664 | break; | |
665 | } | |
666 | if (level == 0) { | |
667 | err = zio_read(bp, endian, buf, 0, data); | |
668 | endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; | |
669 | break; | |
670 | } | |
671 | err = zio_read(bp, endian, &tmpbuf, 0, data); | |
672 | endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; | |
673 | if (err) | |
674 | break; | |
675 | bp_array = tmpbuf; | |
676 | } | |
677 | if (bp_array != dn->dn.dn_blkptr) | |
678 | free(bp_array); | |
679 | if (endian_out) | |
680 | *endian_out = endian; | |
681 | ||
682 | free(bp); | |
683 | return err; | |
684 | } | |
685 | ||
686 | /* | |
687 | * mzap_lookup: Looks up property described by "name" and returns the value | |
688 | * in "value". | |
689 | */ | |
690 | static int | |
691 | mzap_lookup(mzap_phys_t *zapobj, zfs_endian_t endian, | |
692 | int objsize, char *name, uint64_t * value) | |
693 | { | |
694 | int i, chunks; | |
695 | mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; | |
696 | ||
697 | chunks = objsize / MZAP_ENT_LEN - 1; | |
698 | for (i = 0; i < chunks; i++) { | |
699 | if (strcmp(mzap_ent[i].mze_name, name) == 0) { | |
700 | *value = zfs_to_cpu64(mzap_ent[i].mze_value, endian); | |
701 | return ZFS_ERR_NONE; | |
702 | } | |
703 | } | |
704 | ||
705 | printf("couldn't find '%s'\n", name); | |
706 | return ZFS_ERR_FILE_NOT_FOUND; | |
707 | } | |
708 | ||
709 | static int | |
710 | mzap_iterate(mzap_phys_t *zapobj, zfs_endian_t endian, int objsize, | |
711 | int (*hook)(const char *name, | |
712 | uint64_t val, | |
713 | struct zfs_data *data), | |
714 | struct zfs_data *data) | |
715 | { | |
716 | int i, chunks; | |
717 | mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; | |
718 | ||
719 | chunks = objsize / MZAP_ENT_LEN - 1; | |
720 | for (i = 0; i < chunks; i++) { | |
721 | if (hook(mzap_ent[i].mze_name, | |
722 | zfs_to_cpu64(mzap_ent[i].mze_value, endian), | |
723 | data)) | |
724 | return 1; | |
725 | } | |
726 | ||
727 | return 0; | |
728 | } | |
729 | ||
730 | static uint64_t | |
731 | zap_hash(uint64_t salt, const char *name) | |
732 | { | |
733 | static uint64_t table[256]; | |
734 | const uint8_t *cp; | |
735 | uint8_t c; | |
736 | uint64_t crc = salt; | |
737 | ||
738 | if (table[128] == 0) { | |
739 | uint64_t *ct; | |
740 | int i, j; | |
741 | for (i = 0; i < 256; i++) { | |
742 | for (ct = table + i, *ct = i, j = 8; j > 0; j--) | |
743 | *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY); | |
744 | } | |
745 | } | |
746 | ||
747 | for (cp = (const uint8_t *) name; (c = *cp) != '\0'; cp++) | |
748 | crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF]; | |
749 | ||
750 | /* | |
751 | * Only use 28 bits, since we need 4 bits in the cookie for the | |
752 | * collision differentiator. We MUST use the high bits, since | |
753 | * those are the onces that we first pay attention to when | |
754 | * chosing the bucket. | |
755 | */ | |
756 | crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1); | |
757 | ||
758 | return crc; | |
759 | } | |
760 | ||
761 | /* | |
762 | * Only to be used on 8-bit arrays. | |
763 | * array_len is actual len in bytes (not encoded le_value_length). | |
764 | * buf is null-terminated. | |
765 | */ | |
766 | /* XXX */ | |
767 | static int | |
768 | zap_leaf_array_equal(zap_leaf_phys_t *l, zfs_endian_t endian, | |
769 | int blksft, int chunk, int array_len, const char *buf) | |
770 | { | |
771 | int bseen = 0; | |
772 | ||
773 | while (bseen < array_len) { | |
774 | struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, blksft, chunk).l_array; | |
775 | int toread = MIN(array_len - bseen, ZAP_LEAF_ARRAY_BYTES); | |
776 | ||
777 | if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) | |
778 | return 0; | |
779 | ||
780 | if (memcmp(la->la_array, buf + bseen, toread) != 0) | |
781 | break; | |
782 | chunk = zfs_to_cpu16(la->la_next, endian); | |
783 | bseen += toread; | |
784 | } | |
785 | return (bseen == array_len); | |
786 | } | |
787 | ||
788 | /* XXX */ | |
789 | static int | |
790 | zap_leaf_array_get(zap_leaf_phys_t *l, zfs_endian_t endian, int blksft, | |
791 | int chunk, int array_len, char *buf) | |
792 | { | |
793 | int bseen = 0; | |
794 | ||
795 | while (bseen < array_len) { | |
796 | struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, blksft, chunk).l_array; | |
797 | int toread = MIN(array_len - bseen, ZAP_LEAF_ARRAY_BYTES); | |
798 | ||
799 | if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) | |
800 | /* Don't use errno because this error is to be ignored. */ | |
801 | return ZFS_ERR_BAD_FS; | |
802 | ||
803 | memcpy(buf + bseen, la->la_array, toread); | |
804 | chunk = zfs_to_cpu16(la->la_next, endian); | |
805 | bseen += toread; | |
806 | } | |
807 | return ZFS_ERR_NONE; | |
808 | } | |
809 | ||
810 | ||
811 | /* | |
812 | * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the | |
813 | * value for the property "name". | |
814 | * | |
815 | */ | |
816 | /* XXX */ | |
817 | static int | |
818 | zap_leaf_lookup(zap_leaf_phys_t *l, zfs_endian_t endian, | |
819 | int blksft, uint64_t h, | |
820 | const char *name, uint64_t *value) | |
821 | { | |
822 | uint16_t chunk; | |
823 | struct zap_leaf_entry *le; | |
824 | ||
825 | /* Verify if this is a valid leaf block */ | |
826 | if (zfs_to_cpu64(l->l_hdr.lh_block_type, endian) != ZBT_LEAF) { | |
827 | printf("invalid leaf type\n"); | |
828 | return ZFS_ERR_BAD_FS; | |
829 | } | |
830 | if (zfs_to_cpu32(l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) { | |
831 | printf("invalid leaf magic\n"); | |
832 | return ZFS_ERR_BAD_FS; | |
833 | } | |
834 | ||
835 | for (chunk = zfs_to_cpu16(l->l_hash[LEAF_HASH(blksft, h)], endian); | |
836 | chunk != CHAIN_END; chunk = le->le_next) { | |
837 | ||
838 | if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) { | |
839 | printf("invalid chunk number\n"); | |
840 | return ZFS_ERR_BAD_FS; | |
841 | } | |
842 | ||
843 | le = ZAP_LEAF_ENTRY(l, blksft, chunk); | |
844 | ||
845 | /* Verify the chunk entry */ | |
846 | if (le->le_type != ZAP_CHUNK_ENTRY) { | |
847 | printf("invalid chunk entry\n"); | |
848 | return ZFS_ERR_BAD_FS; | |
849 | } | |
850 | ||
851 | if (zfs_to_cpu64(le->le_hash, endian) != h) | |
852 | continue; | |
853 | ||
854 | if (zap_leaf_array_equal(l, endian, blksft, | |
855 | zfs_to_cpu16(le->le_name_chunk, endian), | |
856 | zfs_to_cpu16(le->le_name_length, endian), | |
857 | name)) { | |
858 | struct zap_leaf_array *la; | |
859 | ||
860 | if (le->le_int_size != 8 || le->le_value_length != 1) { | |
861 | printf("invalid leaf chunk entry\n"); | |
862 | return ZFS_ERR_BAD_FS; | |
863 | } | |
864 | /* get the uint64_t property value */ | |
865 | la = &ZAP_LEAF_CHUNK(l, blksft, le->le_value_chunk).l_array; | |
866 | ||
867 | *value = be64_to_cpu(la->la_array64); | |
868 | ||
869 | return ZFS_ERR_NONE; | |
870 | } | |
871 | } | |
872 | ||
873 | printf("couldn't find '%s'\n", name); | |
874 | return ZFS_ERR_FILE_NOT_FOUND; | |
875 | } | |
876 | ||
877 | ||
878 | /* Verify if this is a fat zap header block */ | |
879 | static int | |
880 | zap_verify(zap_phys_t *zap) | |
881 | { | |
882 | if (zap->zap_magic != (uint64_t) ZAP_MAGIC) { | |
883 | printf("bad ZAP magic\n"); | |
884 | return ZFS_ERR_BAD_FS; | |
885 | } | |
886 | ||
887 | if (zap->zap_flags != 0) { | |
888 | printf("bad ZAP flags\n"); | |
889 | return ZFS_ERR_BAD_FS; | |
890 | } | |
891 | ||
892 | if (zap->zap_salt == 0) { | |
893 | printf("bad ZAP salt\n"); | |
894 | return ZFS_ERR_BAD_FS; | |
895 | } | |
896 | ||
897 | return ZFS_ERR_NONE; | |
898 | } | |
899 | ||
900 | /* | |
901 | * Fat ZAP lookup | |
902 | * | |
903 | */ | |
904 | /* XXX */ | |
905 | static int | |
906 | fzap_lookup(dnode_end_t *zap_dnode, zap_phys_t *zap, | |
907 | char *name, uint64_t *value, struct zfs_data *data) | |
908 | { | |
909 | void *l; | |
910 | uint64_t hash, idx, blkid; | |
911 | int blksft = zfs_log2(zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, | |
912 | zap_dnode->endian) << DNODE_SHIFT); | |
913 | int err; | |
914 | zfs_endian_t leafendian; | |
915 | ||
916 | err = zap_verify(zap); | |
917 | if (err) | |
918 | return err; | |
919 | ||
920 | hash = zap_hash(zap->zap_salt, name); | |
921 | ||
922 | /* get block id from index */ | |
923 | if (zap->zap_ptrtbl.zt_numblks != 0) { | |
924 | printf("external pointer tables not supported\n"); | |
925 | return ZFS_ERR_NOT_IMPLEMENTED_YET; | |
926 | } | |
927 | idx = ZAP_HASH_IDX(hash, zap->zap_ptrtbl.zt_shift); | |
928 | blkid = ((uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))]; | |
929 | ||
930 | /* Get the leaf block */ | |
931 | if ((1U << blksft) < sizeof(zap_leaf_phys_t)) { | |
932 | printf("ZAP leaf is too small\n"); | |
933 | return ZFS_ERR_BAD_FS; | |
934 | } | |
935 | err = dmu_read(zap_dnode, blkid, &l, &leafendian, data); | |
936 | if (err) | |
937 | return err; | |
938 | ||
939 | err = zap_leaf_lookup(l, leafendian, blksft, hash, name, value); | |
940 | free(l); | |
941 | return err; | |
942 | } | |
943 | ||
944 | /* XXX */ | |
945 | static int | |
946 | fzap_iterate(dnode_end_t *zap_dnode, zap_phys_t *zap, | |
947 | int (*hook)(const char *name, | |
948 | uint64_t val, | |
949 | struct zfs_data *data), | |
950 | struct zfs_data *data) | |
951 | { | |
952 | zap_leaf_phys_t *l; | |
953 | void *l_in; | |
954 | uint64_t idx, blkid; | |
955 | uint16_t chunk; | |
956 | int blksft = zfs_log2(zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, | |
957 | zap_dnode->endian) << DNODE_SHIFT); | |
958 | int err; | |
959 | zfs_endian_t endian; | |
960 | ||
961 | if (zap_verify(zap)) | |
962 | return 0; | |
963 | ||
964 | /* get block id from index */ | |
965 | if (zap->zap_ptrtbl.zt_numblks != 0) { | |
966 | printf("external pointer tables not supported\n"); | |
967 | return 0; | |
968 | } | |
969 | /* Get the leaf block */ | |
970 | if ((1U << blksft) < sizeof(zap_leaf_phys_t)) { | |
971 | printf("ZAP leaf is too small\n"); | |
972 | return 0; | |
973 | } | |
974 | for (idx = 0; idx < zap->zap_ptrtbl.zt_numblks; idx++) { | |
975 | blkid = ((uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))]; | |
976 | ||
977 | err = dmu_read(zap_dnode, blkid, &l_in, &endian, data); | |
978 | l = l_in; | |
979 | if (err) | |
980 | continue; | |
981 | ||
982 | /* Verify if this is a valid leaf block */ | |
983 | if (zfs_to_cpu64(l->l_hdr.lh_block_type, endian) != ZBT_LEAF) { | |
984 | free(l); | |
985 | continue; | |
986 | } | |
987 | if (zfs_to_cpu32(l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) { | |
988 | free(l); | |
989 | continue; | |
990 | } | |
991 | ||
992 | for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS(blksft); chunk++) { | |
993 | char *buf; | |
994 | struct zap_leaf_array *la; | |
995 | struct zap_leaf_entry *le; | |
996 | uint64_t val; | |
997 | le = ZAP_LEAF_ENTRY(l, blksft, chunk); | |
998 | ||
999 | /* Verify the chunk entry */ | |
1000 | if (le->le_type != ZAP_CHUNK_ENTRY) | |
1001 | continue; | |
1002 | ||
1003 | buf = malloc(zfs_to_cpu16(le->le_name_length, endian) | |
1004 | + 1); | |
1005 | if (zap_leaf_array_get(l, endian, blksft, le->le_name_chunk, | |
1006 | le->le_name_length, buf)) { | |
1007 | free(buf); | |
1008 | continue; | |
1009 | } | |
1010 | buf[le->le_name_length] = 0; | |
1011 | ||
1012 | if (le->le_int_size != 8 | |
1013 | || zfs_to_cpu16(le->le_value_length, endian) != 1) | |
1014 | continue; | |
1015 | ||
1016 | /* get the uint64_t property value */ | |
1017 | la = &ZAP_LEAF_CHUNK(l, blksft, le->le_value_chunk).l_array; | |
1018 | val = be64_to_cpu(la->la_array64); | |
1019 | if (hook(buf, val, data)) | |
1020 | return 1; | |
1021 | free(buf); | |
1022 | } | |
1023 | } | |
1024 | return 0; | |
1025 | } | |
1026 | ||
1027 | ||
1028 | /* | |
1029 | * Read in the data of a zap object and find the value for a matching | |
1030 | * property name. | |
1031 | * | |
1032 | */ | |
1033 | static int | |
1034 | zap_lookup(dnode_end_t *zap_dnode, char *name, uint64_t *val, | |
1035 | struct zfs_data *data) | |
1036 | { | |
1037 | uint64_t block_type; | |
1038 | int size; | |
1039 | void *zapbuf; | |
1040 | int err; | |
1041 | zfs_endian_t endian; | |
1042 | ||
1043 | /* Read in the first block of the zap object data. */ | |
1044 | size = zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, | |
1045 | zap_dnode->endian) << SPA_MINBLOCKSHIFT; | |
1046 | err = dmu_read(zap_dnode, 0, &zapbuf, &endian, data); | |
1047 | if (err) | |
1048 | return err; | |
1049 | block_type = zfs_to_cpu64(*((uint64_t *) zapbuf), endian); | |
1050 | ||
1051 | if (block_type == ZBT_MICRO) { | |
1052 | err = (mzap_lookup(zapbuf, endian, size, name, val)); | |
1053 | free(zapbuf); | |
1054 | return err; | |
1055 | } else if (block_type == ZBT_HEADER) { | |
1056 | /* this is a fat zap */ | |
1057 | err = (fzap_lookup(zap_dnode, zapbuf, name, val, data)); | |
1058 | free(zapbuf); | |
1059 | return err; | |
1060 | } | |
1061 | ||
1062 | printf("unknown ZAP type\n"); | |
1063 | return ZFS_ERR_BAD_FS; | |
1064 | } | |
1065 | ||
1066 | static int | |
1067 | zap_iterate(dnode_end_t *zap_dnode, | |
1068 | int (*hook)(const char *name, uint64_t val, | |
1069 | struct zfs_data *data), | |
1070 | struct zfs_data *data) | |
1071 | { | |
1072 | uint64_t block_type; | |
1073 | int size; | |
1074 | void *zapbuf; | |
1075 | int err; | |
1076 | int ret; | |
1077 | zfs_endian_t endian; | |
1078 | ||
1079 | /* Read in the first block of the zap object data. */ | |
1080 | size = zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT; | |
1081 | err = dmu_read(zap_dnode, 0, &zapbuf, &endian, data); | |
1082 | if (err) | |
1083 | return 0; | |
1084 | block_type = zfs_to_cpu64(*((uint64_t *) zapbuf), endian); | |
1085 | ||
1086 | if (block_type == ZBT_MICRO) { | |
1087 | ret = mzap_iterate(zapbuf, endian, size, hook, data); | |
1088 | free(zapbuf); | |
1089 | return ret; | |
1090 | } else if (block_type == ZBT_HEADER) { | |
1091 | /* this is a fat zap */ | |
1092 | ret = fzap_iterate(zap_dnode, zapbuf, hook, data); | |
1093 | free(zapbuf); | |
1094 | return ret; | |
1095 | } | |
1096 | printf("unknown ZAP type\n"); | |
1097 | return 0; | |
1098 | } | |
1099 | ||
1100 | ||
1101 | /* | |
1102 | * Get the dnode of an object number from the metadnode of an object set. | |
1103 | * | |
1104 | * Input | |
1105 | * mdn - metadnode to get the object dnode | |
1106 | * objnum - object number for the object dnode | |
1107 | * buf - data buffer that holds the returning dnode | |
1108 | */ | |
1109 | static int | |
1110 | dnode_get(dnode_end_t *mdn, uint64_t objnum, uint8_t type, | |
1111 | dnode_end_t *buf, struct zfs_data *data) | |
1112 | { | |
1113 | uint64_t blkid, blksz; /* the block id this object dnode is in */ | |
1114 | int epbs; /* shift of number of dnodes in a block */ | |
1115 | int idx; /* index within a block */ | |
1116 | void *dnbuf; | |
1117 | int err; | |
1118 | zfs_endian_t endian; | |
1119 | ||
1120 | blksz = zfs_to_cpu16(mdn->dn.dn_datablkszsec, | |
1121 | mdn->endian) << SPA_MINBLOCKSHIFT; | |
1122 | ||
1123 | epbs = zfs_log2(blksz) - DNODE_SHIFT; | |
1124 | blkid = objnum >> epbs; | |
1125 | idx = objnum & ((1 << epbs) - 1); | |
1126 | ||
1127 | if (data->dnode_buf != NULL && memcmp(data->dnode_mdn, mdn, | |
1128 | sizeof(*mdn)) == 0 | |
1129 | && objnum >= data->dnode_start && objnum < data->dnode_end) { | |
1130 | memmove(&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE); | |
1131 | buf->endian = data->dnode_endian; | |
1132 | if (type && buf->dn.dn_type != type) { | |
1133 | printf("incorrect dnode type: %02X != %02x\n", buf->dn.dn_type, type); | |
1134 | return ZFS_ERR_BAD_FS; | |
1135 | } | |
1136 | return ZFS_ERR_NONE; | |
1137 | } | |
1138 | ||
1139 | err = dmu_read(mdn, blkid, &dnbuf, &endian, data); | |
1140 | if (err) | |
1141 | return err; | |
1142 | ||
1143 | free(data->dnode_buf); | |
1144 | free(data->dnode_mdn); | |
1145 | data->dnode_mdn = malloc(sizeof(*mdn)); | |
1146 | if (!data->dnode_mdn) { | |
1147 | data->dnode_buf = 0; | |
1148 | } else { | |
1149 | memcpy(data->dnode_mdn, mdn, sizeof(*mdn)); | |
1150 | data->dnode_buf = dnbuf; | |
1151 | data->dnode_start = blkid << epbs; | |
1152 | data->dnode_end = (blkid + 1) << epbs; | |
1153 | data->dnode_endian = endian; | |
1154 | } | |
1155 | ||
1156 | memmove(&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); | |
1157 | buf->endian = endian; | |
1158 | if (type && buf->dn.dn_type != type) { | |
1159 | printf("incorrect dnode type\n"); | |
1160 | return ZFS_ERR_BAD_FS; | |
1161 | } | |
1162 | ||
1163 | return ZFS_ERR_NONE; | |
1164 | } | |
1165 | ||
1166 | /* | |
1167 | * Get the file dnode for a given file name where mdn is the meta dnode | |
1168 | * for this ZFS object set. When found, place the file dnode in dn. | |
1169 | * The 'path' argument will be mangled. | |
1170 | * | |
1171 | */ | |
1172 | static int | |
1173 | dnode_get_path(dnode_end_t *mdn, const char *path_in, dnode_end_t *dn, | |
1174 | struct zfs_data *data) | |
1175 | { | |
1176 | uint64_t objnum, version; | |
1177 | char *cname, ch; | |
1178 | int err = ZFS_ERR_NONE; | |
1179 | char *path, *path_buf; | |
1180 | struct dnode_chain { | |
1181 | struct dnode_chain *next; | |
1182 | dnode_end_t dn; | |
1183 | }; | |
1184 | struct dnode_chain *dnode_path = 0, *dn_new, *root; | |
1185 | ||
1186 | dn_new = malloc(sizeof(*dn_new)); | |
1187 | if (!dn_new) | |
1188 | return ZFS_ERR_OUT_OF_MEMORY; | |
1189 | dn_new->next = 0; | |
1190 | dnode_path = root = dn_new; | |
1191 | ||
1192 | err = dnode_get(mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, | |
1193 | &(dnode_path->dn), data); | |
1194 | if (err) { | |
1195 | free(dn_new); | |
1196 | return err; | |
1197 | } | |
1198 | ||
1199 | err = zap_lookup(&(dnode_path->dn), ZPL_VERSION_STR, &version, data); | |
1200 | if (err) { | |
1201 | free(dn_new); | |
1202 | return err; | |
1203 | } | |
1204 | if (version > ZPL_VERSION) { | |
1205 | free(dn_new); | |
1206 | printf("too new ZPL version\n"); | |
1207 | return ZFS_ERR_NOT_IMPLEMENTED_YET; | |
1208 | } | |
1209 | ||
1210 | err = zap_lookup(&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data); | |
1211 | if (err) { | |
1212 | free(dn_new); | |
1213 | return err; | |
1214 | } | |
1215 | ||
1216 | err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data); | |
1217 | if (err) { | |
1218 | free(dn_new); | |
1219 | return err; | |
1220 | } | |
1221 | ||
1222 | path = path_buf = strdup(path_in); | |
1223 | if (!path_buf) { | |
1224 | free(dn_new); | |
1225 | return ZFS_ERR_OUT_OF_MEMORY; | |
1226 | } | |
1227 | ||
1228 | while (1) { | |
1229 | /* skip leading slashes */ | |
1230 | while (*path == '/') | |
1231 | path++; | |
1232 | if (!*path) | |
1233 | break; | |
1234 | /* get the next component name */ | |
1235 | cname = path; | |
1236 | while (*path && *path != '/') | |
1237 | path++; | |
1238 | /* Skip dot. */ | |
1239 | if (cname + 1 == path && cname[0] == '.') | |
1240 | continue; | |
1241 | /* Handle double dot. */ | |
1242 | if (cname + 2 == path && cname[0] == '.' && cname[1] == '.') { | |
1243 | if (dn_new->next) { | |
1244 | dn_new = dnode_path; | |
1245 | dnode_path = dn_new->next; | |
1246 | free(dn_new); | |
1247 | } else { | |
1248 | printf("can't resolve ..\n"); | |
1249 | err = ZFS_ERR_FILE_NOT_FOUND; | |
1250 | break; | |
1251 | } | |
1252 | continue; | |
1253 | } | |
1254 | ||
1255 | ch = *path; | |
1256 | *path = 0; /* ensure null termination */ | |
1257 | ||
1258 | if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) { | |
1259 | free(path_buf); | |
1260 | printf("not a directory\n"); | |
1261 | return ZFS_ERR_BAD_FILE_TYPE; | |
1262 | } | |
1263 | err = zap_lookup(&(dnode_path->dn), cname, &objnum, data); | |
1264 | if (err) | |
1265 | break; | |
1266 | ||
1267 | dn_new = malloc(sizeof(*dn_new)); | |
1268 | if (!dn_new) { | |
1269 | err = ZFS_ERR_OUT_OF_MEMORY; | |
1270 | break; | |
1271 | } | |
1272 | dn_new->next = dnode_path; | |
1273 | dnode_path = dn_new; | |
1274 | ||
1275 | objnum = ZFS_DIRENT_OBJ(objnum); | |
1276 | err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data); | |
1277 | if (err) | |
1278 | break; | |
1279 | ||
1280 | *path = ch; | |
1281 | } | |
1282 | ||
1283 | if (!err) | |
1284 | memcpy(dn, &(dnode_path->dn), sizeof(*dn)); | |
1285 | ||
1286 | while (dnode_path) { | |
1287 | dn_new = dnode_path->next; | |
1288 | free(dnode_path); | |
1289 | dnode_path = dn_new; | |
1290 | } | |
1291 | free(path_buf); | |
1292 | return err; | |
1293 | } | |
1294 | ||
1295 | ||
1296 | /* | |
1297 | * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), | |
1298 | * e.g. pool/rootfs, or a given object number (obj), e.g. the object number | |
1299 | * of pool/rootfs. | |
1300 | * | |
1301 | * If no fsname and no obj are given, return the DSL_DIR metadnode. | |
1302 | * If fsname is given, return its metadnode and its matching object number. | |
1303 | * If only obj is given, return the metadnode for this object number. | |
1304 | * | |
1305 | */ | |
1306 | static int | |
1307 | get_filesystem_dnode(dnode_end_t *mosmdn, char *fsname, | |
1308 | dnode_end_t *mdn, struct zfs_data *data) | |
1309 | { | |
1310 | uint64_t objnum; | |
1311 | int err; | |
1312 | ||
1313 | err = dnode_get(mosmdn, DMU_POOL_DIRECTORY_OBJECT, | |
1314 | DMU_OT_OBJECT_DIRECTORY, mdn, data); | |
1315 | if (err) | |
1316 | return err; | |
1317 | ||
1318 | err = zap_lookup(mdn, DMU_POOL_ROOT_DATASET, &objnum, data); | |
1319 | if (err) | |
1320 | return err; | |
1321 | ||
1322 | err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); | |
1323 | if (err) | |
1324 | return err; | |
1325 | ||
1326 | while (*fsname) { | |
1327 | uint64_t childobj; | |
1328 | char *cname, ch; | |
1329 | ||
1330 | while (*fsname == '/') | |
1331 | fsname++; | |
1332 | ||
1333 | if (!*fsname || *fsname == '@') | |
1334 | break; | |
1335 | ||
1336 | cname = fsname; | |
1337 | while (*fsname && !isspace(*fsname) && *fsname != '/') | |
1338 | fsname++; | |
1339 | ch = *fsname; | |
1340 | *fsname = 0; | |
1341 | ||
1342 | childobj = zfs_to_cpu64((((dsl_dir_phys_t *) DN_BONUS(&mdn->dn)))->dd_child_dir_zapobj, mdn->endian); | |
1343 | err = dnode_get(mosmdn, childobj, | |
1344 | DMU_OT_DSL_DIR_CHILD_MAP, mdn, data); | |
1345 | if (err) | |
1346 | return err; | |
1347 | ||
1348 | err = zap_lookup(mdn, cname, &objnum, data); | |
1349 | if (err) | |
1350 | return err; | |
1351 | ||
1352 | err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); | |
1353 | if (err) | |
1354 | return err; | |
1355 | ||
1356 | *fsname = ch; | |
1357 | } | |
1358 | return ZFS_ERR_NONE; | |
1359 | } | |
1360 | ||
1361 | static int | |
1362 | make_mdn(dnode_end_t *mdn, struct zfs_data *data) | |
1363 | { | |
1364 | void *osp; | |
1365 | blkptr_t *bp; | |
1366 | size_t ospsize; | |
1367 | int err; | |
1368 | ||
1369 | bp = &(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_bp); | |
1370 | err = zio_read(bp, mdn->endian, &osp, &ospsize, data); | |
1371 | if (err) | |
1372 | return err; | |
1373 | if (ospsize < OBJSET_PHYS_SIZE_V14) { | |
1374 | free(osp); | |
1375 | printf("too small osp\n"); | |
1376 | return ZFS_ERR_BAD_FS; | |
1377 | } | |
1378 | ||
1379 | mdn->endian = (zfs_to_cpu64(bp->blk_prop, mdn->endian)>>63) & 1; | |
1380 | memmove((char *) &(mdn->dn), | |
1381 | (char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); | |
1382 | free(osp); | |
1383 | return ZFS_ERR_NONE; | |
1384 | } | |
1385 | ||
1386 | static int | |
1387 | dnode_get_fullpath(const char *fullpath, dnode_end_t *mdn, | |
1388 | uint64_t *mdnobj, dnode_end_t *dn, int *isfs, | |
1389 | struct zfs_data *data) | |
1390 | { | |
1391 | char *fsname, *snapname; | |
1392 | const char *ptr_at, *filename; | |
1393 | uint64_t headobj; | |
1394 | int err; | |
1395 | ||
1396 | ptr_at = strchr(fullpath, '@'); | |
1397 | if (!ptr_at) { | |
1398 | *isfs = 1; | |
1399 | filename = 0; | |
1400 | snapname = 0; | |
1401 | fsname = strdup(fullpath); | |
1402 | } else { | |
1403 | const char *ptr_slash = strchr(ptr_at, '/'); | |
1404 | ||
1405 | *isfs = 0; | |
1406 | fsname = malloc(ptr_at - fullpath + 1); | |
1407 | if (!fsname) | |
1408 | return ZFS_ERR_OUT_OF_MEMORY; | |
1409 | memcpy(fsname, fullpath, ptr_at - fullpath); | |
1410 | fsname[ptr_at - fullpath] = 0; | |
1411 | if (ptr_at[1] && ptr_at[1] != '/') { | |
1412 | snapname = malloc(ptr_slash - ptr_at); | |
1413 | if (!snapname) { | |
1414 | free(fsname); | |
1415 | return ZFS_ERR_OUT_OF_MEMORY; | |
1416 | } | |
1417 | memcpy(snapname, ptr_at + 1, ptr_slash - ptr_at - 1); | |
1418 | snapname[ptr_slash - ptr_at - 1] = 0; | |
1419 | } else { | |
1420 | snapname = 0; | |
1421 | } | |
1422 | if (ptr_slash) | |
1423 | filename = ptr_slash; | |
1424 | else | |
1425 | filename = "/"; | |
1426 | printf("zfs fsname = '%s' snapname='%s' filename = '%s'\n", | |
1427 | fsname, snapname, filename); | |
1428 | } | |
1429 | ||
1430 | ||
1431 | err = get_filesystem_dnode(&(data->mos), fsname, dn, data); | |
1432 | ||
1433 | if (err) { | |
1434 | free(fsname); | |
1435 | free(snapname); | |
1436 | return err; | |
1437 | } | |
1438 | ||
1439 | headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&dn->dn))->dd_head_dataset_obj, dn->endian); | |
1440 | ||
1441 | err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data); | |
1442 | if (err) { | |
1443 | free(fsname); | |
1444 | free(snapname); | |
1445 | return err; | |
1446 | } | |
1447 | ||
1448 | if (snapname) { | |
1449 | uint64_t snapobj; | |
1450 | ||
1451 | snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_snapnames_zapobj, mdn->endian); | |
1452 | ||
1453 | err = dnode_get(&(data->mos), snapobj, | |
1454 | DMU_OT_DSL_DS_SNAP_MAP, mdn, data); | |
1455 | if (!err) | |
1456 | err = zap_lookup(mdn, snapname, &headobj, data); | |
1457 | if (!err) | |
1458 | err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data); | |
1459 | if (err) { | |
1460 | free(fsname); | |
1461 | free(snapname); | |
1462 | return err; | |
1463 | } | |
1464 | } | |
1465 | ||
1466 | if (mdnobj) | |
1467 | *mdnobj = headobj; | |
1468 | ||
1469 | make_mdn(mdn, data); | |
1470 | ||
1471 | if (*isfs) { | |
1472 | free(fsname); | |
1473 | free(snapname); | |
1474 | return ZFS_ERR_NONE; | |
1475 | } | |
1476 | err = dnode_get_path(mdn, filename, dn, data); | |
1477 | free(fsname); | |
1478 | free(snapname); | |
1479 | return err; | |
1480 | } | |
1481 | ||
1482 | /* | |
1483 | * For a given XDR packed nvlist, verify the first 4 bytes and move on. | |
1484 | * | |
1485 | * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) : | |
1486 | * | |
1487 | * encoding method/host endian (4 bytes) | |
1488 | * nvl_version (4 bytes) | |
1489 | * nvl_nvflag (4 bytes) | |
1490 | * encoded nvpairs: | |
1491 | * encoded size of the nvpair (4 bytes) | |
1492 | * decoded size of the nvpair (4 bytes) | |
1493 | * name string size (4 bytes) | |
1494 | * name string data (sizeof(NV_ALIGN4(string)) | |
1495 | * data type (4 bytes) | |
1496 | * # of elements in the nvpair (4 bytes) | |
1497 | * data | |
1498 | * 2 zero's for the last nvpair | |
1499 | * (end of the entire list) (8 bytes) | |
1500 | * | |
1501 | */ | |
1502 | ||
1503 | static int | |
1504 | nvlist_find_value(char *nvlist, char *name, int valtype, char **val, | |
1505 | size_t *size_out, size_t *nelm_out) | |
1506 | { | |
1507 | int name_len, type, encode_size; | |
1508 | char *nvpair, *nvp_name; | |
1509 | ||
1510 | /* Verify if the 1st and 2nd byte in the nvlist are valid. */ | |
1511 | /* NOTE: independently of what endianness header announces all | |
1512 | subsequent values are big-endian. */ | |
1513 | if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN | |
1514 | && nvlist[1] != NV_BIG_ENDIAN)) { | |
1515 | printf("zfs incorrect nvlist header\n"); | |
1516 | return ZFS_ERR_BAD_FS; | |
1517 | } | |
1518 | ||
1519 | /* skip the header, nvl_version, and nvl_nvflag */ | |
1520 | nvlist = nvlist + 4 * 3; | |
1521 | /* | |
1522 | * Loop thru the nvpair list | |
1523 | * The XDR representation of an integer is in big-endian byte order. | |
1524 | */ | |
1525 | while ((encode_size = be32_to_cpu(*(uint32_t *) nvlist))) { | |
1526 | int nelm; | |
1527 | ||
1528 | nvpair = nvlist + 4 * 2; /* skip the encode/decode size */ | |
1529 | ||
1530 | name_len = be32_to_cpu(*(uint32_t *) nvpair); | |
1531 | nvpair += 4; | |
1532 | ||
1533 | nvp_name = nvpair; | |
1534 | nvpair = nvpair + ((name_len + 3) & ~3); /* align */ | |
1535 | ||
1536 | type = be32_to_cpu(*(uint32_t *) nvpair); | |
1537 | nvpair += 4; | |
1538 | ||
1539 | nelm = be32_to_cpu(*(uint32_t *) nvpair); | |
1540 | if (nelm < 1) { | |
1541 | printf("empty nvpair\n"); | |
1542 | return ZFS_ERR_BAD_FS; | |
1543 | } | |
1544 | ||
1545 | nvpair += 4; | |
1546 | ||
1547 | if ((strncmp(nvp_name, name, name_len) == 0) && type == valtype) { | |
1548 | *val = nvpair; | |
1549 | *size_out = encode_size; | |
1550 | if (nelm_out) | |
1551 | *nelm_out = nelm; | |
1552 | return 1; | |
1553 | } | |
1554 | ||
1555 | nvlist += encode_size; /* goto the next nvpair */ | |
1556 | } | |
1557 | return 0; | |
1558 | } | |
1559 | ||
1560 | int | |
1561 | zfs_nvlist_lookup_uint64(char *nvlist, char *name, uint64_t *out) | |
1562 | { | |
1563 | char *nvpair; | |
1564 | size_t size; | |
1565 | int found; | |
1566 | ||
1567 | found = nvlist_find_value(nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0); | |
1568 | if (!found) | |
1569 | return 0; | |
1570 | if (size < sizeof(uint64_t)) { | |
1571 | printf("invalid uint64\n"); | |
1572 | return ZFS_ERR_BAD_FS; | |
1573 | } | |
1574 | ||
1575 | *out = be64_to_cpu(*(uint64_t *) nvpair); | |
1576 | return 1; | |
1577 | } | |
1578 | ||
1579 | char * | |
1580 | zfs_nvlist_lookup_string(char *nvlist, char *name) | |
1581 | { | |
1582 | char *nvpair; | |
1583 | char *ret; | |
1584 | size_t slen; | |
1585 | size_t size; | |
1586 | int found; | |
1587 | ||
1588 | found = nvlist_find_value(nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0); | |
1589 | if (!found) | |
1590 | return 0; | |
1591 | if (size < 4) { | |
1592 | printf("invalid string\n"); | |
1593 | return 0; | |
1594 | } | |
1595 | slen = be32_to_cpu(*(uint32_t *) nvpair); | |
1596 | if (slen > size - 4) | |
1597 | slen = size - 4; | |
1598 | ret = malloc(slen + 1); | |
1599 | if (!ret) | |
1600 | return 0; | |
1601 | memcpy(ret, nvpair + 4, slen); | |
1602 | ret[slen] = 0; | |
1603 | return ret; | |
1604 | } | |
1605 | ||
1606 | char * | |
1607 | zfs_nvlist_lookup_nvlist(char *nvlist, char *name) | |
1608 | { | |
1609 | char *nvpair; | |
1610 | char *ret; | |
1611 | size_t size; | |
1612 | int found; | |
1613 | ||
1614 | found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, | |
1615 | &size, 0); | |
1616 | if (!found) | |
1617 | return 0; | |
1618 | ret = calloc(1, size + 3 * sizeof(uint32_t)); | |
1619 | if (!ret) | |
1620 | return 0; | |
1621 | memcpy(ret, nvlist, sizeof(uint32_t)); | |
1622 | ||
1623 | memcpy(ret + sizeof(uint32_t), nvpair, size); | |
1624 | return ret; | |
1625 | } | |
1626 | ||
1627 | int | |
1628 | zfs_nvlist_lookup_nvlist_array_get_nelm(char *nvlist, char *name) | |
1629 | { | |
1630 | char *nvpair; | |
1631 | size_t nelm, size; | |
1632 | int found; | |
1633 | ||
1634 | found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, | |
1635 | &size, &nelm); | |
1636 | if (!found) | |
1637 | return -1; | |
1638 | return nelm; | |
1639 | } | |
1640 | ||
1641 | char * | |
1642 | zfs_nvlist_lookup_nvlist_array(char *nvlist, char *name, | |
1643 | size_t index) | |
1644 | { | |
1645 | char *nvpair, *nvpairptr; | |
1646 | int found; | |
1647 | char *ret; | |
1648 | size_t size; | |
1649 | unsigned i; | |
1650 | size_t nelm; | |
1651 | ||
1652 | found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, | |
1653 | &size, &nelm); | |
1654 | if (!found) | |
1655 | return 0; | |
1656 | if (index >= nelm) { | |
1657 | printf("trying to lookup past nvlist array\n"); | |
1658 | return 0; | |
1659 | } | |
1660 | ||
1661 | nvpairptr = nvpair; | |
1662 | ||
1663 | for (i = 0; i < index; i++) { | |
1664 | uint32_t encode_size; | |
1665 | ||
1666 | /* skip the header, nvl_version, and nvl_nvflag */ | |
1667 | nvpairptr = nvpairptr + 4 * 2; | |
1668 | ||
1669 | while (nvpairptr < nvpair + size | |
1670 | && (encode_size = be32_to_cpu(*(uint32_t *) nvpairptr))) | |
1671 | nvlist += encode_size; /* goto the next nvpair */ | |
1672 | ||
1673 | nvlist = nvlist + 4 * 2; /* skip the ending 2 zeros - 8 bytes */ | |
1674 | } | |
1675 | ||
1676 | if (nvpairptr >= nvpair + size | |
1677 | || nvpairptr + be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2)) | |
1678 | >= nvpair + size) { | |
1679 | printf("incorrect nvlist array\n"); | |
1680 | return 0; | |
1681 | } | |
1682 | ||
1683 | ret = calloc(1, be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2)) | |
1684 | + 3 * sizeof(uint32_t)); | |
1685 | if (!ret) | |
1686 | return 0; | |
1687 | memcpy(ret, nvlist, sizeof(uint32_t)); | |
1688 | ||
1689 | memcpy(ret + sizeof(uint32_t), nvpairptr, size); | |
1690 | return ret; | |
1691 | } | |
1692 | ||
1693 | static int | |
1694 | int_zfs_fetch_nvlist(struct zfs_data *data, char **nvlist) | |
1695 | { | |
1696 | int err; | |
1697 | ||
1698 | *nvlist = malloc(VDEV_PHYS_SIZE); | |
1699 | /* Read in the vdev name-value pair list (112K). */ | |
1700 | err = zfs_devread(data->vdev_phys_sector, 0, VDEV_PHYS_SIZE, *nvlist); | |
1701 | if (err) { | |
1702 | free(*nvlist); | |
1703 | *nvlist = 0; | |
1704 | return err; | |
1705 | } | |
1706 | return ZFS_ERR_NONE; | |
1707 | } | |
1708 | ||
1709 | /* | |
1710 | * Check the disk label information and retrieve needed vdev name-value pairs. | |
1711 | * | |
1712 | */ | |
1713 | static int | |
1714 | check_pool_label(struct zfs_data *data) | |
1715 | { | |
1716 | uint64_t pool_state; | |
1717 | char *nvlist; /* for the pool */ | |
1718 | char *vdevnvlist; /* for the vdev */ | |
1719 | uint64_t diskguid; | |
1720 | uint64_t version; | |
1721 | int found; | |
1722 | int err; | |
1723 | ||
1724 | err = int_zfs_fetch_nvlist(data, &nvlist); | |
1725 | if (err) | |
1726 | return err; | |
1727 | ||
1728 | found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_STATE, | |
1729 | &pool_state); | |
1730 | if (!found) { | |
1731 | free(nvlist); | |
1732 | printf("zfs pool state not found\n"); | |
1733 | return ZFS_ERR_BAD_FS; | |
1734 | } | |
1735 | ||
1736 | if (pool_state == POOL_STATE_DESTROYED) { | |
1737 | free(nvlist); | |
1738 | printf("zpool is marked as destroyed\n"); | |
1739 | return ZFS_ERR_BAD_FS; | |
1740 | } | |
1741 | ||
1742 | data->label_txg = 0; | |
1743 | found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_TXG, | |
1744 | &data->label_txg); | |
1745 | if (!found) { | |
1746 | free(nvlist); | |
1747 | printf("zfs pool txg not found\n"); | |
1748 | return ZFS_ERR_BAD_FS; | |
1749 | } | |
1750 | ||
1751 | /* not an active device */ | |
1752 | if (data->label_txg == 0) { | |
1753 | free(nvlist); | |
1754 | printf("zpool is not active\n"); | |
1755 | return ZFS_ERR_BAD_FS; | |
1756 | } | |
1757 | ||
1758 | found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_VERSION, | |
1759 | &version); | |
1760 | if (!found) { | |
1761 | free(nvlist); | |
1762 | printf("zpool config version not found\n"); | |
1763 | return ZFS_ERR_BAD_FS; | |
1764 | } | |
1765 | ||
1766 | if (version > SPA_VERSION) { | |
1767 | free(nvlist); | |
1768 | printf("SPA version too new %llu > %llu\n", | |
1769 | (unsigned long long) version, | |
1770 | (unsigned long long) SPA_VERSION); | |
1771 | return ZFS_ERR_NOT_IMPLEMENTED_YET; | |
1772 | } | |
1773 | ||
1774 | vdevnvlist = zfs_nvlist_lookup_nvlist(nvlist, ZPOOL_CONFIG_VDEV_TREE); | |
1775 | if (!vdevnvlist) { | |
1776 | free(nvlist); | |
1777 | printf("ZFS config vdev tree not found\n"); | |
1778 | return ZFS_ERR_BAD_FS; | |
1779 | } | |
1780 | ||
1781 | found = zfs_nvlist_lookup_uint64(vdevnvlist, ZPOOL_CONFIG_ASHIFT, | |
1782 | &data->vdev_ashift); | |
1783 | free(vdevnvlist); | |
1784 | if (!found) { | |
1785 | free(nvlist); | |
1786 | printf("ZPOOL config ashift not found\n"); | |
1787 | return ZFS_ERR_BAD_FS; | |
1788 | } | |
1789 | ||
1790 | found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_GUID, &diskguid); | |
1791 | if (!found) { | |
1792 | free(nvlist); | |
1793 | printf("ZPOOL config guid not found\n"); | |
1794 | return ZFS_ERR_BAD_FS; | |
1795 | } | |
1796 | ||
1797 | found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_GUID, &data->pool_guid); | |
1798 | if (!found) { | |
1799 | free(nvlist); | |
1800 | printf("ZPOOL config pool guid not found\n"); | |
1801 | return ZFS_ERR_BAD_FS; | |
1802 | } | |
1803 | ||
1804 | free(nvlist); | |
1805 | ||
1806 | printf("ZFS Pool GUID: %llu (%016llx) Label: GUID: %llu (%016llx), txg: %llu, SPA v%llu, ashift: %llu\n", | |
1807 | (unsigned long long) data->pool_guid, | |
1808 | (unsigned long long) data->pool_guid, | |
1809 | (unsigned long long) diskguid, | |
1810 | (unsigned long long) diskguid, | |
1811 | (unsigned long long) data->label_txg, | |
1812 | (unsigned long long) version, | |
1813 | (unsigned long long) data->vdev_ashift); | |
1814 | ||
1815 | return ZFS_ERR_NONE; | |
1816 | } | |
1817 | ||
1818 | /* | |
1819 | * vdev_label_start returns the physical disk offset (in bytes) of | |
1820 | * label "l". | |
1821 | */ | |
1822 | static uint64_t vdev_label_start(uint64_t psize, int l) | |
1823 | { | |
1824 | return (l * sizeof(vdev_label_t) + (l < VDEV_LABELS / 2 ? | |
1825 | 0 : psize - | |
1826 | VDEV_LABELS * sizeof(vdev_label_t))); | |
1827 | } | |
1828 | ||
1829 | void | |
1830 | zfs_unmount(struct zfs_data *data) | |
1831 | { | |
1832 | free(data->dnode_buf); | |
1833 | free(data->dnode_mdn); | |
1834 | free(data->file_buf); | |
1835 | free(data); | |
1836 | } | |
1837 | ||
1838 | /* | |
1839 | * zfs_mount() locates a valid uberblock of the root pool and read in its MOS | |
1840 | * to the memory address MOS. | |
1841 | * | |
1842 | */ | |
1843 | struct zfs_data * | |
1844 | zfs_mount(device_t dev) | |
1845 | { | |
1846 | struct zfs_data *data = 0; | |
1847 | int label = 0, bestlabel = -1; | |
1848 | char *ub_array; | |
1849 | uberblock_t *ubbest; | |
1850 | uberblock_t *ubcur = NULL; | |
1851 | void *osp = 0; | |
1852 | size_t ospsize; | |
1853 | int err; | |
1854 | ||
1855 | data = malloc(sizeof(*data)); | |
1856 | if (!data) | |
1857 | return 0; | |
1858 | memset(data, 0, sizeof(*data)); | |
1859 | ||
1860 | ub_array = malloc(VDEV_UBERBLOCK_RING); | |
1861 | if (!ub_array) { | |
1862 | zfs_unmount(data); | |
1863 | return 0; | |
1864 | } | |
1865 | ||
1866 | ubbest = malloc(sizeof(*ubbest)); | |
1867 | if (!ubbest) { | |
1868 | zfs_unmount(data); | |
1869 | return 0; | |
1870 | } | |
1871 | memset(ubbest, 0, sizeof(*ubbest)); | |
1872 | ||
1873 | /* | |
1874 | * some eltorito stacks don't give us a size and | |
1875 | * we end up setting the size to MAXUINT, further | |
1876 | * some of these devices stop working once a single | |
1877 | * read past the end has been issued. Checking | |
1878 | * for a maximum part_length and skipping the backup | |
1879 | * labels at the end of the slice/partition/device | |
1880 | * avoids breaking down on such devices. | |
1881 | */ | |
1882 | const int vdevnum = | |
1883 | dev->part_length == 0 ? | |
1884 | VDEV_LABELS / 2 : VDEV_LABELS; | |
1885 | ||
1886 | /* Size in bytes of the device (disk or partition) aligned to label size*/ | |
1887 | uint64_t device_size = | |
1888 | dev->part_length << SECTOR_BITS; | |
1889 | ||
1890 | const uint64_t alignedbytes = | |
1891 | P2ALIGN(device_size, (uint64_t) sizeof(vdev_label_t)); | |
1892 | ||
1893 | for (label = 0; label < vdevnum; label++) { | |
1894 | uint64_t labelstartbytes = vdev_label_start(alignedbytes, label); | |
1895 | uint64_t labelstart = labelstartbytes >> SECTOR_BITS; | |
1896 | ||
1897 | debug("zfs reading label %d at sector %llu (byte %llu)\n", | |
1898 | label, (unsigned long long) labelstart, | |
1899 | (unsigned long long) labelstartbytes); | |
1900 | ||
1901 | data->vdev_phys_sector = labelstart + | |
1902 | ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SECTOR_BITS); | |
1903 | ||
1904 | err = check_pool_label(data); | |
1905 | if (err) { | |
1906 | printf("zfs error checking label %d\n", label); | |
1907 | continue; | |
1908 | } | |
1909 | ||
1910 | /* Read in the uberblock ring (128K). */ | |
1911 | err = zfs_devread(data->vdev_phys_sector + | |
1912 | (VDEV_PHYS_SIZE >> SECTOR_BITS), | |
1913 | 0, VDEV_UBERBLOCK_RING, ub_array); | |
1914 | if (err) { | |
1915 | printf("zfs error reading uberblock ring for label %d\n", label); | |
1916 | continue; | |
1917 | } | |
1918 | ||
1919 | ubcur = find_bestub(ub_array, data); | |
1920 | if (!ubcur) { | |
1921 | printf("zfs No good uberblocks found in label %d\n", label); | |
1922 | continue; | |
1923 | } | |
1924 | ||
1925 | if (vdev_uberblock_compare(ubcur, ubbest) > 0) { | |
1926 | /* Looks like the block is good, so use it.*/ | |
1927 | memcpy(ubbest, ubcur, sizeof(*ubbest)); | |
1928 | bestlabel = label; | |
1929 | debug("zfs Current best uberblock found in label %d\n", label); | |
1930 | } | |
1931 | } | |
1932 | free(ub_array); | |
1933 | ||
1934 | /* We zero'd the structure to begin with. If we never assigned to it, | |
1935 | magic will still be zero. */ | |
1936 | if (!ubbest->ub_magic) { | |
1937 | printf("couldn't find a valid ZFS label\n"); | |
1938 | zfs_unmount(data); | |
1939 | free(ubbest); | |
1940 | return 0; | |
1941 | } | |
1942 | ||
1943 | debug("zfs ubbest %p in label %d\n", ubbest, bestlabel); | |
1944 | ||
1945 | zfs_endian_t ub_endian = | |
1946 | zfs_to_cpu64(ubbest->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC | |
1947 | ? LITTLE_ENDIAN : BIG_ENDIAN; | |
1948 | ||
1949 | debug("zfs endian set to %s\n", !ub_endian ? "big" : "little"); | |
1950 | ||
1951 | err = zio_read(&ubbest->ub_rootbp, ub_endian, &osp, &ospsize, data); | |
1952 | ||
1953 | if (err) { | |
1954 | printf("couldn't zio_read object directory\n"); | |
1955 | zfs_unmount(data); | |
1956 | free(ubbest); | |
1957 | return 0; | |
1958 | } | |
1959 | ||
1960 | if (ospsize < OBJSET_PHYS_SIZE_V14) { | |
1961 | printf("osp too small\n"); | |
1962 | zfs_unmount(data); | |
1963 | free(osp); | |
1964 | free(ubbest); | |
1965 | return 0; | |
1966 | } | |
1967 | ||
1968 | /* Got the MOS. Save it at the memory addr MOS. */ | |
1969 | memmove(&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); | |
1970 | data->mos.endian = | |
1971 | (zfs_to_cpu64(ubbest->ub_rootbp.blk_prop, ub_endian) >> 63) & 1; | |
1972 | memmove(&(data->current_uberblock), ubbest, sizeof(uberblock_t)); | |
1973 | ||
1974 | free(osp); | |
1975 | free(ubbest); | |
1976 | ||
1977 | return data; | |
1978 | } | |
1979 | ||
1980 | int | |
1981 | zfs_fetch_nvlist(device_t dev, char **nvlist) | |
1982 | { | |
1983 | struct zfs_data *zfs; | |
1984 | int err; | |
1985 | ||
1986 | zfs = zfs_mount(dev); | |
1987 | if (!zfs) | |
1988 | return ZFS_ERR_BAD_FS; | |
1989 | err = int_zfs_fetch_nvlist(zfs, nvlist); | |
1990 | zfs_unmount(zfs); | |
1991 | return err; | |
1992 | } | |
1993 | ||
4d3c95f5 JL |
1994 | /* |
1995 | * zfs_open() locates a file in the rootpool by following the | |
1996 | * MOS and places the dnode of the file in the memory address DNODE. | |
1997 | */ | |
1998 | int | |
1999 | zfs_open(struct zfs_file *file, const char *fsfilename) | |
2000 | { | |
2001 | struct zfs_data *data; | |
2002 | int err; | |
2003 | int isfs; | |
2004 | ||
2005 | data = zfs_mount(file->device); | |
2006 | if (!data) | |
2007 | return ZFS_ERR_BAD_FS; | |
2008 | ||
2009 | err = dnode_get_fullpath(fsfilename, &(data->mdn), 0, | |
2010 | &(data->dnode), &isfs, data); | |
2011 | if (err) { | |
2012 | zfs_unmount(data); | |
2013 | return err; | |
2014 | } | |
2015 | ||
2016 | if (isfs) { | |
2017 | zfs_unmount(data); | |
2018 | printf("Missing @ or / separator\n"); | |
2019 | return ZFS_ERR_FILE_NOT_FOUND; | |
2020 | } | |
2021 | ||
2022 | /* We found the dnode for this file. Verify if it is a plain file. */ | |
2023 | if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) { | |
2024 | zfs_unmount(data); | |
2025 | printf("not a file\n"); | |
2026 | return ZFS_ERR_BAD_FILE_TYPE; | |
2027 | } | |
2028 | ||
2029 | /* get the file size and set the file position to 0 */ | |
2030 | ||
2031 | /* | |
2032 | * For DMU_OT_SA we will need to locate the SIZE attribute | |
2033 | * attribute, which could be either in the bonus buffer | |
2034 | * or the "spill" block. | |
2035 | */ | |
2036 | if (data->dnode.dn.dn_bonustype == DMU_OT_SA) { | |
2037 | void *sahdrp; | |
2038 | int hdrsize; | |
2039 | ||
2040 | if (data->dnode.dn.dn_bonuslen != 0) { | |
2041 | sahdrp = (sa_hdr_phys_t *) DN_BONUS(&data->dnode.dn); | |
2042 | } else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) { | |
2043 | blkptr_t *bp = &data->dnode.dn.dn_spill; | |
2044 | ||
2045 | err = zio_read(bp, data->dnode.endian, &sahdrp, NULL, data); | |
2046 | if (err) | |
2047 | return err; | |
2048 | } else { | |
2049 | printf("filesystem is corrupt :(\n"); | |
2050 | return ZFS_ERR_BAD_FS; | |
2051 | } | |
2052 | ||
2053 | hdrsize = SA_HDR_SIZE(((sa_hdr_phys_t *) sahdrp)); | |
2054 | file->size = *(uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET); | |
2055 | } else { | |
2056 | file->size = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&data->dnode.dn))->zp_size, data->dnode.endian); | |
2057 | } | |
2058 | ||
2059 | file->data = data; | |
2060 | file->offset = 0; | |
2061 | ||
2062 | return ZFS_ERR_NONE; | |
2063 | } | |
2064 | ||
2065 | uint64_t | |
2066 | zfs_read(zfs_file_t file, char *buf, uint64_t len) | |
2067 | { | |
2068 | struct zfs_data *data = (struct zfs_data *) file->data; | |
2069 | int blksz, movesize; | |
2070 | uint64_t length; | |
2071 | int64_t red; | |
2072 | int err; | |
2073 | ||
2074 | if (data->file_buf == NULL) { | |
2075 | data->file_buf = malloc(SPA_MAXBLOCKSIZE); | |
2076 | if (!data->file_buf) | |
2077 | return -1; | |
2078 | data->file_start = data->file_end = 0; | |
2079 | } | |
2080 | ||
2081 | /* | |
2082 | * If offset is in memory, move it into the buffer provided and return. | |
2083 | */ | |
2084 | if (file->offset >= data->file_start | |
2085 | && file->offset + len <= data->file_end) { | |
2086 | memmove(buf, data->file_buf + file->offset - data->file_start, | |
2087 | len); | |
2088 | return len; | |
2089 | } | |
2090 | ||
2091 | blksz = zfs_to_cpu16(data->dnode.dn.dn_datablkszsec, | |
2092 | data->dnode.endian) << SPA_MINBLOCKSHIFT; | |
2093 | ||
2094 | /* | |
2095 | * Entire Dnode is too big to fit into the space available. We | |
2096 | * will need to read it in chunks. This could be optimized to | |
2097 | * read in as large a chunk as there is space available, but for | |
2098 | * now, this only reads in one data block at a time. | |
2099 | */ | |
2100 | length = len; | |
2101 | red = 0; | |
2102 | while (length) { | |
2103 | void *t; | |
2104 | /* | |
2105 | * Find requested blkid and the offset within that block. | |
2106 | */ | |
624c721f AM |
2107 | uint64_t blkid = file->offset + red; |
2108 | blkid = do_div(blkid, blksz); | |
4d3c95f5 JL |
2109 | free(data->file_buf); |
2110 | data->file_buf = 0; | |
2111 | ||
2112 | err = dmu_read(&(data->dnode), blkid, &t, | |
2113 | 0, data); | |
2114 | data->file_buf = t; | |
2115 | if (err) | |
2116 | return -1; | |
2117 | ||
2118 | data->file_start = blkid * blksz; | |
2119 | data->file_end = data->file_start + blksz; | |
2120 | ||
2121 | movesize = MIN(length, data->file_end - (int) file->offset - red); | |
2122 | ||
2123 | memmove(buf, data->file_buf + file->offset + red | |
2124 | - data->file_start, movesize); | |
2125 | buf += movesize; | |
2126 | length -= movesize; | |
2127 | red += movesize; | |
2128 | } | |
2129 | ||
2130 | return len; | |
2131 | } | |
2132 | ||
2133 | int | |
2134 | zfs_close(zfs_file_t file) | |
2135 | { | |
2136 | zfs_unmount((struct zfs_data *) file->data); | |
2137 | return ZFS_ERR_NONE; | |
2138 | } | |
2139 | ||
2140 | int | |
2141 | zfs_getmdnobj(device_t dev, const char *fsfilename, | |
2142 | uint64_t *mdnobj) | |
2143 | { | |
2144 | struct zfs_data *data; | |
2145 | int err; | |
2146 | int isfs; | |
2147 | ||
2148 | data = zfs_mount(dev); | |
2149 | if (!data) | |
2150 | return ZFS_ERR_BAD_FS; | |
2151 | ||
2152 | err = dnode_get_fullpath(fsfilename, &(data->mdn), mdnobj, | |
2153 | &(data->dnode), &isfs, data); | |
2154 | zfs_unmount(data); | |
2155 | return err; | |
2156 | } | |
2157 | ||
2158 | static void | |
2159 | fill_fs_info(struct zfs_dirhook_info *info, | |
2160 | dnode_end_t mdn, struct zfs_data *data) | |
2161 | { | |
2162 | int err; | |
2163 | dnode_end_t dn; | |
2164 | uint64_t objnum; | |
2165 | uint64_t headobj; | |
2166 | ||
2167 | memset(info, 0, sizeof(*info)); | |
2168 | ||
2169 | info->dir = 1; | |
2170 | ||
2171 | if (mdn.dn.dn_type == DMU_OT_DSL_DIR) { | |
2172 | headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&mdn.dn))->dd_head_dataset_obj, mdn.endian); | |
2173 | ||
2174 | err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &mdn, data); | |
2175 | if (err) { | |
2176 | printf("zfs failed here 1\n"); | |
2177 | return; | |
2178 | } | |
2179 | } | |
2180 | make_mdn(&mdn, data); | |
2181 | err = dnode_get(&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, | |
2182 | &dn, data); | |
2183 | if (err) { | |
2184 | printf("zfs failed here 2\n"); | |
2185 | return; | |
2186 | } | |
2187 | ||
2188 | err = zap_lookup(&dn, ZFS_ROOT_OBJ, &objnum, data); | |
2189 | if (err) { | |
2190 | printf("zfs failed here 3\n"); | |
2191 | return; | |
2192 | } | |
2193 | ||
2194 | err = dnode_get(&mdn, objnum, 0, &dn, data); | |
2195 | if (err) { | |
2196 | printf("zfs failed here 4\n"); | |
2197 | return; | |
2198 | } | |
2199 | ||
2200 | info->mtimeset = 1; | |
2201 | info->mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian); | |
2202 | ||
2203 | return; | |
2204 | } | |
2205 | ||
2206 | static int iterate_zap(const char *name, uint64_t val, struct zfs_data *data) | |
2207 | { | |
2208 | struct zfs_dirhook_info info; | |
2209 | dnode_end_t dn; | |
2210 | ||
2211 | memset(&info, 0, sizeof(info)); | |
2212 | ||
2213 | dnode_get(&(data->mdn), val, 0, &dn, data); | |
2214 | info.mtimeset = 1; | |
2215 | info.mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian); | |
2216 | info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); | |
2217 | debug("zfs type=%d, name=%s\n", | |
2218 | (int)dn.dn.dn_type, (char *)name); | |
2219 | if (!data->userhook) | |
2220 | return 0; | |
2221 | return data->userhook(name, &info); | |
2222 | } | |
2223 | ||
2224 | static int iterate_zap_fs(const char *name, uint64_t val, struct zfs_data *data) | |
2225 | { | |
2226 | struct zfs_dirhook_info info; | |
2227 | dnode_end_t mdn; | |
2228 | int err; | |
2229 | err = dnode_get(&(data->mos), val, 0, &mdn, data); | |
2230 | if (err) | |
2231 | return 0; | |
2232 | if (mdn.dn.dn_type != DMU_OT_DSL_DIR) | |
2233 | return 0; | |
2234 | ||
2235 | fill_fs_info(&info, mdn, data); | |
2236 | ||
2237 | if (!data->userhook) | |
2238 | return 0; | |
2239 | return data->userhook(name, &info); | |
2240 | } | |
2241 | ||
2242 | static int iterate_zap_snap(const char *name, uint64_t val, struct zfs_data *data) | |
2243 | { | |
2244 | struct zfs_dirhook_info info; | |
2245 | char *name2; | |
2246 | int ret = 0; | |
2247 | dnode_end_t mdn; | |
2248 | int err; | |
2249 | ||
2250 | err = dnode_get(&(data->mos), val, 0, &mdn, data); | |
2251 | if (err) | |
2252 | return 0; | |
2253 | ||
2254 | if (mdn.dn.dn_type != DMU_OT_DSL_DATASET) | |
2255 | return 0; | |
2256 | ||
2257 | fill_fs_info(&info, mdn, data); | |
2258 | ||
2259 | name2 = malloc(strlen(name) + 2); | |
2260 | name2[0] = '@'; | |
2261 | memcpy(name2 + 1, name, strlen(name) + 1); | |
2262 | if (data->userhook) | |
2263 | ret = data->userhook(name2, &info); | |
2264 | free(name2); | |
2265 | return ret; | |
2266 | } | |
2267 | ||
2268 | int | |
2269 | zfs_ls(device_t device, const char *path, | |
2270 | int (*hook)(const char *, const struct zfs_dirhook_info *)) | |
2271 | { | |
2272 | struct zfs_data *data; | |
2273 | int err; | |
2274 | int isfs; | |
4d3c95f5 JL |
2275 | |
2276 | data = zfs_mount(device); | |
2277 | if (!data) | |
2278 | return ZFS_ERR_BAD_FS; | |
2279 | ||
2280 | data->userhook = hook; | |
2281 | ||
2282 | err = dnode_get_fullpath(path, &(data->mdn), 0, &(data->dnode), &isfs, data); | |
2283 | if (err) { | |
2284 | zfs_unmount(data); | |
2285 | return err; | |
2286 | } | |
2287 | if (isfs) { | |
2288 | uint64_t childobj, headobj; | |
2289 | uint64_t snapobj; | |
2290 | dnode_end_t dn; | |
2291 | struct zfs_dirhook_info info; | |
2292 | ||
2293 | fill_fs_info(&info, data->dnode, data); | |
2294 | hook("@", &info); | |
2295 | ||
2296 | childobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian); | |
2297 | headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian); | |
2298 | err = dnode_get(&(data->mos), childobj, | |
2299 | DMU_OT_DSL_DIR_CHILD_MAP, &dn, data); | |
2300 | if (err) { | |
2301 | zfs_unmount(data); | |
2302 | return err; | |
2303 | } | |
2304 | ||
2305 | ||
2306 | zap_iterate(&dn, iterate_zap_fs, data); | |
2307 | ||
2308 | err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data); | |
2309 | if (err) { | |
2310 | zfs_unmount(data); | |
2311 | return err; | |
2312 | } | |
2313 | ||
2314 | snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&dn.dn))->ds_snapnames_zapobj, dn.endian); | |
2315 | ||
2316 | err = dnode_get(&(data->mos), snapobj, | |
2317 | DMU_OT_DSL_DS_SNAP_MAP, &dn, data); | |
2318 | if (err) { | |
2319 | zfs_unmount(data); | |
2320 | return err; | |
2321 | } | |
2322 | ||
2323 | zap_iterate(&dn, iterate_zap_snap, data); | |
2324 | } else { | |
2325 | if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) { | |
2326 | zfs_unmount(data); | |
2327 | printf("not a directory\n"); | |
2328 | return ZFS_ERR_BAD_FILE_TYPE; | |
2329 | } | |
2330 | zap_iterate(&(data->dnode), iterate_zap, data); | |
2331 | } | |
2332 | zfs_unmount(data); | |
2333 | return ZFS_ERR_NONE; | |
2334 | } |