Home | History | Annotate | Line # | Download | only in ext2fs
ext2fs_xattr.c revision 1.2
      1 /*	$NetBSD: ext2fs_xattr.c,v 1.2 2016/08/13 07:40:10 christos Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2016 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Jaromir Dolecek.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: ext2fs_xattr.c,v 1.2 2016/08/13 07:40:10 christos Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/systm.h>
     37 #include <sys/mount.h>
     38 #include <sys/proc.h>
     39 #include <sys/file.h>
     40 #include <sys/buf.h>
     41 #include <sys/vnode.h>
     42 #include <sys/kernel.h>
     43 #include <sys/kmem.h>
     44 #include <sys/trace.h>
     45 #include <sys/resourcevar.h>
     46 #include <sys/kauth.h>
     47 #include <sys/extattr.h>
     48 
     49 #include <ufs/ufs/inode.h>
     50 #include <ufs/ufs/ufsmount.h>
     51 #include <ufs/ufs/ufs_extern.h>
     52 
     53 #include <ufs/ext2fs/ext2fs.h>
     54 #include <ufs/ext2fs/ext2fs_extern.h>
     55 #include <ufs/ext2fs/ext2fs_xattr.h>
     56 
     57 static const char * const xattr_prefix_index[] = {
     58 	"",
     59 	"user.",
     60 	"system.posix_acl_access",
     61 	"system.posix_acl_default",
     62 	"trusted.",
     63 	"", 	/* unused */
     64 	"security",
     65 	"system.",
     66 	"system.richacl",
     67 	"c",
     68 };
     69 
     70 static int
     71 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)
     72 {
     73 	uint8_t *value;
     74 	int error;
     75 	size_t value_offs, value_len, len, old_len;
     76 
     77 	/*
     78 	 * Individual entries follow the header. Each is aligned on 4-byte
     79 	 * boundary.
     80 	 */
     81 	for(; !EXT2FS_XATTR_IS_LAST_ENTRY(e, end); e = EXT2FS_XATTR_NEXT(e)) {
     82 		/*
     83 		 * Only EXT2FS_XATTR_PREFIX_USER is USER, anything else
     84 		 * is considered SYSTEM.
     85 		 */
     86 		if ((attrnamespace == EXTATTR_NAMESPACE_USER && e->e_name_index != EXT2FS_XATTR_PREFIX_USER) ||
     87 		    (attrnamespace == EXTATTR_NAMESPACE_SYSTEM && e->e_name_index == EXT2FS_XATTR_PREFIX_USER)) {
     88 			continue;
     89 		}
     90 
     91 		if (e->e_name_index != name_index ||
     92 		    e->e_name_len != strlen(name) ||
     93 		    strncmp(e->e_name, name, e->e_name_len) != 0)
     94 			continue;
     95 
     96 		value_offs = fs2h32(e->e_value_offs);
     97 		value_len  = fs2h32(e->e_value_size);
     98 		value      = &start[value_offs];
     99 
    100 		/* make sure the value offset are sane */
    101 		if (&value[value_len] > end)
    102 			return EINVAL;
    103 
    104 		if (uio != NULL) {
    105 			/*
    106 			 * Figure out maximum to transfer -- use buffer size and
    107 			 * local data limit.
    108 			 */
    109 			len = MIN(uio->uio_resid, value_len);
    110 			old_len = uio->uio_resid;
    111 			uio->uio_resid = len;
    112 
    113 			uio->uio_resid = old_len - (len - uio->uio_resid);
    114 
    115 			error = uiomove(value, value_len, uio);
    116 			if (error)
    117 				return error;
    118 		}
    119 
    120 		/* full data size */
    121 		*size += value_len;
    122 
    123 		goto found;
    124 	}
    125 
    126 	/* requested attribute not found */
    127 	return ENODATA;
    128 
    129     found:
    130 	return 0;
    131 }
    132 
    133 static int
    134 ext2fs_get_inode_xattr(struct inode *ip, int attrnamespace, struct uio *uio, size_t *size, uint8_t name_index, const char *name)
    135 {
    136 	struct ext2fs_dinode *di = ip->i_din.e2fs_din;
    137 	struct ext2fs_xattr_ibody_header *h;
    138 	uint8_t *start, *end;
    139 
    140 	start = &((uint8_t *)di)[EXT2_REV0_DINODE_SIZE + di->e2di_extra_isize];
    141 	h     = (struct ext2fs_xattr_ibody_header *)start;
    142 	end   = &((uint8_t *)di)[EXT2_DINODE_SIZE(ip->i_e2fs)];
    143 
    144 	if (end <= start || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
    145 		return ENODATA;
    146 
    147 	return ext2fs_find_xattr(EXT2FS_XATTR_IFIRST(h), start, end, attrnamespace, uio, size, name_index, name);
    148 }
    149 
    150 static int
    151 ext2fs_get_block_xattr(struct inode *ip, int attrnamespace, struct uio *uio, size_t *size, uint8_t name_index, const char *name)
    152 {
    153 	struct ext2fs_dinode *di = ip->i_din.e2fs_din;
    154 	uint8_t *start, *end;
    155 	struct ext2fs_xattr_header *h;
    156 	int error = 0;
    157 	struct buf *bp = NULL;
    158 	daddr_t xblk;
    159 
    160 	xblk = di->e2di_facl;
    161 	if (EXT2F_HAS_INCOMPAT_FEATURE(ip, EXT2F_INCOMPAT_64BIT))
    162 		xblk |= (((daddr_t)di->e2di_facl_high) << 32);
    163 
    164 	/* don't do anything if no attr block was allocated */
    165 	if (xblk == 0)
    166 		return 0;
    167 
    168 	error = bread(ip->i_devvp, fsbtodb(ip->i_e2fs, xblk), (int)ip->i_e2fs->e2fs_bsize, 0, &bp);
    169 	if (error)
    170 		goto out;
    171 
    172 	start = (uint8_t *)bp->b_data;
    173 	h     = (struct ext2fs_xattr_header *)start;
    174 	end   = &((uint8_t *)bp->b_data)[bp->b_bcount];
    175 
    176 	if (end <= start || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
    177 		goto out;
    178 
    179 	error = ext2fs_find_xattr(EXT2FS_XATTR_BFIRST(h), start, end, attrnamespace, uio, size, name_index, name);
    180 
    181     out:
    182 	if (bp)
    183 		brelse(bp, 0);
    184 	return error;
    185 }
    186 int
    187 ext2fs_getextattr(void *v)
    188 {
    189 	struct vop_getextattr_args /* {
    190 	        const struct vnodeop_desc *a_desc;
    191 	        struct vnode *a_vp;
    192 	        int a_attrnamespace;
    193 	        const char *a_name;
    194 	        struct uio *a_uio;
    195 	        size_t *a_size;
    196 	        kauth_cred_t a_cred;
    197 	} */ *ap = v;
    198 	struct inode *ip = VTOI(ap->a_vp);
    199 	char namebuf[EXT2FS_XATTR_NAME_LEN_MAX + 1];
    200 	int error, i;
    201 	const char *prefix, *name;
    202 	uint8_t name_index;
    203 	size_t name_match, valuesize = 0;
    204 
    205 	if (ap->a_attrnamespace == EXTATTR_NAMESPACE_USER)
    206 		prefix = xattr_prefix_index[EXT2FS_XATTR_PREFIX_USER];
    207 	else
    208 		prefix = xattr_prefix_index[EXT2FS_XATTR_PREFIX_SYSTEM];
    209 	snprintf(namebuf, sizeof(namebuf), "%s%s", prefix, ap->a_name);
    210 
    211         error = extattr_check_cred(ap->a_vp, namebuf, ap->a_cred, VREAD);
    212         if (error)
    213                 return error;
    214 
    215         /*
    216          * Allow only offsets of zero to encourage the read/replace
    217          * extended attribute semantic.  Otherwise we can't guarantee
    218          * atomicity, as we don't provide locks for extended attributes.
    219          */
    220         if (ap->a_uio != NULL && ap->a_uio->uio_offset != 0)
    221                 return ENXIO;
    222 
    223 	/* figure out the name index */
    224 	name = ap->a_name;
    225 	name_index = 0;
    226 	name_match = 0;
    227 	for(i=0; i < sizeof(xattr_prefix_index)/sizeof(void *); i++) {
    228 		prefix = xattr_prefix_index[i];
    229 		if (strlen(prefix) > 0 &&
    230 		    strncmp(ap->a_name, prefix, strlen(prefix)) == 0 &&
    231 		    name_match < strlen(prefix)) {
    232 			name = &ap->a_name[strlen(prefix)];
    233 			name_index = i;
    234 			name_match = strlen(prefix);
    235 			continue;
    236 		}
    237 	}
    238 
    239 	/* fetch the xattr */
    240 	error = ext2fs_get_inode_xattr(ip, ap->a_attrnamespace, ap->a_uio, &valuesize, name_index, name);
    241 	if (error == ENODATA) {
    242 		/* not found in inode, try facl */
    243 		error = ext2fs_get_block_xattr(ip, ap->a_attrnamespace, ap->a_uio, &valuesize, name_index, name);
    244 	}
    245 
    246 	if (ap->a_size != NULL)
    247 		*ap->a_size = valuesize;
    248 
    249 	return error;
    250 }
    251 
    252 int
    253 ext2fs_setextattr(void *v)
    254 {
    255 #if 0
    256 	struct vop_setextattr_args /* {
    257 	        const struct vnodeop_desc *a_desc;
    258 	        struct vnode *a_vp;
    259 	        int a_attrnamespace;
    260 	        const char *a_name;
    261 	        struct uio *a_uio;
    262 	        kauth_cred_t a_cred;
    263 	} */ *ap = v;
    264 
    265 	/* XXX set EXT2F_COMPAT_EXTATTR in superblock after successful set */
    266 #endif
    267 
    268 	/* XXX Not implemented */
    269 	return EOPNOTSUPP;
    270 }
    271 
    272 static int
    273 ext2fs_list_xattr(struct ext2fs_xattr_entry *e, uint8_t *end, int attrnamespace, int flags, struct uio *uio, size_t *size)
    274 {
    275 	char name[EXT2FS_XATTR_NAME_LEN_MAX + 1];
    276 	uint8_t len;
    277 	int error;
    278 	const char *prefix;
    279 
    280 	/*
    281 	 * Individual entries follow the header. Each is aligned on 4-byte
    282 	 * boundary.
    283 	 */
    284 	for(; !EXT2FS_XATTR_IS_LAST_ENTRY(e, end); e = EXT2FS_XATTR_NEXT(e)) {
    285 		/*
    286 		 * Only EXT2FS_XATTR_PREFIX_USER is USER, anything else
    287 		 * is considered SYSTEM.
    288 		 */
    289 		if ((attrnamespace == EXTATTR_NAMESPACE_USER && e->e_name_index != EXT2FS_XATTR_PREFIX_USER) ||
    290 		    (attrnamespace == EXTATTR_NAMESPACE_SYSTEM && e->e_name_index == EXT2FS_XATTR_PREFIX_USER)) {
    291 			continue;
    292 		}
    293 
    294 		if (e->e_name_index <= sizeof(xattr_prefix_index)/sizeof(void *))
    295 			prefix = xattr_prefix_index[e->e_name_index];
    296 		else
    297 			prefix = "";
    298 
    299 		len = snprintf(name, sizeof(name), "%s%.*s",
    300 			prefix,
    301 			e->e_name_len, e->e_name);
    302 
    303 		if (uio != NULL) {
    304 			if (flags & EXTATTR_LIST_LENPREFIX) {
    305 				/* write name length */
    306 				uiomove(&len, sizeof(uint8_t), uio);
    307 			} else {
    308 				/* include trailing NUL */
    309 				len++;
    310 			}
    311 
    312 			error = uiomove(name, len, uio);
    313 			if (error)
    314 				return error;
    315 
    316 			*size += len;
    317 		}
    318 	}
    319 
    320 	return 0;
    321 }
    322 
    323 static int
    324 ext2fs_list_inode_xattr(struct inode *ip, int attrnamespace, int flags, struct uio *uio, size_t *size)
    325 {
    326 	struct ext2fs_dinode *di = ip->i_din.e2fs_din;
    327 	void *start, *end;
    328 	struct ext2fs_xattr_ibody_header *h;
    329 
    330 	start = &((uint8_t *)di)[EXT2_REV0_DINODE_SIZE + di->e2di_extra_isize];
    331 	h     = start;
    332 	end   = &((uint8_t *)di)[EXT2_DINODE_SIZE(ip->i_e2fs)];
    333 
    334 	if (end <= start || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
    335 		return 0;
    336 
    337 	return ext2fs_list_xattr(EXT2FS_XATTR_IFIRST(h), end, attrnamespace, flags, uio, size);
    338 }
    339 
    340 static int
    341 ext2fs_list_block_xattr(struct inode *ip, int attrnamespace, int flags, struct uio *uio, size_t *size)
    342 {
    343 	struct ext2fs_dinode *di = ip->i_din.e2fs_din;
    344 	void *end;
    345 	struct ext2fs_xattr_header *h;
    346 	int error = 0;
    347 	struct buf *bp = NULL;
    348 	daddr_t xblk;
    349 
    350 	xblk = di->e2di_facl;
    351 	if (EXT2F_HAS_INCOMPAT_FEATURE(ip, EXT2F_INCOMPAT_64BIT))
    352 		xblk |= (((daddr_t)di->e2di_facl_high) << 32);
    353 
    354 	/* don't do anything if no attr block was allocated */
    355 	if (xblk == 0)
    356 		return 0;
    357 
    358 	error = bread(ip->i_devvp, fsbtodb(ip->i_e2fs, xblk), (int)ip->i_e2fs->e2fs_bsize, 0, &bp);
    359 	if (error)
    360 		goto out;
    361 
    362 	h     = (struct ext2fs_xattr_header *)bp->b_data;
    363 	end   = &((uint8_t *)bp->b_data)[bp->b_bcount];
    364 
    365 	if (end <= (void *)h || fs2h32(h->h_magic) != EXT2FS_XATTR_MAGIC)
    366 		goto out;
    367 
    368 	error = ext2fs_list_xattr(EXT2FS_XATTR_BFIRST(h), end, attrnamespace, flags, uio, size);
    369 
    370     out:
    371 	if (bp)
    372 		brelse(bp, 0);
    373 	return error;
    374 }
    375 
    376 int
    377 ext2fs_listextattr(void *v)
    378 {
    379 	struct vop_listextattr_args /* {
    380 	        const struct vnodeop_desc *a_desc;
    381 	        struct vnode *a_vp;
    382 	        int a_attrnamespace;
    383 	        struct uio *a_uio;
    384 	        size_t *a_size;
    385 	        int a_flag;
    386 	        kauth_cred_t a_cred;
    387 	} */ *ap = v;
    388 	struct inode *ip = VTOI(ap->a_vp);
    389 	int error;
    390 	const char *prefix;
    391 	size_t listsize = 0;
    392 
    393 	if (!EXT2F_HAS_COMPAT_FEATURE(ip, EXT2F_COMPAT_EXTATTR)) {
    394 		/* no EA on the filesystem */
    395 		goto out;
    396 	}
    397 
    398         /*
    399          * XXX: We can move this inside the loop and iterate on individual
    400          *      attributes.
    401          */
    402 	if (ap->a_attrnamespace == EXTATTR_NAMESPACE_USER)
    403 		prefix = xattr_prefix_index[EXT2FS_XATTR_PREFIX_USER];
    404 	else
    405 		prefix = xattr_prefix_index[EXT2FS_XATTR_PREFIX_SYSTEM];
    406         error = extattr_check_cred(ap->a_vp, prefix, ap->a_cred, VREAD);
    407         if (error)
    408                 return error;
    409 
    410         /*
    411          * Allow only offsets of zero to encourage the read/replace
    412          * extended attribute semantic.  Otherwise we can't guarantee
    413          * atomicity, as we don't provide locks for extended attributes.
    414 	 * XXX revisit - vnode lock enough?
    415          */
    416         if (ap->a_uio != NULL && ap->a_uio->uio_offset != 0)
    417                 return ENXIO;
    418 
    419 	/* fetch inode xattrs */
    420 	error = ext2fs_list_inode_xattr(ip, ap->a_attrnamespace, ap->a_flag, ap->a_uio, &listsize);
    421 	if (error)
    422 		return error;
    423 
    424 	error = ext2fs_list_block_xattr(ip, ap->a_attrnamespace, ap->a_flag, ap->a_uio, &listsize);
    425 	if (error)
    426 		return error;
    427 
    428     out:
    429 	if (ap->a_size != NULL)
    430 		*ap->a_size = listsize;
    431 
    432 	return 0;
    433 }
    434 
    435 int
    436 ext2fs_deleteextattr(void *v)
    437 {
    438 #if 0
    439 	struct vop_deleteextattr_args /* {
    440 	        const struct vnodeop_desc *a_desc;
    441 	        struct vnode *a_vp;
    442 	        int a_attrnamespace;
    443 	        const char *a_name;
    444 	        kauth_cred_t a_cred;
    445 	} */ *ap = v;
    446 #endif
    447 
    448 	/* XXX Not implemented */
    449 	return EOPNOTSUPP;
    450 }
    451