14d3c95f5SJorgen Lundman /* 24d3c95f5SJorgen Lundman * 34d3c95f5SJorgen Lundman * ZFS filesystem ported to u-boot by 44d3c95f5SJorgen Lundman * Jorgen Lundman <lundman at lundman.net> 54d3c95f5SJorgen Lundman * 64d3c95f5SJorgen Lundman * GRUB -- GRand Unified Bootloader 74d3c95f5SJorgen Lundman * Copyright (C) 1999,2000,2001,2002,2003,2004 84d3c95f5SJorgen Lundman * Free Software Foundation, Inc. 94d3c95f5SJorgen Lundman * Copyright 2004 Sun Microsystems, Inc. 104d3c95f5SJorgen Lundman * 111a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+ 124d3c95f5SJorgen Lundman */ 134d3c95f5SJorgen Lundman 144d3c95f5SJorgen Lundman #include <common.h> 154d3c95f5SJorgen Lundman #include <malloc.h> 164d3c95f5SJorgen Lundman #include <linux/stat.h> 174d3c95f5SJorgen Lundman #include <linux/time.h> 184d3c95f5SJorgen Lundman #include <linux/ctype.h> 194d3c95f5SJorgen Lundman #include <asm/byteorder.h> 204d3c95f5SJorgen Lundman #include "zfs_common.h" 21624c721fSAlejandro Mery #include "div64.h" 224d3c95f5SJorgen Lundman 234d3c95f5SJorgen Lundman block_dev_desc_t *zfs_dev_desc; 244d3c95f5SJorgen Lundman 254d3c95f5SJorgen Lundman /* 264d3c95f5SJorgen Lundman * The zfs plug-in routines for GRUB are: 274d3c95f5SJorgen Lundman * 284d3c95f5SJorgen Lundman * zfs_mount() - locates a valid uberblock of the root pool and reads 294d3c95f5SJorgen Lundman * in its MOS at the memory address MOS. 304d3c95f5SJorgen Lundman * 314d3c95f5SJorgen Lundman * zfs_open() - locates a plain file object by following the MOS 324d3c95f5SJorgen Lundman * and places its dnode at the memory address DNODE. 334d3c95f5SJorgen Lundman * 344d3c95f5SJorgen Lundman * zfs_read() - read in the data blocks pointed by the DNODE. 354d3c95f5SJorgen Lundman * 364d3c95f5SJorgen Lundman */ 374d3c95f5SJorgen Lundman 384d3c95f5SJorgen Lundman #include <zfs/zfs.h> 394d3c95f5SJorgen Lundman #include <zfs/zio.h> 404d3c95f5SJorgen Lundman #include <zfs/dnode.h> 414d3c95f5SJorgen Lundman #include <zfs/uberblock_impl.h> 424d3c95f5SJorgen Lundman #include <zfs/vdev_impl.h> 434d3c95f5SJorgen Lundman #include <zfs/zio_checksum.h> 444d3c95f5SJorgen Lundman #include <zfs/zap_impl.h> 454d3c95f5SJorgen Lundman #include <zfs/zap_leaf.h> 464d3c95f5SJorgen Lundman #include <zfs/zfs_znode.h> 474d3c95f5SJorgen Lundman #include <zfs/dmu.h> 484d3c95f5SJorgen Lundman #include <zfs/dmu_objset.h> 494d3c95f5SJorgen Lundman #include <zfs/sa_impl.h> 504d3c95f5SJorgen Lundman #include <zfs/dsl_dir.h> 514d3c95f5SJorgen Lundman #include <zfs/dsl_dataset.h> 524d3c95f5SJorgen Lundman 534d3c95f5SJorgen Lundman 544d3c95f5SJorgen Lundman #define ZPOOL_PROP_BOOTFS "bootfs" 554d3c95f5SJorgen Lundman 564d3c95f5SJorgen Lundman 574d3c95f5SJorgen Lundman /* 584d3c95f5SJorgen Lundman * For nvlist manipulation. (from nvpair.h) 594d3c95f5SJorgen Lundman */ 604d3c95f5SJorgen Lundman #define NV_ENCODE_NATIVE 0 614d3c95f5SJorgen Lundman #define NV_ENCODE_XDR 1 624d3c95f5SJorgen Lundman #define NV_BIG_ENDIAN 0 634d3c95f5SJorgen Lundman #define NV_LITTLE_ENDIAN 1 644d3c95f5SJorgen Lundman #define DATA_TYPE_UINT64 8 654d3c95f5SJorgen Lundman #define DATA_TYPE_STRING 9 664d3c95f5SJorgen Lundman #define DATA_TYPE_NVLIST 19 674d3c95f5SJorgen Lundman #define DATA_TYPE_NVLIST_ARRAY 20 684d3c95f5SJorgen Lundman 694d3c95f5SJorgen Lundman 704d3c95f5SJorgen Lundman /* 714d3c95f5SJorgen Lundman * Macros to get fields in a bp or DVA. 724d3c95f5SJorgen Lundman */ 734d3c95f5SJorgen Lundman #define P2PHASE(x, align) ((x) & ((align) - 1)) 744d3c95f5SJorgen Lundman #define DVA_OFFSET_TO_PHYS_SECTOR(offset) \ 754d3c95f5SJorgen Lundman ((offset + VDEV_LABEL_START_SIZE) >> SPA_MINBLOCKSHIFT) 764d3c95f5SJorgen Lundman 774d3c95f5SJorgen Lundman /* 784d3c95f5SJorgen Lundman * return x rounded down to an align boundary 794d3c95f5SJorgen Lundman * eg, P2ALIGN(1200, 1024) == 1024 (1*align) 804d3c95f5SJorgen Lundman * eg, P2ALIGN(1024, 1024) == 1024 (1*align) 814d3c95f5SJorgen Lundman * eg, P2ALIGN(0x1234, 0x100) == 0x1200 (0x12*align) 824d3c95f5SJorgen Lundman * eg, P2ALIGN(0x5600, 0x100) == 0x5600 (0x56*align) 834d3c95f5SJorgen Lundman */ 844d3c95f5SJorgen Lundman #define P2ALIGN(x, align) ((x) & -(align)) 854d3c95f5SJorgen Lundman 864d3c95f5SJorgen Lundman /* 874d3c95f5SJorgen Lundman * FAT ZAP data structures 884d3c95f5SJorgen Lundman */ 894d3c95f5SJorgen Lundman #define ZFS_CRC64_POLY 0xC96C5795D7870F42ULL /* ECMA-182, reflected form */ 904d3c95f5SJorgen Lundman #define ZAP_HASH_IDX(hash, n) (((n) == 0) ? 0 : ((hash) >> (64 - (n)))) 914d3c95f5SJorgen Lundman #define CHAIN_END 0xffff /* end of the chunk chain */ 924d3c95f5SJorgen Lundman 934d3c95f5SJorgen Lundman /* 944d3c95f5SJorgen Lundman * The amount of space within the chunk available for the array is: 954d3c95f5SJorgen Lundman * chunk size - space for type (1) - space for next pointer (2) 964d3c95f5SJorgen Lundman */ 974d3c95f5SJorgen Lundman #define ZAP_LEAF_ARRAY_BYTES (ZAP_LEAF_CHUNKSIZE - 3) 984d3c95f5SJorgen Lundman 994d3c95f5SJorgen Lundman #define ZAP_LEAF_HASH_SHIFT(bs) (bs - 5) 1004d3c95f5SJorgen Lundman #define ZAP_LEAF_HASH_NUMENTRIES(bs) (1 << ZAP_LEAF_HASH_SHIFT(bs)) 1014d3c95f5SJorgen Lundman #define LEAF_HASH(bs, h) \ 1024d3c95f5SJorgen Lundman ((ZAP_LEAF_HASH_NUMENTRIES(bs)-1) & \ 1034d3c95f5SJorgen Lundman ((h) >> (64 - ZAP_LEAF_HASH_SHIFT(bs)-l->l_hdr.lh_prefix_len))) 1044d3c95f5SJorgen Lundman 1054d3c95f5SJorgen Lundman /* 1064d3c95f5SJorgen Lundman * The amount of space available for chunks is: 1074d3c95f5SJorgen Lundman * block size shift - hash entry size (2) * number of hash 1084d3c95f5SJorgen Lundman * entries - header space (2*chunksize) 1094d3c95f5SJorgen Lundman */ 1104d3c95f5SJorgen Lundman #define ZAP_LEAF_NUMCHUNKS(bs) \ 1114d3c95f5SJorgen Lundman (((1<<bs) - 2*ZAP_LEAF_HASH_NUMENTRIES(bs)) / \ 1124d3c95f5SJorgen Lundman ZAP_LEAF_CHUNKSIZE - 2) 1134d3c95f5SJorgen Lundman 1144d3c95f5SJorgen Lundman /* 1154d3c95f5SJorgen Lundman * The chunks start immediately after the hash table. The end of the 1164d3c95f5SJorgen Lundman * hash table is at l_hash + HASH_NUMENTRIES, which we simply cast to a 1174d3c95f5SJorgen Lundman * chunk_t. 1184d3c95f5SJorgen Lundman */ 1194d3c95f5SJorgen Lundman #define ZAP_LEAF_CHUNK(l, bs, idx) \ 1204d3c95f5SJorgen Lundman ((zap_leaf_chunk_t *)(l->l_hash + ZAP_LEAF_HASH_NUMENTRIES(bs)))[idx] 1214d3c95f5SJorgen Lundman #define ZAP_LEAF_ENTRY(l, bs, idx) (&ZAP_LEAF_CHUNK(l, bs, idx).l_entry) 1224d3c95f5SJorgen Lundman 1234d3c95f5SJorgen Lundman 1244d3c95f5SJorgen Lundman /* 1254d3c95f5SJorgen Lundman * Decompression Entry - lzjb 1264d3c95f5SJorgen Lundman */ 1274d3c95f5SJorgen Lundman #ifndef NBBY 1284d3c95f5SJorgen Lundman #define NBBY 8 1294d3c95f5SJorgen Lundman #endif 1304d3c95f5SJorgen Lundman 1314d3c95f5SJorgen Lundman 1324d3c95f5SJorgen Lundman 1334d3c95f5SJorgen Lundman typedef int zfs_decomp_func_t(void *s_start, void *d_start, 1344d3c95f5SJorgen Lundman uint32_t s_len, uint32_t d_len); 1354d3c95f5SJorgen Lundman typedef struct decomp_entry { 1364d3c95f5SJorgen Lundman char *name; 1374d3c95f5SJorgen Lundman zfs_decomp_func_t *decomp_func; 1384d3c95f5SJorgen Lundman } decomp_entry_t; 1394d3c95f5SJorgen Lundman 1404d3c95f5SJorgen Lundman typedef struct dnode_end { 1414d3c95f5SJorgen Lundman dnode_phys_t dn; 1424d3c95f5SJorgen Lundman zfs_endian_t endian; 1434d3c95f5SJorgen Lundman } dnode_end_t; 1444d3c95f5SJorgen Lundman 1454d3c95f5SJorgen Lundman struct zfs_data { 1464d3c95f5SJorgen Lundman /* cache for a file block of the currently zfs_open()-ed file */ 1474d3c95f5SJorgen Lundman char *file_buf; 1484d3c95f5SJorgen Lundman uint64_t file_start; 1494d3c95f5SJorgen Lundman uint64_t file_end; 1504d3c95f5SJorgen Lundman 1514d3c95f5SJorgen Lundman /* XXX: ashift is per vdev, not per pool. We currently only ever touch 1524d3c95f5SJorgen Lundman * a single vdev, but when/if raid-z or stripes are supported, this 1534d3c95f5SJorgen Lundman * may need revision. 1544d3c95f5SJorgen Lundman */ 1554d3c95f5SJorgen Lundman uint64_t vdev_ashift; 1564d3c95f5SJorgen Lundman uint64_t label_txg; 1574d3c95f5SJorgen Lundman uint64_t pool_guid; 1584d3c95f5SJorgen Lundman 1594d3c95f5SJorgen Lundman /* cache for a dnode block */ 1604d3c95f5SJorgen Lundman dnode_phys_t *dnode_buf; 1614d3c95f5SJorgen Lundman dnode_phys_t *dnode_mdn; 1624d3c95f5SJorgen Lundman uint64_t dnode_start; 1634d3c95f5SJorgen Lundman uint64_t dnode_end; 1644d3c95f5SJorgen Lundman zfs_endian_t dnode_endian; 1654d3c95f5SJorgen Lundman 1664d3c95f5SJorgen Lundman uberblock_t current_uberblock; 1674d3c95f5SJorgen Lundman 1684d3c95f5SJorgen Lundman dnode_end_t mos; 1694d3c95f5SJorgen Lundman dnode_end_t mdn; 1704d3c95f5SJorgen Lundman dnode_end_t dnode; 1714d3c95f5SJorgen Lundman 1724d3c95f5SJorgen Lundman uint64_t vdev_phys_sector; 1734d3c95f5SJorgen Lundman 1744d3c95f5SJorgen Lundman int (*userhook)(const char *, const struct zfs_dirhook_info *); 1754d3c95f5SJorgen Lundman struct zfs_dirhook_info *dirinfo; 1764d3c95f5SJorgen Lundman 1774d3c95f5SJorgen Lundman }; 1784d3c95f5SJorgen Lundman 1794d3c95f5SJorgen Lundman 1804d3c95f5SJorgen Lundman 1814d3c95f5SJorgen Lundman 1824d3c95f5SJorgen Lundman static int 1834d3c95f5SJorgen Lundman zlib_decompress(void *s, void *d, 1844d3c95f5SJorgen Lundman uint32_t slen, uint32_t dlen) 1854d3c95f5SJorgen Lundman { 1864d3c95f5SJorgen Lundman if (zlib_decompress(s, d, slen, dlen) < 0) 1874d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 1884d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 1894d3c95f5SJorgen Lundman } 1904d3c95f5SJorgen Lundman 1914d3c95f5SJorgen Lundman static decomp_entry_t decomp_table[ZIO_COMPRESS_FUNCTIONS] = { 1924d3c95f5SJorgen Lundman {"inherit", NULL}, /* ZIO_COMPRESS_INHERIT */ 1934d3c95f5SJorgen Lundman {"on", lzjb_decompress}, /* ZIO_COMPRESS_ON */ 1944d3c95f5SJorgen Lundman {"off", NULL}, /* ZIO_COMPRESS_OFF */ 1954d3c95f5SJorgen Lundman {"lzjb", lzjb_decompress}, /* ZIO_COMPRESS_LZJB */ 1964d3c95f5SJorgen Lundman {"empty", NULL}, /* ZIO_COMPRESS_EMPTY */ 1974d3c95f5SJorgen Lundman {"gzip-1", zlib_decompress}, /* ZIO_COMPRESS_GZIP1 */ 1984d3c95f5SJorgen Lundman {"gzip-2", zlib_decompress}, /* ZIO_COMPRESS_GZIP2 */ 1994d3c95f5SJorgen Lundman {"gzip-3", zlib_decompress}, /* ZIO_COMPRESS_GZIP3 */ 2004d3c95f5SJorgen Lundman {"gzip-4", zlib_decompress}, /* ZIO_COMPRESS_GZIP4 */ 2014d3c95f5SJorgen Lundman {"gzip-5", zlib_decompress}, /* ZIO_COMPRESS_GZIP5 */ 2024d3c95f5SJorgen Lundman {"gzip-6", zlib_decompress}, /* ZIO_COMPRESS_GZIP6 */ 2034d3c95f5SJorgen Lundman {"gzip-7", zlib_decompress}, /* ZIO_COMPRESS_GZIP7 */ 2044d3c95f5SJorgen Lundman {"gzip-8", zlib_decompress}, /* ZIO_COMPRESS_GZIP8 */ 2054d3c95f5SJorgen Lundman {"gzip-9", zlib_decompress}, /* ZIO_COMPRESS_GZIP9 */ 2064d3c95f5SJorgen Lundman }; 2074d3c95f5SJorgen Lundman 2084d3c95f5SJorgen Lundman 2094d3c95f5SJorgen Lundman 2104d3c95f5SJorgen Lundman static int zio_read_data(blkptr_t *bp, zfs_endian_t endian, 2114d3c95f5SJorgen Lundman void *buf, struct zfs_data *data); 2124d3c95f5SJorgen Lundman 2134d3c95f5SJorgen Lundman static int 2144d3c95f5SJorgen Lundman zio_read(blkptr_t *bp, zfs_endian_t endian, void **buf, 2154d3c95f5SJorgen Lundman size_t *size, struct zfs_data *data); 2164d3c95f5SJorgen Lundman 2174d3c95f5SJorgen Lundman /* 2184d3c95f5SJorgen Lundman * Our own version of log2(). Same thing as highbit()-1. 2194d3c95f5SJorgen Lundman */ 2204d3c95f5SJorgen Lundman static int 2214d3c95f5SJorgen Lundman zfs_log2(uint64_t num) 2224d3c95f5SJorgen Lundman { 2234d3c95f5SJorgen Lundman int i = 0; 2244d3c95f5SJorgen Lundman 2254d3c95f5SJorgen Lundman while (num > 1) { 2264d3c95f5SJorgen Lundman i++; 2274d3c95f5SJorgen Lundman num = num >> 1; 2284d3c95f5SJorgen Lundman } 2294d3c95f5SJorgen Lundman 2304d3c95f5SJorgen Lundman return i; 2314d3c95f5SJorgen Lundman } 2324d3c95f5SJorgen Lundman 2334d3c95f5SJorgen Lundman 2344d3c95f5SJorgen Lundman /* Checksum Functions */ 2354d3c95f5SJorgen Lundman static void 2364d3c95f5SJorgen Lundman zio_checksum_off(const void *buf __attribute__ ((unused)), 2374d3c95f5SJorgen Lundman uint64_t size __attribute__ ((unused)), 2384d3c95f5SJorgen Lundman zfs_endian_t endian __attribute__ ((unused)), 2394d3c95f5SJorgen Lundman zio_cksum_t *zcp) 2404d3c95f5SJorgen Lundman { 2414d3c95f5SJorgen Lundman ZIO_SET_CHECKSUM(zcp, 0, 0, 0, 0); 2424d3c95f5SJorgen Lundman } 2434d3c95f5SJorgen Lundman 2444d3c95f5SJorgen Lundman /* Checksum Table and Values */ 2454d3c95f5SJorgen Lundman static zio_checksum_info_t zio_checksum_table[ZIO_CHECKSUM_FUNCTIONS] = { 2464d3c95f5SJorgen Lundman {NULL, 0, 0, "inherit"}, 2474d3c95f5SJorgen Lundman {NULL, 0, 0, "on"}, 2484d3c95f5SJorgen Lundman {zio_checksum_off, 0, 0, "off"}, 2494d3c95f5SJorgen Lundman {zio_checksum_SHA256, 1, 1, "label"}, 2504d3c95f5SJorgen Lundman {zio_checksum_SHA256, 1, 1, "gang_header"}, 2514d3c95f5SJorgen Lundman {NULL, 0, 0, "zilog"}, 2524d3c95f5SJorgen Lundman {fletcher_2_endian, 0, 0, "fletcher2"}, 2534d3c95f5SJorgen Lundman {fletcher_4_endian, 1, 0, "fletcher4"}, 2544d3c95f5SJorgen Lundman {zio_checksum_SHA256, 1, 0, "SHA256"}, 2554d3c95f5SJorgen Lundman {NULL, 0, 0, "zilog2"}, 2564d3c95f5SJorgen Lundman }; 2574d3c95f5SJorgen Lundman 2584d3c95f5SJorgen Lundman /* 2594d3c95f5SJorgen Lundman * zio_checksum_verify: Provides support for checksum verification. 2604d3c95f5SJorgen Lundman * 2614d3c95f5SJorgen Lundman * Fletcher2, Fletcher4, and SHA256 are supported. 2624d3c95f5SJorgen Lundman * 2634d3c95f5SJorgen Lundman */ 2644d3c95f5SJorgen Lundman static int 2654d3c95f5SJorgen Lundman zio_checksum_verify(zio_cksum_t zc, uint32_t checksum, 2664d3c95f5SJorgen Lundman zfs_endian_t endian, char *buf, int size) 2674d3c95f5SJorgen Lundman { 2684d3c95f5SJorgen Lundman zio_eck_t *zec = (zio_eck_t *) (buf + size) - 1; 2694d3c95f5SJorgen Lundman zio_checksum_info_t *ci = &zio_checksum_table[checksum]; 2704d3c95f5SJorgen Lundman zio_cksum_t actual_cksum, expected_cksum; 2714d3c95f5SJorgen Lundman 2724d3c95f5SJorgen Lundman if (checksum >= ZIO_CHECKSUM_FUNCTIONS || ci->ci_func == NULL) { 2734d3c95f5SJorgen Lundman printf("zfs unknown checksum function %d\n", checksum); 2744d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 2754d3c95f5SJorgen Lundman } 2764d3c95f5SJorgen Lundman 2774d3c95f5SJorgen Lundman if (ci->ci_eck) { 2784d3c95f5SJorgen Lundman expected_cksum = zec->zec_cksum; 2794d3c95f5SJorgen Lundman zec->zec_cksum = zc; 2804d3c95f5SJorgen Lundman ci->ci_func(buf, size, endian, &actual_cksum); 2814d3c95f5SJorgen Lundman zec->zec_cksum = expected_cksum; 2824d3c95f5SJorgen Lundman zc = expected_cksum; 2834d3c95f5SJorgen Lundman } else { 2844d3c95f5SJorgen Lundman ci->ci_func(buf, size, endian, &actual_cksum); 2854d3c95f5SJorgen Lundman } 2864d3c95f5SJorgen Lundman 2874d3c95f5SJorgen Lundman if ((actual_cksum.zc_word[0] != zc.zc_word[0]) 2884d3c95f5SJorgen Lundman || (actual_cksum.zc_word[1] != zc.zc_word[1]) 2894d3c95f5SJorgen Lundman || (actual_cksum.zc_word[2] != zc.zc_word[2]) 2904d3c95f5SJorgen Lundman || (actual_cksum.zc_word[3] != zc.zc_word[3])) { 2914d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 2924d3c95f5SJorgen Lundman } 2934d3c95f5SJorgen Lundman 2944d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 2954d3c95f5SJorgen Lundman } 2964d3c95f5SJorgen Lundman 2974d3c95f5SJorgen Lundman /* 2984d3c95f5SJorgen Lundman * vdev_uberblock_compare takes two uberblock structures and returns an integer 2994d3c95f5SJorgen Lundman * indicating the more recent of the two. 3004d3c95f5SJorgen Lundman * Return Value = 1 if ub2 is more recent 3014d3c95f5SJorgen Lundman * Return Value = -1 if ub1 is more recent 3024d3c95f5SJorgen Lundman * The most recent uberblock is determined using its transaction number and 3034d3c95f5SJorgen Lundman * timestamp. The uberblock with the highest transaction number is 3044d3c95f5SJorgen Lundman * considered "newer". If the transaction numbers of the two blocks match, the 3054d3c95f5SJorgen Lundman * timestamps are compared to determine the "newer" of the two. 3064d3c95f5SJorgen Lundman */ 3074d3c95f5SJorgen Lundman static int 3084d3c95f5SJorgen Lundman vdev_uberblock_compare(uberblock_t *ub1, uberblock_t *ub2) 3094d3c95f5SJorgen Lundman { 3104d3c95f5SJorgen Lundman zfs_endian_t ub1_endian, ub2_endian; 3114d3c95f5SJorgen Lundman if (zfs_to_cpu64(ub1->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC) 3124d3c95f5SJorgen Lundman ub1_endian = LITTLE_ENDIAN; 3134d3c95f5SJorgen Lundman else 3144d3c95f5SJorgen Lundman ub1_endian = BIG_ENDIAN; 3154d3c95f5SJorgen Lundman if (zfs_to_cpu64(ub2->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC) 3164d3c95f5SJorgen Lundman ub2_endian = LITTLE_ENDIAN; 3174d3c95f5SJorgen Lundman else 3184d3c95f5SJorgen Lundman ub2_endian = BIG_ENDIAN; 3194d3c95f5SJorgen Lundman 3204d3c95f5SJorgen Lundman if (zfs_to_cpu64(ub1->ub_txg, ub1_endian) 3214d3c95f5SJorgen Lundman < zfs_to_cpu64(ub2->ub_txg, ub2_endian)) 3224d3c95f5SJorgen Lundman return -1; 3234d3c95f5SJorgen Lundman if (zfs_to_cpu64(ub1->ub_txg, ub1_endian) 3244d3c95f5SJorgen Lundman > zfs_to_cpu64(ub2->ub_txg, ub2_endian)) 3254d3c95f5SJorgen Lundman return 1; 3264d3c95f5SJorgen Lundman 3274d3c95f5SJorgen Lundman if (zfs_to_cpu64(ub1->ub_timestamp, ub1_endian) 3284d3c95f5SJorgen Lundman < zfs_to_cpu64(ub2->ub_timestamp, ub2_endian)) 3294d3c95f5SJorgen Lundman return -1; 3304d3c95f5SJorgen Lundman if (zfs_to_cpu64(ub1->ub_timestamp, ub1_endian) 3314d3c95f5SJorgen Lundman > zfs_to_cpu64(ub2->ub_timestamp, ub2_endian)) 3324d3c95f5SJorgen Lundman return 1; 3334d3c95f5SJorgen Lundman 3344d3c95f5SJorgen Lundman return 0; 3354d3c95f5SJorgen Lundman } 3364d3c95f5SJorgen Lundman 3374d3c95f5SJorgen Lundman /* 3384d3c95f5SJorgen Lundman * Three pieces of information are needed to verify an uberblock: the magic 3394d3c95f5SJorgen Lundman * number, the version number, and the checksum. 3404d3c95f5SJorgen Lundman * 3414d3c95f5SJorgen Lundman * Currently Implemented: version number, magic number, label txg 3424d3c95f5SJorgen Lundman * Need to Implement: checksum 3434d3c95f5SJorgen Lundman * 3444d3c95f5SJorgen Lundman */ 3454d3c95f5SJorgen Lundman static int 3464d3c95f5SJorgen Lundman uberblock_verify(uberblock_t *uber, int offset, struct zfs_data *data) 3474d3c95f5SJorgen Lundman { 3484d3c95f5SJorgen Lundman int err; 3494d3c95f5SJorgen Lundman zfs_endian_t endian = UNKNOWN_ENDIAN; 3504d3c95f5SJorgen Lundman zio_cksum_t zc; 3514d3c95f5SJorgen Lundman 3524d3c95f5SJorgen Lundman if (uber->ub_txg < data->label_txg) { 3534d3c95f5SJorgen Lundman debug("ignoring partially written label: uber_txg < label_txg %llu %llu\n", 3544d3c95f5SJorgen Lundman uber->ub_txg, data->label_txg); 3554d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 3564d3c95f5SJorgen Lundman } 3574d3c95f5SJorgen Lundman 3584d3c95f5SJorgen Lundman if (zfs_to_cpu64(uber->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC 3594d3c95f5SJorgen Lundman && zfs_to_cpu64(uber->ub_version, LITTLE_ENDIAN) > 0 3604d3c95f5SJorgen Lundman && zfs_to_cpu64(uber->ub_version, LITTLE_ENDIAN) <= SPA_VERSION) 3614d3c95f5SJorgen Lundman endian = LITTLE_ENDIAN; 3624d3c95f5SJorgen Lundman 3634d3c95f5SJorgen Lundman if (zfs_to_cpu64(uber->ub_magic, BIG_ENDIAN) == UBERBLOCK_MAGIC 3644d3c95f5SJorgen Lundman && zfs_to_cpu64(uber->ub_version, BIG_ENDIAN) > 0 3654d3c95f5SJorgen Lundman && zfs_to_cpu64(uber->ub_version, BIG_ENDIAN) <= SPA_VERSION) 3664d3c95f5SJorgen Lundman endian = BIG_ENDIAN; 3674d3c95f5SJorgen Lundman 3684d3c95f5SJorgen Lundman if (endian == UNKNOWN_ENDIAN) { 3694d3c95f5SJorgen Lundman printf("invalid uberblock magic\n"); 3704d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 3714d3c95f5SJorgen Lundman } 3724d3c95f5SJorgen Lundman 3734d3c95f5SJorgen Lundman memset(&zc, 0, sizeof(zc)); 3744d3c95f5SJorgen Lundman zc.zc_word[0] = cpu_to_zfs64(offset, endian); 3754d3c95f5SJorgen Lundman err = zio_checksum_verify(zc, ZIO_CHECKSUM_LABEL, endian, 3764d3c95f5SJorgen Lundman (char *) uber, UBERBLOCK_SIZE(data->vdev_ashift)); 3774d3c95f5SJorgen Lundman 3784d3c95f5SJorgen Lundman if (!err) { 3794d3c95f5SJorgen Lundman /* Check that the data pointed by the rootbp is usable. */ 3804d3c95f5SJorgen Lundman void *osp = NULL; 3814d3c95f5SJorgen Lundman size_t ospsize; 3824d3c95f5SJorgen Lundman err = zio_read(&uber->ub_rootbp, endian, &osp, &ospsize, data); 3834d3c95f5SJorgen Lundman free(osp); 3844d3c95f5SJorgen Lundman 3854d3c95f5SJorgen Lundman if (!err && ospsize < OBJSET_PHYS_SIZE_V14) { 3864d3c95f5SJorgen Lundman printf("uberblock rootbp points to invalid data\n"); 3874d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 3884d3c95f5SJorgen Lundman } 3894d3c95f5SJorgen Lundman } 3904d3c95f5SJorgen Lundman 3914d3c95f5SJorgen Lundman return err; 3924d3c95f5SJorgen Lundman } 3934d3c95f5SJorgen Lundman 3944d3c95f5SJorgen Lundman /* 3954d3c95f5SJorgen Lundman * Find the best uberblock. 3964d3c95f5SJorgen Lundman * Return: 3974d3c95f5SJorgen Lundman * Success - Pointer to the best uberblock. 3984d3c95f5SJorgen Lundman * Failure - NULL 3994d3c95f5SJorgen Lundman */ 4004d3c95f5SJorgen Lundman static uberblock_t *find_bestub(char *ub_array, struct zfs_data *data) 4014d3c95f5SJorgen Lundman { 4024d3c95f5SJorgen Lundman const uint64_t sector = data->vdev_phys_sector; 4034d3c95f5SJorgen Lundman uberblock_t *ubbest = NULL; 4044d3c95f5SJorgen Lundman uberblock_t *ubnext; 4054d3c95f5SJorgen Lundman unsigned int i, offset, pickedub = 0; 4064d3c95f5SJorgen Lundman int err = ZFS_ERR_NONE; 4074d3c95f5SJorgen Lundman 4084d3c95f5SJorgen Lundman const unsigned int UBCOUNT = UBERBLOCK_COUNT(data->vdev_ashift); 4094d3c95f5SJorgen Lundman const uint64_t UBBYTES = UBERBLOCK_SIZE(data->vdev_ashift); 4104d3c95f5SJorgen Lundman 4114d3c95f5SJorgen Lundman for (i = 0; i < UBCOUNT; i++) { 4124d3c95f5SJorgen Lundman ubnext = (uberblock_t *) (i * UBBYTES + ub_array); 4134d3c95f5SJorgen Lundman offset = (sector << SPA_MINBLOCKSHIFT) + VDEV_PHYS_SIZE + (i * UBBYTES); 4144d3c95f5SJorgen Lundman 4154d3c95f5SJorgen Lundman err = uberblock_verify(ubnext, offset, data); 4164d3c95f5SJorgen Lundman if (err) 4174d3c95f5SJorgen Lundman continue; 4184d3c95f5SJorgen Lundman 4194d3c95f5SJorgen Lundman if (ubbest == NULL || vdev_uberblock_compare(ubnext, ubbest) > 0) { 4204d3c95f5SJorgen Lundman ubbest = ubnext; 4214d3c95f5SJorgen Lundman pickedub = i; 4224d3c95f5SJorgen Lundman } 4234d3c95f5SJorgen Lundman } 4244d3c95f5SJorgen Lundman 4254d3c95f5SJorgen Lundman if (ubbest) 4264d3c95f5SJorgen Lundman debug("zfs Found best uberblock at idx %d, txg %llu\n", 4274d3c95f5SJorgen Lundman pickedub, (unsigned long long) ubbest->ub_txg); 4284d3c95f5SJorgen Lundman 4294d3c95f5SJorgen Lundman return ubbest; 4304d3c95f5SJorgen Lundman } 4314d3c95f5SJorgen Lundman 4324d3c95f5SJorgen Lundman static inline size_t 4334d3c95f5SJorgen Lundman get_psize(blkptr_t *bp, zfs_endian_t endian) 4344d3c95f5SJorgen Lundman { 4354d3c95f5SJorgen Lundman return (((zfs_to_cpu64((bp)->blk_prop, endian) >> 16) & 0xffff) + 1) 4364d3c95f5SJorgen Lundman << SPA_MINBLOCKSHIFT; 4374d3c95f5SJorgen Lundman } 4384d3c95f5SJorgen Lundman 4394d3c95f5SJorgen Lundman static uint64_t 4404d3c95f5SJorgen Lundman dva_get_offset(dva_t *dva, zfs_endian_t endian) 4414d3c95f5SJorgen Lundman { 4424d3c95f5SJorgen Lundman return zfs_to_cpu64((dva)->dva_word[1], 4434d3c95f5SJorgen Lundman endian) << SPA_MINBLOCKSHIFT; 4444d3c95f5SJorgen Lundman } 4454d3c95f5SJorgen Lundman 4464d3c95f5SJorgen Lundman /* 4474d3c95f5SJorgen Lundman * Read a block of data based on the gang block address dva, 4484d3c95f5SJorgen Lundman * and put its data in buf. 4494d3c95f5SJorgen Lundman * 4504d3c95f5SJorgen Lundman */ 4514d3c95f5SJorgen Lundman static int 4524d3c95f5SJorgen Lundman zio_read_gang(blkptr_t *bp, zfs_endian_t endian, dva_t *dva, void *buf, 4534d3c95f5SJorgen Lundman struct zfs_data *data) 4544d3c95f5SJorgen Lundman { 4554d3c95f5SJorgen Lundman zio_gbh_phys_t *zio_gb; 4564d3c95f5SJorgen Lundman uint64_t offset, sector; 4574d3c95f5SJorgen Lundman unsigned i; 4584d3c95f5SJorgen Lundman int err; 4594d3c95f5SJorgen Lundman zio_cksum_t zc; 4604d3c95f5SJorgen Lundman 4614d3c95f5SJorgen Lundman memset(&zc, 0, sizeof(zc)); 4624d3c95f5SJorgen Lundman 4634d3c95f5SJorgen Lundman zio_gb = malloc(SPA_GANGBLOCKSIZE); 4644d3c95f5SJorgen Lundman if (!zio_gb) 4654d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 4664d3c95f5SJorgen Lundman 4674d3c95f5SJorgen Lundman offset = dva_get_offset(dva, endian); 4684d3c95f5SJorgen Lundman sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); 4694d3c95f5SJorgen Lundman 4704d3c95f5SJorgen Lundman /* read in the gang block header */ 4714d3c95f5SJorgen Lundman err = zfs_devread(sector, 0, SPA_GANGBLOCKSIZE, (char *) zio_gb); 4724d3c95f5SJorgen Lundman 4734d3c95f5SJorgen Lundman if (err) { 4744d3c95f5SJorgen Lundman free(zio_gb); 4754d3c95f5SJorgen Lundman return err; 4764d3c95f5SJorgen Lundman } 4774d3c95f5SJorgen Lundman 4784d3c95f5SJorgen Lundman /* XXX */ 4794d3c95f5SJorgen Lundman /* self checksuming the gang block header */ 4804d3c95f5SJorgen Lundman ZIO_SET_CHECKSUM(&zc, DVA_GET_VDEV(dva), 4814d3c95f5SJorgen Lundman dva_get_offset(dva, endian), bp->blk_birth, 0); 4824d3c95f5SJorgen Lundman err = zio_checksum_verify(zc, ZIO_CHECKSUM_GANG_HEADER, endian, 4834d3c95f5SJorgen Lundman (char *) zio_gb, SPA_GANGBLOCKSIZE); 4844d3c95f5SJorgen Lundman if (err) { 4854d3c95f5SJorgen Lundman free(zio_gb); 4864d3c95f5SJorgen Lundman return err; 4874d3c95f5SJorgen Lundman } 4884d3c95f5SJorgen Lundman 4894d3c95f5SJorgen Lundman endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; 4904d3c95f5SJorgen Lundman 4914d3c95f5SJorgen Lundman for (i = 0; i < SPA_GBH_NBLKPTRS; i++) { 4924d3c95f5SJorgen Lundman if (zio_gb->zg_blkptr[i].blk_birth == 0) 4934d3c95f5SJorgen Lundman continue; 4944d3c95f5SJorgen Lundman 4954d3c95f5SJorgen Lundman err = zio_read_data(&zio_gb->zg_blkptr[i], endian, buf, data); 4964d3c95f5SJorgen Lundman if (err) { 4974d3c95f5SJorgen Lundman free(zio_gb); 4984d3c95f5SJorgen Lundman return err; 4994d3c95f5SJorgen Lundman } 5004d3c95f5SJorgen Lundman buf = (char *) buf + get_psize(&zio_gb->zg_blkptr[i], endian); 5014d3c95f5SJorgen Lundman } 5024d3c95f5SJorgen Lundman free(zio_gb); 5034d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 5044d3c95f5SJorgen Lundman } 5054d3c95f5SJorgen Lundman 5064d3c95f5SJorgen Lundman /* 5074d3c95f5SJorgen Lundman * Read in a block of raw data to buf. 5084d3c95f5SJorgen Lundman */ 5094d3c95f5SJorgen Lundman static int 5104d3c95f5SJorgen Lundman zio_read_data(blkptr_t *bp, zfs_endian_t endian, void *buf, 5114d3c95f5SJorgen Lundman struct zfs_data *data) 5124d3c95f5SJorgen Lundman { 5134d3c95f5SJorgen Lundman int i, psize; 5144d3c95f5SJorgen Lundman int err = ZFS_ERR_NONE; 5154d3c95f5SJorgen Lundman 5164d3c95f5SJorgen Lundman psize = get_psize(bp, endian); 5174d3c95f5SJorgen Lundman 5184d3c95f5SJorgen Lundman /* pick a good dva from the block pointer */ 5194d3c95f5SJorgen Lundman for (i = 0; i < SPA_DVAS_PER_BP; i++) { 5204d3c95f5SJorgen Lundman uint64_t offset, sector; 5214d3c95f5SJorgen Lundman 5224d3c95f5SJorgen Lundman if (bp->blk_dva[i].dva_word[0] == 0 && bp->blk_dva[i].dva_word[1] == 0) 5234d3c95f5SJorgen Lundman continue; 5244d3c95f5SJorgen Lundman 5254d3c95f5SJorgen Lundman if ((zfs_to_cpu64(bp->blk_dva[i].dva_word[1], endian)>>63) & 1) { 5264d3c95f5SJorgen Lundman err = zio_read_gang(bp, endian, &bp->blk_dva[i], buf, data); 5274d3c95f5SJorgen Lundman } else { 5284d3c95f5SJorgen Lundman /* read in a data block */ 5294d3c95f5SJorgen Lundman offset = dva_get_offset(&bp->blk_dva[i], endian); 5304d3c95f5SJorgen Lundman sector = DVA_OFFSET_TO_PHYS_SECTOR(offset); 5314d3c95f5SJorgen Lundman 5324d3c95f5SJorgen Lundman err = zfs_devread(sector, 0, psize, buf); 5334d3c95f5SJorgen Lundman } 5344d3c95f5SJorgen Lundman 5354d3c95f5SJorgen Lundman if (!err) { 5364d3c95f5SJorgen Lundman /*Check the underlying checksum before we rule this DVA as "good"*/ 5374d3c95f5SJorgen Lundman uint32_t checkalgo = (zfs_to_cpu64((bp)->blk_prop, endian) >> 40) & 0xff; 5384d3c95f5SJorgen Lundman 5394d3c95f5SJorgen Lundman err = zio_checksum_verify(bp->blk_cksum, checkalgo, endian, buf, psize); 5404d3c95f5SJorgen Lundman if (!err) 5414d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 5424d3c95f5SJorgen Lundman } 5434d3c95f5SJorgen Lundman 5444d3c95f5SJorgen Lundman /* If read failed or checksum bad, reset the error. Hopefully we've got some more DVA's to try.*/ 5454d3c95f5SJorgen Lundman } 5464d3c95f5SJorgen Lundman 5474d3c95f5SJorgen Lundman if (!err) { 5484d3c95f5SJorgen Lundman printf("couldn't find a valid DVA\n"); 5494d3c95f5SJorgen Lundman err = ZFS_ERR_BAD_FS; 5504d3c95f5SJorgen Lundman } 5514d3c95f5SJorgen Lundman 5524d3c95f5SJorgen Lundman return err; 5534d3c95f5SJorgen Lundman } 5544d3c95f5SJorgen Lundman 5554d3c95f5SJorgen Lundman /* 5564d3c95f5SJorgen Lundman * Read in a block of data, verify its checksum, decompress if needed, 5574d3c95f5SJorgen Lundman * and put the uncompressed data in buf. 5584d3c95f5SJorgen Lundman */ 5594d3c95f5SJorgen Lundman static int 5604d3c95f5SJorgen Lundman zio_read(blkptr_t *bp, zfs_endian_t endian, void **buf, 5614d3c95f5SJorgen Lundman size_t *size, struct zfs_data *data) 5624d3c95f5SJorgen Lundman { 5634d3c95f5SJorgen Lundman size_t lsize, psize; 5644d3c95f5SJorgen Lundman unsigned int comp; 5654d3c95f5SJorgen Lundman char *compbuf = NULL; 5664d3c95f5SJorgen Lundman int err; 5674d3c95f5SJorgen Lundman 5684d3c95f5SJorgen Lundman *buf = NULL; 5694d3c95f5SJorgen Lundman 5704d3c95f5SJorgen Lundman comp = (zfs_to_cpu64((bp)->blk_prop, endian)>>32) & 0xff; 5714d3c95f5SJorgen Lundman lsize = (BP_IS_HOLE(bp) ? 0 : 5724d3c95f5SJorgen Lundman (((zfs_to_cpu64((bp)->blk_prop, endian) & 0xffff) + 1) 5734d3c95f5SJorgen Lundman << SPA_MINBLOCKSHIFT)); 5744d3c95f5SJorgen Lundman psize = get_psize(bp, endian); 5754d3c95f5SJorgen Lundman 5764d3c95f5SJorgen Lundman if (size) 5774d3c95f5SJorgen Lundman *size = lsize; 5784d3c95f5SJorgen Lundman 5794d3c95f5SJorgen Lundman if (comp >= ZIO_COMPRESS_FUNCTIONS) { 5804d3c95f5SJorgen Lundman printf("compression algorithm %u not supported\n", (unsigned int) comp); 5814d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 5824d3c95f5SJorgen Lundman } 5834d3c95f5SJorgen Lundman 5844d3c95f5SJorgen Lundman if (comp != ZIO_COMPRESS_OFF && decomp_table[comp].decomp_func == NULL) { 5854d3c95f5SJorgen Lundman printf("compression algorithm %s not supported\n", decomp_table[comp].name); 5864d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 5874d3c95f5SJorgen Lundman } 5884d3c95f5SJorgen Lundman 5894d3c95f5SJorgen Lundman if (comp != ZIO_COMPRESS_OFF) { 5904d3c95f5SJorgen Lundman compbuf = malloc(psize); 5914d3c95f5SJorgen Lundman if (!compbuf) 5924d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 5934d3c95f5SJorgen Lundman } else { 5944d3c95f5SJorgen Lundman compbuf = *buf = malloc(lsize); 5954d3c95f5SJorgen Lundman } 5964d3c95f5SJorgen Lundman 5974d3c95f5SJorgen Lundman err = zio_read_data(bp, endian, compbuf, data); 5984d3c95f5SJorgen Lundman if (err) { 5994d3c95f5SJorgen Lundman free(compbuf); 6004d3c95f5SJorgen Lundman *buf = NULL; 6014d3c95f5SJorgen Lundman return err; 6024d3c95f5SJorgen Lundman } 6034d3c95f5SJorgen Lundman 6044d3c95f5SJorgen Lundman if (comp != ZIO_COMPRESS_OFF) { 6054d3c95f5SJorgen Lundman *buf = malloc(lsize); 6064d3c95f5SJorgen Lundman if (!*buf) { 6074d3c95f5SJorgen Lundman free(compbuf); 6084d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 6094d3c95f5SJorgen Lundman } 6104d3c95f5SJorgen Lundman 6114d3c95f5SJorgen Lundman err = decomp_table[comp].decomp_func(compbuf, *buf, psize, lsize); 6124d3c95f5SJorgen Lundman free(compbuf); 6134d3c95f5SJorgen Lundman if (err) { 6144d3c95f5SJorgen Lundman free(*buf); 6154d3c95f5SJorgen Lundman *buf = NULL; 6164d3c95f5SJorgen Lundman return err; 6174d3c95f5SJorgen Lundman } 6184d3c95f5SJorgen Lundman } 6194d3c95f5SJorgen Lundman 6204d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 6214d3c95f5SJorgen Lundman } 6224d3c95f5SJorgen Lundman 6234d3c95f5SJorgen Lundman /* 6244d3c95f5SJorgen Lundman * Get the block from a block id. 6254d3c95f5SJorgen Lundman * push the block onto the stack. 6264d3c95f5SJorgen Lundman * 6274d3c95f5SJorgen Lundman */ 6284d3c95f5SJorgen Lundman static int 6294d3c95f5SJorgen Lundman dmu_read(dnode_end_t *dn, uint64_t blkid, void **buf, 6304d3c95f5SJorgen Lundman zfs_endian_t *endian_out, struct zfs_data *data) 6314d3c95f5SJorgen Lundman { 6324d3c95f5SJorgen Lundman int idx, level; 6334d3c95f5SJorgen Lundman blkptr_t *bp_array = dn->dn.dn_blkptr; 6344d3c95f5SJorgen Lundman int epbs = dn->dn.dn_indblkshift - SPA_BLKPTRSHIFT; 6354d3c95f5SJorgen Lundman blkptr_t *bp; 6364d3c95f5SJorgen Lundman void *tmpbuf = 0; 6374d3c95f5SJorgen Lundman zfs_endian_t endian; 6384d3c95f5SJorgen Lundman int err = ZFS_ERR_NONE; 6394d3c95f5SJorgen Lundman 6404d3c95f5SJorgen Lundman bp = malloc(sizeof(blkptr_t)); 6414d3c95f5SJorgen Lundman if (!bp) 6424d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 6434d3c95f5SJorgen Lundman 6444d3c95f5SJorgen Lundman endian = dn->endian; 6454d3c95f5SJorgen Lundman for (level = dn->dn.dn_nlevels - 1; level >= 0; level--) { 6464d3c95f5SJorgen Lundman idx = (blkid >> (epbs * level)) & ((1 << epbs) - 1); 6474d3c95f5SJorgen Lundman *bp = bp_array[idx]; 6484d3c95f5SJorgen Lundman if (bp_array != dn->dn.dn_blkptr) { 6494d3c95f5SJorgen Lundman free(bp_array); 6504d3c95f5SJorgen Lundman bp_array = 0; 6514d3c95f5SJorgen Lundman } 6524d3c95f5SJorgen Lundman 6534d3c95f5SJorgen Lundman if (BP_IS_HOLE(bp)) { 6544d3c95f5SJorgen Lundman size_t size = zfs_to_cpu16(dn->dn.dn_datablkszsec, 6554d3c95f5SJorgen Lundman dn->endian) 6564d3c95f5SJorgen Lundman << SPA_MINBLOCKSHIFT; 6574d3c95f5SJorgen Lundman *buf = malloc(size); 6584d3c95f5SJorgen Lundman if (*buf) { 6594d3c95f5SJorgen Lundman err = ZFS_ERR_OUT_OF_MEMORY; 6604d3c95f5SJorgen Lundman break; 6614d3c95f5SJorgen Lundman } 6624d3c95f5SJorgen Lundman memset(*buf, 0, size); 6634d3c95f5SJorgen Lundman endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; 6644d3c95f5SJorgen Lundman break; 6654d3c95f5SJorgen Lundman } 6664d3c95f5SJorgen Lundman if (level == 0) { 6674d3c95f5SJorgen Lundman err = zio_read(bp, endian, buf, 0, data); 6684d3c95f5SJorgen Lundman endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; 6694d3c95f5SJorgen Lundman break; 6704d3c95f5SJorgen Lundman } 6714d3c95f5SJorgen Lundman err = zio_read(bp, endian, &tmpbuf, 0, data); 6724d3c95f5SJorgen Lundman endian = (zfs_to_cpu64(bp->blk_prop, endian) >> 63) & 1; 6734d3c95f5SJorgen Lundman if (err) 6744d3c95f5SJorgen Lundman break; 6754d3c95f5SJorgen Lundman bp_array = tmpbuf; 6764d3c95f5SJorgen Lundman } 6774d3c95f5SJorgen Lundman if (bp_array != dn->dn.dn_blkptr) 6784d3c95f5SJorgen Lundman free(bp_array); 6794d3c95f5SJorgen Lundman if (endian_out) 6804d3c95f5SJorgen Lundman *endian_out = endian; 6814d3c95f5SJorgen Lundman 6824d3c95f5SJorgen Lundman free(bp); 6834d3c95f5SJorgen Lundman return err; 6844d3c95f5SJorgen Lundman } 6854d3c95f5SJorgen Lundman 6864d3c95f5SJorgen Lundman /* 6874d3c95f5SJorgen Lundman * mzap_lookup: Looks up property described by "name" and returns the value 6884d3c95f5SJorgen Lundman * in "value". 6894d3c95f5SJorgen Lundman */ 6904d3c95f5SJorgen Lundman static int 6914d3c95f5SJorgen Lundman mzap_lookup(mzap_phys_t *zapobj, zfs_endian_t endian, 6924d3c95f5SJorgen Lundman int objsize, char *name, uint64_t * value) 6934d3c95f5SJorgen Lundman { 6944d3c95f5SJorgen Lundman int i, chunks; 6954d3c95f5SJorgen Lundman mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; 6964d3c95f5SJorgen Lundman 6974d3c95f5SJorgen Lundman chunks = objsize / MZAP_ENT_LEN - 1; 6984d3c95f5SJorgen Lundman for (i = 0; i < chunks; i++) { 6994d3c95f5SJorgen Lundman if (strcmp(mzap_ent[i].mze_name, name) == 0) { 7004d3c95f5SJorgen Lundman *value = zfs_to_cpu64(mzap_ent[i].mze_value, endian); 7014d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 7024d3c95f5SJorgen Lundman } 7034d3c95f5SJorgen Lundman } 7044d3c95f5SJorgen Lundman 7054d3c95f5SJorgen Lundman printf("couldn't find '%s'\n", name); 7064d3c95f5SJorgen Lundman return ZFS_ERR_FILE_NOT_FOUND; 7074d3c95f5SJorgen Lundman } 7084d3c95f5SJorgen Lundman 7094d3c95f5SJorgen Lundman static int 7104d3c95f5SJorgen Lundman mzap_iterate(mzap_phys_t *zapobj, zfs_endian_t endian, int objsize, 7114d3c95f5SJorgen Lundman int (*hook)(const char *name, 7124d3c95f5SJorgen Lundman uint64_t val, 7134d3c95f5SJorgen Lundman struct zfs_data *data), 7144d3c95f5SJorgen Lundman struct zfs_data *data) 7154d3c95f5SJorgen Lundman { 7164d3c95f5SJorgen Lundman int i, chunks; 7174d3c95f5SJorgen Lundman mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk; 7184d3c95f5SJorgen Lundman 7194d3c95f5SJorgen Lundman chunks = objsize / MZAP_ENT_LEN - 1; 7204d3c95f5SJorgen Lundman for (i = 0; i < chunks; i++) { 7214d3c95f5SJorgen Lundman if (hook(mzap_ent[i].mze_name, 7224d3c95f5SJorgen Lundman zfs_to_cpu64(mzap_ent[i].mze_value, endian), 7234d3c95f5SJorgen Lundman data)) 7244d3c95f5SJorgen Lundman return 1; 7254d3c95f5SJorgen Lundman } 7264d3c95f5SJorgen Lundman 7274d3c95f5SJorgen Lundman return 0; 7284d3c95f5SJorgen Lundman } 7294d3c95f5SJorgen Lundman 7304d3c95f5SJorgen Lundman static uint64_t 7314d3c95f5SJorgen Lundman zap_hash(uint64_t salt, const char *name) 7324d3c95f5SJorgen Lundman { 7334d3c95f5SJorgen Lundman static uint64_t table[256]; 7344d3c95f5SJorgen Lundman const uint8_t *cp; 7354d3c95f5SJorgen Lundman uint8_t c; 7364d3c95f5SJorgen Lundman uint64_t crc = salt; 7374d3c95f5SJorgen Lundman 7384d3c95f5SJorgen Lundman if (table[128] == 0) { 739*e183de0dSJorgen Lundman uint64_t *ct = NULL; 7404d3c95f5SJorgen Lundman int i, j; 7414d3c95f5SJorgen Lundman for (i = 0; i < 256; i++) { 7424d3c95f5SJorgen Lundman for (ct = table + i, *ct = i, j = 8; j > 0; j--) 7434d3c95f5SJorgen Lundman *ct = (*ct >> 1) ^ (-(*ct & 1) & ZFS_CRC64_POLY); 7444d3c95f5SJorgen Lundman } 7454d3c95f5SJorgen Lundman } 7464d3c95f5SJorgen Lundman 7474d3c95f5SJorgen Lundman for (cp = (const uint8_t *) name; (c = *cp) != '\0'; cp++) 7484d3c95f5SJorgen Lundman crc = (crc >> 8) ^ table[(crc ^ c) & 0xFF]; 7494d3c95f5SJorgen Lundman 7504d3c95f5SJorgen Lundman /* 7514d3c95f5SJorgen Lundman * Only use 28 bits, since we need 4 bits in the cookie for the 7524d3c95f5SJorgen Lundman * collision differentiator. We MUST use the high bits, since 7534d3c95f5SJorgen Lundman * those are the onces that we first pay attention to when 7544d3c95f5SJorgen Lundman * chosing the bucket. 7554d3c95f5SJorgen Lundman */ 7564d3c95f5SJorgen Lundman crc &= ~((1ULL << (64 - ZAP_HASHBITS)) - 1); 7574d3c95f5SJorgen Lundman 7584d3c95f5SJorgen Lundman return crc; 7594d3c95f5SJorgen Lundman } 7604d3c95f5SJorgen Lundman 7614d3c95f5SJorgen Lundman /* 7624d3c95f5SJorgen Lundman * Only to be used on 8-bit arrays. 7634d3c95f5SJorgen Lundman * array_len is actual len in bytes (not encoded le_value_length). 7644d3c95f5SJorgen Lundman * buf is null-terminated. 7654d3c95f5SJorgen Lundman */ 7664d3c95f5SJorgen Lundman /* XXX */ 7674d3c95f5SJorgen Lundman static int 7684d3c95f5SJorgen Lundman zap_leaf_array_equal(zap_leaf_phys_t *l, zfs_endian_t endian, 7694d3c95f5SJorgen Lundman int blksft, int chunk, int array_len, const char *buf) 7704d3c95f5SJorgen Lundman { 7714d3c95f5SJorgen Lundman int bseen = 0; 7724d3c95f5SJorgen Lundman 7734d3c95f5SJorgen Lundman while (bseen < array_len) { 7744d3c95f5SJorgen Lundman struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, blksft, chunk).l_array; 775c79cba37SMasahiro Yamada int toread = min(array_len - bseen, ZAP_LEAF_ARRAY_BYTES); 7764d3c95f5SJorgen Lundman 7774d3c95f5SJorgen Lundman if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) 7784d3c95f5SJorgen Lundman return 0; 7794d3c95f5SJorgen Lundman 7804d3c95f5SJorgen Lundman if (memcmp(la->la_array, buf + bseen, toread) != 0) 7814d3c95f5SJorgen Lundman break; 7824d3c95f5SJorgen Lundman chunk = zfs_to_cpu16(la->la_next, endian); 7834d3c95f5SJorgen Lundman bseen += toread; 7844d3c95f5SJorgen Lundman } 7854d3c95f5SJorgen Lundman return (bseen == array_len); 7864d3c95f5SJorgen Lundman } 7874d3c95f5SJorgen Lundman 7884d3c95f5SJorgen Lundman /* XXX */ 7894d3c95f5SJorgen Lundman static int 7904d3c95f5SJorgen Lundman zap_leaf_array_get(zap_leaf_phys_t *l, zfs_endian_t endian, int blksft, 7914d3c95f5SJorgen Lundman int chunk, int array_len, char *buf) 7924d3c95f5SJorgen Lundman { 7934d3c95f5SJorgen Lundman int bseen = 0; 7944d3c95f5SJorgen Lundman 7954d3c95f5SJorgen Lundman while (bseen < array_len) { 7964d3c95f5SJorgen Lundman struct zap_leaf_array *la = &ZAP_LEAF_CHUNK(l, blksft, chunk).l_array; 797c79cba37SMasahiro Yamada int toread = min(array_len - bseen, ZAP_LEAF_ARRAY_BYTES); 7984d3c95f5SJorgen Lundman 7994d3c95f5SJorgen Lundman if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) 8004d3c95f5SJorgen Lundman /* Don't use errno because this error is to be ignored. */ 8014d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8024d3c95f5SJorgen Lundman 8034d3c95f5SJorgen Lundman memcpy(buf + bseen, la->la_array, toread); 8044d3c95f5SJorgen Lundman chunk = zfs_to_cpu16(la->la_next, endian); 8054d3c95f5SJorgen Lundman bseen += toread; 8064d3c95f5SJorgen Lundman } 8074d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 8084d3c95f5SJorgen Lundman } 8094d3c95f5SJorgen Lundman 8104d3c95f5SJorgen Lundman 8114d3c95f5SJorgen Lundman /* 8124d3c95f5SJorgen Lundman * Given a zap_leaf_phys_t, walk thru the zap leaf chunks to get the 8134d3c95f5SJorgen Lundman * value for the property "name". 8144d3c95f5SJorgen Lundman * 8154d3c95f5SJorgen Lundman */ 8164d3c95f5SJorgen Lundman /* XXX */ 8174d3c95f5SJorgen Lundman static int 8184d3c95f5SJorgen Lundman zap_leaf_lookup(zap_leaf_phys_t *l, zfs_endian_t endian, 8194d3c95f5SJorgen Lundman int blksft, uint64_t h, 8204d3c95f5SJorgen Lundman const char *name, uint64_t *value) 8214d3c95f5SJorgen Lundman { 8224d3c95f5SJorgen Lundman uint16_t chunk; 8234d3c95f5SJorgen Lundman struct zap_leaf_entry *le; 8244d3c95f5SJorgen Lundman 8254d3c95f5SJorgen Lundman /* Verify if this is a valid leaf block */ 8264d3c95f5SJorgen Lundman if (zfs_to_cpu64(l->l_hdr.lh_block_type, endian) != ZBT_LEAF) { 8274d3c95f5SJorgen Lundman printf("invalid leaf type\n"); 8284d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8294d3c95f5SJorgen Lundman } 8304d3c95f5SJorgen Lundman if (zfs_to_cpu32(l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) { 8314d3c95f5SJorgen Lundman printf("invalid leaf magic\n"); 8324d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8334d3c95f5SJorgen Lundman } 8344d3c95f5SJorgen Lundman 8354d3c95f5SJorgen Lundman for (chunk = zfs_to_cpu16(l->l_hash[LEAF_HASH(blksft, h)], endian); 8364d3c95f5SJorgen Lundman chunk != CHAIN_END; chunk = le->le_next) { 8374d3c95f5SJorgen Lundman 8384d3c95f5SJorgen Lundman if (chunk >= ZAP_LEAF_NUMCHUNKS(blksft)) { 8394d3c95f5SJorgen Lundman printf("invalid chunk number\n"); 8404d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8414d3c95f5SJorgen Lundman } 8424d3c95f5SJorgen Lundman 8434d3c95f5SJorgen Lundman le = ZAP_LEAF_ENTRY(l, blksft, chunk); 8444d3c95f5SJorgen Lundman 8454d3c95f5SJorgen Lundman /* Verify the chunk entry */ 8464d3c95f5SJorgen Lundman if (le->le_type != ZAP_CHUNK_ENTRY) { 8474d3c95f5SJorgen Lundman printf("invalid chunk entry\n"); 8484d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8494d3c95f5SJorgen Lundman } 8504d3c95f5SJorgen Lundman 8514d3c95f5SJorgen Lundman if (zfs_to_cpu64(le->le_hash, endian) != h) 8524d3c95f5SJorgen Lundman continue; 8534d3c95f5SJorgen Lundman 8544d3c95f5SJorgen Lundman if (zap_leaf_array_equal(l, endian, blksft, 8554d3c95f5SJorgen Lundman zfs_to_cpu16(le->le_name_chunk, endian), 8564d3c95f5SJorgen Lundman zfs_to_cpu16(le->le_name_length, endian), 8574d3c95f5SJorgen Lundman name)) { 8584d3c95f5SJorgen Lundman struct zap_leaf_array *la; 8594d3c95f5SJorgen Lundman 8604d3c95f5SJorgen Lundman if (le->le_int_size != 8 || le->le_value_length != 1) { 8614d3c95f5SJorgen Lundman printf("invalid leaf chunk entry\n"); 8624d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8634d3c95f5SJorgen Lundman } 8644d3c95f5SJorgen Lundman /* get the uint64_t property value */ 8654d3c95f5SJorgen Lundman la = &ZAP_LEAF_CHUNK(l, blksft, le->le_value_chunk).l_array; 8664d3c95f5SJorgen Lundman 8674d3c95f5SJorgen Lundman *value = be64_to_cpu(la->la_array64); 8684d3c95f5SJorgen Lundman 8694d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 8704d3c95f5SJorgen Lundman } 8714d3c95f5SJorgen Lundman } 8724d3c95f5SJorgen Lundman 8734d3c95f5SJorgen Lundman printf("couldn't find '%s'\n", name); 8744d3c95f5SJorgen Lundman return ZFS_ERR_FILE_NOT_FOUND; 8754d3c95f5SJorgen Lundman } 8764d3c95f5SJorgen Lundman 8774d3c95f5SJorgen Lundman 8784d3c95f5SJorgen Lundman /* Verify if this is a fat zap header block */ 8794d3c95f5SJorgen Lundman static int 8804d3c95f5SJorgen Lundman zap_verify(zap_phys_t *zap) 8814d3c95f5SJorgen Lundman { 8824d3c95f5SJorgen Lundman if (zap->zap_magic != (uint64_t) ZAP_MAGIC) { 8834d3c95f5SJorgen Lundman printf("bad ZAP magic\n"); 8844d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8854d3c95f5SJorgen Lundman } 8864d3c95f5SJorgen Lundman 8874d3c95f5SJorgen Lundman if (zap->zap_flags != 0) { 8884d3c95f5SJorgen Lundman printf("bad ZAP flags\n"); 8894d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8904d3c95f5SJorgen Lundman } 8914d3c95f5SJorgen Lundman 8924d3c95f5SJorgen Lundman if (zap->zap_salt == 0) { 8934d3c95f5SJorgen Lundman printf("bad ZAP salt\n"); 8944d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 8954d3c95f5SJorgen Lundman } 8964d3c95f5SJorgen Lundman 8974d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 8984d3c95f5SJorgen Lundman } 8994d3c95f5SJorgen Lundman 9004d3c95f5SJorgen Lundman /* 9014d3c95f5SJorgen Lundman * Fat ZAP lookup 9024d3c95f5SJorgen Lundman * 9034d3c95f5SJorgen Lundman */ 9044d3c95f5SJorgen Lundman /* XXX */ 9054d3c95f5SJorgen Lundman static int 9064d3c95f5SJorgen Lundman fzap_lookup(dnode_end_t *zap_dnode, zap_phys_t *zap, 9074d3c95f5SJorgen Lundman char *name, uint64_t *value, struct zfs_data *data) 9084d3c95f5SJorgen Lundman { 9094d3c95f5SJorgen Lundman void *l; 9104d3c95f5SJorgen Lundman uint64_t hash, idx, blkid; 9114d3c95f5SJorgen Lundman int blksft = zfs_log2(zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, 9124d3c95f5SJorgen Lundman zap_dnode->endian) << DNODE_SHIFT); 9134d3c95f5SJorgen Lundman int err; 9144d3c95f5SJorgen Lundman zfs_endian_t leafendian; 9154d3c95f5SJorgen Lundman 9164d3c95f5SJorgen Lundman err = zap_verify(zap); 9174d3c95f5SJorgen Lundman if (err) 9184d3c95f5SJorgen Lundman return err; 9194d3c95f5SJorgen Lundman 9204d3c95f5SJorgen Lundman hash = zap_hash(zap->zap_salt, name); 9214d3c95f5SJorgen Lundman 9224d3c95f5SJorgen Lundman /* get block id from index */ 9234d3c95f5SJorgen Lundman if (zap->zap_ptrtbl.zt_numblks != 0) { 9244d3c95f5SJorgen Lundman printf("external pointer tables not supported\n"); 9254d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 9264d3c95f5SJorgen Lundman } 9274d3c95f5SJorgen Lundman idx = ZAP_HASH_IDX(hash, zap->zap_ptrtbl.zt_shift); 9284d3c95f5SJorgen Lundman blkid = ((uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))]; 9294d3c95f5SJorgen Lundman 9304d3c95f5SJorgen Lundman /* Get the leaf block */ 9314d3c95f5SJorgen Lundman if ((1U << blksft) < sizeof(zap_leaf_phys_t)) { 9324d3c95f5SJorgen Lundman printf("ZAP leaf is too small\n"); 9334d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 9344d3c95f5SJorgen Lundman } 9354d3c95f5SJorgen Lundman err = dmu_read(zap_dnode, blkid, &l, &leafendian, data); 9364d3c95f5SJorgen Lundman if (err) 9374d3c95f5SJorgen Lundman return err; 9384d3c95f5SJorgen Lundman 9394d3c95f5SJorgen Lundman err = zap_leaf_lookup(l, leafendian, blksft, hash, name, value); 9404d3c95f5SJorgen Lundman free(l); 9414d3c95f5SJorgen Lundman return err; 9424d3c95f5SJorgen Lundman } 9434d3c95f5SJorgen Lundman 9444d3c95f5SJorgen Lundman /* XXX */ 9454d3c95f5SJorgen Lundman static int 9464d3c95f5SJorgen Lundman fzap_iterate(dnode_end_t *zap_dnode, zap_phys_t *zap, 9474d3c95f5SJorgen Lundman int (*hook)(const char *name, 9484d3c95f5SJorgen Lundman uint64_t val, 9494d3c95f5SJorgen Lundman struct zfs_data *data), 9504d3c95f5SJorgen Lundman struct zfs_data *data) 9514d3c95f5SJorgen Lundman { 9524d3c95f5SJorgen Lundman zap_leaf_phys_t *l; 9534d3c95f5SJorgen Lundman void *l_in; 9544d3c95f5SJorgen Lundman uint64_t idx, blkid; 9554d3c95f5SJorgen Lundman uint16_t chunk; 9564d3c95f5SJorgen Lundman int blksft = zfs_log2(zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, 9574d3c95f5SJorgen Lundman zap_dnode->endian) << DNODE_SHIFT); 9584d3c95f5SJorgen Lundman int err; 9594d3c95f5SJorgen Lundman zfs_endian_t endian; 9604d3c95f5SJorgen Lundman 9614d3c95f5SJorgen Lundman if (zap_verify(zap)) 9624d3c95f5SJorgen Lundman return 0; 9634d3c95f5SJorgen Lundman 9644d3c95f5SJorgen Lundman /* get block id from index */ 9654d3c95f5SJorgen Lundman if (zap->zap_ptrtbl.zt_numblks != 0) { 9664d3c95f5SJorgen Lundman printf("external pointer tables not supported\n"); 9674d3c95f5SJorgen Lundman return 0; 9684d3c95f5SJorgen Lundman } 9694d3c95f5SJorgen Lundman /* Get the leaf block */ 9704d3c95f5SJorgen Lundman if ((1U << blksft) < sizeof(zap_leaf_phys_t)) { 9714d3c95f5SJorgen Lundman printf("ZAP leaf is too small\n"); 9724d3c95f5SJorgen Lundman return 0; 9734d3c95f5SJorgen Lundman } 9744d3c95f5SJorgen Lundman for (idx = 0; idx < zap->zap_ptrtbl.zt_numblks; idx++) { 9754d3c95f5SJorgen Lundman blkid = ((uint64_t *) zap)[idx + (1 << (blksft - 3 - 1))]; 9764d3c95f5SJorgen Lundman 9774d3c95f5SJorgen Lundman err = dmu_read(zap_dnode, blkid, &l_in, &endian, data); 9784d3c95f5SJorgen Lundman l = l_in; 9794d3c95f5SJorgen Lundman if (err) 9804d3c95f5SJorgen Lundman continue; 9814d3c95f5SJorgen Lundman 9824d3c95f5SJorgen Lundman /* Verify if this is a valid leaf block */ 9834d3c95f5SJorgen Lundman if (zfs_to_cpu64(l->l_hdr.lh_block_type, endian) != ZBT_LEAF) { 9844d3c95f5SJorgen Lundman free(l); 9854d3c95f5SJorgen Lundman continue; 9864d3c95f5SJorgen Lundman } 9874d3c95f5SJorgen Lundman if (zfs_to_cpu32(l->l_hdr.lh_magic, endian) != ZAP_LEAF_MAGIC) { 9884d3c95f5SJorgen Lundman free(l); 9894d3c95f5SJorgen Lundman continue; 9904d3c95f5SJorgen Lundman } 9914d3c95f5SJorgen Lundman 9924d3c95f5SJorgen Lundman for (chunk = 0; chunk < ZAP_LEAF_NUMCHUNKS(blksft); chunk++) { 9934d3c95f5SJorgen Lundman char *buf; 9944d3c95f5SJorgen Lundman struct zap_leaf_array *la; 9954d3c95f5SJorgen Lundman struct zap_leaf_entry *le; 9964d3c95f5SJorgen Lundman uint64_t val; 9974d3c95f5SJorgen Lundman le = ZAP_LEAF_ENTRY(l, blksft, chunk); 9984d3c95f5SJorgen Lundman 9994d3c95f5SJorgen Lundman /* Verify the chunk entry */ 10004d3c95f5SJorgen Lundman if (le->le_type != ZAP_CHUNK_ENTRY) 10014d3c95f5SJorgen Lundman continue; 10024d3c95f5SJorgen Lundman 10034d3c95f5SJorgen Lundman buf = malloc(zfs_to_cpu16(le->le_name_length, endian) 10044d3c95f5SJorgen Lundman + 1); 10054d3c95f5SJorgen Lundman if (zap_leaf_array_get(l, endian, blksft, le->le_name_chunk, 10064d3c95f5SJorgen Lundman le->le_name_length, buf)) { 10074d3c95f5SJorgen Lundman free(buf); 10084d3c95f5SJorgen Lundman continue; 10094d3c95f5SJorgen Lundman } 10104d3c95f5SJorgen Lundman buf[le->le_name_length] = 0; 10114d3c95f5SJorgen Lundman 10124d3c95f5SJorgen Lundman if (le->le_int_size != 8 10134d3c95f5SJorgen Lundman || zfs_to_cpu16(le->le_value_length, endian) != 1) 10144d3c95f5SJorgen Lundman continue; 10154d3c95f5SJorgen Lundman 10164d3c95f5SJorgen Lundman /* get the uint64_t property value */ 10174d3c95f5SJorgen Lundman la = &ZAP_LEAF_CHUNK(l, blksft, le->le_value_chunk).l_array; 10184d3c95f5SJorgen Lundman val = be64_to_cpu(la->la_array64); 10194d3c95f5SJorgen Lundman if (hook(buf, val, data)) 10204d3c95f5SJorgen Lundman return 1; 10214d3c95f5SJorgen Lundman free(buf); 10224d3c95f5SJorgen Lundman } 10234d3c95f5SJorgen Lundman } 10244d3c95f5SJorgen Lundman return 0; 10254d3c95f5SJorgen Lundman } 10264d3c95f5SJorgen Lundman 10274d3c95f5SJorgen Lundman 10284d3c95f5SJorgen Lundman /* 10294d3c95f5SJorgen Lundman * Read in the data of a zap object and find the value for a matching 10304d3c95f5SJorgen Lundman * property name. 10314d3c95f5SJorgen Lundman * 10324d3c95f5SJorgen Lundman */ 10334d3c95f5SJorgen Lundman static int 10344d3c95f5SJorgen Lundman zap_lookup(dnode_end_t *zap_dnode, char *name, uint64_t *val, 10354d3c95f5SJorgen Lundman struct zfs_data *data) 10364d3c95f5SJorgen Lundman { 10374d3c95f5SJorgen Lundman uint64_t block_type; 10384d3c95f5SJorgen Lundman int size; 10394d3c95f5SJorgen Lundman void *zapbuf; 10404d3c95f5SJorgen Lundman int err; 10414d3c95f5SJorgen Lundman zfs_endian_t endian; 10424d3c95f5SJorgen Lundman 10434d3c95f5SJorgen Lundman /* Read in the first block of the zap object data. */ 10444d3c95f5SJorgen Lundman size = zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, 10454d3c95f5SJorgen Lundman zap_dnode->endian) << SPA_MINBLOCKSHIFT; 10464d3c95f5SJorgen Lundman err = dmu_read(zap_dnode, 0, &zapbuf, &endian, data); 10474d3c95f5SJorgen Lundman if (err) 10484d3c95f5SJorgen Lundman return err; 10494d3c95f5SJorgen Lundman block_type = zfs_to_cpu64(*((uint64_t *) zapbuf), endian); 10504d3c95f5SJorgen Lundman 10514d3c95f5SJorgen Lundman if (block_type == ZBT_MICRO) { 10524d3c95f5SJorgen Lundman err = (mzap_lookup(zapbuf, endian, size, name, val)); 10534d3c95f5SJorgen Lundman free(zapbuf); 10544d3c95f5SJorgen Lundman return err; 10554d3c95f5SJorgen Lundman } else if (block_type == ZBT_HEADER) { 10564d3c95f5SJorgen Lundman /* this is a fat zap */ 10574d3c95f5SJorgen Lundman err = (fzap_lookup(zap_dnode, zapbuf, name, val, data)); 10584d3c95f5SJorgen Lundman free(zapbuf); 10594d3c95f5SJorgen Lundman return err; 10604d3c95f5SJorgen Lundman } 10614d3c95f5SJorgen Lundman 10624d3c95f5SJorgen Lundman printf("unknown ZAP type\n"); 1063*e183de0dSJorgen Lundman free(zapbuf); 10644d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 10654d3c95f5SJorgen Lundman } 10664d3c95f5SJorgen Lundman 10674d3c95f5SJorgen Lundman static int 10684d3c95f5SJorgen Lundman zap_iterate(dnode_end_t *zap_dnode, 10694d3c95f5SJorgen Lundman int (*hook)(const char *name, uint64_t val, 10704d3c95f5SJorgen Lundman struct zfs_data *data), 10714d3c95f5SJorgen Lundman struct zfs_data *data) 10724d3c95f5SJorgen Lundman { 10734d3c95f5SJorgen Lundman uint64_t block_type; 10744d3c95f5SJorgen Lundman int size; 10754d3c95f5SJorgen Lundman void *zapbuf; 10764d3c95f5SJorgen Lundman int err; 10774d3c95f5SJorgen Lundman int ret; 10784d3c95f5SJorgen Lundman zfs_endian_t endian; 10794d3c95f5SJorgen Lundman 10804d3c95f5SJorgen Lundman /* Read in the first block of the zap object data. */ 10814d3c95f5SJorgen Lundman size = zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT; 10824d3c95f5SJorgen Lundman err = dmu_read(zap_dnode, 0, &zapbuf, &endian, data); 10834d3c95f5SJorgen Lundman if (err) 10844d3c95f5SJorgen Lundman return 0; 10854d3c95f5SJorgen Lundman block_type = zfs_to_cpu64(*((uint64_t *) zapbuf), endian); 10864d3c95f5SJorgen Lundman 10874d3c95f5SJorgen Lundman if (block_type == ZBT_MICRO) { 10884d3c95f5SJorgen Lundman ret = mzap_iterate(zapbuf, endian, size, hook, data); 10894d3c95f5SJorgen Lundman free(zapbuf); 10904d3c95f5SJorgen Lundman return ret; 10914d3c95f5SJorgen Lundman } else if (block_type == ZBT_HEADER) { 10924d3c95f5SJorgen Lundman /* this is a fat zap */ 10934d3c95f5SJorgen Lundman ret = fzap_iterate(zap_dnode, zapbuf, hook, data); 10944d3c95f5SJorgen Lundman free(zapbuf); 10954d3c95f5SJorgen Lundman return ret; 10964d3c95f5SJorgen Lundman } 10974d3c95f5SJorgen Lundman printf("unknown ZAP type\n"); 1098*e183de0dSJorgen Lundman free(zapbuf); 10994d3c95f5SJorgen Lundman return 0; 11004d3c95f5SJorgen Lundman } 11014d3c95f5SJorgen Lundman 11024d3c95f5SJorgen Lundman 11034d3c95f5SJorgen Lundman /* 11044d3c95f5SJorgen Lundman * Get the dnode of an object number from the metadnode of an object set. 11054d3c95f5SJorgen Lundman * 11064d3c95f5SJorgen Lundman * Input 11074d3c95f5SJorgen Lundman * mdn - metadnode to get the object dnode 11084d3c95f5SJorgen Lundman * objnum - object number for the object dnode 11094d3c95f5SJorgen Lundman * buf - data buffer that holds the returning dnode 11104d3c95f5SJorgen Lundman */ 11114d3c95f5SJorgen Lundman static int 11124d3c95f5SJorgen Lundman dnode_get(dnode_end_t *mdn, uint64_t objnum, uint8_t type, 11134d3c95f5SJorgen Lundman dnode_end_t *buf, struct zfs_data *data) 11144d3c95f5SJorgen Lundman { 11154d3c95f5SJorgen Lundman uint64_t blkid, blksz; /* the block id this object dnode is in */ 11164d3c95f5SJorgen Lundman int epbs; /* shift of number of dnodes in a block */ 11174d3c95f5SJorgen Lundman int idx; /* index within a block */ 11184d3c95f5SJorgen Lundman void *dnbuf; 11194d3c95f5SJorgen Lundman int err; 11204d3c95f5SJorgen Lundman zfs_endian_t endian; 11214d3c95f5SJorgen Lundman 11224d3c95f5SJorgen Lundman blksz = zfs_to_cpu16(mdn->dn.dn_datablkszsec, 11234d3c95f5SJorgen Lundman mdn->endian) << SPA_MINBLOCKSHIFT; 11244d3c95f5SJorgen Lundman 11254d3c95f5SJorgen Lundman epbs = zfs_log2(blksz) - DNODE_SHIFT; 11264d3c95f5SJorgen Lundman blkid = objnum >> epbs; 11274d3c95f5SJorgen Lundman idx = objnum & ((1 << epbs) - 1); 11284d3c95f5SJorgen Lundman 11294d3c95f5SJorgen Lundman if (data->dnode_buf != NULL && memcmp(data->dnode_mdn, mdn, 11304d3c95f5SJorgen Lundman sizeof(*mdn)) == 0 11314d3c95f5SJorgen Lundman && objnum >= data->dnode_start && objnum < data->dnode_end) { 11324d3c95f5SJorgen Lundman memmove(&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE); 11334d3c95f5SJorgen Lundman buf->endian = data->dnode_endian; 11344d3c95f5SJorgen Lundman if (type && buf->dn.dn_type != type) { 11354d3c95f5SJorgen Lundman printf("incorrect dnode type: %02X != %02x\n", buf->dn.dn_type, type); 11364d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 11374d3c95f5SJorgen Lundman } 11384d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 11394d3c95f5SJorgen Lundman } 11404d3c95f5SJorgen Lundman 11414d3c95f5SJorgen Lundman err = dmu_read(mdn, blkid, &dnbuf, &endian, data); 11424d3c95f5SJorgen Lundman if (err) 11434d3c95f5SJorgen Lundman return err; 11444d3c95f5SJorgen Lundman 11454d3c95f5SJorgen Lundman free(data->dnode_buf); 11464d3c95f5SJorgen Lundman free(data->dnode_mdn); 11474d3c95f5SJorgen Lundman data->dnode_mdn = malloc(sizeof(*mdn)); 11484d3c95f5SJorgen Lundman if (!data->dnode_mdn) { 11494d3c95f5SJorgen Lundman data->dnode_buf = 0; 11504d3c95f5SJorgen Lundman } else { 11514d3c95f5SJorgen Lundman memcpy(data->dnode_mdn, mdn, sizeof(*mdn)); 11524d3c95f5SJorgen Lundman data->dnode_buf = dnbuf; 11534d3c95f5SJorgen Lundman data->dnode_start = blkid << epbs; 11544d3c95f5SJorgen Lundman data->dnode_end = (blkid + 1) << epbs; 11554d3c95f5SJorgen Lundman data->dnode_endian = endian; 11564d3c95f5SJorgen Lundman } 11574d3c95f5SJorgen Lundman 11584d3c95f5SJorgen Lundman memmove(&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); 11594d3c95f5SJorgen Lundman buf->endian = endian; 11604d3c95f5SJorgen Lundman if (type && buf->dn.dn_type != type) { 11614d3c95f5SJorgen Lundman printf("incorrect dnode type\n"); 11624d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 11634d3c95f5SJorgen Lundman } 11644d3c95f5SJorgen Lundman 11654d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 11664d3c95f5SJorgen Lundman } 11674d3c95f5SJorgen Lundman 11684d3c95f5SJorgen Lundman /* 11694d3c95f5SJorgen Lundman * Get the file dnode for a given file name where mdn is the meta dnode 11704d3c95f5SJorgen Lundman * for this ZFS object set. When found, place the file dnode in dn. 11714d3c95f5SJorgen Lundman * The 'path' argument will be mangled. 11724d3c95f5SJorgen Lundman * 11734d3c95f5SJorgen Lundman */ 11744d3c95f5SJorgen Lundman static int 11754d3c95f5SJorgen Lundman dnode_get_path(dnode_end_t *mdn, const char *path_in, dnode_end_t *dn, 11764d3c95f5SJorgen Lundman struct zfs_data *data) 11774d3c95f5SJorgen Lundman { 11784d3c95f5SJorgen Lundman uint64_t objnum, version; 11794d3c95f5SJorgen Lundman char *cname, ch; 11804d3c95f5SJorgen Lundman int err = ZFS_ERR_NONE; 11814d3c95f5SJorgen Lundman char *path, *path_buf; 11824d3c95f5SJorgen Lundman struct dnode_chain { 11834d3c95f5SJorgen Lundman struct dnode_chain *next; 11844d3c95f5SJorgen Lundman dnode_end_t dn; 11854d3c95f5SJorgen Lundman }; 11864d3c95f5SJorgen Lundman struct dnode_chain *dnode_path = 0, *dn_new, *root; 11874d3c95f5SJorgen Lundman 11884d3c95f5SJorgen Lundman dn_new = malloc(sizeof(*dn_new)); 11894d3c95f5SJorgen Lundman if (!dn_new) 11904d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 11914d3c95f5SJorgen Lundman dn_new->next = 0; 11924d3c95f5SJorgen Lundman dnode_path = root = dn_new; 11934d3c95f5SJorgen Lundman 11944d3c95f5SJorgen Lundman err = dnode_get(mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, 11954d3c95f5SJorgen Lundman &(dnode_path->dn), data); 11964d3c95f5SJorgen Lundman if (err) { 11974d3c95f5SJorgen Lundman free(dn_new); 11984d3c95f5SJorgen Lundman return err; 11994d3c95f5SJorgen Lundman } 12004d3c95f5SJorgen Lundman 12014d3c95f5SJorgen Lundman err = zap_lookup(&(dnode_path->dn), ZPL_VERSION_STR, &version, data); 12024d3c95f5SJorgen Lundman if (err) { 12034d3c95f5SJorgen Lundman free(dn_new); 12044d3c95f5SJorgen Lundman return err; 12054d3c95f5SJorgen Lundman } 12064d3c95f5SJorgen Lundman if (version > ZPL_VERSION) { 12074d3c95f5SJorgen Lundman free(dn_new); 12084d3c95f5SJorgen Lundman printf("too new ZPL version\n"); 12094d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 12104d3c95f5SJorgen Lundman } 12114d3c95f5SJorgen Lundman 12124d3c95f5SJorgen Lundman err = zap_lookup(&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data); 12134d3c95f5SJorgen Lundman if (err) { 12144d3c95f5SJorgen Lundman free(dn_new); 12154d3c95f5SJorgen Lundman return err; 12164d3c95f5SJorgen Lundman } 12174d3c95f5SJorgen Lundman 12184d3c95f5SJorgen Lundman err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data); 12194d3c95f5SJorgen Lundman if (err) { 12204d3c95f5SJorgen Lundman free(dn_new); 12214d3c95f5SJorgen Lundman return err; 12224d3c95f5SJorgen Lundman } 12234d3c95f5SJorgen Lundman 12244d3c95f5SJorgen Lundman path = path_buf = strdup(path_in); 12254d3c95f5SJorgen Lundman if (!path_buf) { 12264d3c95f5SJorgen Lundman free(dn_new); 12274d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 12284d3c95f5SJorgen Lundman } 12294d3c95f5SJorgen Lundman 12304d3c95f5SJorgen Lundman while (1) { 12314d3c95f5SJorgen Lundman /* skip leading slashes */ 12324d3c95f5SJorgen Lundman while (*path == '/') 12334d3c95f5SJorgen Lundman path++; 12344d3c95f5SJorgen Lundman if (!*path) 12354d3c95f5SJorgen Lundman break; 12364d3c95f5SJorgen Lundman /* get the next component name */ 12374d3c95f5SJorgen Lundman cname = path; 12384d3c95f5SJorgen Lundman while (*path && *path != '/') 12394d3c95f5SJorgen Lundman path++; 12404d3c95f5SJorgen Lundman /* Skip dot. */ 12414d3c95f5SJorgen Lundman if (cname + 1 == path && cname[0] == '.') 12424d3c95f5SJorgen Lundman continue; 12434d3c95f5SJorgen Lundman /* Handle double dot. */ 12444d3c95f5SJorgen Lundman if (cname + 2 == path && cname[0] == '.' && cname[1] == '.') { 12454d3c95f5SJorgen Lundman if (dn_new->next) { 12464d3c95f5SJorgen Lundman dn_new = dnode_path; 12474d3c95f5SJorgen Lundman dnode_path = dn_new->next; 12484d3c95f5SJorgen Lundman free(dn_new); 12494d3c95f5SJorgen Lundman } else { 12504d3c95f5SJorgen Lundman printf("can't resolve ..\n"); 12514d3c95f5SJorgen Lundman err = ZFS_ERR_FILE_NOT_FOUND; 12524d3c95f5SJorgen Lundman break; 12534d3c95f5SJorgen Lundman } 12544d3c95f5SJorgen Lundman continue; 12554d3c95f5SJorgen Lundman } 12564d3c95f5SJorgen Lundman 12574d3c95f5SJorgen Lundman ch = *path; 12584d3c95f5SJorgen Lundman *path = 0; /* ensure null termination */ 12594d3c95f5SJorgen Lundman 12604d3c95f5SJorgen Lundman if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) { 12614d3c95f5SJorgen Lundman free(path_buf); 12624d3c95f5SJorgen Lundman printf("not a directory\n"); 12634d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FILE_TYPE; 12644d3c95f5SJorgen Lundman } 12654d3c95f5SJorgen Lundman err = zap_lookup(&(dnode_path->dn), cname, &objnum, data); 12664d3c95f5SJorgen Lundman if (err) 12674d3c95f5SJorgen Lundman break; 12684d3c95f5SJorgen Lundman 12694d3c95f5SJorgen Lundman dn_new = malloc(sizeof(*dn_new)); 12704d3c95f5SJorgen Lundman if (!dn_new) { 12714d3c95f5SJorgen Lundman err = ZFS_ERR_OUT_OF_MEMORY; 12724d3c95f5SJorgen Lundman break; 12734d3c95f5SJorgen Lundman } 12744d3c95f5SJorgen Lundman dn_new->next = dnode_path; 12754d3c95f5SJorgen Lundman dnode_path = dn_new; 12764d3c95f5SJorgen Lundman 12774d3c95f5SJorgen Lundman objnum = ZFS_DIRENT_OBJ(objnum); 12784d3c95f5SJorgen Lundman err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data); 12794d3c95f5SJorgen Lundman if (err) 12804d3c95f5SJorgen Lundman break; 12814d3c95f5SJorgen Lundman 12824d3c95f5SJorgen Lundman *path = ch; 12834d3c95f5SJorgen Lundman } 12844d3c95f5SJorgen Lundman 12854d3c95f5SJorgen Lundman if (!err) 12864d3c95f5SJorgen Lundman memcpy(dn, &(dnode_path->dn), sizeof(*dn)); 12874d3c95f5SJorgen Lundman 12884d3c95f5SJorgen Lundman while (dnode_path) { 12894d3c95f5SJorgen Lundman dn_new = dnode_path->next; 12904d3c95f5SJorgen Lundman free(dnode_path); 12914d3c95f5SJorgen Lundman dnode_path = dn_new; 12924d3c95f5SJorgen Lundman } 12934d3c95f5SJorgen Lundman free(path_buf); 12944d3c95f5SJorgen Lundman return err; 12954d3c95f5SJorgen Lundman } 12964d3c95f5SJorgen Lundman 12974d3c95f5SJorgen Lundman 12984d3c95f5SJorgen Lundman /* 12994d3c95f5SJorgen Lundman * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), 13004d3c95f5SJorgen Lundman * e.g. pool/rootfs, or a given object number (obj), e.g. the object number 13014d3c95f5SJorgen Lundman * of pool/rootfs. 13024d3c95f5SJorgen Lundman * 13034d3c95f5SJorgen Lundman * If no fsname and no obj are given, return the DSL_DIR metadnode. 13044d3c95f5SJorgen Lundman * If fsname is given, return its metadnode and its matching object number. 13054d3c95f5SJorgen Lundman * If only obj is given, return the metadnode for this object number. 13064d3c95f5SJorgen Lundman * 13074d3c95f5SJorgen Lundman */ 13084d3c95f5SJorgen Lundman static int 13094d3c95f5SJorgen Lundman get_filesystem_dnode(dnode_end_t *mosmdn, char *fsname, 13104d3c95f5SJorgen Lundman dnode_end_t *mdn, struct zfs_data *data) 13114d3c95f5SJorgen Lundman { 13124d3c95f5SJorgen Lundman uint64_t objnum; 13134d3c95f5SJorgen Lundman int err; 13144d3c95f5SJorgen Lundman 13154d3c95f5SJorgen Lundman err = dnode_get(mosmdn, DMU_POOL_DIRECTORY_OBJECT, 13164d3c95f5SJorgen Lundman DMU_OT_OBJECT_DIRECTORY, mdn, data); 13174d3c95f5SJorgen Lundman if (err) 13184d3c95f5SJorgen Lundman return err; 13194d3c95f5SJorgen Lundman 13204d3c95f5SJorgen Lundman err = zap_lookup(mdn, DMU_POOL_ROOT_DATASET, &objnum, data); 13214d3c95f5SJorgen Lundman if (err) 13224d3c95f5SJorgen Lundman return err; 13234d3c95f5SJorgen Lundman 13244d3c95f5SJorgen Lundman err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); 13254d3c95f5SJorgen Lundman if (err) 13264d3c95f5SJorgen Lundman return err; 13274d3c95f5SJorgen Lundman 13284d3c95f5SJorgen Lundman while (*fsname) { 13294d3c95f5SJorgen Lundman uint64_t childobj; 13304d3c95f5SJorgen Lundman char *cname, ch; 13314d3c95f5SJorgen Lundman 13324d3c95f5SJorgen Lundman while (*fsname == '/') 13334d3c95f5SJorgen Lundman fsname++; 13344d3c95f5SJorgen Lundman 13354d3c95f5SJorgen Lundman if (!*fsname || *fsname == '@') 13364d3c95f5SJorgen Lundman break; 13374d3c95f5SJorgen Lundman 13384d3c95f5SJorgen Lundman cname = fsname; 13394d3c95f5SJorgen Lundman while (*fsname && !isspace(*fsname) && *fsname != '/') 13404d3c95f5SJorgen Lundman fsname++; 13414d3c95f5SJorgen Lundman ch = *fsname; 13424d3c95f5SJorgen Lundman *fsname = 0; 13434d3c95f5SJorgen Lundman 13444d3c95f5SJorgen Lundman childobj = zfs_to_cpu64((((dsl_dir_phys_t *) DN_BONUS(&mdn->dn)))->dd_child_dir_zapobj, mdn->endian); 13454d3c95f5SJorgen Lundman err = dnode_get(mosmdn, childobj, 13464d3c95f5SJorgen Lundman DMU_OT_DSL_DIR_CHILD_MAP, mdn, data); 13474d3c95f5SJorgen Lundman if (err) 13484d3c95f5SJorgen Lundman return err; 13494d3c95f5SJorgen Lundman 13504d3c95f5SJorgen Lundman err = zap_lookup(mdn, cname, &objnum, data); 13514d3c95f5SJorgen Lundman if (err) 13524d3c95f5SJorgen Lundman return err; 13534d3c95f5SJorgen Lundman 13544d3c95f5SJorgen Lundman err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); 13554d3c95f5SJorgen Lundman if (err) 13564d3c95f5SJorgen Lundman return err; 13574d3c95f5SJorgen Lundman 13584d3c95f5SJorgen Lundman *fsname = ch; 13594d3c95f5SJorgen Lundman } 13604d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 13614d3c95f5SJorgen Lundman } 13624d3c95f5SJorgen Lundman 13634d3c95f5SJorgen Lundman static int 13644d3c95f5SJorgen Lundman make_mdn(dnode_end_t *mdn, struct zfs_data *data) 13654d3c95f5SJorgen Lundman { 13664d3c95f5SJorgen Lundman void *osp; 13674d3c95f5SJorgen Lundman blkptr_t *bp; 13684d3c95f5SJorgen Lundman size_t ospsize; 13694d3c95f5SJorgen Lundman int err; 13704d3c95f5SJorgen Lundman 13714d3c95f5SJorgen Lundman bp = &(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_bp); 13724d3c95f5SJorgen Lundman err = zio_read(bp, mdn->endian, &osp, &ospsize, data); 13734d3c95f5SJorgen Lundman if (err) 13744d3c95f5SJorgen Lundman return err; 13754d3c95f5SJorgen Lundman if (ospsize < OBJSET_PHYS_SIZE_V14) { 13764d3c95f5SJorgen Lundman free(osp); 13774d3c95f5SJorgen Lundman printf("too small osp\n"); 13784d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 13794d3c95f5SJorgen Lundman } 13804d3c95f5SJorgen Lundman 13814d3c95f5SJorgen Lundman mdn->endian = (zfs_to_cpu64(bp->blk_prop, mdn->endian)>>63) & 1; 13824d3c95f5SJorgen Lundman memmove((char *) &(mdn->dn), 13834d3c95f5SJorgen Lundman (char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); 13844d3c95f5SJorgen Lundman free(osp); 13854d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 13864d3c95f5SJorgen Lundman } 13874d3c95f5SJorgen Lundman 13884d3c95f5SJorgen Lundman static int 13894d3c95f5SJorgen Lundman dnode_get_fullpath(const char *fullpath, dnode_end_t *mdn, 13904d3c95f5SJorgen Lundman uint64_t *mdnobj, dnode_end_t *dn, int *isfs, 13914d3c95f5SJorgen Lundman struct zfs_data *data) 13924d3c95f5SJorgen Lundman { 13934d3c95f5SJorgen Lundman char *fsname, *snapname; 13944d3c95f5SJorgen Lundman const char *ptr_at, *filename; 13954d3c95f5SJorgen Lundman uint64_t headobj; 13964d3c95f5SJorgen Lundman int err; 13974d3c95f5SJorgen Lundman 13984d3c95f5SJorgen Lundman ptr_at = strchr(fullpath, '@'); 13994d3c95f5SJorgen Lundman if (!ptr_at) { 14004d3c95f5SJorgen Lundman *isfs = 1; 14014d3c95f5SJorgen Lundman filename = 0; 14024d3c95f5SJorgen Lundman snapname = 0; 14034d3c95f5SJorgen Lundman fsname = strdup(fullpath); 14044d3c95f5SJorgen Lundman } else { 14054d3c95f5SJorgen Lundman const char *ptr_slash = strchr(ptr_at, '/'); 14064d3c95f5SJorgen Lundman 14074d3c95f5SJorgen Lundman *isfs = 0; 14084d3c95f5SJorgen Lundman fsname = malloc(ptr_at - fullpath + 1); 14094d3c95f5SJorgen Lundman if (!fsname) 14104d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 14114d3c95f5SJorgen Lundman memcpy(fsname, fullpath, ptr_at - fullpath); 14124d3c95f5SJorgen Lundman fsname[ptr_at - fullpath] = 0; 14134d3c95f5SJorgen Lundman if (ptr_at[1] && ptr_at[1] != '/') { 14144d3c95f5SJorgen Lundman snapname = malloc(ptr_slash - ptr_at); 14154d3c95f5SJorgen Lundman if (!snapname) { 14164d3c95f5SJorgen Lundman free(fsname); 14174d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 14184d3c95f5SJorgen Lundman } 14194d3c95f5SJorgen Lundman memcpy(snapname, ptr_at + 1, ptr_slash - ptr_at - 1); 14204d3c95f5SJorgen Lundman snapname[ptr_slash - ptr_at - 1] = 0; 14214d3c95f5SJorgen Lundman } else { 14224d3c95f5SJorgen Lundman snapname = 0; 14234d3c95f5SJorgen Lundman } 14244d3c95f5SJorgen Lundman if (ptr_slash) 14254d3c95f5SJorgen Lundman filename = ptr_slash; 14264d3c95f5SJorgen Lundman else 14274d3c95f5SJorgen Lundman filename = "/"; 14284d3c95f5SJorgen Lundman printf("zfs fsname = '%s' snapname='%s' filename = '%s'\n", 14294d3c95f5SJorgen Lundman fsname, snapname, filename); 14304d3c95f5SJorgen Lundman } 14314d3c95f5SJorgen Lundman 14324d3c95f5SJorgen Lundman 14334d3c95f5SJorgen Lundman err = get_filesystem_dnode(&(data->mos), fsname, dn, data); 14344d3c95f5SJorgen Lundman 14354d3c95f5SJorgen Lundman if (err) { 14364d3c95f5SJorgen Lundman free(fsname); 14374d3c95f5SJorgen Lundman free(snapname); 14384d3c95f5SJorgen Lundman return err; 14394d3c95f5SJorgen Lundman } 14404d3c95f5SJorgen Lundman 14414d3c95f5SJorgen Lundman headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&dn->dn))->dd_head_dataset_obj, dn->endian); 14424d3c95f5SJorgen Lundman 14434d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data); 14444d3c95f5SJorgen Lundman if (err) { 14454d3c95f5SJorgen Lundman free(fsname); 14464d3c95f5SJorgen Lundman free(snapname); 14474d3c95f5SJorgen Lundman return err; 14484d3c95f5SJorgen Lundman } 14494d3c95f5SJorgen Lundman 14504d3c95f5SJorgen Lundman if (snapname) { 14514d3c95f5SJorgen Lundman uint64_t snapobj; 14524d3c95f5SJorgen Lundman 14534d3c95f5SJorgen Lundman snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_snapnames_zapobj, mdn->endian); 14544d3c95f5SJorgen Lundman 14554d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), snapobj, 14564d3c95f5SJorgen Lundman DMU_OT_DSL_DS_SNAP_MAP, mdn, data); 14574d3c95f5SJorgen Lundman if (!err) 14584d3c95f5SJorgen Lundman err = zap_lookup(mdn, snapname, &headobj, data); 14594d3c95f5SJorgen Lundman if (!err) 14604d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data); 14614d3c95f5SJorgen Lundman if (err) { 14624d3c95f5SJorgen Lundman free(fsname); 14634d3c95f5SJorgen Lundman free(snapname); 14644d3c95f5SJorgen Lundman return err; 14654d3c95f5SJorgen Lundman } 14664d3c95f5SJorgen Lundman } 14674d3c95f5SJorgen Lundman 14684d3c95f5SJorgen Lundman if (mdnobj) 14694d3c95f5SJorgen Lundman *mdnobj = headobj; 14704d3c95f5SJorgen Lundman 14714d3c95f5SJorgen Lundman make_mdn(mdn, data); 14724d3c95f5SJorgen Lundman 14734d3c95f5SJorgen Lundman if (*isfs) { 14744d3c95f5SJorgen Lundman free(fsname); 14754d3c95f5SJorgen Lundman free(snapname); 14764d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 14774d3c95f5SJorgen Lundman } 14784d3c95f5SJorgen Lundman err = dnode_get_path(mdn, filename, dn, data); 14794d3c95f5SJorgen Lundman free(fsname); 14804d3c95f5SJorgen Lundman free(snapname); 14814d3c95f5SJorgen Lundman return err; 14824d3c95f5SJorgen Lundman } 14834d3c95f5SJorgen Lundman 14844d3c95f5SJorgen Lundman /* 14854d3c95f5SJorgen Lundman * For a given XDR packed nvlist, verify the first 4 bytes and move on. 14864d3c95f5SJorgen Lundman * 14874d3c95f5SJorgen Lundman * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) : 14884d3c95f5SJorgen Lundman * 14894d3c95f5SJorgen Lundman * encoding method/host endian (4 bytes) 14904d3c95f5SJorgen Lundman * nvl_version (4 bytes) 14914d3c95f5SJorgen Lundman * nvl_nvflag (4 bytes) 14924d3c95f5SJorgen Lundman * encoded nvpairs: 14934d3c95f5SJorgen Lundman * encoded size of the nvpair (4 bytes) 14944d3c95f5SJorgen Lundman * decoded size of the nvpair (4 bytes) 14954d3c95f5SJorgen Lundman * name string size (4 bytes) 14964d3c95f5SJorgen Lundman * name string data (sizeof(NV_ALIGN4(string)) 14974d3c95f5SJorgen Lundman * data type (4 bytes) 14984d3c95f5SJorgen Lundman * # of elements in the nvpair (4 bytes) 14994d3c95f5SJorgen Lundman * data 15004d3c95f5SJorgen Lundman * 2 zero's for the last nvpair 15014d3c95f5SJorgen Lundman * (end of the entire list) (8 bytes) 15024d3c95f5SJorgen Lundman * 15034d3c95f5SJorgen Lundman */ 15044d3c95f5SJorgen Lundman 15054d3c95f5SJorgen Lundman static int 15064d3c95f5SJorgen Lundman nvlist_find_value(char *nvlist, char *name, int valtype, char **val, 15074d3c95f5SJorgen Lundman size_t *size_out, size_t *nelm_out) 15084d3c95f5SJorgen Lundman { 15094d3c95f5SJorgen Lundman int name_len, type, encode_size; 15104d3c95f5SJorgen Lundman char *nvpair, *nvp_name; 15114d3c95f5SJorgen Lundman 15124d3c95f5SJorgen Lundman /* Verify if the 1st and 2nd byte in the nvlist are valid. */ 15134d3c95f5SJorgen Lundman /* NOTE: independently of what endianness header announces all 15144d3c95f5SJorgen Lundman subsequent values are big-endian. */ 15154d3c95f5SJorgen Lundman if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN 15164d3c95f5SJorgen Lundman && nvlist[1] != NV_BIG_ENDIAN)) { 15174d3c95f5SJorgen Lundman printf("zfs incorrect nvlist header\n"); 15184d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 15194d3c95f5SJorgen Lundman } 15204d3c95f5SJorgen Lundman 15214d3c95f5SJorgen Lundman /* skip the header, nvl_version, and nvl_nvflag */ 15224d3c95f5SJorgen Lundman nvlist = nvlist + 4 * 3; 15234d3c95f5SJorgen Lundman /* 15244d3c95f5SJorgen Lundman * Loop thru the nvpair list 15254d3c95f5SJorgen Lundman * The XDR representation of an integer is in big-endian byte order. 15264d3c95f5SJorgen Lundman */ 15274d3c95f5SJorgen Lundman while ((encode_size = be32_to_cpu(*(uint32_t *) nvlist))) { 15284d3c95f5SJorgen Lundman int nelm; 15294d3c95f5SJorgen Lundman 15304d3c95f5SJorgen Lundman nvpair = nvlist + 4 * 2; /* skip the encode/decode size */ 15314d3c95f5SJorgen Lundman 15324d3c95f5SJorgen Lundman name_len = be32_to_cpu(*(uint32_t *) nvpair); 15334d3c95f5SJorgen Lundman nvpair += 4; 15344d3c95f5SJorgen Lundman 15354d3c95f5SJorgen Lundman nvp_name = nvpair; 15364d3c95f5SJorgen Lundman nvpair = nvpair + ((name_len + 3) & ~3); /* align */ 15374d3c95f5SJorgen Lundman 15384d3c95f5SJorgen Lundman type = be32_to_cpu(*(uint32_t *) nvpair); 15394d3c95f5SJorgen Lundman nvpair += 4; 15404d3c95f5SJorgen Lundman 15414d3c95f5SJorgen Lundman nelm = be32_to_cpu(*(uint32_t *) nvpair); 15424d3c95f5SJorgen Lundman if (nelm < 1) { 15434d3c95f5SJorgen Lundman printf("empty nvpair\n"); 15444d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 15454d3c95f5SJorgen Lundman } 15464d3c95f5SJorgen Lundman 15474d3c95f5SJorgen Lundman nvpair += 4; 15484d3c95f5SJorgen Lundman 15494d3c95f5SJorgen Lundman if ((strncmp(nvp_name, name, name_len) == 0) && type == valtype) { 15504d3c95f5SJorgen Lundman *val = nvpair; 15514d3c95f5SJorgen Lundman *size_out = encode_size; 15524d3c95f5SJorgen Lundman if (nelm_out) 15534d3c95f5SJorgen Lundman *nelm_out = nelm; 15544d3c95f5SJorgen Lundman return 1; 15554d3c95f5SJorgen Lundman } 15564d3c95f5SJorgen Lundman 15574d3c95f5SJorgen Lundman nvlist += encode_size; /* goto the next nvpair */ 15584d3c95f5SJorgen Lundman } 15594d3c95f5SJorgen Lundman return 0; 15604d3c95f5SJorgen Lundman } 15614d3c95f5SJorgen Lundman 15624d3c95f5SJorgen Lundman int 15634d3c95f5SJorgen Lundman zfs_nvlist_lookup_uint64(char *nvlist, char *name, uint64_t *out) 15644d3c95f5SJorgen Lundman { 15654d3c95f5SJorgen Lundman char *nvpair; 15664d3c95f5SJorgen Lundman size_t size; 15674d3c95f5SJorgen Lundman int found; 15684d3c95f5SJorgen Lundman 15694d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0); 15704d3c95f5SJorgen Lundman if (!found) 15714d3c95f5SJorgen Lundman return 0; 15724d3c95f5SJorgen Lundman if (size < sizeof(uint64_t)) { 15734d3c95f5SJorgen Lundman printf("invalid uint64\n"); 15744d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 15754d3c95f5SJorgen Lundman } 15764d3c95f5SJorgen Lundman 15774d3c95f5SJorgen Lundman *out = be64_to_cpu(*(uint64_t *) nvpair); 15784d3c95f5SJorgen Lundman return 1; 15794d3c95f5SJorgen Lundman } 15804d3c95f5SJorgen Lundman 15814d3c95f5SJorgen Lundman char * 15824d3c95f5SJorgen Lundman zfs_nvlist_lookup_string(char *nvlist, char *name) 15834d3c95f5SJorgen Lundman { 15844d3c95f5SJorgen Lundman char *nvpair; 15854d3c95f5SJorgen Lundman char *ret; 15864d3c95f5SJorgen Lundman size_t slen; 15874d3c95f5SJorgen Lundman size_t size; 15884d3c95f5SJorgen Lundman int found; 15894d3c95f5SJorgen Lundman 15904d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0); 15914d3c95f5SJorgen Lundman if (!found) 15924d3c95f5SJorgen Lundman return 0; 15934d3c95f5SJorgen Lundman if (size < 4) { 15944d3c95f5SJorgen Lundman printf("invalid string\n"); 15954d3c95f5SJorgen Lundman return 0; 15964d3c95f5SJorgen Lundman } 15974d3c95f5SJorgen Lundman slen = be32_to_cpu(*(uint32_t *) nvpair); 15984d3c95f5SJorgen Lundman if (slen > size - 4) 15994d3c95f5SJorgen Lundman slen = size - 4; 16004d3c95f5SJorgen Lundman ret = malloc(slen + 1); 16014d3c95f5SJorgen Lundman if (!ret) 16024d3c95f5SJorgen Lundman return 0; 16034d3c95f5SJorgen Lundman memcpy(ret, nvpair + 4, slen); 16044d3c95f5SJorgen Lundman ret[slen] = 0; 16054d3c95f5SJorgen Lundman return ret; 16064d3c95f5SJorgen Lundman } 16074d3c95f5SJorgen Lundman 16084d3c95f5SJorgen Lundman char * 16094d3c95f5SJorgen Lundman zfs_nvlist_lookup_nvlist(char *nvlist, char *name) 16104d3c95f5SJorgen Lundman { 16114d3c95f5SJorgen Lundman char *nvpair; 16124d3c95f5SJorgen Lundman char *ret; 16134d3c95f5SJorgen Lundman size_t size; 16144d3c95f5SJorgen Lundman int found; 16154d3c95f5SJorgen Lundman 16164d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, 16174d3c95f5SJorgen Lundman &size, 0); 16184d3c95f5SJorgen Lundman if (!found) 16194d3c95f5SJorgen Lundman return 0; 16204d3c95f5SJorgen Lundman ret = calloc(1, size + 3 * sizeof(uint32_t)); 16214d3c95f5SJorgen Lundman if (!ret) 16224d3c95f5SJorgen Lundman return 0; 16234d3c95f5SJorgen Lundman memcpy(ret, nvlist, sizeof(uint32_t)); 16244d3c95f5SJorgen Lundman 16254d3c95f5SJorgen Lundman memcpy(ret + sizeof(uint32_t), nvpair, size); 16264d3c95f5SJorgen Lundman return ret; 16274d3c95f5SJorgen Lundman } 16284d3c95f5SJorgen Lundman 16294d3c95f5SJorgen Lundman int 16304d3c95f5SJorgen Lundman zfs_nvlist_lookup_nvlist_array_get_nelm(char *nvlist, char *name) 16314d3c95f5SJorgen Lundman { 16324d3c95f5SJorgen Lundman char *nvpair; 16334d3c95f5SJorgen Lundman size_t nelm, size; 16344d3c95f5SJorgen Lundman int found; 16354d3c95f5SJorgen Lundman 16364d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, 16374d3c95f5SJorgen Lundman &size, &nelm); 16384d3c95f5SJorgen Lundman if (!found) 16394d3c95f5SJorgen Lundman return -1; 16404d3c95f5SJorgen Lundman return nelm; 16414d3c95f5SJorgen Lundman } 16424d3c95f5SJorgen Lundman 16434d3c95f5SJorgen Lundman char * 16444d3c95f5SJorgen Lundman zfs_nvlist_lookup_nvlist_array(char *nvlist, char *name, 16454d3c95f5SJorgen Lundman size_t index) 16464d3c95f5SJorgen Lundman { 16474d3c95f5SJorgen Lundman char *nvpair, *nvpairptr; 16484d3c95f5SJorgen Lundman int found; 16494d3c95f5SJorgen Lundman char *ret; 16504d3c95f5SJorgen Lundman size_t size; 16514d3c95f5SJorgen Lundman unsigned i; 16524d3c95f5SJorgen Lundman size_t nelm; 16534d3c95f5SJorgen Lundman 16544d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, 16554d3c95f5SJorgen Lundman &size, &nelm); 16564d3c95f5SJorgen Lundman if (!found) 16574d3c95f5SJorgen Lundman return 0; 16584d3c95f5SJorgen Lundman if (index >= nelm) { 16594d3c95f5SJorgen Lundman printf("trying to lookup past nvlist array\n"); 16604d3c95f5SJorgen Lundman return 0; 16614d3c95f5SJorgen Lundman } 16624d3c95f5SJorgen Lundman 16634d3c95f5SJorgen Lundman nvpairptr = nvpair; 16644d3c95f5SJorgen Lundman 16654d3c95f5SJorgen Lundman for (i = 0; i < index; i++) { 16664d3c95f5SJorgen Lundman uint32_t encode_size; 16674d3c95f5SJorgen Lundman 16684d3c95f5SJorgen Lundman /* skip the header, nvl_version, and nvl_nvflag */ 16694d3c95f5SJorgen Lundman nvpairptr = nvpairptr + 4 * 2; 16704d3c95f5SJorgen Lundman 16714d3c95f5SJorgen Lundman while (nvpairptr < nvpair + size 16724d3c95f5SJorgen Lundman && (encode_size = be32_to_cpu(*(uint32_t *) nvpairptr))) 16734d3c95f5SJorgen Lundman nvlist += encode_size; /* goto the next nvpair */ 16744d3c95f5SJorgen Lundman 16754d3c95f5SJorgen Lundman nvlist = nvlist + 4 * 2; /* skip the ending 2 zeros - 8 bytes */ 16764d3c95f5SJorgen Lundman } 16774d3c95f5SJorgen Lundman 16784d3c95f5SJorgen Lundman if (nvpairptr >= nvpair + size 16794d3c95f5SJorgen Lundman || nvpairptr + be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2)) 16804d3c95f5SJorgen Lundman >= nvpair + size) { 16814d3c95f5SJorgen Lundman printf("incorrect nvlist array\n"); 16824d3c95f5SJorgen Lundman return 0; 16834d3c95f5SJorgen Lundman } 16844d3c95f5SJorgen Lundman 16854d3c95f5SJorgen Lundman ret = calloc(1, be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2)) 16864d3c95f5SJorgen Lundman + 3 * sizeof(uint32_t)); 16874d3c95f5SJorgen Lundman if (!ret) 16884d3c95f5SJorgen Lundman return 0; 16894d3c95f5SJorgen Lundman memcpy(ret, nvlist, sizeof(uint32_t)); 16904d3c95f5SJorgen Lundman 16914d3c95f5SJorgen Lundman memcpy(ret + sizeof(uint32_t), nvpairptr, size); 16924d3c95f5SJorgen Lundman return ret; 16934d3c95f5SJorgen Lundman } 16944d3c95f5SJorgen Lundman 16954d3c95f5SJorgen Lundman static int 16964d3c95f5SJorgen Lundman int_zfs_fetch_nvlist(struct zfs_data *data, char **nvlist) 16974d3c95f5SJorgen Lundman { 16984d3c95f5SJorgen Lundman int err; 16994d3c95f5SJorgen Lundman 17004d3c95f5SJorgen Lundman *nvlist = malloc(VDEV_PHYS_SIZE); 17014d3c95f5SJorgen Lundman /* Read in the vdev name-value pair list (112K). */ 17024d3c95f5SJorgen Lundman err = zfs_devread(data->vdev_phys_sector, 0, VDEV_PHYS_SIZE, *nvlist); 17034d3c95f5SJorgen Lundman if (err) { 17044d3c95f5SJorgen Lundman free(*nvlist); 17054d3c95f5SJorgen Lundman *nvlist = 0; 17064d3c95f5SJorgen Lundman return err; 17074d3c95f5SJorgen Lundman } 17084d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 17094d3c95f5SJorgen Lundman } 17104d3c95f5SJorgen Lundman 17114d3c95f5SJorgen Lundman /* 17124d3c95f5SJorgen Lundman * Check the disk label information and retrieve needed vdev name-value pairs. 17134d3c95f5SJorgen Lundman * 17144d3c95f5SJorgen Lundman */ 17154d3c95f5SJorgen Lundman static int 17164d3c95f5SJorgen Lundman check_pool_label(struct zfs_data *data) 17174d3c95f5SJorgen Lundman { 17184d3c95f5SJorgen Lundman uint64_t pool_state; 17194d3c95f5SJorgen Lundman char *nvlist; /* for the pool */ 17204d3c95f5SJorgen Lundman char *vdevnvlist; /* for the vdev */ 17214d3c95f5SJorgen Lundman uint64_t diskguid; 17224d3c95f5SJorgen Lundman uint64_t version; 17234d3c95f5SJorgen Lundman int found; 17244d3c95f5SJorgen Lundman int err; 17254d3c95f5SJorgen Lundman 17264d3c95f5SJorgen Lundman err = int_zfs_fetch_nvlist(data, &nvlist); 17274d3c95f5SJorgen Lundman if (err) 17284d3c95f5SJorgen Lundman return err; 17294d3c95f5SJorgen Lundman 17304d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_STATE, 17314d3c95f5SJorgen Lundman &pool_state); 17324d3c95f5SJorgen Lundman if (!found) { 17334d3c95f5SJorgen Lundman free(nvlist); 17344d3c95f5SJorgen Lundman printf("zfs pool state not found\n"); 17354d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17364d3c95f5SJorgen Lundman } 17374d3c95f5SJorgen Lundman 17384d3c95f5SJorgen Lundman if (pool_state == POOL_STATE_DESTROYED) { 17394d3c95f5SJorgen Lundman free(nvlist); 17404d3c95f5SJorgen Lundman printf("zpool is marked as destroyed\n"); 17414d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17424d3c95f5SJorgen Lundman } 17434d3c95f5SJorgen Lundman 17444d3c95f5SJorgen Lundman data->label_txg = 0; 17454d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_TXG, 17464d3c95f5SJorgen Lundman &data->label_txg); 17474d3c95f5SJorgen Lundman if (!found) { 17484d3c95f5SJorgen Lundman free(nvlist); 17494d3c95f5SJorgen Lundman printf("zfs pool txg not found\n"); 17504d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17514d3c95f5SJorgen Lundman } 17524d3c95f5SJorgen Lundman 17534d3c95f5SJorgen Lundman /* not an active device */ 17544d3c95f5SJorgen Lundman if (data->label_txg == 0) { 17554d3c95f5SJorgen Lundman free(nvlist); 17564d3c95f5SJorgen Lundman printf("zpool is not active\n"); 17574d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17584d3c95f5SJorgen Lundman } 17594d3c95f5SJorgen Lundman 17604d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_VERSION, 17614d3c95f5SJorgen Lundman &version); 17624d3c95f5SJorgen Lundman if (!found) { 17634d3c95f5SJorgen Lundman free(nvlist); 17644d3c95f5SJorgen Lundman printf("zpool config version not found\n"); 17654d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17664d3c95f5SJorgen Lundman } 17674d3c95f5SJorgen Lundman 17684d3c95f5SJorgen Lundman if (version > SPA_VERSION) { 17694d3c95f5SJorgen Lundman free(nvlist); 17704d3c95f5SJorgen Lundman printf("SPA version too new %llu > %llu\n", 17714d3c95f5SJorgen Lundman (unsigned long long) version, 17724d3c95f5SJorgen Lundman (unsigned long long) SPA_VERSION); 17734d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 17744d3c95f5SJorgen Lundman } 17754d3c95f5SJorgen Lundman 17764d3c95f5SJorgen Lundman vdevnvlist = zfs_nvlist_lookup_nvlist(nvlist, ZPOOL_CONFIG_VDEV_TREE); 17774d3c95f5SJorgen Lundman if (!vdevnvlist) { 17784d3c95f5SJorgen Lundman free(nvlist); 17794d3c95f5SJorgen Lundman printf("ZFS config vdev tree not found\n"); 17804d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17814d3c95f5SJorgen Lundman } 17824d3c95f5SJorgen Lundman 17834d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(vdevnvlist, ZPOOL_CONFIG_ASHIFT, 17844d3c95f5SJorgen Lundman &data->vdev_ashift); 17854d3c95f5SJorgen Lundman free(vdevnvlist); 17864d3c95f5SJorgen Lundman if (!found) { 17874d3c95f5SJorgen Lundman free(nvlist); 17884d3c95f5SJorgen Lundman printf("ZPOOL config ashift not found\n"); 17894d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17904d3c95f5SJorgen Lundman } 17914d3c95f5SJorgen Lundman 17924d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_GUID, &diskguid); 17934d3c95f5SJorgen Lundman if (!found) { 17944d3c95f5SJorgen Lundman free(nvlist); 17954d3c95f5SJorgen Lundman printf("ZPOOL config guid not found\n"); 17964d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17974d3c95f5SJorgen Lundman } 17984d3c95f5SJorgen Lundman 17994d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_GUID, &data->pool_guid); 18004d3c95f5SJorgen Lundman if (!found) { 18014d3c95f5SJorgen Lundman free(nvlist); 18024d3c95f5SJorgen Lundman printf("ZPOOL config pool guid not found\n"); 18034d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 18044d3c95f5SJorgen Lundman } 18054d3c95f5SJorgen Lundman 18064d3c95f5SJorgen Lundman free(nvlist); 18074d3c95f5SJorgen Lundman 18084d3c95f5SJorgen Lundman printf("ZFS Pool GUID: %llu (%016llx) Label: GUID: %llu (%016llx), txg: %llu, SPA v%llu, ashift: %llu\n", 18094d3c95f5SJorgen Lundman (unsigned long long) data->pool_guid, 18104d3c95f5SJorgen Lundman (unsigned long long) data->pool_guid, 18114d3c95f5SJorgen Lundman (unsigned long long) diskguid, 18124d3c95f5SJorgen Lundman (unsigned long long) diskguid, 18134d3c95f5SJorgen Lundman (unsigned long long) data->label_txg, 18144d3c95f5SJorgen Lundman (unsigned long long) version, 18154d3c95f5SJorgen Lundman (unsigned long long) data->vdev_ashift); 18164d3c95f5SJorgen Lundman 18174d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 18184d3c95f5SJorgen Lundman } 18194d3c95f5SJorgen Lundman 18204d3c95f5SJorgen Lundman /* 18214d3c95f5SJorgen Lundman * vdev_label_start returns the physical disk offset (in bytes) of 18224d3c95f5SJorgen Lundman * label "l". 18234d3c95f5SJorgen Lundman */ 18244d3c95f5SJorgen Lundman static uint64_t vdev_label_start(uint64_t psize, int l) 18254d3c95f5SJorgen Lundman { 18264d3c95f5SJorgen Lundman return (l * sizeof(vdev_label_t) + (l < VDEV_LABELS / 2 ? 18274d3c95f5SJorgen Lundman 0 : psize - 18284d3c95f5SJorgen Lundman VDEV_LABELS * sizeof(vdev_label_t))); 18294d3c95f5SJorgen Lundman } 18304d3c95f5SJorgen Lundman 18314d3c95f5SJorgen Lundman void 18324d3c95f5SJorgen Lundman zfs_unmount(struct zfs_data *data) 18334d3c95f5SJorgen Lundman { 18344d3c95f5SJorgen Lundman free(data->dnode_buf); 18354d3c95f5SJorgen Lundman free(data->dnode_mdn); 18364d3c95f5SJorgen Lundman free(data->file_buf); 18374d3c95f5SJorgen Lundman free(data); 18384d3c95f5SJorgen Lundman } 18394d3c95f5SJorgen Lundman 18404d3c95f5SJorgen Lundman /* 18414d3c95f5SJorgen Lundman * zfs_mount() locates a valid uberblock of the root pool and read in its MOS 18424d3c95f5SJorgen Lundman * to the memory address MOS. 18434d3c95f5SJorgen Lundman * 18444d3c95f5SJorgen Lundman */ 18454d3c95f5SJorgen Lundman struct zfs_data * 18464d3c95f5SJorgen Lundman zfs_mount(device_t dev) 18474d3c95f5SJorgen Lundman { 18484d3c95f5SJorgen Lundman struct zfs_data *data = 0; 18494d3c95f5SJorgen Lundman int label = 0, bestlabel = -1; 18504d3c95f5SJorgen Lundman char *ub_array; 18514d3c95f5SJorgen Lundman uberblock_t *ubbest; 18524d3c95f5SJorgen Lundman uberblock_t *ubcur = NULL; 18534d3c95f5SJorgen Lundman void *osp = 0; 18544d3c95f5SJorgen Lundman size_t ospsize; 18554d3c95f5SJorgen Lundman int err; 18564d3c95f5SJorgen Lundman 18574d3c95f5SJorgen Lundman data = malloc(sizeof(*data)); 18584d3c95f5SJorgen Lundman if (!data) 18594d3c95f5SJorgen Lundman return 0; 18604d3c95f5SJorgen Lundman memset(data, 0, sizeof(*data)); 18614d3c95f5SJorgen Lundman 18624d3c95f5SJorgen Lundman ub_array = malloc(VDEV_UBERBLOCK_RING); 18634d3c95f5SJorgen Lundman if (!ub_array) { 18644d3c95f5SJorgen Lundman zfs_unmount(data); 18654d3c95f5SJorgen Lundman return 0; 18664d3c95f5SJorgen Lundman } 18674d3c95f5SJorgen Lundman 18684d3c95f5SJorgen Lundman ubbest = malloc(sizeof(*ubbest)); 18694d3c95f5SJorgen Lundman if (!ubbest) { 1870*e183de0dSJorgen Lundman free(ub_array); 18714d3c95f5SJorgen Lundman zfs_unmount(data); 18724d3c95f5SJorgen Lundman return 0; 18734d3c95f5SJorgen Lundman } 18744d3c95f5SJorgen Lundman memset(ubbest, 0, sizeof(*ubbest)); 18754d3c95f5SJorgen Lundman 18764d3c95f5SJorgen Lundman /* 18774d3c95f5SJorgen Lundman * some eltorito stacks don't give us a size and 18784d3c95f5SJorgen Lundman * we end up setting the size to MAXUINT, further 18794d3c95f5SJorgen Lundman * some of these devices stop working once a single 18804d3c95f5SJorgen Lundman * read past the end has been issued. Checking 18814d3c95f5SJorgen Lundman * for a maximum part_length and skipping the backup 18824d3c95f5SJorgen Lundman * labels at the end of the slice/partition/device 18834d3c95f5SJorgen Lundman * avoids breaking down on such devices. 18844d3c95f5SJorgen Lundman */ 18854d3c95f5SJorgen Lundman const int vdevnum = 18864d3c95f5SJorgen Lundman dev->part_length == 0 ? 18874d3c95f5SJorgen Lundman VDEV_LABELS / 2 : VDEV_LABELS; 18884d3c95f5SJorgen Lundman 18894d3c95f5SJorgen Lundman /* Size in bytes of the device (disk or partition) aligned to label size*/ 18904d3c95f5SJorgen Lundman uint64_t device_size = 18914d3c95f5SJorgen Lundman dev->part_length << SECTOR_BITS; 18924d3c95f5SJorgen Lundman 18934d3c95f5SJorgen Lundman const uint64_t alignedbytes = 18944d3c95f5SJorgen Lundman P2ALIGN(device_size, (uint64_t) sizeof(vdev_label_t)); 18954d3c95f5SJorgen Lundman 18964d3c95f5SJorgen Lundman for (label = 0; label < vdevnum; label++) { 18974d3c95f5SJorgen Lundman uint64_t labelstartbytes = vdev_label_start(alignedbytes, label); 18984d3c95f5SJorgen Lundman uint64_t labelstart = labelstartbytes >> SECTOR_BITS; 18994d3c95f5SJorgen Lundman 19004d3c95f5SJorgen Lundman debug("zfs reading label %d at sector %llu (byte %llu)\n", 19014d3c95f5SJorgen Lundman label, (unsigned long long) labelstart, 19024d3c95f5SJorgen Lundman (unsigned long long) labelstartbytes); 19034d3c95f5SJorgen Lundman 19044d3c95f5SJorgen Lundman data->vdev_phys_sector = labelstart + 19054d3c95f5SJorgen Lundman ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SECTOR_BITS); 19064d3c95f5SJorgen Lundman 19074d3c95f5SJorgen Lundman err = check_pool_label(data); 19084d3c95f5SJorgen Lundman if (err) { 19094d3c95f5SJorgen Lundman printf("zfs error checking label %d\n", label); 19104d3c95f5SJorgen Lundman continue; 19114d3c95f5SJorgen Lundman } 19124d3c95f5SJorgen Lundman 19134d3c95f5SJorgen Lundman /* Read in the uberblock ring (128K). */ 19144d3c95f5SJorgen Lundman err = zfs_devread(data->vdev_phys_sector + 19154d3c95f5SJorgen Lundman (VDEV_PHYS_SIZE >> SECTOR_BITS), 19164d3c95f5SJorgen Lundman 0, VDEV_UBERBLOCK_RING, ub_array); 19174d3c95f5SJorgen Lundman if (err) { 19184d3c95f5SJorgen Lundman printf("zfs error reading uberblock ring for label %d\n", label); 19194d3c95f5SJorgen Lundman continue; 19204d3c95f5SJorgen Lundman } 19214d3c95f5SJorgen Lundman 19224d3c95f5SJorgen Lundman ubcur = find_bestub(ub_array, data); 19234d3c95f5SJorgen Lundman if (!ubcur) { 19244d3c95f5SJorgen Lundman printf("zfs No good uberblocks found in label %d\n", label); 19254d3c95f5SJorgen Lundman continue; 19264d3c95f5SJorgen Lundman } 19274d3c95f5SJorgen Lundman 19284d3c95f5SJorgen Lundman if (vdev_uberblock_compare(ubcur, ubbest) > 0) { 19294d3c95f5SJorgen Lundman /* Looks like the block is good, so use it.*/ 19304d3c95f5SJorgen Lundman memcpy(ubbest, ubcur, sizeof(*ubbest)); 19314d3c95f5SJorgen Lundman bestlabel = label; 19324d3c95f5SJorgen Lundman debug("zfs Current best uberblock found in label %d\n", label); 19334d3c95f5SJorgen Lundman } 19344d3c95f5SJorgen Lundman } 19354d3c95f5SJorgen Lundman free(ub_array); 19364d3c95f5SJorgen Lundman 19374d3c95f5SJorgen Lundman /* We zero'd the structure to begin with. If we never assigned to it, 19384d3c95f5SJorgen Lundman magic will still be zero. */ 19394d3c95f5SJorgen Lundman if (!ubbest->ub_magic) { 19404d3c95f5SJorgen Lundman printf("couldn't find a valid ZFS label\n"); 19414d3c95f5SJorgen Lundman zfs_unmount(data); 19424d3c95f5SJorgen Lundman free(ubbest); 19434d3c95f5SJorgen Lundman return 0; 19444d3c95f5SJorgen Lundman } 19454d3c95f5SJorgen Lundman 19464d3c95f5SJorgen Lundman debug("zfs ubbest %p in label %d\n", ubbest, bestlabel); 19474d3c95f5SJorgen Lundman 19484d3c95f5SJorgen Lundman zfs_endian_t ub_endian = 19494d3c95f5SJorgen Lundman zfs_to_cpu64(ubbest->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC 19504d3c95f5SJorgen Lundman ? LITTLE_ENDIAN : BIG_ENDIAN; 19514d3c95f5SJorgen Lundman 19524d3c95f5SJorgen Lundman debug("zfs endian set to %s\n", !ub_endian ? "big" : "little"); 19534d3c95f5SJorgen Lundman 19544d3c95f5SJorgen Lundman err = zio_read(&ubbest->ub_rootbp, ub_endian, &osp, &ospsize, data); 19554d3c95f5SJorgen Lundman 19564d3c95f5SJorgen Lundman if (err) { 19574d3c95f5SJorgen Lundman printf("couldn't zio_read object directory\n"); 19584d3c95f5SJorgen Lundman zfs_unmount(data); 1959*e183de0dSJorgen Lundman free(osp); 19604d3c95f5SJorgen Lundman free(ubbest); 19614d3c95f5SJorgen Lundman return 0; 19624d3c95f5SJorgen Lundman } 19634d3c95f5SJorgen Lundman 19644d3c95f5SJorgen Lundman if (ospsize < OBJSET_PHYS_SIZE_V14) { 19654d3c95f5SJorgen Lundman printf("osp too small\n"); 19664d3c95f5SJorgen Lundman zfs_unmount(data); 19674d3c95f5SJorgen Lundman free(osp); 19684d3c95f5SJorgen Lundman free(ubbest); 19694d3c95f5SJorgen Lundman return 0; 19704d3c95f5SJorgen Lundman } 19714d3c95f5SJorgen Lundman 19724d3c95f5SJorgen Lundman /* Got the MOS. Save it at the memory addr MOS. */ 19734d3c95f5SJorgen Lundman memmove(&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); 19744d3c95f5SJorgen Lundman data->mos.endian = 19754d3c95f5SJorgen Lundman (zfs_to_cpu64(ubbest->ub_rootbp.blk_prop, ub_endian) >> 63) & 1; 19764d3c95f5SJorgen Lundman memmove(&(data->current_uberblock), ubbest, sizeof(uberblock_t)); 19774d3c95f5SJorgen Lundman 19784d3c95f5SJorgen Lundman free(osp); 19794d3c95f5SJorgen Lundman free(ubbest); 19804d3c95f5SJorgen Lundman 19814d3c95f5SJorgen Lundman return data; 19824d3c95f5SJorgen Lundman } 19834d3c95f5SJorgen Lundman 19844d3c95f5SJorgen Lundman int 19854d3c95f5SJorgen Lundman zfs_fetch_nvlist(device_t dev, char **nvlist) 19864d3c95f5SJorgen Lundman { 19874d3c95f5SJorgen Lundman struct zfs_data *zfs; 19884d3c95f5SJorgen Lundman int err; 19894d3c95f5SJorgen Lundman 19904d3c95f5SJorgen Lundman zfs = zfs_mount(dev); 19914d3c95f5SJorgen Lundman if (!zfs) 19924d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 19934d3c95f5SJorgen Lundman err = int_zfs_fetch_nvlist(zfs, nvlist); 19944d3c95f5SJorgen Lundman zfs_unmount(zfs); 19954d3c95f5SJorgen Lundman return err; 19964d3c95f5SJorgen Lundman } 19974d3c95f5SJorgen Lundman 19984d3c95f5SJorgen Lundman /* 19994d3c95f5SJorgen Lundman * zfs_open() locates a file in the rootpool by following the 20004d3c95f5SJorgen Lundman * MOS and places the dnode of the file in the memory address DNODE. 20014d3c95f5SJorgen Lundman */ 20024d3c95f5SJorgen Lundman int 20034d3c95f5SJorgen Lundman zfs_open(struct zfs_file *file, const char *fsfilename) 20044d3c95f5SJorgen Lundman { 20054d3c95f5SJorgen Lundman struct zfs_data *data; 20064d3c95f5SJorgen Lundman int err; 20074d3c95f5SJorgen Lundman int isfs; 20084d3c95f5SJorgen Lundman 20094d3c95f5SJorgen Lundman data = zfs_mount(file->device); 20104d3c95f5SJorgen Lundman if (!data) 20114d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 20124d3c95f5SJorgen Lundman 20134d3c95f5SJorgen Lundman err = dnode_get_fullpath(fsfilename, &(data->mdn), 0, 20144d3c95f5SJorgen Lundman &(data->dnode), &isfs, data); 20154d3c95f5SJorgen Lundman if (err) { 20164d3c95f5SJorgen Lundman zfs_unmount(data); 20174d3c95f5SJorgen Lundman return err; 20184d3c95f5SJorgen Lundman } 20194d3c95f5SJorgen Lundman 20204d3c95f5SJorgen Lundman if (isfs) { 20214d3c95f5SJorgen Lundman zfs_unmount(data); 20224d3c95f5SJorgen Lundman printf("Missing @ or / separator\n"); 20234d3c95f5SJorgen Lundman return ZFS_ERR_FILE_NOT_FOUND; 20244d3c95f5SJorgen Lundman } 20254d3c95f5SJorgen Lundman 20264d3c95f5SJorgen Lundman /* We found the dnode for this file. Verify if it is a plain file. */ 20274d3c95f5SJorgen Lundman if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) { 20284d3c95f5SJorgen Lundman zfs_unmount(data); 20294d3c95f5SJorgen Lundman printf("not a file\n"); 20304d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FILE_TYPE; 20314d3c95f5SJorgen Lundman } 20324d3c95f5SJorgen Lundman 20334d3c95f5SJorgen Lundman /* get the file size and set the file position to 0 */ 20344d3c95f5SJorgen Lundman 20354d3c95f5SJorgen Lundman /* 20364d3c95f5SJorgen Lundman * For DMU_OT_SA we will need to locate the SIZE attribute 20374d3c95f5SJorgen Lundman * attribute, which could be either in the bonus buffer 20384d3c95f5SJorgen Lundman * or the "spill" block. 20394d3c95f5SJorgen Lundman */ 20404d3c95f5SJorgen Lundman if (data->dnode.dn.dn_bonustype == DMU_OT_SA) { 20414d3c95f5SJorgen Lundman void *sahdrp; 20424d3c95f5SJorgen Lundman int hdrsize; 20434d3c95f5SJorgen Lundman 20444d3c95f5SJorgen Lundman if (data->dnode.dn.dn_bonuslen != 0) { 20454d3c95f5SJorgen Lundman sahdrp = (sa_hdr_phys_t *) DN_BONUS(&data->dnode.dn); 20464d3c95f5SJorgen Lundman } else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) { 20474d3c95f5SJorgen Lundman blkptr_t *bp = &data->dnode.dn.dn_spill; 20484d3c95f5SJorgen Lundman 20494d3c95f5SJorgen Lundman err = zio_read(bp, data->dnode.endian, &sahdrp, NULL, data); 20504d3c95f5SJorgen Lundman if (err) 20514d3c95f5SJorgen Lundman return err; 20524d3c95f5SJorgen Lundman } else { 20534d3c95f5SJorgen Lundman printf("filesystem is corrupt :(\n"); 20544d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 20554d3c95f5SJorgen Lundman } 20564d3c95f5SJorgen Lundman 20574d3c95f5SJorgen Lundman hdrsize = SA_HDR_SIZE(((sa_hdr_phys_t *) sahdrp)); 20584d3c95f5SJorgen Lundman file->size = *(uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET); 2059*e183de0dSJorgen Lundman if ((data->dnode.dn.dn_bonuslen == 0) && 2060*e183de0dSJorgen Lundman (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)) 2061*e183de0dSJorgen Lundman free(sahdrp); 20624d3c95f5SJorgen Lundman } else { 20634d3c95f5SJorgen Lundman file->size = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&data->dnode.dn))->zp_size, data->dnode.endian); 20644d3c95f5SJorgen Lundman } 20654d3c95f5SJorgen Lundman 20664d3c95f5SJorgen Lundman file->data = data; 20674d3c95f5SJorgen Lundman file->offset = 0; 20684d3c95f5SJorgen Lundman 20694d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 20704d3c95f5SJorgen Lundman } 20714d3c95f5SJorgen Lundman 20724d3c95f5SJorgen Lundman uint64_t 20734d3c95f5SJorgen Lundman zfs_read(zfs_file_t file, char *buf, uint64_t len) 20744d3c95f5SJorgen Lundman { 20754d3c95f5SJorgen Lundman struct zfs_data *data = (struct zfs_data *) file->data; 20764d3c95f5SJorgen Lundman int blksz, movesize; 20774d3c95f5SJorgen Lundman uint64_t length; 20784d3c95f5SJorgen Lundman int64_t red; 20794d3c95f5SJorgen Lundman int err; 20804d3c95f5SJorgen Lundman 20814d3c95f5SJorgen Lundman if (data->file_buf == NULL) { 20824d3c95f5SJorgen Lundman data->file_buf = malloc(SPA_MAXBLOCKSIZE); 20834d3c95f5SJorgen Lundman if (!data->file_buf) 20844d3c95f5SJorgen Lundman return -1; 20854d3c95f5SJorgen Lundman data->file_start = data->file_end = 0; 20864d3c95f5SJorgen Lundman } 20874d3c95f5SJorgen Lundman 20884d3c95f5SJorgen Lundman /* 20894d3c95f5SJorgen Lundman * If offset is in memory, move it into the buffer provided and return. 20904d3c95f5SJorgen Lundman */ 20914d3c95f5SJorgen Lundman if (file->offset >= data->file_start 20924d3c95f5SJorgen Lundman && file->offset + len <= data->file_end) { 20934d3c95f5SJorgen Lundman memmove(buf, data->file_buf + file->offset - data->file_start, 20944d3c95f5SJorgen Lundman len); 20954d3c95f5SJorgen Lundman return len; 20964d3c95f5SJorgen Lundman } 20974d3c95f5SJorgen Lundman 20984d3c95f5SJorgen Lundman blksz = zfs_to_cpu16(data->dnode.dn.dn_datablkszsec, 20994d3c95f5SJorgen Lundman data->dnode.endian) << SPA_MINBLOCKSHIFT; 21004d3c95f5SJorgen Lundman 21014d3c95f5SJorgen Lundman /* 21024d3c95f5SJorgen Lundman * Entire Dnode is too big to fit into the space available. We 21034d3c95f5SJorgen Lundman * will need to read it in chunks. This could be optimized to 21044d3c95f5SJorgen Lundman * read in as large a chunk as there is space available, but for 21054d3c95f5SJorgen Lundman * now, this only reads in one data block at a time. 21064d3c95f5SJorgen Lundman */ 21074d3c95f5SJorgen Lundman length = len; 21084d3c95f5SJorgen Lundman red = 0; 21094d3c95f5SJorgen Lundman while (length) { 21104d3c95f5SJorgen Lundman void *t; 21114d3c95f5SJorgen Lundman /* 21124d3c95f5SJorgen Lundman * Find requested blkid and the offset within that block. 21134d3c95f5SJorgen Lundman */ 2114624c721fSAlejandro Mery uint64_t blkid = file->offset + red; 2115624c721fSAlejandro Mery blkid = do_div(blkid, blksz); 21164d3c95f5SJorgen Lundman free(data->file_buf); 21174d3c95f5SJorgen Lundman data->file_buf = 0; 21184d3c95f5SJorgen Lundman 21194d3c95f5SJorgen Lundman err = dmu_read(&(data->dnode), blkid, &t, 21204d3c95f5SJorgen Lundman 0, data); 21214d3c95f5SJorgen Lundman data->file_buf = t; 21224d3c95f5SJorgen Lundman if (err) 21234d3c95f5SJorgen Lundman return -1; 21244d3c95f5SJorgen Lundman 21254d3c95f5SJorgen Lundman data->file_start = blkid * blksz; 21264d3c95f5SJorgen Lundman data->file_end = data->file_start + blksz; 21274d3c95f5SJorgen Lundman 2128c79cba37SMasahiro Yamada movesize = min(length, data->file_end - (int)file->offset - red); 21294d3c95f5SJorgen Lundman 21304d3c95f5SJorgen Lundman memmove(buf, data->file_buf + file->offset + red 21314d3c95f5SJorgen Lundman - data->file_start, movesize); 21324d3c95f5SJorgen Lundman buf += movesize; 21334d3c95f5SJorgen Lundman length -= movesize; 21344d3c95f5SJorgen Lundman red += movesize; 21354d3c95f5SJorgen Lundman } 21364d3c95f5SJorgen Lundman 21374d3c95f5SJorgen Lundman return len; 21384d3c95f5SJorgen Lundman } 21394d3c95f5SJorgen Lundman 21404d3c95f5SJorgen Lundman int 21414d3c95f5SJorgen Lundman zfs_close(zfs_file_t file) 21424d3c95f5SJorgen Lundman { 21434d3c95f5SJorgen Lundman zfs_unmount((struct zfs_data *) file->data); 21444d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 21454d3c95f5SJorgen Lundman } 21464d3c95f5SJorgen Lundman 21474d3c95f5SJorgen Lundman int 21484d3c95f5SJorgen Lundman zfs_getmdnobj(device_t dev, const char *fsfilename, 21494d3c95f5SJorgen Lundman uint64_t *mdnobj) 21504d3c95f5SJorgen Lundman { 21514d3c95f5SJorgen Lundman struct zfs_data *data; 21524d3c95f5SJorgen Lundman int err; 21534d3c95f5SJorgen Lundman int isfs; 21544d3c95f5SJorgen Lundman 21554d3c95f5SJorgen Lundman data = zfs_mount(dev); 21564d3c95f5SJorgen Lundman if (!data) 21574d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 21584d3c95f5SJorgen Lundman 21594d3c95f5SJorgen Lundman err = dnode_get_fullpath(fsfilename, &(data->mdn), mdnobj, 21604d3c95f5SJorgen Lundman &(data->dnode), &isfs, data); 21614d3c95f5SJorgen Lundman zfs_unmount(data); 21624d3c95f5SJorgen Lundman return err; 21634d3c95f5SJorgen Lundman } 21644d3c95f5SJorgen Lundman 21654d3c95f5SJorgen Lundman static void 21664d3c95f5SJorgen Lundman fill_fs_info(struct zfs_dirhook_info *info, 21674d3c95f5SJorgen Lundman dnode_end_t mdn, struct zfs_data *data) 21684d3c95f5SJorgen Lundman { 21694d3c95f5SJorgen Lundman int err; 21704d3c95f5SJorgen Lundman dnode_end_t dn; 21714d3c95f5SJorgen Lundman uint64_t objnum; 21724d3c95f5SJorgen Lundman uint64_t headobj; 21734d3c95f5SJorgen Lundman 21744d3c95f5SJorgen Lundman memset(info, 0, sizeof(*info)); 21754d3c95f5SJorgen Lundman 21764d3c95f5SJorgen Lundman info->dir = 1; 21774d3c95f5SJorgen Lundman 21784d3c95f5SJorgen Lundman if (mdn.dn.dn_type == DMU_OT_DSL_DIR) { 21794d3c95f5SJorgen Lundman headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&mdn.dn))->dd_head_dataset_obj, mdn.endian); 21804d3c95f5SJorgen Lundman 21814d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &mdn, data); 21824d3c95f5SJorgen Lundman if (err) { 21834d3c95f5SJorgen Lundman printf("zfs failed here 1\n"); 21844d3c95f5SJorgen Lundman return; 21854d3c95f5SJorgen Lundman } 21864d3c95f5SJorgen Lundman } 21874d3c95f5SJorgen Lundman make_mdn(&mdn, data); 21884d3c95f5SJorgen Lundman err = dnode_get(&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, 21894d3c95f5SJorgen Lundman &dn, data); 21904d3c95f5SJorgen Lundman if (err) { 21914d3c95f5SJorgen Lundman printf("zfs failed here 2\n"); 21924d3c95f5SJorgen Lundman return; 21934d3c95f5SJorgen Lundman } 21944d3c95f5SJorgen Lundman 21954d3c95f5SJorgen Lundman err = zap_lookup(&dn, ZFS_ROOT_OBJ, &objnum, data); 21964d3c95f5SJorgen Lundman if (err) { 21974d3c95f5SJorgen Lundman printf("zfs failed here 3\n"); 21984d3c95f5SJorgen Lundman return; 21994d3c95f5SJorgen Lundman } 22004d3c95f5SJorgen Lundman 22014d3c95f5SJorgen Lundman err = dnode_get(&mdn, objnum, 0, &dn, data); 22024d3c95f5SJorgen Lundman if (err) { 22034d3c95f5SJorgen Lundman printf("zfs failed here 4\n"); 22044d3c95f5SJorgen Lundman return; 22054d3c95f5SJorgen Lundman } 22064d3c95f5SJorgen Lundman 22074d3c95f5SJorgen Lundman info->mtimeset = 1; 22084d3c95f5SJorgen Lundman info->mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian); 22094d3c95f5SJorgen Lundman 22104d3c95f5SJorgen Lundman return; 22114d3c95f5SJorgen Lundman } 22124d3c95f5SJorgen Lundman 22134d3c95f5SJorgen Lundman static int iterate_zap(const char *name, uint64_t val, struct zfs_data *data) 22144d3c95f5SJorgen Lundman { 22154d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22164d3c95f5SJorgen Lundman dnode_end_t dn; 22174d3c95f5SJorgen Lundman 22184d3c95f5SJorgen Lundman memset(&info, 0, sizeof(info)); 22194d3c95f5SJorgen Lundman 22204d3c95f5SJorgen Lundman dnode_get(&(data->mdn), val, 0, &dn, data); 22214d3c95f5SJorgen Lundman info.mtimeset = 1; 22224d3c95f5SJorgen Lundman info.mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian); 22234d3c95f5SJorgen Lundman info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); 22244d3c95f5SJorgen Lundman debug("zfs type=%d, name=%s\n", 22254d3c95f5SJorgen Lundman (int)dn.dn.dn_type, (char *)name); 22264d3c95f5SJorgen Lundman if (!data->userhook) 22274d3c95f5SJorgen Lundman return 0; 22284d3c95f5SJorgen Lundman return data->userhook(name, &info); 22294d3c95f5SJorgen Lundman } 22304d3c95f5SJorgen Lundman 22314d3c95f5SJorgen Lundman static int iterate_zap_fs(const char *name, uint64_t val, struct zfs_data *data) 22324d3c95f5SJorgen Lundman { 22334d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22344d3c95f5SJorgen Lundman dnode_end_t mdn; 22354d3c95f5SJorgen Lundman int err; 22364d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), val, 0, &mdn, data); 22374d3c95f5SJorgen Lundman if (err) 22384d3c95f5SJorgen Lundman return 0; 22394d3c95f5SJorgen Lundman if (mdn.dn.dn_type != DMU_OT_DSL_DIR) 22404d3c95f5SJorgen Lundman return 0; 22414d3c95f5SJorgen Lundman 22424d3c95f5SJorgen Lundman fill_fs_info(&info, mdn, data); 22434d3c95f5SJorgen Lundman 22444d3c95f5SJorgen Lundman if (!data->userhook) 22454d3c95f5SJorgen Lundman return 0; 22464d3c95f5SJorgen Lundman return data->userhook(name, &info); 22474d3c95f5SJorgen Lundman } 22484d3c95f5SJorgen Lundman 22494d3c95f5SJorgen Lundman static int iterate_zap_snap(const char *name, uint64_t val, struct zfs_data *data) 22504d3c95f5SJorgen Lundman { 22514d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22524d3c95f5SJorgen Lundman char *name2; 22534d3c95f5SJorgen Lundman int ret = 0; 22544d3c95f5SJorgen Lundman dnode_end_t mdn; 22554d3c95f5SJorgen Lundman int err; 22564d3c95f5SJorgen Lundman 22574d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), val, 0, &mdn, data); 22584d3c95f5SJorgen Lundman if (err) 22594d3c95f5SJorgen Lundman return 0; 22604d3c95f5SJorgen Lundman 22614d3c95f5SJorgen Lundman if (mdn.dn.dn_type != DMU_OT_DSL_DATASET) 22624d3c95f5SJorgen Lundman return 0; 22634d3c95f5SJorgen Lundman 22644d3c95f5SJorgen Lundman fill_fs_info(&info, mdn, data); 22654d3c95f5SJorgen Lundman 22664d3c95f5SJorgen Lundman name2 = malloc(strlen(name) + 2); 22674d3c95f5SJorgen Lundman name2[0] = '@'; 22684d3c95f5SJorgen Lundman memcpy(name2 + 1, name, strlen(name) + 1); 22694d3c95f5SJorgen Lundman if (data->userhook) 22704d3c95f5SJorgen Lundman ret = data->userhook(name2, &info); 22714d3c95f5SJorgen Lundman free(name2); 22724d3c95f5SJorgen Lundman return ret; 22734d3c95f5SJorgen Lundman } 22744d3c95f5SJorgen Lundman 22754d3c95f5SJorgen Lundman int 22764d3c95f5SJorgen Lundman zfs_ls(device_t device, const char *path, 22774d3c95f5SJorgen Lundman int (*hook)(const char *, const struct zfs_dirhook_info *)) 22784d3c95f5SJorgen Lundman { 22794d3c95f5SJorgen Lundman struct zfs_data *data; 22804d3c95f5SJorgen Lundman int err; 22814d3c95f5SJorgen Lundman int isfs; 22824d3c95f5SJorgen Lundman 22834d3c95f5SJorgen Lundman data = zfs_mount(device); 22844d3c95f5SJorgen Lundman if (!data) 22854d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 22864d3c95f5SJorgen Lundman 22874d3c95f5SJorgen Lundman data->userhook = hook; 22884d3c95f5SJorgen Lundman 22894d3c95f5SJorgen Lundman err = dnode_get_fullpath(path, &(data->mdn), 0, &(data->dnode), &isfs, data); 22904d3c95f5SJorgen Lundman if (err) { 22914d3c95f5SJorgen Lundman zfs_unmount(data); 22924d3c95f5SJorgen Lundman return err; 22934d3c95f5SJorgen Lundman } 22944d3c95f5SJorgen Lundman if (isfs) { 22954d3c95f5SJorgen Lundman uint64_t childobj, headobj; 22964d3c95f5SJorgen Lundman uint64_t snapobj; 22974d3c95f5SJorgen Lundman dnode_end_t dn; 22984d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22994d3c95f5SJorgen Lundman 23004d3c95f5SJorgen Lundman fill_fs_info(&info, data->dnode, data); 23014d3c95f5SJorgen Lundman hook("@", &info); 23024d3c95f5SJorgen Lundman 23034d3c95f5SJorgen Lundman childobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian); 23044d3c95f5SJorgen Lundman headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian); 23054d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), childobj, 23064d3c95f5SJorgen Lundman DMU_OT_DSL_DIR_CHILD_MAP, &dn, data); 23074d3c95f5SJorgen Lundman if (err) { 23084d3c95f5SJorgen Lundman zfs_unmount(data); 23094d3c95f5SJorgen Lundman return err; 23104d3c95f5SJorgen Lundman } 23114d3c95f5SJorgen Lundman 23124d3c95f5SJorgen Lundman 23134d3c95f5SJorgen Lundman zap_iterate(&dn, iterate_zap_fs, data); 23144d3c95f5SJorgen Lundman 23154d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data); 23164d3c95f5SJorgen Lundman if (err) { 23174d3c95f5SJorgen Lundman zfs_unmount(data); 23184d3c95f5SJorgen Lundman return err; 23194d3c95f5SJorgen Lundman } 23204d3c95f5SJorgen Lundman 23214d3c95f5SJorgen Lundman snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&dn.dn))->ds_snapnames_zapobj, dn.endian); 23224d3c95f5SJorgen Lundman 23234d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), snapobj, 23244d3c95f5SJorgen Lundman DMU_OT_DSL_DS_SNAP_MAP, &dn, data); 23254d3c95f5SJorgen Lundman if (err) { 23264d3c95f5SJorgen Lundman zfs_unmount(data); 23274d3c95f5SJorgen Lundman return err; 23284d3c95f5SJorgen Lundman } 23294d3c95f5SJorgen Lundman 23304d3c95f5SJorgen Lundman zap_iterate(&dn, iterate_zap_snap, data); 23314d3c95f5SJorgen Lundman } else { 23324d3c95f5SJorgen Lundman if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) { 23334d3c95f5SJorgen Lundman zfs_unmount(data); 23344d3c95f5SJorgen Lundman printf("not a directory\n"); 23354d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FILE_TYPE; 23364d3c95f5SJorgen Lundman } 23374d3c95f5SJorgen Lundman zap_iterate(&(data->dnode), iterate_zap, data); 23384d3c95f5SJorgen Lundman } 23394d3c95f5SJorgen Lundman zfs_unmount(data); 23404d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 23414d3c95f5SJorgen Lundman } 2342