ufs_extattr.c revision 91406
1/*-
2 * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson
3 * Copyright (c) 2002 Networks Associates Technologies, Inc.
4 * All rights reserved.
5 *
6 * This software was developed by Robert Watson for the TrustedBSD Project.
7 *
8 * This software was developed for the FreeBSD Project in part by NAI Labs,
9 * the Security Research Division of Network Associates, Inc. under
10 * DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA
11 * CHATS research program.
12 *
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 *    notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 *    notice, this list of conditions and the following disclaimer in the
20 *    documentation and/or other materials provided with the distribution.
21 * 3. The names of the authors may not be used to endorse or promote
22 *    products derived from this software without specific prior written
23 *    permission.
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 * $FreeBSD: head/sys/ufs/ufs/ufs_extattr.c 91406 2002-02-27 18:32:23Z jhb $
38 */
39/*
40 * Developed by the TrustedBSD Project.
41 * Support for file system extended attribute: UFS-specific support functions.
42 */
43
44#include "opt_ufs.h"
45
46#include <sys/param.h>
47#include <sys/systm.h>
48#include <sys/kernel.h>
49#include <sys/namei.h>
50#include <sys/malloc.h>
51#include <sys/fcntl.h>
52#include <sys/proc.h>
53#include <sys/vnode.h>
54#include <sys/mount.h>
55#include <sys/lock.h>
56#include <sys/dirent.h>
57#include <sys/extattr.h>
58#include <sys/sysctl.h>
59
60#include <vm/vm_zone.h>
61
62#include <ufs/ufs/dir.h>
63#include <ufs/ufs/extattr.h>
64#include <ufs/ufs/quota.h>
65#include <ufs/ufs/ufsmount.h>
66#include <ufs/ufs/inode.h>
67#include <ufs/ufs/ufs_extern.h>
68
69#ifdef UFS_EXTATTR
70
71#define	MIN(a,b) (((a)<(b))?(a):(b))
72
73static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
74
75static int ufs_extattr_sync = 0;
76SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync,
77    0, "");
78
79static int	ufs_extattr_valid_attrname(int attrnamespace,
80		    const char *attrname);
81static int	ufs_extattr_credcheck(struct vnode *vp,
82		    struct ufs_extattr_list_entry *uele, struct ucred *cred,
83		    struct thread *td, int access);
84static int	ufs_extattr_enable_with_open(struct ufsmount *ump,
85		    struct vnode *vp, int attrnamespace, const char *attrname,
86		    struct thread *td);
87static int	ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
88		    const char *attrname, struct vnode *backing_vnode,
89		    struct thread *td);
90static int	ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
91		    const char *attrname, struct thread *td);
92static int	ufs_extattr_get(struct vnode *vp, int attrnamespace,
93		    const char *name, struct uio *uio, size_t *size,
94		    struct ucred *cred, struct thread *td);
95static int	ufs_extattr_set(struct vnode *vp, int attrnamespace,
96		    const char *name, struct uio *uio, struct ucred *cred,
97		    struct thread *td);
98static int	ufs_extattr_rm(struct vnode *vp, int attrnamespace,
99		    const char *name, struct ucred *cred, struct thread *td);
100
101/*
102 * Per-FS attribute lock protecting attribute operations.
103 * XXX Right now there is a lot of lock contention due to having a single
104 * lock per-FS; really, this should be far more fine-grained.
105 */
106static void
107ufs_extattr_uepm_lock(struct ufsmount *ump, struct thread *td)
108{
109
110	/* Ideally, LK_CANRECURSE would not be used, here. */
111	lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY |
112	    LK_CANRECURSE, 0, td);
113}
114
115static void
116ufs_extattr_uepm_unlock(struct ufsmount *ump, struct thread *td)
117{
118
119	lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, td);
120}
121
122/*
123 * Determine whether the name passed is a valid name for an actual
124 * attribute.
125 *
126 * Invalid currently consists of:
127 *	 NULL pointer for attrname
128 *	 zero-length attrname (used to retrieve application attribute list)
129 */
130static int
131ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
132{
133
134	if (attrname == NULL)
135		return (0);
136	if (strlen(attrname) == 0)
137		return (0);
138	return (1);
139}
140
141/*
142 * Locate an attribute given a name and mountpoint.
143 * Must be holding uepm lock for the mount point.
144 */
145static struct ufs_extattr_list_entry *
146ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
147    const char *attrname)
148{
149	struct ufs_extattr_list_entry	*search_attribute;
150
151	for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
152	    search_attribute;
153	    search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
154		if (!(strncmp(attrname, search_attribute->uele_attrname,
155		    UFS_EXTATTR_MAXEXTATTRNAME)) &&
156		    (attrnamespace == search_attribute->uele_attrnamespace)) {
157			return (search_attribute);
158		}
159	}
160
161	return (0);
162}
163
164/*
165 * Initialize per-FS structures supporting extended attributes.  Do not
166 * start extended attributes yet.
167 */
168void
169ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
170{
171
172	uepm->uepm_flags = 0;
173
174	LIST_INIT(&uepm->uepm_list);
175	/* XXX is PVFS right, here? */
176	lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0);
177	uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
178}
179
180/*
181 * Destroy per-FS structures supporting extended attributes.  Assumes
182 * that EAs have already been stopped, and will panic if not.
183 */
184void
185ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
186{
187
188	if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
189		panic("ufs_extattr_uepm_destroy: not initialized");
190
191	if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
192		panic("ufs_extattr_uepm_destroy: called while still started");
193
194	/*
195	 * It's not clear that either order for the next two lines is
196	 * ideal, and it should never be a problem if this is only called
197	 * during unmount, and with vfs_busy().
198	 */
199	uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
200	lockdestroy(&uepm->uepm_lock);
201}
202
203/*
204 * Start extended attribute support on an FS.
205 */
206int
207ufs_extattr_start(struct mount *mp, struct thread *td)
208{
209	struct ufsmount	*ump;
210	int	error = 0;
211
212	ump = VFSTOUFS(mp);
213
214	ufs_extattr_uepm_lock(ump, td);
215
216	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
217		error = EOPNOTSUPP;
218		goto unlock;
219	}
220	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
221		error = EBUSY;
222		goto unlock;
223	}
224
225	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
226
227	ump->um_extattr.uepm_ucred = crhold(td->td_ucred);
228
229unlock:
230	ufs_extattr_uepm_unlock(ump, td);
231
232	return (error);
233}
234
235#ifdef UFS_EXTATTR_AUTOSTART
236/*
237 * Helper routine: given a locked parent directory and filename, return
238 * the locked vnode of the inode associated with the name.  Will not
239 * follow symlinks, may return any type of vnode.  Lock on parent will
240 * be released even in the event of a failure.  In the event that the
241 * target is the parent (i.e., "."), there will be two references and
242 * one lock, requiring the caller to possibly special-case.
243 */
244#define	UE_GETDIR_LOCKPARENT	1
245#define	UE_GETDIR_LOCKPARENT_DONT	2
246static int
247ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname,
248    struct vnode **vp, struct thread *td)
249{
250	struct vop_cachedlookup_args vargs;
251	struct componentname cnp;
252	struct vnode *target_vp;
253	int error;
254
255	bzero(&cnp, sizeof(cnp));
256	cnp.cn_nameiop = LOOKUP;
257	cnp.cn_flags = ISLASTCN;
258	if (lockparent == UE_GETDIR_LOCKPARENT)
259		cnp.cn_flags |= LOCKPARENT;
260	cnp.cn_thread = td;
261	cnp.cn_cred = td->td_ucred;
262	cnp.cn_pnbuf = zalloc(namei_zone);
263	cnp.cn_nameptr = cnp.cn_pnbuf;
264	error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN,
265	    (size_t *) &cnp.cn_namelen);
266	if (error) {
267		if (lockparent == UE_GETDIR_LOCKPARENT_DONT) {
268			VOP_UNLOCK(start_dvp, 0, td);
269		}
270		zfree(namei_zone, cnp.cn_pnbuf);
271		printf("ufs_extattr_lookup: copystr failed\n");
272		return (error);
273	}
274	cnp.cn_namelen--;	/* trim nul termination */
275	vargs.a_desc = NULL;
276	vargs.a_dvp = start_dvp;
277	vargs.a_vpp = &target_vp;
278	vargs.a_cnp = &cnp;
279	error = ufs_lookup(&vargs);
280	zfree(namei_zone, cnp.cn_pnbuf);
281	if (error) {
282		/*
283		 * Error condition, may have to release the lock on the parent
284		 * if ufs_lookup() didn't.
285		 */
286		if (!(cnp.cn_flags & PDIRUNLOCK) &&
287		    (lockparent == UE_GETDIR_LOCKPARENT_DONT))
288			VOP_UNLOCK(start_dvp, 0, td);
289
290		/*
291		 * Check that ufs_lookup() didn't release the lock when we
292		 * didn't want it to.
293		 */
294		if ((cnp.cn_flags & PDIRUNLOCK) &&
295		    (lockparent == UE_GETDIR_LOCKPARENT))
296			panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK");
297
298		return (error);
299	}
300/*
301	if (target_vp == start_dvp)
302		panic("ufs_extattr_lookup: target_vp == start_dvp");
303*/
304
305	if (target_vp != start_dvp &&
306	    !(cnp.cn_flags & PDIRUNLOCK) &&
307	    (lockparent == UE_GETDIR_LOCKPARENT_DONT))
308		panic("ufs_extattr_lookup: !lockparent but !PDIRUNLOCK");
309
310	if ((cnp.cn_flags & PDIRUNLOCK) &&
311	    (lockparent == UE_GETDIR_LOCKPARENT))
312		panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK");
313
314	/* printf("ufs_extattr_lookup: success\n"); */
315	*vp = target_vp;
316	return (0);
317}
318#endif /* !UFS_EXTATTR_AUTOSTART */
319
320/*
321 * Enable an EA using the passed file system, backing vnode, attribute name,
322 * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
323 * to be locked when passed in.  The vnode will be returned unlocked,
324 * regardless of success/failure of the function.  As a result, the caller
325 * will always need to vrele(), but not vput().
326 */
327static int
328ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
329    int attrnamespace, const char *attrname, struct thread *td)
330{
331	int error;
332
333	error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td);
334	if (error) {
335		printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
336		    "with %d\n", error);
337		VOP_UNLOCK(vp, 0, td);
338		return (error);
339	}
340
341	/*
342	 * XXX: Note, should VOP_CLOSE() if vfs_object_create() fails, but due
343	 * to a similar piece of code in vn_open(), we don't.
344	 */
345	if (vn_canvmio(vp) == TRUE)
346		if ((error = vfs_object_create(vp, td,
347		    td->td_ucred)) != 0) {
348			/*
349			 * XXX: bug replicated from vn_open(): should
350			 * VOP_CLOSE() here.
351			 */
352			VOP_UNLOCK(vp, 0, td);
353			return (error);
354		}
355
356	vp->v_writecount++;
357
358	vref(vp);
359
360	VOP_UNLOCK(vp, 0, td);
361
362	error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td);
363	if (error != 0)
364		vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
365	return (error);
366}
367
368#ifdef UFS_EXTATTR_AUTOSTART
369/*
370 * Given a locked directory vnode, iterate over the names in the directory
371 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
372 * attribute files.  Then invoke ufs_extattr_enable_with_open() on each
373 * to attempt to start the attribute.  Leaves the directory locked on
374 * exit.
375 */
376static int
377ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
378    int attrnamespace, struct thread *td)
379{
380	struct vop_readdir_args vargs;
381	struct dirent *dp, *edp;
382	struct vnode *attr_vp;
383	struct uio auio;
384	struct iovec aiov;
385	char *dirbuf;
386	int error, eofflag = 0;
387
388	if (dvp->v_type != VDIR)
389		return (ENOTDIR);
390
391	MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK);
392
393	auio.uio_iov = &aiov;
394	auio.uio_iovcnt = 1;
395	auio.uio_rw = UIO_READ;
396	auio.uio_segflg = UIO_SYSSPACE;
397	auio.uio_td = td;
398	auio.uio_offset = 0;
399
400	vargs.a_desc = NULL;
401	vargs.a_vp = dvp;
402	vargs.a_uio = &auio;
403	vargs.a_cred = td->td_ucred;
404	vargs.a_eofflag = &eofflag;
405	vargs.a_ncookies = NULL;
406	vargs.a_cookies = NULL;
407
408	while (!eofflag) {
409		auio.uio_resid = DIRBLKSIZ;
410		aiov.iov_base = dirbuf;
411		aiov.iov_len = DIRBLKSIZ;
412		error = ufs_readdir(&vargs);
413		if (error) {
414			printf("ufs_extattr_iterate_directory: ufs_readdir "
415			    "%d\n", error);
416			return (error);
417		}
418
419		edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
420		for (dp = (struct dirent *)dirbuf; dp < edp; ) {
421#if (BYTE_ORDER == LITTLE_ENDIAN)
422			dp->d_type = dp->d_namlen;
423			dp->d_namlen = 0;
424#else
425			dp->d_type = 0;
426#endif
427			if (dp->d_reclen == 0)
428				break;
429			error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT,
430			    dp->d_name, &attr_vp, td);
431			if (error) {
432				printf("ufs_extattr_iterate_directory: lookup "
433				    "%s %d\n", dp->d_name, error);
434			} else if (attr_vp == dvp) {
435				vrele(attr_vp);
436			} else if (attr_vp->v_type != VREG) {
437				vput(attr_vp);
438			} else {
439				error = ufs_extattr_enable_with_open(ump,
440				    attr_vp, attrnamespace, dp->d_name, td);
441				vrele(attr_vp);
442				if (error) {
443					printf("ufs_extattr_iterate_directory: "
444					    "enable %s %d\n", dp->d_name,
445					    error);
446				} else if (bootverbose) {
447					printf("UFS autostarted EA %s\n",
448					    dp->d_name);
449				}
450			}
451			dp = (struct dirent *) ((char *)dp + dp->d_reclen);
452			if (dp >= edp)
453				break;
454		}
455	}
456	FREE(dirbuf, M_TEMP);
457
458	return (0);
459}
460
461/*
462 * Auto-start of extended attributes, to be executed (optionally) at
463 * mount-time.
464 */
465int
466ufs_extattr_autostart(struct mount *mp, struct thread *td)
467{
468	struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
469	int error;
470
471	/*
472	 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the file system root?
473	 * If so, automatically start EA's.
474	 */
475	error = VFS_ROOT(mp, &rvp);
476	if (error) {
477		printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
478		    error);
479		return (error);
480	}
481
482	error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT,
483	    UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td);
484	if (error) {
485		/* rvp ref'd but now unlocked */
486		vrele(rvp);
487		return (error);
488	}
489	if (rvp == attr_dvp) {
490		/* Should never happen. */
491		vrele(attr_dvp);
492		vput(rvp);
493		return (EINVAL);
494	}
495	vrele(rvp);
496
497	if (attr_dvp->v_type != VDIR) {
498		printf("ufs_extattr_autostart: %s != VDIR\n",
499		    UFS_EXTATTR_FSROOTSUBDIR);
500		goto return_vput_attr_dvp;
501	}
502
503	error = ufs_extattr_start(mp, td);
504	if (error) {
505		printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
506		    error);
507		goto return_vput_attr_dvp;
508	}
509
510	/*
511	 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
512	 * UFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
513	 * and start with appropriate type.  Failures in either don't
514	 * result in an over-all failure.  attr_dvp is left locked to
515	 * be cleaned up on exit.
516	 */
517	error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
518	    UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td);
519	if (!error) {
520		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
521		    attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td);
522		if (error)
523			printf("ufs_extattr_iterate_directory returned %d\n",
524			    error);
525		vput(attr_system_dvp);
526	}
527
528	error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
529	    UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td);
530	if (!error) {
531		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
532		    attr_user_dvp, EXTATTR_NAMESPACE_USER, td);
533		if (error)
534			printf("ufs_extattr_iterate_directory returned %d\n",
535			    error);
536		vput(attr_user_dvp);
537	}
538
539	/* Mask startup failures in sub-directories. */
540	error = 0;
541
542return_vput_attr_dvp:
543	vput(attr_dvp);
544
545	return (error);
546}
547#endif /* !UFS_EXTATTR_AUTOSTART */
548
549/*
550 * Stop extended attribute support on an FS.
551 */
552int
553ufs_extattr_stop(struct mount *mp, struct thread *td)
554{
555	struct ufs_extattr_list_entry	*uele;
556	struct ufsmount	*ump = VFSTOUFS(mp);
557	int	error = 0;
558
559	ufs_extattr_uepm_lock(ump, td);
560
561	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
562		error = EOPNOTSUPP;
563		goto unlock;
564	}
565
566	while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
567		uele = LIST_FIRST(&ump->um_extattr.uepm_list);
568		ufs_extattr_disable(ump, uele->uele_attrnamespace,
569		    uele->uele_attrname, td);
570	}
571
572	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
573
574	crfree(ump->um_extattr.uepm_ucred);
575	ump->um_extattr.uepm_ucred = NULL;
576
577unlock:
578	ufs_extattr_uepm_unlock(ump, td);
579
580	return (error);
581}
582
583/*
584 * Enable a named attribute on the specified file system; provide an
585 * unlocked backing vnode to hold the attribute data.
586 */
587static int
588ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
589    const char *attrname, struct vnode *backing_vnode, struct thread *td)
590{
591	struct ufs_extattr_list_entry	*attribute;
592	struct iovec	aiov;
593	struct uio	auio;
594	int	error = 0;
595
596	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
597		return (EINVAL);
598	if (backing_vnode->v_type != VREG)
599		return (EINVAL);
600
601	MALLOC(attribute, struct ufs_extattr_list_entry *,
602	    sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK);
603	if (attribute == NULL)
604		return (ENOMEM);
605
606	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
607		error = EOPNOTSUPP;
608		goto free_exit;
609	}
610
611	if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
612		error = EEXIST;
613		goto free_exit;
614	}
615
616	strncpy(attribute->uele_attrname, attrname,
617	    UFS_EXTATTR_MAXEXTATTRNAME);
618	attribute->uele_attrnamespace = attrnamespace;
619	bzero(&attribute->uele_fileheader,
620	    sizeof(struct ufs_extattr_fileheader));
621
622	attribute->uele_backing_vnode = backing_vnode;
623
624	auio.uio_iov = &aiov;
625	auio.uio_iovcnt = 1;
626	aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
627	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
628	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
629	auio.uio_offset = (off_t) 0;
630	auio.uio_segflg = UIO_SYSSPACE;
631	auio.uio_rw = UIO_READ;
632	auio.uio_td = td;
633
634	VOP_LEASE(backing_vnode, td, td->td_ucred, LEASE_WRITE);
635	vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, td);
636	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
637	    ump->um_extattr.uepm_ucred);
638	VOP_UNLOCK(backing_vnode, 0, td);
639
640	if (error)
641		goto free_exit;
642
643	if (auio.uio_resid != 0) {
644		printf("ufs_extattr_enable: malformed attribute header\n");
645		error = EINVAL;
646		goto free_exit;
647	}
648
649	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
650		printf("ufs_extattr_enable: invalid attribute header magic\n");
651		error = EINVAL;
652		goto free_exit;
653	}
654
655	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
656		printf("ufs_extattr_enable: incorrect attribute header "
657		    "version\n");
658		error = EINVAL;
659		goto free_exit;
660	}
661
662	backing_vnode->v_flag |= VSYSTEM;
663	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
664	    uele_entries);
665
666	return (0);
667
668free_exit:
669	FREE(attribute, M_UFS_EXTATTR);
670	return (error);
671}
672
673/*
674 * Disable extended attribute support on an FS.
675 */
676static int
677ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
678    const char *attrname, struct thread *td)
679{
680	struct ufs_extattr_list_entry	*uele;
681	int	error = 0;
682
683	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
684		return (EINVAL);
685
686	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
687	if (!uele)
688		/* XXX: ENOENT here will eventually be ENOATTR. */
689		return (ENOENT);
690
691	LIST_REMOVE(uele, uele_entries);
692
693	uele->uele_backing_vnode->v_flag &= ~VSYSTEM;
694	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
695	    td->td_ucred, td);
696
697	FREE(uele, M_UFS_EXTATTR);
698
699	return (error);
700}
701
702/*
703 * VFS call to manage extended attributes in UFS.  If filename_vp is
704 * non-NULL, it must be passed in locked, and regardless of errors in
705 * processing, will be unlocked.
706 */
707int
708ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
709    int attrnamespace, const char *attrname, struct thread *td)
710{
711	struct ufsmount	*ump = VFSTOUFS(mp);
712	int	error;
713
714	/*
715	 * Processes with privilege, but in jail, are not allowed to
716	 * configure extended attributes.
717	 */
718	if ((error = suser_xxx(td->td_ucred, td->td_proc, 0))) {
719		if (filename_vp != NULL)
720			VOP_UNLOCK(filename_vp, 0, td);
721		return (error);
722	}
723
724	switch(cmd) {
725	case UFS_EXTATTR_CMD_START:
726		if (filename_vp != NULL) {
727			VOP_UNLOCK(filename_vp, 0, td);
728			return (EINVAL);
729		}
730		if (attrname != NULL)
731			return (EINVAL);
732
733		error = ufs_extattr_start(mp, td);
734
735		return (error);
736
737	case UFS_EXTATTR_CMD_STOP:
738		if (filename_vp != NULL) {
739			VOP_UNLOCK(filename_vp, 0, td);
740			return (EINVAL);
741		}
742		if (attrname != NULL)
743			return (EINVAL);
744
745		error = ufs_extattr_stop(mp, td);
746
747		return (error);
748
749	case UFS_EXTATTR_CMD_ENABLE:
750
751		if (filename_vp == NULL)
752			return (EINVAL);
753		if (attrname == NULL) {
754			VOP_UNLOCK(filename_vp, 0, td);
755			return (EINVAL);
756		}
757
758		/*
759		 * ufs_extattr_enable_with_open() will always unlock the
760		 * vnode, regardless of failure.
761		 */
762		ufs_extattr_uepm_lock(ump, td);
763		error = ufs_extattr_enable_with_open(ump, filename_vp,
764		    attrnamespace, attrname, td);
765		ufs_extattr_uepm_unlock(ump, td);
766
767		return (error);
768
769	case UFS_EXTATTR_CMD_DISABLE:
770
771		if (filename_vp != NULL) {
772			VOP_UNLOCK(filename_vp, 0, td);
773			return (EINVAL);
774		}
775		if (attrname == NULL)
776			return (EINVAL);
777
778		ufs_extattr_uepm_lock(ump, td);
779		error = ufs_extattr_disable(ump, attrnamespace, attrname,
780		    td);
781		ufs_extattr_uepm_unlock(ump, td);
782
783		return (error);
784
785	default:
786		return (EINVAL);
787	}
788}
789
790/*
791 * Credential check based on process requesting service, and per-attribute
792 * permissions.
793 */
794static int
795ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele,
796    struct ucred *cred, struct thread *td, int access)
797{
798
799	/*
800	 * Kernel-invoked always succeeds.
801	 */
802	if (cred == NULL)
803		return (0);
804
805	/*
806	 * Do not allow privileged processes in jail to directly
807	 * manipulate system attributes.
808	 *
809	 * XXX What capability should apply here?
810	 * Probably CAP_SYS_SETFFLAG.
811	 */
812	switch (uele->uele_attrnamespace) {
813	case EXTATTR_NAMESPACE_SYSTEM:
814		/* Potentially should be: return (EPERM); */
815		return (suser_xxx(cred, td->td_proc, 0));
816	case EXTATTR_NAMESPACE_USER:
817		return (VOP_ACCESS(vp, access, cred, td));
818	default:
819		return (EPERM);
820	}
821}
822
823/*
824 * Vnode operating to retrieve a named extended attribute.
825 */
826int
827ufs_vop_getextattr(struct vop_getextattr_args *ap)
828/*
829vop_getextattr {
830	IN struct vnode *a_vp;
831	IN int a_attrnamespace;
832	IN const char *a_name;
833	INOUT struct uio *a_uio;
834	OUT struct size_t *a_size;
835	IN struct ucred *a_cred;
836	IN struct thread *a_td;
837};
838*/
839{
840	struct mount	*mp = ap->a_vp->v_mount;
841	struct ufsmount	*ump = VFSTOUFS(mp);
842	int	error;
843
844	ufs_extattr_uepm_lock(ump, ap->a_td);
845
846	error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
847	    ap->a_uio, ap->a_size, ap->a_cred, ap->a_td);
848
849	ufs_extattr_uepm_unlock(ump, ap->a_td);
850
851	return (error);
852}
853
854/*
855 * Real work associated with retrieving a named attribute--assumes that
856 * the attribute lock has already been grabbed.
857 */
858static int
859ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
860    struct uio *uio, size_t *size, struct ucred *cred, struct thread *td)
861{
862	struct ufs_extattr_list_entry	*attribute;
863	struct ufs_extattr_header	ueh;
864	struct iovec	local_aiov;
865	struct uio	local_aio;
866	struct mount	*mp = vp->v_mount;
867	struct ufsmount	*ump = VFSTOUFS(mp);
868	struct inode	*ip = VTOI(vp);
869	off_t	base_offset;
870	size_t	len, old_len;
871	int	error = 0;
872
873	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
874		return (EOPNOTSUPP);
875
876	if (strlen(name) == 0) {
877		/* XXX retrieve attribute lists. */
878		/* XXX should probably be checking for name == NULL? */
879		return (EINVAL);
880	}
881
882	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
883	if (!attribute)
884		/* XXX: ENOENT here will eventually be ENOATTR. */
885		return (ENOENT);
886
887	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IREAD)))
888		return (error);
889
890	/*
891	 * Allow only offsets of zero to encourage the read/replace
892	 * extended attribute semantic.  Otherwise we can't guarantee
893	 * atomicity, as we don't provide locks for extended attributes.
894	 */
895	if (uio != NULL && uio->uio_offset != 0)
896		return (ENXIO);
897
898	/*
899	 * Find base offset of header in file based on file header size, and
900	 * data header size + maximum data size, indexed by inode number.
901	 */
902	base_offset = sizeof(struct ufs_extattr_fileheader) +
903	    ip->i_number * (sizeof(struct ufs_extattr_header) +
904	    attribute->uele_fileheader.uef_size);
905
906	/*
907	 * Read in the data header to see if the data is defined, and if so
908	 * how much.
909	 */
910	bzero(&ueh, sizeof(struct ufs_extattr_header));
911	local_aiov.iov_base = (caddr_t) &ueh;
912	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
913	local_aio.uio_iov = &local_aiov;
914	local_aio.uio_iovcnt = 1;
915	local_aio.uio_rw = UIO_READ;
916	local_aio.uio_segflg = UIO_SYSSPACE;
917	local_aio.uio_td = td;
918	local_aio.uio_offset = base_offset;
919	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
920
921	/*
922	 * Acquire locks.
923	 */
924	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_READ);
925	/*
926	 * Don't need to get a lock on the backing file if the getattr is
927	 * being applied to the backing file, as the lock is already held.
928	 */
929	if (attribute->uele_backing_vnode != vp)
930		vn_lock(attribute->uele_backing_vnode, LK_SHARED |
931		    LK_NOPAUSE | LK_RETRY, td);
932
933	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
934	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
935	if (error)
936		goto vopunlock_exit;
937
938	/* Defined? */
939	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
940		/* XXX: ENOENT here will eventually be ENOATTR. */
941		error = ENOENT;
942		goto vopunlock_exit;
943	}
944
945	/* Valid for the current inode generation? */
946	if (ueh.ueh_i_gen != ip->i_gen) {
947		/*
948		 * The inode itself has a different generation number
949		 * than the attribute data.  For now, the best solution
950		 * is to coerce this to undefined, and let it get cleaned
951		 * up by the next write or extattrctl clean.
952		 */
953		printf("ufs_extattr_get: inode number inconsistency (%d, %d)\n",
954		    ueh.ueh_i_gen, ip->i_gen);
955		/* XXX: ENOENT here will eventually be ENOATTR. */
956		error = ENOENT;
957		goto vopunlock_exit;
958	}
959
960	/* Local size consistency check. */
961	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
962		error = ENXIO;
963		goto vopunlock_exit;
964	}
965
966	/* Return full data size if caller requested it. */
967	if (size != NULL)
968		*size = ueh.ueh_len;
969
970	/* Return data if the caller requested it. */
971	if (uio != NULL) {
972		/* Allow for offset into the attribute data. */
973		uio->uio_offset = base_offset + sizeof(struct
974		    ufs_extattr_header);
975
976		/*
977		 * Figure out maximum to transfer -- use buffer size and
978		 * local data limit.
979		 */
980		len = MIN(uio->uio_resid, ueh.ueh_len);
981		old_len = uio->uio_resid;
982		uio->uio_resid = len;
983
984		error = VOP_READ(attribute->uele_backing_vnode, uio,
985		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
986		if (error)
987			goto vopunlock_exit;
988
989		uio->uio_resid = old_len - (len - uio->uio_resid);
990	}
991
992vopunlock_exit:
993
994	if (uio != NULL)
995		uio->uio_offset = 0;
996
997	if (attribute->uele_backing_vnode != vp)
998		VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
999
1000	return (error);
1001}
1002
1003/*
1004 * Vnode operation to set a named attribute.
1005 */
1006int
1007ufs_vop_setextattr(struct vop_setextattr_args *ap)
1008/*
1009vop_setextattr {
1010	IN struct vnode *a_vp;
1011	IN int a_attrnamespace;
1012	IN const char *a_name;
1013	INOUT struct uio *a_uio;
1014	IN struct ucred *a_cred;
1015	IN struct thread *a_td;
1016};
1017*/
1018{
1019	struct mount	*mp = ap->a_vp->v_mount;
1020	struct ufsmount	*ump = VFSTOUFS(mp);
1021
1022	int	error;
1023
1024	ufs_extattr_uepm_lock(ump, ap->a_td);
1025
1026	if (ap->a_uio != NULL)
1027		error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace,
1028		    ap->a_name, ap->a_uio, ap->a_cred, ap->a_td);
1029	else
1030		error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace,
1031		    ap->a_name, ap->a_cred, ap->a_td);
1032
1033	ufs_extattr_uepm_unlock(ump, ap->a_td);
1034
1035	return (error);
1036}
1037
1038/*
1039 * Real work associated with setting a vnode's extended attributes;
1040 * assumes that the attribute lock has already been grabbed.
1041 */
1042static int
1043ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1044    struct uio *uio, struct ucred *cred, struct thread *td)
1045{
1046	struct ufs_extattr_list_entry	*attribute;
1047	struct ufs_extattr_header	ueh;
1048	struct iovec	local_aiov;
1049	struct uio	local_aio;
1050	struct mount	*mp = vp->v_mount;
1051	struct ufsmount	*ump = VFSTOUFS(mp);
1052	struct inode	*ip = VTOI(vp);
1053	off_t	base_offset;
1054	int	error = 0, ioflag;
1055
1056	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1057		return (EROFS);
1058	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1059		return (EOPNOTSUPP);
1060	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1061		return (EINVAL);
1062
1063	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1064	if (!attribute)
1065		/* XXX: ENOENT here will eventually be ENOATTR. */
1066		return (ENOENT);
1067
1068	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE)))
1069		return (error);
1070
1071	/*
1072	 * Early rejection of invalid offsets/length.
1073	 * Reject: any offset but 0 (replace)
1074	 *	 Any size greater than attribute size limit
1075 	 */
1076	if (uio->uio_offset != 0 ||
1077	    uio->uio_resid > attribute->uele_fileheader.uef_size)
1078		return (ENXIO);
1079
1080	/*
1081	 * Find base offset of header in file based on file header size, and
1082	 * data header size + maximum data size, indexed by inode number.
1083	 */
1084	base_offset = sizeof(struct ufs_extattr_fileheader) +
1085	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1086	    attribute->uele_fileheader.uef_size);
1087
1088	/*
1089	 * Write out a data header for the data.
1090	 */
1091	ueh.ueh_len = uio->uio_resid;
1092	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
1093	ueh.ueh_i_gen = ip->i_gen;
1094	local_aiov.iov_base = (caddr_t) &ueh;
1095	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1096	local_aio.uio_iov = &local_aiov;
1097	local_aio.uio_iovcnt = 1;
1098	local_aio.uio_rw = UIO_WRITE;
1099	local_aio.uio_segflg = UIO_SYSSPACE;
1100	local_aio.uio_td = td;
1101	local_aio.uio_offset = base_offset;
1102	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1103
1104	/*
1105	 * Acquire locks.
1106	 */
1107	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE);
1108
1109	/*
1110	 * Don't need to get a lock on the backing file if the setattr is
1111	 * being applied to the backing file, as the lock is already held.
1112	 */
1113	if (attribute->uele_backing_vnode != vp)
1114		vn_lock(attribute->uele_backing_vnode,
1115		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td);
1116
1117	ioflag = IO_NODELOCKED;
1118	if (ufs_extattr_sync)
1119		ioflag |= IO_SYNC;
1120	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1121	    ump->um_extattr.uepm_ucred);
1122	if (error)
1123		goto vopunlock_exit;
1124
1125	if (local_aio.uio_resid != 0) {
1126		error = ENXIO;
1127		goto vopunlock_exit;
1128	}
1129
1130	/*
1131	 * Write out user data.
1132	 */
1133	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1134
1135	ioflag = IO_NODELOCKED;
1136	if (ufs_extattr_sync)
1137		ioflag |= IO_SYNC;
1138	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1139	    ump->um_extattr.uepm_ucred);
1140
1141vopunlock_exit:
1142	uio->uio_offset = 0;
1143
1144	if (attribute->uele_backing_vnode != vp)
1145		VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1146
1147	return (error);
1148}
1149
1150/*
1151 * Real work associated with removing an extended attribute from a vnode.
1152 * Assumes the attribute lock has already been grabbed.
1153 */
1154static int
1155ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1156    struct ucred *cred, struct thread *td)
1157{
1158	struct ufs_extattr_list_entry	*attribute;
1159	struct ufs_extattr_header	ueh;
1160	struct iovec	local_aiov;
1161	struct uio	local_aio;
1162	struct mount	*mp = vp->v_mount;
1163	struct ufsmount	*ump = VFSTOUFS(mp);
1164	struct inode	*ip = VTOI(vp);
1165	off_t	base_offset;
1166	int	error = 0, ioflag;
1167
1168	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1169		return (EROFS);
1170	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1171		return (EOPNOTSUPP);
1172	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1173		return (EINVAL);
1174
1175	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1176	if (!attribute)
1177		/* XXX: ENOENT here will eventually be ENOATTR. */
1178		return (ENOENT);
1179
1180	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE)))
1181		return (error);
1182
1183	/*
1184	 * Find base offset of header in file based on file header size, and
1185	 * data header size + maximum data size, indexed by inode number.
1186	 */
1187	base_offset = sizeof(struct ufs_extattr_fileheader) +
1188	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1189	    attribute->uele_fileheader.uef_size);
1190
1191	/*
1192	 * Check to see if currently defined.
1193	 */
1194	bzero(&ueh, sizeof(struct ufs_extattr_header));
1195
1196	local_aiov.iov_base = (caddr_t) &ueh;
1197	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1198	local_aio.uio_iov = &local_aiov;
1199	local_aio.uio_iovcnt = 1;
1200	local_aio.uio_rw = UIO_READ;
1201	local_aio.uio_segflg = UIO_SYSSPACE;
1202	local_aio.uio_td = td;
1203	local_aio.uio_offset = base_offset;
1204	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1205
1206	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE);
1207
1208	/*
1209	 * Don't need to get the lock on the backing vnode if the vnode we're
1210	 * modifying is it, as we already hold the lock.
1211	 */
1212	if (attribute->uele_backing_vnode != vp)
1213		vn_lock(attribute->uele_backing_vnode,
1214		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td);
1215
1216	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
1217	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1218	if (error)
1219		goto vopunlock_exit;
1220
1221	/* Defined? */
1222	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
1223		/* XXX: ENOENT here will eventually be ENOATTR. */
1224		error = ENOENT;
1225		goto vopunlock_exit;
1226	}
1227
1228	/* Valid for the current inode generation? */
1229	if (ueh.ueh_i_gen != ip->i_gen) {
1230		/*
1231		 * The inode itself has a different generation number than
1232		 * the attribute data.  For now, the best solution is to
1233		 * coerce this to undefined, and let it get cleaned up by
1234		 * the next write or extattrctl clean.
1235		 */
1236		printf("ufs_extattr_rm: inode number inconsistency (%d, %d)\n",
1237		    ueh.ueh_i_gen, ip->i_gen);
1238		/* XXX: ENOENT here will eventually be ENOATTR. */
1239		error = ENOENT;
1240		goto vopunlock_exit;
1241	}
1242
1243	/* Flag it as not in use. */
1244	ueh.ueh_flags = 0;
1245	ueh.ueh_len = 0;
1246
1247	local_aiov.iov_base = (caddr_t) &ueh;
1248	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1249	local_aio.uio_iov = &local_aiov;
1250	local_aio.uio_iovcnt = 1;
1251	local_aio.uio_rw = UIO_WRITE;
1252	local_aio.uio_segflg = UIO_SYSSPACE;
1253	local_aio.uio_td = td;
1254	local_aio.uio_offset = base_offset;
1255	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1256
1257	ioflag = IO_NODELOCKED;
1258	if (ufs_extattr_sync)
1259		ioflag |= IO_SYNC;
1260	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1261	    ump->um_extattr.uepm_ucred);
1262	if (error)
1263		goto vopunlock_exit;
1264
1265	if (local_aio.uio_resid != 0)
1266		error = ENXIO;
1267
1268vopunlock_exit:
1269	VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1270
1271	return (error);
1272}
1273
1274/*
1275 * Called by UFS when an inode is no longer active and should have its
1276 * attributes stripped.
1277 */
1278void
1279ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td)
1280{
1281	struct ufs_extattr_list_entry	*uele;
1282	struct mount	*mp = vp->v_mount;
1283	struct ufsmount	*ump = VFSTOUFS(mp);
1284
1285	/*
1286	 * In that case, we cannot lock. We should not have any active vnodes
1287	 * on the fs if this is not yet initialized but is going to be, so
1288	 * this can go unlocked.
1289	 */
1290	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1291		return;
1292
1293	ufs_extattr_uepm_lock(ump, td);
1294
1295	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1296		ufs_extattr_uepm_unlock(ump, td);
1297		return;
1298	}
1299
1300	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1301		ufs_extattr_rm(vp, uele->uele_attrnamespace,
1302		    uele->uele_attrname, NULL, td);
1303
1304	ufs_extattr_uepm_unlock(ump, td);
1305}
1306
1307#endif /* !UFS_EXTATTR */
1308