ufs_extattr.c revision 101308
1/*-
2 * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson
3 * Copyright (c) 2002 Networks Associates Technology, 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 101308 2002-08-04 10:29:36Z jeff $
38 */
39/*
40 * Developed by the TrustedBSD Project.
41 * Support for filesystem 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/uma.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 = uma_zalloc(namei_zone, M_WAITOK);
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		uma_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	uma_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 filesystem, 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 filesystem 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 filesystem; 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	ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable");
663	backing_vnode->v_vflag |= VV_SYSTEM;
664	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
665	    uele_entries);
666
667	return (0);
668
669free_exit:
670	FREE(attribute, M_UFS_EXTATTR);
671	return (error);
672}
673
674/*
675 * Disable extended attribute support on an FS.
676 */
677static int
678ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
679    const char *attrname, struct thread *td)
680{
681	struct ufs_extattr_list_entry	*uele;
682	int	error = 0;
683
684	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
685		return (EINVAL);
686
687	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
688	if (!uele)
689		return (ENOATTR);
690
691	LIST_REMOVE(uele, uele_entries);
692
693	ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable");
694	uele->uele_backing_vnode->v_vflag &= ~VV_SYSTEM;
695	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
696	    td->td_ucred, td);
697
698	FREE(uele, M_UFS_EXTATTR);
699
700	return (error);
701}
702
703/*
704 * VFS call to manage extended attributes in UFS.  If filename_vp is
705 * non-NULL, it must be passed in locked, and regardless of errors in
706 * processing, will be unlocked.
707 */
708int
709ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
710    int attrnamespace, const char *attrname, struct thread *td)
711{
712	struct ufsmount	*ump = VFSTOUFS(mp);
713	int	error;
714
715	/*
716	 * Processes with privilege, but in jail, are not allowed to
717	 * configure extended attributes.
718	 */
719	if ((error = suser(td))) {
720		if (filename_vp != NULL)
721			VOP_UNLOCK(filename_vp, 0, td);
722		return (error);
723	}
724
725	switch(cmd) {
726	case UFS_EXTATTR_CMD_START:
727		if (filename_vp != NULL) {
728			VOP_UNLOCK(filename_vp, 0, td);
729			return (EINVAL);
730		}
731		if (attrname != NULL)
732			return (EINVAL);
733
734		error = ufs_extattr_start(mp, td);
735
736		return (error);
737
738	case UFS_EXTATTR_CMD_STOP:
739		if (filename_vp != NULL) {
740			VOP_UNLOCK(filename_vp, 0, td);
741			return (EINVAL);
742		}
743		if (attrname != NULL)
744			return (EINVAL);
745
746		error = ufs_extattr_stop(mp, td);
747
748		return (error);
749
750	case UFS_EXTATTR_CMD_ENABLE:
751
752		if (filename_vp == NULL)
753			return (EINVAL);
754		if (attrname == NULL) {
755			VOP_UNLOCK(filename_vp, 0, td);
756			return (EINVAL);
757		}
758
759		/*
760		 * ufs_extattr_enable_with_open() will always unlock the
761		 * vnode, regardless of failure.
762		 */
763		ufs_extattr_uepm_lock(ump, td);
764		error = ufs_extattr_enable_with_open(ump, filename_vp,
765		    attrnamespace, attrname, td);
766		ufs_extattr_uepm_unlock(ump, td);
767
768		return (error);
769
770	case UFS_EXTATTR_CMD_DISABLE:
771
772		if (filename_vp != NULL) {
773			VOP_UNLOCK(filename_vp, 0, td);
774			return (EINVAL);
775		}
776		if (attrname == NULL)
777			return (EINVAL);
778
779		ufs_extattr_uepm_lock(ump, td);
780		error = ufs_extattr_disable(ump, attrnamespace, attrname,
781		    td);
782		ufs_extattr_uepm_unlock(ump, td);
783
784		return (error);
785
786	default:
787		return (EINVAL);
788	}
789}
790
791/*
792 * Credential check based on process requesting service, and per-attribute
793 * permissions.
794 */
795static int
796ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele,
797    struct ucred *cred, struct thread *td, int access)
798{
799
800	/*
801	 * Kernel-invoked always succeeds.
802	 */
803	if (cred == NULL)
804		return (0);
805
806	/*
807	 * Do not allow privileged processes in jail to directly
808	 * manipulate system attributes.
809	 *
810	 * XXX What capability should apply here?
811	 * Probably CAP_SYS_SETFFLAG.
812	 */
813	switch (uele->uele_attrnamespace) {
814	case EXTATTR_NAMESPACE_SYSTEM:
815		/* Potentially should be: return (EPERM); */
816		return (suser_cred(cred, 0));
817	case EXTATTR_NAMESPACE_USER:
818		return (VOP_ACCESS(vp, access, cred, td));
819	default:
820		return (EPERM);
821	}
822}
823
824/*
825 * Vnode operating to retrieve a named extended attribute.
826 */
827int
828ufs_getextattr(struct vop_getextattr_args *ap)
829/*
830vop_getextattr {
831	IN struct vnode *a_vp;
832	IN int a_attrnamespace;
833	IN const char *a_name;
834	INOUT struct uio *a_uio;
835	OUT struct size_t *a_size;
836	IN struct ucred *a_cred;
837	IN struct thread *a_td;
838};
839*/
840{
841	struct mount	*mp = ap->a_vp->v_mount;
842	struct ufsmount	*ump = VFSTOUFS(mp);
843	int	error;
844
845	ufs_extattr_uepm_lock(ump, ap->a_td);
846
847	error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
848	    ap->a_uio, ap->a_size, ap->a_cred, ap->a_td);
849
850	ufs_extattr_uepm_unlock(ump, ap->a_td);
851
852	return (error);
853}
854
855/*
856 * Real work associated with retrieving a named attribute--assumes that
857 * the attribute lock has already been grabbed.
858 */
859static int
860ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
861    struct uio *uio, size_t *size, struct ucred *cred, struct thread *td)
862{
863	struct ufs_extattr_list_entry	*attribute;
864	struct ufs_extattr_header	ueh;
865	struct iovec	local_aiov;
866	struct uio	local_aio;
867	struct mount	*mp = vp->v_mount;
868	struct ufsmount	*ump = VFSTOUFS(mp);
869	struct inode	*ip = VTOI(vp);
870	off_t	base_offset;
871	size_t	len, old_len;
872	int	error = 0;
873
874	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
875		return (EOPNOTSUPP);
876
877	if (strlen(name) == 0) {
878		/* XXX retrieve attribute lists. */
879		/* XXX should probably be checking for name == NULL? */
880		return (EINVAL);
881	}
882
883	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
884	if (!attribute)
885		return (ENOATTR);
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		error = ENOATTR;
941		goto vopunlock_exit;
942	}
943
944	/* Valid for the current inode generation? */
945	if (ueh.ueh_i_gen != ip->i_gen) {
946		/*
947		 * The inode itself has a different generation number
948		 * than the attribute data.  For now, the best solution
949		 * is to coerce this to undefined, and let it get cleaned
950		 * up by the next write or extattrctl clean.
951		 */
952		printf("ufs_extattr_get (%s): inode number inconsistency (%d, %lld)\n",
953		    mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, ip->i_gen);
954		error = ENOATTR;
955		goto vopunlock_exit;
956	}
957
958	/* Local size consistency check. */
959	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
960		error = ENXIO;
961		goto vopunlock_exit;
962	}
963
964	/* Return full data size if caller requested it. */
965	if (size != NULL)
966		*size = ueh.ueh_len;
967
968	/* Return data if the caller requested it. */
969	if (uio != NULL) {
970		/* Allow for offset into the attribute data. */
971		uio->uio_offset = base_offset + sizeof(struct
972		    ufs_extattr_header);
973
974		/*
975		 * Figure out maximum to transfer -- use buffer size and
976		 * local data limit.
977		 */
978		len = MIN(uio->uio_resid, ueh.ueh_len);
979		old_len = uio->uio_resid;
980		uio->uio_resid = len;
981
982		error = VOP_READ(attribute->uele_backing_vnode, uio,
983		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
984		if (error)
985			goto vopunlock_exit;
986
987		uio->uio_resid = old_len - (len - uio->uio_resid);
988	}
989
990vopunlock_exit:
991
992	if (uio != NULL)
993		uio->uio_offset = 0;
994
995	if (attribute->uele_backing_vnode != vp)
996		VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
997
998	return (error);
999}
1000
1001/*
1002 * Vnode operation to set a named attribute.
1003 */
1004int
1005ufs_setextattr(struct vop_setextattr_args *ap)
1006/*
1007vop_setextattr {
1008	IN struct vnode *a_vp;
1009	IN int a_attrnamespace;
1010	IN const char *a_name;
1011	INOUT struct uio *a_uio;
1012	IN struct ucred *a_cred;
1013	IN struct thread *a_td;
1014};
1015*/
1016{
1017	struct mount	*mp = ap->a_vp->v_mount;
1018	struct ufsmount	*ump = VFSTOUFS(mp);
1019
1020	int	error;
1021
1022	ufs_extattr_uepm_lock(ump, ap->a_td);
1023
1024	if (ap->a_uio != NULL)
1025		error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace,
1026		    ap->a_name, ap->a_uio, ap->a_cred, ap->a_td);
1027	else
1028		error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace,
1029		    ap->a_name, ap->a_cred, ap->a_td);
1030
1031	ufs_extattr_uepm_unlock(ump, ap->a_td);
1032
1033	return (error);
1034}
1035
1036/*
1037 * Real work associated with setting a vnode's extended attributes;
1038 * assumes that the attribute lock has already been grabbed.
1039 */
1040static int
1041ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1042    struct uio *uio, struct ucred *cred, struct thread *td)
1043{
1044	struct ufs_extattr_list_entry	*attribute;
1045	struct ufs_extattr_header	ueh;
1046	struct iovec	local_aiov;
1047	struct uio	local_aio;
1048	struct mount	*mp = vp->v_mount;
1049	struct ufsmount	*ump = VFSTOUFS(mp);
1050	struct inode	*ip = VTOI(vp);
1051	off_t	base_offset;
1052	int	error = 0, ioflag;
1053
1054	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1055		return (EROFS);
1056	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1057		return (EOPNOTSUPP);
1058	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1059		return (EINVAL);
1060
1061	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1062	if (!attribute)
1063		return (ENOATTR);
1064
1065	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE)))
1066		return (error);
1067
1068	/*
1069	 * Early rejection of invalid offsets/length.
1070	 * Reject: any offset but 0 (replace)
1071	 *	 Any size greater than attribute size limit
1072 	 */
1073	if (uio->uio_offset != 0 ||
1074	    uio->uio_resid > attribute->uele_fileheader.uef_size)
1075		return (ENXIO);
1076
1077	/*
1078	 * Find base offset of header in file based on file header size, and
1079	 * data header size + maximum data size, indexed by inode number.
1080	 */
1081	base_offset = sizeof(struct ufs_extattr_fileheader) +
1082	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1083	    attribute->uele_fileheader.uef_size);
1084
1085	/*
1086	 * Write out a data header for the data.
1087	 */
1088	ueh.ueh_len = uio->uio_resid;
1089	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
1090	ueh.ueh_i_gen = ip->i_gen;
1091	local_aiov.iov_base = (caddr_t) &ueh;
1092	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1093	local_aio.uio_iov = &local_aiov;
1094	local_aio.uio_iovcnt = 1;
1095	local_aio.uio_rw = UIO_WRITE;
1096	local_aio.uio_segflg = UIO_SYSSPACE;
1097	local_aio.uio_td = td;
1098	local_aio.uio_offset = base_offset;
1099	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1100
1101	/*
1102	 * Acquire locks.
1103	 */
1104	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE);
1105
1106	/*
1107	 * Don't need to get a lock on the backing file if the setattr is
1108	 * being applied to the backing file, as the lock is already held.
1109	 */
1110	if (attribute->uele_backing_vnode != vp)
1111		vn_lock(attribute->uele_backing_vnode,
1112		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td);
1113
1114	ioflag = IO_NODELOCKED;
1115	if (ufs_extattr_sync)
1116		ioflag |= IO_SYNC;
1117	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1118	    ump->um_extattr.uepm_ucred);
1119	if (error)
1120		goto vopunlock_exit;
1121
1122	if (local_aio.uio_resid != 0) {
1123		error = ENXIO;
1124		goto vopunlock_exit;
1125	}
1126
1127	/*
1128	 * Write out user data.
1129	 */
1130	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1131
1132	ioflag = IO_NODELOCKED;
1133	if (ufs_extattr_sync)
1134		ioflag |= IO_SYNC;
1135	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1136	    ump->um_extattr.uepm_ucred);
1137
1138vopunlock_exit:
1139	uio->uio_offset = 0;
1140
1141	if (attribute->uele_backing_vnode != vp)
1142		VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1143
1144	return (error);
1145}
1146
1147/*
1148 * Real work associated with removing an extended attribute from a vnode.
1149 * Assumes the attribute lock has already been grabbed.
1150 */
1151static int
1152ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1153    struct ucred *cred, struct thread *td)
1154{
1155	struct ufs_extattr_list_entry	*attribute;
1156	struct ufs_extattr_header	ueh;
1157	struct iovec	local_aiov;
1158	struct uio	local_aio;
1159	struct mount	*mp = vp->v_mount;
1160	struct ufsmount	*ump = VFSTOUFS(mp);
1161	struct inode	*ip = VTOI(vp);
1162	off_t	base_offset;
1163	int	error = 0, ioflag;
1164
1165	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1166		return (EROFS);
1167	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1168		return (EOPNOTSUPP);
1169	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1170		return (EINVAL);
1171
1172	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1173	if (!attribute)
1174		return (ENOATTR);
1175
1176	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE)))
1177		return (error);
1178
1179	/*
1180	 * Find base offset of header in file based on file header size, and
1181	 * data header size + maximum data size, indexed by inode number.
1182	 */
1183	base_offset = sizeof(struct ufs_extattr_fileheader) +
1184	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1185	    attribute->uele_fileheader.uef_size);
1186
1187	/*
1188	 * Check to see if currently defined.
1189	 */
1190	bzero(&ueh, sizeof(struct ufs_extattr_header));
1191
1192	local_aiov.iov_base = (caddr_t) &ueh;
1193	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1194	local_aio.uio_iov = &local_aiov;
1195	local_aio.uio_iovcnt = 1;
1196	local_aio.uio_rw = UIO_READ;
1197	local_aio.uio_segflg = UIO_SYSSPACE;
1198	local_aio.uio_td = td;
1199	local_aio.uio_offset = base_offset;
1200	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1201
1202	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE);
1203
1204	/*
1205	 * Don't need to get the lock on the backing vnode if the vnode we're
1206	 * modifying is it, as we already hold the lock.
1207	 */
1208	if (attribute->uele_backing_vnode != vp)
1209		vn_lock(attribute->uele_backing_vnode,
1210		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td);
1211
1212	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
1213	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1214	if (error)
1215		goto vopunlock_exit;
1216
1217	/* Defined? */
1218	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
1219		error = ENOATTR;
1220		goto vopunlock_exit;
1221	}
1222
1223	/* Valid for the current inode generation? */
1224	if (ueh.ueh_i_gen != ip->i_gen) {
1225		/*
1226		 * The inode itself has a different generation number than
1227		 * the attribute data.  For now, the best solution is to
1228		 * coerce this to undefined, and let it get cleaned up by
1229		 * the next write or extattrctl clean.
1230		 */
1231		printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %lld)\n",
1232		    mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, ip->i_gen);
1233		error = ENOATTR;
1234		goto vopunlock_exit;
1235	}
1236
1237	/* Flag it as not in use. */
1238	ueh.ueh_flags = 0;
1239	ueh.ueh_len = 0;
1240
1241	local_aiov.iov_base = (caddr_t) &ueh;
1242	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1243	local_aio.uio_iov = &local_aiov;
1244	local_aio.uio_iovcnt = 1;
1245	local_aio.uio_rw = UIO_WRITE;
1246	local_aio.uio_segflg = UIO_SYSSPACE;
1247	local_aio.uio_td = td;
1248	local_aio.uio_offset = base_offset;
1249	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1250
1251	ioflag = IO_NODELOCKED;
1252	if (ufs_extattr_sync)
1253		ioflag |= IO_SYNC;
1254	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1255	    ump->um_extattr.uepm_ucred);
1256	if (error)
1257		goto vopunlock_exit;
1258
1259	if (local_aio.uio_resid != 0)
1260		error = ENXIO;
1261
1262vopunlock_exit:
1263	VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1264
1265	return (error);
1266}
1267
1268/*
1269 * Called by UFS when an inode is no longer active and should have its
1270 * attributes stripped.
1271 */
1272void
1273ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td)
1274{
1275	struct ufs_extattr_list_entry	*uele;
1276	struct mount	*mp = vp->v_mount;
1277	struct ufsmount	*ump = VFSTOUFS(mp);
1278
1279	/*
1280	 * In that case, we cannot lock. We should not have any active vnodes
1281	 * on the fs if this is not yet initialized but is going to be, so
1282	 * this can go unlocked.
1283	 */
1284	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1285		return;
1286
1287	ufs_extattr_uepm_lock(ump, td);
1288
1289	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1290		ufs_extattr_uepm_unlock(ump, td);
1291		return;
1292	}
1293
1294	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1295		ufs_extattr_rm(vp, uele->uele_attrnamespace,
1296		    uele->uele_attrname, NULL, td);
1297
1298	ufs_extattr_uepm_unlock(ump, td);
1299}
1300
1301#endif /* !UFS_EXTATTR */
1302