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