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