xref: /minix/sys/ufs/lfs/ulfs_extattr.c (revision 0a6a1f1d)
1 /*	$NetBSD: ulfs_extattr.c,v 1.7 2014/02/07 15:29:23 hannken 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.7 2014/02/07 15:29:23 hannken 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 *
from_freebsd_extattr(int attrnamespace,const char * attrname)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
internal_extattr_check_cred(vnode_t * vp,int attrnamespace,const char * name,kauth_cred_t cred,int access_mode)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
ulfs_extattr_uepm_lock(struct ulfsmount * ump)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
ulfs_extattr_uepm_unlock(struct ulfsmount * ump)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
ulfs_extattr_valid_attrname(int attrnamespace,const char * attrname)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 *
ulfs_extattr_autocreate_attr(struct vnode * vp,int attrnamespace,const char * attrname,struct lwp * l)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 *
ulfs_extattr_find_attr(struct ulfsmount * ump,int attrnamespace,const char * attrname)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
ulfs_extattr_uepm_init(struct ulfs_extattr_per_mount * uepm)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
ulfs_extattr_uepm_destroy(struct ulfs_extattr_per_mount * uepm)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
ulfs_extattr_start(struct mount * mp,struct lwp * l)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
ulfs_extattr_lookup(struct vnode * start_dvp,int lockparent,const char * dirname,struct vnode ** vp,struct lwp * l)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_v2_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) {
483 		error = vn_lock(target_vp, LK_EXCLUSIVE);
484 		if (lockparent == 0)
485 			VOP_UNLOCK(start_dvp);
486 		if (error) {
487 			vrele(target_vp);
488 			return error;
489 		}
490 	}
491 
492 	KASSERT(VOP_ISLOCKED(target_vp) == LK_EXCLUSIVE);
493 	*vp = target_vp;
494 	return (0);
495 }
496 
497 /*
498  * Enable an EA using the passed filesystem, backing vnode, attribute name,
499  * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
500  * to be locked when passed in.  The vnode will be returned unlocked,
501  * regardless of success/failure of the function.  As a result, the caller
502  * will always need to vrele(), but not vput().
503  */
504 static int
ulfs_extattr_enable_with_open(struct ulfsmount * ump,struct vnode * vp,int attrnamespace,const char * attrname,struct lwp * l)505 ulfs_extattr_enable_with_open(struct ulfsmount *ump, struct vnode *vp,
506     int attrnamespace, const char *attrname, struct lwp *l)
507 {
508 	int error;
509 
510 	error = VOP_OPEN(vp, FREAD|FWRITE, l->l_cred);
511 	if (error) {
512 		printf("ulfs_extattr_enable_with_open.VOP_OPEN(): failed "
513 		    "with %d\n", error);
514 		VOP_UNLOCK(vp);
515 		return (error);
516 	}
517 
518 	mutex_enter(vp->v_interlock);
519 	vp->v_writecount++;
520 	mutex_exit(vp->v_interlock);
521 
522 	vref(vp);
523 
524 	VOP_UNLOCK(vp);
525 
526 	error = ulfs_extattr_enable(ump, attrnamespace, attrname, vp, l);
527 	if (error != 0)
528 		vn_close(vp, FREAD|FWRITE, l->l_cred);
529 	return (error);
530 }
531 
532 /*
533  * Given a locked directory vnode, iterate over the names in the directory
534  * and use ulfs_extattr_lookup() to retrieve locked vnodes of potential
535  * attribute files.  Then invoke ulfs_extattr_enable_with_open() on each
536  * to attempt to start the attribute.  Leaves the directory locked on
537  * exit.
538  */
539 static int
ulfs_extattr_iterate_directory(struct ulfsmount * ump,struct vnode * dvp,int attrnamespace,struct lwp * l)540 ulfs_extattr_iterate_directory(struct ulfsmount *ump, struct vnode *dvp,
541     int attrnamespace, struct lwp *l)
542 {
543 	struct vop_readdir_args vargs;
544 	struct statvfs *sbp = &ump->um_mountp->mnt_stat;
545 	struct dirent *dp, *edp;
546 	struct vnode *attr_vp;
547 	struct uio auio;
548 	struct iovec aiov;
549 	char *dirbuf;
550 	int error, eofflag = 0;
551 
552 	if (dvp->v_type != VDIR)
553 		return (ENOTDIR);
554 
555 	dirbuf = kmem_alloc(LFS_DIRBLKSIZ, KM_SLEEP);
556 
557 	auio.uio_iov = &aiov;
558 	auio.uio_iovcnt = 1;
559 	auio.uio_rw = UIO_READ;
560 	auio.uio_offset = 0;
561 	UIO_SETUP_SYSSPACE(&auio);
562 
563 	vargs.a_desc = NULL;
564 	vargs.a_vp = dvp;
565 	vargs.a_uio = &auio;
566 	vargs.a_cred = l->l_cred;
567 	vargs.a_eofflag = &eofflag;
568 	vargs.a_ncookies = NULL;
569 	vargs.a_cookies = NULL;
570 
571 	while (!eofflag) {
572 		auio.uio_resid = LFS_DIRBLKSIZ;
573 		aiov.iov_base = dirbuf;
574 		aiov.iov_len = LFS_DIRBLKSIZ;
575 		error = ulfs_readdir(&vargs);
576 		if (error) {
577 			printf("ulfs_extattr_iterate_directory: ulfs_readdir "
578 			    "%d\n", error);
579 			return (error);
580 		}
581 
582 		/*
583 		 * XXXRW: While in LFS, we always get LFS_DIRBLKSIZ returns from
584 		 * the directory code on success, on other file systems this
585 		 * may not be the case.  For portability, we should check the
586 		 * read length on return from ulfs_readdir().
587 		 */
588 		edp = (struct dirent *)&dirbuf[LFS_DIRBLKSIZ];
589 		for (dp = (struct dirent *)dirbuf; dp < edp; ) {
590 			if (dp->d_reclen == 0)
591 				break;
592 			/* Skip "." and ".." */
593 			if (dp->d_name[0] == '.' &&
594 			    (dp->d_name[1] == '\0' ||
595 			     (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
596 				goto next;
597 			error = ulfs_extattr_lookup(dvp, LOCKPARENT,
598 			    dp->d_name, &attr_vp, l);
599 			if (error == ENOENT) {
600 				goto next; /* keep silent */
601 			} else if (error) {
602 				printf("ulfs_extattr_iterate_directory: lookup "
603 				    "%s %d\n", dp->d_name, error);
604 			} else if (attr_vp == dvp) {
605 				vrele(attr_vp);
606 			} else if (attr_vp->v_type != VREG) {
607 				vput(attr_vp);
608 			} else {
609 				error = ulfs_extattr_enable_with_open(ump,
610 				    attr_vp, attrnamespace, dp->d_name, l);
611 				vrele(attr_vp);
612 				if (error) {
613 					printf("ulfs_extattr_iterate_directory: "
614 					    "enable %s %d\n", dp->d_name,
615 					    error);
616 				} else if (bootverbose) {
617 					printf("%s: EA %s loaded\n",
618 					       sbp->f_mntonname, dp->d_name);
619 				}
620 			}
621  next:
622 			dp = (struct dirent *) ((char *)dp + dp->d_reclen);
623 			if (dp >= edp)
624 				break;
625 		}
626 	}
627 	kmem_free(dirbuf, LFS_DIRBLKSIZ);
628 
629 	return (0);
630 }
631 
632 /*
633  * Auto-start of extended attributes, to be executed (optionally) at
634  * mount-time.
635  */
636 int
ulfs_extattr_autostart(struct mount * mp,struct lwp * l)637 ulfs_extattr_autostart(struct mount *mp, struct lwp *l)
638 {
639 	struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
640 	int error;
641 
642 	/*
643 	 * Does ULFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
644 	 * If so, automatically start EA's.
645 	 */
646 	error = VFS_ROOT(mp, &rvp);
647 	if (error) {
648 		printf("ulfs_extattr_autostart.VFS_ROOT() returned %d\n",
649 		    error);
650 		return (error);
651 	}
652 
653 	KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
654 
655 	error = ulfs_extattr_lookup(rvp, 0,
656 	    ULFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, l);
657 	if (error) {
658 		/* rvp ref'd but now unlocked */
659 		KASSERT(VOP_ISLOCKED(rvp) == 0);
660 		vrele(rvp);
661 		return (error);
662 	}
663 	if (rvp == attr_dvp) {
664 		/* Should never happen. */
665 		KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
666 		vrele(attr_dvp);
667 		vput(rvp);
668 		return (EINVAL);
669 	}
670 	KASSERT(VOP_ISLOCKED(rvp) == 0);
671 	vrele(rvp);
672 
673 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
674 
675 	if (attr_dvp->v_type != VDIR) {
676 		printf("ulfs_extattr_autostart: %s != VDIR\n",
677 		    ULFS_EXTATTR_FSROOTSUBDIR);
678 		goto return_vput_attr_dvp;
679 	}
680 
681 	error = ulfs_extattr_start(mp, l);
682 	if (error) {
683 		printf("ulfs_extattr_autostart: ulfs_extattr_start failed (%d)\n",
684 		    error);
685 		goto return_vput_attr_dvp;
686 	}
687 
688 	/*
689 	 * Look for two subdirectories: ULFS_EXTATTR_SUBDIR_SYSTEM,
690 	 * ULFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
691 	 * and start with appropriate type.  Failures in either don't
692 	 * result in an over-all failure.  attr_dvp is left locked to
693 	 * be cleaned up on exit.
694 	 */
695 	error = ulfs_extattr_lookup(attr_dvp, LOCKPARENT,
696 	    ULFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, l);
697 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
698 	if (error == 0) {
699 		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
700 		error = ulfs_extattr_iterate_directory(VFSTOULFS(mp),
701 		    attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, l);
702 		if (error)
703 			printf("ulfs_extattr_iterate_directory returned %d\n",
704 			    error);
705 		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
706 		vput(attr_system_dvp);
707 	}
708 
709 	error = ulfs_extattr_lookup(attr_dvp, LOCKPARENT,
710 	    ULFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, l);
711 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
712 	if (error == 0) {
713 		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
714 		error = ulfs_extattr_iterate_directory(VFSTOULFS(mp),
715 		    attr_user_dvp, EXTATTR_NAMESPACE_USER, l);
716 		if (error)
717 			printf("ulfs_extattr_iterate_directory returned %d\n",
718 			    error);
719 		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
720 		vput(attr_user_dvp);
721 	}
722 
723 	/* Mask startup failures in sub-directories. */
724 	error = 0;
725 
726  return_vput_attr_dvp:
727 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
728 	vput(attr_dvp);
729 
730 	return (error);
731 }
732 
733 /*
734  * Stop extended attribute support on an FS.
735  */
736 void
ulfs_extattr_stop(struct mount * mp,struct lwp * l)737 ulfs_extattr_stop(struct mount *mp, struct lwp *l)
738 {
739 	struct ulfs_extattr_list_entry *uele;
740 	struct ulfsmount *ump = VFSTOULFS(mp);
741 
742 	ulfs_extattr_uepm_lock(ump);
743 
744 	/*
745 	 * If we haven't been started, no big deal.  Just short-circuit
746 	 * the processing work.
747 	 */
748 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED)) {
749 		goto unlock;
750 	}
751 
752 	while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
753 		uele = LIST_FIRST(&ump->um_extattr.uepm_list);
754 		ulfs_extattr_disable(ump, uele->uele_attrnamespace,
755 		    uele->uele_attrname, l);
756 	}
757 
758 	ump->um_extattr.uepm_flags &= ~ULFS_EXTATTR_UEPM_STARTED;
759 
760 	kauth_cred_free(ump->um_extattr.uepm_ucred);
761 	ump->um_extattr.uepm_ucred = NULL;
762 
763  unlock:
764 	ulfs_extattr_uepm_unlock(ump);
765 }
766 
767 /*
768  * Enable a named attribute on the specified filesystem; provide an
769  * unlocked backing vnode to hold the attribute data.
770  */
771 static int
ulfs_extattr_enable(struct ulfsmount * ump,int attrnamespace,const char * attrname,struct vnode * backing_vnode,struct lwp * l)772 ulfs_extattr_enable(struct ulfsmount *ump, int attrnamespace,
773     const char *attrname, struct vnode *backing_vnode, struct lwp *l)
774 {
775 	struct ulfs_extattr_list_entry *attribute;
776 	struct iovec aiov;
777 	struct uio auio;
778 	int error = 0;
779 
780 	if (!ulfs_extattr_valid_attrname(attrnamespace, attrname))
781 		return (EINVAL);
782 	if (backing_vnode->v_type != VREG)
783 		return (EINVAL);
784 
785 	attribute = kmem_zalloc(sizeof(*attribute), KM_SLEEP);
786 
787 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED)) {
788 		error = EOPNOTSUPP;
789 		goto free_exit;
790 	}
791 
792 	if (ulfs_extattr_find_attr(ump, attrnamespace, attrname)) {
793 		error = EEXIST;
794 		goto free_exit;
795 	}
796 
797 	strncpy(attribute->uele_attrname, attrname,
798 	    ULFS_EXTATTR_MAXEXTATTRNAME);
799 	attribute->uele_attrnamespace = attrnamespace;
800 	memset(&attribute->uele_fileheader, 0,
801 	    sizeof(struct ulfs_extattr_fileheader));
802 
803 	attribute->uele_backing_vnode = backing_vnode;
804 
805 	auio.uio_iov = &aiov;
806 	auio.uio_iovcnt = 1;
807 	aiov.iov_base = (void *) &attribute->uele_fileheader;
808 	aiov.iov_len = sizeof(struct ulfs_extattr_fileheader);
809 	auio.uio_resid = sizeof(struct ulfs_extattr_fileheader);
810 	auio.uio_offset = (off_t) 0;
811 	auio.uio_rw = UIO_READ;
812 	UIO_SETUP_SYSSPACE(&auio);
813 
814 	vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
815 	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
816 	    ump->um_extattr.uepm_ucred);
817 
818 	if (error)
819 		goto unlock_free_exit;
820 
821 	if (auio.uio_resid != 0) {
822 		printf("ulfs_extattr_enable: malformed attribute header\n");
823 		error = EINVAL;
824 		goto unlock_free_exit;
825 	}
826 
827 	/*
828 	 * Try to determine the byte order of the attribute file.
829 	 */
830 	if (attribute->uele_fileheader.uef_magic != ULFS_EXTATTR_MAGIC) {
831 		attribute->uele_flags |= UELE_F_NEEDSWAP;
832 		attribute->uele_fileheader.uef_magic =
833 		    ulfs_rw32(attribute->uele_fileheader.uef_magic,
834 			     UELE_NEEDSWAP(attribute));
835 		if (attribute->uele_fileheader.uef_magic != ULFS_EXTATTR_MAGIC) {
836 			printf("ulfs_extattr_enable: invalid attribute header "
837 			       "magic\n");
838 			error = EINVAL;
839 			goto unlock_free_exit;
840 		}
841 	}
842 	attribute->uele_fileheader.uef_version =
843 	    ulfs_rw32(attribute->uele_fileheader.uef_version,
844 		     UELE_NEEDSWAP(attribute));
845 	attribute->uele_fileheader.uef_size =
846 	    ulfs_rw32(attribute->uele_fileheader.uef_size,
847 		     UELE_NEEDSWAP(attribute));
848 
849 	if (attribute->uele_fileheader.uef_version != ULFS_EXTATTR_VERSION) {
850 		printf("ulfs_extattr_enable: incorrect attribute header "
851 		    "version\n");
852 		error = EINVAL;
853 		goto unlock_free_exit;
854 	}
855 
856 	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
857 	    uele_entries);
858 
859 	VOP_UNLOCK(backing_vnode);
860 	return (0);
861 
862  unlock_free_exit:
863 	VOP_UNLOCK(backing_vnode);
864 
865  free_exit:
866 	kmem_free(attribute, sizeof(*attribute));
867 	return (error);
868 }
869 
870 /*
871  * Disable extended attribute support on an FS.
872  */
873 static int
ulfs_extattr_disable(struct ulfsmount * ump,int attrnamespace,const char * attrname,struct lwp * l)874 ulfs_extattr_disable(struct ulfsmount *ump, int attrnamespace,
875     const char *attrname, struct lwp *l)
876 {
877 	struct ulfs_extattr_list_entry *uele;
878 	int error = 0;
879 
880 	if (!ulfs_extattr_valid_attrname(attrnamespace, attrname))
881 		return (EINVAL);
882 
883 	uele = ulfs_extattr_find_attr(ump, attrnamespace, attrname);
884 	if (!uele)
885 		return (ENODATA);
886 
887 	LIST_REMOVE(uele, uele_entries);
888 
889 	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
890 	    l->l_cred);
891 
892 	kmem_free(uele, sizeof(*uele));
893 
894 	return (error);
895 }
896 
897 /*
898  * VFS call to manage extended attributes in ULFS.  If filename_vp is
899  * non-NULL, it must be passed in locked, and regardless of errors in
900  * processing, will be unlocked.
901  */
902 int
ulfs_extattrctl(struct mount * mp,int cmd,struct vnode * filename_vp,int attrnamespace,const char * attrname)903 ulfs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
904     int attrnamespace, const char *attrname)
905 {
906 	struct lwp *l = curlwp;
907 	struct ulfsmount *ump = VFSTOULFS(mp);
908 	int error;
909 
910 	/*
911 	 * Only privileged processes can configure extended attributes.
912 	 */
913 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_EXTATTR,
914 	    0, mp, NULL, NULL);
915 	if (error) {
916 		if (filename_vp != NULL)
917 			VOP_UNLOCK(filename_vp);
918 		return (error);
919 	}
920 
921 	switch(cmd) {
922 	case ULFS_EXTATTR_CMD_START:
923 		if (filename_vp != NULL) {
924 			VOP_UNLOCK(filename_vp);
925 			return (EINVAL);
926 		}
927 		if (attrname != NULL)
928 			return (EINVAL);
929 
930 		error = ulfs_extattr_autostart(mp, l);
931 		return (error);
932 
933 	case ULFS_EXTATTR_CMD_STOP:
934 		if (filename_vp != NULL) {
935 			VOP_UNLOCK(filename_vp);
936 			return (EINVAL);
937 		}
938 		if (attrname != NULL)
939 			return (EINVAL);
940 
941 		ulfs_extattr_stop(mp, l);
942 		return (0);
943 
944 	case ULFS_EXTATTR_CMD_ENABLE:
945 		if (filename_vp == NULL)
946 			return (EINVAL);
947 		if (attrname == NULL) {
948 			VOP_UNLOCK(filename_vp);
949 			return (EINVAL);
950 		}
951 
952 		/*
953 		 * ulfs_extattr_enable_with_open() will always unlock the
954 		 * vnode, regardless of failure.
955 		 */
956 		ulfs_extattr_uepm_lock(ump);
957 		error = ulfs_extattr_enable_with_open(ump, filename_vp,
958 		    attrnamespace, attrname, l);
959 		ulfs_extattr_uepm_unlock(ump);
960 		return (error);
961 
962 	case ULFS_EXTATTR_CMD_DISABLE:
963 		if (filename_vp != NULL) {
964 			VOP_UNLOCK(filename_vp);
965 			return (EINVAL);
966 		}
967 		if (attrname == NULL)
968 			return (EINVAL);
969 
970 		ulfs_extattr_uepm_lock(ump);
971 		error = ulfs_extattr_disable(ump, attrnamespace, attrname, l);
972 		ulfs_extattr_uepm_unlock(ump);
973 		return (error);
974 
975 	default:
976 		return (EINVAL);
977 	}
978 }
979 
980 /*
981  * Read extended attribute header for a given vnode and attribute.
982  * Backing vnode should be locked and unlocked by caller.
983  */
984 static int
ulfs_extattr_get_header(struct vnode * vp,struct ulfs_extattr_list_entry * uele,struct ulfs_extattr_header * ueh,off_t * bap)985 ulfs_extattr_get_header(struct vnode *vp, struct ulfs_extattr_list_entry *uele,
986     struct ulfs_extattr_header *ueh, off_t *bap)
987 {
988 	struct mount *mp = vp->v_mount;
989 	struct ulfsmount *ump = VFSTOULFS(mp);
990 	struct inode *ip = VTOI(vp);
991 	off_t base_offset;
992 	struct iovec aiov;
993 	struct uio aio;
994 	int error;
995 
996 	/*
997 	 * Find base offset of header in file based on file header size, and
998 	 * data header size + maximum data size, indexed by inode number.
999 	 */
1000 	base_offset = sizeof(struct ulfs_extattr_fileheader) +
1001 	    ip->i_number * (sizeof(struct ulfs_extattr_header) +
1002 	    uele->uele_fileheader.uef_size);
1003 
1004 	/*
1005 	 * Read in the data header to see if the data is defined, and if so
1006 	 * how much.
1007 	 */
1008 	memset(ueh, 0, sizeof(struct ulfs_extattr_header));
1009 	aiov.iov_base = ueh;
1010 	aiov.iov_len = sizeof(struct ulfs_extattr_header);
1011 	aio.uio_iov = &aiov;
1012 	aio.uio_iovcnt = 1;
1013 	aio.uio_rw = UIO_READ;
1014 	aio.uio_offset = base_offset;
1015 	aio.uio_resid = sizeof(struct ulfs_extattr_header);
1016 	UIO_SETUP_SYSSPACE(&aio);
1017 
1018 	error = VOP_READ(uele->uele_backing_vnode, &aio,
1019 	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1020 	if (error)
1021 		return error;
1022 
1023 	/*
1024 	 * Attribute headers are kept in file system byte order.
1025 	 * XXX What about the blob of data?
1026 	 */
1027 	ueh->ueh_flags = ulfs_rw32(ueh->ueh_flags, UELE_NEEDSWAP(uele));
1028 	ueh->ueh_len   = ulfs_rw32(ueh->ueh_len, UELE_NEEDSWAP(uele));
1029 	ueh->ueh_i_gen = ulfs_rw32(ueh->ueh_i_gen, UELE_NEEDSWAP(uele));
1030 
1031 	/* Defined? */
1032 	if ((ueh->ueh_flags & ULFS_EXTATTR_ATTR_FLAG_INUSE) == 0)
1033 		return ENODATA;
1034 
1035 	/* Valid for the current inode generation? */
1036 	if (ueh->ueh_i_gen != ip->i_gen) {
1037 		/*
1038 		 * The inode itself has a different generation number
1039 		 * than the uele data.  For now, the best solution
1040 		 * is to coerce this to undefined, and let it get cleaned
1041 		 * up by the next write or extattrctl clean.
1042 		 */
1043 		printf("%s (%s): inode gen inconsistency (%u, %jd)\n",
1044 		       __func__,  mp->mnt_stat.f_mntonname, ueh->ueh_i_gen,
1045 		       (intmax_t)ip->i_gen);
1046 		return ENODATA;
1047 	}
1048 
1049 	/* Local size consistency check. */
1050 	if (ueh->ueh_len > uele->uele_fileheader.uef_size)
1051 		return ENXIO;
1052 
1053 	/* Return base offset */
1054 	if (bap != NULL)
1055 		*bap = base_offset;
1056 
1057 	return 0;
1058 }
1059 
1060 /*
1061  * Vnode operation to retrieve a named extended attribute.
1062  */
1063 int
ulfs_getextattr(struct vop_getextattr_args * ap)1064 ulfs_getextattr(struct vop_getextattr_args *ap)
1065 /*
1066 vop_getextattr {
1067 	IN struct vnode *a_vp;
1068 	IN int a_attrnamespace;
1069 	IN const char *a_name;
1070 	INOUT struct uio *a_uio;
1071 	OUT size_t *a_size;
1072 	IN kauth_cred_t a_cred;
1073 };
1074 */
1075 {
1076 	struct mount *mp = ap->a_vp->v_mount;
1077 	struct ulfsmount *ump = VFSTOULFS(mp);
1078 	int error;
1079 
1080 	ulfs_extattr_uepm_lock(ump);
1081 
1082 	error = ulfs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1083 	    ap->a_uio, ap->a_size, ap->a_cred, curlwp);
1084 
1085 	ulfs_extattr_uepm_unlock(ump);
1086 
1087 	return (error);
1088 }
1089 
1090 /*
1091  * Real work associated with retrieving a named attribute--assumes that
1092  * the attribute lock has already been grabbed.
1093  */
1094 static int
ulfs_extattr_get(struct vnode * vp,int attrnamespace,const char * name,struct uio * uio,size_t * size,kauth_cred_t cred,struct lwp * l)1095 ulfs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
1096     struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l)
1097 {
1098 	struct ulfs_extattr_list_entry *attribute;
1099 	struct ulfs_extattr_header ueh;
1100 	struct mount *mp = vp->v_mount;
1101 	struct ulfsmount *ump = VFSTOULFS(mp);
1102 	off_t base_offset;
1103 	size_t len, old_len;
1104 	int error = 0;
1105 
1106 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1107 		return (EOPNOTSUPP);
1108 
1109 	if (strlen(name) == 0)
1110 		return (EINVAL);
1111 
1112 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1113 	    VREAD);
1114 	if (error)
1115 		return (error);
1116 
1117 	attribute = ulfs_extattr_find_attr(ump, attrnamespace, name);
1118 	if (!attribute)
1119 		return (ENODATA);
1120 
1121 	/*
1122 	 * Allow only offsets of zero to encourage the read/replace
1123 	 * extended attribute semantic.  Otherwise we can't guarantee
1124 	 * atomicity, as we don't provide locks for extended attributes.
1125 	 */
1126 	if (uio != NULL && uio->uio_offset != 0)
1127 		return (ENXIO);
1128 
1129 	/*
1130 	 * Don't need to get a lock on the backing file if the getattr is
1131 	 * being applied to the backing file, as the lock is already held.
1132 	 */
1133 	if (attribute->uele_backing_vnode != vp)
1134 		vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
1135 
1136 	error = ulfs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1137 	if (error)
1138 		goto vopunlock_exit;
1139 
1140 	/* Return full data size if caller requested it. */
1141 	if (size != NULL)
1142 		*size = ueh.ueh_len;
1143 
1144 	/* Return data if the caller requested it. */
1145 	if (uio != NULL) {
1146 		/* Allow for offset into the attribute data. */
1147 		uio->uio_offset = base_offset + sizeof(struct
1148 		    ulfs_extattr_header);
1149 
1150 		/*
1151 		 * Figure out maximum to transfer -- use buffer size and
1152 		 * local data limit.
1153 		 */
1154 		len = MIN(uio->uio_resid, ueh.ueh_len);
1155 		old_len = uio->uio_resid;
1156 		uio->uio_resid = len;
1157 
1158 		error = VOP_READ(attribute->uele_backing_vnode, uio,
1159 		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1160 		if (error)
1161 			goto vopunlock_exit;
1162 
1163 		uio->uio_resid = old_len - (len - uio->uio_resid);
1164 	}
1165 
1166  vopunlock_exit:
1167 
1168 	if (uio != NULL)
1169 		uio->uio_offset = 0;
1170 
1171 	if (attribute->uele_backing_vnode != vp)
1172 		VOP_UNLOCK(attribute->uele_backing_vnode);
1173 
1174 	return (error);
1175 }
1176 
1177 /*
1178  * Vnode operation to list extended attribute for a vnode
1179  */
1180 int
ulfs_listextattr(struct vop_listextattr_args * ap)1181 ulfs_listextattr(struct vop_listextattr_args *ap)
1182 /*
1183 vop_listextattr {
1184 	IN struct vnode *a_vp;
1185 	IN int a_attrnamespace;
1186 	INOUT struct uio *a_uio;
1187 	OUT size_t *a_size;
1188 	IN int flag;
1189 	IN kauth_cred_t a_cred;
1190 	struct proc *a_p;
1191 };
1192 */
1193 {
1194 	struct mount *mp = ap->a_vp->v_mount;
1195 	struct ulfsmount *ump = VFSTOULFS(mp);
1196 	int error;
1197 
1198 	ulfs_extattr_uepm_lock(ump);
1199 
1200 	error = ulfs_extattr_list(ap->a_vp, ap->a_attrnamespace,
1201 	    ap->a_uio, ap->a_size, ap->a_flag, ap->a_cred, curlwp);
1202 
1203 	ulfs_extattr_uepm_unlock(ump);
1204 
1205 	return (error);
1206 }
1207 
1208 /*
1209  * Real work associated with retrieving list of attributes--assumes that
1210  * the attribute lock has already been grabbed.
1211  */
1212 static int
ulfs_extattr_list(struct vnode * vp,int attrnamespace,struct uio * uio,size_t * size,int flag,kauth_cred_t cred,struct lwp * l)1213 ulfs_extattr_list(struct vnode *vp, int attrnamespace,
1214     struct uio *uio, size_t *size, int flag,
1215     kauth_cred_t cred, struct lwp *l)
1216 {
1217 	struct ulfs_extattr_list_entry *uele;
1218 	struct ulfs_extattr_header ueh;
1219 	struct mount *mp = vp->v_mount;
1220 	struct ulfsmount *ump = VFSTOULFS(mp);
1221 	size_t listsize = 0;
1222 	int error = 0;
1223 
1224 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1225 		return (EOPNOTSUPP);
1226 
1227 	/*
1228 	 * XXX: We can move this inside the loop and iterate on individual
1229 	 *	attributes.
1230 	 */
1231 	error = internal_extattr_check_cred(vp, attrnamespace, "", cred,
1232 	    VREAD);
1233 	if (error)
1234 		return (error);
1235 
1236 	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) {
1237 		unsigned char attrnamelen;
1238 
1239 		if (uele->uele_attrnamespace != attrnamespace)
1240 			continue;
1241 
1242 		error = ulfs_extattr_get_header(vp, uele, &ueh, NULL);
1243 		if (error == ENODATA)
1244 			continue;
1245 		if (error != 0)
1246 			return error;
1247 
1248 		/*
1249 		 * Don't need to get a lock on the backing file if
1250 		 * the listattr is being applied to the backing file,
1251 		 * as the lock is already held.
1252 		 */
1253 		if (uele->uele_backing_vnode != vp)
1254 			vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
1255 
1256 		/*
1257 		 * +1 for trailing NUL (listxattr flavor)
1258 		 *  or leading name length (extattr_list_file flavor)
1259 	 	 */
1260 		attrnamelen = strlen(uele->uele_attrname);
1261 		listsize += attrnamelen + 1;
1262 
1263 		/* Return data if the caller requested it. */
1264 		if (uio != NULL) {
1265 			/*
1266 			 * We support two flavors. Either NUL-terminated
1267 			 * strings (a la listxattr), or non NUL-terminated,
1268 			 * one byte length prefixed strings (for
1269 			 * extattr_list_file). EXTATTR_LIST_LENPREFIX switches
1270 		 	 * that second behavior.
1271 			 */
1272 			if (flag & EXTATTR_LIST_LENPREFIX) {
1273 				uint8_t len = (uint8_t)attrnamelen;
1274 
1275 				/* Copy leading name length */
1276 				error = uiomove(&len, sizeof(len), uio);
1277 				if (error != 0)
1278 					break;
1279 			} else {
1280 				/* Include trailing NULL */
1281 				attrnamelen++;
1282 			}
1283 
1284 			error = uiomove(uele->uele_attrname,
1285 					(size_t)attrnamelen, uio);
1286 			if (error != 0)
1287 				break;
1288 		}
1289 
1290 		if (uele->uele_backing_vnode != vp)
1291 			VOP_UNLOCK(uele->uele_backing_vnode);
1292 
1293 		if (error != 0)
1294 			return error;
1295 	}
1296 
1297 	if (uio != NULL)
1298 		uio->uio_offset = 0;
1299 
1300 	/* Return full data size if caller requested it. */
1301 	if (size != NULL)
1302 		*size = listsize;
1303 
1304 	return 0;
1305 }
1306 
1307 /*
1308  * Vnode operation to remove a named attribute.
1309  */
1310 int
ulfs_deleteextattr(struct vop_deleteextattr_args * ap)1311 ulfs_deleteextattr(struct vop_deleteextattr_args *ap)
1312 /*
1313 vop_deleteextattr {
1314 	IN struct vnode *a_vp;
1315 	IN int a_attrnamespace;
1316 	IN const char *a_name;
1317 	IN kauth_cred_t a_cred;
1318 };
1319 */
1320 {
1321 	struct mount *mp = ap->a_vp->v_mount;
1322 	struct ulfsmount *ump = VFSTOULFS(mp);
1323 	int error;
1324 
1325 	ulfs_extattr_uepm_lock(ump);
1326 
1327 	error = ulfs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1328 	    ap->a_cred, curlwp);
1329 
1330 	ulfs_extattr_uepm_unlock(ump);
1331 
1332 	return (error);
1333 }
1334 
1335 /*
1336  * Vnode operation to set a named attribute.
1337  */
1338 int
ulfs_setextattr(struct vop_setextattr_args * ap)1339 ulfs_setextattr(struct vop_setextattr_args *ap)
1340 /*
1341 vop_setextattr {
1342 	IN struct vnode *a_vp;
1343 	IN int a_attrnamespace;
1344 	IN const char *a_name;
1345 	INOUT struct uio *a_uio;
1346 	IN kauth_cred_t a_cred;
1347 };
1348 */
1349 {
1350 	struct mount *mp = ap->a_vp->v_mount;
1351 	struct ulfsmount *ump = VFSTOULFS(mp);
1352 	int error;
1353 
1354 	ulfs_extattr_uepm_lock(ump);
1355 
1356 	/*
1357 	 * XXX: No longer a supported way to delete extended attributes.
1358 	 */
1359 	if (ap->a_uio == NULL) {
1360 		ulfs_extattr_uepm_unlock(ump);
1361 		return (EINVAL);
1362 	}
1363 
1364 	error = ulfs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1365 	    ap->a_uio, ap->a_cred, curlwp);
1366 
1367 	ulfs_extattr_uepm_unlock(ump);
1368 
1369 	return (error);
1370 }
1371 
1372 /*
1373  * Real work associated with setting a vnode's extended attributes;
1374  * assumes that the attribute lock has already been grabbed.
1375  */
1376 static int
ulfs_extattr_set(struct vnode * vp,int attrnamespace,const char * name,struct uio * uio,kauth_cred_t cred,struct lwp * l)1377 ulfs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1378     struct uio *uio, kauth_cred_t cred, struct lwp *l)
1379 {
1380 	struct ulfs_extattr_list_entry *attribute;
1381 	struct ulfs_extattr_header ueh;
1382 	struct iovec local_aiov;
1383 	struct uio local_aio;
1384 	struct mount *mp = vp->v_mount;
1385 	struct ulfsmount *ump = VFSTOULFS(mp);
1386 	struct inode *ip = VTOI(vp);
1387 	off_t base_offset;
1388 	int error = 0, ioflag;
1389 
1390 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1391 		return (EROFS);
1392 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1393 		return (EOPNOTSUPP);
1394 	if (!ulfs_extattr_valid_attrname(attrnamespace, name))
1395 		return (EINVAL);
1396 
1397 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1398 	    VWRITE);
1399 	if (error)
1400 		return (error);
1401 
1402 	attribute = ulfs_extattr_find_attr(ump, attrnamespace, name);
1403 	if (!attribute) {
1404 		attribute =  ulfs_extattr_autocreate_attr(vp, attrnamespace,
1405 							 name, l);
1406 		if  (!attribute)
1407 			return (ENODATA);
1408 	}
1409 
1410 	/*
1411 	 * Early rejection of invalid offsets/length.
1412 	 * Reject: any offset but 0 (replace)
1413 	 *	 Any size greater than attribute size limit
1414  	 */
1415 	if (uio->uio_offset != 0 ||
1416 	    uio->uio_resid > attribute->uele_fileheader.uef_size)
1417 		return (ENXIO);
1418 
1419 	/*
1420 	 * Find base offset of header in file based on file header size, and
1421 	 * data header size + maximum data size, indexed by inode number.
1422 	 */
1423 	base_offset = sizeof(struct ulfs_extattr_fileheader) +
1424 	    ip->i_number * (sizeof(struct ulfs_extattr_header) +
1425 	    attribute->uele_fileheader.uef_size);
1426 
1427 	/*
1428 	 * Write out a data header for the data.
1429 	 */
1430 	ueh.ueh_len = ulfs_rw32((uint32_t) uio->uio_resid,
1431 	    UELE_NEEDSWAP(attribute));
1432 	ueh.ueh_flags = ulfs_rw32(ULFS_EXTATTR_ATTR_FLAG_INUSE,
1433 				 UELE_NEEDSWAP(attribute));
1434 	ueh.ueh_i_gen = ulfs_rw32(ip->i_gen, UELE_NEEDSWAP(attribute));
1435 	local_aiov.iov_base = &ueh;
1436 	local_aiov.iov_len = sizeof(struct ulfs_extattr_header);
1437 	local_aio.uio_iov = &local_aiov;
1438 	local_aio.uio_iovcnt = 1;
1439 	local_aio.uio_rw = UIO_WRITE;
1440 	local_aio.uio_offset = base_offset;
1441 	local_aio.uio_resid = sizeof(struct ulfs_extattr_header);
1442 	UIO_SETUP_SYSSPACE(&local_aio);
1443 
1444 	/*
1445 	 * Don't need to get a lock on the backing file if the setattr is
1446 	 * being applied to the backing file, as the lock is already held.
1447 	 */
1448 	if (attribute->uele_backing_vnode != vp)
1449 		vn_lock(attribute->uele_backing_vnode,
1450 		    LK_EXCLUSIVE | LK_RETRY);
1451 
1452 	ioflag = IO_NODELOCKED;
1453 	if (ulfs_extattr_sync)
1454 		ioflag |= IO_SYNC;
1455 	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1456 	    ump->um_extattr.uepm_ucred);
1457 	if (error)
1458 		goto vopunlock_exit;
1459 
1460 	if (local_aio.uio_resid != 0) {
1461 		error = ENXIO;
1462 		goto vopunlock_exit;
1463 	}
1464 
1465 	/*
1466 	 * Write out user data.
1467 	 * XXX NOT ATOMIC WITH RESPECT TO THE HEADER.
1468 	 */
1469 	uio->uio_offset = base_offset + sizeof(struct ulfs_extattr_header);
1470 
1471 	ioflag = IO_NODELOCKED;
1472 	if (ulfs_extattr_sync)
1473 		ioflag |= IO_SYNC;
1474 	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1475 	    ump->um_extattr.uepm_ucred);
1476 
1477  vopunlock_exit:
1478 	uio->uio_offset = 0;
1479 
1480 	if (attribute->uele_backing_vnode != vp)
1481 		VOP_UNLOCK(attribute->uele_backing_vnode);
1482 
1483 	return (error);
1484 }
1485 
1486 /*
1487  * Real work associated with removing an extended attribute from a vnode.
1488  * Assumes the attribute lock has already been grabbed.
1489  */
1490 static int
ulfs_extattr_rm(struct vnode * vp,int attrnamespace,const char * name,kauth_cred_t cred,struct lwp * l)1491 ulfs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1492     kauth_cred_t cred, struct lwp *l)
1493 {
1494 	struct ulfs_extattr_list_entry *attribute;
1495 	struct ulfs_extattr_header ueh;
1496 	struct mount *mp = vp->v_mount;
1497 	struct ulfsmount *ump = VFSTOULFS(mp);
1498 	struct iovec local_aiov;
1499 	struct uio local_aio;
1500 	off_t base_offset;
1501 	int error = 0, ioflag;
1502 
1503 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1504 		return (EROFS);
1505 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED))
1506 		return (EOPNOTSUPP);
1507 	if (!ulfs_extattr_valid_attrname(attrnamespace, name))
1508 		return (EINVAL);
1509 
1510 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1511 	    VWRITE);
1512 	if (error)
1513 		return (error);
1514 
1515 	attribute = ulfs_extattr_find_attr(ump, attrnamespace, name);
1516 	if (!attribute)
1517 		return (ENODATA);
1518 
1519 	/*
1520 	 * Don't need to get a lock on the backing file if the getattr is
1521 	 * being applied to the backing file, as the lock is already held.
1522 	 */
1523 	if (attribute->uele_backing_vnode != vp)
1524 		vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
1525 
1526 	error = ulfs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1527 	if (error)
1528 		goto vopunlock_exit;
1529 
1530 	/* Flag it as not in use. */
1531 	ueh.ueh_flags = 0;		/* No need to byte swap 0 */
1532 	ueh.ueh_len = 0;		/* ...ditto... */
1533 
1534 	local_aiov.iov_base = &ueh;
1535 	local_aiov.iov_len = sizeof(struct ulfs_extattr_header);
1536 	local_aio.uio_iov = &local_aiov;
1537 	local_aio.uio_iovcnt = 1;
1538 	local_aio.uio_rw = UIO_WRITE;
1539 	local_aio.uio_offset = base_offset;
1540 	local_aio.uio_resid = sizeof(struct ulfs_extattr_header);
1541 	UIO_SETUP_SYSSPACE(&local_aio);
1542 
1543 	ioflag = IO_NODELOCKED;
1544 	if (ulfs_extattr_sync)
1545 		ioflag |= IO_SYNC;
1546 	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1547 	    ump->um_extattr.uepm_ucred);
1548 	if (error)
1549 		goto vopunlock_exit;
1550 
1551 	if (local_aio.uio_resid != 0)
1552 		error = ENXIO;
1553 
1554  vopunlock_exit:
1555 	VOP_UNLOCK(attribute->uele_backing_vnode);
1556 
1557 	return (error);
1558 }
1559 
1560 /*
1561  * Called by ULFS when an inode is no longer active and should have its
1562  * attributes stripped.
1563  */
1564 void
ulfs_extattr_vnode_inactive(struct vnode * vp,struct lwp * l)1565 ulfs_extattr_vnode_inactive(struct vnode *vp, struct lwp *l)
1566 {
1567 	struct ulfs_extattr_list_entry *uele;
1568 	struct mount *mp = vp->v_mount;
1569 	struct ulfsmount *ump = VFSTOULFS(mp);
1570 
1571 	/*
1572 	 * In that case, we cannot lock. We should not have any active vnodes
1573 	 * on the fs if this is not yet initialized but is going to be, so
1574 	 * this can go unlocked.
1575 	 */
1576 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_INITIALIZED))
1577 		return;
1578 
1579 	ulfs_extattr_uepm_lock(ump);
1580 
1581 	if (!(ump->um_extattr.uepm_flags & ULFS_EXTATTR_UEPM_STARTED)) {
1582 		ulfs_extattr_uepm_unlock(ump);
1583 		return;
1584 	}
1585 
1586 	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1587 		ulfs_extattr_rm(vp, uele->uele_attrnamespace,
1588 		    uele->uele_attrname, lwp0.l_cred, l);
1589 
1590 	ulfs_extattr_uepm_unlock(ump);
1591 }
1592 
1593 void
ulfs_extattr_init(void)1594 ulfs_extattr_init(void)
1595 {
1596 
1597 }
1598 
1599 void
ulfs_extattr_done(void)1600 ulfs_extattr_done(void)
1601 {
1602 
1603 }
1604