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