diff --git a/cmd/zdb/zdb.c b/cmd/zdb/zdb.c
index 3a3a97718846..781887050df2 100644
--- a/cmd/zdb/zdb.c
+++ b/cmd/zdb/zdb.c
@@ -1086,7 +1086,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 *attrp = zap_attribute_alloc();
+ zap_attribute_t *attrp = zap_attribute_long_alloc();
void *prop;
unsigned i;
@@ -1313,7 +1313,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 *attrp = zap_attribute_alloc();
+ zap_attribute_t *attrp = zap_attribute_long_alloc();
const char *typenames[] = {
/* 0 */ "not specified",
/* 1 */ "FIFO",
diff --git a/cmd/zhack.c b/cmd/zhack.c
index 844f57d98989..7b87431e5284 100644
--- a/cmd/zhack.c
+++ b/cmd/zhack.c
@@ -203,7 +203,7 @@ static void
dump_obj(objset_t *os, uint64_t obj, const char *name)
{
zap_cursor_t zc;
- zap_attribute_t *za = zap_attribute_alloc();
+ zap_attribute_t *za = zap_attribute_long_alloc();
(void) printf("%s_obj:\n", name);
diff --git a/include/os/linux/zfs/sys/zfs_vfsops_os.h b/include/os/linux/zfs/sys/zfs_vfsops_os.h
index b4d5db21f5e5..e742e8dc392e 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 830c76e5743a..7298e9d3a15f 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 025567e2183f..cda6c4afcc9e 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 0452f4967440..79521eeb3e8a 100644
--- a/include/sys/zap.h
+++ b/include/sys/zap.h
@@ -306,7 +306,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
@@ -378,6 +378,7 @@ 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);
/*
diff --git a/include/sys/zfs_ioctl.h b/include/sys/zfs_ioctl.h
index 26dfe97604de..d9cfed1f2891 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 2515ba321759..ebe3cf5e1e1b 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/libnvpair/libnvpair.abi b/lib/libnvpair/libnvpair.abi
index ef92f3e9bda6..b99a0d6a3373 100644
--- a/lib/libnvpair/libnvpair.abi
+++ b/lib/libnvpair/libnvpair.abi
@@ -1156,6 +1156,11 @@
+
+
+
+
+
@@ -2536,11 +2541,6 @@
-
-
-
-
-
diff --git a/lib/libuutil/libuutil.abi b/lib/libuutil/libuutil.abi
index e942d24c6531..620f384d8f5b 100644
--- a/lib/libuutil/libuutil.abi
+++ b/lib/libuutil/libuutil.abi
@@ -596,14 +596,11 @@
-
+
-
-
-
-
+
@@ -800,9 +797,16 @@
+
+
+
+
+
+
+
@@ -912,6 +916,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -920,12 +943,23 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -937,8 +971,9 @@
-
+
+
diff --git a/lib/libzfs/libzfs.abi b/lib/libzfs/libzfs.abi
index cdd2f04c2629..875ff12d6d0e 100644
--- a/lib/libzfs/libzfs.abi
+++ b/lib/libzfs/libzfs.abi
@@ -606,7 +606,7 @@
-
+
@@ -654,6 +654,10 @@
+
+
+
+
@@ -1112,14 +1116,11 @@
-
+
-
-
-
-
+
@@ -1824,7 +1825,8 @@
-
+
+
@@ -2832,6 +2834,9 @@
+
+
+
@@ -2844,6 +2849,10 @@
+
+
+
+
@@ -2964,6 +2973,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2979,6 +3006,7 @@
+
@@ -3214,9 +3242,13 @@
+
+
+
+
@@ -3249,6 +3281,7 @@
+
@@ -3340,18 +3373,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
@@ -3394,11 +3418,6 @@
-
-
-
-
-
@@ -3411,10 +3430,6 @@
-
-
-
-
@@ -3432,12 +3447,9 @@
-
-
-
-
-
+
+
@@ -3462,10 +3474,6 @@
-
-
-
-
@@ -3478,13 +3486,6 @@
-
-
-
-
-
-
-
@@ -3794,12 +3795,18 @@
+
+
+
+
+
+
@@ -4232,9 +4239,13 @@
-
-
-
+
+
+
+
+
+
+
@@ -4258,9 +4269,13 @@
-
-
-
+
+
+
+
+
+
+
@@ -4277,6 +4292,10 @@
+
+
+
+
@@ -4316,6 +4335,13 @@
+
+
+
+
+
+
+
@@ -4846,6 +4872,11 @@
+
+
+
+
+
@@ -5889,7 +5920,8 @@
-
+
+
@@ -6778,7 +6810,7 @@
-
+
@@ -6786,7 +6818,7 @@
-
+
@@ -7895,10 +7927,6 @@
-
-
-
-
@@ -8939,8 +8967,8 @@
-
-
+
+
@@ -9017,7 +9045,7 @@
-
+
diff --git a/lib/libzfs_core/libzfs_core.abi b/lib/libzfs_core/libzfs_core.abi
index 5b95c8f779db..ab7231971c26 100644
--- a/lib/libzfs_core/libzfs_core.abi
+++ b/lib/libzfs_core/libzfs_core.abi
@@ -594,14 +594,11 @@
-
+
-
-
-
-
+
@@ -770,6 +767,13 @@
+
+
+
+
+
+
+
@@ -873,12 +877,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -895,8 +929,9 @@
-
+
+
@@ -1119,7 +1154,7 @@
-
+
@@ -1127,7 +1162,7 @@
-
+
diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c
index 9ba44689bad6..c6b6ec5ec00e 100644
--- a/module/os/freebsd/zfs/zfs_vnops_os.c
+++ b/module/os/freebsd/zfs/zfs_vnops_os.c
@@ -1819,7 +1819,7 @@ zfs_readdir(vnode_t *vp, zfs_uio_t *uio, cred_t *cr, int *eofp,
/*
* Grab next entry.
*/
- if ((error = zap_cursor_retrieve(&zc, &zap))) {
+ if ((error = zap_cursor_retrieve(&zc, zap))) {
if ((*eofp = (error == ENOENT)) != 0)
break;
else
diff --git a/module/os/freebsd/zfs/zfs_znode.c b/module/os/freebsd/zfs/zfs_znode.c
index 0d4c94555c6b..b7d1d798dc97 100644
--- a/module/os/freebsd/zfs/zfs_znode.c
+++ b/module/os/freebsd/zfs/zfs_znode.c
@@ -1979,7 +1979,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;
}
@@ -2192,7 +2192,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 ad2ca15e297a..dc03aab2ef96 100644
--- a/module/os/linux/zfs/zfs_dir.c
+++ b/module/os/linux/zfs/zfs_dir.c
@@ -802,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];
@@ -847,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 2015c20d7340..55da2d71e8c5 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 88ff8c7e7df4..9b4f4ceb4013 100644
--- a/module/os/linux/zfs/zfs_vnops_os.c
+++ b/module/os/linux/zfs/zfs_vnops_os.c
@@ -531,6 +531,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
@@ -1537,7 +1577,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_alloc();
+ zap = zap_attribute_long_alloc();
/*
* Initialize the iterator cursor.
diff --git a/module/os/linux/zfs/zfs_znode.c b/module/os/linux/zfs/zfs_znode.c
index b99df188c64b..10a23449edf9 100644
--- a/module/os/linux/zfs/zfs_znode.c
+++ b/module/os/linux/zfs/zfs_znode.c
@@ -2144,6 +2144,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';
@@ -2159,9 +2160,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;
@@ -2185,7 +2187,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;
}
@@ -2216,6 +2219,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 aa80b72e2d7a..e9ca7b13490a 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 ad1753f7a071..6d737d9fc6cb 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/zcommon/zfeature_common.c b/module/zcommon/zfeature_common.c
index 309d9bf14cd4..d9c95b21e72f 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 764993b45e7c..886414de6148 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/dmu_recv.c b/module/zfs/dmu_recv.c
index 54aa60259ea1..eccfed0eb78f 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_send.c b/module/zfs/dmu_send.c
index 37c68528bf95..771718fa53df 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_dataset.c b/module/zfs/dsl_dataset.c
index 09e4583d6c1a..063c71891a98 100644
--- a/module/zfs/dsl_dataset.c
+++ b/module/zfs/dsl_dataset.c
@@ -493,7 +493,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),
diff --git a/module/zfs/dsl_dir.c b/module/zfs/dsl_dir.c
index 5aeff86b948f..1b60fa620b8d 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;
diff --git a/module/zfs/zap.c b/module/zfs/zap.c
index 7b3be5ecabc8..6af7141f57c7 100644
--- a/module/zfs/zap.c
+++ b/module/zfs/zap.c
@@ -750,7 +750,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);
}
@@ -1016,7 +1017,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;
@@ -1024,12 +1025,13 @@ zap_value_search(objset_t *os, uint64_t zapobj, uint64_t value, uint64_t mask,
if (mask == 0)
mask = -1ULL;
- zap_attribute_t *za = zap_attribute_alloc();
+ 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;
}
}
@@ -1044,7 +1046,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 = zap_attribute_alloc();
+ 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)) {
@@ -1069,7 +1071,7 @@ zap_join_key(objset_t *os, uint64_t fromobj, uint64_t intoobj,
zap_cursor_t zc;
int err = 0;
- zap_attribute_t *za = zap_attribute_alloc();
+ 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)) {
@@ -1094,7 +1096,7 @@ zap_join_increment(objset_t *os, uint64_t fromobj, uint64_t intoobj,
zap_cursor_t zc;
int err = 0;
- zap_attribute_t *za = zap_attribute_alloc();
+ 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)) {
diff --git a/module/zfs/zap_micro.c b/module/zfs/zap_micro.c
index 4d472a60b0fd..47104f7e3fc2 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,23 +149,39 @@ 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;
- if (zap_normalize(zn->zn_zap, matchname, norm,
- zn->zn_normflags) != 0)
- return (B_FALSE);
+ /*
+ * Cannot allocate this on-stack as it exceed the stack-limit of
+ * 1024.
+ */
+ if (namelen > ZAP_MAXNAMELEN)
+ norm = kmem_alloc(namelen, KM_SLEEP);
- return (strcmp(zn->zn_key_norm, norm) == 0);
+ if (zap_normalize(zn->zn_zap, matchname, norm,
+ 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()
@@ -177,6 +193,14 @@ zap_init()
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
@@ -184,33 +208,47 @@ zap_fini()
{
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_cache_alloc(zap_name_cache, 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_cache_free(zap_name_cache, zn);
+ 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;
- zn->zn_normbuf_len = ZAP_MAXNAMELEN;
/*
* If we're dealing with a case sensitive lookup on a mixed or
@@ -226,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;
@@ -245,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;
}
@@ -256,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);
@@ -491,7 +530,7 @@ mzap_open(objset_t *os, uint64_t obj, 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];
@@ -701,7 +740,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)
@@ -1527,21 +1566,38 @@ zap_remove_uint64(objset_t *os, uint64_t zapobj, const uint64_t *key,
}
-zap_attribute_t *
-zap_attribute_alloc(void)
+static zap_attribute_t *
+zap_attribute_alloc_impl(boolean_t longname)
{
- uint32_t len = ZAP_MAXNAMELEN;
zap_attribute_t *za;
- za = kmem_cache_alloc(zap_attr_cache, KM_SLEEP);
- za->za_name_len = len;
+ 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()
+{
+ return (zap_attribute_alloc_impl(B_FALSE));
+}
+
+zap_attribute_t *
+zap_attribute_long_alloc()
+{
+ return (zap_attribute_alloc_impl(B_TRUE));
+}
+
void
zap_attribute_free(zap_attribute_t *za)
{
- kmem_cache_free(zap_attr_cache, 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);
+ }
}
/*
diff --git a/module/zfs/zfs_ioctl.c b/module/zfs/zfs_ioctl.c
index b2b06881bdd4..bbdd78dd75b0 100644
--- a/module/zfs/zfs_ioctl.c
+++ b/module/zfs/zfs_ioctl.c
@@ -2566,6 +2566,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/tests/runfiles/linux.run b/tests/runfiles/linux.run
index a0b74ef4a8c6..ddaeaaf3b20e 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 fe9c92108725..24d81147e5b8 100644
--- a/tests/zfs-tests/tests/Makefile.am
+++ b/tests/zfs-tests/tests/Makefile.am
@@ -1552,6 +1552,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 \
@@ -1884,6 +1889,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 6ebce9459190..bb1896a72825 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
@@ -107,5 +107,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 000000000000..aac8062ec408
--- /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 000000000000..e7ecd2351ce4
--- /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 000000000000..dd2acabb3e02
--- /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 000000000000..f684b514399d
--- /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 000000000000..3f6759469b7b
--- /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 26e7c2cc25bc..0b2984c59d4e 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 000000000000..3f7edc6810dd
--- /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"