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