PageRenderTime 77ms CodeModel.GetById 3ms app.highlight 64ms RepoModel.GetById 1ms app.codeStats 0ms

/sys/ufs/ufs/ufs_extattr.c

https://bitbucket.org/gthummalapalle/minix
C | 1551 lines | 978 code | 228 blank | 345 comment | 233 complexity | 98715d3fdd22bc08b2c1701ecfb3e4f1 MD5 | raw file
   1/*	$NetBSD: ufs_extattr.c,v 1.35 2011/07/07 14:56:45 manu Exp $	*/
   2
   3/*-
   4 * Copyright (c) 1999-2002 Robert N. M. Watson
   5 * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
   6 * All rights reserved.
   7 *
   8 * This software was developed by Robert Watson for the TrustedBSD Project.
   9 *
  10 * This software was developed for the FreeBSD Project in part by Network
  11 * Associates Laboratories, the Security Research Division of Network
  12 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
  13 * as part of the DARPA CHATS research program.
  14 *
  15 * Redistribution and use in source and binary forms, with or without
  16 * modification, are permitted provided that the following conditions
  17 * are met:
  18 * 1. Redistributions of source code must retain the above copyright
  19 *    notice, this list of conditions and the following disclaimer.
  20 * 2. Redistributions in binary form must reproduce the above copyright
  21 *    notice, this list of conditions and the following disclaimer in the
  22 *    documentation and/or other materials provided with the distribution.
  23 *
  24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  34 * SUCH DAMAGE.
  35 *
  36 */
  37
  38/*
  39 * Support for file system extended attributes on the UFS1 file system.
  40 *
  41 * Extended attributes are defined in the form name=value, where name is
  42 * a nul-terminated string in the style of a file name, and value is a
  43 * binary blob of zero or more bytes.  The UFS1 extended attribute service
  44 * layers support for extended attributes onto a backing file, in the style
  45 * of the quota implementation, meaning that it requires no underlying format
  46 * changes to the file system.  This design choice exchanges simplicity,
  47 * usability, and easy deployment for performance.
  48 */
  49
  50#include <sys/cdefs.h>
  51__KERNEL_RCSID(0, "$NetBSD: ufs_extattr.c,v 1.35 2011/07/07 14:56:45 manu Exp $");
  52
  53#ifdef _KERNEL_OPT
  54#include "opt_ffs.h"
  55#endif
  56
  57#include <sys/param.h>
  58#include <sys/systm.h>
  59#include <sys/reboot.h>
  60#include <sys/kauth.h>
  61#include <sys/kernel.h>
  62#include <sys/namei.h>
  63#include <sys/malloc.h>
  64#include <sys/fcntl.h>
  65#include <sys/lwp.h>
  66#include <sys/vnode.h>
  67#include <sys/mount.h>
  68#include <sys/lock.h>
  69#include <sys/dirent.h>
  70#include <sys/extattr.h>
  71#include <sys/sysctl.h>
  72
  73#include <ufs/ufs/dir.h>
  74#include <ufs/ufs/extattr.h>
  75#include <ufs/ufs/ufsmount.h>
  76#include <ufs/ufs/inode.h>
  77#include <ufs/ufs/ufs_bswap.h>
  78#include <ufs/ufs/ufs_extern.h>
  79
  80static MALLOC_JUSTDEFINE(M_UFS_EXTATTR, "ufs_extattr","ufs extended attribute");
  81
  82int ufs_extattr_sync = 1;
  83int ufs_extattr_autocreate = 1024;
  84
  85static int	ufs_extattr_valid_attrname(int attrnamespace,
  86		    const char *attrname);
  87static int	ufs_extattr_enable_with_open(struct ufsmount *ump,
  88		    struct vnode *vp, int attrnamespace, const char *attrname,
  89		    struct lwp *l);
  90static int	ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
  91		    const char *attrname, struct vnode *backing_vnode,
  92		    struct lwp *l);
  93static int	ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
  94		    const char *attrname, struct lwp *l);
  95static int	ufs_extattr_get(struct vnode *vp, int attrnamespace,
  96		    const char *name, struct uio *uio, size_t *size,
  97		    kauth_cred_t cred, struct lwp *l);
  98static int	ufs_extattr_list(struct vnode *vp, int attrnamespace,
  99		    struct uio *uio, size_t *size, int flag,
 100		    kauth_cred_t cred, struct lwp *l);
 101static int	ufs_extattr_set(struct vnode *vp, int attrnamespace,
 102		    const char *name, struct uio *uio, kauth_cred_t cred,
 103		    struct lwp *l);
 104static int	ufs_extattr_rm(struct vnode *vp, int attrnamespace,
 105		    const char *name, kauth_cred_t cred, struct lwp *l);
 106static struct ufs_extattr_list_entry *ufs_extattr_find_attr(struct ufsmount *,
 107		    int, const char *);
 108static int	ufs_extattr_get_header(struct vnode *, 
 109		    struct ufs_extattr_list_entry *, 
 110		    struct ufs_extattr_header *, off_t *);
 111
 112/*
 113 * Per-FS attribute lock protecting attribute operations.
 114 * XXX Right now there is a lot of lock contention due to having a single
 115 * lock per-FS; really, this should be far more fine-grained.
 116 */
 117static void
 118ufs_extattr_uepm_lock(struct ufsmount *ump)
 119{
 120
 121	/* XXX Why does this need to be recursive? */
 122	if (mutex_owned(&ump->um_extattr.uepm_lock)) {
 123		ump->um_extattr.uepm_lockcnt++;
 124		return;
 125	}
 126	mutex_enter(&ump->um_extattr.uepm_lock);
 127}
 128
 129static void
 130ufs_extattr_uepm_unlock(struct ufsmount *ump)
 131{
 132
 133	if (ump->um_extattr.uepm_lockcnt != 0) {
 134		KASSERT(mutex_owned(&ump->um_extattr.uepm_lock));
 135		ump->um_extattr.uepm_lockcnt--;
 136		return;
 137	}
 138	mutex_exit(&ump->um_extattr.uepm_lock);
 139}
 140
 141/*-
 142 * Determine whether the name passed is a valid name for an actual
 143 * attribute.
 144 *
 145 * Invalid currently consists of:
 146 *	 NULL pointer for attrname
 147 *	 zero-length attrname (used to retrieve application attribute list)
 148 */
 149static int
 150ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
 151{
 152
 153	if (attrname == NULL)
 154		return (0);
 155	if (strlen(attrname) == 0)
 156		return (0);
 157	return (1);
 158}
 159
 160/*
 161 * Autocreate an attribute storage
 162 */
 163static struct ufs_extattr_list_entry *
 164ufs_extattr_autocreate_attr(struct vnode *vp, int attrnamespace,
 165    const char *attrname, struct lwp *l)
 166{
 167	struct mount *mp = vp->v_mount;
 168	struct ufsmount *ump = VFSTOUFS(mp);
 169	struct vnode *backing_vp;
 170	struct nameidata nd;
 171	struct pathbuf *pb;
 172	char *path;
 173	struct ufs_extattr_fileheader uef;
 174	struct ufs_extattr_list_entry *uele;
 175	int error;
 176
 177	path = PNBUF_GET();
 178
 179	/* 
 180	 * We only support system and user namespace autocreation
 181	 */ 
 182	switch (attrnamespace) {
 183	case EXTATTR_NAMESPACE_SYSTEM:
 184		(void)snprintf(path, PATH_MAX, "%s/%s/%s/%s", 
 185			       mp->mnt_stat.f_mntonname,
 186			       UFS_EXTATTR_FSROOTSUBDIR,
 187			       UFS_EXTATTR_SUBDIR_SYSTEM,
 188			       attrname);
 189		break;
 190	case EXTATTR_NAMESPACE_USER:
 191		(void)snprintf(path, PATH_MAX, "%s/%s/%s/%s", 
 192			       mp->mnt_stat.f_mntonname,
 193			       UFS_EXTATTR_FSROOTSUBDIR,
 194			       UFS_EXTATTR_SUBDIR_USER,
 195			       attrname);
 196		break;
 197	default:
 198		PNBUF_PUT(path);
 199		return NULL;
 200		break;
 201	}
 202
 203	/*
 204	 * When setting attribute on the root vnode, we get it 
 205	 * already locked, and vn_open/namei/VFS_ROOT will try to
 206	 * look it, causing a panic. Unlock it first.
 207	 */ 
 208	if (vp->v_vflag && VV_ROOT) {
 209		KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
 210		VOP_UNLOCK(vp);
 211	}
 212	KASSERT(VOP_ISLOCKED(vp) == 0);
 213
 214	pb = pathbuf_create(path);
 215	NDINIT(&nd, CREATE, LOCKPARENT, pb);
 216	
 217	error = vn_open(&nd, O_CREAT|O_RDWR, 0600);
 218
 219	/*
 220	 * Reacquire the lock on the vnode if it was root.
 221	 */
 222	KASSERT(VOP_ISLOCKED(vp) == 0);
 223	if (vp->v_vflag && VV_ROOT)
 224		vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
 225	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
 226
 227	if (error != 0) {
 228		pathbuf_destroy(pb);
 229		PNBUF_PUT(path);
 230		return NULL;
 231	}
 232
 233	KASSERT(nd.ni_vp != NULL);
 234	KASSERT(VOP_ISLOCKED(nd.ni_vp) == LK_EXCLUSIVE);
 235	KASSERT(VOP_ISLOCKED(nd.ni_dvp) == 0);
 236
 237	/*
 238 	 * backing_vp is the backing store. 
 239	 */	
 240	backing_vp = nd.ni_vp;
 241	pathbuf_destroy(pb);
 242	PNBUF_PUT(path);
 243
 244	uef.uef_magic = UFS_EXTATTR_MAGIC;
 245	uef.uef_version = UFS_EXTATTR_VERSION;
 246	uef.uef_size = ufs_extattr_autocreate;
 247
 248	error = vn_rdwr(UIO_WRITE, backing_vp, &uef, sizeof(uef), 0,
 249		        UIO_SYSSPACE, IO_NODELOCKED|IO_APPEND, 
 250			l->l_cred, NULL, l);
 251
 252	VOP_UNLOCK(backing_vp);
 253
 254	if (error != 0) {
 255		printf("%s: write uef header failed for %s, error = %d\n", 
 256		       __func__, attrname, error);
 257		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
 258		return NULL;
 259	}
 260
 261	/*
 262	 * ufs_extattr_enable_with_open increases the vnode reference
 263	 * count. Not sure why, but do the same here.
 264	 */
 265	vref(vp);
 266
 267	/*
 268	 * Now enable attribute. 
 269	 */
 270	error = ufs_extattr_enable(ump,attrnamespace, attrname, backing_vp, l);
 271	KASSERT(VOP_ISLOCKED(backing_vp) == 0);
 272
 273	if (error != 0) {
 274		printf("%s: enable %s failed, error %d\n", 
 275		       __func__, attrname, error);
 276		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
 277		return NULL;
 278	}
 279
 280	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
 281	if (uele == NULL) {
 282		printf("%s: atttribute %s created but not found!\n",
 283		       __func__, attrname);
 284		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
 285		return NULL;
 286	}
 287
 288	printf("%s: EA backing store autocreated for %s\n",
 289	       mp->mnt_stat.f_mntonname, attrname);
 290
 291	return uele;
 292}
 293
 294/*
 295 * Locate an attribute given a name and mountpoint.
 296 * Must be holding uepm lock for the mount point.
 297 */
 298static struct ufs_extattr_list_entry *
 299ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
 300    const char *attrname)
 301{
 302	struct ufs_extattr_list_entry *search_attribute;
 303
 304	for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
 305	    search_attribute != NULL;
 306	    search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
 307		if (!(strncmp(attrname, search_attribute->uele_attrname,
 308		    UFS_EXTATTR_MAXEXTATTRNAME)) &&
 309		    (attrnamespace == search_attribute->uele_attrnamespace)) {
 310			return (search_attribute);
 311		}
 312	}
 313
 314	return (0);
 315}
 316
 317/*
 318 * Initialize per-FS structures supporting extended attributes.  Do not
 319 * start extended attributes yet.
 320 */
 321void
 322ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
 323{
 324
 325	uepm->uepm_flags = 0;
 326	uepm->uepm_lockcnt = 0;
 327
 328	LIST_INIT(&uepm->uepm_list);
 329	mutex_init(&uepm->uepm_lock, MUTEX_DEFAULT, IPL_NONE);
 330	uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
 331}
 332
 333/*
 334 * Destroy per-FS structures supporting extended attributes.  Assumes
 335 * that EAs have already been stopped, and will panic if not.
 336 */
 337void
 338ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
 339{
 340
 341	if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
 342		panic("ufs_extattr_uepm_destroy: not initialized");
 343
 344	if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
 345		panic("ufs_extattr_uepm_destroy: called while still started");
 346
 347	/*
 348	 * It's not clear that either order for the next two lines is
 349	 * ideal, and it should never be a problem if this is only called
 350	 * during unmount, and with vfs_busy().
 351	 */
 352	uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
 353	mutex_destroy(&uepm->uepm_lock);
 354}
 355
 356/*
 357 * Start extended attribute support on an FS.
 358 */
 359int
 360ufs_extattr_start(struct mount *mp, struct lwp *l)
 361{
 362	struct ufsmount *ump;
 363	int error = 0;
 364
 365	ump = VFSTOUFS(mp);
 366
 367	ufs_extattr_uepm_lock(ump);
 368
 369	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
 370		error = EOPNOTSUPP;
 371		goto unlock;
 372	}
 373	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
 374		error = EBUSY;
 375		goto unlock;
 376	}
 377
 378	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
 379
 380	ump->um_extattr.uepm_ucred = l->l_cred;
 381	kauth_cred_hold(ump->um_extattr.uepm_ucred);
 382
 383 unlock:
 384	ufs_extattr_uepm_unlock(ump);
 385
 386	return (error);
 387}
 388
 389/*
 390 * Helper routine: given a locked parent directory and filename, return
 391 * the locked vnode of the inode associated with the name.  Will not
 392 * follow symlinks, may return any type of vnode.  Lock on parent will
 393 * be released even in the event of a failure.  In the event that the
 394 * target is the parent (i.e., "."), there will be two references and
 395 * one lock, requiring the caller to possibly special-case.
 396 */
 397static int
 398ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, const char *dirname,
 399    struct vnode **vp, struct lwp *l)
 400{
 401	struct vop_lookup_args vargs;
 402	struct componentname cnp;
 403	struct vnode *target_vp;
 404	char *pnbuf;
 405	int error;
 406
 407	KASSERT(VOP_ISLOCKED(start_dvp) == LK_EXCLUSIVE);
 408
 409	pnbuf = PNBUF_GET();
 410
 411	memset(&cnp, 0, sizeof(cnp));
 412	cnp.cn_nameiop = LOOKUP;
 413	cnp.cn_flags = ISLASTCN | lockparent;
 414	cnp.cn_cred = l->l_cred;
 415	cnp.cn_nameptr = pnbuf;
 416	error = copystr(dirname, pnbuf, MAXPATHLEN, &cnp.cn_namelen);
 417	if (error) {
 418		if (lockparent == 0) {
 419			VOP_UNLOCK(start_dvp);
 420		}
 421		PNBUF_PUT(pnbuf);
 422		printf("ufs_extattr_lookup: copystr failed\n");
 423		return (error);
 424	}
 425	cnp.cn_namelen--;	/* trim nul termination */
 426	vargs.a_desc = NULL;
 427	vargs.a_dvp = start_dvp;
 428	vargs.a_vpp = &target_vp;
 429	vargs.a_cnp = &cnp;
 430	error = ufs_lookup(&vargs);
 431	PNBUF_PUT(pnbuf);
 432	if (error) {
 433		if (lockparent == 0) {
 434			VOP_UNLOCK(start_dvp);
 435		}
 436		return (error);
 437	}
 438#if 0
 439	if (target_vp == start_dvp)
 440		panic("ufs_extattr_lookup: target_vp == start_dvp");
 441#endif
 442
 443	if ((target_vp != start_dvp) && (lockparent == 0))
 444		 VOP_UNLOCK(start_dvp);
 445
 446	KASSERT(VOP_ISLOCKED(target_vp) == LK_EXCLUSIVE);
 447	*vp = target_vp;
 448	return (0);
 449}
 450
 451/*
 452 * Enable an EA using the passed filesystem, backing vnode, attribute name,
 453 * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
 454 * to be locked when passed in.  The vnode will be returned unlocked,
 455 * regardless of success/failure of the function.  As a result, the caller
 456 * will always need to vrele(), but not vput().
 457 */
 458static int
 459ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
 460    int attrnamespace, const char *attrname, struct lwp *l)
 461{
 462	int error;
 463
 464	error = VOP_OPEN(vp, FREAD|FWRITE, l->l_cred);
 465	if (error) {
 466		printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
 467		    "with %d\n", error);
 468		VOP_UNLOCK(vp);
 469		return (error);
 470	}
 471
 472	mutex_enter(vp->v_interlock);
 473	vp->v_writecount++;
 474	mutex_exit(vp->v_interlock);
 475
 476	vref(vp);
 477
 478	VOP_UNLOCK(vp);
 479
 480	error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, l);
 481	if (error != 0)
 482		vn_close(vp, FREAD|FWRITE, l->l_cred);
 483	return (error);
 484}
 485
 486/*
 487 * Given a locked directory vnode, iterate over the names in the directory
 488 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
 489 * attribute files.  Then invoke ufs_extattr_enable_with_open() on each
 490 * to attempt to start the attribute.  Leaves the directory locked on
 491 * exit.
 492 */
 493static int
 494ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
 495    int attrnamespace, struct lwp *l)
 496{
 497	struct vop_readdir_args vargs;
 498	struct statvfs *sbp = &ump->um_mountp->mnt_stat;
 499	struct dirent *dp, *edp;
 500	struct vnode *attr_vp;
 501	struct uio auio;
 502	struct iovec aiov;
 503	char *dirbuf;
 504	int error, eofflag = 0;
 505
 506	if (dvp->v_type != VDIR)
 507		return (ENOTDIR);
 508
 509	dirbuf = malloc(DIRBLKSIZ, M_TEMP, M_WAITOK);
 510
 511	auio.uio_iov = &aiov;
 512	auio.uio_iovcnt = 1;
 513	auio.uio_rw = UIO_READ;
 514	auio.uio_offset = 0;
 515	UIO_SETUP_SYSSPACE(&auio);
 516
 517	vargs.a_desc = NULL;
 518	vargs.a_vp = dvp;
 519	vargs.a_uio = &auio;
 520	vargs.a_cred = l->l_cred;
 521	vargs.a_eofflag = &eofflag;
 522	vargs.a_ncookies = NULL;
 523	vargs.a_cookies = NULL;
 524
 525	while (!eofflag) {
 526		auio.uio_resid = DIRBLKSIZ;
 527		aiov.iov_base = dirbuf;
 528		aiov.iov_len = DIRBLKSIZ;
 529		error = ufs_readdir(&vargs);
 530		if (error) {
 531			printf("ufs_extattr_iterate_directory: ufs_readdir "
 532			    "%d\n", error);
 533			return (error);
 534		}
 535
 536		/*
 537		 * XXXRW: While in UFS, we always get DIRBLKSIZ returns from
 538		 * the directory code on success, on other file systems this
 539		 * may not be the case.  For portability, we should check the
 540		 * read length on return from ufs_readdir().
 541		 */
 542		edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
 543		for (dp = (struct dirent *)dirbuf; dp < edp; ) {
 544			if (dp->d_reclen == 0)
 545				break;
 546			/* Skip "." and ".." */
 547			if (dp->d_name[0] == '.' &&
 548			    (dp->d_name[1] == '\0' ||
 549			     (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
 550				goto next;
 551			error = ufs_extattr_lookup(dvp, LOCKPARENT,
 552			    dp->d_name, &attr_vp, l);
 553			if (error == ENOENT) {
 554				goto next; /* keep silent */
 555			} else if (error) {
 556				printf("ufs_extattr_iterate_directory: lookup "
 557				    "%s %d\n", dp->d_name, error);
 558			} else if (attr_vp == dvp) {
 559				vrele(attr_vp);
 560			} else if (attr_vp->v_type != VREG) {
 561				vput(attr_vp);
 562			} else {
 563				error = ufs_extattr_enable_with_open(ump,
 564				    attr_vp, attrnamespace, dp->d_name, l);
 565				vrele(attr_vp);
 566				if (error) {
 567					printf("ufs_extattr_iterate_directory: "
 568					    "enable %s %d\n", dp->d_name,
 569					    error);
 570				} else if (bootverbose) {
 571					printf("%s: EA %s loaded\n",
 572					       sbp->f_mntonname, dp->d_name);
 573				}
 574			}
 575 next:
 576			dp = (struct dirent *) ((char *)dp + dp->d_reclen);
 577			if (dp >= edp)
 578				break;
 579		}
 580	}
 581	free(dirbuf, M_TEMP);
 582	
 583	return (0);
 584}
 585
 586/*
 587 * Auto-start of extended attributes, to be executed (optionally) at
 588 * mount-time.
 589 */
 590int
 591ufs_extattr_autostart(struct mount *mp, struct lwp *l)
 592{
 593	struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
 594	int error;
 595
 596	/*
 597	 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
 598	 * If so, automatically start EA's.
 599	 */
 600	error = VFS_ROOT(mp, &rvp);
 601	if (error) {
 602		printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
 603		    error);
 604		return (error);
 605	}
 606
 607	KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
 608
 609	error = ufs_extattr_lookup(rvp, 0,
 610	    UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, l);
 611	if (error) {
 612		/* rvp ref'd but now unlocked */
 613		KASSERT(VOP_ISLOCKED(rvp) == 0);
 614		vrele(rvp);
 615		return (error);
 616	}
 617	if (rvp == attr_dvp) {
 618		/* Should never happen. */
 619		KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
 620		vrele(attr_dvp);
 621		vput(rvp);
 622		return (EINVAL);
 623	}
 624	KASSERT(VOP_ISLOCKED(rvp) == 0);
 625	vrele(rvp);
 626
 627	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
 628
 629	if (attr_dvp->v_type != VDIR) {
 630		printf("ufs_extattr_autostart: %s != VDIR\n",
 631		    UFS_EXTATTR_FSROOTSUBDIR);
 632		goto return_vput_attr_dvp;
 633	}
 634
 635	error = ufs_extattr_start(mp, l);
 636	if (error) {
 637		printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
 638		    error);
 639		goto return_vput_attr_dvp;
 640	}
 641
 642	/*
 643	 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
 644	 * UFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
 645	 * and start with appropriate type.  Failures in either don't
 646	 * result in an over-all failure.  attr_dvp is left locked to
 647	 * be cleaned up on exit.
 648	 */
 649	error = ufs_extattr_lookup(attr_dvp, LOCKPARENT,
 650	    UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, l);
 651	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
 652	if (error == 0) {
 653		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
 654		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
 655		    attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, l);
 656		if (error)
 657			printf("ufs_extattr_iterate_directory returned %d\n",
 658			    error);
 659		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
 660		vput(attr_system_dvp);
 661	}
 662
 663	error = ufs_extattr_lookup(attr_dvp, LOCKPARENT,
 664	    UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, l);
 665	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
 666	if (error == 0) {
 667		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
 668		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
 669		    attr_user_dvp, EXTATTR_NAMESPACE_USER, l);
 670		if (error)
 671			printf("ufs_extattr_iterate_directory returned %d\n",
 672			    error);
 673		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
 674		vput(attr_user_dvp);
 675	}
 676
 677	/* Mask startup failures in sub-directories. */
 678	error = 0;
 679
 680 return_vput_attr_dvp:
 681	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
 682	vput(attr_dvp);
 683
 684	return (error);
 685}
 686
 687/*
 688 * Stop extended attribute support on an FS.
 689 */
 690void
 691ufs_extattr_stop(struct mount *mp, struct lwp *l)
 692{
 693	struct ufs_extattr_list_entry *uele;
 694	struct ufsmount *ump = VFSTOUFS(mp);
 695
 696	ufs_extattr_uepm_lock(ump);
 697
 698	/*
 699	 * If we haven't been started, no big deal.  Just short-circuit
 700	 * the processing work.
 701	 */
 702	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
 703		goto unlock;
 704	}
 705
 706	while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
 707		uele = LIST_FIRST(&ump->um_extattr.uepm_list);
 708		ufs_extattr_disable(ump, uele->uele_attrnamespace,
 709		    uele->uele_attrname, l);
 710	}
 711
 712	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
 713
 714	kauth_cred_free(ump->um_extattr.uepm_ucred);
 715	ump->um_extattr.uepm_ucred = NULL;
 716
 717 unlock:
 718	ufs_extattr_uepm_unlock(ump);
 719}
 720
 721/*
 722 * Enable a named attribute on the specified filesystem; provide an
 723 * unlocked backing vnode to hold the attribute data.
 724 */
 725static int
 726ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
 727    const char *attrname, struct vnode *backing_vnode, struct lwp *l)
 728{
 729	struct ufs_extattr_list_entry *attribute;
 730	struct iovec aiov;
 731	struct uio auio;
 732	int error = 0;
 733
 734	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
 735		return (EINVAL);
 736	if (backing_vnode->v_type != VREG)
 737		return (EINVAL);
 738
 739	attribute = malloc(sizeof(*attribute), M_UFS_EXTATTR,
 740	    M_WAITOK | M_ZERO);
 741
 742	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
 743		error = EOPNOTSUPP;
 744		goto free_exit;
 745	}
 746
 747	if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
 748		error = EEXIST;
 749		goto free_exit;
 750	}
 751
 752	strncpy(attribute->uele_attrname, attrname,
 753	    UFS_EXTATTR_MAXEXTATTRNAME);
 754	attribute->uele_attrnamespace = attrnamespace;
 755	memset(&attribute->uele_fileheader, 0,
 756	    sizeof(struct ufs_extattr_fileheader));
 757	
 758	attribute->uele_backing_vnode = backing_vnode;
 759
 760	auio.uio_iov = &aiov;
 761	auio.uio_iovcnt = 1;
 762	aiov.iov_base = (void *) &attribute->uele_fileheader;
 763	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
 764	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
 765	auio.uio_offset = (off_t) 0;
 766	auio.uio_rw = UIO_READ;
 767	UIO_SETUP_SYSSPACE(&auio);
 768
 769	vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
 770	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
 771	    ump->um_extattr.uepm_ucred);
 772
 773	if (error)
 774		goto unlock_free_exit;
 775
 776	if (auio.uio_resid != 0) {
 777		printf("ufs_extattr_enable: malformed attribute header\n");
 778		error = EINVAL;
 779		goto unlock_free_exit;
 780	}
 781
 782	/*
 783	 * Try to determine the byte order of the attribute file.
 784	 */
 785	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
 786		attribute->uele_flags |= UELE_F_NEEDSWAP;
 787		attribute->uele_fileheader.uef_magic =
 788		    ufs_rw32(attribute->uele_fileheader.uef_magic,
 789			     UELE_NEEDSWAP(attribute));
 790		if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
 791			printf("ufs_extattr_enable: invalid attribute header "
 792			       "magic\n");
 793			error = EINVAL;
 794			goto unlock_free_exit;
 795		}
 796	}
 797	attribute->uele_fileheader.uef_version =
 798	    ufs_rw32(attribute->uele_fileheader.uef_version,
 799		     UELE_NEEDSWAP(attribute));
 800	attribute->uele_fileheader.uef_size =
 801	    ufs_rw32(attribute->uele_fileheader.uef_size,
 802		     UELE_NEEDSWAP(attribute));
 803
 804	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
 805		printf("ufs_extattr_enable: incorrect attribute header "
 806		    "version\n");
 807		error = EINVAL;
 808		goto unlock_free_exit;
 809	}
 810
 811	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
 812	    uele_entries);
 813
 814	VOP_UNLOCK(backing_vnode);
 815	return (0);
 816
 817 unlock_free_exit:
 818	VOP_UNLOCK(backing_vnode);
 819
 820 free_exit:
 821	free(attribute, M_UFS_EXTATTR);
 822	return (error);
 823}
 824
 825/*
 826 * Disable extended attribute support on an FS.
 827 */
 828static int
 829ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
 830    const char *attrname, struct lwp *l)
 831{
 832	struct ufs_extattr_list_entry *uele;
 833	int error = 0;
 834
 835	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
 836		return (EINVAL);
 837
 838	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
 839	if (!uele)
 840		return (ENOATTR);
 841
 842	LIST_REMOVE(uele, uele_entries);
 843
 844	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
 845	    l->l_cred);
 846
 847	free(uele, M_UFS_EXTATTR);
 848
 849	return (error);
 850}
 851
 852/*
 853 * VFS call to manage extended attributes in UFS.  If filename_vp is
 854 * non-NULL, it must be passed in locked, and regardless of errors in
 855 * processing, will be unlocked.
 856 */
 857int
 858ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
 859    int attrnamespace, const char *attrname)
 860{
 861	struct lwp *l = curlwp;
 862	struct ufsmount *ump = VFSTOUFS(mp);
 863	int error;
 864
 865	/*
 866	 * Only privileged processes can configure extended attributes.
 867	 */
 868	if ((error = kauth_authorize_generic(l->l_cred, KAUTH_GENERIC_ISSUSER,
 869	    NULL)) != 0) {
 870		if (filename_vp != NULL)
 871			VOP_UNLOCK(filename_vp);
 872		return (error);
 873	}
 874
 875	switch(cmd) {
 876	case UFS_EXTATTR_CMD_START:
 877		if (filename_vp != NULL) {
 878			VOP_UNLOCK(filename_vp);
 879			return (EINVAL);
 880		}
 881		if (attrname != NULL)
 882			return (EINVAL);
 883
 884		error = ufs_extattr_autostart(mp, l);
 885		return (error);
 886		
 887	case UFS_EXTATTR_CMD_STOP:
 888		if (filename_vp != NULL) {
 889			VOP_UNLOCK(filename_vp);
 890			return (EINVAL);
 891		}
 892		if (attrname != NULL)
 893			return (EINVAL);
 894
 895		ufs_extattr_stop(mp, l);
 896		return (0);
 897
 898	case UFS_EXTATTR_CMD_ENABLE:
 899		if (filename_vp == NULL)
 900			return (EINVAL);
 901		if (attrname == NULL) {
 902			VOP_UNLOCK(filename_vp);
 903			return (EINVAL);
 904		}
 905
 906		/*
 907		 * ufs_extattr_enable_with_open() will always unlock the
 908		 * vnode, regardless of failure.
 909		 */
 910		ufs_extattr_uepm_lock(ump);
 911		error = ufs_extattr_enable_with_open(ump, filename_vp,
 912		    attrnamespace, attrname, l);
 913		ufs_extattr_uepm_unlock(ump);
 914		return (error);
 915
 916	case UFS_EXTATTR_CMD_DISABLE:
 917		if (filename_vp != NULL) {
 918			VOP_UNLOCK(filename_vp);
 919			return (EINVAL);
 920		}
 921		if (attrname == NULL)
 922			return (EINVAL);
 923
 924		ufs_extattr_uepm_lock(ump);
 925		error = ufs_extattr_disable(ump, attrnamespace, attrname, l);
 926		ufs_extattr_uepm_unlock(ump);
 927		return (error);
 928
 929	default:
 930		return (EINVAL);
 931	}
 932}
 933
 934/*
 935 * Read extended attribute header for a given vnode and attribute.
 936 * Backing vnode should be locked and unlocked by caller.
 937 */
 938static int
 939ufs_extattr_get_header(struct vnode *vp, struct ufs_extattr_list_entry *uele,
 940    struct ufs_extattr_header *ueh, off_t *bap)
 941{
 942	struct mount *mp = vp->v_mount;
 943	struct ufsmount *ump = VFSTOUFS(mp);
 944	struct inode *ip = VTOI(vp);
 945	off_t base_offset;
 946	struct iovec aiov;
 947	struct uio aio;
 948	int error;
 949
 950	/*
 951	 * Find base offset of header in file based on file header size, and
 952	 * data header size + maximum data size, indexed by inode number.
 953	 */
 954	base_offset = sizeof(struct ufs_extattr_fileheader) +
 955	    ip->i_number * (sizeof(struct ufs_extattr_header) +
 956	    uele->uele_fileheader.uef_size);
 957
 958	/*
 959	 * Read in the data header to see if the data is defined, and if so
 960	 * how much.
 961	 */
 962	memset(ueh, 0, sizeof(struct ufs_extattr_header));
 963	aiov.iov_base = ueh;
 964	aiov.iov_len = sizeof(struct ufs_extattr_header);
 965	aio.uio_iov = &aiov;
 966	aio.uio_iovcnt = 1;
 967	aio.uio_rw = UIO_READ;
 968	aio.uio_offset = base_offset;
 969	aio.uio_resid = sizeof(struct ufs_extattr_header);
 970	UIO_SETUP_SYSSPACE(&aio);
 971
 972	error = VOP_READ(uele->uele_backing_vnode, &aio,
 973	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
 974	if (error)
 975		return error;
 976
 977	/*
 978	 * Attribute headers are kept in file system byte order.
 979	 * XXX What about the blob of data?
 980	 */
 981	ueh->ueh_flags = ufs_rw32(ueh->ueh_flags, UELE_NEEDSWAP(uele));
 982	ueh->ueh_len   = ufs_rw32(ueh->ueh_len, UELE_NEEDSWAP(uele));
 983	ueh->ueh_i_gen = ufs_rw32(ueh->ueh_i_gen, UELE_NEEDSWAP(uele));
 984
 985	/* Defined? */
 986	if ((ueh->ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0)
 987		return ENOATTR;
 988
 989	/* Valid for the current inode generation? */
 990	if (ueh->ueh_i_gen != ip->i_gen) {
 991		/*
 992		 * The inode itself has a different generation number
 993		 * than the uele data.  For now, the best solution
 994		 * is to coerce this to undefined, and let it get cleaned
 995		 * up by the next write or extattrctl clean.
 996		 */
 997		printf("%s (%s): inode gen inconsistency (%u, %jd)\n",
 998		       __func__,  mp->mnt_stat.f_mntonname, ueh->ueh_i_gen,
 999		       (intmax_t)ip->i_gen);
1000		return ENOATTR;
1001	}
1002
1003	/* Local size consistency check. */
1004	if (ueh->ueh_len > uele->uele_fileheader.uef_size)
1005		return ENXIO;
1006
1007	/* Return base offset */
1008	if (bap != NULL)
1009		*bap = base_offset;
1010
1011	return 0;
1012}
1013
1014/*
1015 * Vnode operation to retrieve a named extended attribute.
1016 */
1017int
1018ufs_getextattr(struct vop_getextattr_args *ap)
1019/*
1020vop_getextattr {
1021	IN struct vnode *a_vp;
1022	IN int a_attrnamespace;
1023	IN const char *a_name;
1024	INOUT struct uio *a_uio;
1025	OUT size_t *a_size;
1026	IN kauth_cred_t a_cred;
1027};
1028*/
1029{
1030	struct mount *mp = ap->a_vp->v_mount;
1031	struct ufsmount *ump = VFSTOUFS(mp);
1032	int error;
1033
1034	ufs_extattr_uepm_lock(ump);
1035
1036	error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1037	    ap->a_uio, ap->a_size, ap->a_cred, curlwp);
1038
1039	ufs_extattr_uepm_unlock(ump);
1040
1041	return (error);
1042}
1043
1044/*
1045 * Real work associated with retrieving a named attribute--assumes that
1046 * the attribute lock has already been grabbed.
1047 */
1048static int
1049ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
1050    struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l)
1051{
1052	struct ufs_extattr_list_entry *attribute;
1053	struct ufs_extattr_header ueh;
1054	struct mount *mp = vp->v_mount;
1055	struct ufsmount *ump = VFSTOUFS(mp);
1056	off_t base_offset;
1057	size_t len, old_len;
1058	int error = 0;
1059
1060	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1061		return (EOPNOTSUPP);
1062
1063	if (strlen(name) == 0)
1064		return (EINVAL);
1065
1066	error = extattr_check_cred(vp, attrnamespace, cred, l, IREAD);
1067	if (error)
1068		return (error);
1069
1070	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1071	if (!attribute)
1072		return (ENOATTR);
1073
1074	/*
1075	 * Allow only offsets of zero to encourage the read/replace
1076	 * extended attribute semantic.  Otherwise we can't guarantee
1077	 * atomicity, as we don't provide locks for extended attributes.
1078	 */
1079	if (uio != NULL && uio->uio_offset != 0)
1080		return (ENXIO);
1081
1082	/*
1083	 * Don't need to get a lock on the backing file if the getattr is
1084	 * being applied to the backing file, as the lock is already held.
1085	 */
1086	if (attribute->uele_backing_vnode != vp)
1087		vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
1088
1089	error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1090	if (error)
1091		goto vopunlock_exit;
1092
1093	/* Return full data size if caller requested it. */
1094	if (size != NULL)
1095		*size = ueh.ueh_len;
1096
1097	/* Return data if the caller requested it. */
1098	if (uio != NULL) {
1099		/* Allow for offset into the attribute data. */
1100		uio->uio_offset = base_offset + sizeof(struct
1101		    ufs_extattr_header);
1102
1103		/*
1104		 * Figure out maximum to transfer -- use buffer size and
1105		 * local data limit.
1106		 */
1107		len = MIN(uio->uio_resid, ueh.ueh_len);
1108		old_len = uio->uio_resid;
1109		uio->uio_resid = len;
1110
1111		error = VOP_READ(attribute->uele_backing_vnode, uio,
1112		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1113		if (error)
1114			goto vopunlock_exit;
1115
1116		uio->uio_resid = old_len - (len - uio->uio_resid);
1117	}
1118
1119 vopunlock_exit:
1120
1121	if (uio != NULL)
1122		uio->uio_offset = 0;
1123
1124	if (attribute->uele_backing_vnode != vp)
1125		VOP_UNLOCK(attribute->uele_backing_vnode);
1126
1127	return (error);
1128}
1129
1130/*
1131 * Vnode operation to list extended attribute for a vnode
1132 */
1133int
1134ufs_listextattr(struct vop_listextattr_args *ap)
1135/*
1136vop_listextattr {
1137	IN struct vnode *a_vp;
1138	IN int a_attrnamespace;
1139	INOUT struct uio *a_uio;
1140	OUT size_t *a_size;
1141	IN int flag;
1142	IN kauth_cred_t a_cred;
1143	struct proc *a_p;
1144};
1145*/
1146{
1147	struct mount *mp = ap->a_vp->v_mount;
1148	struct ufsmount *ump = VFSTOUFS(mp);
1149	int error;
1150
1151	ufs_extattr_uepm_lock(ump);
1152
1153	error = ufs_extattr_list(ap->a_vp, ap->a_attrnamespace,
1154	    ap->a_uio, ap->a_size, ap->a_flag, ap->a_cred, curlwp);
1155
1156	ufs_extattr_uepm_unlock(ump);
1157
1158	return (error);
1159}
1160
1161/*
1162 * Real work associated with retrieving list of attributes--assumes that
1163 * the attribute lock has already been grabbed.
1164 */
1165static int
1166ufs_extattr_list(struct vnode *vp, int attrnamespace,
1167    struct uio *uio, size_t *size, int flag, 
1168    kauth_cred_t cred, struct lwp *l)
1169{
1170	struct ufs_extattr_list_entry *uele;
1171	struct ufs_extattr_header ueh;
1172	struct mount *mp = vp->v_mount;
1173	struct ufsmount *ump = VFSTOUFS(mp);
1174	size_t listsize = 0;
1175	int error = 0;
1176
1177	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1178		return (EOPNOTSUPP);
1179
1180	error = extattr_check_cred(vp, attrnamespace, cred, l, IREAD);
1181	if (error)
1182		return (error);
1183
1184	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) {
1185		unsigned char attrnamelen;
1186
1187		if (uele->uele_attrnamespace != attrnamespace)
1188			continue;
1189
1190		error = ufs_extattr_get_header(vp, uele, &ueh, NULL);
1191		if (error == ENOATTR)
1192			continue;	
1193		if (error != 0)
1194			return error;
1195
1196		/*
1197		 * Don't need to get a lock on the backing file if 
1198		 * the listattr is being applied to the backing file, 
1199		 * as the lock is already held.
1200		 */
1201		if (uele->uele_backing_vnode != vp)
1202			vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
1203
1204		/*
1205		 * +1 for trailing NUL (listxattr flavor)
1206		 *  or leading name length (extattr_list_file flavor)
1207	 	 */
1208		attrnamelen = strlen(uele->uele_attrname);
1209		listsize += attrnamelen + 1;
1210
1211		/* Return data if the caller requested it. */
1212		if (uio != NULL) {
1213			/*
1214			 * We support two flavors. Either NUL-terminated
1215			 * strings (a la listxattr), or non NUL-terminated,
1216			 * one byte length prefixed strings (for
1217			 * extattr_list_file). EXTATTR_LIST_LENPREFIX switches
1218		 	 * that second behavior.
1219			 */
1220			if (flag & EXTATTR_LIST_LENPREFIX) {
1221				uint8_t len = (uint8_t)attrnamelen;
1222
1223				/* Copy leading name length */
1224				error = uiomove(&len, sizeof(len), uio);
1225				if (error != 0)
1226					break;	
1227			} else {
1228				/* Include trailing NULL */
1229				attrnamelen++; 
1230			}
1231
1232			error = uiomove(uele->uele_attrname, 
1233					(size_t)attrnamelen, uio);
1234			if (error != 0)
1235				break;	
1236		}
1237
1238		if (uele->uele_backing_vnode != vp)
1239			VOP_UNLOCK(uele->uele_backing_vnode);
1240
1241		if (error != 0)
1242			return error;
1243	}
1244
1245	if (uio != NULL)
1246		uio->uio_offset = 0;
1247
1248	/* Return full data size if caller requested it. */
1249	if (size != NULL)
1250		*size = listsize;
1251
1252	return 0;
1253}
1254
1255/*
1256 * Vnode operation to remove a named attribute.
1257 */
1258int
1259ufs_deleteextattr(struct vop_deleteextattr_args *ap)
1260/*
1261vop_deleteextattr {
1262	IN struct vnode *a_vp;
1263	IN int a_attrnamespace;
1264	IN const char *a_name;
1265	IN kauth_cred_t a_cred;
1266};
1267*/
1268{
1269	struct mount *mp = ap->a_vp->v_mount;
1270	struct ufsmount *ump = VFSTOUFS(mp); 
1271	int error;
1272
1273	ufs_extattr_uepm_lock(ump);
1274
1275	error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1276	    ap->a_cred, curlwp);
1277
1278	ufs_extattr_uepm_unlock(ump);
1279
1280	return (error);
1281}
1282
1283/*
1284 * Vnode operation to set a named attribute.
1285 */
1286int
1287ufs_setextattr(struct vop_setextattr_args *ap)
1288/*
1289vop_setextattr {
1290	IN struct vnode *a_vp;
1291	IN int a_attrnamespace;
1292	IN const char *a_name;
1293	INOUT struct uio *a_uio;
1294	IN kauth_cred_t a_cred;
1295};
1296*/
1297{
1298	struct mount *mp = ap->a_vp->v_mount;
1299	struct ufsmount *ump = VFSTOUFS(mp); 
1300	int error;
1301
1302	ufs_extattr_uepm_lock(ump);
1303
1304	/*
1305	 * XXX: No longer a supported way to delete extended attributes.
1306	 */
1307	if (ap->a_uio == NULL) {
1308		ufs_extattr_uepm_unlock(ump);
1309		return (EINVAL);
1310	}
1311
1312	error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1313	    ap->a_uio, ap->a_cred, curlwp);
1314
1315	ufs_extattr_uepm_unlock(ump);
1316
1317	return (error);
1318}
1319
1320/*
1321 * Real work associated with setting a vnode's extended attributes;
1322 * assumes that the attribute lock has already been grabbed.
1323 */
1324static int
1325ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1326    struct uio *uio, kauth_cred_t cred, struct lwp *l)
1327{
1328	struct ufs_extattr_list_entry *attribute;
1329	struct ufs_extattr_header ueh;
1330	struct iovec local_aiov;
1331	struct uio local_aio;
1332	struct mount *mp = vp->v_mount;
1333	struct ufsmount *ump = VFSTOUFS(mp);
1334	struct inode *ip = VTOI(vp);
1335	off_t base_offset;
1336	int error = 0, ioflag;
1337
1338	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1339		return (EROFS);
1340	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1341		return (EOPNOTSUPP);
1342	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1343		return (EINVAL);
1344
1345	error = extattr_check_cred(vp, attrnamespace, cred, l, IWRITE);
1346	if (error)
1347		return (error);
1348
1349	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1350	if (!attribute) {
1351		attribute =  ufs_extattr_autocreate_attr(vp, attrnamespace, 
1352							 name, l);
1353		if  (!attribute)
1354			return (ENOATTR);
1355	}
1356
1357	/*
1358	 * Early rejection of invalid offsets/length.
1359	 * Reject: any offset but 0 (replace)
1360	 *	 Any size greater than attribute size limit
1361 	 */
1362	if (uio->uio_offset != 0 ||
1363	    uio->uio_resid > attribute->uele_fileheader.uef_size)
1364		return (ENXIO);
1365
1366	/*
1367	 * Find base offset of header in file based on file header size, and
1368	 * data header size + maximum data size, indexed by inode number.
1369	 */
1370	base_offset = sizeof(struct ufs_extattr_fileheader) +
1371	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1372	    attribute->uele_fileheader.uef_size);
1373
1374	/*
1375	 * Write out a data header for the data.
1376	 */
1377	ueh.ueh_len = ufs_rw32((uint32_t) uio->uio_resid,
1378	    UELE_NEEDSWAP(attribute));
1379	ueh.ueh_flags = ufs_rw32(UFS_EXTATTR_ATTR_FLAG_INUSE,
1380				 UELE_NEEDSWAP(attribute));
1381	ueh.ueh_i_gen = ufs_rw32(ip->i_gen, UELE_NEEDSWAP(attribute));
1382	local_aiov.iov_base = &ueh;
1383	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1384	local_aio.uio_iov = &local_aiov;
1385	local_aio.uio_iovcnt = 1;
1386	local_aio.uio_rw = UIO_WRITE;
1387	local_aio.uio_offset = base_offset;
1388	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1389	UIO_SETUP_SYSSPACE(&local_aio);
1390
1391	/*
1392	 * Don't need to get a lock on the backing file if the setattr is
1393	 * being applied to the backing file, as the lock is already held.
1394	 */
1395	if (attribute->uele_backing_vnode != vp)
1396		vn_lock(attribute->uele_backing_vnode, 
1397		    LK_EXCLUSIVE | LK_RETRY);
1398
1399	ioflag = IO_NODELOCKED;
1400	if (ufs_extattr_sync)
1401		ioflag |= IO_SYNC;
1402	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1403	    ump->um_extattr.uepm_ucred);
1404	if (error)
1405		goto vopunlock_exit;
1406
1407	if (local_aio.uio_resid != 0) {
1408		error = ENXIO;
1409		goto vopunlock_exit;
1410	}
1411
1412	/*
1413	 * Write out user data.
1414	 * XXX NOT ATOMIC WITH RESPECT TO THE HEADER.
1415	 */
1416	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1417
1418	ioflag = IO_NODELOCKED;
1419	if (ufs_extattr_sync)
1420		ioflag |= IO_SYNC;
1421	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1422	    ump->um_extattr.uepm_ucred);
1423
1424 vopunlock_exit:
1425	uio->uio_offset = 0;
1426
1427	if (attribute->uele_backing_vnode != vp)
1428		VOP_UNLOCK(attribute->uele_backing_vnode);
1429
1430	return (error);
1431}
1432
1433/*
1434 * Real work associated with removing an extended attribute from a vnode.
1435 * Assumes the attribute lock has already been grabbed.
1436 */
1437static int
1438ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1439    kauth_cred_t cred, struct lwp *l)
1440{
1441	struct ufs_extattr_list_entry *attribute;
1442	struct ufs_extattr_header ueh;
1443	struct mount *mp = vp->v_mount;
1444	struct ufsmount *ump = VFSTOUFS(mp);
1445	struct iovec local_aiov;
1446	struct uio local_aio;
1447	off_t base_offset;
1448	int error = 0, ioflag;
1449
1450	if (vp->v_mount->mnt_flag & MNT_RDONLY)  
1451		return (EROFS);
1452	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1453		return (EOPNOTSUPP);
1454	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1455		return (EINVAL);
1456
1457	error = extattr_check_cred(vp, attrnamespace, cred, l, IWRITE);
1458	if (error)
1459		return (error);
1460
1461	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1462	if (!attribute)
1463		return (ENOATTR);
1464
1465	/*
1466	 * Don't need to get a lock on the backing file if the getattr is
1467	 * being applied to the backing file, as the lock is already held.
1468	 */
1469	if (attribute->uele_backing_vnode != vp)
1470		vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
1471
1472	error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1473	if (error)
1474		goto vopunlock_exit;
1475
1476	/* Flag it as not in use. */
1477	ueh.ueh_flags = 0;		/* No need to byte swap 0 */
1478	ueh.ueh_len = 0;		/* ...ditto... */
1479
1480	local_aiov.iov_base = &ueh;
1481	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1482	local_aio.uio_iov = &local_aiov;
1483	local_aio.uio_iovcnt = 1;
1484	local_aio.uio_rw = UIO_WRITE;
1485	local_aio.uio_offset = base_offset;
1486	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1487	UIO_SETUP_SYSSPACE(&local_aio);
1488
1489	ioflag = IO_NODELOCKED;
1490	if (ufs_extattr_sync)
1491		ioflag |= IO_SYNC;
1492	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1493	    ump->um_extattr.uepm_ucred);
1494	if (error)
1495		goto vopunlock_exit;
1496
1497	if (local_aio.uio_resid != 0)
1498		error = ENXIO;
1499
1500 vopunlock_exit:
1501	VOP_UNLOCK(attribute->uele_backing_vnode);
1502
1503	return (error);
1504}
1505
1506/*
1507 * Called by UFS when an inode is no longer active and should have its
1508 * attributes stripped.
1509 */
1510void
1511ufs_extattr_vnode_inactive(struct vnode *vp, struct lwp *l)
1512{
1513	struct ufs_extattr_list_entry *uele;
1514	struct mount *mp = vp->v_mount;
1515	struct ufsmount *ump = VFSTOUFS(mp);
1516
1517	/*
1518	 * In that case, we cannot lock. We should not have any active vnodes
1519	 * on the fs if this is not yet initialized but is going to be, so
1520	 * this can go unlocked.
1521	 */
1522	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1523		return;
1524
1525	ufs_extattr_uepm_lock(ump);
1526
1527	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1528		ufs_extattr_uepm_unlock(ump);
1529		return;
1530	}
1531
1532	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1533		ufs_extattr_rm(vp, uele->uele_attrnamespace,
1534		    uele->uele_attrname, lwp0.l_cred, l);
1535
1536	ufs_extattr_uepm_unlock(ump);
1537}
1538
1539void
1540ufs_extattr_init(void)
1541{
1542
1543	malloc_type_attach(M_UFS_EXTATTR);
1544}
1545
1546void
1547ufs_extattr_done(void)
1548{
1549
1550	malloc_type_detach(M_UFS_EXTATTR);
1551}