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