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) { 7394d3c95f5SJorgen Lundman uint64_t *ct; 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; 775*c79cba37SMasahiro 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; 797*c79cba37SMasahiro 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"); 10634d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 10644d3c95f5SJorgen Lundman } 10654d3c95f5SJorgen Lundman 10664d3c95f5SJorgen Lundman static int 10674d3c95f5SJorgen Lundman zap_iterate(dnode_end_t *zap_dnode, 10684d3c95f5SJorgen Lundman int (*hook)(const char *name, uint64_t val, 10694d3c95f5SJorgen Lundman struct zfs_data *data), 10704d3c95f5SJorgen Lundman struct zfs_data *data) 10714d3c95f5SJorgen Lundman { 10724d3c95f5SJorgen Lundman uint64_t block_type; 10734d3c95f5SJorgen Lundman int size; 10744d3c95f5SJorgen Lundman void *zapbuf; 10754d3c95f5SJorgen Lundman int err; 10764d3c95f5SJorgen Lundman int ret; 10774d3c95f5SJorgen Lundman zfs_endian_t endian; 10784d3c95f5SJorgen Lundman 10794d3c95f5SJorgen Lundman /* Read in the first block of the zap object data. */ 10804d3c95f5SJorgen Lundman size = zfs_to_cpu16(zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT; 10814d3c95f5SJorgen Lundman err = dmu_read(zap_dnode, 0, &zapbuf, &endian, data); 10824d3c95f5SJorgen Lundman if (err) 10834d3c95f5SJorgen Lundman return 0; 10844d3c95f5SJorgen Lundman block_type = zfs_to_cpu64(*((uint64_t *) zapbuf), endian); 10854d3c95f5SJorgen Lundman 10864d3c95f5SJorgen Lundman if (block_type == ZBT_MICRO) { 10874d3c95f5SJorgen Lundman ret = mzap_iterate(zapbuf, endian, size, hook, data); 10884d3c95f5SJorgen Lundman free(zapbuf); 10894d3c95f5SJorgen Lundman return ret; 10904d3c95f5SJorgen Lundman } else if (block_type == ZBT_HEADER) { 10914d3c95f5SJorgen Lundman /* this is a fat zap */ 10924d3c95f5SJorgen Lundman ret = fzap_iterate(zap_dnode, zapbuf, hook, data); 10934d3c95f5SJorgen Lundman free(zapbuf); 10944d3c95f5SJorgen Lundman return ret; 10954d3c95f5SJorgen Lundman } 10964d3c95f5SJorgen Lundman printf("unknown ZAP type\n"); 10974d3c95f5SJorgen Lundman return 0; 10984d3c95f5SJorgen Lundman } 10994d3c95f5SJorgen Lundman 11004d3c95f5SJorgen Lundman 11014d3c95f5SJorgen Lundman /* 11024d3c95f5SJorgen Lundman * Get the dnode of an object number from the metadnode of an object set. 11034d3c95f5SJorgen Lundman * 11044d3c95f5SJorgen Lundman * Input 11054d3c95f5SJorgen Lundman * mdn - metadnode to get the object dnode 11064d3c95f5SJorgen Lundman * objnum - object number for the object dnode 11074d3c95f5SJorgen Lundman * buf - data buffer that holds the returning dnode 11084d3c95f5SJorgen Lundman */ 11094d3c95f5SJorgen Lundman static int 11104d3c95f5SJorgen Lundman dnode_get(dnode_end_t *mdn, uint64_t objnum, uint8_t type, 11114d3c95f5SJorgen Lundman dnode_end_t *buf, struct zfs_data *data) 11124d3c95f5SJorgen Lundman { 11134d3c95f5SJorgen Lundman uint64_t blkid, blksz; /* the block id this object dnode is in */ 11144d3c95f5SJorgen Lundman int epbs; /* shift of number of dnodes in a block */ 11154d3c95f5SJorgen Lundman int idx; /* index within a block */ 11164d3c95f5SJorgen Lundman void *dnbuf; 11174d3c95f5SJorgen Lundman int err; 11184d3c95f5SJorgen Lundman zfs_endian_t endian; 11194d3c95f5SJorgen Lundman 11204d3c95f5SJorgen Lundman blksz = zfs_to_cpu16(mdn->dn.dn_datablkszsec, 11214d3c95f5SJorgen Lundman mdn->endian) << SPA_MINBLOCKSHIFT; 11224d3c95f5SJorgen Lundman 11234d3c95f5SJorgen Lundman epbs = zfs_log2(blksz) - DNODE_SHIFT; 11244d3c95f5SJorgen Lundman blkid = objnum >> epbs; 11254d3c95f5SJorgen Lundman idx = objnum & ((1 << epbs) - 1); 11264d3c95f5SJorgen Lundman 11274d3c95f5SJorgen Lundman if (data->dnode_buf != NULL && memcmp(data->dnode_mdn, mdn, 11284d3c95f5SJorgen Lundman sizeof(*mdn)) == 0 11294d3c95f5SJorgen Lundman && objnum >= data->dnode_start && objnum < data->dnode_end) { 11304d3c95f5SJorgen Lundman memmove(&(buf->dn), &(data->dnode_buf)[idx], DNODE_SIZE); 11314d3c95f5SJorgen Lundman buf->endian = data->dnode_endian; 11324d3c95f5SJorgen Lundman if (type && buf->dn.dn_type != type) { 11334d3c95f5SJorgen Lundman printf("incorrect dnode type: %02X != %02x\n", buf->dn.dn_type, type); 11344d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 11354d3c95f5SJorgen Lundman } 11364d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 11374d3c95f5SJorgen Lundman } 11384d3c95f5SJorgen Lundman 11394d3c95f5SJorgen Lundman err = dmu_read(mdn, blkid, &dnbuf, &endian, data); 11404d3c95f5SJorgen Lundman if (err) 11414d3c95f5SJorgen Lundman return err; 11424d3c95f5SJorgen Lundman 11434d3c95f5SJorgen Lundman free(data->dnode_buf); 11444d3c95f5SJorgen Lundman free(data->dnode_mdn); 11454d3c95f5SJorgen Lundman data->dnode_mdn = malloc(sizeof(*mdn)); 11464d3c95f5SJorgen Lundman if (!data->dnode_mdn) { 11474d3c95f5SJorgen Lundman data->dnode_buf = 0; 11484d3c95f5SJorgen Lundman } else { 11494d3c95f5SJorgen Lundman memcpy(data->dnode_mdn, mdn, sizeof(*mdn)); 11504d3c95f5SJorgen Lundman data->dnode_buf = dnbuf; 11514d3c95f5SJorgen Lundman data->dnode_start = blkid << epbs; 11524d3c95f5SJorgen Lundman data->dnode_end = (blkid + 1) << epbs; 11534d3c95f5SJorgen Lundman data->dnode_endian = endian; 11544d3c95f5SJorgen Lundman } 11554d3c95f5SJorgen Lundman 11564d3c95f5SJorgen Lundman memmove(&(buf->dn), (dnode_phys_t *) dnbuf + idx, DNODE_SIZE); 11574d3c95f5SJorgen Lundman buf->endian = endian; 11584d3c95f5SJorgen Lundman if (type && buf->dn.dn_type != type) { 11594d3c95f5SJorgen Lundman printf("incorrect dnode type\n"); 11604d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 11614d3c95f5SJorgen Lundman } 11624d3c95f5SJorgen Lundman 11634d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 11644d3c95f5SJorgen Lundman } 11654d3c95f5SJorgen Lundman 11664d3c95f5SJorgen Lundman /* 11674d3c95f5SJorgen Lundman * Get the file dnode for a given file name where mdn is the meta dnode 11684d3c95f5SJorgen Lundman * for this ZFS object set. When found, place the file dnode in dn. 11694d3c95f5SJorgen Lundman * The 'path' argument will be mangled. 11704d3c95f5SJorgen Lundman * 11714d3c95f5SJorgen Lundman */ 11724d3c95f5SJorgen Lundman static int 11734d3c95f5SJorgen Lundman dnode_get_path(dnode_end_t *mdn, const char *path_in, dnode_end_t *dn, 11744d3c95f5SJorgen Lundman struct zfs_data *data) 11754d3c95f5SJorgen Lundman { 11764d3c95f5SJorgen Lundman uint64_t objnum, version; 11774d3c95f5SJorgen Lundman char *cname, ch; 11784d3c95f5SJorgen Lundman int err = ZFS_ERR_NONE; 11794d3c95f5SJorgen Lundman char *path, *path_buf; 11804d3c95f5SJorgen Lundman struct dnode_chain { 11814d3c95f5SJorgen Lundman struct dnode_chain *next; 11824d3c95f5SJorgen Lundman dnode_end_t dn; 11834d3c95f5SJorgen Lundman }; 11844d3c95f5SJorgen Lundman struct dnode_chain *dnode_path = 0, *dn_new, *root; 11854d3c95f5SJorgen Lundman 11864d3c95f5SJorgen Lundman dn_new = malloc(sizeof(*dn_new)); 11874d3c95f5SJorgen Lundman if (!dn_new) 11884d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 11894d3c95f5SJorgen Lundman dn_new->next = 0; 11904d3c95f5SJorgen Lundman dnode_path = root = dn_new; 11914d3c95f5SJorgen Lundman 11924d3c95f5SJorgen Lundman err = dnode_get(mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, 11934d3c95f5SJorgen Lundman &(dnode_path->dn), data); 11944d3c95f5SJorgen Lundman if (err) { 11954d3c95f5SJorgen Lundman free(dn_new); 11964d3c95f5SJorgen Lundman return err; 11974d3c95f5SJorgen Lundman } 11984d3c95f5SJorgen Lundman 11994d3c95f5SJorgen Lundman err = zap_lookup(&(dnode_path->dn), ZPL_VERSION_STR, &version, data); 12004d3c95f5SJorgen Lundman if (err) { 12014d3c95f5SJorgen Lundman free(dn_new); 12024d3c95f5SJorgen Lundman return err; 12034d3c95f5SJorgen Lundman } 12044d3c95f5SJorgen Lundman if (version > ZPL_VERSION) { 12054d3c95f5SJorgen Lundman free(dn_new); 12064d3c95f5SJorgen Lundman printf("too new ZPL version\n"); 12074d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 12084d3c95f5SJorgen Lundman } 12094d3c95f5SJorgen Lundman 12104d3c95f5SJorgen Lundman err = zap_lookup(&(dnode_path->dn), ZFS_ROOT_OBJ, &objnum, data); 12114d3c95f5SJorgen Lundman if (err) { 12124d3c95f5SJorgen Lundman free(dn_new); 12134d3c95f5SJorgen Lundman return err; 12144d3c95f5SJorgen Lundman } 12154d3c95f5SJorgen Lundman 12164d3c95f5SJorgen Lundman err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data); 12174d3c95f5SJorgen Lundman if (err) { 12184d3c95f5SJorgen Lundman free(dn_new); 12194d3c95f5SJorgen Lundman return err; 12204d3c95f5SJorgen Lundman } 12214d3c95f5SJorgen Lundman 12224d3c95f5SJorgen Lundman path = path_buf = strdup(path_in); 12234d3c95f5SJorgen Lundman if (!path_buf) { 12244d3c95f5SJorgen Lundman free(dn_new); 12254d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 12264d3c95f5SJorgen Lundman } 12274d3c95f5SJorgen Lundman 12284d3c95f5SJorgen Lundman while (1) { 12294d3c95f5SJorgen Lundman /* skip leading slashes */ 12304d3c95f5SJorgen Lundman while (*path == '/') 12314d3c95f5SJorgen Lundman path++; 12324d3c95f5SJorgen Lundman if (!*path) 12334d3c95f5SJorgen Lundman break; 12344d3c95f5SJorgen Lundman /* get the next component name */ 12354d3c95f5SJorgen Lundman cname = path; 12364d3c95f5SJorgen Lundman while (*path && *path != '/') 12374d3c95f5SJorgen Lundman path++; 12384d3c95f5SJorgen Lundman /* Skip dot. */ 12394d3c95f5SJorgen Lundman if (cname + 1 == path && cname[0] == '.') 12404d3c95f5SJorgen Lundman continue; 12414d3c95f5SJorgen Lundman /* Handle double dot. */ 12424d3c95f5SJorgen Lundman if (cname + 2 == path && cname[0] == '.' && cname[1] == '.') { 12434d3c95f5SJorgen Lundman if (dn_new->next) { 12444d3c95f5SJorgen Lundman dn_new = dnode_path; 12454d3c95f5SJorgen Lundman dnode_path = dn_new->next; 12464d3c95f5SJorgen Lundman free(dn_new); 12474d3c95f5SJorgen Lundman } else { 12484d3c95f5SJorgen Lundman printf("can't resolve ..\n"); 12494d3c95f5SJorgen Lundman err = ZFS_ERR_FILE_NOT_FOUND; 12504d3c95f5SJorgen Lundman break; 12514d3c95f5SJorgen Lundman } 12524d3c95f5SJorgen Lundman continue; 12534d3c95f5SJorgen Lundman } 12544d3c95f5SJorgen Lundman 12554d3c95f5SJorgen Lundman ch = *path; 12564d3c95f5SJorgen Lundman *path = 0; /* ensure null termination */ 12574d3c95f5SJorgen Lundman 12584d3c95f5SJorgen Lundman if (dnode_path->dn.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) { 12594d3c95f5SJorgen Lundman free(path_buf); 12604d3c95f5SJorgen Lundman printf("not a directory\n"); 12614d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FILE_TYPE; 12624d3c95f5SJorgen Lundman } 12634d3c95f5SJorgen Lundman err = zap_lookup(&(dnode_path->dn), cname, &objnum, data); 12644d3c95f5SJorgen Lundman if (err) 12654d3c95f5SJorgen Lundman break; 12664d3c95f5SJorgen Lundman 12674d3c95f5SJorgen Lundman dn_new = malloc(sizeof(*dn_new)); 12684d3c95f5SJorgen Lundman if (!dn_new) { 12694d3c95f5SJorgen Lundman err = ZFS_ERR_OUT_OF_MEMORY; 12704d3c95f5SJorgen Lundman break; 12714d3c95f5SJorgen Lundman } 12724d3c95f5SJorgen Lundman dn_new->next = dnode_path; 12734d3c95f5SJorgen Lundman dnode_path = dn_new; 12744d3c95f5SJorgen Lundman 12754d3c95f5SJorgen Lundman objnum = ZFS_DIRENT_OBJ(objnum); 12764d3c95f5SJorgen Lundman err = dnode_get(mdn, objnum, 0, &(dnode_path->dn), data); 12774d3c95f5SJorgen Lundman if (err) 12784d3c95f5SJorgen Lundman break; 12794d3c95f5SJorgen Lundman 12804d3c95f5SJorgen Lundman *path = ch; 12814d3c95f5SJorgen Lundman } 12824d3c95f5SJorgen Lundman 12834d3c95f5SJorgen Lundman if (!err) 12844d3c95f5SJorgen Lundman memcpy(dn, &(dnode_path->dn), sizeof(*dn)); 12854d3c95f5SJorgen Lundman 12864d3c95f5SJorgen Lundman while (dnode_path) { 12874d3c95f5SJorgen Lundman dn_new = dnode_path->next; 12884d3c95f5SJorgen Lundman free(dnode_path); 12894d3c95f5SJorgen Lundman dnode_path = dn_new; 12904d3c95f5SJorgen Lundman } 12914d3c95f5SJorgen Lundman free(path_buf); 12924d3c95f5SJorgen Lundman return err; 12934d3c95f5SJorgen Lundman } 12944d3c95f5SJorgen Lundman 12954d3c95f5SJorgen Lundman 12964d3c95f5SJorgen Lundman /* 12974d3c95f5SJorgen Lundman * Given a MOS metadnode, get the metadnode of a given filesystem name (fsname), 12984d3c95f5SJorgen Lundman * e.g. pool/rootfs, or a given object number (obj), e.g. the object number 12994d3c95f5SJorgen Lundman * of pool/rootfs. 13004d3c95f5SJorgen Lundman * 13014d3c95f5SJorgen Lundman * If no fsname and no obj are given, return the DSL_DIR metadnode. 13024d3c95f5SJorgen Lundman * If fsname is given, return its metadnode and its matching object number. 13034d3c95f5SJorgen Lundman * If only obj is given, return the metadnode for this object number. 13044d3c95f5SJorgen Lundman * 13054d3c95f5SJorgen Lundman */ 13064d3c95f5SJorgen Lundman static int 13074d3c95f5SJorgen Lundman get_filesystem_dnode(dnode_end_t *mosmdn, char *fsname, 13084d3c95f5SJorgen Lundman dnode_end_t *mdn, struct zfs_data *data) 13094d3c95f5SJorgen Lundman { 13104d3c95f5SJorgen Lundman uint64_t objnum; 13114d3c95f5SJorgen Lundman int err; 13124d3c95f5SJorgen Lundman 13134d3c95f5SJorgen Lundman err = dnode_get(mosmdn, DMU_POOL_DIRECTORY_OBJECT, 13144d3c95f5SJorgen Lundman DMU_OT_OBJECT_DIRECTORY, mdn, data); 13154d3c95f5SJorgen Lundman if (err) 13164d3c95f5SJorgen Lundman return err; 13174d3c95f5SJorgen Lundman 13184d3c95f5SJorgen Lundman err = zap_lookup(mdn, DMU_POOL_ROOT_DATASET, &objnum, data); 13194d3c95f5SJorgen Lundman if (err) 13204d3c95f5SJorgen Lundman return err; 13214d3c95f5SJorgen Lundman 13224d3c95f5SJorgen Lundman err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); 13234d3c95f5SJorgen Lundman if (err) 13244d3c95f5SJorgen Lundman return err; 13254d3c95f5SJorgen Lundman 13264d3c95f5SJorgen Lundman while (*fsname) { 13274d3c95f5SJorgen Lundman uint64_t childobj; 13284d3c95f5SJorgen Lundman char *cname, ch; 13294d3c95f5SJorgen Lundman 13304d3c95f5SJorgen Lundman while (*fsname == '/') 13314d3c95f5SJorgen Lundman fsname++; 13324d3c95f5SJorgen Lundman 13334d3c95f5SJorgen Lundman if (!*fsname || *fsname == '@') 13344d3c95f5SJorgen Lundman break; 13354d3c95f5SJorgen Lundman 13364d3c95f5SJorgen Lundman cname = fsname; 13374d3c95f5SJorgen Lundman while (*fsname && !isspace(*fsname) && *fsname != '/') 13384d3c95f5SJorgen Lundman fsname++; 13394d3c95f5SJorgen Lundman ch = *fsname; 13404d3c95f5SJorgen Lundman *fsname = 0; 13414d3c95f5SJorgen Lundman 13424d3c95f5SJorgen Lundman childobj = zfs_to_cpu64((((dsl_dir_phys_t *) DN_BONUS(&mdn->dn)))->dd_child_dir_zapobj, mdn->endian); 13434d3c95f5SJorgen Lundman err = dnode_get(mosmdn, childobj, 13444d3c95f5SJorgen Lundman DMU_OT_DSL_DIR_CHILD_MAP, mdn, data); 13454d3c95f5SJorgen Lundman if (err) 13464d3c95f5SJorgen Lundman return err; 13474d3c95f5SJorgen Lundman 13484d3c95f5SJorgen Lundman err = zap_lookup(mdn, cname, &objnum, data); 13494d3c95f5SJorgen Lundman if (err) 13504d3c95f5SJorgen Lundman return err; 13514d3c95f5SJorgen Lundman 13524d3c95f5SJorgen Lundman err = dnode_get(mosmdn, objnum, DMU_OT_DSL_DIR, mdn, data); 13534d3c95f5SJorgen Lundman if (err) 13544d3c95f5SJorgen Lundman return err; 13554d3c95f5SJorgen Lundman 13564d3c95f5SJorgen Lundman *fsname = ch; 13574d3c95f5SJorgen Lundman } 13584d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 13594d3c95f5SJorgen Lundman } 13604d3c95f5SJorgen Lundman 13614d3c95f5SJorgen Lundman static int 13624d3c95f5SJorgen Lundman make_mdn(dnode_end_t *mdn, struct zfs_data *data) 13634d3c95f5SJorgen Lundman { 13644d3c95f5SJorgen Lundman void *osp; 13654d3c95f5SJorgen Lundman blkptr_t *bp; 13664d3c95f5SJorgen Lundman size_t ospsize; 13674d3c95f5SJorgen Lundman int err; 13684d3c95f5SJorgen Lundman 13694d3c95f5SJorgen Lundman bp = &(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_bp); 13704d3c95f5SJorgen Lundman err = zio_read(bp, mdn->endian, &osp, &ospsize, data); 13714d3c95f5SJorgen Lundman if (err) 13724d3c95f5SJorgen Lundman return err; 13734d3c95f5SJorgen Lundman if (ospsize < OBJSET_PHYS_SIZE_V14) { 13744d3c95f5SJorgen Lundman free(osp); 13754d3c95f5SJorgen Lundman printf("too small osp\n"); 13764d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 13774d3c95f5SJorgen Lundman } 13784d3c95f5SJorgen Lundman 13794d3c95f5SJorgen Lundman mdn->endian = (zfs_to_cpu64(bp->blk_prop, mdn->endian)>>63) & 1; 13804d3c95f5SJorgen Lundman memmove((char *) &(mdn->dn), 13814d3c95f5SJorgen Lundman (char *) &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); 13824d3c95f5SJorgen Lundman free(osp); 13834d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 13844d3c95f5SJorgen Lundman } 13854d3c95f5SJorgen Lundman 13864d3c95f5SJorgen Lundman static int 13874d3c95f5SJorgen Lundman dnode_get_fullpath(const char *fullpath, dnode_end_t *mdn, 13884d3c95f5SJorgen Lundman uint64_t *mdnobj, dnode_end_t *dn, int *isfs, 13894d3c95f5SJorgen Lundman struct zfs_data *data) 13904d3c95f5SJorgen Lundman { 13914d3c95f5SJorgen Lundman char *fsname, *snapname; 13924d3c95f5SJorgen Lundman const char *ptr_at, *filename; 13934d3c95f5SJorgen Lundman uint64_t headobj; 13944d3c95f5SJorgen Lundman int err; 13954d3c95f5SJorgen Lundman 13964d3c95f5SJorgen Lundman ptr_at = strchr(fullpath, '@'); 13974d3c95f5SJorgen Lundman if (!ptr_at) { 13984d3c95f5SJorgen Lundman *isfs = 1; 13994d3c95f5SJorgen Lundman filename = 0; 14004d3c95f5SJorgen Lundman snapname = 0; 14014d3c95f5SJorgen Lundman fsname = strdup(fullpath); 14024d3c95f5SJorgen Lundman } else { 14034d3c95f5SJorgen Lundman const char *ptr_slash = strchr(ptr_at, '/'); 14044d3c95f5SJorgen Lundman 14054d3c95f5SJorgen Lundman *isfs = 0; 14064d3c95f5SJorgen Lundman fsname = malloc(ptr_at - fullpath + 1); 14074d3c95f5SJorgen Lundman if (!fsname) 14084d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 14094d3c95f5SJorgen Lundman memcpy(fsname, fullpath, ptr_at - fullpath); 14104d3c95f5SJorgen Lundman fsname[ptr_at - fullpath] = 0; 14114d3c95f5SJorgen Lundman if (ptr_at[1] && ptr_at[1] != '/') { 14124d3c95f5SJorgen Lundman snapname = malloc(ptr_slash - ptr_at); 14134d3c95f5SJorgen Lundman if (!snapname) { 14144d3c95f5SJorgen Lundman free(fsname); 14154d3c95f5SJorgen Lundman return ZFS_ERR_OUT_OF_MEMORY; 14164d3c95f5SJorgen Lundman } 14174d3c95f5SJorgen Lundman memcpy(snapname, ptr_at + 1, ptr_slash - ptr_at - 1); 14184d3c95f5SJorgen Lundman snapname[ptr_slash - ptr_at - 1] = 0; 14194d3c95f5SJorgen Lundman } else { 14204d3c95f5SJorgen Lundman snapname = 0; 14214d3c95f5SJorgen Lundman } 14224d3c95f5SJorgen Lundman if (ptr_slash) 14234d3c95f5SJorgen Lundman filename = ptr_slash; 14244d3c95f5SJorgen Lundman else 14254d3c95f5SJorgen Lundman filename = "/"; 14264d3c95f5SJorgen Lundman printf("zfs fsname = '%s' snapname='%s' filename = '%s'\n", 14274d3c95f5SJorgen Lundman fsname, snapname, filename); 14284d3c95f5SJorgen Lundman } 14294d3c95f5SJorgen Lundman 14304d3c95f5SJorgen Lundman 14314d3c95f5SJorgen Lundman err = get_filesystem_dnode(&(data->mos), fsname, dn, data); 14324d3c95f5SJorgen Lundman 14334d3c95f5SJorgen Lundman if (err) { 14344d3c95f5SJorgen Lundman free(fsname); 14354d3c95f5SJorgen Lundman free(snapname); 14364d3c95f5SJorgen Lundman return err; 14374d3c95f5SJorgen Lundman } 14384d3c95f5SJorgen Lundman 14394d3c95f5SJorgen Lundman headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&dn->dn))->dd_head_dataset_obj, dn->endian); 14404d3c95f5SJorgen Lundman 14414d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data); 14424d3c95f5SJorgen Lundman if (err) { 14434d3c95f5SJorgen Lundman free(fsname); 14444d3c95f5SJorgen Lundman free(snapname); 14454d3c95f5SJorgen Lundman return err; 14464d3c95f5SJorgen Lundman } 14474d3c95f5SJorgen Lundman 14484d3c95f5SJorgen Lundman if (snapname) { 14494d3c95f5SJorgen Lundman uint64_t snapobj; 14504d3c95f5SJorgen Lundman 14514d3c95f5SJorgen Lundman snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&mdn->dn))->ds_snapnames_zapobj, mdn->endian); 14524d3c95f5SJorgen Lundman 14534d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), snapobj, 14544d3c95f5SJorgen Lundman DMU_OT_DSL_DS_SNAP_MAP, mdn, data); 14554d3c95f5SJorgen Lundman if (!err) 14564d3c95f5SJorgen Lundman err = zap_lookup(mdn, snapname, &headobj, data); 14574d3c95f5SJorgen Lundman if (!err) 14584d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, mdn, data); 14594d3c95f5SJorgen Lundman if (err) { 14604d3c95f5SJorgen Lundman free(fsname); 14614d3c95f5SJorgen Lundman free(snapname); 14624d3c95f5SJorgen Lundman return err; 14634d3c95f5SJorgen Lundman } 14644d3c95f5SJorgen Lundman } 14654d3c95f5SJorgen Lundman 14664d3c95f5SJorgen Lundman if (mdnobj) 14674d3c95f5SJorgen Lundman *mdnobj = headobj; 14684d3c95f5SJorgen Lundman 14694d3c95f5SJorgen Lundman make_mdn(mdn, data); 14704d3c95f5SJorgen Lundman 14714d3c95f5SJorgen Lundman if (*isfs) { 14724d3c95f5SJorgen Lundman free(fsname); 14734d3c95f5SJorgen Lundman free(snapname); 14744d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 14754d3c95f5SJorgen Lundman } 14764d3c95f5SJorgen Lundman err = dnode_get_path(mdn, filename, dn, data); 14774d3c95f5SJorgen Lundman free(fsname); 14784d3c95f5SJorgen Lundman free(snapname); 14794d3c95f5SJorgen Lundman return err; 14804d3c95f5SJorgen Lundman } 14814d3c95f5SJorgen Lundman 14824d3c95f5SJorgen Lundman /* 14834d3c95f5SJorgen Lundman * For a given XDR packed nvlist, verify the first 4 bytes and move on. 14844d3c95f5SJorgen Lundman * 14854d3c95f5SJorgen Lundman * An XDR packed nvlist is encoded as (comments from nvs_xdr_create) : 14864d3c95f5SJorgen Lundman * 14874d3c95f5SJorgen Lundman * encoding method/host endian (4 bytes) 14884d3c95f5SJorgen Lundman * nvl_version (4 bytes) 14894d3c95f5SJorgen Lundman * nvl_nvflag (4 bytes) 14904d3c95f5SJorgen Lundman * encoded nvpairs: 14914d3c95f5SJorgen Lundman * encoded size of the nvpair (4 bytes) 14924d3c95f5SJorgen Lundman * decoded size of the nvpair (4 bytes) 14934d3c95f5SJorgen Lundman * name string size (4 bytes) 14944d3c95f5SJorgen Lundman * name string data (sizeof(NV_ALIGN4(string)) 14954d3c95f5SJorgen Lundman * data type (4 bytes) 14964d3c95f5SJorgen Lundman * # of elements in the nvpair (4 bytes) 14974d3c95f5SJorgen Lundman * data 14984d3c95f5SJorgen Lundman * 2 zero's for the last nvpair 14994d3c95f5SJorgen Lundman * (end of the entire list) (8 bytes) 15004d3c95f5SJorgen Lundman * 15014d3c95f5SJorgen Lundman */ 15024d3c95f5SJorgen Lundman 15034d3c95f5SJorgen Lundman static int 15044d3c95f5SJorgen Lundman nvlist_find_value(char *nvlist, char *name, int valtype, char **val, 15054d3c95f5SJorgen Lundman size_t *size_out, size_t *nelm_out) 15064d3c95f5SJorgen Lundman { 15074d3c95f5SJorgen Lundman int name_len, type, encode_size; 15084d3c95f5SJorgen Lundman char *nvpair, *nvp_name; 15094d3c95f5SJorgen Lundman 15104d3c95f5SJorgen Lundman /* Verify if the 1st and 2nd byte in the nvlist are valid. */ 15114d3c95f5SJorgen Lundman /* NOTE: independently of what endianness header announces all 15124d3c95f5SJorgen Lundman subsequent values are big-endian. */ 15134d3c95f5SJorgen Lundman if (nvlist[0] != NV_ENCODE_XDR || (nvlist[1] != NV_LITTLE_ENDIAN 15144d3c95f5SJorgen Lundman && nvlist[1] != NV_BIG_ENDIAN)) { 15154d3c95f5SJorgen Lundman printf("zfs incorrect nvlist header\n"); 15164d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 15174d3c95f5SJorgen Lundman } 15184d3c95f5SJorgen Lundman 15194d3c95f5SJorgen Lundman /* skip the header, nvl_version, and nvl_nvflag */ 15204d3c95f5SJorgen Lundman nvlist = nvlist + 4 * 3; 15214d3c95f5SJorgen Lundman /* 15224d3c95f5SJorgen Lundman * Loop thru the nvpair list 15234d3c95f5SJorgen Lundman * The XDR representation of an integer is in big-endian byte order. 15244d3c95f5SJorgen Lundman */ 15254d3c95f5SJorgen Lundman while ((encode_size = be32_to_cpu(*(uint32_t *) nvlist))) { 15264d3c95f5SJorgen Lundman int nelm; 15274d3c95f5SJorgen Lundman 15284d3c95f5SJorgen Lundman nvpair = nvlist + 4 * 2; /* skip the encode/decode size */ 15294d3c95f5SJorgen Lundman 15304d3c95f5SJorgen Lundman name_len = be32_to_cpu(*(uint32_t *) nvpair); 15314d3c95f5SJorgen Lundman nvpair += 4; 15324d3c95f5SJorgen Lundman 15334d3c95f5SJorgen Lundman nvp_name = nvpair; 15344d3c95f5SJorgen Lundman nvpair = nvpair + ((name_len + 3) & ~3); /* align */ 15354d3c95f5SJorgen Lundman 15364d3c95f5SJorgen Lundman type = be32_to_cpu(*(uint32_t *) nvpair); 15374d3c95f5SJorgen Lundman nvpair += 4; 15384d3c95f5SJorgen Lundman 15394d3c95f5SJorgen Lundman nelm = be32_to_cpu(*(uint32_t *) nvpair); 15404d3c95f5SJorgen Lundman if (nelm < 1) { 15414d3c95f5SJorgen Lundman printf("empty nvpair\n"); 15424d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 15434d3c95f5SJorgen Lundman } 15444d3c95f5SJorgen Lundman 15454d3c95f5SJorgen Lundman nvpair += 4; 15464d3c95f5SJorgen Lundman 15474d3c95f5SJorgen Lundman if ((strncmp(nvp_name, name, name_len) == 0) && type == valtype) { 15484d3c95f5SJorgen Lundman *val = nvpair; 15494d3c95f5SJorgen Lundman *size_out = encode_size; 15504d3c95f5SJorgen Lundman if (nelm_out) 15514d3c95f5SJorgen Lundman *nelm_out = nelm; 15524d3c95f5SJorgen Lundman return 1; 15534d3c95f5SJorgen Lundman } 15544d3c95f5SJorgen Lundman 15554d3c95f5SJorgen Lundman nvlist += encode_size; /* goto the next nvpair */ 15564d3c95f5SJorgen Lundman } 15574d3c95f5SJorgen Lundman return 0; 15584d3c95f5SJorgen Lundman } 15594d3c95f5SJorgen Lundman 15604d3c95f5SJorgen Lundman int 15614d3c95f5SJorgen Lundman zfs_nvlist_lookup_uint64(char *nvlist, char *name, uint64_t *out) 15624d3c95f5SJorgen Lundman { 15634d3c95f5SJorgen Lundman char *nvpair; 15644d3c95f5SJorgen Lundman size_t size; 15654d3c95f5SJorgen Lundman int found; 15664d3c95f5SJorgen Lundman 15674d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_UINT64, &nvpair, &size, 0); 15684d3c95f5SJorgen Lundman if (!found) 15694d3c95f5SJorgen Lundman return 0; 15704d3c95f5SJorgen Lundman if (size < sizeof(uint64_t)) { 15714d3c95f5SJorgen Lundman printf("invalid uint64\n"); 15724d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 15734d3c95f5SJorgen Lundman } 15744d3c95f5SJorgen Lundman 15754d3c95f5SJorgen Lundman *out = be64_to_cpu(*(uint64_t *) nvpair); 15764d3c95f5SJorgen Lundman return 1; 15774d3c95f5SJorgen Lundman } 15784d3c95f5SJorgen Lundman 15794d3c95f5SJorgen Lundman char * 15804d3c95f5SJorgen Lundman zfs_nvlist_lookup_string(char *nvlist, char *name) 15814d3c95f5SJorgen Lundman { 15824d3c95f5SJorgen Lundman char *nvpair; 15834d3c95f5SJorgen Lundman char *ret; 15844d3c95f5SJorgen Lundman size_t slen; 15854d3c95f5SJorgen Lundman size_t size; 15864d3c95f5SJorgen Lundman int found; 15874d3c95f5SJorgen Lundman 15884d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_STRING, &nvpair, &size, 0); 15894d3c95f5SJorgen Lundman if (!found) 15904d3c95f5SJorgen Lundman return 0; 15914d3c95f5SJorgen Lundman if (size < 4) { 15924d3c95f5SJorgen Lundman printf("invalid string\n"); 15934d3c95f5SJorgen Lundman return 0; 15944d3c95f5SJorgen Lundman } 15954d3c95f5SJorgen Lundman slen = be32_to_cpu(*(uint32_t *) nvpair); 15964d3c95f5SJorgen Lundman if (slen > size - 4) 15974d3c95f5SJorgen Lundman slen = size - 4; 15984d3c95f5SJorgen Lundman ret = malloc(slen + 1); 15994d3c95f5SJorgen Lundman if (!ret) 16004d3c95f5SJorgen Lundman return 0; 16014d3c95f5SJorgen Lundman memcpy(ret, nvpair + 4, slen); 16024d3c95f5SJorgen Lundman ret[slen] = 0; 16034d3c95f5SJorgen Lundman return ret; 16044d3c95f5SJorgen Lundman } 16054d3c95f5SJorgen Lundman 16064d3c95f5SJorgen Lundman char * 16074d3c95f5SJorgen Lundman zfs_nvlist_lookup_nvlist(char *nvlist, char *name) 16084d3c95f5SJorgen Lundman { 16094d3c95f5SJorgen Lundman char *nvpair; 16104d3c95f5SJorgen Lundman char *ret; 16114d3c95f5SJorgen Lundman size_t size; 16124d3c95f5SJorgen Lundman int found; 16134d3c95f5SJorgen Lundman 16144d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, 16154d3c95f5SJorgen Lundman &size, 0); 16164d3c95f5SJorgen Lundman if (!found) 16174d3c95f5SJorgen Lundman return 0; 16184d3c95f5SJorgen Lundman ret = calloc(1, size + 3 * sizeof(uint32_t)); 16194d3c95f5SJorgen Lundman if (!ret) 16204d3c95f5SJorgen Lundman return 0; 16214d3c95f5SJorgen Lundman memcpy(ret, nvlist, sizeof(uint32_t)); 16224d3c95f5SJorgen Lundman 16234d3c95f5SJorgen Lundman memcpy(ret + sizeof(uint32_t), nvpair, size); 16244d3c95f5SJorgen Lundman return ret; 16254d3c95f5SJorgen Lundman } 16264d3c95f5SJorgen Lundman 16274d3c95f5SJorgen Lundman int 16284d3c95f5SJorgen Lundman zfs_nvlist_lookup_nvlist_array_get_nelm(char *nvlist, char *name) 16294d3c95f5SJorgen Lundman { 16304d3c95f5SJorgen Lundman char *nvpair; 16314d3c95f5SJorgen Lundman size_t nelm, size; 16324d3c95f5SJorgen Lundman int found; 16334d3c95f5SJorgen Lundman 16344d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, 16354d3c95f5SJorgen Lundman &size, &nelm); 16364d3c95f5SJorgen Lundman if (!found) 16374d3c95f5SJorgen Lundman return -1; 16384d3c95f5SJorgen Lundman return nelm; 16394d3c95f5SJorgen Lundman } 16404d3c95f5SJorgen Lundman 16414d3c95f5SJorgen Lundman char * 16424d3c95f5SJorgen Lundman zfs_nvlist_lookup_nvlist_array(char *nvlist, char *name, 16434d3c95f5SJorgen Lundman size_t index) 16444d3c95f5SJorgen Lundman { 16454d3c95f5SJorgen Lundman char *nvpair, *nvpairptr; 16464d3c95f5SJorgen Lundman int found; 16474d3c95f5SJorgen Lundman char *ret; 16484d3c95f5SJorgen Lundman size_t size; 16494d3c95f5SJorgen Lundman unsigned i; 16504d3c95f5SJorgen Lundman size_t nelm; 16514d3c95f5SJorgen Lundman 16524d3c95f5SJorgen Lundman found = nvlist_find_value(nvlist, name, DATA_TYPE_NVLIST, &nvpair, 16534d3c95f5SJorgen Lundman &size, &nelm); 16544d3c95f5SJorgen Lundman if (!found) 16554d3c95f5SJorgen Lundman return 0; 16564d3c95f5SJorgen Lundman if (index >= nelm) { 16574d3c95f5SJorgen Lundman printf("trying to lookup past nvlist array\n"); 16584d3c95f5SJorgen Lundman return 0; 16594d3c95f5SJorgen Lundman } 16604d3c95f5SJorgen Lundman 16614d3c95f5SJorgen Lundman nvpairptr = nvpair; 16624d3c95f5SJorgen Lundman 16634d3c95f5SJorgen Lundman for (i = 0; i < index; i++) { 16644d3c95f5SJorgen Lundman uint32_t encode_size; 16654d3c95f5SJorgen Lundman 16664d3c95f5SJorgen Lundman /* skip the header, nvl_version, and nvl_nvflag */ 16674d3c95f5SJorgen Lundman nvpairptr = nvpairptr + 4 * 2; 16684d3c95f5SJorgen Lundman 16694d3c95f5SJorgen Lundman while (nvpairptr < nvpair + size 16704d3c95f5SJorgen Lundman && (encode_size = be32_to_cpu(*(uint32_t *) nvpairptr))) 16714d3c95f5SJorgen Lundman nvlist += encode_size; /* goto the next nvpair */ 16724d3c95f5SJorgen Lundman 16734d3c95f5SJorgen Lundman nvlist = nvlist + 4 * 2; /* skip the ending 2 zeros - 8 bytes */ 16744d3c95f5SJorgen Lundman } 16754d3c95f5SJorgen Lundman 16764d3c95f5SJorgen Lundman if (nvpairptr >= nvpair + size 16774d3c95f5SJorgen Lundman || nvpairptr + be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2)) 16784d3c95f5SJorgen Lundman >= nvpair + size) { 16794d3c95f5SJorgen Lundman printf("incorrect nvlist array\n"); 16804d3c95f5SJorgen Lundman return 0; 16814d3c95f5SJorgen Lundman } 16824d3c95f5SJorgen Lundman 16834d3c95f5SJorgen Lundman ret = calloc(1, be32_to_cpu(*(uint32_t *) (nvpairptr + 4 * 2)) 16844d3c95f5SJorgen Lundman + 3 * sizeof(uint32_t)); 16854d3c95f5SJorgen Lundman if (!ret) 16864d3c95f5SJorgen Lundman return 0; 16874d3c95f5SJorgen Lundman memcpy(ret, nvlist, sizeof(uint32_t)); 16884d3c95f5SJorgen Lundman 16894d3c95f5SJorgen Lundman memcpy(ret + sizeof(uint32_t), nvpairptr, size); 16904d3c95f5SJorgen Lundman return ret; 16914d3c95f5SJorgen Lundman } 16924d3c95f5SJorgen Lundman 16934d3c95f5SJorgen Lundman static int 16944d3c95f5SJorgen Lundman int_zfs_fetch_nvlist(struct zfs_data *data, char **nvlist) 16954d3c95f5SJorgen Lundman { 16964d3c95f5SJorgen Lundman int err; 16974d3c95f5SJorgen Lundman 16984d3c95f5SJorgen Lundman *nvlist = malloc(VDEV_PHYS_SIZE); 16994d3c95f5SJorgen Lundman /* Read in the vdev name-value pair list (112K). */ 17004d3c95f5SJorgen Lundman err = zfs_devread(data->vdev_phys_sector, 0, VDEV_PHYS_SIZE, *nvlist); 17014d3c95f5SJorgen Lundman if (err) { 17024d3c95f5SJorgen Lundman free(*nvlist); 17034d3c95f5SJorgen Lundman *nvlist = 0; 17044d3c95f5SJorgen Lundman return err; 17054d3c95f5SJorgen Lundman } 17064d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 17074d3c95f5SJorgen Lundman } 17084d3c95f5SJorgen Lundman 17094d3c95f5SJorgen Lundman /* 17104d3c95f5SJorgen Lundman * Check the disk label information and retrieve needed vdev name-value pairs. 17114d3c95f5SJorgen Lundman * 17124d3c95f5SJorgen Lundman */ 17134d3c95f5SJorgen Lundman static int 17144d3c95f5SJorgen Lundman check_pool_label(struct zfs_data *data) 17154d3c95f5SJorgen Lundman { 17164d3c95f5SJorgen Lundman uint64_t pool_state; 17174d3c95f5SJorgen Lundman char *nvlist; /* for the pool */ 17184d3c95f5SJorgen Lundman char *vdevnvlist; /* for the vdev */ 17194d3c95f5SJorgen Lundman uint64_t diskguid; 17204d3c95f5SJorgen Lundman uint64_t version; 17214d3c95f5SJorgen Lundman int found; 17224d3c95f5SJorgen Lundman int err; 17234d3c95f5SJorgen Lundman 17244d3c95f5SJorgen Lundman err = int_zfs_fetch_nvlist(data, &nvlist); 17254d3c95f5SJorgen Lundman if (err) 17264d3c95f5SJorgen Lundman return err; 17274d3c95f5SJorgen Lundman 17284d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_STATE, 17294d3c95f5SJorgen Lundman &pool_state); 17304d3c95f5SJorgen Lundman if (!found) { 17314d3c95f5SJorgen Lundman free(nvlist); 17324d3c95f5SJorgen Lundman printf("zfs pool state not found\n"); 17334d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17344d3c95f5SJorgen Lundman } 17354d3c95f5SJorgen Lundman 17364d3c95f5SJorgen Lundman if (pool_state == POOL_STATE_DESTROYED) { 17374d3c95f5SJorgen Lundman free(nvlist); 17384d3c95f5SJorgen Lundman printf("zpool is marked as destroyed\n"); 17394d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17404d3c95f5SJorgen Lundman } 17414d3c95f5SJorgen Lundman 17424d3c95f5SJorgen Lundman data->label_txg = 0; 17434d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_TXG, 17444d3c95f5SJorgen Lundman &data->label_txg); 17454d3c95f5SJorgen Lundman if (!found) { 17464d3c95f5SJorgen Lundman free(nvlist); 17474d3c95f5SJorgen Lundman printf("zfs pool txg not found\n"); 17484d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17494d3c95f5SJorgen Lundman } 17504d3c95f5SJorgen Lundman 17514d3c95f5SJorgen Lundman /* not an active device */ 17524d3c95f5SJorgen Lundman if (data->label_txg == 0) { 17534d3c95f5SJorgen Lundman free(nvlist); 17544d3c95f5SJorgen Lundman printf("zpool is not active\n"); 17554d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17564d3c95f5SJorgen Lundman } 17574d3c95f5SJorgen Lundman 17584d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_VERSION, 17594d3c95f5SJorgen Lundman &version); 17604d3c95f5SJorgen Lundman if (!found) { 17614d3c95f5SJorgen Lundman free(nvlist); 17624d3c95f5SJorgen Lundman printf("zpool config version not found\n"); 17634d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17644d3c95f5SJorgen Lundman } 17654d3c95f5SJorgen Lundman 17664d3c95f5SJorgen Lundman if (version > SPA_VERSION) { 17674d3c95f5SJorgen Lundman free(nvlist); 17684d3c95f5SJorgen Lundman printf("SPA version too new %llu > %llu\n", 17694d3c95f5SJorgen Lundman (unsigned long long) version, 17704d3c95f5SJorgen Lundman (unsigned long long) SPA_VERSION); 17714d3c95f5SJorgen Lundman return ZFS_ERR_NOT_IMPLEMENTED_YET; 17724d3c95f5SJorgen Lundman } 17734d3c95f5SJorgen Lundman 17744d3c95f5SJorgen Lundman vdevnvlist = zfs_nvlist_lookup_nvlist(nvlist, ZPOOL_CONFIG_VDEV_TREE); 17754d3c95f5SJorgen Lundman if (!vdevnvlist) { 17764d3c95f5SJorgen Lundman free(nvlist); 17774d3c95f5SJorgen Lundman printf("ZFS config vdev tree not found\n"); 17784d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17794d3c95f5SJorgen Lundman } 17804d3c95f5SJorgen Lundman 17814d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(vdevnvlist, ZPOOL_CONFIG_ASHIFT, 17824d3c95f5SJorgen Lundman &data->vdev_ashift); 17834d3c95f5SJorgen Lundman free(vdevnvlist); 17844d3c95f5SJorgen Lundman if (!found) { 17854d3c95f5SJorgen Lundman free(nvlist); 17864d3c95f5SJorgen Lundman printf("ZPOOL config ashift not found\n"); 17874d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17884d3c95f5SJorgen Lundman } 17894d3c95f5SJorgen Lundman 17904d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_GUID, &diskguid); 17914d3c95f5SJorgen Lundman if (!found) { 17924d3c95f5SJorgen Lundman free(nvlist); 17934d3c95f5SJorgen Lundman printf("ZPOOL config guid not found\n"); 17944d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 17954d3c95f5SJorgen Lundman } 17964d3c95f5SJorgen Lundman 17974d3c95f5SJorgen Lundman found = zfs_nvlist_lookup_uint64(nvlist, ZPOOL_CONFIG_POOL_GUID, &data->pool_guid); 17984d3c95f5SJorgen Lundman if (!found) { 17994d3c95f5SJorgen Lundman free(nvlist); 18004d3c95f5SJorgen Lundman printf("ZPOOL config pool guid not found\n"); 18014d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 18024d3c95f5SJorgen Lundman } 18034d3c95f5SJorgen Lundman 18044d3c95f5SJorgen Lundman free(nvlist); 18054d3c95f5SJorgen Lundman 18064d3c95f5SJorgen Lundman printf("ZFS Pool GUID: %llu (%016llx) Label: GUID: %llu (%016llx), txg: %llu, SPA v%llu, ashift: %llu\n", 18074d3c95f5SJorgen Lundman (unsigned long long) data->pool_guid, 18084d3c95f5SJorgen Lundman (unsigned long long) data->pool_guid, 18094d3c95f5SJorgen Lundman (unsigned long long) diskguid, 18104d3c95f5SJorgen Lundman (unsigned long long) diskguid, 18114d3c95f5SJorgen Lundman (unsigned long long) data->label_txg, 18124d3c95f5SJorgen Lundman (unsigned long long) version, 18134d3c95f5SJorgen Lundman (unsigned long long) data->vdev_ashift); 18144d3c95f5SJorgen Lundman 18154d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 18164d3c95f5SJorgen Lundman } 18174d3c95f5SJorgen Lundman 18184d3c95f5SJorgen Lundman /* 18194d3c95f5SJorgen Lundman * vdev_label_start returns the physical disk offset (in bytes) of 18204d3c95f5SJorgen Lundman * label "l". 18214d3c95f5SJorgen Lundman */ 18224d3c95f5SJorgen Lundman static uint64_t vdev_label_start(uint64_t psize, int l) 18234d3c95f5SJorgen Lundman { 18244d3c95f5SJorgen Lundman return (l * sizeof(vdev_label_t) + (l < VDEV_LABELS / 2 ? 18254d3c95f5SJorgen Lundman 0 : psize - 18264d3c95f5SJorgen Lundman VDEV_LABELS * sizeof(vdev_label_t))); 18274d3c95f5SJorgen Lundman } 18284d3c95f5SJorgen Lundman 18294d3c95f5SJorgen Lundman void 18304d3c95f5SJorgen Lundman zfs_unmount(struct zfs_data *data) 18314d3c95f5SJorgen Lundman { 18324d3c95f5SJorgen Lundman free(data->dnode_buf); 18334d3c95f5SJorgen Lundman free(data->dnode_mdn); 18344d3c95f5SJorgen Lundman free(data->file_buf); 18354d3c95f5SJorgen Lundman free(data); 18364d3c95f5SJorgen Lundman } 18374d3c95f5SJorgen Lundman 18384d3c95f5SJorgen Lundman /* 18394d3c95f5SJorgen Lundman * zfs_mount() locates a valid uberblock of the root pool and read in its MOS 18404d3c95f5SJorgen Lundman * to the memory address MOS. 18414d3c95f5SJorgen Lundman * 18424d3c95f5SJorgen Lundman */ 18434d3c95f5SJorgen Lundman struct zfs_data * 18444d3c95f5SJorgen Lundman zfs_mount(device_t dev) 18454d3c95f5SJorgen Lundman { 18464d3c95f5SJorgen Lundman struct zfs_data *data = 0; 18474d3c95f5SJorgen Lundman int label = 0, bestlabel = -1; 18484d3c95f5SJorgen Lundman char *ub_array; 18494d3c95f5SJorgen Lundman uberblock_t *ubbest; 18504d3c95f5SJorgen Lundman uberblock_t *ubcur = NULL; 18514d3c95f5SJorgen Lundman void *osp = 0; 18524d3c95f5SJorgen Lundman size_t ospsize; 18534d3c95f5SJorgen Lundman int err; 18544d3c95f5SJorgen Lundman 18554d3c95f5SJorgen Lundman data = malloc(sizeof(*data)); 18564d3c95f5SJorgen Lundman if (!data) 18574d3c95f5SJorgen Lundman return 0; 18584d3c95f5SJorgen Lundman memset(data, 0, sizeof(*data)); 18594d3c95f5SJorgen Lundman 18604d3c95f5SJorgen Lundman ub_array = malloc(VDEV_UBERBLOCK_RING); 18614d3c95f5SJorgen Lundman if (!ub_array) { 18624d3c95f5SJorgen Lundman zfs_unmount(data); 18634d3c95f5SJorgen Lundman return 0; 18644d3c95f5SJorgen Lundman } 18654d3c95f5SJorgen Lundman 18664d3c95f5SJorgen Lundman ubbest = malloc(sizeof(*ubbest)); 18674d3c95f5SJorgen Lundman if (!ubbest) { 18684d3c95f5SJorgen Lundman zfs_unmount(data); 18694d3c95f5SJorgen Lundman return 0; 18704d3c95f5SJorgen Lundman } 18714d3c95f5SJorgen Lundman memset(ubbest, 0, sizeof(*ubbest)); 18724d3c95f5SJorgen Lundman 18734d3c95f5SJorgen Lundman /* 18744d3c95f5SJorgen Lundman * some eltorito stacks don't give us a size and 18754d3c95f5SJorgen Lundman * we end up setting the size to MAXUINT, further 18764d3c95f5SJorgen Lundman * some of these devices stop working once a single 18774d3c95f5SJorgen Lundman * read past the end has been issued. Checking 18784d3c95f5SJorgen Lundman * for a maximum part_length and skipping the backup 18794d3c95f5SJorgen Lundman * labels at the end of the slice/partition/device 18804d3c95f5SJorgen Lundman * avoids breaking down on such devices. 18814d3c95f5SJorgen Lundman */ 18824d3c95f5SJorgen Lundman const int vdevnum = 18834d3c95f5SJorgen Lundman dev->part_length == 0 ? 18844d3c95f5SJorgen Lundman VDEV_LABELS / 2 : VDEV_LABELS; 18854d3c95f5SJorgen Lundman 18864d3c95f5SJorgen Lundman /* Size in bytes of the device (disk or partition) aligned to label size*/ 18874d3c95f5SJorgen Lundman uint64_t device_size = 18884d3c95f5SJorgen Lundman dev->part_length << SECTOR_BITS; 18894d3c95f5SJorgen Lundman 18904d3c95f5SJorgen Lundman const uint64_t alignedbytes = 18914d3c95f5SJorgen Lundman P2ALIGN(device_size, (uint64_t) sizeof(vdev_label_t)); 18924d3c95f5SJorgen Lundman 18934d3c95f5SJorgen Lundman for (label = 0; label < vdevnum; label++) { 18944d3c95f5SJorgen Lundman uint64_t labelstartbytes = vdev_label_start(alignedbytes, label); 18954d3c95f5SJorgen Lundman uint64_t labelstart = labelstartbytes >> SECTOR_BITS; 18964d3c95f5SJorgen Lundman 18974d3c95f5SJorgen Lundman debug("zfs reading label %d at sector %llu (byte %llu)\n", 18984d3c95f5SJorgen Lundman label, (unsigned long long) labelstart, 18994d3c95f5SJorgen Lundman (unsigned long long) labelstartbytes); 19004d3c95f5SJorgen Lundman 19014d3c95f5SJorgen Lundman data->vdev_phys_sector = labelstart + 19024d3c95f5SJorgen Lundman ((VDEV_SKIP_SIZE + VDEV_BOOT_HEADER_SIZE) >> SECTOR_BITS); 19034d3c95f5SJorgen Lundman 19044d3c95f5SJorgen Lundman err = check_pool_label(data); 19054d3c95f5SJorgen Lundman if (err) { 19064d3c95f5SJorgen Lundman printf("zfs error checking label %d\n", label); 19074d3c95f5SJorgen Lundman continue; 19084d3c95f5SJorgen Lundman } 19094d3c95f5SJorgen Lundman 19104d3c95f5SJorgen Lundman /* Read in the uberblock ring (128K). */ 19114d3c95f5SJorgen Lundman err = zfs_devread(data->vdev_phys_sector + 19124d3c95f5SJorgen Lundman (VDEV_PHYS_SIZE >> SECTOR_BITS), 19134d3c95f5SJorgen Lundman 0, VDEV_UBERBLOCK_RING, ub_array); 19144d3c95f5SJorgen Lundman if (err) { 19154d3c95f5SJorgen Lundman printf("zfs error reading uberblock ring for label %d\n", label); 19164d3c95f5SJorgen Lundman continue; 19174d3c95f5SJorgen Lundman } 19184d3c95f5SJorgen Lundman 19194d3c95f5SJorgen Lundman ubcur = find_bestub(ub_array, data); 19204d3c95f5SJorgen Lundman if (!ubcur) { 19214d3c95f5SJorgen Lundman printf("zfs No good uberblocks found in label %d\n", label); 19224d3c95f5SJorgen Lundman continue; 19234d3c95f5SJorgen Lundman } 19244d3c95f5SJorgen Lundman 19254d3c95f5SJorgen Lundman if (vdev_uberblock_compare(ubcur, ubbest) > 0) { 19264d3c95f5SJorgen Lundman /* Looks like the block is good, so use it.*/ 19274d3c95f5SJorgen Lundman memcpy(ubbest, ubcur, sizeof(*ubbest)); 19284d3c95f5SJorgen Lundman bestlabel = label; 19294d3c95f5SJorgen Lundman debug("zfs Current best uberblock found in label %d\n", label); 19304d3c95f5SJorgen Lundman } 19314d3c95f5SJorgen Lundman } 19324d3c95f5SJorgen Lundman free(ub_array); 19334d3c95f5SJorgen Lundman 19344d3c95f5SJorgen Lundman /* We zero'd the structure to begin with. If we never assigned to it, 19354d3c95f5SJorgen Lundman magic will still be zero. */ 19364d3c95f5SJorgen Lundman if (!ubbest->ub_magic) { 19374d3c95f5SJorgen Lundman printf("couldn't find a valid ZFS label\n"); 19384d3c95f5SJorgen Lundman zfs_unmount(data); 19394d3c95f5SJorgen Lundman free(ubbest); 19404d3c95f5SJorgen Lundman return 0; 19414d3c95f5SJorgen Lundman } 19424d3c95f5SJorgen Lundman 19434d3c95f5SJorgen Lundman debug("zfs ubbest %p in label %d\n", ubbest, bestlabel); 19444d3c95f5SJorgen Lundman 19454d3c95f5SJorgen Lundman zfs_endian_t ub_endian = 19464d3c95f5SJorgen Lundman zfs_to_cpu64(ubbest->ub_magic, LITTLE_ENDIAN) == UBERBLOCK_MAGIC 19474d3c95f5SJorgen Lundman ? LITTLE_ENDIAN : BIG_ENDIAN; 19484d3c95f5SJorgen Lundman 19494d3c95f5SJorgen Lundman debug("zfs endian set to %s\n", !ub_endian ? "big" : "little"); 19504d3c95f5SJorgen Lundman 19514d3c95f5SJorgen Lundman err = zio_read(&ubbest->ub_rootbp, ub_endian, &osp, &ospsize, data); 19524d3c95f5SJorgen Lundman 19534d3c95f5SJorgen Lundman if (err) { 19544d3c95f5SJorgen Lundman printf("couldn't zio_read object directory\n"); 19554d3c95f5SJorgen Lundman zfs_unmount(data); 19564d3c95f5SJorgen Lundman free(ubbest); 19574d3c95f5SJorgen Lundman return 0; 19584d3c95f5SJorgen Lundman } 19594d3c95f5SJorgen Lundman 19604d3c95f5SJorgen Lundman if (ospsize < OBJSET_PHYS_SIZE_V14) { 19614d3c95f5SJorgen Lundman printf("osp too small\n"); 19624d3c95f5SJorgen Lundman zfs_unmount(data); 19634d3c95f5SJorgen Lundman free(osp); 19644d3c95f5SJorgen Lundman free(ubbest); 19654d3c95f5SJorgen Lundman return 0; 19664d3c95f5SJorgen Lundman } 19674d3c95f5SJorgen Lundman 19684d3c95f5SJorgen Lundman /* Got the MOS. Save it at the memory addr MOS. */ 19694d3c95f5SJorgen Lundman memmove(&(data->mos.dn), &((objset_phys_t *) osp)->os_meta_dnode, DNODE_SIZE); 19704d3c95f5SJorgen Lundman data->mos.endian = 19714d3c95f5SJorgen Lundman (zfs_to_cpu64(ubbest->ub_rootbp.blk_prop, ub_endian) >> 63) & 1; 19724d3c95f5SJorgen Lundman memmove(&(data->current_uberblock), ubbest, sizeof(uberblock_t)); 19734d3c95f5SJorgen Lundman 19744d3c95f5SJorgen Lundman free(osp); 19754d3c95f5SJorgen Lundman free(ubbest); 19764d3c95f5SJorgen Lundman 19774d3c95f5SJorgen Lundman return data; 19784d3c95f5SJorgen Lundman } 19794d3c95f5SJorgen Lundman 19804d3c95f5SJorgen Lundman int 19814d3c95f5SJorgen Lundman zfs_fetch_nvlist(device_t dev, char **nvlist) 19824d3c95f5SJorgen Lundman { 19834d3c95f5SJorgen Lundman struct zfs_data *zfs; 19844d3c95f5SJorgen Lundman int err; 19854d3c95f5SJorgen Lundman 19864d3c95f5SJorgen Lundman zfs = zfs_mount(dev); 19874d3c95f5SJorgen Lundman if (!zfs) 19884d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 19894d3c95f5SJorgen Lundman err = int_zfs_fetch_nvlist(zfs, nvlist); 19904d3c95f5SJorgen Lundman zfs_unmount(zfs); 19914d3c95f5SJorgen Lundman return err; 19924d3c95f5SJorgen Lundman } 19934d3c95f5SJorgen Lundman 19944d3c95f5SJorgen Lundman /* 19954d3c95f5SJorgen Lundman * zfs_open() locates a file in the rootpool by following the 19964d3c95f5SJorgen Lundman * MOS and places the dnode of the file in the memory address DNODE. 19974d3c95f5SJorgen Lundman */ 19984d3c95f5SJorgen Lundman int 19994d3c95f5SJorgen Lundman zfs_open(struct zfs_file *file, const char *fsfilename) 20004d3c95f5SJorgen Lundman { 20014d3c95f5SJorgen Lundman struct zfs_data *data; 20024d3c95f5SJorgen Lundman int err; 20034d3c95f5SJorgen Lundman int isfs; 20044d3c95f5SJorgen Lundman 20054d3c95f5SJorgen Lundman data = zfs_mount(file->device); 20064d3c95f5SJorgen Lundman if (!data) 20074d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 20084d3c95f5SJorgen Lundman 20094d3c95f5SJorgen Lundman err = dnode_get_fullpath(fsfilename, &(data->mdn), 0, 20104d3c95f5SJorgen Lundman &(data->dnode), &isfs, data); 20114d3c95f5SJorgen Lundman if (err) { 20124d3c95f5SJorgen Lundman zfs_unmount(data); 20134d3c95f5SJorgen Lundman return err; 20144d3c95f5SJorgen Lundman } 20154d3c95f5SJorgen Lundman 20164d3c95f5SJorgen Lundman if (isfs) { 20174d3c95f5SJorgen Lundman zfs_unmount(data); 20184d3c95f5SJorgen Lundman printf("Missing @ or / separator\n"); 20194d3c95f5SJorgen Lundman return ZFS_ERR_FILE_NOT_FOUND; 20204d3c95f5SJorgen Lundman } 20214d3c95f5SJorgen Lundman 20224d3c95f5SJorgen Lundman /* We found the dnode for this file. Verify if it is a plain file. */ 20234d3c95f5SJorgen Lundman if (data->dnode.dn.dn_type != DMU_OT_PLAIN_FILE_CONTENTS) { 20244d3c95f5SJorgen Lundman zfs_unmount(data); 20254d3c95f5SJorgen Lundman printf("not a file\n"); 20264d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FILE_TYPE; 20274d3c95f5SJorgen Lundman } 20284d3c95f5SJorgen Lundman 20294d3c95f5SJorgen Lundman /* get the file size and set the file position to 0 */ 20304d3c95f5SJorgen Lundman 20314d3c95f5SJorgen Lundman /* 20324d3c95f5SJorgen Lundman * For DMU_OT_SA we will need to locate the SIZE attribute 20334d3c95f5SJorgen Lundman * attribute, which could be either in the bonus buffer 20344d3c95f5SJorgen Lundman * or the "spill" block. 20354d3c95f5SJorgen Lundman */ 20364d3c95f5SJorgen Lundman if (data->dnode.dn.dn_bonustype == DMU_OT_SA) { 20374d3c95f5SJorgen Lundman void *sahdrp; 20384d3c95f5SJorgen Lundman int hdrsize; 20394d3c95f5SJorgen Lundman 20404d3c95f5SJorgen Lundman if (data->dnode.dn.dn_bonuslen != 0) { 20414d3c95f5SJorgen Lundman sahdrp = (sa_hdr_phys_t *) DN_BONUS(&data->dnode.dn); 20424d3c95f5SJorgen Lundman } else if (data->dnode.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR) { 20434d3c95f5SJorgen Lundman blkptr_t *bp = &data->dnode.dn.dn_spill; 20444d3c95f5SJorgen Lundman 20454d3c95f5SJorgen Lundman err = zio_read(bp, data->dnode.endian, &sahdrp, NULL, data); 20464d3c95f5SJorgen Lundman if (err) 20474d3c95f5SJorgen Lundman return err; 20484d3c95f5SJorgen Lundman } else { 20494d3c95f5SJorgen Lundman printf("filesystem is corrupt :(\n"); 20504d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 20514d3c95f5SJorgen Lundman } 20524d3c95f5SJorgen Lundman 20534d3c95f5SJorgen Lundman hdrsize = SA_HDR_SIZE(((sa_hdr_phys_t *) sahdrp)); 20544d3c95f5SJorgen Lundman file->size = *(uint64_t *) ((char *) sahdrp + hdrsize + SA_SIZE_OFFSET); 20554d3c95f5SJorgen Lundman } else { 20564d3c95f5SJorgen Lundman file->size = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&data->dnode.dn))->zp_size, data->dnode.endian); 20574d3c95f5SJorgen Lundman } 20584d3c95f5SJorgen Lundman 20594d3c95f5SJorgen Lundman file->data = data; 20604d3c95f5SJorgen Lundman file->offset = 0; 20614d3c95f5SJorgen Lundman 20624d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 20634d3c95f5SJorgen Lundman } 20644d3c95f5SJorgen Lundman 20654d3c95f5SJorgen Lundman uint64_t 20664d3c95f5SJorgen Lundman zfs_read(zfs_file_t file, char *buf, uint64_t len) 20674d3c95f5SJorgen Lundman { 20684d3c95f5SJorgen Lundman struct zfs_data *data = (struct zfs_data *) file->data; 20694d3c95f5SJorgen Lundman int blksz, movesize; 20704d3c95f5SJorgen Lundman uint64_t length; 20714d3c95f5SJorgen Lundman int64_t red; 20724d3c95f5SJorgen Lundman int err; 20734d3c95f5SJorgen Lundman 20744d3c95f5SJorgen Lundman if (data->file_buf == NULL) { 20754d3c95f5SJorgen Lundman data->file_buf = malloc(SPA_MAXBLOCKSIZE); 20764d3c95f5SJorgen Lundman if (!data->file_buf) 20774d3c95f5SJorgen Lundman return -1; 20784d3c95f5SJorgen Lundman data->file_start = data->file_end = 0; 20794d3c95f5SJorgen Lundman } 20804d3c95f5SJorgen Lundman 20814d3c95f5SJorgen Lundman /* 20824d3c95f5SJorgen Lundman * If offset is in memory, move it into the buffer provided and return. 20834d3c95f5SJorgen Lundman */ 20844d3c95f5SJorgen Lundman if (file->offset >= data->file_start 20854d3c95f5SJorgen Lundman && file->offset + len <= data->file_end) { 20864d3c95f5SJorgen Lundman memmove(buf, data->file_buf + file->offset - data->file_start, 20874d3c95f5SJorgen Lundman len); 20884d3c95f5SJorgen Lundman return len; 20894d3c95f5SJorgen Lundman } 20904d3c95f5SJorgen Lundman 20914d3c95f5SJorgen Lundman blksz = zfs_to_cpu16(data->dnode.dn.dn_datablkszsec, 20924d3c95f5SJorgen Lundman data->dnode.endian) << SPA_MINBLOCKSHIFT; 20934d3c95f5SJorgen Lundman 20944d3c95f5SJorgen Lundman /* 20954d3c95f5SJorgen Lundman * Entire Dnode is too big to fit into the space available. We 20964d3c95f5SJorgen Lundman * will need to read it in chunks. This could be optimized to 20974d3c95f5SJorgen Lundman * read in as large a chunk as there is space available, but for 20984d3c95f5SJorgen Lundman * now, this only reads in one data block at a time. 20994d3c95f5SJorgen Lundman */ 21004d3c95f5SJorgen Lundman length = len; 21014d3c95f5SJorgen Lundman red = 0; 21024d3c95f5SJorgen Lundman while (length) { 21034d3c95f5SJorgen Lundman void *t; 21044d3c95f5SJorgen Lundman /* 21054d3c95f5SJorgen Lundman * Find requested blkid and the offset within that block. 21064d3c95f5SJorgen Lundman */ 2107624c721fSAlejandro Mery uint64_t blkid = file->offset + red; 2108624c721fSAlejandro Mery blkid = do_div(blkid, blksz); 21094d3c95f5SJorgen Lundman free(data->file_buf); 21104d3c95f5SJorgen Lundman data->file_buf = 0; 21114d3c95f5SJorgen Lundman 21124d3c95f5SJorgen Lundman err = dmu_read(&(data->dnode), blkid, &t, 21134d3c95f5SJorgen Lundman 0, data); 21144d3c95f5SJorgen Lundman data->file_buf = t; 21154d3c95f5SJorgen Lundman if (err) 21164d3c95f5SJorgen Lundman return -1; 21174d3c95f5SJorgen Lundman 21184d3c95f5SJorgen Lundman data->file_start = blkid * blksz; 21194d3c95f5SJorgen Lundman data->file_end = data->file_start + blksz; 21204d3c95f5SJorgen Lundman 2121*c79cba37SMasahiro Yamada movesize = min(length, data->file_end - (int)file->offset - red); 21224d3c95f5SJorgen Lundman 21234d3c95f5SJorgen Lundman memmove(buf, data->file_buf + file->offset + red 21244d3c95f5SJorgen Lundman - data->file_start, movesize); 21254d3c95f5SJorgen Lundman buf += movesize; 21264d3c95f5SJorgen Lundman length -= movesize; 21274d3c95f5SJorgen Lundman red += movesize; 21284d3c95f5SJorgen Lundman } 21294d3c95f5SJorgen Lundman 21304d3c95f5SJorgen Lundman return len; 21314d3c95f5SJorgen Lundman } 21324d3c95f5SJorgen Lundman 21334d3c95f5SJorgen Lundman int 21344d3c95f5SJorgen Lundman zfs_close(zfs_file_t file) 21354d3c95f5SJorgen Lundman { 21364d3c95f5SJorgen Lundman zfs_unmount((struct zfs_data *) file->data); 21374d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 21384d3c95f5SJorgen Lundman } 21394d3c95f5SJorgen Lundman 21404d3c95f5SJorgen Lundman int 21414d3c95f5SJorgen Lundman zfs_getmdnobj(device_t dev, const char *fsfilename, 21424d3c95f5SJorgen Lundman uint64_t *mdnobj) 21434d3c95f5SJorgen Lundman { 21444d3c95f5SJorgen Lundman struct zfs_data *data; 21454d3c95f5SJorgen Lundman int err; 21464d3c95f5SJorgen Lundman int isfs; 21474d3c95f5SJorgen Lundman 21484d3c95f5SJorgen Lundman data = zfs_mount(dev); 21494d3c95f5SJorgen Lundman if (!data) 21504d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 21514d3c95f5SJorgen Lundman 21524d3c95f5SJorgen Lundman err = dnode_get_fullpath(fsfilename, &(data->mdn), mdnobj, 21534d3c95f5SJorgen Lundman &(data->dnode), &isfs, data); 21544d3c95f5SJorgen Lundman zfs_unmount(data); 21554d3c95f5SJorgen Lundman return err; 21564d3c95f5SJorgen Lundman } 21574d3c95f5SJorgen Lundman 21584d3c95f5SJorgen Lundman static void 21594d3c95f5SJorgen Lundman fill_fs_info(struct zfs_dirhook_info *info, 21604d3c95f5SJorgen Lundman dnode_end_t mdn, struct zfs_data *data) 21614d3c95f5SJorgen Lundman { 21624d3c95f5SJorgen Lundman int err; 21634d3c95f5SJorgen Lundman dnode_end_t dn; 21644d3c95f5SJorgen Lundman uint64_t objnum; 21654d3c95f5SJorgen Lundman uint64_t headobj; 21664d3c95f5SJorgen Lundman 21674d3c95f5SJorgen Lundman memset(info, 0, sizeof(*info)); 21684d3c95f5SJorgen Lundman 21694d3c95f5SJorgen Lundman info->dir = 1; 21704d3c95f5SJorgen Lundman 21714d3c95f5SJorgen Lundman if (mdn.dn.dn_type == DMU_OT_DSL_DIR) { 21724d3c95f5SJorgen Lundman headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&mdn.dn))->dd_head_dataset_obj, mdn.endian); 21734d3c95f5SJorgen Lundman 21744d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &mdn, data); 21754d3c95f5SJorgen Lundman if (err) { 21764d3c95f5SJorgen Lundman printf("zfs failed here 1\n"); 21774d3c95f5SJorgen Lundman return; 21784d3c95f5SJorgen Lundman } 21794d3c95f5SJorgen Lundman } 21804d3c95f5SJorgen Lundman make_mdn(&mdn, data); 21814d3c95f5SJorgen Lundman err = dnode_get(&mdn, MASTER_NODE_OBJ, DMU_OT_MASTER_NODE, 21824d3c95f5SJorgen Lundman &dn, data); 21834d3c95f5SJorgen Lundman if (err) { 21844d3c95f5SJorgen Lundman printf("zfs failed here 2\n"); 21854d3c95f5SJorgen Lundman return; 21864d3c95f5SJorgen Lundman } 21874d3c95f5SJorgen Lundman 21884d3c95f5SJorgen Lundman err = zap_lookup(&dn, ZFS_ROOT_OBJ, &objnum, data); 21894d3c95f5SJorgen Lundman if (err) { 21904d3c95f5SJorgen Lundman printf("zfs failed here 3\n"); 21914d3c95f5SJorgen Lundman return; 21924d3c95f5SJorgen Lundman } 21934d3c95f5SJorgen Lundman 21944d3c95f5SJorgen Lundman err = dnode_get(&mdn, objnum, 0, &dn, data); 21954d3c95f5SJorgen Lundman if (err) { 21964d3c95f5SJorgen Lundman printf("zfs failed here 4\n"); 21974d3c95f5SJorgen Lundman return; 21984d3c95f5SJorgen Lundman } 21994d3c95f5SJorgen Lundman 22004d3c95f5SJorgen Lundman info->mtimeset = 1; 22014d3c95f5SJorgen Lundman info->mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian); 22024d3c95f5SJorgen Lundman 22034d3c95f5SJorgen Lundman return; 22044d3c95f5SJorgen Lundman } 22054d3c95f5SJorgen Lundman 22064d3c95f5SJorgen Lundman static int iterate_zap(const char *name, uint64_t val, struct zfs_data *data) 22074d3c95f5SJorgen Lundman { 22084d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22094d3c95f5SJorgen Lundman dnode_end_t dn; 22104d3c95f5SJorgen Lundman 22114d3c95f5SJorgen Lundman memset(&info, 0, sizeof(info)); 22124d3c95f5SJorgen Lundman 22134d3c95f5SJorgen Lundman dnode_get(&(data->mdn), val, 0, &dn, data); 22144d3c95f5SJorgen Lundman info.mtimeset = 1; 22154d3c95f5SJorgen Lundman info.mtime = zfs_to_cpu64(((znode_phys_t *) DN_BONUS(&dn.dn))->zp_mtime[0], dn.endian); 22164d3c95f5SJorgen Lundman info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS); 22174d3c95f5SJorgen Lundman debug("zfs type=%d, name=%s\n", 22184d3c95f5SJorgen Lundman (int)dn.dn.dn_type, (char *)name); 22194d3c95f5SJorgen Lundman if (!data->userhook) 22204d3c95f5SJorgen Lundman return 0; 22214d3c95f5SJorgen Lundman return data->userhook(name, &info); 22224d3c95f5SJorgen Lundman } 22234d3c95f5SJorgen Lundman 22244d3c95f5SJorgen Lundman static int iterate_zap_fs(const char *name, uint64_t val, struct zfs_data *data) 22254d3c95f5SJorgen Lundman { 22264d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22274d3c95f5SJorgen Lundman dnode_end_t mdn; 22284d3c95f5SJorgen Lundman int err; 22294d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), val, 0, &mdn, data); 22304d3c95f5SJorgen Lundman if (err) 22314d3c95f5SJorgen Lundman return 0; 22324d3c95f5SJorgen Lundman if (mdn.dn.dn_type != DMU_OT_DSL_DIR) 22334d3c95f5SJorgen Lundman return 0; 22344d3c95f5SJorgen Lundman 22354d3c95f5SJorgen Lundman fill_fs_info(&info, mdn, data); 22364d3c95f5SJorgen Lundman 22374d3c95f5SJorgen Lundman if (!data->userhook) 22384d3c95f5SJorgen Lundman return 0; 22394d3c95f5SJorgen Lundman return data->userhook(name, &info); 22404d3c95f5SJorgen Lundman } 22414d3c95f5SJorgen Lundman 22424d3c95f5SJorgen Lundman static int iterate_zap_snap(const char *name, uint64_t val, struct zfs_data *data) 22434d3c95f5SJorgen Lundman { 22444d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22454d3c95f5SJorgen Lundman char *name2; 22464d3c95f5SJorgen Lundman int ret = 0; 22474d3c95f5SJorgen Lundman dnode_end_t mdn; 22484d3c95f5SJorgen Lundman int err; 22494d3c95f5SJorgen Lundman 22504d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), val, 0, &mdn, data); 22514d3c95f5SJorgen Lundman if (err) 22524d3c95f5SJorgen Lundman return 0; 22534d3c95f5SJorgen Lundman 22544d3c95f5SJorgen Lundman if (mdn.dn.dn_type != DMU_OT_DSL_DATASET) 22554d3c95f5SJorgen Lundman return 0; 22564d3c95f5SJorgen Lundman 22574d3c95f5SJorgen Lundman fill_fs_info(&info, mdn, data); 22584d3c95f5SJorgen Lundman 22594d3c95f5SJorgen Lundman name2 = malloc(strlen(name) + 2); 22604d3c95f5SJorgen Lundman name2[0] = '@'; 22614d3c95f5SJorgen Lundman memcpy(name2 + 1, name, strlen(name) + 1); 22624d3c95f5SJorgen Lundman if (data->userhook) 22634d3c95f5SJorgen Lundman ret = data->userhook(name2, &info); 22644d3c95f5SJorgen Lundman free(name2); 22654d3c95f5SJorgen Lundman return ret; 22664d3c95f5SJorgen Lundman } 22674d3c95f5SJorgen Lundman 22684d3c95f5SJorgen Lundman int 22694d3c95f5SJorgen Lundman zfs_ls(device_t device, const char *path, 22704d3c95f5SJorgen Lundman int (*hook)(const char *, const struct zfs_dirhook_info *)) 22714d3c95f5SJorgen Lundman { 22724d3c95f5SJorgen Lundman struct zfs_data *data; 22734d3c95f5SJorgen Lundman int err; 22744d3c95f5SJorgen Lundman int isfs; 22754d3c95f5SJorgen Lundman 22764d3c95f5SJorgen Lundman data = zfs_mount(device); 22774d3c95f5SJorgen Lundman if (!data) 22784d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FS; 22794d3c95f5SJorgen Lundman 22804d3c95f5SJorgen Lundman data->userhook = hook; 22814d3c95f5SJorgen Lundman 22824d3c95f5SJorgen Lundman err = dnode_get_fullpath(path, &(data->mdn), 0, &(data->dnode), &isfs, data); 22834d3c95f5SJorgen Lundman if (err) { 22844d3c95f5SJorgen Lundman zfs_unmount(data); 22854d3c95f5SJorgen Lundman return err; 22864d3c95f5SJorgen Lundman } 22874d3c95f5SJorgen Lundman if (isfs) { 22884d3c95f5SJorgen Lundman uint64_t childobj, headobj; 22894d3c95f5SJorgen Lundman uint64_t snapobj; 22904d3c95f5SJorgen Lundman dnode_end_t dn; 22914d3c95f5SJorgen Lundman struct zfs_dirhook_info info; 22924d3c95f5SJorgen Lundman 22934d3c95f5SJorgen Lundman fill_fs_info(&info, data->dnode, data); 22944d3c95f5SJorgen Lundman hook("@", &info); 22954d3c95f5SJorgen Lundman 22964d3c95f5SJorgen Lundman childobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian); 22974d3c95f5SJorgen Lundman headobj = zfs_to_cpu64(((dsl_dir_phys_t *) DN_BONUS(&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian); 22984d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), childobj, 22994d3c95f5SJorgen Lundman DMU_OT_DSL_DIR_CHILD_MAP, &dn, data); 23004d3c95f5SJorgen Lundman if (err) { 23014d3c95f5SJorgen Lundman zfs_unmount(data); 23024d3c95f5SJorgen Lundman return err; 23034d3c95f5SJorgen Lundman } 23044d3c95f5SJorgen Lundman 23054d3c95f5SJorgen Lundman 23064d3c95f5SJorgen Lundman zap_iterate(&dn, iterate_zap_fs, data); 23074d3c95f5SJorgen Lundman 23084d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data); 23094d3c95f5SJorgen Lundman if (err) { 23104d3c95f5SJorgen Lundman zfs_unmount(data); 23114d3c95f5SJorgen Lundman return err; 23124d3c95f5SJorgen Lundman } 23134d3c95f5SJorgen Lundman 23144d3c95f5SJorgen Lundman snapobj = zfs_to_cpu64(((dsl_dataset_phys_t *) DN_BONUS(&dn.dn))->ds_snapnames_zapobj, dn.endian); 23154d3c95f5SJorgen Lundman 23164d3c95f5SJorgen Lundman err = dnode_get(&(data->mos), snapobj, 23174d3c95f5SJorgen Lundman DMU_OT_DSL_DS_SNAP_MAP, &dn, data); 23184d3c95f5SJorgen Lundman if (err) { 23194d3c95f5SJorgen Lundman zfs_unmount(data); 23204d3c95f5SJorgen Lundman return err; 23214d3c95f5SJorgen Lundman } 23224d3c95f5SJorgen Lundman 23234d3c95f5SJorgen Lundman zap_iterate(&dn, iterate_zap_snap, data); 23244d3c95f5SJorgen Lundman } else { 23254d3c95f5SJorgen Lundman if (data->dnode.dn.dn_type != DMU_OT_DIRECTORY_CONTENTS) { 23264d3c95f5SJorgen Lundman zfs_unmount(data); 23274d3c95f5SJorgen Lundman printf("not a directory\n"); 23284d3c95f5SJorgen Lundman return ZFS_ERR_BAD_FILE_TYPE; 23294d3c95f5SJorgen Lundman } 23304d3c95f5SJorgen Lundman zap_iterate(&(data->dnode), iterate_zap, data); 23314d3c95f5SJorgen Lundman } 23324d3c95f5SJorgen Lundman zfs_unmount(data); 23334d3c95f5SJorgen Lundman return ZFS_ERR_NONE; 23344d3c95f5SJorgen Lundman } 2335