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