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