ufs_extattr.c revision 96755
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 96755 2002-05-16 21:28:32Z trhodes $
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	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		return (ENOATTR);
689
690	LIST_REMOVE(uele, uele_entries);
691
692	uele->uele_backing_vnode->v_flag &= ~VSYSTEM;
693	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
694	    td->td_ucred, td);
695
696	FREE(uele, M_UFS_EXTATTR);
697
698	return (error);
699}
700
701/*
702 * VFS call to manage extended attributes in UFS.  If filename_vp is
703 * non-NULL, it must be passed in locked, and regardless of errors in
704 * processing, will be unlocked.
705 */
706int
707ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
708    int attrnamespace, const char *attrname, struct thread *td)
709{
710	struct ufsmount	*ump = VFSTOUFS(mp);
711	int	error;
712
713	/*
714	 * Processes with privilege, but in jail, are not allowed to
715	 * configure extended attributes.
716	 */
717	if ((error = suser(td))) {
718		if (filename_vp != NULL)
719			VOP_UNLOCK(filename_vp, 0, td);
720		return (error);
721	}
722
723	switch(cmd) {
724	case UFS_EXTATTR_CMD_START:
725		if (filename_vp != NULL) {
726			VOP_UNLOCK(filename_vp, 0, td);
727			return (EINVAL);
728		}
729		if (attrname != NULL)
730			return (EINVAL);
731
732		error = ufs_extattr_start(mp, td);
733
734		return (error);
735
736	case UFS_EXTATTR_CMD_STOP:
737		if (filename_vp != NULL) {
738			VOP_UNLOCK(filename_vp, 0, td);
739			return (EINVAL);
740		}
741		if (attrname != NULL)
742			return (EINVAL);
743
744		error = ufs_extattr_stop(mp, td);
745
746		return (error);
747
748	case UFS_EXTATTR_CMD_ENABLE:
749
750		if (filename_vp == NULL)
751			return (EINVAL);
752		if (attrname == NULL) {
753			VOP_UNLOCK(filename_vp, 0, td);
754			return (EINVAL);
755		}
756
757		/*
758		 * ufs_extattr_enable_with_open() will always unlock the
759		 * vnode, regardless of failure.
760		 */
761		ufs_extattr_uepm_lock(ump, td);
762		error = ufs_extattr_enable_with_open(ump, filename_vp,
763		    attrnamespace, attrname, td);
764		ufs_extattr_uepm_unlock(ump, td);
765
766		return (error);
767
768	case UFS_EXTATTR_CMD_DISABLE:
769
770		if (filename_vp != NULL) {
771			VOP_UNLOCK(filename_vp, 0, td);
772			return (EINVAL);
773		}
774		if (attrname == NULL)
775			return (EINVAL);
776
777		ufs_extattr_uepm_lock(ump, td);
778		error = ufs_extattr_disable(ump, attrnamespace, attrname,
779		    td);
780		ufs_extattr_uepm_unlock(ump, td);
781
782		return (error);
783
784	default:
785		return (EINVAL);
786	}
787}
788
789/*
790 * Credential check based on process requesting service, and per-attribute
791 * permissions.
792 */
793static int
794ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele,
795    struct ucred *cred, struct thread *td, int access)
796{
797
798	/*
799	 * Kernel-invoked always succeeds.
800	 */
801	if (cred == NULL)
802		return (0);
803
804	/*
805	 * Do not allow privileged processes in jail to directly
806	 * manipulate system attributes.
807	 *
808	 * XXX What capability should apply here?
809	 * Probably CAP_SYS_SETFFLAG.
810	 */
811	switch (uele->uele_attrnamespace) {
812	case EXTATTR_NAMESPACE_SYSTEM:
813		/* Potentially should be: return (EPERM); */
814		return (suser_cred(cred, 0));
815	case EXTATTR_NAMESPACE_USER:
816		return (VOP_ACCESS(vp, access, cred, td));
817	default:
818		return (EPERM);
819	}
820}
821
822/*
823 * Vnode operating to retrieve a named extended attribute.
824 */
825int
826ufs_getextattr(struct vop_getextattr_args *ap)
827/*
828vop_getextattr {
829	IN struct vnode *a_vp;
830	IN int a_attrnamespace;
831	IN const char *a_name;
832	INOUT struct uio *a_uio;
833	OUT struct size_t *a_size;
834	IN struct ucred *a_cred;
835	IN struct thread *a_td;
836};
837*/
838{
839	struct mount	*mp = ap->a_vp->v_mount;
840	struct ufsmount	*ump = VFSTOUFS(mp);
841	int	error;
842
843	ufs_extattr_uepm_lock(ump, ap->a_td);
844
845	error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
846	    ap->a_uio, ap->a_size, ap->a_cred, ap->a_td);
847
848	ufs_extattr_uepm_unlock(ump, ap->a_td);
849
850	return (error);
851}
852
853/*
854 * Real work associated with retrieving a named attribute--assumes that
855 * the attribute lock has already been grabbed.
856 */
857static int
858ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
859    struct uio *uio, size_t *size, struct ucred *cred, struct thread *td)
860{
861	struct ufs_extattr_list_entry	*attribute;
862	struct ufs_extattr_header	ueh;
863	struct iovec	local_aiov;
864	struct uio	local_aio;
865	struct mount	*mp = vp->v_mount;
866	struct ufsmount	*ump = VFSTOUFS(mp);
867	struct inode	*ip = VTOI(vp);
868	off_t	base_offset;
869	size_t	len, old_len;
870	int	error = 0;
871
872	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
873		return (EOPNOTSUPP);
874
875	if (strlen(name) == 0) {
876		/* XXX retrieve attribute lists. */
877		/* XXX should probably be checking for name == NULL? */
878		return (EINVAL);
879	}
880
881	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
882	if (!attribute)
883		return (ENOATTR);
884
885	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IREAD)))
886		return (error);
887
888	/*
889	 * Allow only offsets of zero to encourage the read/replace
890	 * extended attribute semantic.  Otherwise we can't guarantee
891	 * atomicity, as we don't provide locks for extended attributes.
892	 */
893	if (uio != NULL && uio->uio_offset != 0)
894		return (ENXIO);
895
896	/*
897	 * Find base offset of header in file based on file header size, and
898	 * data header size + maximum data size, indexed by inode number.
899	 */
900	base_offset = sizeof(struct ufs_extattr_fileheader) +
901	    ip->i_number * (sizeof(struct ufs_extattr_header) +
902	    attribute->uele_fileheader.uef_size);
903
904	/*
905	 * Read in the data header to see if the data is defined, and if so
906	 * how much.
907	 */
908	bzero(&ueh, sizeof(struct ufs_extattr_header));
909	local_aiov.iov_base = (caddr_t) &ueh;
910	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
911	local_aio.uio_iov = &local_aiov;
912	local_aio.uio_iovcnt = 1;
913	local_aio.uio_rw = UIO_READ;
914	local_aio.uio_segflg = UIO_SYSSPACE;
915	local_aio.uio_td = td;
916	local_aio.uio_offset = base_offset;
917	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
918
919	/*
920	 * Acquire locks.
921	 */
922	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_READ);
923	/*
924	 * Don't need to get a lock on the backing file if the getattr is
925	 * being applied to the backing file, as the lock is already held.
926	 */
927	if (attribute->uele_backing_vnode != vp)
928		vn_lock(attribute->uele_backing_vnode, LK_SHARED |
929		    LK_NOPAUSE | LK_RETRY, td);
930
931	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
932	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
933	if (error)
934		goto vopunlock_exit;
935
936	/* Defined? */
937	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
938		error = ENOATTR;
939		goto vopunlock_exit;
940	}
941
942	/* Valid for the current inode generation? */
943	if (ueh.ueh_i_gen != ip->i_gen) {
944		/*
945		 * The inode itself has a different generation number
946		 * than the attribute data.  For now, the best solution
947		 * is to coerce this to undefined, and let it get cleaned
948		 * up by the next write or extattrctl clean.
949		 */
950		printf("ufs_extattr_get (%s): inode number inconsistency (%d, %d)\n",
951		    mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, ip->i_gen);
952		error = ENOATTR;
953		goto vopunlock_exit;
954	}
955
956	/* Local size consistency check. */
957	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
958		error = ENXIO;
959		goto vopunlock_exit;
960	}
961
962	/* Return full data size if caller requested it. */
963	if (size != NULL)
964		*size = ueh.ueh_len;
965
966	/* Return data if the caller requested it. */
967	if (uio != NULL) {
968		/* Allow for offset into the attribute data. */
969		uio->uio_offset = base_offset + sizeof(struct
970		    ufs_extattr_header);
971
972		/*
973		 * Figure out maximum to transfer -- use buffer size and
974		 * local data limit.
975		 */
976		len = MIN(uio->uio_resid, ueh.ueh_len);
977		old_len = uio->uio_resid;
978		uio->uio_resid = len;
979
980		error = VOP_READ(attribute->uele_backing_vnode, uio,
981		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
982		if (error)
983			goto vopunlock_exit;
984
985		uio->uio_resid = old_len - (len - uio->uio_resid);
986	}
987
988vopunlock_exit:
989
990	if (uio != NULL)
991		uio->uio_offset = 0;
992
993	if (attribute->uele_backing_vnode != vp)
994		VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
995
996	return (error);
997}
998
999/*
1000 * Vnode operation to set a named attribute.
1001 */
1002int
1003ufs_setextattr(struct vop_setextattr_args *ap)
1004/*
1005vop_setextattr {
1006	IN struct vnode *a_vp;
1007	IN int a_attrnamespace;
1008	IN const char *a_name;
1009	INOUT struct uio *a_uio;
1010	IN struct ucred *a_cred;
1011	IN struct thread *a_td;
1012};
1013*/
1014{
1015	struct mount	*mp = ap->a_vp->v_mount;
1016	struct ufsmount	*ump = VFSTOUFS(mp);
1017
1018	int	error;
1019
1020	ufs_extattr_uepm_lock(ump, ap->a_td);
1021
1022	if (ap->a_uio != NULL)
1023		error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace,
1024		    ap->a_name, ap->a_uio, ap->a_cred, ap->a_td);
1025	else
1026		error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace,
1027		    ap->a_name, ap->a_cred, ap->a_td);
1028
1029	ufs_extattr_uepm_unlock(ump, ap->a_td);
1030
1031	return (error);
1032}
1033
1034/*
1035 * Real work associated with setting a vnode's extended attributes;
1036 * assumes that the attribute lock has already been grabbed.
1037 */
1038static int
1039ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1040    struct uio *uio, struct ucred *cred, struct thread *td)
1041{
1042	struct ufs_extattr_list_entry	*attribute;
1043	struct ufs_extattr_header	ueh;
1044	struct iovec	local_aiov;
1045	struct uio	local_aio;
1046	struct mount	*mp = vp->v_mount;
1047	struct ufsmount	*ump = VFSTOUFS(mp);
1048	struct inode	*ip = VTOI(vp);
1049	off_t	base_offset;
1050	int	error = 0, ioflag;
1051
1052	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1053		return (EROFS);
1054	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1055		return (EOPNOTSUPP);
1056	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1057		return (EINVAL);
1058
1059	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1060	if (!attribute)
1061		return (ENOATTR);
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		return (ENOATTR);
1173
1174	if ((error = ufs_extattr_credcheck(vp, attribute, cred, td, IWRITE)))
1175		return (error);
1176
1177	/*
1178	 * Find base offset of header in file based on file header size, and
1179	 * data header size + maximum data size, indexed by inode number.
1180	 */
1181	base_offset = sizeof(struct ufs_extattr_fileheader) +
1182	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1183	    attribute->uele_fileheader.uef_size);
1184
1185	/*
1186	 * Check to see if currently defined.
1187	 */
1188	bzero(&ueh, sizeof(struct ufs_extattr_header));
1189
1190	local_aiov.iov_base = (caddr_t) &ueh;
1191	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1192	local_aio.uio_iov = &local_aiov;
1193	local_aio.uio_iovcnt = 1;
1194	local_aio.uio_rw = UIO_READ;
1195	local_aio.uio_segflg = UIO_SYSSPACE;
1196	local_aio.uio_td = td;
1197	local_aio.uio_offset = base_offset;
1198	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1199
1200	VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE);
1201
1202	/*
1203	 * Don't need to get the lock on the backing vnode if the vnode we're
1204	 * modifying is it, as we already hold the lock.
1205	 */
1206	if (attribute->uele_backing_vnode != vp)
1207		vn_lock(attribute->uele_backing_vnode,
1208		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td);
1209
1210	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
1211	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1212	if (error)
1213		goto vopunlock_exit;
1214
1215	/* Defined? */
1216	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
1217		error = ENOATTR;
1218		goto vopunlock_exit;
1219	}
1220
1221	/* Valid for the current inode generation? */
1222	if (ueh.ueh_i_gen != ip->i_gen) {
1223		/*
1224		 * The inode itself has a different generation number than
1225		 * the attribute data.  For now, the best solution is to
1226		 * coerce this to undefined, and let it get cleaned up by
1227		 * the next write or extattrctl clean.
1228		 */
1229		printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %d)\n",
1230		    mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, ip->i_gen);
1231		error = ENOATTR;
1232		goto vopunlock_exit;
1233	}
1234
1235	/* Flag it as not in use. */
1236	ueh.ueh_flags = 0;
1237	ueh.ueh_len = 0;
1238
1239	local_aiov.iov_base = (caddr_t) &ueh;
1240	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1241	local_aio.uio_iov = &local_aiov;
1242	local_aio.uio_iovcnt = 1;
1243	local_aio.uio_rw = UIO_WRITE;
1244	local_aio.uio_segflg = UIO_SYSSPACE;
1245	local_aio.uio_td = td;
1246	local_aio.uio_offset = base_offset;
1247	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1248
1249	ioflag = IO_NODELOCKED;
1250	if (ufs_extattr_sync)
1251		ioflag |= IO_SYNC;
1252	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1253	    ump->um_extattr.uepm_ucred);
1254	if (error)
1255		goto vopunlock_exit;
1256
1257	if (local_aio.uio_resid != 0)
1258		error = ENXIO;
1259
1260vopunlock_exit:
1261	VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1262
1263	return (error);
1264}
1265
1266/*
1267 * Called by UFS when an inode is no longer active and should have its
1268 * attributes stripped.
1269 */
1270void
1271ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td)
1272{
1273	struct ufs_extattr_list_entry	*uele;
1274	struct mount	*mp = vp->v_mount;
1275	struct ufsmount	*ump = VFSTOUFS(mp);
1276
1277	/*
1278	 * In that case, we cannot lock. We should not have any active vnodes
1279	 * on the fs if this is not yet initialized but is going to be, so
1280	 * this can go unlocked.
1281	 */
1282	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1283		return;
1284
1285	ufs_extattr_uepm_lock(ump, td);
1286
1287	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1288		ufs_extattr_uepm_unlock(ump, td);
1289		return;
1290	}
1291
1292	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1293		ufs_extattr_rm(vp, uele->uele_attrnamespace,
1294		    uele->uele_attrname, NULL, td);
1295
1296	ufs_extattr_uepm_unlock(ump, td);
1297}
1298
1299#endif /* !UFS_EXTATTR */
1300