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