From da94c5388a4e4344e86b837ff35c2c693569fc77 Mon Sep 17 00:00:00 2001 From: Alex Zhuravlev Date: Tue, 29 May 2012 14:54:10 +0400 Subject: [PATCH] LU-1305 osd: xattr support for osd-zfs xattr set/get/list, including SA Signed-off-by: Alex Zhuravlev Change-Id: Ifae82853911a1891ad4b3e6e127e29f6d83bba7b Reviewed-on: http://review.whamcloud.com/2969 Reviewed-by: Andreas Dilger Tested-by: Hudson Reviewed-by: Johann Lombardi --- lustre/osd-zfs/osd_xattr.c | 802 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 802 insertions(+) create mode 100644 lustre/osd-zfs/osd_xattr.c diff --git a/lustre/osd-zfs/osd_xattr.c b/lustre/osd-zfs/osd_xattr.c new file mode 100644 index 0000000..c7ea065 --- /dev/null +++ b/lustre/osd-zfs/osd_xattr.c @@ -0,0 +1,802 @@ +/* + * GPL HEADER START + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 only, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License version 2 for more details (a copy is included + * in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU General Public License + * version 2 along with this program; If not, see + * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + * GPL HEADER END + */ +/* + * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. + * Use is subject to license terms. + */ +/* + * Copyright (c) 2011, 2012 Whamcloud, Inc. + * Use is subject to license terms. + */ +/* + * This file is part of Lustre, http://www.lustre.org/ + * Lustre is a trademark of Sun Microsystems, Inc. + * + * lustre/osd-zfs/osd_xattr.c + * functions to manipulate extended attributes and system attributes + * + * Author: Alex Zhuravlev + * Author: Mike Pershin + */ + +#ifndef EXPORT_SYMTAB +# define EXPORT_SYMTAB +#endif +#define DEBUG_SUBSYSTEM S_OSD + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "osd_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* + * Copy an extended attribute into the buffer provided, or compute the + * required buffer size. + * + * If buf is NULL, it computes the required buffer size. + * + * Returns 0 on success or a negative error number on failure. + * On success, the number of bytes used / required is stored in 'size'. + * + * No locking is done here. + */ +int __osd_xattr_cache(const struct lu_env *env, struct osd_object *obj) +{ + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + sa_handle_t *sa_hdl; + char *buf; + int size; + int rc; + + LASSERT(obj->oo_sa_xattr == NULL); + LASSERT(obj->oo_db != NULL); + + rc = -sa_handle_get(uos->os, obj->oo_db->db_object, NULL, + SA_HDL_PRIVATE, &sa_hdl); + if (rc) + return rc; + + rc = -sa_size(sa_hdl, SA_ZPL_DXATTR(uos), &size); + if (rc) { + if (rc == -ENOENT) + rc = -nvlist_alloc(&obj->oo_sa_xattr, + NV_UNIQUE_NAME, KM_SLEEP); + goto out_sa; + } + + buf = sa_spill_alloc(KM_SLEEP); + if (buf == NULL) { + rc = -ENOMEM; + goto out_sa; + } + rc = -sa_lookup(sa_hdl, SA_ZPL_DXATTR(uos), buf, size); + if (rc == 0) + rc = -nvlist_unpack(buf, size, &obj->oo_sa_xattr, KM_SLEEP); + sa_spill_free(buf); +out_sa: + sa_handle_destroy(sa_hdl); + + return rc; +} + +int __osd_sa_xattr_get(const struct lu_env *env, struct osd_object *obj, + const struct lu_buf *buf, const char *name, int *sizep) +{ + uchar_t *nv_value; + int rc; + + LASSERT(obj->oo_sa_hdl); + + if (obj->oo_sa_xattr == NULL) { + rc = __osd_xattr_cache(env, obj); + if (rc) + return rc; + } + + LASSERT(obj->oo_sa_xattr); + rc = -nvlist_lookup_byte_array(obj->oo_sa_xattr, name, &nv_value, + sizep); + if (rc) + return rc; + + if (buf == NULL || buf->lb_buf == NULL) { + /* return the required size by *sizep */ + return 0; + } + + if (*sizep > buf->lb_len) + return -ERANGE; /* match ldiskfs error */ + + memcpy(buf->lb_buf, nv_value, *sizep); + return 0; +} + +int __osd_xattr_get(const struct lu_env *env, struct osd_object *obj, + struct lu_buf *buf, const char *name, int *sizep) +{ + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + uint64_t xa_data_obj; + dmu_buf_t *xa_data_db; + sa_handle_t *sa_hdl = NULL; + uint64_t size; + int rc; + + /* check SA_ZPL_DXATTR first then fallback to directory xattr */ + rc = __osd_sa_xattr_get(env, obj, buf, name, sizep); + if (rc != -ENOENT) + return rc; + + /* are there any extended attributes? */ + if (obj->oo_xattr == ZFS_NO_OBJECT) + return -ENOENT; + + /* Lookup the object number containing the xattr data */ + rc = -zap_lookup(uos->os, obj->oo_xattr, name, sizeof(uint64_t), 1, + &xa_data_obj); + if (rc) + return rc; + + rc = __osd_obj2dbuf(env, uos->os, xa_data_obj, &xa_data_db, FTAG); + if (rc) + return rc; + + rc = -sa_handle_get(uos->os, xa_data_obj, NULL, SA_HDL_PRIVATE, + &sa_hdl); + if (rc) + goto out_rele; + + /* Get the xattr value length / object size */ + rc = -sa_lookup(sa_hdl, SA_ZPL_SIZE(uos), &size, 8); + if (rc) + goto out; + + if (size > INT_MAX) { + rc = -EOVERFLOW; + goto out; + } + + *sizep = (int)size; + + if (buf == NULL || buf->lb_buf == NULL) { + /* We only need to return the required size */ + goto out; + } + if (*sizep > buf->lb_len) { + rc = -ERANGE; /* match ldiskfs error */ + goto out; + } + + rc = -dmu_read(uos->os, xa_data_db->db_object, 0, + size, buf->lb_buf, DMU_READ_PREFETCH); + +out: + sa_handle_destroy(sa_hdl); +out_rele: + dmu_buf_rele(xa_data_db, FTAG); + return rc; +} + +int osd_xattr_get(const struct lu_env *env, struct dt_object *dt, + struct lu_buf *buf, const char *name, + struct lustre_capa *capa) +{ + struct osd_object *obj = osd_dt_obj(dt); + int rc, size = 0; + ENTRY; + + LASSERT(obj->oo_db != NULL); + LASSERT(osd_invariant(obj)); + LASSERT(dt_object_exists(dt)); + + cfs_down(&obj->oo_guard); + rc = __osd_xattr_get(env, obj, buf, name, &size); + cfs_up(&obj->oo_guard); + + if (rc == -ENOENT) + rc = -ENODATA; + else if (rc == 0) + rc = size; + RETURN(rc); +} + +void __osd_xattr_declare_set(const struct lu_env *env, struct osd_object *obj, + int vallen, const char *name, struct osd_thandle *oh) +{ + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + dmu_buf_t *db = obj->oo_db; + dmu_tx_t *tx = oh->ot_tx; + uint64_t xa_data_obj; + int rc = 0; + int here; + + here = dt_object_exists(&obj->oo_dt); + + /* object may be not yet created */ + if (here) { + LASSERT(db); + LASSERT(obj->oo_sa_hdl); + /* we might just update SA_ZPL_DXATTR */ + dmu_tx_hold_sa(tx, obj->oo_sa_hdl, 1); + + if (obj->oo_xattr == ZFS_NO_OBJECT) + rc = -ENOENT; + } + + if (!here || rc == -ENOENT) { + /* we'll be updating SA_ZPL_XATTR */ + if (here) { + LASSERT(obj->oo_sa_hdl); + dmu_tx_hold_sa(tx, obj->oo_sa_hdl, 1); + } + /* xattr zap + entry */ + dmu_tx_hold_zap(tx, DMU_NEW_OBJECT, TRUE, (char *) name); + /* xattr value obj */ + dmu_tx_hold_sa_create(tx, ZFS_SA_BASE_ATTR_SIZE); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, vallen); + return; + } + + rc = -zap_lookup(uos->os, obj->oo_xattr, name, sizeof(uint64_t), 1, + &xa_data_obj); + if (rc == 0) { + /* + * Entry already exists. + * We'll truncate the existing object. + */ + dmu_tx_hold_bonus(tx, xa_data_obj); + dmu_tx_hold_free(tx, xa_data_obj, vallen, DMU_OBJECT_END); + dmu_tx_hold_write(tx, xa_data_obj, 0, vallen); + return; + } else if (rc == -ENOENT) { + /* + * Entry doesn't exist, we need to create a new one and a new + * object to store the value. + */ + dmu_tx_hold_bonus(tx, obj->oo_xattr); + dmu_tx_hold_zap(tx, obj->oo_xattr, TRUE, (char *) name); + dmu_tx_hold_sa_create(tx, ZFS_SA_BASE_ATTR_SIZE); + dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0, vallen); + return; + } + + /* An error happened */ + tx->tx_err = -rc; +} + +int osd_declare_xattr_set(const struct lu_env *env, struct dt_object *dt, + const struct lu_buf *buf, const char *name, + int fl, struct thandle *handle) +{ + struct osd_object *obj = osd_dt_obj(dt); + struct osd_thandle *oh; + ENTRY; + + LASSERT(handle != NULL); + oh = container_of0(handle, struct osd_thandle, ot_super); + + cfs_down(&obj->oo_guard); + __osd_xattr_declare_set(env, obj, buf->lb_len, name, oh); + cfs_up(&obj->oo_guard); + + RETURN(0); +} + +/* + * Set an extended attribute. + * This transaction must have called udmu_xattr_declare_set() first. + * + * Returns 0 on success or a negative error number on failure. + * + * No locking is done here. + */ +static int +__osd_sa_xattr_update(const struct lu_env *env, struct osd_object *obj, + struct osd_thandle *oh) +{ + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + char *dxattr; + size_t sa_size; + int rc; + + ENTRY; + LASSERT(obj->oo_sa_hdl); + LASSERT(obj->oo_sa_xattr); + + /* Update the SA for additions, modifications, and removals. */ + rc = -nvlist_size(obj->oo_sa_xattr, &sa_size, NV_ENCODE_XDR); + if (rc) + return rc; + + dxattr = sa_spill_alloc(KM_SLEEP); + if (dxattr == NULL) + RETURN(-ENOMEM); + + rc = -nvlist_pack(obj->oo_sa_xattr, &dxattr, &sa_size, + NV_ENCODE_XDR, KM_SLEEP); + if (rc) + GOTO(out_free, rc); + + rc = osd_object_sa_update(obj, SA_ZPL_DXATTR(uos), dxattr, sa_size, oh); +out_free: + sa_spill_free(dxattr); + RETURN(rc); +} + +int __osd_sa_xattr_set(const struct lu_env *env, struct osd_object *obj, + const struct lu_buf *buf, const char *name, int fl, + struct osd_thandle *oh) +{ + uchar_t *nv_value; + size_t size; + int nv_size; + int rc; + int too_big = 0; + + LASSERT(obj->oo_sa_hdl); + if (obj->oo_sa_xattr == NULL) { + rc = __osd_xattr_cache(env, obj); + if (rc) + return rc; + } + + LASSERT(obj->oo_sa_xattr); + /* Limited to 32k to keep nvpair memory allocations small */ + if (buf->lb_len > DXATTR_MAX_ENTRY_SIZE) { + too_big = 1; + } else { + /* Prevent the DXATTR SA from consuming the entire SA + * region */ + rc = -nvlist_size(obj->oo_sa_xattr, &size, NV_ENCODE_XDR); + if (rc) + return rc; + + if (size + buf->lb_len > DXATTR_MAX_SA_SIZE) + too_big = 1; + } + + /* even in case of -EFBIG we must lookup xattr and check can we + * rewrite it then delete from SA */ + rc = -nvlist_lookup_byte_array(obj->oo_sa_xattr, name, &nv_value, + &nv_size); + if (rc == 0) { + if (fl & LU_XATTR_CREATE) { + return -EEXIST; + } else if (too_big) { + rc = -nvlist_remove(obj->oo_sa_xattr, name, + DATA_TYPE_BYTE_ARRAY); + if (rc < 0) + return rc; + rc = __osd_sa_xattr_update(env, obj, oh); + return rc == 0 ? -EFBIG : rc; + } + } else if (rc == -ENOENT) { + if (fl & LU_XATTR_REPLACE) + return -ENODATA; + else if (too_big) + return -EFBIG; + } else { + return rc; + } + + rc = -nvlist_add_byte_array(obj->oo_sa_xattr, name, + (uchar_t *)buf->lb_buf, buf->lb_len); + if (rc) + return rc; + + rc = __osd_sa_xattr_update(env, obj, oh); + return rc; +} + +static int +__osd_xattr_set(const struct lu_env *env, struct osd_object *obj, + const struct lu_buf *buf, const char *name, int fl, + struct osd_thandle *oh) +{ + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + dmu_buf_t *xa_zap_db = NULL; + dmu_buf_t *xa_data_db = NULL; + uint64_t xa_data_obj; + sa_handle_t *sa_hdl = NULL; + dmu_tx_t *tx = oh->ot_tx; + uint64_t size; + int rc; + + LASSERT(obj->oo_sa_hdl); + + if (obj->oo_xattr == ZFS_NO_OBJECT) { + struct lu_attr *la = &osd_oti_get(env)->oti_la; + + la->la_valid = LA_MODE; + la->la_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO; + rc = __osd_zap_create(env, uos, &xa_zap_db, tx, la, FTAG, 0); + if (rc) + return rc; + + obj->oo_xattr = xa_zap_db->db_object; + rc = osd_object_sa_update(obj, SA_ZPL_XATTR(uos), + &obj->oo_xattr, 8, oh); + if (rc) + goto out; + } + + rc = -zap_lookup(uos->os, obj->oo_xattr, name, sizeof(uint64_t), 1, + &xa_data_obj); + if (rc == 0) { + if (fl & LU_XATTR_CREATE) { + rc = -EEXIST; + goto out; + } + /* + * Entry already exists. + * We'll truncate the existing object. + */ + rc = __osd_obj2dbuf(env, uos->os, xa_data_obj, + &xa_data_db, FTAG); + if (rc) + goto out; + + rc = -sa_handle_get(uos->os, xa_data_obj, NULL, + SA_HDL_PRIVATE, &sa_hdl); + if (rc) + goto out; + + rc = -sa_lookup(sa_hdl, SA_ZPL_SIZE(uos), &size, 8); + if (rc) + goto out_sa; + + rc = -dmu_free_range(uos->os, xa_data_db->db_object, + 0, DMU_OBJECT_END, tx); + if (rc) + goto out_sa; + } else if (rc == -ENOENT) { + struct lu_attr *la = &osd_oti_get(env)->oti_la; + /* + * Entry doesn't exist, we need to create a new one and a new + * object to store the value. + */ + if (fl & LU_XATTR_REPLACE) { + /* should be ENOATTR according to the + * man, but that is undefined here */ + rc = -ENODATA; + goto out; + } + + la->la_valid = LA_MODE; + la->la_mode = S_IFREG | S_IRUGO | S_IWUSR; + rc = __osd_object_create(env, uos, &xa_data_db, tx, la, FTAG); + if (rc) + goto out; + xa_data_obj = xa_data_db->db_object; + + rc = -sa_handle_get(uos->os, xa_data_obj, NULL, + SA_HDL_PRIVATE, &sa_hdl); + if (rc) + goto out; + + rc = -zap_add(uos->os, obj->oo_xattr, name, sizeof(uint64_t), + 1, &xa_data_obj, tx); + if (rc) + goto out_sa; + } else { + /* There was an error looking up the xattr name */ + goto out; + } + + /* Finally write the xattr value */ + dmu_write(uos->os, xa_data_obj, 0, buf->lb_len, buf->lb_buf, tx); + + size = buf->lb_len; + rc = -sa_update(sa_hdl, SA_ZPL_SIZE(uos), &size, 8, tx); + +out_sa: + sa_handle_destroy(sa_hdl); +out: + if (xa_data_db != NULL) + dmu_buf_rele(xa_data_db, FTAG); + if (xa_zap_db != NULL) + dmu_buf_rele(xa_zap_db, FTAG); + + return rc; +} + +int osd_xattr_set(const struct lu_env *env, struct dt_object *dt, + const struct lu_buf *buf, const char *name, int fl, + struct thandle *handle, struct lustre_capa *capa) +{ + struct osd_object *obj = osd_dt_obj(dt); + struct osd_thandle *oh; + int rc = 0; + ENTRY; + + LASSERT(handle != NULL); + LASSERT(osd_invariant(obj)); + LASSERT(dt_object_exists(dt)); + LASSERT(obj->oo_db); + + oh = container_of0(handle, struct osd_thandle, ot_super); + + cfs_down(&obj->oo_guard); + CDEBUG(D_INODE, "Setting xattr %s with size %d\n", + name, (int)buf->lb_len); + rc = __osd_sa_xattr_set(env, obj, buf, name, fl, oh); + /* place xattr in dnode if SA is full */ + if (rc == -EFBIG) + rc = __osd_xattr_set(env, obj, buf, name, fl, oh); + cfs_up(&obj->oo_guard); + + RETURN(rc); +} + +static void +__osd_xattr_declare_del(const struct lu_env *env, struct osd_object *obj, + const char *name, struct osd_thandle *oh) +{ + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + dmu_tx_t *tx = oh->ot_tx; + uint64_t xa_data_obj; + int rc; + + /* update SA_ZPL_DXATTR if xattr was in SA */ + dmu_tx_hold_sa(tx, obj->oo_sa_hdl, 0); + + if (obj->oo_xattr == ZFS_NO_OBJECT) + return; + + rc = -zap_lookup(uos->os, obj->oo_xattr, name, 8, 1, &xa_data_obj); + if (rc == 0) { + /* + * Entry exists. + * We'll delete the existing object and ZAP entry. + */ + dmu_tx_hold_bonus(tx, xa_data_obj); + dmu_tx_hold_free(tx, xa_data_obj, 0, DMU_OBJECT_END); + dmu_tx_hold_zap(tx, obj->oo_xattr, FALSE, (char *) name); + return; + } else if (rc == -ENOENT) { + /* + * Entry doesn't exist, nothing to be changed. + */ + return; + } + + /* An error happened */ + tx->tx_err = -rc; +} + +int osd_declare_xattr_del(const struct lu_env *env, struct dt_object *dt, + const char *name, struct thandle *handle) +{ + struct osd_object *obj = osd_dt_obj(dt); + struct osd_thandle *oh; + ENTRY; + + LASSERT(handle != NULL); + LASSERT(dt_object_exists(dt)); + LASSERT(osd_invariant(obj)); + + oh = container_of0(handle, struct osd_thandle, ot_super); + LASSERT(oh->ot_tx != NULL); + LASSERT(obj->oo_db != NULL); + + cfs_down(&obj->oo_guard); + __osd_xattr_declare_del(env, obj, name, oh); + cfs_up(&obj->oo_guard); + + RETURN(0); +} + +int __osd_sa_xattr_del(const struct lu_env *env, struct osd_object *obj, + const char *name, struct osd_thandle *oh) +{ + int rc; + + if (obj->oo_sa_xattr == NULL) { + rc = __osd_xattr_cache(env, obj); + if (rc) + return rc; + } + + rc = -nvlist_remove(obj->oo_sa_xattr, name, DATA_TYPE_BYTE_ARRAY); + if (rc == 0) + rc = __osd_sa_xattr_update(env, obj, oh); + return rc; +} + +int __osd_xattr_del(const struct lu_env *env, struct osd_object *obj, + const char *name, struct osd_thandle *oh) +{ + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + uint64_t xa_data_obj; + int rc; + + /* try remove xattr from SA at first */ + rc = __osd_sa_xattr_del(env, obj, name, oh); + if (rc != -ENOENT) + return rc; + + if (obj->oo_xattr == ZFS_NO_OBJECT) + return rc; + + rc = -zap_lookup(uos->os, obj->oo_xattr, name, sizeof(uint64_t), 1, + &xa_data_obj); + if (rc == -ENOENT) { + rc = 0; + } else if (rc == 0) { + /* + * Entry exists. + * We'll delete the existing object and ZAP entry. + */ + rc = __osd_object_free(uos, xa_data_obj, oh->ot_tx); + if (rc) + return rc; + + rc = -zap_remove(uos->os, obj->oo_xattr, name, oh->ot_tx); + } + + return rc; +} + +int osd_xattr_del(const struct lu_env *env, struct dt_object *dt, + const char *name, struct thandle *handle, + struct lustre_capa *capa) +{ + struct osd_object *obj = osd_dt_obj(dt); + struct osd_thandle *oh; + int rc; + ENTRY; + + LASSERT(handle != NULL); + LASSERT(obj->oo_db != NULL); + LASSERT(osd_invariant(obj)); + LASSERT(dt_object_exists(dt)); + oh = container_of0(handle, struct osd_thandle, ot_super); + LASSERT(oh->ot_tx != NULL); + + cfs_down(&obj->oo_guard); + rc = __osd_xattr_del(env, obj, name, oh); + cfs_up(&obj->oo_guard); + + RETURN(rc); +} + +static int +osd_sa_xattr_list(const struct lu_env *env, struct osd_object *obj, + struct lu_buf *lb) +{ + nvpair_t *nvp = NULL; + int len, counted = 0, remain = lb->lb_len; + int rc = 0; + + if (obj->oo_sa_xattr == NULL) { + rc = __osd_xattr_cache(env, obj); + if (rc) + return rc; + } + + LASSERT(obj->oo_sa_xattr); + + while ((nvp = nvlist_next_nvpair(obj->oo_sa_xattr, nvp)) != NULL) { + len = strlen(nvpair_name(nvp)); + if (len >= remain) + return -ERANGE; + + memcpy(lb->lb_buf, nvpair_name(nvp), len); + lb->lb_buf += len; + *((char *)lb->lb_buf) = '\0'; + lb->lb_buf++; + remain -= len + 1; + counted += len + 1; + } + return counted; +} + +int osd_xattr_list(const struct lu_env *env, struct dt_object *dt, + struct lu_buf *lb, struct lustre_capa *capa) +{ + struct osd_thread_info *oti = osd_oti_get(env); + struct osd_object *obj = osd_dt_obj(dt); + struct osd_device *osd = osd_obj2dev(obj); + udmu_objset_t *uos = &osd->od_objset; + zap_cursor_t *zc; + int rc, counted = 0, remain = lb->lb_len; + ENTRY; + + LASSERT(obj->oo_db != NULL); + LASSERT(osd_invariant(obj)); + LASSERT(dt_object_exists(dt)); + + cfs_down(&obj->oo_guard); + + rc = osd_sa_xattr_list(env, obj, lb); + if (rc < 0) + GOTO(out, rc); + counted = rc; + remain -= counted; + + /* continue with dnode xattr if any */ + if (obj->oo_xattr == ZFS_NO_OBJECT) + GOTO(out, rc = counted); + + rc = -udmu_zap_cursor_init(&zc, uos, obj->oo_xattr, 0); + if (rc) + GOTO(out, rc); + + while ((rc = -udmu_zap_cursor_retrieve_key(env, zc, oti->oti_key, + MAXNAMELEN)) == 0) { + rc = strlen(oti->oti_key); + if (rc >= remain) + GOTO(out_fini, rc = -ERANGE); + + memcpy(lb->lb_buf, oti->oti_key, rc); + lb->lb_buf += rc; + *((char *)lb->lb_buf) = '\0'; + lb->lb_buf++; + remain -= rc + 1; + counted += rc + 1; + + zap_cursor_advance(zc); + } + if (rc < 0) + GOTO(out_fini, rc); + rc = counted; + +out_fini: + udmu_zap_cursor_fini(zc); +out: + cfs_up(&obj->oo_guard); + RETURN(rc); + +} + + -- 1.8.3.1