mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-26 19:19:32 +03:00
c22d56e3ed
For now, userspace has no znode implementation. Some of the property and path handling code is used there though and is the same on all platforms, so we only need a single copy of it. Reviewed by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Alexander Motin <mav@FreeBSD.org> Signed-off-by: Rob Norris <robn@despairlabs.com> Sponsored-by: https://despairlabs.com/sponsor/ Closes #16492
398 lines
9.2 KiB
C
398 lines
9.2 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;
|
|
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);
|
|
}
|
|
|
|
for (;;) {
|
|
uint64_t pobj = 0;
|
|
char component[MAXNAMELEN + 2];
|
|
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);
|
|
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);
|
|
|
|
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);
|
|
}
|