mirror of
				https://git.proxmox.com/git/mirror_zfs.git
				synced 2025-10-26 18:05:04 +03:00 
			
		
		
		
	Sponsored-by: https://despairlabs.com/sponsor/ Signed-off-by: Rob Norris <robn@despairlabs.com> Reviewed-by: Tony Hutter <hutter2@llnl.gov> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
		
			
				
	
	
		
			403 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			403 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: CDDL-1.0
 | 
						|
/*
 | 
						|
 * 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);
 | 
						|
}
 |