From 665afcfbb3e59dc73dc25ae59cb488d5d150f8ad Mon Sep 17 00:00:00 2001 From: Chunwei Chen Date: Mon, 23 Sep 2024 22:54:17 +0000 Subject: [PATCH] Support for longnames for files/directories (FreeBSD part/Doesn't work) Note, FreeBSD vfs layer imposes a limit of 255 name lengh, so even though we add code to support it here, it won't actually work. Signed-off-by: Chunwei Chen --- include/os/freebsd/zfs/sys/zfs_vfsops_os.h | 1 + module/os/freebsd/zfs/zfs_dir.c | 9 ++++++++ module/os/freebsd/zfs/zfs_vfsops.c | 13 ++++++++++- module/os/freebsd/zfs/zfs_vnops_os.c | 25 +++++++++++++++++++++- 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/include/os/freebsd/zfs/sys/zfs_vfsops_os.h b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h index 3e54f3e846f7..9fbca35cde8c 100644 --- a/include/os/freebsd/zfs/sys/zfs_vfsops_os.h +++ b/include/os/freebsd/zfs/sys/zfs_vfsops_os.h @@ -83,6 +83,7 @@ struct zfsvfs { boolean_t z_use_sa; /* version allow system attributes */ boolean_t z_xattr_sa; /* allow xattrs to be stores as SA */ boolean_t z_use_namecache; /* make use of FreeBSD name cache */ + boolean_t z_longname; /* Dataset supports long names */ uint8_t z_xattr; /* xattr type in use */ uint64_t z_version; /* ZPL version */ uint64_t z_shares_dir; /* hidden shares dir */ diff --git a/module/os/freebsd/zfs/zfs_dir.c b/module/os/freebsd/zfs/zfs_dir.c index 1ac0ab1ed384..4cf589a79638 100644 --- a/module/os/freebsd/zfs/zfs_dir.c +++ b/module/os/freebsd/zfs/zfs_dir.c @@ -581,6 +581,7 @@ zfs_link_create(znode_t *dzp, const char *name, znode_t *zp, dmu_tx_t *tx, { zfsvfs_t *zfsvfs = zp->z_zfsvfs; vnode_t *vp = ZTOV(zp); + dsl_dataset_t *ds = dmu_objset_ds(zfsvfs->z_os); uint64_t value; int zp_is_dir = (vp->v_type == VDIR); sa_bulk_attr_t bulk[5]; @@ -627,6 +628,14 @@ zfs_link_create(znode_t *dzp, const char *name, znode_t *zp, dmu_tx_t *tx, return (error); } + /* + * If we added a longname activate the SPA_FEATURE_LONGNAME. + */ + if (strlen(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/freebsd/zfs/zfs_vfsops.c b/module/os/freebsd/zfs/zfs_vfsops.c index b96393df4626..a3fac1636981 100644 --- a/module/os/freebsd/zfs/zfs_vfsops.c +++ b/module/os/freebsd/zfs/zfs_vfsops.c @@ -614,6 +614,14 @@ acl_type_changed_cb(void *arg, uint64_t newval) zfsvfs->z_acl_type = newval; } +static void +longname_changed_cb(void *arg, uint64_t newval) +{ + zfsvfs_t *zfsvfs = arg; + + zfsvfs->z_longname = newval; +} + static int zfs_register_callbacks(vfs_t *vfsp) { @@ -751,6 +759,8 @@ zfs_register_callbacks(vfs_t *vfsp) error = error ? error : dsl_prop_register(ds, zfs_prop_to_name(ZFS_PROP_ACLINHERIT), acl_inherit_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; @@ -1489,7 +1499,8 @@ zfs_statfs(vfs_t *vfsp, struct statfs *statp) strlcpy(statp->f_mntonname, vfsp->mnt_stat.f_mntonname, sizeof (statp->f_mntonname)); - statp->f_namemax = MAXNAMELEN - 1; + statp->f_namemax = + zfsvfs->z_longname ? (ZAP_MAXNAMELEN_NEW - 1) : (MAXNAMELEN - 1); zfs_exit(zfsvfs, FTAG); return (0); diff --git a/module/os/freebsd/zfs/zfs_vnops_os.c b/module/os/freebsd/zfs/zfs_vnops_os.c index 9df1fef60a13..1eb1713dc115 100644 --- a/module/os/freebsd/zfs/zfs_vnops_os.c +++ b/module/os/freebsd/zfs/zfs_vnops_os.c @@ -892,6 +892,14 @@ zfs_lookup(vnode_t *dvp, const char *nm, vnode_t **vpp, return (error); } +static inline bool +is_nametoolong(zfsvfs_t *zfsvfs, const char *name) +{ + size_t dlen = strlen(name); + return ((!zfsvfs->z_longname && dlen >= ZAP_MAXNAMELEN) || + dlen >= ZAP_MAXNAMELEN_NEW); +} + /* * Attempt to create a new entry in a directory. If the entry * already exists, truncate the file if permissible, else return @@ -937,6 +945,9 @@ zfs_create(znode_t *dzp, const char *name, vattr_t *vap, int excl, int mode, vnode_t *dvp = ZTOV(dzp); #endif + if (is_nametoolong(zfsvfs, name)) + return (SET_ERROR(ENAMETOOLONG)); + /* * If we have an ephemeral id, ACL, or XVATTR then * make sure file system is at proper version @@ -1301,6 +1312,9 @@ zfs_mkdir(znode_t *dzp, const char *dirname, vattr_t *vap, znode_t **zpp, ASSERT3U(vap->va_type, ==, VDIR); + if (is_nametoolong(zfsvfs, dirname)) + return (SET_ERROR(ENAMETOOLONG)); + /* * If we have an ephemeral id, ACL, or XVATTR then * make sure file system is at proper version @@ -1616,7 +1630,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(); + zap = zap_attribute_long_alloc(); /* * Initialize the iterator cursor. @@ -3294,6 +3308,9 @@ zfs_rename(znode_t *sdzp, const char *sname, znode_t *tdzp, const char *tname, int error; svp = tvp = NULL; + if (is_nametoolong(tdzp->z_zfsvfs, tname)) + return (SET_ERROR(ENAMETOOLONG)); + if (rflags != 0 || wo_vap != NULL) return (SET_ERROR(EINVAL)); @@ -3358,6 +3375,9 @@ zfs_symlink(znode_t *dzp, const char *name, vattr_t *vap, ASSERT3S(vap->va_type, ==, VLNK); + if (is_nametoolong(zfsvfs, name)) + return (SET_ERROR(ENAMETOOLONG)); + if ((error = zfs_enter_verify_zp(zfsvfs, dzp, FTAG)) != 0) return (error); zilog = zfsvfs->z_log; @@ -3540,6 +3560,9 @@ zfs_link(znode_t *tdzp, znode_t *szp, const char *name, cred_t *cr, ASSERT3S(ZTOV(tdzp)->v_type, ==, VDIR); + if (is_nametoolong(zfsvfs, name)) + return (SET_ERROR(ENAMETOOLONG)); + if ((error = zfs_enter_verify_zp(zfsvfs, tdzp, FTAG)) != 0) return (error); zilog = zfsvfs->z_log;