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