mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-26 11:19:32 +03:00
20232ecfaa
This patch adds the ability for zfs to support file/dir name up to 1023 bytes. This number is chosen so we can support up to 255 4-byte characters. This new feature is represented by the new feature flag feature@longname. A new dataset property "longname" is also introduced to toggle longname support for each dataset individually. This property can be disabled, even if it contains longname files. In such case, new file cannot be created with longname but existing longname files can still be looked up. Note that, to my knowledge native Linux filesystems don't support name longer than 255 bytes. So there might be programs not able to work with longname. Note that NFS server may needs to use exportfs_get_name to reconnect dentries, and the buffer being passed is limit to NAME_MAX+1 (256). So NFS may not work when longname is enabled. 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. Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <mav@FreeBSD.org> Signed-off-by: Chunwei Chen <david.chen@nutanix.com> Closes #15921
402 lines
9.3 KiB
C
402 lines
9.3 KiB
C
/*
|
|
* 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 https://opensource.org/licenses/CDDL-1.0.
|
|
* 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) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
|
* Copyright (c) 2012, 2018 by Delphix. All rights reserved.
|
|
* Copyright (c) 2014 Integros [integros.com]
|
|
*/
|
|
|
|
/* Portions Copyright 2007 Jeremy Teo */
|
|
/* Portions Copyright 2011 Martin Matuska <mm@FreeBSD.org> */
|
|
|
|
#include <sys/dmu.h>
|
|
#include <sys/dmu_objset.h>
|
|
#include <sys/dmu_tx.h>
|
|
#include <sys/zfs_refcount.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/zap.h>
|
|
#include <sys/zfs_znode.h>
|
|
#include <sys/sa.h>
|
|
#include <sys/zfs_sa.h>
|
|
#include <sys/zfs_stat.h>
|
|
|
|
#include "zfs_prop.h"
|
|
#include "zfs_comutil.h"
|
|
|
|
static int
|
|
zfs_sa_setup(objset_t *osp, sa_attr_type_t **sa_table)
|
|
{
|
|
uint64_t sa_obj = 0;
|
|
int error;
|
|
|
|
error = zap_lookup(osp, MASTER_NODE_OBJ, ZFS_SA_ATTRS, 8, 1, &sa_obj);
|
|
if (error != 0 && error != ENOENT)
|
|
return (error);
|
|
|
|
error = sa_setup(osp, sa_obj, zfs_attr_table, ZPL_END, sa_table);
|
|
return (error);
|
|
}
|
|
|
|
static int
|
|
zfs_grab_sa_handle(objset_t *osp, uint64_t obj, sa_handle_t **hdlp,
|
|
dmu_buf_t **db, const void *tag)
|
|
{
|
|
dmu_object_info_t doi;
|
|
int error;
|
|
|
|
if ((error = sa_buf_hold(osp, obj, tag, db)) != 0)
|
|
return (error);
|
|
|
|
dmu_object_info_from_db(*db, &doi);
|
|
if ((doi.doi_bonus_type != DMU_OT_SA &&
|
|
doi.doi_bonus_type != DMU_OT_ZNODE) ||
|
|
(doi.doi_bonus_type == DMU_OT_ZNODE &&
|
|
doi.doi_bonus_size < sizeof (znode_phys_t))) {
|
|
sa_buf_rele(*db, tag);
|
|
return (SET_ERROR(ENOTSUP));
|
|
}
|
|
|
|
error = sa_handle_get(osp, obj, NULL, SA_HDL_PRIVATE, hdlp);
|
|
if (error != 0) {
|
|
sa_buf_rele(*db, tag);
|
|
return (error);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
zfs_release_sa_handle(sa_handle_t *hdl, dmu_buf_t *db, const void *tag)
|
|
{
|
|
sa_handle_destroy(hdl);
|
|
sa_buf_rele(db, tag);
|
|
}
|
|
|
|
/*
|
|
* Given an object number, return its parent object number and whether
|
|
* or not the object is an extended attribute directory.
|
|
*/
|
|
int
|
|
zfs_obj_to_pobj(objset_t *osp, sa_handle_t *hdl, sa_attr_type_t *sa_table,
|
|
uint64_t *pobjp, int *is_xattrdir)
|
|
{
|
|
uint64_t parent;
|
|
uint64_t pflags;
|
|
uint64_t mode;
|
|
uint64_t parent_mode;
|
|
sa_bulk_attr_t bulk[3];
|
|
sa_handle_t *sa_hdl;
|
|
dmu_buf_t *sa_db;
|
|
int count = 0;
|
|
int error;
|
|
|
|
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_PARENT], NULL,
|
|
&parent, sizeof (parent));
|
|
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_FLAGS], NULL,
|
|
&pflags, sizeof (pflags));
|
|
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL,
|
|
&mode, sizeof (mode));
|
|
|
|
if ((error = sa_bulk_lookup(hdl, bulk, count)) != 0)
|
|
return (error);
|
|
|
|
/*
|
|
* When a link is removed its parent pointer is not changed and will
|
|
* be invalid. There are two cases where a link is removed but the
|
|
* file stays around, when it goes to the delete queue and when there
|
|
* are additional links.
|
|
*/
|
|
error = zfs_grab_sa_handle(osp, parent, &sa_hdl, &sa_db, FTAG);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
error = sa_lookup(sa_hdl, ZPL_MODE, &parent_mode, sizeof (parent_mode));
|
|
zfs_release_sa_handle(sa_hdl, sa_db, FTAG);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
*is_xattrdir = ((pflags & ZFS_XATTR) != 0) && S_ISDIR(mode);
|
|
|
|
/*
|
|
* Extended attributes can be applied to files, directories, etc.
|
|
* Otherwise the parent must be a directory.
|
|
*/
|
|
if (!*is_xattrdir && !S_ISDIR(parent_mode))
|
|
return (SET_ERROR(EINVAL));
|
|
|
|
*pobjp = parent;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Given an object number, return some zpl level statistics
|
|
*/
|
|
static int
|
|
zfs_obj_to_stats_impl(sa_handle_t *hdl, sa_attr_type_t *sa_table,
|
|
zfs_stat_t *sb)
|
|
{
|
|
sa_bulk_attr_t bulk[4];
|
|
int count = 0;
|
|
|
|
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_MODE], NULL,
|
|
&sb->zs_mode, sizeof (sb->zs_mode));
|
|
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_GEN], NULL,
|
|
&sb->zs_gen, sizeof (sb->zs_gen));
|
|
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_LINKS], NULL,
|
|
&sb->zs_links, sizeof (sb->zs_links));
|
|
SA_ADD_BULK_ATTR(bulk, count, sa_table[ZPL_CTIME], NULL,
|
|
&sb->zs_ctime, sizeof (sb->zs_ctime));
|
|
|
|
return (sa_bulk_lookup(hdl, bulk, count));
|
|
}
|
|
|
|
static int
|
|
zfs_obj_to_path_impl(objset_t *osp, uint64_t obj, sa_handle_t *hdl,
|
|
sa_attr_type_t *sa_table, char *buf, int len)
|
|
{
|
|
sa_handle_t *sa_hdl;
|
|
sa_handle_t *prevhdl = NULL;
|
|
dmu_buf_t *prevdb = NULL;
|
|
dmu_buf_t *sa_db = NULL;
|
|
char *path = buf + len - 1;
|
|
char *comp_buf;
|
|
int error;
|
|
|
|
*path = '\0';
|
|
sa_hdl = hdl;
|
|
|
|
uint64_t deleteq_obj;
|
|
VERIFY0(zap_lookup(osp, MASTER_NODE_OBJ,
|
|
ZFS_UNLINKED_SET, sizeof (uint64_t), 1, &deleteq_obj));
|
|
error = zap_lookup_int(osp, deleteq_obj, obj);
|
|
if (error == 0) {
|
|
return (ESTALE);
|
|
} else if (error != ENOENT) {
|
|
return (error);
|
|
}
|
|
|
|
comp_buf = kmem_alloc(ZAP_MAXNAMELEN_NEW + 2, KM_SLEEP);
|
|
for (;;) {
|
|
uint64_t pobj = 0;
|
|
char *component = comp_buf;
|
|
size_t complen;
|
|
int is_xattrdir = 0;
|
|
|
|
if (prevdb) {
|
|
ASSERT3P(prevhdl, !=, NULL);
|
|
zfs_release_sa_handle(prevhdl, prevdb, FTAG);
|
|
}
|
|
|
|
if ((error = zfs_obj_to_pobj(osp, sa_hdl, sa_table, &pobj,
|
|
&is_xattrdir)) != 0)
|
|
break;
|
|
|
|
if (pobj == obj) {
|
|
if (path[0] != '/')
|
|
*--path = '/';
|
|
break;
|
|
}
|
|
|
|
component[0] = '/';
|
|
if (is_xattrdir) {
|
|
strcpy(component + 1, "<xattrdir>");
|
|
} else {
|
|
error = zap_value_search(osp, pobj, obj,
|
|
ZFS_DIRENT_OBJ(-1ULL), component + 1,
|
|
ZAP_MAXNAMELEN_NEW);
|
|
if (error != 0)
|
|
break;
|
|
}
|
|
|
|
complen = strlen(component);
|
|
path -= complen;
|
|
ASSERT3P(path, >=, buf);
|
|
memcpy(path, component, complen);
|
|
obj = pobj;
|
|
|
|
if (sa_hdl != hdl) {
|
|
prevhdl = sa_hdl;
|
|
prevdb = sa_db;
|
|
}
|
|
error = zfs_grab_sa_handle(osp, obj, &sa_hdl, &sa_db, FTAG);
|
|
if (error != 0) {
|
|
sa_hdl = prevhdl;
|
|
sa_db = prevdb;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (sa_hdl != NULL && sa_hdl != hdl) {
|
|
ASSERT3P(sa_db, !=, NULL);
|
|
zfs_release_sa_handle(sa_hdl, sa_db, FTAG);
|
|
}
|
|
|
|
if (error == 0)
|
|
(void) memmove(buf, path, buf + len - path);
|
|
|
|
kmem_free(comp_buf, ZAP_MAXNAMELEN_NEW +2);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
zfs_obj_to_path(objset_t *osp, uint64_t obj, char *buf, int len)
|
|
{
|
|
sa_attr_type_t *sa_table;
|
|
sa_handle_t *hdl;
|
|
dmu_buf_t *db;
|
|
int error;
|
|
|
|
error = zfs_sa_setup(osp, &sa_table);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
error = zfs_grab_sa_handle(osp, obj, &hdl, &db, FTAG);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
error = zfs_obj_to_path_impl(osp, obj, hdl, sa_table, buf, len);
|
|
|
|
zfs_release_sa_handle(hdl, db, FTAG);
|
|
return (error);
|
|
}
|
|
|
|
int
|
|
zfs_obj_to_stats(objset_t *osp, uint64_t obj, zfs_stat_t *sb,
|
|
char *buf, int len)
|
|
{
|
|
char *path = buf + len - 1;
|
|
sa_attr_type_t *sa_table;
|
|
sa_handle_t *hdl;
|
|
dmu_buf_t *db;
|
|
int error;
|
|
|
|
*path = '\0';
|
|
|
|
error = zfs_sa_setup(osp, &sa_table);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
error = zfs_grab_sa_handle(osp, obj, &hdl, &db, FTAG);
|
|
if (error != 0)
|
|
return (error);
|
|
|
|
error = zfs_obj_to_stats_impl(hdl, sa_table, sb);
|
|
if (error != 0) {
|
|
zfs_release_sa_handle(hdl, db, FTAG);
|
|
return (error);
|
|
}
|
|
|
|
error = zfs_obj_to_path_impl(osp, obj, hdl, sa_table, buf, len);
|
|
|
|
zfs_release_sa_handle(hdl, db, FTAG);
|
|
return (error);
|
|
}
|
|
|
|
/*
|
|
* Read a property stored within the master node.
|
|
*/
|
|
int
|
|
zfs_get_zplprop(objset_t *os, zfs_prop_t prop, uint64_t *value)
|
|
{
|
|
uint64_t *cached_copy = NULL;
|
|
|
|
/*
|
|
* Figure out where in the objset_t the cached copy would live, if it
|
|
* is available for the requested property.
|
|
*/
|
|
if (os != NULL) {
|
|
switch (prop) {
|
|
case ZFS_PROP_VERSION:
|
|
cached_copy = &os->os_version;
|
|
break;
|
|
case ZFS_PROP_NORMALIZE:
|
|
cached_copy = &os->os_normalization;
|
|
break;
|
|
case ZFS_PROP_UTF8ONLY:
|
|
cached_copy = &os->os_utf8only;
|
|
break;
|
|
case ZFS_PROP_CASE:
|
|
cached_copy = &os->os_casesensitivity;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
if (cached_copy != NULL && *cached_copy != OBJSET_PROP_UNINITIALIZED) {
|
|
*value = *cached_copy;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* If the property wasn't cached, look up the file system's value for
|
|
* the property. For the version property, we look up a slightly
|
|
* different string.
|
|
*/
|
|
const char *pname;
|
|
int error = ENOENT;
|
|
if (prop == ZFS_PROP_VERSION)
|
|
pname = ZPL_VERSION_STR;
|
|
else
|
|
pname = zfs_prop_to_name(prop);
|
|
|
|
if (os != NULL) {
|
|
ASSERT3U(os->os_phys->os_type, ==, DMU_OST_ZFS);
|
|
error = zap_lookup(os, MASTER_NODE_OBJ, pname, 8, 1, value);
|
|
}
|
|
|
|
if (error == ENOENT) {
|
|
/* No value set, use the default value */
|
|
switch (prop) {
|
|
case ZFS_PROP_VERSION:
|
|
*value = ZPL_VERSION;
|
|
break;
|
|
case ZFS_PROP_NORMALIZE:
|
|
case ZFS_PROP_UTF8ONLY:
|
|
*value = 0;
|
|
break;
|
|
case ZFS_PROP_CASE:
|
|
*value = ZFS_CASE_SENSITIVE;
|
|
break;
|
|
case ZFS_PROP_ACLTYPE:
|
|
#ifdef __FreeBSD__
|
|
*value = ZFS_ACLTYPE_NFSV4;
|
|
#else
|
|
*value = ZFS_ACLTYPE_OFF;
|
|
#endif
|
|
break;
|
|
default:
|
|
return (error);
|
|
}
|
|
error = 0;
|
|
}
|
|
|
|
/*
|
|
* If one of the methods for getting the property value above worked,
|
|
* copy it into the objset_t's cache.
|
|
*/
|
|
if (error == 0 && cached_copy != NULL) {
|
|
*cached_copy = *value;
|
|
}
|
|
|
|
return (error);
|
|
}
|