diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c index dec70c60ce..fcc5f211f4 100644 --- a/cmd/zdb/zdb.c +++ b/cmd/zdb/zdb.c @@ -1117,7 +1117,7 @@ dump_zap(objset_t *os, uint64_t object, void *data, size_t size) { (void) data, (void) size; zap_cursor_t zc; - zap_attribute_t attr; + zap_attribute_t *attrp = zap_attribute_long_alloc(); void *prop; unsigned i; @@ -1125,53 +1125,54 @@ dump_zap(objset_t *os, uint64_t object, void *data, size_t size) (void) printf("\n"); for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; + zap_cursor_retrieve(&zc, attrp) == 0; zap_cursor_advance(&zc)) { boolean_t key64 = !!(zap_getflags(zc.zc_zap) & ZAP_FLAG_UINT64_KEY); if (key64) (void) printf("\t\t0x%010lx = ", - *(uint64_t *)attr.za_name); + *(uint64_t *)attrp->za_name); else - (void) printf("\t\t%s = ", attr.za_name); + (void) printf("\t\t%s = ", attrp->za_name); - if (attr.za_num_integers == 0) { + if (attrp->za_num_integers == 0) { (void) printf("\n"); continue; } - prop = umem_zalloc(attr.za_num_integers * - attr.za_integer_length, UMEM_NOFAIL); + prop = umem_zalloc(attrp->za_num_integers * + attrp->za_integer_length, UMEM_NOFAIL); if (key64) (void) zap_lookup_uint64(os, object, - (const uint64_t *)attr.za_name, 1, - attr.za_integer_length, attr.za_num_integers, + (const uint64_t *)attrp->za_name, 1, + attrp->za_integer_length, attrp->za_num_integers, prop); else - (void) zap_lookup(os, object, attr.za_name, - attr.za_integer_length, attr.za_num_integers, + (void) zap_lookup(os, object, attrp->za_name, + attrp->za_integer_length, attrp->za_num_integers, prop); - if (attr.za_integer_length == 1 && !key64) { - if (strcmp(attr.za_name, + if (attrp->za_integer_length == 1 && !key64) { + if (strcmp(attrp->za_name, DSL_CRYPTO_KEY_MASTER_KEY) == 0 || - strcmp(attr.za_name, + strcmp(attrp->za_name, DSL_CRYPTO_KEY_HMAC_KEY) == 0 || - strcmp(attr.za_name, DSL_CRYPTO_KEY_IV) == 0 || - strcmp(attr.za_name, DSL_CRYPTO_KEY_MAC) == 0 || - strcmp(attr.za_name, DMU_POOL_CHECKSUM_SALT) == 0) { + strcmp(attrp->za_name, DSL_CRYPTO_KEY_IV) == 0 || + strcmp(attrp->za_name, DSL_CRYPTO_KEY_MAC) == 0 || + strcmp(attrp->za_name, + DMU_POOL_CHECKSUM_SALT) == 0) { uint8_t *u8 = prop; - for (i = 0; i < attr.za_num_integers; i++) { + for (i = 0; i < attrp->za_num_integers; i++) { (void) printf("%02x", u8[i]); } } else { (void) printf("%s", (char *)prop); } } else { - for (i = 0; i < attr.za_num_integers; i++) { - switch (attr.za_integer_length) { + for (i = 0; i < attrp->za_num_integers; i++) { + switch (attrp->za_integer_length) { case 1: (void) printf("%u ", ((uint8_t *)prop)[i]); @@ -1192,9 +1193,11 @@ dump_zap(objset_t *os, uint64_t object, void *data, size_t size) } } (void) printf("\n"); - umem_free(prop, attr.za_num_integers * attr.za_integer_length); + umem_free(prop, + attrp->za_num_integers * attrp->za_integer_length); } zap_cursor_fini(&zc); + zap_attribute_free(attrp); } static void @@ -1295,26 +1298,27 @@ dump_sa_attrs(objset_t *os, uint64_t object, void *data, size_t size) { (void) data, (void) size; zap_cursor_t zc; - zap_attribute_t attr; + zap_attribute_t *attrp = zap_attribute_alloc(); dump_zap_stats(os, object); (void) printf("\n"); for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; + zap_cursor_retrieve(&zc, attrp) == 0; zap_cursor_advance(&zc)) { - (void) printf("\t\t%s = ", attr.za_name); - if (attr.za_num_integers == 0) { + (void) printf("\t\t%s = ", attrp->za_name); + if (attrp->za_num_integers == 0) { (void) printf("\n"); continue; } (void) printf(" %llx : [%d:%d:%d]\n", - (u_longlong_t)attr.za_first_integer, - (int)ATTR_LENGTH(attr.za_first_integer), - (int)ATTR_BSWAP(attr.za_first_integer), - (int)ATTR_NUM(attr.za_first_integer)); + (u_longlong_t)attrp->za_first_integer, + (int)ATTR_LENGTH(attrp->za_first_integer), + (int)ATTR_BSWAP(attrp->za_first_integer), + (int)ATTR_NUM(attrp->za_first_integer)); } zap_cursor_fini(&zc); + zap_attribute_free(attrp); } static void @@ -1322,7 +1326,7 @@ dump_sa_layouts(objset_t *os, uint64_t object, void *data, size_t size) { (void) data, (void) size; zap_cursor_t zc; - zap_attribute_t attr; + zap_attribute_t *attrp = zap_attribute_alloc(); uint16_t *layout_attrs; unsigned i; @@ -1330,29 +1334,30 @@ dump_sa_layouts(objset_t *os, uint64_t object, void *data, size_t size) (void) printf("\n"); for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; + zap_cursor_retrieve(&zc, attrp) == 0; zap_cursor_advance(&zc)) { - (void) printf("\t\t%s = [", attr.za_name); - if (attr.za_num_integers == 0) { + (void) printf("\t\t%s = [", attrp->za_name); + if (attrp->za_num_integers == 0) { (void) printf("\n"); continue; } - VERIFY(attr.za_integer_length == 2); - layout_attrs = umem_zalloc(attr.za_num_integers * - attr.za_integer_length, UMEM_NOFAIL); + VERIFY(attrp->za_integer_length == 2); + layout_attrs = umem_zalloc(attrp->za_num_integers * + attrp->za_integer_length, UMEM_NOFAIL); - VERIFY(zap_lookup(os, object, attr.za_name, - attr.za_integer_length, - attr.za_num_integers, layout_attrs) == 0); + VERIFY(zap_lookup(os, object, attrp->za_name, + attrp->za_integer_length, + attrp->za_num_integers, layout_attrs) == 0); - for (i = 0; i != attr.za_num_integers; i++) + for (i = 0; i != attrp->za_num_integers; i++) (void) printf(" %d ", (int)layout_attrs[i]); (void) printf("]\n"); umem_free(layout_attrs, - attr.za_num_integers * attr.za_integer_length); + attrp->za_num_integers * attrp->za_integer_length); } zap_cursor_fini(&zc); + zap_attribute_free(attrp); } static void @@ -1360,7 +1365,7 @@ dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size) { (void) data, (void) size; zap_cursor_t zc; - zap_attribute_t attr; + zap_attribute_t *attrp = zap_attribute_long_alloc(); const char *typenames[] = { /* 0 */ "not specified", /* 1 */ "FIFO", @@ -1384,13 +1389,14 @@ dump_zpldir(objset_t *os, uint64_t object, void *data, size_t size) (void) printf("\n"); for (zap_cursor_init(&zc, os, object); - zap_cursor_retrieve(&zc, &attr) == 0; + zap_cursor_retrieve(&zc, attrp) == 0; zap_cursor_advance(&zc)) { (void) printf("\t\t%s = %lld (type: %s)\n", - attr.za_name, ZFS_DIRENT_OBJ(attr.za_first_integer), - typenames[ZFS_DIRENT_TYPE(attr.za_first_integer)]); + attrp->za_name, ZFS_DIRENT_OBJ(attrp->za_first_integer), + typenames[ZFS_DIRENT_TYPE(attrp->za_first_integer)]); } zap_cursor_fini(&zc); + zap_attribute_free(attrp); } static int @@ -2100,23 +2106,24 @@ dump_brt(spa_t *spa) continue; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, brt->brt_mos, brtvd->bv_mos_entries); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { uint64_t refcnt; VERIFY0(zap_lookup_uint64(brt->brt_mos, brtvd->bv_mos_entries, - (const uint64_t *)za.za_name, 1, - za.za_integer_length, za.za_num_integers, &refcnt)); + (const uint64_t *)za->za_name, 1, + za->za_integer_length, za->za_num_integers, &refcnt)); - uint64_t offset = *(const uint64_t *)za.za_name; + uint64_t offset = *(const uint64_t *)za->za_name; snprintf(dva, sizeof (dva), "%" PRIu64 ":%llx", vdevid, (u_longlong_t)offset); printf("%-16s %-10llu\n", dva, (u_longlong_t)refcnt); } zap_cursor_fini(&zc); + zap_attribute_free(za); } } @@ -2898,28 +2905,30 @@ static void dump_bookmarks(objset_t *os, int verbosity) { zap_cursor_t zc; - zap_attribute_t attr; + zap_attribute_t *attrp; dsl_dataset_t *ds = dmu_objset_ds(os); dsl_pool_t *dp = spa_get_dsl(os->os_spa); objset_t *mos = os->os_spa->spa_meta_objset; if (verbosity < 4) return; + attrp = zap_attribute_alloc(); dsl_pool_config_enter(dp, FTAG); for (zap_cursor_init(&zc, mos, ds->ds_bookmarks_obj); - zap_cursor_retrieve(&zc, &attr) == 0; + zap_cursor_retrieve(&zc, attrp) == 0; zap_cursor_advance(&zc)) { char osname[ZFS_MAX_DATASET_NAME_LEN]; char buf[ZFS_MAX_DATASET_NAME_LEN]; int len; dmu_objset_name(os, osname); len = snprintf(buf, sizeof (buf), "%s#%s", osname, - attr.za_name); + attrp->za_name); VERIFY3S(len, <, ZFS_MAX_DATASET_NAME_LEN); (void) dump_bookmark(dp, buf, verbosity >= 5, verbosity >= 6); } zap_cursor_fini(&zc); dsl_pool_config_exit(dp, FTAG); + zap_attribute_free(attrp); } static void @@ -6710,18 +6719,19 @@ iterate_deleted_livelists(spa_t *spa, ll_iter_t func, void *arg) ASSERT0(err); zap_cursor_t zc; - zap_attribute_t attr; + zap_attribute_t *attrp = zap_attribute_alloc(); dsl_deadlist_t ll; /* NULL out os prior to dsl_deadlist_open in case it's garbage */ ll.dl_os = NULL; for (zap_cursor_init(&zc, mos, zap_obj); - zap_cursor_retrieve(&zc, &attr) == 0; + zap_cursor_retrieve(&zc, attrp) == 0; (void) zap_cursor_advance(&zc)) { - dsl_deadlist_open(&ll, mos, attr.za_first_integer); + dsl_deadlist_open(&ll, mos, attrp->za_first_integer); func(&ll, arg); dsl_deadlist_close(&ll); } zap_cursor_fini(&zc); + zap_attribute_free(attrp); } static int @@ -7936,13 +7946,14 @@ static void errorlog_count_refd(objset_t *mos, uint64_t errlog) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, errlog); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { - mos_obj_refd(za.za_first_integer); + mos_obj_refd(za->za_first_integer); } zap_cursor_fini(&zc); + zap_attribute_free(za); } static int diff --git a/cmd/zhack.c b/cmd/zhack.c index f15a6ece53..f297afb65d 100644 --- a/cmd/zhack.c +++ b/cmd/zhack.c @@ -203,26 +203,27 @@ static void dump_obj(objset_t *os, uint64_t obj, const char *name) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_long_alloc(); (void) printf("%s_obj:\n", name); for (zap_cursor_init(&zc, os, obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { - if (za.za_integer_length == 8) { - ASSERT(za.za_num_integers == 1); + if (za->za_integer_length == 8) { + ASSERT(za->za_num_integers == 1); (void) printf("\t%s = %llu\n", - za.za_name, (u_longlong_t)za.za_first_integer); + za->za_name, (u_longlong_t)za->za_first_integer); } else { - ASSERT(za.za_integer_length == 1); + ASSERT(za->za_integer_length == 1); char val[1024]; - VERIFY(zap_lookup(os, obj, za.za_name, + VERIFY(zap_lookup(os, obj, za->za_name, 1, sizeof (val), val) == 0); - (void) printf("\t%s = %s\n", za.za_name, val); + (void) printf("\t%s = %s\n", za->za_name, val); } } zap_cursor_fini(&zc); + zap_attribute_free(za); } static void diff --git a/include/os/linux/zfs/sys/zfs_vfsops_os.h b/include/os/linux/zfs/sys/zfs_vfsops_os.h index b4d5db21f5..e742e8dc39 100644 --- a/include/os/linux/zfs/sys/zfs_vfsops_os.h +++ b/include/os/linux/zfs/sys/zfs_vfsops_os.h @@ -118,6 +118,7 @@ struct zfsvfs { boolean_t z_xattr_sa; /* allow xattrs to be stores as SA */ boolean_t z_draining; /* is true when drain is active */ boolean_t z_drain_cancel; /* signal the unlinked drain to stop */ + boolean_t z_longname; /* Dataset supports long names */ uint64_t z_version; /* ZPL version */ uint64_t z_shares_dir; /* hidden shares dir */ dataset_kstats_t z_kstat; /* fs kstats */ diff --git a/include/os/linux/zfs/sys/zfs_vnops_os.h b/include/os/linux/zfs/sys/zfs_vnops_os.h index 830c76e574..7298e9d3a1 100644 --- a/include/os/linux/zfs/sys/zfs_vnops_os.h +++ b/include/os/linux/zfs/sys/zfs_vnops_os.h @@ -44,6 +44,7 @@ extern int zfs_write_simple(znode_t *zp, const void *data, size_t len, loff_t pos, size_t *resid); extern int zfs_lookup(znode_t *dzp, char *nm, znode_t **zpp, int flags, cred_t *cr, int *direntflags, pathname_t *realpnp); +extern int zfs_get_name(znode_t *dzp, char *name, znode_t *zp); extern int zfs_create(znode_t *dzp, char *name, vattr_t *vap, int excl, int mode, znode_t **zpp, cred_t *cr, int flag, vsecattr_t *vsecp, zidmap_t *mnt_ns); diff --git a/include/sys/fs/zfs.h b/include/sys/fs/zfs.h index c7e48d1edc..73c8f60ad4 100644 --- a/include/sys/fs/zfs.h +++ b/include/sys/fs/zfs.h @@ -81,6 +81,7 @@ typedef enum dmu_objset_type { * All of these include the terminating NUL byte. */ #define ZAP_MAXNAMELEN 256 +#define ZAP_MAXNAMELEN_NEW 1024 #define ZAP_MAXVALUELEN (1024 * 8) #define ZAP_OLDMAXVALUELEN 1024 #define ZFS_MAX_DATASET_NAME_LEN 256 @@ -193,6 +194,7 @@ typedef enum { ZFS_PROP_SNAPSHOTS_CHANGED, ZFS_PROP_PREFETCH, ZFS_PROP_VOLTHREADING, + ZFS_PROP_LONGNAME, ZFS_NUM_PROPS } zfs_prop_t; diff --git a/include/sys/zap.h b/include/sys/zap.h index 0027f7c510..43e8bbea19 100644 --- a/include/sys/zap.h +++ b/include/sys/zap.h @@ -315,7 +315,7 @@ int zap_count(objset_t *ds, uint64_t zapobj, uint64_t *count); * match must be exact (ie, same as mask=-1ULL). */ int zap_value_search(objset_t *os, uint64_t zapobj, - uint64_t value, uint64_t mask, char *name); + uint64_t value, uint64_t mask, char *name, uint64_t namelen); /* * Transfer all the entries from fromobj into intoobj. Only works on @@ -376,9 +376,20 @@ typedef struct { boolean_t za_normalization_conflict; uint64_t za_num_integers; uint64_t za_first_integer; /* no sign extension for <8byte ints */ - char za_name[ZAP_MAXNAMELEN]; + uint32_t za_name_len; + char za_name[]; } zap_attribute_t; +void zap_init(void); +void zap_fini(void); + +/* + * Alloc and free zap_attribute_t. + */ +zap_attribute_t *zap_attribute_alloc(void); +zap_attribute_t *zap_attribute_long_alloc(void); +void zap_attribute_free(zap_attribute_t *attrp); + /* * The interface for listing all the attributes of a zapobj can be * thought of as cursor moving down a list of the attributes one by diff --git a/include/sys/zap_impl.h b/include/sys/zap_impl.h index 2959aa9b2c..0c72c6881f 100644 --- a/include/sys/zap_impl.h +++ b/include/sys/zap_impl.h @@ -191,7 +191,8 @@ typedef struct zap_name { uint64_t zn_hash; matchtype_t zn_matchtype; int zn_normflags; - char zn_normbuf[ZAP_MAXNAMELEN]; + int zn_normbuf_len; + char zn_normbuf[]; } zap_name_t; #define zap_f zap_u.zap_fat diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h index 525d40759f..d177472016 100644 --- a/include/sys/zfs_ioctl.h +++ b/include/sys/zfs_ioctl.h @@ -124,7 +124,7 @@ typedef enum drr_headertype { * default use of "zfs send" won't encounter the bug mentioned above. */ #define DMU_BACKUP_FEATURE_SWITCH_TO_LARGE_BLOCKS (1 << 27) -/* flag #28 is reserved for a Nutanix feature */ +#define DMU_BACKUP_FEATURE_LONGNAME (1 << 28) /* * flag #29 is the last unused bit. It is reserved to indicate a to-be-designed * extension to the stream format which will accomodate more feature flags. @@ -141,7 +141,7 @@ typedef enum drr_headertype { DMU_BACKUP_FEATURE_COMPRESSED | DMU_BACKUP_FEATURE_LARGE_DNODE | \ DMU_BACKUP_FEATURE_RAW | DMU_BACKUP_FEATURE_HOLDS | \ DMU_BACKUP_FEATURE_REDACTED | DMU_BACKUP_FEATURE_SWITCH_TO_LARGE_BLOCKS | \ - DMU_BACKUP_FEATURE_ZSTD) + DMU_BACKUP_FEATURE_ZSTD | DMU_BACKUP_FEATURE_LONGNAME) /* Are all features in the given flag word currently supported? */ #define DMU_STREAM_SUPPORTED(x) (!((x) & ~DMU_BACKUP_FEATURE_MASK)) diff --git a/include/zfeature_common.h b/include/zfeature_common.h index 2515ba3217..ebe3cf5e1e 100644 --- a/include/zfeature_common.h +++ b/include/zfeature_common.h @@ -82,6 +82,7 @@ typedef enum spa_feature { SPA_FEATURE_AVZ_V2, SPA_FEATURE_REDACTION_LIST_SPILL, SPA_FEATURE_RAIDZ_EXPANSION, + SPA_FEATURE_LONGNAME, SPA_FEATURES } spa_feature_t; diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi index 51c8dc9647..50201035e9 100644 --- a/lib/libzfs/libzfs.abi +++ b/lib/libzfs/libzfs.abi @@ -616,7 +616,7 @@ - + @@ -664,6 +664,10 @@ + + + + @@ -1871,7 +1875,8 @@ - + + @@ -3427,18 +3432,9 @@ - - - - - - - - - - - - + + + @@ -3485,11 +3481,6 @@ - - - - - @@ -3502,10 +3493,6 @@ - - - - @@ -3554,10 +3541,6 @@ - - - - @@ -3570,13 +3553,6 @@ - - - - - - - @@ -4383,6 +4359,10 @@ + + + + @@ -4422,6 +4402,13 @@ + + + + + + + @@ -4952,6 +4939,11 @@ + + + + + @@ -6006,7 +5998,8 @@ - + + @@ -8054,10 +8047,6 @@ - - - - @@ -9131,8 +9120,8 @@ - - + + @@ -9209,7 +9198,7 @@ - + diff --git a/module/os/freebsd/zfs/zfs_dir.c b/module/os/freebsd/zfs/zfs_dir.c index 00d499c8c6..1ac0ab1ed3 100644 --- a/module/os/freebsd/zfs/zfs_dir.c +++ b/module/os/freebsd/zfs/zfs_dir.c @@ -287,7 +287,7 @@ void zfs_unlinked_drain(zfsvfs_t *zfsvfs) { zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap; dmu_object_info_t doi; znode_t *zp; dmu_tx_t *tx; @@ -296,8 +296,9 @@ zfs_unlinked_drain(zfsvfs_t *zfsvfs) /* * Iterate over the contents of the unlinked set. */ + zap = zap_attribute_alloc(); for (zap_cursor_init(&zc, zfsvfs->z_os, zfsvfs->z_unlinkedobj); - zap_cursor_retrieve(&zc, &zap) == 0; + zap_cursor_retrieve(&zc, zap) == 0; zap_cursor_advance(&zc)) { /* @@ -305,7 +306,7 @@ zfs_unlinked_drain(zfsvfs_t *zfsvfs) */ error = dmu_object_info(zfsvfs->z_os, - zap.za_first_integer, &doi); + zap->za_first_integer, &doi); if (error != 0) continue; @@ -315,7 +316,7 @@ zfs_unlinked_drain(zfsvfs_t *zfsvfs) * We need to re-mark these list entries for deletion, * so we pull them back into core and set zp->z_unlinked. */ - error = zfs_zget(zfsvfs, zap.za_first_integer, &zp); + error = zfs_zget(zfsvfs, zap->za_first_integer, &zp); /* * We may pick up znodes that are already marked for deletion. @@ -351,6 +352,7 @@ zfs_unlinked_drain(zfsvfs_t *zfsvfs) vput(ZTOV(zp)); } zap_cursor_fini(&zc); + zap_attribute_free(zap); } /* @@ -368,18 +370,19 @@ static int zfs_purgedir(znode_t *dzp) { zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap; znode_t *xzp; dmu_tx_t *tx; zfsvfs_t *zfsvfs = dzp->z_zfsvfs; int skipped = 0; int error; + zap = zap_attribute_alloc(); for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); - (error = zap_cursor_retrieve(&zc, &zap)) == 0; + (error = zap_cursor_retrieve(&zc, zap)) == 0; zap_cursor_advance(&zc)) { error = zfs_zget(zfsvfs, - ZFS_DIRENT_OBJ(zap.za_first_integer), &xzp); + ZFS_DIRENT_OBJ(zap->za_first_integer), &xzp); if (error) { skipped += 1; continue; @@ -391,7 +394,7 @@ zfs_purgedir(znode_t *dzp) tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); - dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap.za_name); + dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap->za_name); dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); /* Is this really needed ? */ @@ -405,7 +408,7 @@ zfs_purgedir(znode_t *dzp) continue; } - error = zfs_link_destroy(dzp, zap.za_name, xzp, tx, 0, NULL); + error = zfs_link_destroy(dzp, zap->za_name, xzp, tx, 0, NULL); if (error) skipped += 1; dmu_tx_commit(tx); @@ -413,6 +416,7 @@ zfs_purgedir(znode_t *dzp) vput(ZTOV(xzp)); } zap_cursor_fini(&zc); + zap_attribute_free(zap); if (error != ENOENT) skipped += 1; return (skipped); diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c index 01b964f98f..5addbfbc5d 100644 --- a/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/module/os/freebsd/zfs/zfs_vnops_os.c @@ -1568,7 +1568,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, caddr_t outbuf; size_t bufsize; zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap; uint_t bytes_wanted; uint64_t offset; /* must be unsigned; checks for < 1 */ uint64_t parent; @@ -1616,6 +1616,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, os = zfsvfs->z_os; offset = zfs_uio_offset(uio); prefetch = zp->z_zn_prefetch; + zap = zap_attribute_alloc(); /* * Initialize the iterator cursor. @@ -1671,33 +1672,33 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, * Special case `.', `..', and `.zfs'. */ if (offset == 0) { - (void) strcpy(zap.za_name, "."); - zap.za_normalization_conflict = 0; + (void) strcpy(zap->za_name, "."); + zap->za_normalization_conflict = 0; objnum = zp->z_id; type = DT_DIR; } else if (offset == 1) { - (void) strcpy(zap.za_name, ".."); - zap.za_normalization_conflict = 0; + (void) strcpy(zap->za_name, ".."); + zap->za_normalization_conflict = 0; objnum = parent; type = DT_DIR; } else if (offset == 2 && zfs_show_ctldir(zp)) { - (void) strcpy(zap.za_name, ZFS_CTLDIR_NAME); - zap.za_normalization_conflict = 0; + (void) strcpy(zap->za_name, ZFS_CTLDIR_NAME); + zap->za_normalization_conflict = 0; objnum = ZFSCTL_INO_ROOT; type = DT_DIR; } else { /* * Grab next entry. */ - if ((error = zap_cursor_retrieve(&zc, &zap))) { + if ((error = zap_cursor_retrieve(&zc, zap))) { if ((*eofp = (error == ENOENT)) != 0) break; else goto update; } - if (zap.za_integer_length != 8 || - zap.za_num_integers != 1) { + if (zap->za_integer_length != 8 || + zap->za_num_integers != 1) { cmn_err(CE_WARN, "zap_readdir: bad directory " "entry, obj = %lld, offset = %lld\n", (u_longlong_t)zp->z_id, @@ -1706,15 +1707,15 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, goto update; } - objnum = ZFS_DIRENT_OBJ(zap.za_first_integer); + objnum = ZFS_DIRENT_OBJ(zap->za_first_integer); /* * MacOS X can extract the object type here such as: * uint8_t type = ZFS_DIRENT_TYPE(zap.za_first_integer); */ - type = ZFS_DIRENT_TYPE(zap.za_first_integer); + type = ZFS_DIRENT_TYPE(zap->za_first_integer); } - reclen = DIRENT64_RECLEN(strlen(zap.za_name)); + reclen = DIRENT64_RECLEN(strlen(zap->za_name)); /* * Will this entry fit in the buffer? @@ -1734,10 +1735,10 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, */ odp->d_ino = objnum; odp->d_reclen = reclen; - odp->d_namlen = strlen(zap.za_name); + odp->d_namlen = strlen(zap->za_name); /* NOTE: d_off is the offset for the *next* entry. */ next = &odp->d_off; - strlcpy(odp->d_name, zap.za_name, odp->d_namlen + 1); + strlcpy(odp->d_name, zap->za_name, odp->d_namlen + 1); odp->d_type = type; dirent_terminate(odp); odp = (dirent64_t *)((intptr_t)odp + reclen); @@ -1788,6 +1789,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp, update: zap_cursor_fini(&zc); + zap_attribute_free(zap); if (zfs_uio_segflg(uio) != UIO_SYSSPACE || zfs_uio_iovcnt(uio) != 1) kmem_free(outbuf, bufsize); diff --git a/module/os/freebsd/zfs/zfs_znode.c b/module/os/freebsd/zfs/zfs_znode.c index e5c50874e1..c48dacc77f 100644 --- a/module/os/freebsd/zfs/zfs_znode.c +++ b/module/os/freebsd/zfs/zfs_znode.c @@ -1967,7 +1967,7 @@ zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl, (void) sprintf(component + 1, ""); } else { error = zap_value_search(osp, pobj, obj, - ZFS_DIRENT_OBJ(-1ULL), component + 1); + ZFS_DIRENT_OBJ(-1ULL), component + 1, MAXNAMELEN); if (error != 0) break; } @@ -2180,7 +2180,7 @@ zfs_znode_parent_and_name(znode_t *zp, znode_t **dzpp, char *buf) return (SET_ERROR(EINVAL)); err = zap_value_search(zfsvfs->z_os, parent, zp->z_id, - ZFS_DIRENT_OBJ(-1ULL), buf); + ZFS_DIRENT_OBJ(-1ULL), buf, MAXNAMELEN); if (err != 0) return (err); err = zfs_zget(zfsvfs, parent, dzpp); diff --git a/module/os/linux/zfs/zfs_dir.c b/module/os/linux/zfs/zfs_dir.c index 1eeabe53d2..dc03aab2ef 100644 --- a/module/os/linux/zfs/zfs_dir.c +++ b/module/os/linux/zfs/zfs_dir.c @@ -476,7 +476,7 @@ zfs_unlinked_drain_task(void *arg) { zfsvfs_t *zfsvfs = arg; zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap = zap_attribute_alloc(); dmu_object_info_t doi; znode_t *zp; int error; @@ -487,7 +487,7 @@ zfs_unlinked_drain_task(void *arg) * Iterate over the contents of the unlinked set. */ for (zap_cursor_init(&zc, zfsvfs->z_os, zfsvfs->z_unlinkedobj); - zap_cursor_retrieve(&zc, &zap) == 0 && !zfsvfs->z_drain_cancel; + zap_cursor_retrieve(&zc, zap) == 0 && !zfsvfs->z_drain_cancel; zap_cursor_advance(&zc)) { /* @@ -495,7 +495,7 @@ zfs_unlinked_drain_task(void *arg) */ error = dmu_object_info(zfsvfs->z_os, - zap.za_first_integer, &doi); + zap->za_first_integer, &doi); if (error != 0) continue; @@ -505,7 +505,7 @@ zfs_unlinked_drain_task(void *arg) * We need to re-mark these list entries for deletion, * so we pull them back into core and set zp->z_unlinked. */ - error = zfs_zget(zfsvfs, zap.za_first_integer, &zp); + error = zfs_zget(zfsvfs, zap->za_first_integer, &zp); /* * We may pick up znodes that are already marked for deletion. @@ -532,6 +532,7 @@ zfs_unlinked_drain_task(void *arg) zfsvfs->z_draining = B_FALSE; zfsvfs->z_drain_task = TASKQID_INVALID; + zap_attribute_free(zap); } /* @@ -589,7 +590,7 @@ static int zfs_purgedir(znode_t *dzp) { zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap = zap_attribute_alloc(); znode_t *xzp; dmu_tx_t *tx; zfsvfs_t *zfsvfs = ZTOZSB(dzp); @@ -598,10 +599,10 @@ zfs_purgedir(znode_t *dzp) int error; for (zap_cursor_init(&zc, zfsvfs->z_os, dzp->z_id); - (error = zap_cursor_retrieve(&zc, &zap)) == 0; + (error = zap_cursor_retrieve(&zc, zap)) == 0; zap_cursor_advance(&zc)) { error = zfs_zget(zfsvfs, - ZFS_DIRENT_OBJ(zap.za_first_integer), &xzp); + ZFS_DIRENT_OBJ(zap->za_first_integer), &xzp); if (error) { skipped += 1; continue; @@ -612,7 +613,7 @@ zfs_purgedir(znode_t *dzp) tx = dmu_tx_create(zfsvfs->z_os); dmu_tx_hold_sa(tx, dzp->z_sa_hdl, B_FALSE); - dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap.za_name); + dmu_tx_hold_zap(tx, dzp->z_id, FALSE, zap->za_name); dmu_tx_hold_sa(tx, xzp->z_sa_hdl, B_FALSE); dmu_tx_hold_zap(tx, zfsvfs->z_unlinkedobj, FALSE, NULL); /* Is this really needed ? */ @@ -627,7 +628,7 @@ zfs_purgedir(znode_t *dzp) } memset(&dl, 0, sizeof (dl)); dl.dl_dzp = dzp; - dl.dl_name = zap.za_name; + dl.dl_name = zap->za_name; error = zfs_link_destroy(&dl, xzp, tx, 0, NULL); if (error) @@ -637,6 +638,7 @@ zfs_purgedir(znode_t *dzp) zfs_zrele_async(xzp); } zap_cursor_fini(&zc); + zap_attribute_free(zap); if (error != ENOENT) skipped += 1; return (skipped); @@ -800,6 +802,7 @@ zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag) { znode_t *dzp = dl->dl_dzp; zfsvfs_t *zfsvfs = ZTOZSB(zp); + dsl_dataset_t *ds = dmu_objset_ds(zfsvfs->z_os); uint64_t value; int zp_is_dir = S_ISDIR(ZTOI(zp)->i_mode); sa_bulk_attr_t bulk[5]; @@ -845,6 +848,14 @@ zfs_link_create(zfs_dirlock_t *dl, znode_t *zp, dmu_tx_t *tx, int flag) return (error); } + /* + * If we added a longname activate the SPA_FEATURE_LONGNAME. + */ + if (strlen(dl->dl_name) >= ZAP_MAXNAMELEN) { + ds->ds_feature_activation[SPA_FEATURE_LONGNAME] = + (void *)B_TRUE; + } + SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_PARENT(zfsvfs), NULL, &dzp->z_id, sizeof (dzp->z_id)); SA_ADD_BULK_ATTR(bulk, count, SA_ZPL_FLAGS(zfsvfs), NULL, diff --git a/module/os/linux/zfs/zfs_vfsops.c b/module/os/linux/zfs/zfs_vfsops.c index a52f08868d..0db7a6666d 100644 --- a/module/os/linux/zfs/zfs_vfsops.c +++ b/module/os/linux/zfs/zfs_vfsops.c @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include "zfs_comutil.h" @@ -448,6 +449,12 @@ acl_inherit_changed_cb(void *arg, uint64_t newval) ((zfsvfs_t *)arg)->z_acl_inherit = newval; } +static void +longname_changed_cb(void *arg, uint64_t newval) +{ + ((zfsvfs_t *)arg)->z_longname = newval; +} + static int zfs_register_callbacks(vfs_t *vfsp) { @@ -508,6 +515,8 @@ zfs_register_callbacks(vfs_t *vfsp) zfsvfs); error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_NBMAND), nbmand_changed_cb, zfsvfs); + error = error ? error : dsl_prop_register(ds, + zfs_prop_to_name(ZFS_PROP_LONGNAME), longname_changed_cb, zfsvfs); dsl_pool_config_exit(dmu_objset_pool(os), FTAG); if (error) goto unregister; @@ -1139,7 +1148,8 @@ zfs_statvfs(struct inode *ip, struct kstatfs *statp) statp->f_fsid.val[0] = (uint32_t)fsid; statp->f_fsid.val[1] = (uint32_t)(fsid >> 32); statp->f_type = ZFS_SUPER_MAGIC; - statp->f_namelen = MAXNAMELEN - 1; + statp->f_namelen = + zfsvfs->z_longname ? (ZAP_MAXNAMELEN_NEW - 1) : (MAXNAMELEN - 1); /* * We have all of 40 characters to stuff a string here. diff --git a/module/os/linux/zfs/zfs_vnops_os.c b/module/os/linux/zfs/zfs_vnops_os.c index 9803c7fecb..805ac93bff 100644 --- a/module/os/linux/zfs/zfs_vnops_os.c +++ b/module/os/linux/zfs/zfs_vnops_os.c @@ -532,6 +532,46 @@ zfs_lookup(znode_t *zdp, char *nm, znode_t **zpp, int flags, cred_t *cr, return (error); } +/* + * Perform a linear search in directory for the name of specific inode. + * Note we don't pass in the buffer size of name because it's hardcoded to + * NAME_MAX+1(256) in Linux. + * + * IN: dzp - znode of directory to search. + * zp - znode of the target + * + * OUT: name - dentry name of the target + * + * RETURN: 0 on success, error code on failure. + */ +int +zfs_get_name(znode_t *dzp, char *name, znode_t *zp) +{ + zfsvfs_t *zfsvfs = ZTOZSB(dzp); + int error = 0; + + if ((error = zfs_enter_verify_zp(zfsvfs, dzp, FTAG)) != 0) + return (error); + + if ((error = zfs_verify_zp(zp)) != 0) { + zfs_exit(zfsvfs, FTAG); + return (error); + } + + /* ctldir should have got their name in zfs_vget */ + if (dzp->z_is_ctldir || zp->z_is_ctldir) { + zfs_exit(zfsvfs, FTAG); + return (ENOENT); + } + + /* buffer len is hardcoded to 256 in Linux kernel */ + error = zap_value_search(zfsvfs->z_os, dzp->z_id, zp->z_id, + ZFS_DIRENT_OBJ(-1ULL), name, ZAP_MAXNAMELEN); + + zfs_exit(zfsvfs, FTAG); + return (error); +} + /* * Attempt to create a new entry in a directory. If the entry * already exists, truncate the file if permissible, else return @@ -1513,7 +1553,7 @@ zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr) zfsvfs_t *zfsvfs = ITOZSB(ip); objset_t *os; zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap; int error; uint8_t prefetch; uint8_t type; @@ -1538,6 +1578,7 @@ zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr) os = zfsvfs->z_os; offset = ctx->pos; prefetch = zp->z_zn_prefetch; + zap = zap_attribute_long_alloc(); /* * Initialize the iterator cursor. @@ -1563,25 +1604,25 @@ zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr) * Special case `.', `..', and `.zfs'. */ if (offset == 0) { - (void) strcpy(zap.za_name, "."); - zap.za_normalization_conflict = 0; + (void) strcpy(zap->za_name, "."); + zap->za_normalization_conflict = 0; objnum = zp->z_id; type = DT_DIR; } else if (offset == 1) { - (void) strcpy(zap.za_name, ".."); - zap.za_normalization_conflict = 0; + (void) strcpy(zap->za_name, ".."); + zap->za_normalization_conflict = 0; objnum = parent; type = DT_DIR; } else if (offset == 2 && zfs_show_ctldir(zp)) { - (void) strcpy(zap.za_name, ZFS_CTLDIR_NAME); - zap.za_normalization_conflict = 0; + (void) strcpy(zap->za_name, ZFS_CTLDIR_NAME); + zap->za_normalization_conflict = 0; objnum = ZFSCTL_INO_ROOT; type = DT_DIR; } else { /* * Grab next entry. */ - if ((error = zap_cursor_retrieve(&zc, &zap))) { + if ((error = zap_cursor_retrieve(&zc, zap))) { if (error == ENOENT) break; else @@ -1595,24 +1636,24 @@ zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr) * * XXX: This should be a feature flag for compatibility */ - if (zap.za_integer_length != 8 || - zap.za_num_integers == 0) { + if (zap->za_integer_length != 8 || + zap->za_num_integers == 0) { cmn_err(CE_WARN, "zap_readdir: bad directory " "entry, obj = %lld, offset = %lld, " "length = %d, num = %lld\n", (u_longlong_t)zp->z_id, (u_longlong_t)offset, - zap.za_integer_length, - (u_longlong_t)zap.za_num_integers); + zap->za_integer_length, + (u_longlong_t)zap->za_num_integers); error = SET_ERROR(ENXIO); goto update; } - objnum = ZFS_DIRENT_OBJ(zap.za_first_integer); - type = ZFS_DIRENT_TYPE(zap.za_first_integer); + objnum = ZFS_DIRENT_OBJ(zap->za_first_integer); + type = ZFS_DIRENT_TYPE(zap->za_first_integer); } - done = !zpl_dir_emit(ctx, zap.za_name, strlen(zap.za_name), + done = !zpl_dir_emit(ctx, zap->za_name, strlen(zap->za_name), objnum, type); if (done) break; @@ -1635,6 +1676,7 @@ zfs_readdir(struct inode *ip, zpl_dir_context_t *ctx, cred_t *cr) update: zap_cursor_fini(&zc); + zap_attribute_free(zap); if (error == ENOENT) error = 0; out: @@ -1733,7 +1775,7 @@ zfs_setattr_dir(znode_t *dzp) zfsvfs_t *zfsvfs = ZTOZSB(dzp); objset_t *os = zfsvfs->z_os; zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap; zfs_dirlock_t *dl; znode_t *zp = NULL; dmu_tx_t *tx = NULL; @@ -1742,15 +1784,16 @@ zfs_setattr_dir(znode_t *dzp) int count; int err; + zap = zap_attribute_alloc(); zap_cursor_init(&zc, os, dzp->z_id); - while ((err = zap_cursor_retrieve(&zc, &zap)) == 0) { + while ((err = zap_cursor_retrieve(&zc, zap)) == 0) { count = 0; - if (zap.za_integer_length != 8 || zap.za_num_integers != 1) { + if (zap->za_integer_length != 8 || zap->za_num_integers != 1) { err = ENXIO; break; } - err = zfs_dirent_lock(&dl, dzp, (char *)zap.za_name, &zp, + err = zfs_dirent_lock(&dl, dzp, (char *)zap->za_name, &zp, ZEXISTS, NULL, NULL); if (err == ENOENT) goto next; @@ -1842,6 +1885,7 @@ next: zfs_dirent_unlock(dl); } zap_cursor_fini(&zc); + zap_attribute_free(zap); return (err == ENOENT ? 0 : err); } diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c index 265153e011..bb672bfa73 100644 --- a/module/os/linux/zfs/zfs_znode.c +++ b/module/os/linux/zfs/zfs_znode.c @@ -2145,6 +2145,7 @@ zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl, dmu_buf_t *prevdb = NULL; dmu_buf_t *sa_db = NULL; char *path = buf + len - 1; + char *comp_buf; int error; *path = '\0'; @@ -2160,9 +2161,10 @@ zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl, return (error); } + comp_buf = kmem_alloc(ZAP_MAXNAMELEN_NEW + 2, KM_SLEEP); for (;;) { uint64_t pobj = 0; - char component[MAXNAMELEN + 2]; + char *component = comp_buf; size_t complen; int is_xattrdir = 0; @@ -2186,7 +2188,8 @@ zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl, strcpy(component + 1, ""); } else { error = zap_value_search(osp, pobj, obj, - ZFS_DIRENT_OBJ(-1ULL), component + 1); + ZFS_DIRENT_OBJ(-1ULL), component + 1, + ZAP_MAXNAMELEN_NEW); if (error != 0) break; } @@ -2217,6 +2220,7 @@ zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl, if (error == 0) (void) memmove(buf, path, buf + len - path); + kmem_free(comp_buf, ZAP_MAXNAMELEN_NEW +2); return (error); } diff --git a/module/os/linux/zfs/zpl_export.c b/module/os/linux/zfs/zpl_export.c index aa80b72e2d..e9ca7b1349 100644 --- a/module/os/linux/zfs/zpl_export.c +++ b/module/os/linux/zfs/zpl_export.c @@ -24,6 +24,7 @@ */ +#include #include #include #include @@ -109,6 +110,35 @@ zpl_fh_to_dentry(struct super_block *sb, struct fid *fh, return (d_obtain_alias(ip)); } +/* + * In case the filesystem contains name longer than 255, we need to override + * the default get_name so we don't get buffer overflow. Unfortunately, since + * the buffer size is hardcoded in Linux, we will get ESTALE error in this + * case. + */ +static int +zpl_get_name(struct dentry *parent, char *name, struct dentry *child) +{ + cred_t *cr = CRED(); + fstrans_cookie_t cookie; + struct inode *dir = parent->d_inode; + struct inode *ip = child->d_inode; + int error; + + if (!dir || !S_ISDIR(dir->i_mode)) + return (-ENOTDIR); + + crhold(cr); + cookie = spl_fstrans_mark(); + spl_inode_lock_shared(dir); + error = -zfs_get_name(ITOZ(dir), name, ITOZ(ip)); + spl_inode_unlock_shared(dir); + spl_fstrans_unmark(cookie); + crfree(cr); + + return (error); +} + static struct dentry * zpl_get_parent(struct dentry *child) { @@ -153,6 +183,7 @@ zpl_commit_metadata(struct inode *inode) const struct export_operations zpl_export_operations = { .encode_fh = zpl_encode_fh, .fh_to_dentry = zpl_fh_to_dentry, + .get_name = zpl_get_name, .get_parent = zpl_get_parent, .commit_metadata = zpl_commit_metadata, }; diff --git a/module/os/linux/zfs/zpl_inode.c b/module/os/linux/zfs/zpl_inode.c index ad1753f7a0..6d737d9fc6 100644 --- a/module/os/linux/zfs/zpl_inode.c +++ b/module/os/linux/zfs/zpl_inode.c @@ -46,9 +46,29 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) pathname_t pn; int zfs_flags = 0; zfsvfs_t *zfsvfs = dentry->d_sb->s_fs_info; + dsl_dataset_t *ds = dmu_objset_ds(zfsvfs->z_os); + size_t dlen = dlen(dentry); - if (dlen(dentry) >= ZAP_MAXNAMELEN) + /* + * If z_longname is disabled, disallow create or rename of names + * longer than ZAP_MAXNAMELEN. + * + * This is needed in cases where longname was enabled first and some + * files/dirs with names > ZAP_MAXNAMELEN were created. And later + * longname was disabled. In such a case allow access to existing + * longnames. But disallow creation newer longnamed entities. + */ + if (!zfsvfs->z_longname && (dlen >= ZAP_MAXNAMELEN)) { + /* + * If this is for create or rename fail it. + */ + if (!dsl_dataset_feature_is_active(ds, SPA_FEATURE_LONGNAME) || + (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))) + return (ERR_PTR(-ENAMETOOLONG)); + } + if (dlen >= ZAP_MAXNAMELEN_NEW) { return (ERR_PTR(-ENAMETOOLONG)); + } crhold(cr); cookie = spl_fstrans_mark(); @@ -131,6 +151,16 @@ zpl_vap_init(vattr_t *vap, struct inode *dir, umode_t mode, cred_t *cr, } } +static inline bool +is_nametoolong(struct dentry *dentry) +{ + zfsvfs_t *zfsvfs = dentry->d_sb->s_fs_info; + size_t dlen = dlen(dentry); + + return ((!zfsvfs->z_longname && dlen >= ZAP_MAXNAMELEN) || + dlen >= ZAP_MAXNAMELEN_NEW); +} + static int #ifdef HAVE_IOPS_CREATE_USERNS zpl_create(struct user_namespace *user_ns, struct inode *dir, @@ -151,6 +181,10 @@ zpl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool flag) zidmap_t *user_ns = kcred->user_ns; #endif + if (is_nametoolong(dentry)) { + return (-ENAMETOOLONG); + } + crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(vap, dir, mode, cr, user_ns); @@ -201,6 +235,10 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, zidmap_t *user_ns = kcred->user_ns; #endif + if (is_nametoolong(dentry)) { + return (-ENAMETOOLONG); + } + /* * We currently expect Linux to supply rdev=0 for all sockets * and fifos, but we want to know if this behavior ever changes. @@ -355,6 +393,10 @@ zpl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) zidmap_t *user_ns = kcred->user_ns; #endif + if (is_nametoolong(dentry)) { + return (-ENAMETOOLONG); + } + crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(vap, dir, mode | S_IFDIR, cr, user_ns); @@ -570,6 +612,10 @@ zpl_rename2(struct inode *sdip, struct dentry *sdentry, zidmap_t *user_ns = kcred->user_ns; #endif + if (is_nametoolong(tdentry)) { + return (-ENAMETOOLONG); + } + crhold(cr); if (rflags & RENAME_WHITEOUT) { wo_vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); @@ -621,6 +667,10 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name) zidmap_t *user_ns = kcred->user_ns; #endif + if (is_nametoolong(dentry)) { + return (-ENAMETOOLONG); + } + crhold(cr); vap = kmem_zalloc(sizeof (vattr_t), KM_SLEEP); zpl_vap_init(vap, dir, S_IFLNK | S_IRWXUGO, cr, user_ns); @@ -773,6 +823,10 @@ zpl_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) int error; fstrans_cookie_t cookie; + if (is_nametoolong(dentry)) { + return (-ENAMETOOLONG); + } + if (ip->i_nlink >= ZFS_LINK_MAX) return (-EMLINK); diff --git a/module/os/linux/zfs/zpl_xattr.c b/module/os/linux/zfs/zpl_xattr.c index 4e4f5210f8..9744a5d0c7 100644 --- a/module/os/linux/zfs/zpl_xattr.c +++ b/module/os/linux/zfs/zpl_xattr.c @@ -153,19 +153,20 @@ static int zpl_xattr_readdir(struct inode *dxip, xattr_filldir_t *xf) { zap_cursor_t zc; - zap_attribute_t zap; + zap_attribute_t *zap = zap_attribute_alloc(); int error; zap_cursor_init(&zc, ITOZSB(dxip)->z_os, ITOZ(dxip)->z_id); - while ((error = -zap_cursor_retrieve(&zc, &zap)) == 0) { + while ((error = -zap_cursor_retrieve(&zc, zap)) == 0) { - if (zap.za_integer_length != 8 || zap.za_num_integers != 1) { + if (zap->za_integer_length != 8 || zap->za_num_integers != 1) { error = -ENXIO; break; } - error = zpl_xattr_filldir(xf, zap.za_name, strlen(zap.za_name)); + error = zpl_xattr_filldir(xf, zap->za_name, + strlen(zap->za_name)); if (error) break; @@ -173,6 +174,7 @@ zpl_xattr_readdir(struct inode *dxip, xattr_filldir_t *xf) } zap_cursor_fini(&zc); + zap_attribute_free(zap); if (error == -ENOENT) error = 0; diff --git a/module/zcommon/zfeature_common.c b/module/zcommon/zfeature_common.c index 309d9bf14c..d9c95b21e7 100644 --- a/module/zcommon/zfeature_common.c +++ b/module/zcommon/zfeature_common.c @@ -754,6 +754,19 @@ zpool_feature_init(void) "Support for raidz expansion", ZFEATURE_FLAG_MOS, ZFEATURE_TYPE_BOOLEAN, NULL, sfeatures); + { + static const spa_feature_t longname_deps[] = { + SPA_FEATURE_EXTENSIBLE_DATASET, + SPA_FEATURE_ENABLED_TXG, + SPA_FEATURE_NONE + }; + zfeature_register(SPA_FEATURE_LONGNAME, + "org.zfsonlinux:longname", "longname", + "support filename upto 1024 bytes", + ZFEATURE_FLAG_READONLY_COMPAT | ZFEATURE_FLAG_PER_DATASET, + ZFEATURE_TYPE_BOOLEAN, longname_deps, sfeatures); + } + zfs_mod_list_supported_free(sfeatures); } diff --git a/module/zcommon/zfs_prop.c b/module/zcommon/zfs_prop.c index 764993b45e..886414de61 100644 --- a/module/zcommon/zfs_prop.c +++ b/module/zcommon/zfs_prop.c @@ -760,6 +760,10 @@ zfs_prop_init(void) ZFS_TYPE_VOLUME, "", "SNAPSHOTS_CHANGED", B_FALSE, B_TRUE, B_TRUE, NULL, sfeatures); + zprop_register_index(ZFS_PROP_LONGNAME, "longname", 0, PROP_INHERIT, + ZFS_TYPE_FILESYSTEM, "on | off", "LONGNAME", boolean_table, + sfeatures); + zfs_mod_list_supported_free(sfeatures); } diff --git a/module/zfs/ddt_zap.c b/module/zfs/ddt_zap.c index 7ce7461a2b..51201d2b1d 100644 --- a/module/zfs/ddt_zap.c +++ b/module/zfs/ddt_zap.c @@ -184,9 +184,10 @@ ddt_zap_walk(objset_t *os, uint64_t object, uint64_t *walk, ddt_key_t *ddk, ddt_phys_t *phys, size_t psize) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; int error; + za = zap_attribute_alloc(); if (*walk == 0) { /* * We don't want to prefetch the entire ZAP object, because @@ -199,20 +200,20 @@ ddt_zap_walk(objset_t *os, uint64_t object, uint64_t *walk, ddt_key_t *ddk, } else { zap_cursor_init_serialized(&zc, os, object, *walk); } - if ((error = zap_cursor_retrieve(&zc, &za)) == 0) { - uint64_t csize = za.za_num_integers; + if ((error = zap_cursor_retrieve(&zc, za)) == 0) { + uint64_t csize = za->za_num_integers; - ASSERT3U(za.za_integer_length, ==, 1); + ASSERT3U(za->za_integer_length, ==, 1); ASSERT3U(csize, <=, psize + 1); uchar_t *cbuf = kmem_alloc(csize, KM_SLEEP); - error = zap_lookup_uint64(os, object, (uint64_t *)za.za_name, + error = zap_lookup_uint64(os, object, (uint64_t *)za->za_name, DDT_KEY_WORDS, 1, csize, cbuf); ASSERT0(error); if (error == 0) { ddt_zap_decompress(cbuf, phys, csize, psize); - *ddk = *(ddt_key_t *)za.za_name; + *ddk = *(ddt_key_t *)za->za_name; } kmem_free(cbuf, csize); @@ -221,6 +222,7 @@ ddt_zap_walk(objset_t *os, uint64_t object, uint64_t *walk, ddt_key_t *ddk, *walk = zap_cursor_serialize(&zc); } zap_cursor_fini(&zc); + zap_attribute_free(za); return (error); } diff --git a/module/zfs/dmu_objset.c b/module/zfs/dmu_objset.c index 8f4fefa4f4..c42dba401c 100644 --- a/module/zfs/dmu_objset.c +++ b/module/zfs/dmu_objset.c @@ -2615,35 +2615,39 @@ dmu_snapshot_list_next(objset_t *os, int namelen, char *name, { dsl_dataset_t *ds = os->os_dsl_dataset; zap_cursor_t cursor; - zap_attribute_t attr; + zap_attribute_t *attr; ASSERT(dsl_pool_config_held(dmu_objset_pool(os))); if (dsl_dataset_phys(ds)->ds_snapnames_zapobj == 0) return (SET_ERROR(ENOENT)); + attr = zap_attribute_alloc(); zap_cursor_init_serialized(&cursor, ds->ds_dir->dd_pool->dp_meta_objset, dsl_dataset_phys(ds)->ds_snapnames_zapobj, *offp); - if (zap_cursor_retrieve(&cursor, &attr) != 0) { + if (zap_cursor_retrieve(&cursor, attr) != 0) { zap_cursor_fini(&cursor); + zap_attribute_free(attr); return (SET_ERROR(ENOENT)); } - if (strlen(attr.za_name) + 1 > namelen) { + if (strlen(attr->za_name) + 1 > namelen) { zap_cursor_fini(&cursor); + zap_attribute_free(attr); return (SET_ERROR(ENAMETOOLONG)); } - (void) strlcpy(name, attr.za_name, namelen); + (void) strlcpy(name, attr->za_name, namelen); if (idp) - *idp = attr.za_first_integer; + *idp = attr->za_first_integer; if (case_conflict) - *case_conflict = attr.za_normalization_conflict; + *case_conflict = attr->za_normalization_conflict; zap_cursor_advance(&cursor); *offp = zap_cursor_serialize(&cursor); zap_cursor_fini(&cursor); + zap_attribute_free(attr); return (0); } @@ -2660,33 +2664,37 @@ dmu_dir_list_next(objset_t *os, int namelen, char *name, { dsl_dir_t *dd = os->os_dsl_dataset->ds_dir; zap_cursor_t cursor; - zap_attribute_t attr; + zap_attribute_t *attr; /* there is no next dir on a snapshot! */ if (os->os_dsl_dataset->ds_object != dsl_dir_phys(dd)->dd_head_dataset_obj) return (SET_ERROR(ENOENT)); + attr = zap_attribute_alloc(); zap_cursor_init_serialized(&cursor, dd->dd_pool->dp_meta_objset, dsl_dir_phys(dd)->dd_child_dir_zapobj, *offp); - if (zap_cursor_retrieve(&cursor, &attr) != 0) { + if (zap_cursor_retrieve(&cursor, attr) != 0) { zap_cursor_fini(&cursor); + zap_attribute_free(attr); return (SET_ERROR(ENOENT)); } - if (strlen(attr.za_name) + 1 > namelen) { + if (strlen(attr->za_name) + 1 > namelen) { zap_cursor_fini(&cursor); + zap_attribute_free(attr); return (SET_ERROR(ENAMETOOLONG)); } - (void) strlcpy(name, attr.za_name, namelen); + (void) strlcpy(name, attr->za_name, namelen); if (idp) - *idp = attr.za_first_integer; + *idp = attr->za_first_integer; zap_cursor_advance(&cursor); *offp = zap_cursor_serialize(&cursor); zap_cursor_fini(&cursor); + zap_attribute_free(attr); return (0); } @@ -2734,7 +2742,7 @@ dmu_objset_find_dp_impl(dmu_objset_find_ctx_t *dcp) } thisobj = dsl_dir_phys(dd)->dd_head_dataset_obj; - attr = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + attr = zap_attribute_alloc(); /* * Iterate over all children. @@ -2795,7 +2803,7 @@ dmu_objset_find_dp_impl(dmu_objset_find_ctx_t *dcp) } } - kmem_free(attr, sizeof (zap_attribute_t)); + zap_attribute_free(attr); if (err != 0) { dsl_dir_rele(dd, FTAG); @@ -2969,7 +2977,7 @@ dmu_objset_find_impl(spa_t *spa, const char *name, } thisobj = dsl_dir_phys(dd)->dd_head_dataset_obj; - attr = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + attr = zap_attribute_alloc(); /* * Iterate over all children. @@ -2997,7 +3005,7 @@ dmu_objset_find_impl(spa_t *spa, const char *name, if (err != 0) { dsl_dir_rele(dd, FTAG); dsl_pool_config_exit(dp, FTAG); - kmem_free(attr, sizeof (zap_attribute_t)); + zap_attribute_free(attr); return (err); } } @@ -3035,7 +3043,7 @@ dmu_objset_find_impl(spa_t *spa, const char *name, } dsl_dir_rele(dd, FTAG); - kmem_free(attr, sizeof (zap_attribute_t)); + zap_attribute_free(attr); dsl_pool_config_exit(dp, FTAG); if (err != 0) diff --git a/module/zfs/dmu_recv.c b/module/zfs/dmu_recv.c index 0119191d79..5ed1c952e0 100644 --- a/module/zfs/dmu_recv.c +++ b/module/zfs/dmu_recv.c @@ -602,6 +602,13 @@ recv_begin_check_feature_flags_impl(uint64_t featureflags, spa_t *spa) !spa_feature_is_enabled(spa, SPA_FEATURE_REDACTED_DATASETS)) return (SET_ERROR(ENOTSUP)); + /* + * If the LONGNAME is not enabled on the target, fail that request. + */ + if ((featureflags & DMU_BACKUP_FEATURE_LONGNAME) && + !spa_feature_is_enabled(spa, SPA_FEATURE_LONGNAME)) + return (SET_ERROR(ENOTSUP)); + return (0); } @@ -990,6 +997,16 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx) dmu_buf_will_dirty(newds->ds_dbuf, tx); dsl_dataset_phys(newds)->ds_flags |= DS_FLAG_INCONSISTENT; + /* + * Activate longname feature if received + */ + if (featureflags & DMU_BACKUP_FEATURE_LONGNAME && + !dsl_dataset_feature_is_active(newds, SPA_FEATURE_LONGNAME)) { + dsl_dataset_activate_feature(newds->ds_object, + SPA_FEATURE_LONGNAME, (void *)B_TRUE, tx); + newds->ds_feature[SPA_FEATURE_LONGNAME] = (void *)B_TRUE; + } + /* * If we actually created a non-clone, we need to create the objset * in our new dataset. If this is a raw send we postpone this until diff --git a/module/zfs/dmu_redact.c b/module/zfs/dmu_redact.c index 1feba0ba83..651461caff 100644 --- a/module/zfs/dmu_redact.c +++ b/module/zfs/dmu_redact.c @@ -189,7 +189,7 @@ zfs_get_deleteq(objset_t *os) objlist_t *deleteq_objlist = objlist_create(); uint64_t deleteq_obj; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; dmu_object_info_t doi; ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS); @@ -208,13 +208,15 @@ zfs_get_deleteq(objset_t *os) avl_create(&at, objnode_compare, sizeof (struct objnode), offsetof(struct objnode, node)); + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, os, deleteq_obj); - zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { struct objnode *obj = kmem_zalloc(sizeof (*obj), KM_SLEEP); - obj->obj = za.za_first_integer; + obj->obj = za->za_first_integer; avl_add(&at, obj); } zap_cursor_fini(&zc); + zap_attribute_free(za); struct objnode *next, *found = avl_first(&at); while (found != NULL) { diff --git a/module/zfs/dmu_send.c b/module/zfs/dmu_send.c index cb2b62fed3..c7d3a5cb6e 100644 --- a/module/zfs/dmu_send.c +++ b/module/zfs/dmu_send.c @@ -2011,6 +2011,10 @@ setup_featureflags(struct dmu_send_params *dspp, objset_t *os, if (dsl_dataset_feature_is_active(to_ds, SPA_FEATURE_LARGE_DNODE)) { *featureflags |= DMU_BACKUP_FEATURE_LARGE_DNODE; } + + if (dsl_dataset_feature_is_active(to_ds, SPA_FEATURE_LONGNAME)) { + *featureflags |= DMU_BACKUP_FEATURE_LONGNAME; + } return (0); } diff --git a/module/zfs/dsl_bookmark.c b/module/zfs/dsl_bookmark.c index 5fd8bc2a26..a65aded4ef 100644 --- a/module/zfs/dsl_bookmark.c +++ b/module/zfs/dsl_bookmark.c @@ -870,13 +870,14 @@ dsl_bookmark_init_ds(dsl_dataset_t *ds) int err = 0; zap_cursor_t zc; - zap_attribute_t attr; + zap_attribute_t *attr; + attr = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, ds->ds_bookmarks_obj); - (err = zap_cursor_retrieve(&zc, &attr)) == 0; + (err = zap_cursor_retrieve(&zc, attr)) == 0; zap_cursor_advance(&zc)) { dsl_bookmark_node_t *dbn = - dsl_bookmark_node_alloc(attr.za_name); + dsl_bookmark_node_alloc(attr->za_name); err = dsl_bookmark_lookup_impl(ds, dbn->dbn_name, &dbn->dbn_phys); @@ -888,6 +889,7 @@ dsl_bookmark_init_ds(dsl_dataset_t *ds) avl_add(&ds->ds_bookmarks, dbn); } zap_cursor_fini(&zc); + zap_attribute_free(attr); if (err == ENOENT) err = 0; return (err); diff --git a/module/zfs/dsl_crypt.c b/module/zfs/dsl_crypt.c index 8e1055d9bc..475f4f5c1e 100644 --- a/module/zfs/dsl_crypt.c +++ b/module/zfs/dsl_crypt.c @@ -1497,7 +1497,7 @@ spa_keystore_change_key_sync_impl(uint64_t rddobj, uint64_t ddobj, } zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); - za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); /* Recurse into all child dsl dirs. */ for (zap_cursor_init(zc, dp->dp_meta_objset, @@ -1529,7 +1529,7 @@ spa_keystore_change_key_sync_impl(uint64_t rddobj, uint64_t ddobj, } zap_cursor_fini(zc); - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); kmem_free(zc, sizeof (zap_cursor_t)); dsl_dir_rele(dd, FTAG); diff --git a/module/zfs/dsl_dataset.c b/module/zfs/dsl_dataset.c index 45d8a290d6..4302617982 100644 --- a/module/zfs/dsl_dataset.c +++ b/module/zfs/dsl_dataset.c @@ -494,7 +494,8 @@ dsl_dataset_get_snapname(dsl_dataset_t *ds) return (err); headphys = headdbuf->db_data; err = zap_value_search(dp->dp_meta_objset, - headphys->ds_snapnames_zapobj, ds->ds_object, 0, ds->ds_snapname); + headphys->ds_snapnames_zapobj, ds->ds_object, 0, ds->ds_snapname, + sizeof (ds->ds_snapname)); if (err != 0 && zfs_recover == B_TRUE) { err = 0; (void) snprintf(ds->ds_snapname, sizeof (ds->ds_snapname), @@ -2290,7 +2291,7 @@ get_clones_stat_impl(dsl_dataset_t *ds, nvlist_t *val) uint64_t count = 0; objset_t *mos = ds->ds_dir->dd_pool->dp_meta_objset; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; ASSERT(dsl_pool_config_held(ds->ds_dir->dd_pool)); @@ -2306,19 +2307,22 @@ get_clones_stat_impl(dsl_dataset_t *ds, nvlist_t *val) if (count != dsl_dataset_phys(ds)->ds_num_children - 1) { return (SET_ERROR(ENOENT)); } + + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, dsl_dataset_phys(ds)->ds_next_clones_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { dsl_dataset_t *clone; char buf[ZFS_MAX_DATASET_NAME_LEN]; VERIFY0(dsl_dataset_hold_obj(ds->ds_dir->dd_pool, - za.za_first_integer, FTAG, &clone)); + za->za_first_integer, FTAG, &clone)); dsl_dir_name(clone->ds_dir, buf); fnvlist_add_boolean(val, buf); dsl_dataset_rele(clone, FTAG); } zap_cursor_fini(&zc); + zap_attribute_free(za); return (0); } @@ -3640,16 +3644,16 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx) if (dsl_dataset_phys(ds)->ds_next_clones_obj && spa_version(dp->dp_spa) >= SPA_VERSION_DIR_CLONES) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, dp->dp_meta_objset, dsl_dataset_phys(ds)->ds_next_clones_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { dsl_dataset_t *cnds; uint64_t o; - if (za.za_first_integer == oldnext_obj) { + if (za->za_first_integer == oldnext_obj) { /* * We've already moved the * origin's reference. @@ -3658,7 +3662,7 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx) } VERIFY0(dsl_dataset_hold_obj(dp, - za.za_first_integer, FTAG, &cnds)); + za->za_first_integer, FTAG, &cnds)); o = dsl_dir_phys(cnds->ds_dir)-> dd_head_dataset_obj; @@ -3669,6 +3673,7 @@ dsl_dataset_promote_sync(void *arg, dmu_tx_t *tx) dsl_dataset_rele(cnds, FTAG); } zap_cursor_fini(&zc); + zap_attribute_free(za); } ASSERT(!dsl_prop_hascb(ds)); diff --git a/module/zfs/dsl_deadlist.c b/module/zfs/dsl_deadlist.c index eff1f7de77..e457e2fd86 100644 --- a/module/zfs/dsl_deadlist.c +++ b/module/zfs/dsl_deadlist.c @@ -133,7 +133,7 @@ static void dsl_deadlist_load_tree(dsl_deadlist_t *dl) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; int error; ASSERT(MUTEX_HELD(&dl->dl_lock)); @@ -159,20 +159,21 @@ dsl_deadlist_load_tree(dsl_deadlist_t *dl) if (dl->dl_havetree) return; + za = zap_attribute_alloc(); avl_create(&dl->dl_tree, dsl_deadlist_compare, sizeof (dsl_deadlist_entry_t), offsetof(dsl_deadlist_entry_t, dle_node)); for (zap_cursor_init(&zc, dl->dl_os, dl->dl_object); - (error = zap_cursor_retrieve(&zc, &za)) == 0; + (error = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { dsl_deadlist_entry_t *dle = kmem_alloc(sizeof (*dle), KM_SLEEP); - dle->dle_mintxg = zfs_strtonum(za.za_name, NULL); + dle->dle_mintxg = zfs_strtonum(za->za_name, NULL); /* * Prefetch all the bpobj's so that we do that i/o * in parallel. Then open them all in a second pass. */ - dle->dle_bpobj.bpo_object = za.za_first_integer; + dle->dle_bpobj.bpo_object = za->za_first_integer; dmu_prefetch_dnode(dl->dl_os, dle->dle_bpobj.bpo_object, ZIO_PRIORITY_SYNC_READ); @@ -180,6 +181,7 @@ dsl_deadlist_load_tree(dsl_deadlist_t *dl) } VERIFY3U(error, ==, ENOENT); zap_cursor_fini(&zc); + zap_attribute_free(za); for (dsl_deadlist_entry_t *dle = avl_first(&dl->dl_tree); dle != NULL; dle = AVL_NEXT(&dl->dl_tree, dle)) { @@ -207,7 +209,7 @@ static void dsl_deadlist_load_cache(dsl_deadlist_t *dl) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; int error; ASSERT(MUTEX_HELD(&dl->dl_lock)); @@ -221,26 +223,28 @@ dsl_deadlist_load_cache(dsl_deadlist_t *dl) avl_create(&dl->dl_cache, dsl_deadlist_cache_compare, sizeof (dsl_deadlist_cache_entry_t), offsetof(dsl_deadlist_cache_entry_t, dlce_node)); + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, dl->dl_os, dl->dl_object); - (error = zap_cursor_retrieve(&zc, &za)) == 0; + (error = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { - if (za.za_first_integer == empty_bpobj) + if (za->za_first_integer == empty_bpobj) continue; dsl_deadlist_cache_entry_t *dlce = kmem_zalloc(sizeof (*dlce), KM_SLEEP); - dlce->dlce_mintxg = zfs_strtonum(za.za_name, NULL); + dlce->dlce_mintxg = zfs_strtonum(za->za_name, NULL); /* * Prefetch all the bpobj's so that we do that i/o * in parallel. Then open them all in a second pass. */ - dlce->dlce_bpobj = za.za_first_integer; + dlce->dlce_bpobj = za->za_first_integer; dmu_prefetch_dnode(dl->dl_os, dlce->dlce_bpobj, ZIO_PRIORITY_SYNC_READ); avl_add(&dl->dl_cache, dlce); } VERIFY3U(error, ==, ENOENT); zap_cursor_fini(&zc); + zap_attribute_free(za); for (dsl_deadlist_cache_entry_t *dlce = avl_first(&dl->dl_cache); dlce != NULL; dlce = AVL_NEXT(&dl->dl_cache, dlce)) { @@ -381,7 +385,7 @@ dsl_deadlist_free(objset_t *os, uint64_t dlobj, dmu_tx_t *tx) { dmu_object_info_t doi; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; int error; VERIFY0(dmu_object_info(os, dlobj, &doi)); @@ -390,10 +394,11 @@ dsl_deadlist_free(objset_t *os, uint64_t dlobj, dmu_tx_t *tx) return; } + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, os, dlobj); - (error = zap_cursor_retrieve(&zc, &za)) == 0; + (error = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { - uint64_t obj = za.za_first_integer; + uint64_t obj = za->za_first_integer; if (obj == dmu_objset_pool(os)->dp_empty_bpobj) bpobj_decr_empty(os, tx); else @@ -401,6 +406,7 @@ dsl_deadlist_free(objset_t *os, uint64_t dlobj, dmu_tx_t *tx) } VERIFY3U(error, ==, ENOENT); zap_cursor_fini(&zc); + zap_attribute_free(za); VERIFY0(dmu_object_free(os, dlobj, tx)); } @@ -875,8 +881,8 @@ dsl_deadlist_merge(dsl_deadlist_t *dl, uint64_t obj, dmu_tx_t *tx) return; } - za = kmem_alloc(sizeof (*za), KM_SLEEP); - pza = kmem_alloc(sizeof (*pza), KM_SLEEP); + za = zap_attribute_alloc(); + pza = zap_attribute_alloc(); mutex_enter(&dl->dl_lock); /* @@ -913,8 +919,8 @@ dsl_deadlist_merge(dsl_deadlist_t *dl, uint64_t obj, dmu_tx_t *tx) dmu_buf_rele(bonus, FTAG); mutex_exit(&dl->dl_lock); - kmem_free(za, sizeof (*za)); - kmem_free(pza, sizeof (*pza)); + zap_attribute_free(za); + zap_attribute_free(pza); } /* diff --git a/module/zfs/dsl_deleg.c b/module/zfs/dsl_deleg.c index 645ad8e5b8..5e7a2b3fa5 100644 --- a/module/zfs/dsl_deleg.c +++ b/module/zfs/dsl_deleg.c @@ -327,9 +327,9 @@ dsl_deleg_get(const char *ddname, nvlist_t **nvp) mos = dp->dp_meta_objset; zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); - za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); basezc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); - baseza = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + baseza = zap_attribute_alloc(); source = kmem_alloc(ZFS_MAX_DATASET_NAME_LEN, KM_SLEEP); VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); @@ -371,9 +371,9 @@ dsl_deleg_get(const char *ddname, nvlist_t **nvp) } kmem_free(source, ZFS_MAX_DATASET_NAME_LEN); - kmem_free(baseza, sizeof (zap_attribute_t)); + zap_attribute_free(baseza); kmem_free(basezc, sizeof (zap_cursor_t)); - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); kmem_free(zc, sizeof (zap_cursor_t)); dsl_dir_rele(startdd, FTAG); @@ -482,7 +482,7 @@ dsl_load_sets(objset_t *mos, uint64_t zapobj, char type, char checkflag, void *valp, avl_tree_t *avl) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; perm_set_t *permnode; avl_index_t idx; uint64_t jumpobj; @@ -495,11 +495,12 @@ dsl_load_sets(objset_t *mos, uint64_t zapobj, if (error != 0) return (error); + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, jumpobj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP); - (void) strlcpy(permnode->p_setname, za.za_name, + (void) strlcpy(permnode->p_setname, za->za_name, sizeof (permnode->p_setname)); permnode->p_matched = B_FALSE; @@ -510,6 +511,7 @@ dsl_load_sets(objset_t *mos, uint64_t zapobj, } } zap_cursor_fini(&zc); + zap_attribute_free(za); return (0); } @@ -683,7 +685,7 @@ copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj, uint64_t jumpobj, pjumpobj; uint64_t zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; char whokey[ZFS_MAX_DELEG_NAME]; zfs_deleg_whokey(whokey, @@ -706,16 +708,18 @@ copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj, VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0); } + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, pjumpobj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { uint64_t zero = 0; - ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); + ASSERT(za->za_integer_length == 8 && za->za_num_integers == 1); - VERIFY(zap_update(mos, jumpobj, za.za_name, + VERIFY(zap_update(mos, jumpobj, za->za_name, 8, 1, &zero, tx) == 0); } zap_cursor_fini(&zc); + zap_attribute_free(za); } /* @@ -746,19 +750,21 @@ int dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; if (zapobj == 0) return (0); + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, zapobj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { - ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); - VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx)); + ASSERT(za->za_integer_length == 8 && za->za_num_integers == 1); + VERIFY(0 == zap_destroy(mos, za->za_first_integer, tx)); } zap_cursor_fini(&zc); VERIFY(0 == zap_destroy(mos, zapobj, tx)); + zap_attribute_free(za); return (0); } diff --git a/module/zfs/dsl_destroy.c b/module/zfs/dsl_destroy.c index d4a6e5b6e9..b2b925b135 100644 --- a/module/zfs/dsl_destroy.c +++ b/module/zfs/dsl_destroy.c @@ -216,7 +216,7 @@ dsl_dir_remove_clones_key_impl(dsl_dir_t *dd, uint64_t mintxg, dmu_tx_t *tx, return; zap_cursor_t *zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); - zap_attribute_t *za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(zc, mos, dsl_dir_phys(dd)->dd_clones); zap_cursor_retrieve(zc, za) == 0; @@ -242,7 +242,7 @@ dsl_dir_remove_clones_key_impl(dsl_dir_t *dd, uint64_t mintxg, dmu_tx_t *tx, } zap_cursor_fini(zc); - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); kmem_free(zc, sizeof (zap_cursor_t)); } diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c index baf970121a..1b60fa620b 100644 --- a/module/zfs/dsl_dir.c +++ b/module/zfs/dsl_dir.c @@ -239,7 +239,8 @@ dsl_dir_hold_obj(dsl_pool_t *dp, uint64_t ddobj, err = zap_value_search(dp->dp_meta_objset, dsl_dir_phys(dd->dd_parent)-> dd_child_dir_zapobj, - ddobj, 0, dd->dd_myname); + ddobj, 0, dd->dd_myname, + sizeof (dd->dd_myname)); } if (err != 0) goto errout; @@ -586,7 +587,7 @@ dsl_dir_init_fs_ss_count(dsl_dir_t *dd, dmu_tx_t *tx) return; zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); - za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); /* Iterate my child dirs */ for (zap_cursor_init(zc, os, dsl_dir_phys(dd)->dd_child_dir_zapobj); @@ -635,7 +636,7 @@ dsl_dir_init_fs_ss_count(dsl_dir_t *dd, dmu_tx_t *tx) dsl_dataset_rele(ds, FTAG); kmem_free(zc, sizeof (zap_cursor_t)); - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); /* we're in a sync task, update counts */ dmu_buf_will_dirty(dd->dd_dbuf, tx); diff --git a/module/zfs/dsl_pool.c b/module/zfs/dsl_pool.c index 342ec5c15c..5ae9688293 100644 --- a/module/zfs/dsl_pool.c +++ b/module/zfs/dsl_pool.c @@ -1196,7 +1196,7 @@ dsl_pool_unlinked_drain_taskq(dsl_pool_t *dp) void dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp) { - zap_attribute_t za; + zap_attribute_t *za; zap_cursor_t zc; objset_t *mos = dp->dp_meta_objset; uint64_t zapobj = dp->dp_tmp_userrefs_obj; @@ -1208,19 +1208,20 @@ dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp) holds = fnvlist_alloc(); + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, zapobj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { char *htag; nvlist_t *tags; - htag = strchr(za.za_name, '-'); + htag = strchr(za->za_name, '-'); *htag = '\0'; ++htag; - if (nvlist_lookup_nvlist(holds, za.za_name, &tags) != 0) { + if (nvlist_lookup_nvlist(holds, za->za_name, &tags) != 0) { tags = fnvlist_alloc(); fnvlist_add_boolean(tags, htag); - fnvlist_add_nvlist(holds, za.za_name, tags); + fnvlist_add_nvlist(holds, za->za_name, tags); fnvlist_free(tags); } else { fnvlist_add_boolean(tags, htag); @@ -1229,6 +1230,7 @@ dsl_pool_clean_tmp_userrefs(dsl_pool_t *dp) dsl_dataset_user_release_tmp(dp, holds); fnvlist_free(holds); zap_cursor_fini(&zc); + zap_attribute_free(za); } /* diff --git a/module/zfs/dsl_prop.c b/module/zfs/dsl_prop.c index 99f931cd86..3b8683593f 100644 --- a/module/zfs/dsl_prop.c +++ b/module/zfs/dsl_prop.c @@ -662,7 +662,7 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, } mutex_exit(&dd->dd_lock); - za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, dsl_dir_phys(dd)->dd_child_dir_zapobj); zap_cursor_retrieve(&zc, za) == 0; @@ -670,7 +670,7 @@ dsl_prop_changed_notify(dsl_pool_t *dp, uint64_t ddobj, dsl_prop_changed_notify(dp, za->za_first_integer, propname, value, FALSE); } - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); zap_cursor_fini(&zc); dsl_dir_rele(dd, FTAG); } @@ -1061,11 +1061,11 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, const char *setpoint, dsl_prop_getflags_t flags, nvlist_t *nv) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); int err = 0; for (zap_cursor_init(&zc, mos, propobj); - (err = zap_cursor_retrieve(&zc, &za)) == 0; + (err = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { nvlist_t *propval; zfs_prop_t prop; @@ -1075,7 +1075,7 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, const char *propname; const char *source; - suffix = strchr(za.za_name, '$'); + suffix = strchr(za->za_name, '$'); if (suffix == NULL) { /* @@ -1085,7 +1085,7 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, if (flags & DSL_PROP_GET_RECEIVED) continue; - propname = za.za_name; + propname = za->za_name; source = setpoint; /* Skip if iuv entries are preset. */ @@ -1102,8 +1102,8 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, if (flags & DSL_PROP_GET_LOCAL) continue; - (void) strlcpy(buf, za.za_name, - MIN(sizeof (buf), suffix - za.za_name + 1)); + (void) strlcpy(buf, za->za_name, + MIN(sizeof (buf), suffix - za->za_name + 1)); propname = buf; if (!(flags & DSL_PROP_GET_RECEIVED)) { @@ -1128,14 +1128,14 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, source = ((flags & DSL_PROP_GET_INHERITING) ? setpoint : ZPROP_SOURCE_VAL_RECVD); } else if (strcmp(suffix, ZPROP_IUV_SUFFIX) == 0) { - (void) strlcpy(buf, za.za_name, - MIN(sizeof (buf), suffix - za.za_name + 1)); + (void) strlcpy(buf, za->za_name, + MIN(sizeof (buf), suffix - za->za_name + 1)); propname = buf; source = setpoint; prop = zfs_name_to_prop(propname); if (dsl_prop_known_index(prop, - za.za_first_integer) != 1) + za->za_first_integer) != 1) continue; } else { /* @@ -1162,28 +1162,28 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, continue; VERIFY(nvlist_alloc(&propval, NV_UNIQUE_NAME, KM_SLEEP) == 0); - if (za.za_integer_length == 1) { + if (za->za_integer_length == 1) { /* * String property */ - char *tmp = kmem_alloc(za.za_num_integers, + char *tmp = kmem_alloc(za->za_num_integers, KM_SLEEP); err = zap_lookup(mos, propobj, - za.za_name, 1, za.za_num_integers, tmp); + za->za_name, 1, za->za_num_integers, tmp); if (err != 0) { - kmem_free(tmp, za.za_num_integers); + kmem_free(tmp, za->za_num_integers); break; } VERIFY(nvlist_add_string(propval, ZPROP_VALUE, tmp) == 0); - kmem_free(tmp, za.za_num_integers); + kmem_free(tmp, za->za_num_integers); } else { /* * Integer property */ - ASSERT(za.za_integer_length == 8); + ASSERT(za->za_integer_length == 8); (void) nvlist_add_uint64(propval, ZPROP_VALUE, - za.za_first_integer); + za->za_first_integer); } VERIFY(nvlist_add_string(propval, ZPROP_SOURCE, source) == 0); @@ -1191,6 +1191,7 @@ dsl_prop_get_all_impl(objset_t *mos, uint64_t propobj, nvlist_free(propval); } zap_cursor_fini(&zc); + zap_attribute_free(za); if (err == ENOENT) err = 0; return (err); diff --git a/module/zfs/dsl_scan.c b/module/zfs/dsl_scan.c index 085cfd3c56..38eb4fab19 100644 --- a/module/zfs/dsl_scan.c +++ b/module/zfs/dsl_scan.c @@ -617,17 +617,18 @@ dsl_scan_init(dsl_pool_t *dp, uint64_t txg) /* reload the queue into the in-core state */ if (scn->scn_phys.scn_queue_obj != 0) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, dp->dp_meta_objset, scn->scn_phys.scn_queue_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; (void) zap_cursor_advance(&zc)) { scan_ds_queue_insert(scn, - zfs_strtonum(za.za_name, NULL), - za.za_first_integer); + zfs_strtonum(za->za_name, NULL), + za->za_first_integer); } zap_cursor_fini(&zc); + zap_attribute_free(za); } spa_scan_stat_init(spa); @@ -2865,16 +2866,17 @@ dsl_scan_visitds(dsl_scan_t *scn, uint64_t dsobj, dmu_tx_t *tx) if (usenext) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, dp->dp_meta_objset, dsl_dataset_phys(ds)->ds_next_clones_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; (void) zap_cursor_advance(&zc)) { scan_ds_queue_insert(scn, - zfs_strtonum(za.za_name, NULL), + zfs_strtonum(za->za_name, NULL), dsl_dataset_phys(ds)->ds_creation_txg); } zap_cursor_fini(&zc); + zap_attribute_free(za); } else { VERIFY0(dmu_objset_find_dp(dp, dp->dp_root_dir_obj, enqueue_clones_cb, &ds->ds_object, @@ -4156,7 +4158,7 @@ dsl_errorscrub_sync(dsl_pool_t *dp, dmu_tx_t *tx) zbookmark_phys_t *zb; boolean_t limit_exceeded = B_FALSE; - za = kmem_zalloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); zb = kmem_zalloc(sizeof (zbookmark_phys_t), KM_SLEEP); if (!spa_feature_is_enabled(spa, SPA_FEATURE_HEAD_ERRLOG)) { @@ -4187,7 +4189,7 @@ dsl_errorscrub_sync(dsl_pool_t *dp, dmu_tx_t *tx) dsl_errorscrub_done(scn, B_TRUE, tx); dsl_errorscrub_sync_state(scn, tx); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); kmem_free(zb, sizeof (*zb)); return; } @@ -4201,7 +4203,7 @@ dsl_errorscrub_sync(dsl_pool_t *dp, dmu_tx_t *tx) zbookmark_err_phys_t head_ds_block; head_ds_cursor = kmem_zalloc(sizeof (zap_cursor_t), KM_SLEEP); - head_ds_attr = kmem_zalloc(sizeof (zap_attribute_t), KM_SLEEP); + head_ds_attr = zap_attribute_alloc(); uint64_t head_ds_err_obj = za->za_first_integer; uint64_t head_ds; @@ -4240,13 +4242,13 @@ dsl_errorscrub_sync(dsl_pool_t *dp, dmu_tx_t *tx) zap_cursor_fini(head_ds_cursor); kmem_free(head_ds_cursor, sizeof (*head_ds_cursor)); - kmem_free(head_ds_attr, sizeof (*head_ds_attr)); + zap_attribute_free(head_ds_attr); if (config_held) dsl_pool_config_exit(dp, FTAG); } - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); kmem_free(zb, sizeof (*zb)); if (!limit_exceeded) dsl_errorscrub_done(scn, B_TRUE, tx); diff --git a/module/zfs/dsl_userhold.c b/module/zfs/dsl_userhold.c index 75953f70f9..ffe208736b 100644 --- a/module/zfs/dsl_userhold.c +++ b/module/zfs/dsl_userhold.c @@ -674,7 +674,7 @@ dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) zap_attribute_t *za; zap_cursor_t zc; - za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, ds->ds_dir->dd_pool->dp_meta_objset, dsl_dataset_phys(ds)->ds_userrefs_obj); zap_cursor_retrieve(&zc, za) == 0; @@ -683,7 +683,7 @@ dsl_dataset_get_holds(const char *dsname, nvlist_t *nvl) za->za_first_integer); } zap_cursor_fini(&zc); - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); } dsl_dataset_rele(ds, FTAG); dsl_pool_rele(dp, FTAG); diff --git a/module/zfs/sa.c b/module/zfs/sa.c index bc4c9dff31..31d30c1b40 100644 --- a/module/zfs/sa.c +++ b/module/zfs/sa.c @@ -840,7 +840,7 @@ sa_attr_table_setup(objset_t *os, const sa_attr_reg_t *reg_attrs, int count) uint64_t attr_value; sa_attr_table_t *tb; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; int registered_count = 0; int i; dmu_objset_type_t ostype = dmu_objset_type(os); @@ -914,11 +914,12 @@ sa_attr_table_setup(objset_t *os, const sa_attr_reg_t *reg_attrs, int count) */ if (sa->sa_reg_attr_obj) { + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, os, sa->sa_reg_attr_obj); - (error = zap_cursor_retrieve(&zc, &za)) == 0; + (error = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { uint64_t value; - value = za.za_first_integer; + value = za->za_first_integer; registered_count++; tb[ATTR_NUM(value)].sa_attr = ATTR_NUM(value); @@ -930,11 +931,12 @@ sa_attr_table_setup(objset_t *os, const sa_attr_reg_t *reg_attrs, int count) continue; } tb[ATTR_NUM(value)].sa_name = - kmem_zalloc(strlen(za.za_name) +1, KM_SLEEP); - (void) strlcpy(tb[ATTR_NUM(value)].sa_name, za.za_name, - strlen(za.za_name) +1); + kmem_zalloc(strlen(za->za_name) +1, KM_SLEEP); + (void) strlcpy(tb[ATTR_NUM(value)].sa_name, za->za_name, + strlen(za->za_name) +1); } zap_cursor_fini(&zc); + zap_attribute_free(za); /* * Make sure we processed the correct number of registered * attributes @@ -996,7 +998,7 @@ sa_setup(objset_t *os, uint64_t sa_obj, const sa_attr_reg_t *reg_attrs, int count, sa_attr_type_t **user_table) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; sa_os_t *sa; dmu_objset_type_t ostype = dmu_objset_type(os); sa_attr_type_t *tb; @@ -1053,33 +1055,35 @@ sa_setup(objset_t *os, uint64_t sa_obj, const sa_attr_reg_t *reg_attrs, goto fail; } + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, os, sa->sa_layout_attr_obj); - (error = zap_cursor_retrieve(&zc, &za)) == 0; + (error = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { sa_attr_type_t *lot_attrs; uint64_t lot_num; lot_attrs = kmem_zalloc(sizeof (sa_attr_type_t) * - za.za_num_integers, KM_SLEEP); + za->za_num_integers, KM_SLEEP); if ((error = (zap_lookup(os, sa->sa_layout_attr_obj, - za.za_name, 2, za.za_num_integers, + za->za_name, 2, za->za_num_integers, lot_attrs))) != 0) { kmem_free(lot_attrs, sizeof (sa_attr_type_t) * - za.za_num_integers); + za->za_num_integers); break; } - VERIFY0(ddi_strtoull(za.za_name, NULL, 10, + VERIFY0(ddi_strtoull(za->za_name, NULL, 10, (unsigned long long *)&lot_num)); (void) sa_add_layout_entry(os, lot_attrs, - za.za_num_integers, lot_num, + za->za_num_integers, lot_num, sa_layout_info_hash(lot_attrs, - za.za_num_integers), B_FALSE, NULL); + za->za_num_integers), B_FALSE, NULL); kmem_free(lot_attrs, sizeof (sa_attr_type_t) * - za.za_num_integers); + za->za_num_integers); } zap_cursor_fini(&zc); + zap_attribute_free(za); /* * Make sure layout count matches number of entries added diff --git a/module/zfs/spa.c b/module/zfs/spa.c index 99a8d107ec..44ef250f2d 100644 --- a/module/zfs/spa.c +++ b/module/zfs/spa.c @@ -548,7 +548,7 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp) { objset_t *mos = spa->spa_meta_objset; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; dsl_pool_t *dp; int err; @@ -560,6 +560,7 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp) dp = spa_get_dsl(spa); dsl_pool_config_enter(dp, FTAG); + za = zap_attribute_alloc(); mutex_enter(&spa->spa_props_lock); /* @@ -575,21 +576,21 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp) * Get properties from the MOS pool property object. */ for (zap_cursor_init(&zc, mos, spa->spa_pool_props_object); - (err = zap_cursor_retrieve(&zc, &za)) == 0; + (err = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { uint64_t intval = 0; char *strval = NULL; zprop_source_t src = ZPROP_SRC_DEFAULT; zpool_prop_t prop; - if ((prop = zpool_name_to_prop(za.za_name)) == - ZPOOL_PROP_INVAL && !zfs_prop_user(za.za_name)) + if ((prop = zpool_name_to_prop(za->za_name)) == + ZPOOL_PROP_INVAL && !zfs_prop_user(za->za_name)) continue; - switch (za.za_integer_length) { + switch (za->za_integer_length) { case 8: /* integer property */ - if (za.za_first_integer != + if (za->za_first_integer != zpool_prop_default_numeric(prop)) src = ZPROP_SRC_LOCAL; @@ -597,7 +598,7 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp) dsl_dataset_t *ds = NULL; err = dsl_dataset_hold_obj(dp, - za.za_first_integer, FTAG, &ds); + za->za_first_integer, FTAG, &ds); if (err != 0) break; @@ -607,7 +608,7 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp) dsl_dataset_rele(ds, FTAG); } else { strval = NULL; - intval = za.za_first_integer; + intval = za->za_first_integer; } spa_prop_add_list(*nvp, prop, strval, intval, src); @@ -619,21 +620,21 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp) case 1: /* string property */ - strval = kmem_alloc(za.za_num_integers, KM_SLEEP); + strval = kmem_alloc(za->za_num_integers, KM_SLEEP); err = zap_lookup(mos, spa->spa_pool_props_object, - za.za_name, 1, za.za_num_integers, strval); + za->za_name, 1, za->za_num_integers, strval); if (err) { - kmem_free(strval, za.za_num_integers); + kmem_free(strval, za->za_num_integers); break; } if (prop != ZPOOL_PROP_INVAL) { spa_prop_add_list(*nvp, prop, strval, 0, src); } else { src = ZPROP_SRC_LOCAL; - spa_prop_add_user(*nvp, za.za_name, strval, + spa_prop_add_user(*nvp, za->za_name, strval, src); } - kmem_free(strval, za.za_num_integers); + kmem_free(strval, za->za_num_integers); break; default: @@ -644,6 +645,7 @@ spa_prop_get(spa_t *spa, nvlist_t **nvp) out: mutex_exit(&spa->spa_props_lock); dsl_pool_config_exit(dp, FTAG); + zap_attribute_free(za); if (err && err != ENOENT) { nvlist_free(*nvp); *nvp = NULL; @@ -2967,12 +2969,13 @@ dsl_get_next_livelist_obj(objset_t *os, uint64_t zap_obj, uint64_t *llp) { int err; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); zap_cursor_init(&zc, os, zap_obj); - err = zap_cursor_retrieve(&zc, &za); + err = zap_cursor_retrieve(&zc, za); zap_cursor_fini(&zc); if (err == 0) - *llp = za.za_first_integer; + *llp = za->za_first_integer; + zap_attribute_free(za); return (err); } @@ -6043,17 +6046,17 @@ static void spa_feature_stats_from_disk(spa_t *spa, nvlist_t *features) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); if (spa->spa_feat_for_read_obj != 0) { for (zap_cursor_init(&zc, spa->spa_meta_objset, spa->spa_feat_for_read_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { - ASSERT(za.za_integer_length == sizeof (uint64_t) && - za.za_num_integers == 1); - VERIFY0(nvlist_add_uint64(features, za.za_name, - za.za_first_integer)); + ASSERT(za->za_integer_length == sizeof (uint64_t) && + za->za_num_integers == 1); + VERIFY0(nvlist_add_uint64(features, za->za_name, + za->za_first_integer)); } zap_cursor_fini(&zc); } @@ -6061,15 +6064,16 @@ spa_feature_stats_from_disk(spa_t *spa, nvlist_t *features) if (spa->spa_feat_for_write_obj != 0) { for (zap_cursor_init(&zc, spa->spa_meta_objset, spa->spa_feat_for_write_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { - ASSERT(za.za_integer_length == sizeof (uint64_t) && - za.za_num_integers == 1); - VERIFY0(nvlist_add_uint64(features, za.za_name, - za.za_first_integer)); + ASSERT(za->za_integer_length == sizeof (uint64_t) && + za->za_num_integers == 1); + VERIFY0(nvlist_add_uint64(features, za->za_name, + za->za_first_integer)); } zap_cursor_fini(&zc); } + zap_attribute_free(za); } static void @@ -9429,13 +9433,13 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx) /* Diff old AVZ with new one */ zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa->spa_all_vdev_zaps); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { - uint64_t vdzap = za.za_first_integer; + uint64_t vdzap = za->za_first_integer; if (zap_lookup_int(spa->spa_meta_objset, new_avz, vdzap) == ENOENT) { /* @@ -9448,6 +9452,7 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx) } zap_cursor_fini(&zc); + zap_attribute_free(za); /* Destroy the old AVZ */ VERIFY0(zap_destroy(spa->spa_meta_objset, @@ -9461,18 +9466,19 @@ spa_sync_config_object(spa_t *spa, dmu_tx_t *tx) spa->spa_all_vdev_zaps = new_avz; } else if (spa->spa_avz_action == AVZ_ACTION_DESTROY) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); /* Walk through the AVZ and destroy all listed ZAPs */ for (zap_cursor_init(&zc, spa->spa_meta_objset, spa->spa_all_vdev_zaps); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { - uint64_t zap = za.za_first_integer; + uint64_t zap = za->za_first_integer; VERIFY0(zap_destroy(spa->spa_meta_objset, zap, tx)); } zap_cursor_fini(&zc); + zap_attribute_free(za); /* Destroy and unlink the AVZ itself */ VERIFY0(zap_destroy(spa->spa_meta_objset, diff --git a/module/zfs/spa_errlog.c b/module/zfs/spa_errlog.c index 62d7b4fa2d..a49e28ee7a 100644 --- a/module/zfs/spa_errlog.c +++ b/module/zfs/spa_errlog.c @@ -429,7 +429,7 @@ check_filesystem(spa_t *spa, uint64_t head_ds, zbookmark_err_phys_t *zep, zap_attribute_t *za; zc = kmem_zalloc(sizeof (zap_cursor_t), KM_SLEEP); - za = kmem_zalloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); for (zap_cursor_init(zc, spa->spa_meta_objset, zap_clone); zap_cursor_retrieve(zc, za) == 0; @@ -463,7 +463,7 @@ check_filesystem(spa_t *spa, uint64_t head_ds, zbookmark_err_phys_t *zep, } zap_cursor_fini(zc); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); kmem_free(zc, sizeof (*zc)); out: @@ -617,10 +617,10 @@ spa_add_healed_error(spa_t *spa, uint64_t obj, zbookmark_phys_t *healed_zb, errphys_to_name(&healed_zep, name, sizeof (name)); zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa->spa_errlog_last); - zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { - if (zap_contains(spa->spa_meta_objset, za.za_first_integer, + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { + if (zap_contains(spa->spa_meta_objset, za->za_first_integer, name) == 0) { if (!MUTEX_HELD(&spa->spa_errlog_lock)) { mutex_enter(&spa->spa_errlog_lock); @@ -657,6 +657,7 @@ spa_add_healed_error(spa_t *spa, uint64_t obj, zbookmark_phys_t *healed_zb, } } zap_cursor_fini(&zc); + zap_attribute_free(za); } /* @@ -704,24 +705,25 @@ spa_remove_healed_errors(spa_t *spa, avl_tree_t *s, avl_tree_t *l, dmu_tx_t *tx) } else { errphys_to_name(&se->se_zep, name, sizeof (name)); zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa->spa_errlog_last); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { zap_remove(spa->spa_meta_objset, - za.za_first_integer, name, tx); + za->za_first_integer, name, tx); } zap_cursor_fini(&zc); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa->spa_errlog_scrub); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { zap_remove(spa->spa_meta_objset, - za.za_first_integer, name, tx); + za->za_first_integer, name, tx); } zap_cursor_fini(&zc); + zap_attribute_free(za); } kmem_free(se, sizeof (spa_error_entry_t)); } @@ -746,15 +748,16 @@ approx_errlog_size_impl(spa_t *spa, uint64_t spa_err_obj) uint64_t total = 0; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa_err_obj); - zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { uint64_t count; - if (zap_count(spa->spa_meta_objset, za.za_first_integer, + if (zap_count(spa->spa_meta_objset, za->za_first_integer, &count) == 0) total += count; } zap_cursor_fini(&zc); + zap_attribute_free(za); return (total); } @@ -806,7 +809,7 @@ sync_upgrade_errlog(spa_t *spa, uint64_t spa_err_obj, uint64_t *newobj, dmu_tx_t *tx) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; zbookmark_phys_t zb; uint64_t count; @@ -822,14 +825,15 @@ sync_upgrade_errlog(spa_t *spa, uint64_t spa_err_obj, uint64_t *newobj, return; } + za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa_err_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { if (spa_upgrade_errlog_limit != 0 && zc.zc_cd == spa_upgrade_errlog_limit) break; - name_to_bookmark(za.za_name, &zb); + name_to_bookmark(za->za_name, &zb); zbookmark_err_phys_t zep; zep.zb_object = zb.zb_object; @@ -909,6 +913,7 @@ sync_upgrade_errlog(spa_t *spa, uint64_t spa_err_obj, uint64_t *newobj, buf, 1, strlen(name) + 1, name, tx); } zap_cursor_fini(&zc); + zap_attribute_free(za); VERIFY0(dmu_object_free(spa->spa_meta_objset, spa_err_obj, tx)); } @@ -954,7 +959,7 @@ process_error_log(spa_t *spa, uint64_t obj, void *uaddr, uint64_t *count) zap_attribute_t *za; zc = kmem_zalloc(sizeof (zap_cursor_t), KM_SLEEP); - za = kmem_zalloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); if (!spa_feature_is_enabled(spa, SPA_FEATURE_HEAD_ERRLOG)) { for (zap_cursor_init(zc, spa->spa_meta_objset, obj); @@ -963,7 +968,7 @@ process_error_log(spa_t *spa, uint64_t obj, void *uaddr, uint64_t *count) if (*count == 0) { zap_cursor_fini(zc); kmem_free(zc, sizeof (*zc)); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); return (SET_ERROR(ENOMEM)); } @@ -974,13 +979,13 @@ process_error_log(spa_t *spa, uint64_t obj, void *uaddr, uint64_t *count) if (error != 0) { zap_cursor_fini(zc); kmem_free(zc, sizeof (*zc)); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); return (error); } } zap_cursor_fini(zc); kmem_free(zc, sizeof (*zc)); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); return (0); } @@ -992,7 +997,7 @@ process_error_log(spa_t *spa, uint64_t obj, void *uaddr, uint64_t *count) zap_attribute_t *head_ds_attr; head_ds_cursor = kmem_zalloc(sizeof (zap_cursor_t), KM_SLEEP); - head_ds_attr = kmem_zalloc(sizeof (zap_attribute_t), KM_SLEEP); + head_ds_attr = zap_attribute_alloc(); uint64_t head_ds_err_obj = za->za_first_integer; uint64_t head_ds; @@ -1010,20 +1015,20 @@ process_error_log(spa_t *spa, uint64_t obj, void *uaddr, uint64_t *count) zap_cursor_fini(head_ds_cursor); kmem_free(head_ds_cursor, sizeof (*head_ds_cursor)); - kmem_free(head_ds_attr, sizeof (*head_ds_attr)); + zap_attribute_free(head_ds_attr); zap_cursor_fini(zc); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); kmem_free(zc, sizeof (*zc)); return (error); } } zap_cursor_fini(head_ds_cursor); kmem_free(head_ds_cursor, sizeof (*head_ds_cursor)); - kmem_free(head_ds_attr, sizeof (*head_ds_attr)); + zap_attribute_free(head_ds_attr); } zap_cursor_fini(zc); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); kmem_free(zc, sizeof (*zc)); return (0); } @@ -1230,14 +1235,15 @@ delete_errlog(spa_t *spa, uint64_t spa_err_obj, dmu_tx_t *tx) { if (spa_feature_is_enabled(spa, SPA_FEATURE_HEAD_ERRLOG)) { zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa_err_obj); - zap_cursor_retrieve(&zc, &za) == 0; + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { VERIFY0(dmu_object_free(spa->spa_meta_objset, - za.za_first_integer, tx)); + za->za_first_integer, tx)); } zap_cursor_fini(&zc); + zap_attribute_free(za); } VERIFY0(dmu_object_free(spa->spa_meta_objset, spa_err_obj, tx)); } @@ -1339,20 +1345,21 @@ delete_dataset_errlog(spa_t *spa, uint64_t spa_err_obj, uint64_t ds, return; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa->spa_meta_objset, spa_err_obj); - zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { uint64_t head_ds; - name_to_object(za.za_name, &head_ds); + name_to_object(za->za_name, &head_ds); if (head_ds == ds) { (void) zap_remove(spa->spa_meta_objset, spa_err_obj, - za.za_name, tx); + za->za_name, tx); VERIFY0(dmu_object_free(spa->spa_meta_objset, - za.za_first_integer, tx)); + za->za_first_integer, tx)); break; } } zap_cursor_fini(&zc); + zap_attribute_free(za); } void @@ -1435,22 +1442,23 @@ swap_errlog(spa_t *spa, uint64_t spa_err_obj, uint64_t new_head, uint64_t } zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); zbookmark_err_phys_t err_block; for (zap_cursor_init(&zc, spa->spa_meta_objset, old_head_errlog); - zap_cursor_retrieve(&zc, &za) == 0; zap_cursor_advance(&zc)) { + zap_cursor_retrieve(&zc, za) == 0; zap_cursor_advance(&zc)) { const char *name = ""; - name_to_errphys(za.za_name, &err_block); + name_to_errphys(za->za_name, &err_block); if (err_block.zb_birth < txg) { (void) zap_update(spa->spa_meta_objset, new_head_errlog, - za.za_name, 1, strlen(name) + 1, name, tx); + za->za_name, 1, strlen(name) + 1, name, tx); (void) zap_remove(spa->spa_meta_objset, old_head_errlog, - za.za_name, tx); + za->za_name, tx); } } zap_cursor_fini(&zc); + zap_attribute_free(za); } void diff --git a/module/zfs/spa_log_spacemap.c b/module/zfs/spa_log_spacemap.c index 32158e8c59..f55218e357 100644 --- a/module/zfs/spa_log_spacemap.c +++ b/module/zfs/spa_log_spacemap.c @@ -1020,16 +1020,17 @@ spa_ld_log_sm_metadata(spa_t *spa) } zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, spa_meta_objset(spa), spacemap_zap); - (error = zap_cursor_retrieve(&zc, &za)) == 0; + (error = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { - uint64_t log_txg = zfs_strtonum(za.za_name, NULL); + uint64_t log_txg = zfs_strtonum(za->za_name, NULL); spa_log_sm_t *sls = - spa_log_sm_alloc(za.za_first_integer, log_txg); + spa_log_sm_alloc(za->za_first_integer, log_txg); avl_add(&spa->spa_sm_logs_by_txg, sls); } zap_cursor_fini(&zc); + zap_attribute_free(za); if (error != ENOENT) { spa_load_failed(spa, "spa_ld_log_sm_metadata(): failed at " "zap_cursor_retrieve(spacemap_zap) [error %d]", diff --git a/module/zfs/spa_misc.c b/module/zfs/spa_misc.c index 97191e7685..f486513fca 100644 --- a/module/zfs/spa_misc.c +++ b/module/zfs/spa_misc.c @@ -2590,6 +2590,7 @@ spa_init(spa_mode_t mode) scan_init(); qat_init(); spa_import_progress_init(); + zap_init(); } void @@ -2616,6 +2617,7 @@ spa_fini(void) scan_fini(); qat_fini(); spa_import_progress_destroy(); + zap_fini(); avl_destroy(&spa_namespace_avl); avl_destroy(&spa_spare_avl); diff --git a/module/zfs/vdev.c b/module/zfs/vdev.c index 6ae0a14127..5098154491 100644 --- a/module/zfs/vdev.c +++ b/module/zfs/vdev.c @@ -6432,33 +6432,33 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) * Get all properties from the MOS vdev property object. */ zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za = zap_attribute_alloc(); for (zap_cursor_init(&zc, mos, objid); - (err = zap_cursor_retrieve(&zc, &za)) == 0; + (err = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { intval = 0; strval = NULL; zprop_source_t src = ZPROP_SRC_DEFAULT; - propname = za.za_name; + propname = za->za_name; - switch (za.za_integer_length) { + switch (za->za_integer_length) { case 8: /* We do not allow integer user properties */ /* This is likely an internal value */ break; case 1: /* string property */ - strval = kmem_alloc(za.za_num_integers, + strval = kmem_alloc(za->za_num_integers, KM_SLEEP); - err = zap_lookup(mos, objid, za.za_name, 1, - za.za_num_integers, strval); + err = zap_lookup(mos, objid, za->za_name, 1, + za->za_num_integers, strval); if (err) { - kmem_free(strval, za.za_num_integers); + kmem_free(strval, za->za_num_integers); break; } vdev_prop_add_list(outnvl, propname, strval, 0, src); - kmem_free(strval, za.za_num_integers); + kmem_free(strval, za->za_num_integers); break; default: @@ -6466,6 +6466,7 @@ vdev_prop_get(vdev_t *vd, nvlist_t *innvl, nvlist_t *outnvl) } } zap_cursor_fini(&zc); + zap_attribute_free(za); } mutex_exit(&spa->spa_props_lock); diff --git a/module/zfs/zap.c b/module/zfs/zap.c index 03b76ea1b7..159c65a0fc 100644 --- a/module/zfs/zap.c +++ b/module/zfs/zap.c @@ -832,7 +832,8 @@ zap_put_leaf_maybe_grow_ptrtbl(zap_name_t *zn, zap_leaf_t *l, static int fzap_checkname(zap_name_t *zn) { - if (zn->zn_key_orig_numints * zn->zn_key_intlen > ZAP_MAXNAMELEN) + uint32_t maxnamelen = zn->zn_normbuf_len; + if (zn->zn_key_orig_numints * zn->zn_key_intlen > maxnamelen) return (SET_ERROR(ENAMETOOLONG)); return (0); } @@ -1102,7 +1103,7 @@ zap_create_link_dnsize(objset_t *os, dmu_object_type_t ot, uint64_t parent_obj, int zap_value_search(objset_t *os, uint64_t zapobj, uint64_t value, uint64_t mask, - char *name) + char *name, uint64_t namelen) { zap_cursor_t zc; int err; @@ -1110,17 +1111,18 @@ zap_value_search(objset_t *os, uint64_t zapobj, uint64_t value, uint64_t mask, if (mask == 0) mask = -1ULL; - zap_attribute_t *za = kmem_alloc(sizeof (*za), KM_SLEEP); + zap_attribute_t *za = zap_attribute_long_alloc(); for (zap_cursor_init(&zc, os, zapobj); (err = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { if ((za->za_first_integer & mask) == (value & mask)) { - (void) strlcpy(name, za->za_name, MAXNAMELEN); + if (strlcpy(name, za->za_name, namelen) >= namelen) + err = SET_ERROR(ENAMETOOLONG); break; } } zap_cursor_fini(&zc); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); return (err); } @@ -1130,7 +1132,7 @@ zap_join(objset_t *os, uint64_t fromobj, uint64_t intoobj, dmu_tx_t *tx) zap_cursor_t zc; int err = 0; - zap_attribute_t *za = kmem_alloc(sizeof (*za), KM_SLEEP); + zap_attribute_t *za = zap_attribute_long_alloc(); for (zap_cursor_init(&zc, os, fromobj); zap_cursor_retrieve(&zc, za) == 0; (void) zap_cursor_advance(&zc)) { @@ -1144,7 +1146,7 @@ zap_join(objset_t *os, uint64_t fromobj, uint64_t intoobj, dmu_tx_t *tx) break; } zap_cursor_fini(&zc); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); return (err); } @@ -1155,7 +1157,7 @@ zap_join_key(objset_t *os, uint64_t fromobj, uint64_t intoobj, zap_cursor_t zc; int err = 0; - zap_attribute_t *za = kmem_alloc(sizeof (*za), KM_SLEEP); + zap_attribute_t *za = zap_attribute_long_alloc(); for (zap_cursor_init(&zc, os, fromobj); zap_cursor_retrieve(&zc, za) == 0; (void) zap_cursor_advance(&zc)) { @@ -1169,7 +1171,7 @@ zap_join_key(objset_t *os, uint64_t fromobj, uint64_t intoobj, break; } zap_cursor_fini(&zc); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); return (err); } @@ -1180,7 +1182,7 @@ zap_join_increment(objset_t *os, uint64_t fromobj, uint64_t intoobj, zap_cursor_t zc; int err = 0; - zap_attribute_t *za = kmem_alloc(sizeof (*za), KM_SLEEP); + zap_attribute_t *za = zap_attribute_long_alloc(); for (zap_cursor_init(&zc, os, fromobj); zap_cursor_retrieve(&zc, za) == 0; (void) zap_cursor_advance(&zc)) { @@ -1200,7 +1202,7 @@ zap_join_increment(objset_t *os, uint64_t fromobj, uint64_t intoobj, break; } zap_cursor_fini(&zc); - kmem_free(za, sizeof (*za)); + zap_attribute_free(za); return (err); } @@ -1378,7 +1380,7 @@ again: ASSERT(err == 0 || err == EOVERFLOW); } err = zap_entry_read_name(zap, &zeh, - sizeof (za->za_name), za->za_name); + za->za_name_len, za->za_name); ASSERT(err == 0); za->za_normalization_conflict = diff --git a/module/zfs/zap_micro.c b/module/zfs/zap_micro.c index 0d6533b0e1..a428a040a4 100644 --- a/module/zfs/zap_micro.c +++ b/module/zfs/zap_micro.c @@ -131,12 +131,12 @@ zap_hash(zap_name_t *zn) } static int -zap_normalize(zap_t *zap, const char *name, char *namenorm, int normflags) +zap_normalize(zap_t *zap, const char *name, char *namenorm, int normflags, + size_t outlen) { ASSERT(!(zap_getflags(zap) & ZAP_FLAG_UINT64_KEY)); size_t inlen = strlen(name) + 1; - size_t outlen = ZAP_MAXNAMELEN; int err = 0; (void) u8_textprep_str((char *)name, &inlen, namenorm, &outlen, @@ -149,43 +149,104 @@ zap_normalize(zap_t *zap, const char *name, char *namenorm, int normflags) boolean_t zap_match(zap_name_t *zn, const char *matchname) { + boolean_t res = B_FALSE; ASSERT(!(zap_getflags(zn->zn_zap) & ZAP_FLAG_UINT64_KEY)); if (zn->zn_matchtype & MT_NORMALIZE) { - char norm[ZAP_MAXNAMELEN]; + size_t namelen = zn->zn_normbuf_len; + char normbuf[ZAP_MAXNAMELEN]; + char *norm = normbuf; + + /* + * Cannot allocate this on-stack as it exceed the stack-limit of + * 1024. + */ + if (namelen > ZAP_MAXNAMELEN) + norm = kmem_alloc(namelen, KM_SLEEP); if (zap_normalize(zn->zn_zap, matchname, norm, - zn->zn_normflags) != 0) - return (B_FALSE); - - return (strcmp(zn->zn_key_norm, norm) == 0); + zn->zn_normflags, namelen) != 0) { + res = B_FALSE; + } else { + res = (strcmp(zn->zn_key_norm, norm) == 0); + } + if (norm != normbuf) + kmem_free(norm, namelen); } else { - return (strcmp(zn->zn_key_orig, matchname) == 0); + res = (strcmp(zn->zn_key_orig, matchname) == 0); } + return (res); +} + +static kmem_cache_t *zap_name_cache; +static kmem_cache_t *zap_attr_cache; +static kmem_cache_t *zap_name_long_cache; +static kmem_cache_t *zap_attr_long_cache; + +void +zap_init(void) +{ + zap_name_cache = kmem_cache_create("zap_name", + sizeof (zap_name_t) + ZAP_MAXNAMELEN, 0, NULL, NULL, + NULL, NULL, NULL, 0); + + zap_attr_cache = kmem_cache_create("zap_attr_cache", + sizeof (zap_attribute_t) + ZAP_MAXNAMELEN, 0, NULL, + NULL, NULL, NULL, NULL, 0); + + zap_name_long_cache = kmem_cache_create("zap_name_long", + sizeof (zap_name_t) + ZAP_MAXNAMELEN_NEW, 0, NULL, NULL, + NULL, NULL, NULL, 0); + + zap_attr_long_cache = kmem_cache_create("zap_attr_long_cache", + sizeof (zap_attribute_t) + ZAP_MAXNAMELEN_NEW, 0, NULL, + NULL, NULL, NULL, NULL, 0); +} + +void +zap_fini(void) +{ + kmem_cache_destroy(zap_name_cache); + kmem_cache_destroy(zap_attr_cache); + kmem_cache_destroy(zap_name_long_cache); + kmem_cache_destroy(zap_attr_long_cache); } static zap_name_t * -zap_name_alloc(zap_t *zap) +zap_name_alloc(zap_t *zap, boolean_t longname) { - zap_name_t *zn = kmem_alloc(sizeof (zap_name_t), KM_SLEEP); + kmem_cache_t *cache = longname ? zap_name_long_cache : zap_name_cache; + zap_name_t *zn = kmem_cache_alloc(cache, KM_SLEEP); + zn->zn_zap = zap; + zn->zn_normbuf_len = longname ? ZAP_MAXNAMELEN_NEW : ZAP_MAXNAMELEN; return (zn); } void zap_name_free(zap_name_t *zn) { - kmem_free(zn, sizeof (zap_name_t)); + if (zn->zn_normbuf_len == ZAP_MAXNAMELEN) { + kmem_cache_free(zap_name_cache, zn); + } else { + ASSERT3U(zn->zn_normbuf_len, ==, ZAP_MAXNAMELEN_NEW); + kmem_cache_free(zap_name_long_cache, zn); + } } static int zap_name_init_str(zap_name_t *zn, const char *key, matchtype_t mt) { zap_t *zap = zn->zn_zap; + size_t key_len = strlen(key) + 1; + + /* Make sure zn is allocated for longname if key is long */ + IMPLY(key_len > ZAP_MAXNAMELEN, + zn->zn_normbuf_len == ZAP_MAXNAMELEN_NEW); zn->zn_key_intlen = sizeof (*key); zn->zn_key_orig = key; - zn->zn_key_orig_numints = strlen(zn->zn_key_orig) + 1; + zn->zn_key_orig_numints = key_len; zn->zn_matchtype = mt; zn->zn_normflags = zap->zap_normflags; @@ -203,7 +264,7 @@ zap_name_init_str(zap_name_t *zn, const char *key, matchtype_t mt) * what the hash is computed from. */ if (zap_normalize(zap, key, zn->zn_normbuf, - zap->zap_normflags) != 0) + zap->zap_normflags, zn->zn_normbuf_len) != 0) return (SET_ERROR(ENOTSUP)); zn->zn_key_norm = zn->zn_normbuf; zn->zn_key_norm_numints = strlen(zn->zn_key_norm) + 1; @@ -222,7 +283,7 @@ zap_name_init_str(zap_name_t *zn, const char *key, matchtype_t mt) * what the matching is based on. (Not the hash!) */ if (zap_normalize(zap, key, zn->zn_normbuf, - zn->zn_normflags) != 0) + zn->zn_normflags, zn->zn_normbuf_len) != 0) return (SET_ERROR(ENOTSUP)); zn->zn_key_norm_numints = strlen(zn->zn_key_norm) + 1; } @@ -233,7 +294,8 @@ zap_name_init_str(zap_name_t *zn, const char *key, matchtype_t mt) zap_name_t * zap_name_alloc_str(zap_t *zap, const char *key, matchtype_t mt) { - zap_name_t *zn = zap_name_alloc(zap); + size_t key_len = strlen(key) + 1; + zap_name_t *zn = zap_name_alloc(zap, (key_len > ZAP_MAXNAMELEN)); if (zap_name_init_str(zn, key, mt) != 0) { zap_name_free(zn); return (NULL); @@ -244,7 +306,7 @@ zap_name_alloc_str(zap_t *zap, const char *key, matchtype_t mt) static zap_name_t * zap_name_alloc_uint64(zap_t *zap, const uint64_t *key, int numints) { - zap_name_t *zn = kmem_alloc(sizeof (zap_name_t), KM_SLEEP); + zap_name_t *zn = kmem_cache_alloc(zap_name_cache, KM_SLEEP); ASSERT(zap->zap_normflags == 0); zn->zn_zap = zap; @@ -252,6 +314,7 @@ zap_name_alloc_uint64(zap_t *zap, const uint64_t *key, int numints) zn->zn_key_orig = zn->zn_key_norm = key; zn->zn_key_orig_numints = zn->zn_key_norm_numints = numints; zn->zn_matchtype = 0; + zn->zn_normbuf_len = ZAP_MAXNAMELEN; zn->zn_hash = zap_hash(zn); return (zn); @@ -467,7 +530,7 @@ mzap_open(dmu_buf_t *db) zfs_btree_create_custom(&zap->zap_m.zap_tree, mze_compare, mze_find_in_buf, sizeof (mzap_ent_t), 512); - zap_name_t *zn = zap_name_alloc(zap); + zap_name_t *zn = zap_name_alloc(zap, B_FALSE); for (uint16_t i = 0; i < zap->zap_m.zap_num_chunks; i++) { mzap_ent_phys_t *mze = &zap_m_phys(zap)->mz_chunk[i]; @@ -674,7 +737,7 @@ mzap_upgrade(zap_t **zapp, const void *tag, dmu_tx_t *tx, zap_flags_t flags) fzap_upgrade(zap, tx, flags); - zap_name_t *zn = zap_name_alloc(zap); + zap_name_t *zn = zap_name_alloc(zap, B_FALSE); for (int i = 0; i < nchunks; i++) { mzap_ent_phys_t *mze = &mzp->mz_chunk[i]; if (mze->mze_name[0] == 0) @@ -1600,6 +1663,41 @@ zap_remove_uint64_by_dnode(dnode_t *dn, const uint64_t *key, int key_numints, return (err); } + +static zap_attribute_t * +zap_attribute_alloc_impl(boolean_t longname) +{ + zap_attribute_t *za; + + za = kmem_cache_alloc((longname)? zap_attr_long_cache : zap_attr_cache, + KM_SLEEP); + za->za_name_len = (longname)? ZAP_MAXNAMELEN_NEW : ZAP_MAXNAMELEN; + return (za); +} + +zap_attribute_t * +zap_attribute_alloc(void) +{ + return (zap_attribute_alloc_impl(B_FALSE)); +} + +zap_attribute_t * +zap_attribute_long_alloc(void) +{ + return (zap_attribute_alloc_impl(B_TRUE)); +} + +void +zap_attribute_free(zap_attribute_t *za) +{ + if (za->za_name_len == ZAP_MAXNAMELEN) { + kmem_cache_free(zap_attr_cache, za); + } else { + ASSERT3U(za->za_name_len, ==, ZAP_MAXNAMELEN_NEW); + kmem_cache_free(zap_attr_long_cache, za); + } +} + /* * Routines for iterating over the attributes. */ @@ -1736,7 +1834,7 @@ zap_cursor_retrieve(zap_cursor_t *zc, zap_attribute_t *za) za->za_num_integers = 1; za->za_first_integer = mzep->mze_value; (void) strlcpy(za->za_name, mzep->mze_name, - sizeof (za->za_name)); + za->za_name_len); zc->zc_hash = (uint64_t)mze->mze_hash << 32; zc->zc_cd = mze->mze_cd; err = 0; diff --git a/module/zfs/zcp_iter.c b/module/zfs/zcp_iter.c index 2da0bf9740..b4aa7a1317 100644 --- a/module/zfs/zcp_iter.c +++ b/module/zfs/zcp_iter.c @@ -54,7 +54,7 @@ zcp_clones_iter(lua_State *state) uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); dsl_pool_t *dp = zcp_run_info(state)->zri_pool; dsl_dataset_t *ds, *clone; - zap_attribute_t za; + zap_attribute_t *za; zap_cursor_t zc; err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); @@ -75,9 +75,11 @@ zcp_clones_iter(lua_State *state) dsl_dataset_phys(ds)->ds_next_clones_obj, cursor); dsl_dataset_rele(ds, FTAG); - err = zap_cursor_retrieve(&zc, &za); + za = zap_attribute_alloc(); + err = zap_cursor_retrieve(&zc, za); if (err != 0) { zap_cursor_fini(&zc); + zap_attribute_free(za); if (err != ENOENT) { return (luaL_error(state, "unexpected error %d from zap_cursor_retrieve()", @@ -89,7 +91,8 @@ zcp_clones_iter(lua_State *state) cursor = zap_cursor_serialize(&zc); zap_cursor_fini(&zc); - err = dsl_dataset_hold_obj(dp, za.za_first_integer, FTAG, &clone); + err = dsl_dataset_hold_obj(dp, za->za_first_integer, FTAG, &clone); + zap_attribute_free(za); if (err != 0) { return (luaL_error(state, "unexpected error %d from " @@ -499,7 +502,7 @@ zcp_bookmarks_iter(lua_State *state) uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); dsl_pool_t *dp = zcp_run_info(state)->zri_pool; dsl_dataset_t *ds; - zap_attribute_t za; + zap_attribute_t *za; zap_cursor_t zc; int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); @@ -536,9 +539,11 @@ zcp_bookmarks_iter(lua_State *state) ds->ds_bookmarks_obj, cursor); dsl_dataset_rele(ds, FTAG); - err = zap_cursor_retrieve(&zc, &za); + za = zap_attribute_alloc(); + err = zap_cursor_retrieve(&zc, za); if (err != 0) { zap_cursor_fini(&zc); + zap_attribute_free(za); if (err != ENOENT) { return (luaL_error(state, "unexpected error %d from zap_cursor_retrieve()", @@ -552,7 +557,8 @@ zcp_bookmarks_iter(lua_State *state) /* Create the full "pool/fs#bookmark" string to return */ int n = snprintf(bookmark_name, ZFS_MAX_DATASET_NAME_LEN, "%s#%s", - ds_name, za.za_name); + ds_name, za->za_name); + zap_attribute_free(za); if (n >= ZFS_MAX_DATASET_NAME_LEN) { return (luaL_error(state, "unexpected error %d from snprintf()", ENAMETOOLONG)); @@ -610,7 +616,7 @@ zcp_holds_iter(lua_State *state) uint64_t cursor = lua_tonumber(state, lua_upvalueindex(2)); dsl_pool_t *dp = zcp_run_info(state)->zri_pool; dsl_dataset_t *ds; - zap_attribute_t za; + zap_attribute_t *za; zap_cursor_t zc; int err = dsl_dataset_hold_obj(dp, dsobj, FTAG, &ds); @@ -631,9 +637,11 @@ zcp_holds_iter(lua_State *state) dsl_dataset_phys(ds)->ds_userrefs_obj, cursor); dsl_dataset_rele(ds, FTAG); - err = zap_cursor_retrieve(&zc, &za); + za = zap_attribute_alloc(); + err = zap_cursor_retrieve(&zc, za); if (err != 0) { zap_cursor_fini(&zc); + zap_attribute_free(za); if (err != ENOENT) { return (luaL_error(state, "unexpected error %d from zap_cursor_retrieve()", @@ -648,8 +656,9 @@ zcp_holds_iter(lua_State *state) lua_pushnumber(state, cursor); lua_replace(state, lua_upvalueindex(2)); - (void) lua_pushstring(state, za.za_name); - (void) lua_pushnumber(state, za.za_first_integer); + (void) lua_pushstring(state, za->za_name); + (void) lua_pushnumber(state, za->za_first_integer); + zap_attribute_free(za); return (2); } diff --git a/module/zfs/zfeature.c b/module/zfs/zfeature.c index 1d25bc4068..38479c7225 100644 --- a/module/zfs/zfeature.c +++ b/module/zfs/zfeature.c @@ -183,7 +183,7 @@ spa_features_check(spa_t *spa, boolean_t for_write, char *buf; zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); - za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + za = zap_attribute_alloc(); buf = kmem_alloc(MAXPATHLEN, KM_SLEEP); supported = B_TRUE; @@ -217,7 +217,7 @@ spa_features_check(spa_t *spa, boolean_t for_write, zap_cursor_fini(zc); kmem_free(buf, MAXPATHLEN); - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); kmem_free(zc, sizeof (zap_cursor_t)); return (supported); diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c index 897335dd4e..8e16a01993 100644 --- a/module/zfs/zfs_ioctl.c +++ b/module/zfs/zfs_ioctl.c @@ -2567,6 +2567,44 @@ zfs_prop_set_special(const char *dsname, zprop_source_t source, } break; } + case ZFS_PROP_LONGNAME: + { + zfsvfs_t *zfsvfs; + dsl_dataset_t *ds; + + /* + * Ignore the checks if the property is being applied as part of + * 'zfs receive'. Because, we already check if the local pool + * has SPA_FEATURE_LONGNAME enabled in dmu_recv_begin_check(). + */ + if (source == ZPROP_SRC_RECEIVED) { + cmn_err(CE_NOTE, "Skipping ZFS_PROP_LONGNAME checks " + "for dsname=%s\n", dsname); + err = -1; + break; + } + + if ((err = zfsvfs_hold(dsname, FTAG, &zfsvfs, B_FALSE)) != 0) { + cmn_err(CE_WARN, "%s:%d Failed to hold for dsname=%s " + "err=%d\n", __FILE__, __LINE__, dsname, err); + break; + } + + ds = dmu_objset_ds(zfsvfs->z_os); + + if (!spa_feature_is_enabled(zfsvfs->z_os->os_spa, + SPA_FEATURE_LONGNAME)) { + err = ENOTSUP; + } else { + /* + * Set err to -1 to force the zfs_set_prop_nvlist code + * down the default path to set the value in the nvlist. + */ + err = -1; + } + zfsvfs_rele(zfsvfs, FTAG); + break; + } default: err = -1; } diff --git a/module/zfs/zfs_quota.c b/module/zfs/zfs_quota.c index 9b351eefc0..0fe152bc4d 100644 --- a/module/zfs/zfs_quota.c +++ b/module/zfs/zfs_quota.c @@ -165,7 +165,7 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, { int error; zap_cursor_t zc; - zap_attribute_t za; + zap_attribute_t *za; zfs_useracct_t *buf = vbuf; uint64_t obj; int offset = 0; @@ -196,8 +196,9 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, type == ZFS_PROP_PROJECTOBJUSED) offset = DMU_OBJACCT_PREFIX_LEN; + za = zap_attribute_alloc(); for (zap_cursor_init_serialized(&zc, zfsvfs->z_os, obj, *cookiep); - (error = zap_cursor_retrieve(&zc, &za)) == 0; + (error = zap_cursor_retrieve(&zc, za)) == 0; zap_cursor_advance(&zc)) { if ((uintptr_t)buf - (uintptr_t)vbuf + sizeof (zfs_useracct_t) > *bufsizep) @@ -207,14 +208,14 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, * skip object quota (with zap name prefix DMU_OBJACCT_PREFIX) * when dealing with block quota and vice versa. */ - if ((offset > 0) != (strncmp(za.za_name, DMU_OBJACCT_PREFIX, + if ((offset > 0) != (strncmp(za->za_name, DMU_OBJACCT_PREFIX, DMU_OBJACCT_PREFIX_LEN) == 0)) continue; - fuidstr_to_sid(zfsvfs, za.za_name + offset, + fuidstr_to_sid(zfsvfs, za->za_name + offset, buf->zu_domain, sizeof (buf->zu_domain), &buf->zu_rid); - buf->zu_space = za.za_first_integer; + buf->zu_space = za->za_first_integer; buf++; } if (error == ENOENT) @@ -224,6 +225,7 @@ zfs_userspace_many(zfsvfs_t *zfsvfs, zfs_userquota_prop_t type, *bufsizep = (uintptr_t)buf - (uintptr_t)vbuf; *cookiep = zap_cursor_serialize(&zc); zap_cursor_fini(&zc); + zap_attribute_free(za); return (error); } diff --git a/module/zfs/zvol.c b/module/zfs/zvol.c index 001f774a6d..fec595b2c4 100644 --- a/module/zfs/zvol.c +++ b/module/zfs/zvol.c @@ -1034,7 +1034,7 @@ zvol_add_clones(const char *dsname, list_t *minors_list) goto out; zap_cursor_t *zc = kmem_alloc(sizeof (zap_cursor_t), KM_SLEEP); - zap_attribute_t *za = kmem_alloc(sizeof (zap_attribute_t), KM_SLEEP); + zap_attribute_t *za = zap_attribute_alloc(); objset_t *mos = dd->dd_pool->dp_meta_objset; for (zap_cursor_init(zc, mos, dsl_dir_phys(dd)->dd_clones); @@ -1060,7 +1060,7 @@ zvol_add_clones(const char *dsname, list_t *minors_list) } } zap_cursor_fini(zc); - kmem_free(za, sizeof (zap_attribute_t)); + zap_attribute_free(za); kmem_free(zc, sizeof (zap_cursor_t)); out: diff --git a/tests/runfiles/linux.run b/tests/runfiles/linux.run index 5817e64900..39cc302504 100644 --- a/tests/runfiles/linux.run +++ b/tests/runfiles/linux.run @@ -138,6 +138,10 @@ pre = post = tags = ['functional', 'largest_pool'] +[tests/functional/longname:Linux] +tests = ['longname_001_pos', 'longname_002_pos', 'longname_003_pos'] +tags = ['functional', 'longname'] + [tests/functional/mmap:Linux] tests = ['mmap_libaio_001_pos', 'mmap_sync_001_pos'] tags = ['functional', 'mmap'] @@ -182,7 +186,7 @@ tests = ['renameat2_noreplace', 'renameat2_exchange', 'renameat2_whiteout'] tags = ['functional', 'renameat2'] [tests/functional/rsend:Linux] -tests = ['send_realloc_dnode_size', 'send_encrypted_files'] +tests = ['send_realloc_dnode_size', 'send_encrypted_files', 'send-c_longname'] tags = ['functional', 'rsend'] [tests/functional/simd:Linux] diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 9dcb097e2b..f4cef05a1c 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -1571,6 +1571,11 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/link_count/link_count_001.ksh \ functional/link_count/link_count_root_inode.ksh \ functional/link_count/setup.ksh \ + functional/longname/cleanup.ksh \ + functional/longname/longname_001_pos.ksh \ + functional/longname/longname_002_pos.ksh \ + functional/longname/longname_003_pos.ksh \ + functional/longname/setup.ksh \ functional/log_spacemap/log_spacemap_import_logs.ksh \ functional/migration/cleanup.ksh \ functional/migration/migration_001_pos.ksh \ @@ -1904,6 +1909,7 @@ nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ functional/rsend/rsend_031_pos.ksh \ functional/rsend/send-c_embedded_blocks.ksh \ functional/rsend/send-c_incremental.ksh \ + functional/rsend/send-c_longname.ksh \ functional/rsend/send-c_lz4_disabled.ksh \ functional/rsend/send-c_mixed_compression.ksh \ functional/rsend/send-c_props.ksh \ diff --git a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg index e8a94ce209..92d9027960 100644 --- a/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg +++ b/tests/zfs-tests/tests/functional/cli_root/zpool_get/zpool_get.cfg @@ -109,5 +109,6 @@ if is_linux || is_freebsd; then "feature@block_cloning" "feature@vdev_zaps_v2" "feature@raidz_expansion" + "feature@longname" ) fi diff --git a/tests/zfs-tests/tests/functional/longname/cleanup.ksh b/tests/zfs-tests/tests/functional/longname/cleanup.ksh new file mode 100755 index 0000000000..aac8062ec4 --- /dev/null +++ b/tests/zfs-tests/tests/functional/longname/cleanup.ksh @@ -0,0 +1,34 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2021 by Nutanix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +default_cleanup diff --git a/tests/zfs-tests/tests/functional/longname/longname_001_pos.ksh b/tests/zfs-tests/tests/functional/longname/longname_001_pos.ksh new file mode 100755 index 0000000000..e7ecd2351c --- /dev/null +++ b/tests/zfs-tests/tests/functional/longname/longname_001_pos.ksh @@ -0,0 +1,139 @@ +#! /bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2021 by Nutanix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Verify the support for long filenames. +# +# STRATEGY: +# 0. On a fresh dataset ensure property "longname" is enabled by default. +# 1. Disable the longname. +# 2. Try to create a filename whose length is > 256 bytes. This should fail. +# 3. Enable "longname" property on the dataset. +# 4. Try to create files and dirs whose names are > 256 bytes. +# 5. Ensure that "ls" is able to list the file. +# 6. Ensure stat(1) is able to stat the file/directory. +# 7. Try to rename the files and directories +# 8. Try to delete the files and directories + +verify_runnable "global" + +WORKDIR=$TESTDIR/workdir +MOVEDIR=$TESTDIR/movedir/level2/level3/level4/level5/level6 + +function cleanup +{ + log_must rm -rf $WORKDIR + log_must rm -rf $TESTDIR/movedir +} + +LONGNAME=$(printf 'a%.0s' {1..512}) +LONGFNAME="file-$LONGNAME" +LONGDNAME="dir-$LONGNAME" +LONGPNAME="mypipe-$LONGNAME" +LONGCNAME="char_dev-$LONGNAME" +LONGLNAME="link-$LONGNAME" +LONGNAME_255=$(printf 'a%.0s' {1..255}) +LONGNAME_1023=$(printf 'a%.0s' {1..1023}) + + +log_assert "Check longname support for directories/files" + +log_onexit cleanup + +log_must mkdir $WORKDIR +log_must mkdir -p $MOVEDIR + +# Disable longname support +log_must zfs set longname=off $TESTPOOL/$TESTFS + +#Ensure a file of length 255bytes can be created +log_must touch $WORKDIR/$LONGNAME_255 + +#Where as file of length 256bytes should fail +long_mustnot touch $WORKDIR/${LONGNAME_255}b + +# Try to create a file with long name with property "longname=off" +log_mustnot touch $WORKDIR/$LONGFNAME +log_mustnot mkdir $WORKDIR/$LONGDNAME + +# Enable longname support +log_must zfs set longname=on $TESTPOOL/$TESTFS + +# Retry the longname creates and that should succeed +log_must mkdir $WORKDIR/$LONGDNAME +log_must touch $WORKDIR/$LONGFNAME + +# Should be able to create a file with name of 1023 chars +log_must touch $WORKDIR/$LONGNAME_1023 + +# And names longer that 1023 should fail +log_mustnot touch $WORKDIR/${LONGNAME_1023}b + +# Ensure the longnamed dir/file can be listed. +name=$(ls $WORKDIR/$LONGFNAME) +if [[ "${name}" != "$WORKDIR/$LONGFNAME" ]]; then + log_fail "Unable to list: $WORKDIR/$LONGFNAME ret:$name" +fi + +name=$(ls -d $WORKDIR/$LONGDNAME) +if [[ "${name}" != "$WORKDIR/$LONGDNAME" ]]; then + log_fail "Unable to list: $WORKDIR/$LONGDNAME ret:$name" +fi + +# Ensure stat works +log_must stat $WORKDIR/$LONGFNAME +log_must stat $WORKDIR/$LONGDNAME + +# Ensure softlinks can be created from a longname to +# another longnamed file. +log_must ln -s $WORKDIR/$LONGFNAME $WORKDIR/$LONGLNAME + +# Ensure a longnamed pipe and character device file +# can be created +log_must mknod $WORKDIR/$LONGPNAME p +log_must mknod $WORKDIR/$LONGCNAME c 92 1 + +# Fetch the inode numbers of the objects created. +INODE_NO_FILE=$(stat -c %i $WORKDIR/$LONGFNAME) +INODE_NO_DIR=$(stat -c %i $WORKDIR/$LONGDNAME) +INODE_NO_LINK=$(stat -c %i $WORKDIR/$LONGLNAME) +INODE_NO_PIPE=$(stat -c %i $WORKDIR/$LONGPNAME) +INODE_NO_CHAR=$(stat -c %i $WORKDIR/$LONGCNAME) + +# Ensure we can rename the longname file +log_must mv $WORKDIR/$LONGFNAME $WORKDIR/file2 + +# Delete the long named dir/file +log_must rmdir $WORKDIR/$LONGDNAME +log_must rm $WORKDIR/file2 +log_must rm $WORKDIR/$LONGPNAME +log_must rm $WORKDIR/$LONGCNAME +log_must rm $WORKDIR/$LONGLNAME + +log_pass diff --git a/tests/zfs-tests/tests/functional/longname/longname_002_pos.ksh b/tests/zfs-tests/tests/functional/longname/longname_002_pos.ksh new file mode 100755 index 0000000000..dd2acabb3e --- /dev/null +++ b/tests/zfs-tests/tests/functional/longname/longname_002_pos.ksh @@ -0,0 +1,115 @@ +#! /bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2021 by Nutanix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Check if longname feature is disabled by default and can be enabled. +# +# STRATEGY: +# 1. Create a zpool with longname feature disabled +# 2. Attempt to enable 'longname' property should fail. +# 3. Attempt to create a longnamed (>255) file should fail. +# 4. Enable the feature@longname +# 5. Enable 'longname' property on the dataset. +# 6. Should be able to create long named files and directories. +# 7. Should be able to disable longname property. +# 8. This should disallow creating new longnamed file/dirs. But, should be +# able to access existing longnamed files/dirs. +verify_runnable "global" + +function cleanup +{ + log_must rm -rf $WORKDIR + poolexists $TESTPOOL && zpool destroy $TESTPOOL +} + +log_assert "Check feature@longname and 'longname' dataset propery work correctly" + +log_onexit cleanup + +log_must zpool destroy $TESTPOOL + +log_must zpool create -o feature@longname=disabled $TESTPOOL $DISKS + +log_must zfs create $TESTPOOL/$TESTFS2 + +log_must zfs set mountpoint=$TESTDIR2 $TESTPOOL/$TESTFS2 + +log_mustnot zfs set longname=on $TESTPOOL/$TESTFS2 + +LONGNAME=$(printf 'a%.0s' {1..512}) +LONGFNAME="file-$LONGNAME" +LONGDNAME="dir-$LONGNAME" +SHORTDNAME="dir-short" +SHORTFNAME="file-short" +WORKDIR=$TESTDIR2/workdir + +log_must mkdir $WORKDIR +log_mustnot touch $WORKDIR/$LONGFNAME +log_mustnot mkdir $WORKDIR/$LONGDNAME + +log_must zpool set feature@longname=enabled $TESTPOOL +log_must zfs set longname=on $TESTPOOL/$TESTFS2 + +log_must mkdir $WORKDIR/$LONGDNAME +log_must touch $WORKDIR/$LONGFNAME + +# Ensure the above changes are synced out. +log_must zpool sync $TESTPOOL + +# Ensure that the feature is activated once longnamed files are created. +state=$(zpool get feature@longname -H -o value $TESTPOOL) +log_note "feature@longname on pool: $TESTPOOL : $state" + +if [[ "$state" != "active" ]]; then + log_fail "feature@longname has state $state (expected active)" +fi + +# Set longname=off. +log_must zfs set longname=off $TESTPOOL/$TESTFS2 + +# Ensure no new file/directory with longnames can be created or can be renamed +# to. +log_mustnot mkdir $WORKDIR/${LONGDNAME}.1 +log_mustnot touch $WORKDIR/${LONGFNAME}.1 +log_must mkdir $WORKDIR/$SHORTDNAME +log_mustnot mv $WORKDIR/$SHORTDNAME $WORKDIR/${LONGDNAME}.1 +log_must touch $WORKDIR/$SHORTFNAME +log_mustnot mv $WORKDIR/$SHORTFNAME $WORKDIR/${LONGFNAME}.1 + +#Cleanup shortnames +log_must rmdir $WORKDIR/$SHORTDNAME +log_must rm $WORKDIR/$SHORTFNAME + +# But, should be able to stat and rename existing files +log_must stat $WORKDIR/$LONGDNAME +log_must stat $WORKDIR/$LONGFNAME +log_must mv $WORKDIR/$LONGDNAME $WORKDIR/$SHORTDNAME +log_must mv $WORKDIR/$LONGFNAME $WORKDIR/$SHORTFNAME + +log_pass diff --git a/tests/zfs-tests/tests/functional/longname/longname_003_pos.ksh b/tests/zfs-tests/tests/functional/longname/longname_003_pos.ksh new file mode 100755 index 0000000000..f684b51439 --- /dev/null +++ b/tests/zfs-tests/tests/functional/longname/longname_003_pos.ksh @@ -0,0 +1,113 @@ +#! /bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright (c) 2021 by Nutanix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +# +# DESCRIPTION: +# Check if longnames are handled correctly by ZIL replay and feature is activated. +# +# STRATEGY: +# 1. Create a zpool with longname feature disabled +# 2. Enable the feature@longname +# 3. Enable 'longname' property on the dataset. +# 4. Freeze the zpool +# 5. Create a longname +# 6. Export and import the zpool. +# 7. Replaying of longname create should activate the feature@longname +verify_runnable "global" + +function cleanup +{ + log_must rm -rf $WORKDIR + poolexists $TESTPOOL && zpool destroy $TESTPOOL +} + +log_assert "Check feature@longname and 'longname' dataset propery work correctly" + +log_onexit cleanup + +poolexists $TESTPOOL && zpool destroy $TESTPOOL + +log_must zpool create -o feature@longname=disabled $TESTPOOL $DISKS + +log_must zfs create $TESTPOOL/$TESTFS + +log_must zfs set mountpoint=$TESTDIR $TESTPOOL/$TESTFS + +LONGNAME=$(printf 'a%.0s' {1..512}) +LONGFNAME="file-$LONGNAME" +LONGDNAME="dir-$LONGNAME" +SHORTDNAME="dir-short" +SHORTFNAME="file-short" +WORKDIR=$TESTDIR/workdir + +log_must mkdir $WORKDIR + +log_must zpool set feature@longname=enabled $TESTPOOL +log_must zfs set longname=on $TESTPOOL/$TESTFS + +# Ensure that the feature is NOT activated yet as no longnamed file is created. +state=$(zpool get feature@longname -H -o value $TESTPOOL) +log_note "feature@longname on pool: $TESTPOOL : $state" + +if [[ "$state" != "enabled" ]]; then + log_fail "feature@longname has state $state (expected enabled)" +fi + +# +# This dd command works around an issue where ZIL records aren't created +# after freezing the pool unless a ZIL header already exists. Create a file +# synchronously to force ZFS to write one out. +# +log_must dd if=/dev/zero of=/$WORKDIR/sync conv=fdatasync,fsync bs=1 count=1 + +log_must zpool freeze $TESTPOOL + +log_must mkdir $WORKDIR/$LONGDNAME +log_must touch $WORKDIR/$LONGFNAME + +# Export and re-import the zpool +log_must zpool export $TESTPOOL +log_must zpool import $TESTPOOL + +# Ensure that the feature is activated once longnamed files are created. +state=$(zpool get feature@longname -H -o value $TESTPOOL) +log_note "feature@longname on pool: $TESTPOOL : $state" +if [[ "$state" != "active" ]]; then + log_fail "feature@longname has state $state (expected active)" +fi + +# Destroying the dataset where the feature is activated should put the feature +# back to 'enabled' state +log_must zfs destroy -r $TESTPOOL/$TESTFS +state=$(zpool get feature@longname -H -o value $TESTPOOL) +log_note "feature@longname on pool: $TESTPOOL : $state" +if [[ "$state" != "enabled" ]]; then + log_fail "feature@longname has state $state (expected active)" +fi + +log_pass diff --git a/tests/zfs-tests/tests/functional/longname/setup.ksh b/tests/zfs-tests/tests/functional/longname/setup.ksh new file mode 100755 index 0000000000..3f6759469b --- /dev/null +++ b/tests/zfs-tests/tests/functional/longname/setup.ksh @@ -0,0 +1,35 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or http://www.opensolaris.org/os/licensing. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2007 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +# +# Copyright (c) 2021 by Nutanix. All rights reserved. +# + +. $STF_SUITE/include/libtest.shlib + +DISK=${DISKS%% *} +default_setup $DISK diff --git a/tests/zfs-tests/tests/functional/rsend/rsend.kshlib b/tests/zfs-tests/tests/functional/rsend/rsend.kshlib index 26e7c2cc25..0b2984c59d 100644 --- a/tests/zfs-tests/tests/functional/rsend/rsend.kshlib +++ b/tests/zfs-tests/tests/functional/rsend/rsend.kshlib @@ -720,6 +720,7 @@ function stream_has_features feature[resuming]="100000" feature[redacted]="200000" feature[compressed]="400000" + feature[longname]="10000000" typeset flag known derived=0 for flag in "$@"; do diff --git a/tests/zfs-tests/tests/functional/rsend/send-c_longname.ksh b/tests/zfs-tests/tests/functional/rsend/send-c_longname.ksh new file mode 100755 index 0000000000..3f7edc6810 --- /dev/null +++ b/tests/zfs-tests/tests/functional/rsend/send-c_longname.ksh @@ -0,0 +1,98 @@ +#!/bin/ksh -p + +# +# This file and its contents are supplied under the terms of the +# Common Development and Distribution License ("CDDL"), version 1.0. +# You may only use this file in accordance with the terms of version +# 1.0 of the CDDL. +# +# A full copy of the text of the CDDL should have accompanied this +# source. A copy of the CDDL is also available via the Internet at +# http://www.illumos.org/license/CDDL. +# + +# +# Copyright (c) 2021 by Nutanix. All rights reserved. +# + +. $STF_SUITE/tests/functional/rsend/rsend.kshlib +. $STF_SUITE/include/properties.shlib + +# +# Description: +# Verify that longname featureflag is present in the stream. +# +# Strategy: +# 1. Create a filesystem with longnamed files/directories. +# 2. Verify that the sendstream has the longname featureflag is present in the +# send stream. +# 3. Verify the created streams can be received correctly. +# 4. Verify that the longnamed files/directories are present in the received +# filesystem. +# + +verify_runnable "both" + +log_assert "Verify that longnames are handled correctly in send stream." +log_onexit cleanup_pool $POOL $POOL2 $POOL3 + +typeset sendfs=$POOL/sendfs +typeset recvfs=$POOL2/recvfs +typeset recvfs3=$POOL3/recvfs +typeset stream=$BACKDIR/stream +typeset dump=$BACKDIR/dump + +log_must zfs create -o longname=on $sendfs +typeset dir=$(get_prop mountpoint $sendfs) + +# Create a longnamed dir and a file in the send dataset +LONGNAME=$(printf 'a%.0s' {1..512}) +LONGFNAME="file-$LONGNAME" +LONGDNAME="dir-$LONGNAME" +log_must mkdir $dir/$LONGDNAME +log_must touch $dir/$LONGFNAME + +# When POOL3 is created by rsend.kshlib feature@longname is 'enabled'. +# Recreate the POOL3 with feature@longname disabled. +datasetexists $POOL3 && log_must zpool destroy $POOL3 +log_must zpool create -o feature@longname=disabled $POOL3 $DISK3 + +# Generate the streams and zstreamdump output. +log_must zfs snapshot $sendfs@now +log_must eval "zfs send -p $sendfs@now >$stream" +log_must eval "zstream dump -v <$stream >$dump" +log_must eval "zfs recv $recvfs <$stream" +cmp_ds_cont $sendfs $recvfs +log_must stream_has_features $stream longname + +# Ensure the the receiving pool has feature@longname activated after receiving. +feat_val=$(zpool get -H -o value feature@longname $POOL2) +log_note "Zpool $POOL2 feature@longname=$feat_val" +if [[ "$feat_val" != "active" ]]; then + log_fail "pool $POOL2 feature@longname=$feat_val (expected 'active')" +fi + +# Receiving of the stream on $POOL3 should fail as longname is not enabled +log_mustnot eval "zfs recv $recvfs3 <$stream" + +# Enable feature@longname and retry the receiving the stream. +# It should succeed this time. +log_must eval "zpool set feature@longname=enabled $POOL3" +log_must eval "zfs recv $recvfs3 <$stream" + +log_must zfs get longname $recvfs3 +prop_val=$(zfs get -H -o value longname $recvfs3) +log_note "dataset $recvfs3 has longname=$prop_val" +if [[ "$prop_val" != "on" ]]; then + log_fail "$recvfs3 has longname=$prop_val (expected 'on')" +fi + +# +# TODO: +# - Add a testcase to cover the case where send-stream does not contain +# properties (generated without "-p"). +# In this case the target dataset would have longname files/directories which +# cannot be accessed if the dataset property 'longname=off'. +# + +log_pass "Longnames are handled correctly in send/recv"