xref: /netbsd-src/sys/ufs/lfs/ulfs_extattr.c (revision 6a493d6bc668897c91594964a732d38505b70cbb)
1 /*	$NetBSD: ulfs_extattr.c,v 1.6 2013/06/08 22:23:52 dholland Exp $	*/
2 /*  from NetBSD: ufs_extattr.c,v 1.41 2012/12/08 13:42:36 manu Exp  */
3 
4 /*-
5  * Copyright (c) 1999-2002 Robert N. M. Watson
6  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
7  * All rights reserved.
8  *
9  * This software was developed by Robert Watson for the TrustedBSD Project.
10  *
11  * This software was developed for the FreeBSD Project in part by Network
12  * Associates Laboratories, the Security Research Division of Network
13  * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
14  * as part of the DARPA CHATS research program.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must reproduce the above copyright
22  *    notice, this list of conditions and the following disclaimer in the
23  *    documentation and/or other materials provided with the distribution.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  */
38 
39 /*
40  * Support for file system extended attributes on the ULFS1 file system.
41  *
42  * Extended attributes are defined in the form name=value, where name is
43  * a nul-terminated string in the style of a file name, and value is a
44  * binary blob of zero or more bytes.  The ULFS1 extended attribute service
45  * layers support for extended attributes onto a backing file, in the style
46  * of the quota implementation, meaning that it requires no underlying format
47  * changes to the file system.  This design choice exchanges simplicity,
48  * usability, and easy deployment for performance.
49  */
50 
51 #include <sys/cdefs.h>
52 __KERNEL_RCSID(0, "$NetBSD: ulfs_extattr.c,v 1.6 2013/06/08 22:23:52 dholland Exp $");
53 
54 #ifdef _KERNEL_OPT
55 #include "opt_lfs.h"
56 #endif
57 
58 #include <sys/param.h>
59 #include <sys/systm.h>
60 #include <sys/reboot.h>
61 #include <sys/kauth.h>
62 #include <sys/kernel.h>
63 #include <sys/namei.h>
64 #include <sys/kmem.h>
65 #include <sys/fcntl.h>
66 #include <sys/lwp.h>
67 #include <sys/vnode.h>
68 #include <sys/mount.h>
69 #include <sys/lock.h>
70 #include <sys/dirent.h>
71 #include <sys/extattr.h>
72 #include <sys/sysctl.h>
73 
74 #include <ufs/lfs/ulfs_extattr.h>
75 #include <ufs/lfs/ulfsmount.h>
76 #include <ufs/lfs/ulfs_inode.h>
77 #include <ufs/lfs/ulfs_bswap.h>
78 #include <ufs/lfs/ulfs_extern.h>
79 
80 int ulfs_extattr_sync = 1;
81 int ulfs_extattr_autocreate = 1024;
82 
83 static int	ulfs_extattr_valid_attrname(int attrnamespace,
84 		    const char *attrname);
85 static int	ulfs_extattr_enable_with_open(struct ulfsmount *ump,
86 		    struct vnode *vp, int attrnamespace, const char *attrname,
87 		    struct lwp *l);
88 static int	ulfs_extattr_enable(struct ulfsmount *ump, int attrnamespace,
89 		    const char *attrname, struct vnode *backing_vnode,
90 		    struct lwp *l);
91 static int	ulfs_extattr_disable(struct ulfsmount *ump, int attrnamespace,
92 		    const char *attrname, struct lwp *l);
93 static int	ulfs_extattr_get(struct vnode *vp, int attrnamespace,
94 		    const char *name, struct uio *uio, size_t *size,
95 		    kauth_cred_t cred, struct lwp *l);
96 static int	ulfs_extattr_list(struct vnode *vp, int attrnamespace,
97 		    struct uio *uio, size_t *size, int flag,
98 		    kauth_cred_t cred, struct lwp *l);
99 static int	ulfs_extattr_set(struct vnode *vp, int attrnamespace,
100 		    const char *name, struct uio *uio, kauth_cred_t cred,
101 		    struct lwp *l);
102 static int	ulfs_extattr_rm(struct vnode *vp, int attrnamespace,
103 		    const char *name, kauth_cred_t cred, struct lwp *l);
104 static struct ulfs_extattr_list_entry *ulfs_extattr_find_attr(struct ulfsmount *,
105 		    int, const char *);
106 static int	ulfs_extattr_get_header(struct vnode *,
107 		    struct ulfs_extattr_list_entry *,
108 		    struct ulfs_extattr_header *, off_t *);
109 
110 /*
111  * Convert a FreeBSD extended attribute and namespace to a consistent string
112  * representation.
113  *
114  * The returned value, if not NULL, is guaranteed to be an allocated object
115  * of its size as returned by strlen() + 1 and must be freed by the caller.
116  */
117 static char *
118 from_freebsd_extattr(int attrnamespace, const char *attrname)
119 {
120 	const char *namespace;
121 	char *attr;
122 	size_t len;
123 
124 	if (attrnamespace == EXTATTR_NAMESPACE_SYSTEM)
125 		namespace = "system";
126 	else if (attrnamespace == EXTATTR_NAMESPACE_USER)
127 		namespace = "user";
128 	else
129 		return NULL;
130 
131 	/* <namespace>.<attrname>\0 */
132 	len = strlen(namespace) + 1 + strlen(attrname) + 1;
133 
134 	attr = kmem_alloc(len, KM_SLEEP);
135 
136 	snprintf(attr, len, "%s.%s", namespace, attrname);
137 
138 	return attr;
139 }
140 
141 /*
142  * Internal wrapper around a conversion-check-free sequence.
143  */
144 static int
145 internal_extattr_check_cred(vnode_t *vp, int attrnamespace, const char *name,
146     kauth_cred_t cred, int access_mode)
147 {
148 	char *attr;
149 	int error;
150 
151 	attr = from_freebsd_extattr(attrnamespace, name);
152 	if (attr == NULL)
153 		return EINVAL;
154 
155 	error = extattr_check_cred(vp, attr, cred, access_mode);
156 
157 	kmem_free(attr, strlen(attr) + 1);
158 
159 	return error;
160 }
161 
162 /*
163  * Per-FS attribute lock protecting attribute operations.
164  * XXX Right now there is a lot of lock contention due to having a single
165  * lock per-FS; really, this should be far more fine-grained.
166  */
167 static void
168 ulfs_extattr_uepm_lock(struct ulfsmount *ump)
169 {
170 
171 	/* XXX Why does this need to be recursive? */
172 	if (mutex_owned(&ump->um_extattr.uepm_lock)) {
173 		ump->um_extattr.uepm_lockcnt++;
174 		return;
175 	}
176 	mutex_enter(&ump->um_extattr.uepm_lock);
177 }
178 
179 static void
180 ulfs_extattr_uepm_unlock(struct ulfsmount *ump)
181 {
182 
183 	if (ump->um_extattr.uepm_lockcnt != 0) {
184 		KASSERT(mutex_owned(&ump->um_extattr.uepm_lock));
185 		ump->um_extattr.uepm_lockcnt--;
186 		return;
187 	}
188 	mutex_exit(&ump->um_extattr.uepm_lock);
189 }
190 
191 /*-
192  * Determine whether the name passed is a valid name for an actual
193  * attribute.
194  *
195  * Invalid currently consists of:
196  *	 NULL pointer for attrname
197  *	 zero-length attrname (used to retrieve application attribute list)
198  */
199 static int
200 ulfs_extattr_valid_attrname(int attrnamespace, const char *attrname)
201 {
202 
203 	if (attrname == NULL)
204 		return (0);
205 	if (strlen(attrname) == 0)
206 		return (0);
207 	return (1);
208 }
209 
210 /*
211  * Autocreate an attribute storage
212  */
213 static struct ulfs_extattr_list_entry *
214 ulfs_extattr_autocreate_attr(struct vnode *vp, int attrnamespace,
215     const char *attrname, struct lwp *l)
216 {
217 	struct mount *mp = vp->v_mount;
218 	struct ulfsmount *ump = VFSTOULFS(mp);
219 	struct vnode *backing_vp;
220 	struct nameidata nd;
221 	struct pathbuf *pb;
222 	char *path;
223 	struct ulfs_extattr_fileheader uef;
224 	struct ulfs_extattr_list_entry *uele;
225 	int error;
226 
227 	path = PNBUF_GET();
228 
229 	/*
230 	 * We only support system and user namespace autocreation
231 	 */
232 	switch (attrnamespace) {
233 	case EXTATTR_NAMESPACE_SYSTEM:
234 		(void)snprintf(path, PATH_MAX, "%s/%s/%s/%s",
235 			       mp->mnt_stat.f_mntonname,
236 			       ULFS_EXTATTR_FSROOTSUBDIR,
237 			       ULFS_EXTATTR_SUBDIR_SYSTEM,
238 			       attrname);
239 		break;
240 	case EXTATTR_NAMESPACE_USER:
241 		(void)snprintf(path, PATH_MAX, "%s/%s/%s/%s",
242 			       mp->mnt_stat.f_mntonname,
243 			       ULFS_EXTATTR_FSROOTSUBDIR,
244 			       ULFS_EXTATTR_SUBDIR_USER,
245 			       attrname);
246 		break;
247 	default:
248 		PNBUF_PUT(path);
249 		return NULL;
250 		break;
251 	}
252 
253 	/*
254 	 * XXX unlock/lock should only be done when setting extattr
255 	 * on backing store or one of its parent directory
256 	 * including root, but we always do it for now.
257 	 */
258 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
259 	VOP_UNLOCK(vp);
260 
261 	pb = pathbuf_create(path);
262 	NDINIT(&nd, CREATE, LOCKPARENT, pb);
263 
264 	error = vn_open(&nd, O_CREAT|O_RDWR, 0600);
265 
266 	/*
267 	 * Reacquire the lock on the vnode
268 	 */
269 	KASSERT(VOP_ISLOCKED(vp) == 0);
270 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
271 
272 	if (error != 0) {
273 		pathbuf_destroy(pb);
274 		PNBUF_PUT(path);
275 		return NULL;
276 	}
277 
278 	KASSERT(nd.ni_vp != NULL);
279 	KASSERT(VOP_ISLOCKED(nd.ni_vp) == LK_EXCLUSIVE);
280 	KASSERT(VOP_ISLOCKED(nd.ni_dvp) == 0);
281 
282 	/*
283  	 * backing_vp is the backing store.
284 	 */
285 	backing_vp = nd.ni_vp;
286 	pathbuf_destroy(pb);
287 	PNBUF_PUT(path);
288 
289 	uef.uef_magic = ULFS_EXTATTR_MAGIC;
290 	uef.uef_version = ULFS_EXTATTR_VERSION;
291 	uef.uef_size = ulfs_extattr_autocreate;
292 
293 	error = vn_rdwr(UIO_WRITE, backing_vp, &uef, sizeof(uef), 0,
294 		        UIO_SYSSPACE, IO_NODELOCKED|IO_APPEND,
295 			l->l_cred, NULL, l);
296 
297 	VOP_UNLOCK(backing_vp);
298 
299 	if (error != 0) {
300 		printf("%s: write uef header failed for %s, error = %d\n",
301 		       __func__, attrname, error);
302 		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
303 		return NULL;
304 	}
305 
306 	/*
307 	 * Now enable attribute.
308 	 */
309 	error = ulfs_extattr_enable(ump,attrnamespace, attrname, backing_vp, l);
310 	KASSERT(VOP_ISLOCKED(backing_vp) == 0);
311 
312 	if (error != 0) {
313 		printf("%s: enable %s failed, error %d\n",
314 		       __func__, attrname, error);
315 		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
316 		return NULL;
317 	}
318 
319 	uele = ulfs_extattr_find_attr(ump, attrnamespace, attrname);
320 	if (uele == NULL) {
321 		printf("%s: atttribute %s created but not found!\n",
322 		       __func__, attrname);
323 		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
324 		return NULL;
325 	}
326 
327 	printf("%s: EA backing store autocreated for %s\n",
328 	       mp->mnt_stat.f_mntonname, attrname);
329 
330 	return uele;
331 }
332 
333 /*
334  * Locate an attribute given a name and mountpoint.
335  * Must be holding uepm lock for the mount point.
336  */
337 static struct ulfs_extattr_list_entry *
338 ulfs_extattr_find_attr(struct ulfsmount *ump, int attrnamespace,
339     const char *attrname)
340 {
341 	struct ulfs_extattr_list_entry *search_attribute;
342 
343 	for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
344 	    search_attribute != NULL;
345 	    search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
346 		if (!(strncmp(attrname, search_attribute->uele_attrname,
347 		    ULFS_EXTATTR_MAXEXTATTRNAME)) &&
348 		    (attrnamespace == search_attribute->uele_attrnamespace)) {
349 			return (search_attribute);
350 		}
351 	}
352 
353 	return (0);
354 }
355 
356 /*
357  * Initialize per-FS structures supporting extended attributes.  Do not
358  * start extended attributes yet.
359  */
360 void
361 ulfs_extattr_uepm_init(struct ulfs_extattr_per_mount *uepm)
362 {
363 
364 	uepm->uepm_flags = 0;
365 	uepm->uepm_lockcnt = 0;
366 
367 	LIST_INIT(&uepm->uepm_list);
368 	mutex_init(&uepm->uepm_lock, MUTEX_DEFAULT, IPL_NONE);
369 	uepm->uepm_flags |= ULFS_EXTATTR_UEPM_INITIALIZED;
370 }
371 
372 /*
373  * Destroy per-FS structures supporting extended attributes.  Assumes
374  * that EAs have already been stopped, and will panic if not.
375  */
376 void
377 ulfs_extattr_uepm_destroy(struct ulfs_extattr_per_mount *uepm)
378 {
379 
380 	if (!(uepm->uepm_flags & ULFS_EXTATTR_UEPM_INITIALIZED))
381 		panic("ulfs_extattr_uepm_destroy: not initialized");
382 
383 	if ((uepm->uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
384 		panic("ulfs_extattr_uepm_destroy: called while still started");
385 
386 	/*
387 	 * It's not clear that either order for the next two lines is
388 	 * ideal, and it should never be a problem if this is only called
389 	 * during unmount, and with vfs_busy().
390 	 */
391 	uepm->uepm_flags &= ~ULFS_EXTATTR_UEPM_INITIALIZED;
392 	mutex_destroy(&uepm->uepm_lock);
393 }
394 
395 /*
396  * Start extended attribute support on an FS.
397  */
398 int
399 ulfs_extattr_start(struct mount *mp, struct lwp *l)
400 {
401 	struct ulfsmount *ump;
402 	int error = 0;
403 
404 	ump = VFSTOULFS(mp);
405 
406 	ulfs_extattr_uepm_lock(ump);
407 
408 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_INITIALIZED)) {
409 		error = EOPNOTSUPP;
410 		goto unlock;
411 	}
412 	if (ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED) {
413 		error = EBUSY;
414 		goto unlock;
415 	}
416 
417 	ump->um_extattr.uepm_flags |= ULFS_EXTATTR_UEPM_STARTED;
418 
419 	ump->um_extattr.uepm_ucred = l->l_cred;
420 	kauth_cred_hold(ump->um_extattr.uepm_ucred);
421 
422  unlock:
423 	ulfs_extattr_uepm_unlock(ump);
424 
425 	return (error);
426 }
427 
428 /*
429  * Helper routine: given a locked parent directory and filename, return
430  * the locked vnode of the inode associated with the name.  Will not
431  * follow symlinks, may return any type of vnode.  Lock on parent will
432  * be released even in the event of a failure.  In the event that the
433  * target is the parent (i.e., "."), there will be two references and
434  * one lock, requiring the caller to possibly special-case.
435  */
436 static int
437 ulfs_extattr_lookup(struct vnode *start_dvp, int lockparent, const char *dirname,
438     struct vnode **vp, struct lwp *l)
439 {
440 	struct vop_lookup_args vargs;
441 	struct componentname cnp;
442 	struct vnode *target_vp;
443 	char *pnbuf;
444 	int error;
445 
446 	KASSERT(VOP_ISLOCKED(start_dvp) == LK_EXCLUSIVE);
447 
448 	pnbuf = PNBUF_GET();
449 
450 	memset(&cnp, 0, sizeof(cnp));
451 	cnp.cn_nameiop = LOOKUP;
452 	cnp.cn_flags = ISLASTCN | lockparent;
453 	cnp.cn_cred = l->l_cred;
454 	cnp.cn_nameptr = pnbuf;
455 	error = copystr(dirname, pnbuf, MAXPATHLEN, &cnp.cn_namelen);
456 	if (error) {
457 		if (lockparent == 0) {
458 			VOP_UNLOCK(start_dvp);
459 		}
460 		PNBUF_PUT(pnbuf);
461 		printf("ulfs_extattr_lookup: copystr failed\n");
462 		return (error);
463 	}
464 	cnp.cn_namelen--;	/* trim nul termination */
465 	vargs.a_desc = NULL;
466 	vargs.a_dvp = start_dvp;
467 	vargs.a_vpp = &target_vp;
468 	vargs.a_cnp = &cnp;
469 	error = ulfs_lookup(&vargs);
470 	PNBUF_PUT(pnbuf);
471 	if (error) {
472 		if (lockparent == 0) {
473 			VOP_UNLOCK(start_dvp);
474 		}
475 		return (error);
476 	}
477 #if 0
478 	if (target_vp == start_dvp)
479 		panic("ulfs_extattr_lookup: target_vp == start_dvp");
480 #endif
481 
482 	if ((target_vp != start_dvp) && (lockparent == 0))
483 		 VOP_UNLOCK(start_dvp);
484 
485 	KASSERT(VOP_ISLOCKED(target_vp) == LK_EXCLUSIVE);
486 	*vp = target_vp;
487 	return (0);
488 }
489 
490 /*
491  * Enable an EA using the passed filesystem, backing vnode, attribute name,
492  * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
493  * to be locked when passed in.  The vnode will be returned unlocked,
494  * regardless of success/failure of the function.  As a result, the caller
495  * will always need to vrele(), but not vput().
496  */
497 static int
498 ulfs_extattr_enable_with_open(struct ulfsmount *ump, struct vnode *vp,
499     int attrnamespace, const char *attrname, struct lwp *l)
500 {
501 	int error;
502 
503 	error = VOP_OPEN(vp, FREAD|FWRITE, l->l_cred);
504 	if (error) {
505 		printf("ulfs_extattr_enable_with_open.VOP_OPEN(): failed "
506 		    "with %d\n", error);
507 		VOP_UNLOCK(vp);
508 		return (error);
509 	}
510 
511 	mutex_enter(vp->v_interlock);
512 	vp->v_writecount++;
513 	mutex_exit(vp->v_interlock);
514 
515 	vref(vp);
516 
517 	VOP_UNLOCK(vp);
518 
519 	error = ulfs_extattr_enable(ump, attrnamespace, attrname, vp, l);
520 	if (error != 0)
521 		vn_close(vp, FREAD|FWRITE, l->l_cred);
522 	return (error);
523 }
524 
525 /*
526  * Given a locked directory vnode, iterate over the names in the directory
527  * and use ulfs_extattr_lookup() to retrieve locked vnodes of potential
528  * attribute files.  Then invoke ulfs_extattr_enable_with_open() on each
529  * to attempt to start the attribute.  Leaves the directory locked on
530  * exit.
531  */
532 static int
533 ulfs_extattr_iterate_directory(struct ulfsmount *ump, struct vnode *dvp,
534     int attrnamespace, struct lwp *l)
535 {
536 	struct vop_readdir_args vargs;
537 	struct statvfs *sbp = &ump->um_mountp->mnt_stat;
538 	struct dirent *dp, *edp;
539 	struct vnode *attr_vp;
540 	struct uio auio;
541 	struct iovec aiov;
542 	char *dirbuf;
543 	int error, eofflag = 0;
544 
545 	if (dvp->v_type != VDIR)
546 		return (ENOTDIR);
547 
548 	dirbuf = kmem_alloc(LFS_DIRBLKSIZ, KM_SLEEP);
549 
550 	auio.uio_iov = &aiov;
551 	auio.uio_iovcnt = 1;
552 	auio.uio_rw = UIO_READ;
553 	auio.uio_offset = 0;
554 	UIO_SETUP_SYSSPACE(&auio);
555 
556 	vargs.a_desc = NULL;
557 	vargs.a_vp = dvp;
558 	vargs.a_uio = &auio;
559 	vargs.a_cred = l->l_cred;
560 	vargs.a_eofflag = &eofflag;
561 	vargs.a_ncookies = NULL;
562 	vargs.a_cookies = NULL;
563 
564 	while (!eofflag) {
565 		auio.uio_resid = LFS_DIRBLKSIZ;
566 		aiov.iov_base = dirbuf;
567 		aiov.iov_len = LFS_DIRBLKSIZ;
568 		error = ulfs_readdir(&vargs);
569 		if (error) {
570 			printf("ulfs_extattr_iterate_directory: ulfs_readdir "
571 			    "%d\n", error);
572 			return (error);
573 		}
574 
575 		/*
576 		 * XXXRW: While in LFS, we always get LFS_DIRBLKSIZ returns from
577 		 * the directory code on success, on other file systems this
578 		 * may not be the case.  For portability, we should check the
579 		 * read length on return from ulfs_readdir().
580 		 */
581 		edp = (struct dirent *)&dirbuf[LFS_DIRBLKSIZ];
582 		for (dp = (struct dirent *)dirbuf; dp < edp; ) {
583 			if (dp->d_reclen == 0)
584 				break;
585 			/* Skip "." and ".." */
586 			if (dp->d_name[0] == '.' &&
587 			    (dp->d_name[1] == '\0' ||
588 			     (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
589 				goto next;
590 			error = ulfs_extattr_lookup(dvp, LOCKPARENT,
591 			    dp->d_name, &attr_vp, l);
592 			if (error == ENOENT) {
593 				goto next; /* keep silent */
594 			} else if (error) {
595 				printf("ulfs_extattr_iterate_directory: lookup "
596 				    "%s %d\n", dp->d_name, error);
597 			} else if (attr_vp == dvp) {
598 				vrele(attr_vp);
599 			} else if (attr_vp->v_type != VREG) {
600 				vput(attr_vp);
601 			} else {
602 				error = ulfs_extattr_enable_with_open(ump,
603 				    attr_vp, attrnamespace, dp->d_name, l);
604 				vrele(attr_vp);
605 				if (error) {
606 					printf("ulfs_extattr_iterate_directory: "
607 					    "enable %s %d\n", dp->d_name,
608 					    error);
609 				} else if (bootverbose) {
610 					printf("%s: EA %s loaded\n",
611 					       sbp->f_mntonname, dp->d_name);
612 				}
613 			}
614  next:
615 			dp = (struct dirent *) ((char *)dp + dp->d_reclen);
616 			if (dp >= edp)
617 				break;
618 		}
619 	}
620 	kmem_free(dirbuf, LFS_DIRBLKSIZ);
621 
622 	return (0);
623 }
624 
625 /*
626  * Auto-start of extended attributes, to be executed (optionally) at
627  * mount-time.
628  */
629 int
630 ulfs_extattr_autostart(struct mount *mp, struct lwp *l)
631 {
632 	struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
633 	int error;
634 
635 	/*
636 	 * Does ULFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
637 	 * If so, automatically start EA's.
638 	 */
639 	error = VFS_ROOT(mp, &rvp);
640 	if (error) {
641 		printf("ulfs_extattr_autostart.VFS_ROOT() returned %d\n",
642 		    error);
643 		return (error);
644 	}
645 
646 	KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
647 
648 	error = ulfs_extattr_lookup(rvp, 0,
649 	    ULFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, l);
650 	if (error) {
651 		/* rvp ref'd but now unlocked */
652 		KASSERT(VOP_ISLOCKED(rvp) == 0);
653 		vrele(rvp);
654 		return (error);
655 	}
656 	if (rvp == attr_dvp) {
657 		/* Should never happen. */
658 		KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
659 		vrele(attr_dvp);
660 		vput(rvp);
661 		return (EINVAL);
662 	}
663 	KASSERT(VOP_ISLOCKED(rvp) == 0);
664 	vrele(rvp);
665 
666 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
667 
668 	if (attr_dvp->v_type != VDIR) {
669 		printf("ulfs_extattr_autostart: %s != VDIR\n",
670 		    ULFS_EXTATTR_FSROOTSUBDIR);
671 		goto return_vput_attr_dvp;
672 	}
673 
674 	error = ulfs_extattr_start(mp, l);
675 	if (error) {
676 		printf("ulfs_extattr_autostart: ulfs_extattr_start failed (%d)\n",
677 		    error);
678 		goto return_vput_attr_dvp;
679 	}
680 
681 	/*
682 	 * Look for two subdirectories: ULFS_EXTATTR_SUBDIR_SYSTEM,
683 	 * ULFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
684 	 * and start with appropriate type.  Failures in either don't
685 	 * result in an over-all failure.  attr_dvp is left locked to
686 	 * be cleaned up on exit.
687 	 */
688 	error = ulfs_extattr_lookup(attr_dvp, LOCKPARENT,
689 	    ULFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, l);
690 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
691 	if (error == 0) {
692 		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
693 		error = ulfs_extattr_iterate_directory(VFSTOULFS(mp),
694 		    attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, l);
695 		if (error)
696 			printf("ulfs_extattr_iterate_directory returned %d\n",
697 			    error);
698 		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
699 		vput(attr_system_dvp);
700 	}
701 
702 	error = ulfs_extattr_lookup(attr_dvp, LOCKPARENT,
703 	    ULFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, l);
704 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
705 	if (error == 0) {
706 		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
707 		error = ulfs_extattr_iterate_directory(VFSTOULFS(mp),
708 		    attr_user_dvp, EXTATTR_NAMESPACE_USER, l);
709 		if (error)
710 			printf("ulfs_extattr_iterate_directory returned %d\n",
711 			    error);
712 		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
713 		vput(attr_user_dvp);
714 	}
715 
716 	/* Mask startup failures in sub-directories. */
717 	error = 0;
718 
719  return_vput_attr_dvp:
720 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
721 	vput(attr_dvp);
722 
723 	return (error);
724 }
725 
726 /*
727  * Stop extended attribute support on an FS.
728  */
729 void
730 ulfs_extattr_stop(struct mount *mp, struct lwp *l)
731 {
732 	struct ulfs_extattr_list_entry *uele;
733 	struct ulfsmount *ump = VFSTOULFS(mp);
734 
735 	ulfs_extattr_uepm_lock(ump);
736 
737 	/*
738 	 * If we haven't been started, no big deal.  Just short-circuit
739 	 * the processing work.
740 	 */
741 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED)) {
742 		goto unlock;
743 	}
744 
745 	while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
746 		uele = LIST_FIRST(&ump->um_extattr.uepm_list);
747 		ulfs_extattr_disable(ump, uele->uele_attrnamespace,
748 		    uele->uele_attrname, l);
749 	}
750 
751 	ump->um_extattr.uepm_flags &= ~ULFS_EXTATTR_UEPM_STARTED;
752 
753 	kauth_cred_free(ump->um_extattr.uepm_ucred);
754 	ump->um_extattr.uepm_ucred = NULL;
755 
756  unlock:
757 	ulfs_extattr_uepm_unlock(ump);
758 }
759 
760 /*
761  * Enable a named attribute on the specified filesystem; provide an
762  * unlocked backing vnode to hold the attribute data.
763  */
764 static int
765 ulfs_extattr_enable(struct ulfsmount *ump, int attrnamespace,
766     const char *attrname, struct vnode *backing_vnode, struct lwp *l)
767 {
768 	struct ulfs_extattr_list_entry *attribute;
769 	struct iovec aiov;
770 	struct uio auio;
771 	int error = 0;
772 
773 	if (!ulfs_extattr_valid_attrname(attrnamespace, attrname))
774 		return (EINVAL);
775 	if (backing_vnode->v_type != VREG)
776 		return (EINVAL);
777 
778 	attribute = kmem_zalloc(sizeof(*attribute), KM_SLEEP);
779 
780 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED)) {
781 		error = EOPNOTSUPP;
782 		goto free_exit;
783 	}
784 
785 	if (ulfs_extattr_find_attr(ump, attrnamespace, attrname)) {
786 		error = EEXIST;
787 		goto free_exit;
788 	}
789 
790 	strncpy(attribute->uele_attrname, attrname,
791 	    ULFS_EXTATTR_MAXEXTATTRNAME);
792 	attribute->uele_attrnamespace = attrnamespace;
793 	memset(&attribute->uele_fileheader, 0,
794 	    sizeof(struct ulfs_extattr_fileheader));
795 
796 	attribute->uele_backing_vnode = backing_vnode;
797 
798 	auio.uio_iov = &aiov;
799 	auio.uio_iovcnt = 1;
800 	aiov.iov_base = (void *) &attribute->uele_fileheader;
801 	aiov.iov_len = sizeof(struct ulfs_extattr_fileheader);
802 	auio.uio_resid = sizeof(struct ulfs_extattr_fileheader);
803 	auio.uio_offset = (off_t) 0;
804 	auio.uio_rw = UIO_READ;
805 	UIO_SETUP_SYSSPACE(&auio);
806 
807 	vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
808 	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
809 	    ump->um_extattr.uepm_ucred);
810 
811 	if (error)
812 		goto unlock_free_exit;
813 
814 	if (auio.uio_resid != 0) {
815 		printf("ulfs_extattr_enable: malformed attribute header\n");
816 		error = EINVAL;
817 		goto unlock_free_exit;
818 	}
819 
820 	/*
821 	 * Try to determine the byte order of the attribute file.
822 	 */
823 	if (attribute->uele_fileheader.uef_magic != ULFS_EXTATTR_MAGIC) {
824 		attribute->uele_flags |= UELE_F_NEEDSWAP;
825 		attribute->uele_fileheader.uef_magic =
826 		    ulfs_rw32(attribute->uele_fileheader.uef_magic,
827 			     UELE_NEEDSWAP(attribute));
828 		if (attribute->uele_fileheader.uef_magic != ULFS_EXTATTR_MAGIC) {
829 			printf("ulfs_extattr_enable: invalid attribute header "
830 			       "magic\n");
831 			error = EINVAL;
832 			goto unlock_free_exit;
833 		}
834 	}
835 	attribute->uele_fileheader.uef_version =
836 	    ulfs_rw32(attribute->uele_fileheader.uef_version,
837 		     UELE_NEEDSWAP(attribute));
838 	attribute->uele_fileheader.uef_size =
839 	    ulfs_rw32(attribute->uele_fileheader.uef_size,
840 		     UELE_NEEDSWAP(attribute));
841 
842 	if (attribute->uele_fileheader.uef_version != ULFS_EXTATTR_VERSION) {
843 		printf("ulfs_extattr_enable: incorrect attribute header "
844 		    "version\n");
845 		error = EINVAL;
846 		goto unlock_free_exit;
847 	}
848 
849 	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
850 	    uele_entries);
851 
852 	VOP_UNLOCK(backing_vnode);
853 	return (0);
854 
855  unlock_free_exit:
856 	VOP_UNLOCK(backing_vnode);
857 
858  free_exit:
859 	kmem_free(attribute, sizeof(*attribute));
860 	return (error);
861 }
862 
863 /*
864  * Disable extended attribute support on an FS.
865  */
866 static int
867 ulfs_extattr_disable(struct ulfsmount *ump, int attrnamespace,
868     const char *attrname, struct lwp *l)
869 {
870 	struct ulfs_extattr_list_entry *uele;
871 	int error = 0;
872 
873 	if (!ulfs_extattr_valid_attrname(attrnamespace, attrname))
874 		return (EINVAL);
875 
876 	uele = ulfs_extattr_find_attr(ump, attrnamespace, attrname);
877 	if (!uele)
878 		return (ENODATA);
879 
880 	LIST_REMOVE(uele, uele_entries);
881 
882 	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
883 	    l->l_cred);
884 
885 	kmem_free(uele, sizeof(*uele));
886 
887 	return (error);
888 }
889 
890 /*
891  * VFS call to manage extended attributes in ULFS.  If filename_vp is
892  * non-NULL, it must be passed in locked, and regardless of errors in
893  * processing, will be unlocked.
894  */
895 int
896 ulfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
897     int attrnamespace, const char *attrname)
898 {
899 	struct lwp *l = curlwp;
900 	struct ulfsmount *ump = VFSTOULFS(mp);
901 	int error;
902 
903 	/*
904 	 * Only privileged processes can configure extended attributes.
905 	 */
906 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_EXTATTR,
907 	    0, mp, NULL, NULL);
908 	if (error) {
909 		if (filename_vp != NULL)
910 			VOP_UNLOCK(filename_vp);
911 		return (error);
912 	}
913 
914 	switch(cmd) {
915 	case ULFS_EXTATTR_CMD_START:
916 		if (filename_vp != NULL) {
917 			VOP_UNLOCK(filename_vp);
918 			return (EINVAL);
919 		}
920 		if (attrname != NULL)
921 			return (EINVAL);
922 
923 		error = ulfs_extattr_autostart(mp, l);
924 		return (error);
925 
926 	case ULFS_EXTATTR_CMD_STOP:
927 		if (filename_vp != NULL) {
928 			VOP_UNLOCK(filename_vp);
929 			return (EINVAL);
930 		}
931 		if (attrname != NULL)
932 			return (EINVAL);
933 
934 		ulfs_extattr_stop(mp, l);
935 		return (0);
936 
937 	case ULFS_EXTATTR_CMD_ENABLE:
938 		if (filename_vp == NULL)
939 			return (EINVAL);
940 		if (attrname == NULL) {
941 			VOP_UNLOCK(filename_vp);
942 			return (EINVAL);
943 		}
944 
945 		/*
946 		 * ulfs_extattr_enable_with_open() will always unlock the
947 		 * vnode, regardless of failure.
948 		 */
949 		ulfs_extattr_uepm_lock(ump);
950 		error = ulfs_extattr_enable_with_open(ump, filename_vp,
951 		    attrnamespace, attrname, l);
952 		ulfs_extattr_uepm_unlock(ump);
953 		return (error);
954 
955 	case ULFS_EXTATTR_CMD_DISABLE:
956 		if (filename_vp != NULL) {
957 			VOP_UNLOCK(filename_vp);
958 			return (EINVAL);
959 		}
960 		if (attrname == NULL)
961 			return (EINVAL);
962 
963 		ulfs_extattr_uepm_lock(ump);
964 		error = ulfs_extattr_disable(ump, attrnamespace, attrname, l);
965 		ulfs_extattr_uepm_unlock(ump);
966 		return (error);
967 
968 	default:
969 		return (EINVAL);
970 	}
971 }
972 
973 /*
974  * Read extended attribute header for a given vnode and attribute.
975  * Backing vnode should be locked and unlocked by caller.
976  */
977 static int
978 ulfs_extattr_get_header(struct vnode *vp, struct ulfs_extattr_list_entry *uele,
979     struct ulfs_extattr_header *ueh, off_t *bap)
980 {
981 	struct mount *mp = vp->v_mount;
982 	struct ulfsmount *ump = VFSTOULFS(mp);
983 	struct inode *ip = VTOI(vp);
984 	off_t base_offset;
985 	struct iovec aiov;
986 	struct uio aio;
987 	int error;
988 
989 	/*
990 	 * Find base offset of header in file based on file header size, and
991 	 * data header size + maximum data size, indexed by inode number.
992 	 */
993 	base_offset = sizeof(struct ulfs_extattr_fileheader) +
994 	    ip->i_number * (sizeof(struct ulfs_extattr_header) +
995 	    uele->uele_fileheader.uef_size);
996 
997 	/*
998 	 * Read in the data header to see if the data is defined, and if so
999 	 * how much.
1000 	 */
1001 	memset(ueh, 0, sizeof(struct ulfs_extattr_header));
1002 	aiov.iov_base = ueh;
1003 	aiov.iov_len = sizeof(struct ulfs_extattr_header);
1004 	aio.uio_iov = &aiov;
1005 	aio.uio_iovcnt = 1;
1006 	aio.uio_rw = UIO_READ;
1007 	aio.uio_offset = base_offset;
1008 	aio.uio_resid = sizeof(struct ulfs_extattr_header);
1009 	UIO_SETUP_SYSSPACE(&aio);
1010 
1011 	error = VOP_READ(uele->uele_backing_vnode, &aio,
1012 	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1013 	if (error)
1014 		return error;
1015 
1016 	/*
1017 	 * Attribute headers are kept in file system byte order.
1018 	 * XXX What about the blob of data?
1019 	 */
1020 	ueh->ueh_flags = ulfs_rw32(ueh->ueh_flags, UELE_NEEDSWAP(uele));
1021 	ueh->ueh_len   = ulfs_rw32(ueh->ueh_len, UELE_NEEDSWAP(uele));
1022 	ueh->ueh_i_gen = ulfs_rw32(ueh->ueh_i_gen, UELE_NEEDSWAP(uele));
1023 
1024 	/* Defined? */
1025 	if ((ueh->ueh_flags & ULFS_EXTATTR_ATTR_FLAG_INUSE) == 0)
1026 		return ENODATA;
1027 
1028 	/* Valid for the current inode generation? */
1029 	if (ueh->ueh_i_gen != ip->i_gen) {
1030 		/*
1031 		 * The inode itself has a different generation number
1032 		 * than the uele data.  For now, the best solution
1033 		 * is to coerce this to undefined, and let it get cleaned
1034 		 * up by the next write or extattrctl clean.
1035 		 */
1036 		printf("%s (%s): inode gen inconsistency (%u, %jd)\n",
1037 		       __func__,  mp->mnt_stat.f_mntonname, ueh->ueh_i_gen,
1038 		       (intmax_t)ip->i_gen);
1039 		return ENODATA;
1040 	}
1041 
1042 	/* Local size consistency check. */
1043 	if (ueh->ueh_len > uele->uele_fileheader.uef_size)
1044 		return ENXIO;
1045 
1046 	/* Return base offset */
1047 	if (bap != NULL)
1048 		*bap = base_offset;
1049 
1050 	return 0;
1051 }
1052 
1053 /*
1054  * Vnode operation to retrieve a named extended attribute.
1055  */
1056 int
1057 ulfs_getextattr(struct vop_getextattr_args *ap)
1058 /*
1059 vop_getextattr {
1060 	IN struct vnode *a_vp;
1061 	IN int a_attrnamespace;
1062 	IN const char *a_name;
1063 	INOUT struct uio *a_uio;
1064 	OUT size_t *a_size;
1065 	IN kauth_cred_t a_cred;
1066 };
1067 */
1068 {
1069 	struct mount *mp = ap->a_vp->v_mount;
1070 	struct ulfsmount *ump = VFSTOULFS(mp);
1071 	int error;
1072 
1073 	ulfs_extattr_uepm_lock(ump);
1074 
1075 	error = ulfs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1076 	    ap->a_uio, ap->a_size, ap->a_cred, curlwp);
1077 
1078 	ulfs_extattr_uepm_unlock(ump);
1079 
1080 	return (error);
1081 }
1082 
1083 /*
1084  * Real work associated with retrieving a named attribute--assumes that
1085  * the attribute lock has already been grabbed.
1086  */
1087 static int
1088 ulfs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
1089     struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l)
1090 {
1091 	struct ulfs_extattr_list_entry *attribute;
1092 	struct ulfs_extattr_header ueh;
1093 	struct mount *mp = vp->v_mount;
1094 	struct ulfsmount *ump = VFSTOULFS(mp);
1095 	off_t base_offset;
1096 	size_t len, old_len;
1097 	int error = 0;
1098 
1099 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1100 		return (EOPNOTSUPP);
1101 
1102 	if (strlen(name) == 0)
1103 		return (EINVAL);
1104 
1105 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1106 	    VREAD);
1107 	if (error)
1108 		return (error);
1109 
1110 	attribute = ulfs_extattr_find_attr(ump, attrnamespace, name);
1111 	if (!attribute)
1112 		return (ENODATA);
1113 
1114 	/*
1115 	 * Allow only offsets of zero to encourage the read/replace
1116 	 * extended attribute semantic.  Otherwise we can't guarantee
1117 	 * atomicity, as we don't provide locks for extended attributes.
1118 	 */
1119 	if (uio != NULL && uio->uio_offset != 0)
1120 		return (ENXIO);
1121 
1122 	/*
1123 	 * Don't need to get a lock on the backing file if the getattr is
1124 	 * being applied to the backing file, as the lock is already held.
1125 	 */
1126 	if (attribute->uele_backing_vnode != vp)
1127 		vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
1128 
1129 	error = ulfs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1130 	if (error)
1131 		goto vopunlock_exit;
1132 
1133 	/* Return full data size if caller requested it. */
1134 	if (size != NULL)
1135 		*size = ueh.ueh_len;
1136 
1137 	/* Return data if the caller requested it. */
1138 	if (uio != NULL) {
1139 		/* Allow for offset into the attribute data. */
1140 		uio->uio_offset = base_offset + sizeof(struct
1141 		    ulfs_extattr_header);
1142 
1143 		/*
1144 		 * Figure out maximum to transfer -- use buffer size and
1145 		 * local data limit.
1146 		 */
1147 		len = MIN(uio->uio_resid, ueh.ueh_len);
1148 		old_len = uio->uio_resid;
1149 		uio->uio_resid = len;
1150 
1151 		error = VOP_READ(attribute->uele_backing_vnode, uio,
1152 		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1153 		if (error)
1154 			goto vopunlock_exit;
1155 
1156 		uio->uio_resid = old_len - (len - uio->uio_resid);
1157 	}
1158 
1159  vopunlock_exit:
1160 
1161 	if (uio != NULL)
1162 		uio->uio_offset = 0;
1163 
1164 	if (attribute->uele_backing_vnode != vp)
1165 		VOP_UNLOCK(attribute->uele_backing_vnode);
1166 
1167 	return (error);
1168 }
1169 
1170 /*
1171  * Vnode operation to list extended attribute for a vnode
1172  */
1173 int
1174 ulfs_listextattr(struct vop_listextattr_args *ap)
1175 /*
1176 vop_listextattr {
1177 	IN struct vnode *a_vp;
1178 	IN int a_attrnamespace;
1179 	INOUT struct uio *a_uio;
1180 	OUT size_t *a_size;
1181 	IN int flag;
1182 	IN kauth_cred_t a_cred;
1183 	struct proc *a_p;
1184 };
1185 */
1186 {
1187 	struct mount *mp = ap->a_vp->v_mount;
1188 	struct ulfsmount *ump = VFSTOULFS(mp);
1189 	int error;
1190 
1191 	ulfs_extattr_uepm_lock(ump);
1192 
1193 	error = ulfs_extattr_list(ap->a_vp, ap->a_attrnamespace,
1194 	    ap->a_uio, ap->a_size, ap->a_flag, ap->a_cred, curlwp);
1195 
1196 	ulfs_extattr_uepm_unlock(ump);
1197 
1198 	return (error);
1199 }
1200 
1201 /*
1202  * Real work associated with retrieving list of attributes--assumes that
1203  * the attribute lock has already been grabbed.
1204  */
1205 static int
1206 ulfs_extattr_list(struct vnode *vp, int attrnamespace,
1207     struct uio *uio, size_t *size, int flag,
1208     kauth_cred_t cred, struct lwp *l)
1209 {
1210 	struct ulfs_extattr_list_entry *uele;
1211 	struct ulfs_extattr_header ueh;
1212 	struct mount *mp = vp->v_mount;
1213 	struct ulfsmount *ump = VFSTOULFS(mp);
1214 	size_t listsize = 0;
1215 	int error = 0;
1216 
1217 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1218 		return (EOPNOTSUPP);
1219 
1220 	/*
1221 	 * XXX: We can move this inside the loop and iterate on individual
1222 	 *	attributes.
1223 	 */
1224 	error = internal_extattr_check_cred(vp, attrnamespace, "", cred,
1225 	    VREAD);
1226 	if (error)
1227 		return (error);
1228 
1229 	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) {
1230 		unsigned char attrnamelen;
1231 
1232 		if (uele->uele_attrnamespace != attrnamespace)
1233 			continue;
1234 
1235 		error = ulfs_extattr_get_header(vp, uele, &ueh, NULL);
1236 		if (error == ENODATA)
1237 			continue;
1238 		if (error != 0)
1239 			return error;
1240 
1241 		/*
1242 		 * Don't need to get a lock on the backing file if
1243 		 * the listattr is being applied to the backing file,
1244 		 * as the lock is already held.
1245 		 */
1246 		if (uele->uele_backing_vnode != vp)
1247 			vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
1248 
1249 		/*
1250 		 * +1 for trailing NUL (listxattr flavor)
1251 		 *  or leading name length (extattr_list_file flavor)
1252 	 	 */
1253 		attrnamelen = strlen(uele->uele_attrname);
1254 		listsize += attrnamelen + 1;
1255 
1256 		/* Return data if the caller requested it. */
1257 		if (uio != NULL) {
1258 			/*
1259 			 * We support two flavors. Either NUL-terminated
1260 			 * strings (a la listxattr), or non NUL-terminated,
1261 			 * one byte length prefixed strings (for
1262 			 * extattr_list_file). EXTATTR_LIST_LENPREFIX switches
1263 		 	 * that second behavior.
1264 			 */
1265 			if (flag & EXTATTR_LIST_LENPREFIX) {
1266 				uint8_t len = (uint8_t)attrnamelen;
1267 
1268 				/* Copy leading name length */
1269 				error = uiomove(&len, sizeof(len), uio);
1270 				if (error != 0)
1271 					break;
1272 			} else {
1273 				/* Include trailing NULL */
1274 				attrnamelen++;
1275 			}
1276 
1277 			error = uiomove(uele->uele_attrname,
1278 					(size_t)attrnamelen, uio);
1279 			if (error != 0)
1280 				break;
1281 		}
1282 
1283 		if (uele->uele_backing_vnode != vp)
1284 			VOP_UNLOCK(uele->uele_backing_vnode);
1285 
1286 		if (error != 0)
1287 			return error;
1288 	}
1289 
1290 	if (uio != NULL)
1291 		uio->uio_offset = 0;
1292 
1293 	/* Return full data size if caller requested it. */
1294 	if (size != NULL)
1295 		*size = listsize;
1296 
1297 	return 0;
1298 }
1299 
1300 /*
1301  * Vnode operation to remove a named attribute.
1302  */
1303 int
1304 ulfs_deleteextattr(struct vop_deleteextattr_args *ap)
1305 /*
1306 vop_deleteextattr {
1307 	IN struct vnode *a_vp;
1308 	IN int a_attrnamespace;
1309 	IN const char *a_name;
1310 	IN kauth_cred_t a_cred;
1311 };
1312 */
1313 {
1314 	struct mount *mp = ap->a_vp->v_mount;
1315 	struct ulfsmount *ump = VFSTOULFS(mp);
1316 	int error;
1317 
1318 	ulfs_extattr_uepm_lock(ump);
1319 
1320 	error = ulfs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1321 	    ap->a_cred, curlwp);
1322 
1323 	ulfs_extattr_uepm_unlock(ump);
1324 
1325 	return (error);
1326 }
1327 
1328 /*
1329  * Vnode operation to set a named attribute.
1330  */
1331 int
1332 ulfs_setextattr(struct vop_setextattr_args *ap)
1333 /*
1334 vop_setextattr {
1335 	IN struct vnode *a_vp;
1336 	IN int a_attrnamespace;
1337 	IN const char *a_name;
1338 	INOUT struct uio *a_uio;
1339 	IN kauth_cred_t a_cred;
1340 };
1341 */
1342 {
1343 	struct mount *mp = ap->a_vp->v_mount;
1344 	struct ulfsmount *ump = VFSTOULFS(mp);
1345 	int error;
1346 
1347 	ulfs_extattr_uepm_lock(ump);
1348 
1349 	/*
1350 	 * XXX: No longer a supported way to delete extended attributes.
1351 	 */
1352 	if (ap->a_uio == NULL) {
1353 		ulfs_extattr_uepm_unlock(ump);
1354 		return (EINVAL);
1355 	}
1356 
1357 	error = ulfs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1358 	    ap->a_uio, ap->a_cred, curlwp);
1359 
1360 	ulfs_extattr_uepm_unlock(ump);
1361 
1362 	return (error);
1363 }
1364 
1365 /*
1366  * Real work associated with setting a vnode's extended attributes;
1367  * assumes that the attribute lock has already been grabbed.
1368  */
1369 static int
1370 ulfs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1371     struct uio *uio, kauth_cred_t cred, struct lwp *l)
1372 {
1373 	struct ulfs_extattr_list_entry *attribute;
1374 	struct ulfs_extattr_header ueh;
1375 	struct iovec local_aiov;
1376 	struct uio local_aio;
1377 	struct mount *mp = vp->v_mount;
1378 	struct ulfsmount *ump = VFSTOULFS(mp);
1379 	struct inode *ip = VTOI(vp);
1380 	off_t base_offset;
1381 	int error = 0, ioflag;
1382 
1383 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1384 		return (EROFS);
1385 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1386 		return (EOPNOTSUPP);
1387 	if (!ulfs_extattr_valid_attrname(attrnamespace, name))
1388 		return (EINVAL);
1389 
1390 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1391 	    VWRITE);
1392 	if (error)
1393 		return (error);
1394 
1395 	attribute = ulfs_extattr_find_attr(ump, attrnamespace, name);
1396 	if (!attribute) {
1397 		attribute =  ulfs_extattr_autocreate_attr(vp, attrnamespace,
1398 							 name, l);
1399 		if  (!attribute)
1400 			return (ENODATA);
1401 	}
1402 
1403 	/*
1404 	 * Early rejection of invalid offsets/length.
1405 	 * Reject: any offset but 0 (replace)
1406 	 *	 Any size greater than attribute size limit
1407  	 */
1408 	if (uio->uio_offset != 0 ||
1409 	    uio->uio_resid > attribute->uele_fileheader.uef_size)
1410 		return (ENXIO);
1411 
1412 	/*
1413 	 * Find base offset of header in file based on file header size, and
1414 	 * data header size + maximum data size, indexed by inode number.
1415 	 */
1416 	base_offset = sizeof(struct ulfs_extattr_fileheader) +
1417 	    ip->i_number * (sizeof(struct ulfs_extattr_header) +
1418 	    attribute->uele_fileheader.uef_size);
1419 
1420 	/*
1421 	 * Write out a data header for the data.
1422 	 */
1423 	ueh.ueh_len = ulfs_rw32((uint32_t) uio->uio_resid,
1424 	    UELE_NEEDSWAP(attribute));
1425 	ueh.ueh_flags = ulfs_rw32(ULFS_EXTATTR_ATTR_FLAG_INUSE,
1426 				 UELE_NEEDSWAP(attribute));
1427 	ueh.ueh_i_gen = ulfs_rw32(ip->i_gen, UELE_NEEDSWAP(attribute));
1428 	local_aiov.iov_base = &ueh;
1429 	local_aiov.iov_len = sizeof(struct ulfs_extattr_header);
1430 	local_aio.uio_iov = &local_aiov;
1431 	local_aio.uio_iovcnt = 1;
1432 	local_aio.uio_rw = UIO_WRITE;
1433 	local_aio.uio_offset = base_offset;
1434 	local_aio.uio_resid = sizeof(struct ulfs_extattr_header);
1435 	UIO_SETUP_SYSSPACE(&local_aio);
1436 
1437 	/*
1438 	 * Don't need to get a lock on the backing file if the setattr is
1439 	 * being applied to the backing file, as the lock is already held.
1440 	 */
1441 	if (attribute->uele_backing_vnode != vp)
1442 		vn_lock(attribute->uele_backing_vnode,
1443 		    LK_EXCLUSIVE | LK_RETRY);
1444 
1445 	ioflag = IO_NODELOCKED;
1446 	if (ulfs_extattr_sync)
1447 		ioflag |= IO_SYNC;
1448 	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1449 	    ump->um_extattr.uepm_ucred);
1450 	if (error)
1451 		goto vopunlock_exit;
1452 
1453 	if (local_aio.uio_resid != 0) {
1454 		error = ENXIO;
1455 		goto vopunlock_exit;
1456 	}
1457 
1458 	/*
1459 	 * Write out user data.
1460 	 * XXX NOT ATOMIC WITH RESPECT TO THE HEADER.
1461 	 */
1462 	uio->uio_offset = base_offset + sizeof(struct ulfs_extattr_header);
1463 
1464 	ioflag = IO_NODELOCKED;
1465 	if (ulfs_extattr_sync)
1466 		ioflag |= IO_SYNC;
1467 	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1468 	    ump->um_extattr.uepm_ucred);
1469 
1470  vopunlock_exit:
1471 	uio->uio_offset = 0;
1472 
1473 	if (attribute->uele_backing_vnode != vp)
1474 		VOP_UNLOCK(attribute->uele_backing_vnode);
1475 
1476 	return (error);
1477 }
1478 
1479 /*
1480  * Real work associated with removing an extended attribute from a vnode.
1481  * Assumes the attribute lock has already been grabbed.
1482  */
1483 static int
1484 ulfs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1485     kauth_cred_t cred, struct lwp *l)
1486 {
1487 	struct ulfs_extattr_list_entry *attribute;
1488 	struct ulfs_extattr_header ueh;
1489 	struct mount *mp = vp->v_mount;
1490 	struct ulfsmount *ump = VFSTOULFS(mp);
1491 	struct iovec local_aiov;
1492 	struct uio local_aio;
1493 	off_t base_offset;
1494 	int error = 0, ioflag;
1495 
1496 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1497 		return (EROFS);
1498 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1499 		return (EOPNOTSUPP);
1500 	if (!ulfs_extattr_valid_attrname(attrnamespace, name))
1501 		return (EINVAL);
1502 
1503 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1504 	    VWRITE);
1505 	if (error)
1506 		return (error);
1507 
1508 	attribute = ulfs_extattr_find_attr(ump, attrnamespace, name);
1509 	if (!attribute)
1510 		return (ENODATA);
1511 
1512 	/*
1513 	 * Don't need to get a lock on the backing file if the getattr is
1514 	 * being applied to the backing file, as the lock is already held.
1515 	 */
1516 	if (attribute->uele_backing_vnode != vp)
1517 		vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
1518 
1519 	error = ulfs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1520 	if (error)
1521 		goto vopunlock_exit;
1522 
1523 	/* Flag it as not in use. */
1524 	ueh.ueh_flags = 0;		/* No need to byte swap 0 */
1525 	ueh.ueh_len = 0;		/* ...ditto... */
1526 
1527 	local_aiov.iov_base = &ueh;
1528 	local_aiov.iov_len = sizeof(struct ulfs_extattr_header);
1529 	local_aio.uio_iov = &local_aiov;
1530 	local_aio.uio_iovcnt = 1;
1531 	local_aio.uio_rw = UIO_WRITE;
1532 	local_aio.uio_offset = base_offset;
1533 	local_aio.uio_resid = sizeof(struct ulfs_extattr_header);
1534 	UIO_SETUP_SYSSPACE(&local_aio);
1535 
1536 	ioflag = IO_NODELOCKED;
1537 	if (ulfs_extattr_sync)
1538 		ioflag |= IO_SYNC;
1539 	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1540 	    ump->um_extattr.uepm_ucred);
1541 	if (error)
1542 		goto vopunlock_exit;
1543 
1544 	if (local_aio.uio_resid != 0)
1545 		error = ENXIO;
1546 
1547  vopunlock_exit:
1548 	VOP_UNLOCK(attribute->uele_backing_vnode);
1549 
1550 	return (error);
1551 }
1552 
1553 /*
1554  * Called by ULFS when an inode is no longer active and should have its
1555  * attributes stripped.
1556  */
1557 void
1558 ulfs_extattr_vnode_inactive(struct vnode *vp, struct lwp *l)
1559 {
1560 	struct ulfs_extattr_list_entry *uele;
1561 	struct mount *mp = vp->v_mount;
1562 	struct ulfsmount *ump = VFSTOULFS(mp);
1563 
1564 	/*
1565 	 * In that case, we cannot lock. We should not have any active vnodes
1566 	 * on the fs if this is not yet initialized but is going to be, so
1567 	 * this can go unlocked.
1568 	 */
1569 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_INITIALIZED))
1570 		return;
1571 
1572 	ulfs_extattr_uepm_lock(ump);
1573 
1574 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED)) {
1575 		ulfs_extattr_uepm_unlock(ump);
1576 		return;
1577 	}
1578 
1579 	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1580 		ulfs_extattr_rm(vp, uele->uele_attrnamespace,
1581 		    uele->uele_attrname, lwp0.l_cred, l);
1582 
1583 	ulfs_extattr_uepm_unlock(ump);
1584 }
1585 
1586 void
1587 ulfs_extattr_init(void)
1588 {
1589 
1590 }
1591 
1592 void
1593 ulfs_extattr_done(void)
1594 {
1595 
1596 }
1597