1*9aa2a9c3Schristos /* $NetBSD: ext2fs_xattr.c,v 1.5 2020/05/16 18:31:53 christos Exp $ */
22058250bSjdolecek
32058250bSjdolecek /*-
42058250bSjdolecek * Copyright (c) 2016 The NetBSD Foundation, Inc.
52058250bSjdolecek * All rights reserved.
62058250bSjdolecek *
72058250bSjdolecek * This code is derived from software contributed to The NetBSD Foundation
82058250bSjdolecek * by Jaromir Dolecek.
92058250bSjdolecek *
102058250bSjdolecek * Redistribution and use in source and binary forms, with or without
112058250bSjdolecek * modification, are permitted provided that the following conditions
122058250bSjdolecek * are met:
132058250bSjdolecek * 1. Redistributions of source code must retain the above copyright
142058250bSjdolecek * notice, this list of conditions and the following disclaimer.
152058250bSjdolecek * 2. Redistributions in binary form must reproduce the above copyright
162058250bSjdolecek * notice, this list of conditions and the following disclaimer in the
172058250bSjdolecek * documentation and/or other materials provided with the distribution.
182058250bSjdolecek *
192058250bSjdolecek * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
202058250bSjdolecek * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
212058250bSjdolecek * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
222058250bSjdolecek * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
232058250bSjdolecek * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
242058250bSjdolecek * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
252058250bSjdolecek * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
262058250bSjdolecek * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
272058250bSjdolecek * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
282058250bSjdolecek * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
292058250bSjdolecek * POSSIBILITY OF SUCH DAMAGE.
302058250bSjdolecek */
312058250bSjdolecek
322058250bSjdolecek #include <sys/cdefs.h>
33*9aa2a9c3Schristos __KERNEL_RCSID(0, "$NetBSD: ext2fs_xattr.c,v 1.5 2020/05/16 18:31:53 christos Exp $");
342058250bSjdolecek
352058250bSjdolecek #include <sys/param.h>
362058250bSjdolecek #include <sys/systm.h>
372058250bSjdolecek #include <sys/mount.h>
382058250bSjdolecek #include <sys/proc.h>
392058250bSjdolecek #include <sys/file.h>
402058250bSjdolecek #include <sys/buf.h>
412058250bSjdolecek #include <sys/vnode.h>
422058250bSjdolecek #include <sys/kernel.h>
432058250bSjdolecek #include <sys/kmem.h>
442058250bSjdolecek #include <sys/trace.h>
452058250bSjdolecek #include <sys/resourcevar.h>
462058250bSjdolecek #include <sys/kauth.h>
472058250bSjdolecek #include <sys/extattr.h>
482058250bSjdolecek
492058250bSjdolecek #include <ufs/ufs/inode.h>
502058250bSjdolecek #include <ufs/ufs/ufsmount.h>
512058250bSjdolecek #include <ufs/ufs/ufs_extern.h>
522058250bSjdolecek
532058250bSjdolecek #include <ufs/ext2fs/ext2fs.h>
542058250bSjdolecek #include <ufs/ext2fs/ext2fs_extern.h>
552058250bSjdolecek #include <ufs/ext2fs/ext2fs_xattr.h>
562058250bSjdolecek
572058250bSjdolecek static const char * const xattr_prefix_index[] = {
582058250bSjdolecek "",
592058250bSjdolecek "user.",
602058250bSjdolecek "system.posix_acl_access",
612058250bSjdolecek "system.posix_acl_default",
622058250bSjdolecek "trusted.",
632058250bSjdolecek "", /* unused */
642058250bSjdolecek "security",
652058250bSjdolecek "system.",
662058250bSjdolecek "system.richacl",
672058250bSjdolecek "c",
682058250bSjdolecek };
692058250bSjdolecek
702058250bSjdolecek static int
ext2fs_find_xattr(struct ext2fs_xattr_entry * e,uint8_t * start,uint8_t * end,int attrnamespace,struct uio * uio,size_t * size,uint8_t name_index,const char * name)7136b34638Schristos ext2fs_find_xattr(struct ext2fs_xattr_entry *e, uint8_t *start, uint8_t *end,
7236b34638Schristos int attrnamespace, struct uio *uio, size_t *size, uint8_t name_index,
7336b34638Schristos const char *name)
742058250bSjdolecek {
752058250bSjdolecek uint8_t *value;
762058250bSjdolecek int error;
772058250bSjdolecek size_t value_offs, value_len, len, old_len;
782058250bSjdolecek
792058250bSjdolecek /*
802058250bSjdolecek * Individual entries follow the header. Each is aligned on 4-byte
812058250bSjdolecek * boundary.
822058250bSjdolecek */
832058250bSjdolecek for(; !EXT2FS_XATTR_IS_LAST_ENTRY(e, end); e = EXT2FS_XATTR_NEXT(e)) {
842058250bSjdolecek /*
852058250bSjdolecek * Only EXT2FS_XATTR_PREFIX_USER is USER, anything else
862058250bSjdolecek * is considered SYSTEM.
872058250bSjdolecek */
8836b34638Schristos if ((attrnamespace == EXTATTR_NAMESPACE_USER
8936b34638Schristos && e->e_name_index != EXT2FS_XATTR_PREFIX_USER) ||
9036b34638Schristos (attrnamespace == EXTATTR_NAMESPACE_SYSTEM
9136b34638Schristos && e->e_name_index == EXT2FS_XATTR_PREFIX_USER)) {
922058250bSjdolecek continue;
932058250bSjdolecek }
942058250bSjdolecek
952058250bSjdolecek if (e->e_name_index != name_index ||
962058250bSjdolecek e->e_name_len != strlen(name) ||
972058250bSjdolecek strncmp(e->e_name, name, e->e_name_len) != 0)
982058250bSjdolecek continue;
992058250bSjdolecek
1002058250bSjdolecek value_offs = fs2h32(e->e_value_offs);
1012058250bSjdolecek value_len = fs2h32(e->e_value_size);
1022058250bSjdolecek value = &start[value_offs];
1032058250bSjdolecek
1042058250bSjdolecek /* make sure the value offset are sane */
1052058250bSjdolecek if (&value[value_len] > end)
10601d303e7Schristos return EINVAL;
1072058250bSjdolecek
1082058250bSjdolecek if (uio != NULL) {
1092058250bSjdolecek /*
11036b34638Schristos * Figure out maximum to transfer -- use buffer size
11136b34638Schristos * and local data limit.
1122058250bSjdolecek */
1132058250bSjdolecek len = MIN(uio->uio_resid, value_len);
1142058250bSjdolecek old_len = uio->uio_resid;
1152058250bSjdolecek uio->uio_resid = len;
1162058250bSjdolecek
1172058250bSjdolecek uio->uio_resid = old_len - (len - uio->uio_resid);
1182058250bSjdolecek
1192058250bSjdolecek error = uiomove(value, value_len, uio);
1202058250bSjdolecek if (error)
1212058250bSjdolecek return error;
1222058250bSjdolecek }
1232058250bSjdolecek
1242058250bSjdolecek /* full data size */
1252058250bSjdolecek *size += value_len;
1262058250bSjdolecek
1272058250bSjdolecek goto found;
1282058250bSjdolecek }
1292058250bSjdolecek
1302058250bSjdolecek /* requested attribute not found */
13101d303e7Schristos return ENODATA;
1322058250bSjdolecek
1332058250bSjdolecek found:
13401d303e7Schristos return 0;
1352058250bSjdolecek }
1362058250bSjdolecek
1372058250bSjdolecek static int
ext2fs_get_inode_xattr(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size,uint8_t name_index,const char * name)13836b34638Schristos ext2fs_get_inode_xattr(struct inode *ip, int attrnamespace, struct uio *uio,
13936b34638Schristos size_t *size, uint8_t name_index, const char *name)
1402058250bSjdolecek {
1412058250bSjdolecek struct ext2fs_dinode *di = ip->i_din.e2fs_din;
1422058250bSjdolecek struct ext2fs_xattr_ibody_header *h;
1432058250bSjdolecek uint8_t *start, *end;
1442058250bSjdolecek
1452058250bSjdolecek start = &((uint8_t *)di)[EXT2_REV0_DINODE_SIZE + di->e2di_extra_isize];
1462058250bSjdolecek h = (struct ext2fs_xattr_ibody_header *)start;
1472058250bSjdolecek end = &((uint8_t *)di)[EXT2_DINODE_SIZE(ip->i_e2fs)];
1482058250bSjdolecek
1492058250bSjdolecek if (end <= start || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
15001d303e7Schristos return ENODATA;
1512058250bSjdolecek
15236b34638Schristos return ext2fs_find_xattr(EXT2FS_XATTR_IFIRST(h), start, end,
15336b34638Schristos attrnamespace, uio, size, name_index, name);
1542058250bSjdolecek }
1552058250bSjdolecek
1562058250bSjdolecek static int
ext2fs_get_block_xattr(struct inode * ip,int attrnamespace,struct uio * uio,size_t * size,uint8_t name_index,const char * name)15736b34638Schristos ext2fs_get_block_xattr(struct inode *ip, int attrnamespace, struct uio *uio,
15836b34638Schristos size_t *size, uint8_t name_index, const char *name)
1592058250bSjdolecek {
1602058250bSjdolecek struct ext2fs_dinode *di = ip->i_din.e2fs_din;
1612058250bSjdolecek uint8_t *start, *end;
1622058250bSjdolecek struct ext2fs_xattr_header *h;
1632058250bSjdolecek int error = 0;
1642058250bSjdolecek struct buf *bp = NULL;
1652058250bSjdolecek daddr_t xblk;
1662058250bSjdolecek
1672058250bSjdolecek xblk = di->e2di_facl;
1686bd2592fSjdolecek if (EXT2F_HAS_INCOMPAT_FEATURE(ip->i_e2fs, EXT2F_INCOMPAT_64BIT))
1692058250bSjdolecek xblk |= (((daddr_t)di->e2di_facl_high) << 32);
1702058250bSjdolecek
1712058250bSjdolecek /* don't do anything if no attr block was allocated */
1722058250bSjdolecek if (xblk == 0)
17301d303e7Schristos return 0;
1742058250bSjdolecek
17536b34638Schristos error = bread(ip->i_devvp, fsbtodb(ip->i_e2fs, xblk),
17636b34638Schristos (int)ip->i_e2fs->e2fs_bsize, 0, &bp);
1772058250bSjdolecek if (error)
1782058250bSjdolecek goto out;
1792058250bSjdolecek
1802058250bSjdolecek start = (uint8_t *)bp->b_data;
1812058250bSjdolecek h = (struct ext2fs_xattr_header *)start;
1822058250bSjdolecek end = &((uint8_t *)bp->b_data)[bp->b_bcount];
1832058250bSjdolecek
1842058250bSjdolecek if (end <= start || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
1852058250bSjdolecek goto out;
1862058250bSjdolecek
18736b34638Schristos error = ext2fs_find_xattr(EXT2FS_XATTR_BFIRST(h), start, end,
18836b34638Schristos attrnamespace, uio, size, name_index, name);
1892058250bSjdolecek
1902058250bSjdolecek out:
1912058250bSjdolecek if (bp)
1922058250bSjdolecek brelse(bp, 0);
19301d303e7Schristos return error;
1942058250bSjdolecek }
1952058250bSjdolecek int
ext2fs_getextattr(void * v)1962058250bSjdolecek ext2fs_getextattr(void *v)
1972058250bSjdolecek {
1982058250bSjdolecek struct vop_getextattr_args /* {
1992058250bSjdolecek const struct vnodeop_desc *a_desc;
2002058250bSjdolecek struct vnode *a_vp;
2012058250bSjdolecek int a_attrnamespace;
2022058250bSjdolecek const char *a_name;
2032058250bSjdolecek struct uio *a_uio;
2042058250bSjdolecek size_t *a_size;
2052058250bSjdolecek kauth_cred_t a_cred;
2062058250bSjdolecek } */ *ap = v;
2072058250bSjdolecek struct inode *ip = VTOI(ap->a_vp);
20836b34638Schristos int error;
2092058250bSjdolecek const char *prefix, *name;
2102058250bSjdolecek uint8_t name_index;
2112058250bSjdolecek size_t name_match, valuesize = 0;
2122058250bSjdolecek
213*9aa2a9c3Schristos error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred,
214*9aa2a9c3Schristos VREAD);
2152058250bSjdolecek if (error)
21601d303e7Schristos return error;
2172058250bSjdolecek
2182058250bSjdolecek /*
2192058250bSjdolecek * Allow only offsets of zero to encourage the read/replace
2202058250bSjdolecek * extended attribute semantic. Otherwise we can't guarantee
2212058250bSjdolecek * atomicity, as we don't provide locks for extended attributes.
2222058250bSjdolecek */
2232058250bSjdolecek if (ap->a_uio != NULL && ap->a_uio->uio_offset != 0)
22401d303e7Schristos return ENXIO;
2252058250bSjdolecek
2262058250bSjdolecek /* figure out the name index */
2272058250bSjdolecek name = ap->a_name;
2282058250bSjdolecek name_index = 0;
2292058250bSjdolecek name_match = 0;
23036b34638Schristos for(size_t i = 0; i < __arraycount(xattr_prefix_index); i++) {
2312058250bSjdolecek prefix = xattr_prefix_index[i];
23236b34638Schristos size_t l = strlen(prefix);
23336b34638Schristos if (l > 0 && strncmp(ap->a_name, prefix, l) == 0 &&
23436b34638Schristos name_match < l) {
23536b34638Schristos name = &ap->a_name[l];
2362058250bSjdolecek name_index = i;
23736b34638Schristos name_match = l;
2382058250bSjdolecek continue;
2392058250bSjdolecek }
2402058250bSjdolecek }
2412058250bSjdolecek
2422058250bSjdolecek /* fetch the xattr */
24336b34638Schristos error = ext2fs_get_inode_xattr(ip, ap->a_attrnamespace, ap->a_uio,
24436b34638Schristos &valuesize, name_index, name);
2452058250bSjdolecek if (error == ENODATA) {
2462058250bSjdolecek /* not found in inode, try facl */
24736b34638Schristos error = ext2fs_get_block_xattr(ip, ap->a_attrnamespace,
24836b34638Schristos ap->a_uio, &valuesize, name_index, name);
2492058250bSjdolecek }
2502058250bSjdolecek
2512058250bSjdolecek if (ap->a_size != NULL)
2522058250bSjdolecek *ap->a_size = valuesize;
2532058250bSjdolecek
25401d303e7Schristos return error;
2552058250bSjdolecek }
2562058250bSjdolecek
2572058250bSjdolecek int
ext2fs_setextattr(void * v)2582058250bSjdolecek ext2fs_setextattr(void *v)
2592058250bSjdolecek {
2602058250bSjdolecek #if 0
2612058250bSjdolecek struct vop_setextattr_args /* {
2622058250bSjdolecek const struct vnodeop_desc *a_desc;
2632058250bSjdolecek struct vnode *a_vp;
2642058250bSjdolecek int a_attrnamespace;
2652058250bSjdolecek const char *a_name;
2662058250bSjdolecek struct uio *a_uio;
2672058250bSjdolecek kauth_cred_t a_cred;
2682058250bSjdolecek } */ *ap = v;
2692058250bSjdolecek
2702058250bSjdolecek /* XXX set EXT2F_COMPAT_EXTATTR in superblock after successful set */
2712058250bSjdolecek #endif
2722058250bSjdolecek
2732058250bSjdolecek /* XXX Not implemented */
27401d303e7Schristos return EOPNOTSUPP;
2752058250bSjdolecek }
2762058250bSjdolecek
2772058250bSjdolecek static int
ext2fs_list_xattr(struct ext2fs_xattr_entry * e,uint8_t * end,int attrnamespace,int flags,struct uio * uio,size_t * size)27836b34638Schristos ext2fs_list_xattr(struct ext2fs_xattr_entry *e, uint8_t *end,
27936b34638Schristos int attrnamespace, int flags, struct uio *uio, size_t *size)
2802058250bSjdolecek {
2812058250bSjdolecek char name[EXT2FS_XATTR_NAME_LEN_MAX + 1];
2822058250bSjdolecek uint8_t len;
2832058250bSjdolecek int error;
2842058250bSjdolecek const char *prefix;
2852058250bSjdolecek
2862058250bSjdolecek /*
2872058250bSjdolecek * Individual entries follow the header. Each is aligned on 4-byte
2882058250bSjdolecek * boundary.
2892058250bSjdolecek */
2902058250bSjdolecek for(; !EXT2FS_XATTR_IS_LAST_ENTRY(e, end); e = EXT2FS_XATTR_NEXT(e)) {
2912058250bSjdolecek /*
2922058250bSjdolecek * Only EXT2FS_XATTR_PREFIX_USER is USER, anything else
2932058250bSjdolecek * is considered SYSTEM.
2942058250bSjdolecek */
29536b34638Schristos if ((attrnamespace == EXTATTR_NAMESPACE_USER
29636b34638Schristos && e->e_name_index != EXT2FS_XATTR_PREFIX_USER) ||
29736b34638Schristos (attrnamespace == EXTATTR_NAMESPACE_SYSTEM
29836b34638Schristos && e->e_name_index == EXT2FS_XATTR_PREFIX_USER)) {
2992058250bSjdolecek continue;
3002058250bSjdolecek }
3012058250bSjdolecek
30236b34638Schristos if (e->e_name_index < __arraycount(xattr_prefix_index))
3032058250bSjdolecek prefix = xattr_prefix_index[e->e_name_index];
3042058250bSjdolecek else
3052058250bSjdolecek prefix = "";
3062058250bSjdolecek
3072058250bSjdolecek len = snprintf(name, sizeof(name), "%s%.*s",
3082058250bSjdolecek prefix,
3092058250bSjdolecek e->e_name_len, e->e_name);
3102058250bSjdolecek
3112058250bSjdolecek if (uio != NULL) {
3122058250bSjdolecek if (flags & EXTATTR_LIST_LENPREFIX) {
3132058250bSjdolecek /* write name length */
31436b34638Schristos error = uiomove(&len, sizeof(uint8_t), uio);
31536b34638Schristos if (error)
31636b34638Schristos return error;
3172058250bSjdolecek } else {
3182058250bSjdolecek /* include trailing NUL */
3192058250bSjdolecek len++;
3202058250bSjdolecek }
3212058250bSjdolecek
3222058250bSjdolecek error = uiomove(name, len, uio);
3232058250bSjdolecek if (error)
3242058250bSjdolecek return error;
3252058250bSjdolecek
3262058250bSjdolecek *size += len;
3272058250bSjdolecek }
3282058250bSjdolecek }
3292058250bSjdolecek
33001d303e7Schristos return 0;
3312058250bSjdolecek }
3322058250bSjdolecek
3332058250bSjdolecek static int
ext2fs_list_inode_xattr(struct inode * ip,int attrnamespace,int flags,struct uio * uio,size_t * size)33436b34638Schristos ext2fs_list_inode_xattr(struct inode *ip, int attrnamespace, int flags,
33536b34638Schristos struct uio *uio, size_t *size)
3362058250bSjdolecek {
3372058250bSjdolecek struct ext2fs_dinode *di = ip->i_din.e2fs_din;
3382058250bSjdolecek void *start, *end;
3392058250bSjdolecek struct ext2fs_xattr_ibody_header *h;
3402058250bSjdolecek
3412058250bSjdolecek start = &((uint8_t *)di)[EXT2_REV0_DINODE_SIZE + di->e2di_extra_isize];
3422058250bSjdolecek h = start;
3432058250bSjdolecek end = &((uint8_t *)di)[EXT2_DINODE_SIZE(ip->i_e2fs)];
3442058250bSjdolecek
3452058250bSjdolecek if (end <= start || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
34601d303e7Schristos return 0;
3472058250bSjdolecek
34836b34638Schristos return ext2fs_list_xattr(EXT2FS_XATTR_IFIRST(h), end, attrnamespace,
34936b34638Schristos flags, uio, size);
3502058250bSjdolecek }
3512058250bSjdolecek
3522058250bSjdolecek static int
ext2fs_list_block_xattr(struct inode * ip,int attrnamespace,int flags,struct uio * uio,size_t * size)35336b34638Schristos ext2fs_list_block_xattr(struct inode *ip, int attrnamespace, int flags,
35436b34638Schristos struct uio *uio, size_t *size)
3552058250bSjdolecek {
3562058250bSjdolecek struct ext2fs_dinode *di = ip->i_din.e2fs_din;
3572058250bSjdolecek void *end;
3582058250bSjdolecek struct ext2fs_xattr_header *h;
3592058250bSjdolecek int error = 0;
3602058250bSjdolecek struct buf *bp = NULL;
3612058250bSjdolecek daddr_t xblk;
3622058250bSjdolecek
3632058250bSjdolecek xblk = di->e2di_facl;
3646bd2592fSjdolecek if (EXT2F_HAS_INCOMPAT_FEATURE(ip->i_e2fs, EXT2F_INCOMPAT_64BIT))
3652058250bSjdolecek xblk |= (((daddr_t)di->e2di_facl_high) << 32);
3662058250bSjdolecek
3672058250bSjdolecek /* don't do anything if no attr block was allocated */
3682058250bSjdolecek if (xblk == 0)
36901d303e7Schristos return 0;
3702058250bSjdolecek
37136b34638Schristos error = bread(ip->i_devvp, fsbtodb(ip->i_e2fs, xblk),
37236b34638Schristos (int)ip->i_e2fs->e2fs_bsize, 0, &bp);
3732058250bSjdolecek if (error)
3742058250bSjdolecek goto out;
3752058250bSjdolecek
3762058250bSjdolecek h = (struct ext2fs_xattr_header *)bp->b_data;
3772058250bSjdolecek end = &((uint8_t *)bp->b_data)[bp->b_bcount];
3782058250bSjdolecek
3792058250bSjdolecek if (end <= (void *)h || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
3802058250bSjdolecek goto out;
3812058250bSjdolecek
38236b34638Schristos error = ext2fs_list_xattr(EXT2FS_XATTR_BFIRST(h), end, attrnamespace,
38336b34638Schristos flags, uio, size);
3842058250bSjdolecek
3852058250bSjdolecek out:
3862058250bSjdolecek if (bp)
3872058250bSjdolecek brelse(bp, 0);
38801d303e7Schristos return error;
3892058250bSjdolecek }
3902058250bSjdolecek
3912058250bSjdolecek int
ext2fs_listextattr(void * v)3922058250bSjdolecek ext2fs_listextattr(void *v)
3932058250bSjdolecek {
3942058250bSjdolecek struct vop_listextattr_args /* {
3952058250bSjdolecek const struct vnodeop_desc *a_desc;
3962058250bSjdolecek struct vnode *a_vp;
3972058250bSjdolecek int a_attrnamespace;
3982058250bSjdolecek struct uio *a_uio;
3992058250bSjdolecek size_t *a_size;
4002058250bSjdolecek int a_flag;
4012058250bSjdolecek kauth_cred_t a_cred;
4022058250bSjdolecek } */ *ap = v;
4032058250bSjdolecek struct inode *ip = VTOI(ap->a_vp);
4042058250bSjdolecek int error;
4052058250bSjdolecek size_t listsize = 0;
4062058250bSjdolecek
4076bd2592fSjdolecek if (!EXT2F_HAS_COMPAT_FEATURE(ip->i_e2fs, EXT2F_COMPAT_EXTATTR)) {
4082058250bSjdolecek /* no EA on the filesystem */
4092058250bSjdolecek goto out;
4102058250bSjdolecek }
4112058250bSjdolecek
412*9aa2a9c3Schristos error = extattr_check_cred(ap->a_vp, ap->a_attrnamespace, ap->a_cred,
413*9aa2a9c3Schristos VREAD);
4142058250bSjdolecek if (error)
41501d303e7Schristos return error;
4162058250bSjdolecek
4172058250bSjdolecek /*
4182058250bSjdolecek * Allow only offsets of zero to encourage the read/replace
4192058250bSjdolecek * extended attribute semantic. Otherwise we can't guarantee
4202058250bSjdolecek * atomicity, as we don't provide locks for extended attributes.
4212058250bSjdolecek * XXX revisit - vnode lock enough?
4222058250bSjdolecek */
4232058250bSjdolecek if (ap->a_uio != NULL && ap->a_uio->uio_offset != 0)
42401d303e7Schristos return ENXIO;
4252058250bSjdolecek
4262058250bSjdolecek /* fetch inode xattrs */
42736b34638Schristos error = ext2fs_list_inode_xattr(ip, ap->a_attrnamespace, ap->a_flag,
42836b34638Schristos ap->a_uio, &listsize);
4292058250bSjdolecek if (error)
43001d303e7Schristos return error;
4312058250bSjdolecek
43236b34638Schristos error = ext2fs_list_block_xattr(ip, ap->a_attrnamespace, ap->a_flag,
43336b34638Schristos ap->a_uio, &listsize);
4342058250bSjdolecek if (error)
43501d303e7Schristos return error;
4362058250bSjdolecek
4372058250bSjdolecek out:
4382058250bSjdolecek if (ap->a_size != NULL)
4392058250bSjdolecek *ap->a_size = listsize;
4402058250bSjdolecek
44101d303e7Schristos return 0;
4422058250bSjdolecek }
4432058250bSjdolecek
4442058250bSjdolecek int
ext2fs_deleteextattr(void * v)4452058250bSjdolecek ext2fs_deleteextattr(void *v)
4462058250bSjdolecek {
4472058250bSjdolecek #if 0
4482058250bSjdolecek struct vop_deleteextattr_args /* {
4492058250bSjdolecek const struct vnodeop_desc *a_desc;
4502058250bSjdolecek struct vnode *a_vp;
4512058250bSjdolecek int a_attrnamespace;
4522058250bSjdolecek const char *a_name;
4532058250bSjdolecek kauth_cred_t a_cred;
4542058250bSjdolecek } */ *ap = v;
4552058250bSjdolecek #endif
4562058250bSjdolecek
4572058250bSjdolecek /* XXX Not implemented */
45801d303e7Schristos return EOPNOTSUPP;
4592058250bSjdolecek }
460