xref: /minix/sys/ufs/ufs/ufs_extattr.c (revision 0a6a1f1d)
1 /*	$NetBSD: ufs_extattr.c,v 1.46 2014/11/19 16:26:47 manu 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.46 2014/11/19 16:26:47 manu 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  * 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
ufs_extattr_uepm_lock(struct ufsmount * ump)168 ufs_extattr_uepm_lock(struct ufsmount *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
ufs_extattr_uepm_unlock(struct ufsmount * ump)180 ufs_extattr_uepm_unlock(struct ufsmount *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
ufs_extattr_valid_attrname(int attrnamespace,const char * attrname)200 ufs_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 int
ufs_extattr_autocreate_attr(struct vnode * vp,int attrnamespace,const char * attrname,struct lwp * l,struct ufs_extattr_list_entry ** uelep)214 ufs_extattr_autocreate_attr(struct vnode *vp, int attrnamespace,
215     const char *attrname, struct lwp *l, struct ufs_extattr_list_entry **uelep)
216 {
217 	struct mount *mp = vp->v_mount;
218 	struct ufsmount *ump = VFSTOUFS(mp);
219 	struct vnode *backing_vp;
220 	struct nameidata nd;
221 	struct pathbuf *pb;
222 	char *path;
223 	struct ufs_extattr_fileheader uef;
224 	struct ufs_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 			       UFS_EXTATTR_FSROOTSUBDIR,
237 			       UFS_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 			       UFS_EXTATTR_FSROOTSUBDIR,
244 			       UFS_EXTATTR_SUBDIR_USER,
245 			       attrname);
246 		break;
247 	default:
248 		PNBUF_PUT(path);
249 		*uelep = NULL;
250 		return EINVAL;
251 		break;
252 	}
253 
254 	/*
255 	 * Release extended attribute mount lock, otherwise
256 	 * we can deadlock with another thread that would lock
257 	 * vp after we unlock it below, and call
258 	 * ufs_extattr_uepm_lock(ump), for instance
259 	 * in ufs_getextattr().
260 	 */
261 	ufs_extattr_uepm_unlock(ump);
262 
263 	/*
264 	 * XXX unlock/lock should only be done when setting extattr
265 	 * on backing store or one of its parent directory
266 	 * including root, but we always do it for now.
267 	 */
268 	KASSERT(VOP_ISLOCKED(vp) == LK_EXCLUSIVE);
269 	VOP_UNLOCK(vp);
270 
271 	pb = pathbuf_create(path);
272 	NDINIT(&nd, CREATE, LOCKPARENT, pb);
273 
274 	/*
275 	 * Since we do not hold ufs_extattr_uepm_lock anymore,
276 	 * another thread may race with us for backend creation,
277 	 * but only one can succeed here thanks to O_EXCL
278 	 */
279 	error = vn_open(&nd, O_CREAT|O_EXCL|O_RDWR, 0600);
280 
281 	/*
282 	 * Reacquire the lock on the vnode
283 	 */
284 	KASSERT(VOP_ISLOCKED(vp) == 0);
285 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
286 
287 	ufs_extattr_uepm_lock(ump);
288 
289 	if (error != 0) {
290 		pathbuf_destroy(pb);
291 		PNBUF_PUT(path);
292 		*uelep = NULL;
293 		return error;
294 	}
295 
296 	KASSERT(nd.ni_vp != NULL);
297 	KASSERT(VOP_ISLOCKED(nd.ni_vp) == LK_EXCLUSIVE);
298 	KASSERT(VOP_ISLOCKED(nd.ni_dvp) == 0);
299 
300 	/*
301  	 * backing_vp is the backing store.
302 	 */
303 	backing_vp = nd.ni_vp;
304 	pathbuf_destroy(pb);
305 	PNBUF_PUT(path);
306 
307 	uef.uef_magic = UFS_EXTATTR_MAGIC;
308 	uef.uef_version = UFS_EXTATTR_VERSION;
309 	uef.uef_size = ufs_extattr_autocreate;
310 
311 	error = vn_rdwr(UIO_WRITE, backing_vp, &uef, sizeof(uef), 0,
312 		        UIO_SYSSPACE, IO_NODELOCKED|IO_APPEND,
313 			l->l_cred, NULL, l);
314 
315 	VOP_UNLOCK(backing_vp);
316 
317 	if (error != 0) {
318 		printf("%s: write uef header failed for %s, error = %d\n",
319 		       __func__, attrname, error);
320 		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
321 		*uelep = NULL;
322 		return error;
323 	}
324 
325 	/*
326 	 * Now enable attribute.
327 	 */
328 	error = ufs_extattr_enable(ump,attrnamespace, attrname, backing_vp, l);
329 	KASSERT(VOP_ISLOCKED(backing_vp) == 0);
330 
331 	if (error != 0) {
332 		printf("%s: enable %s failed, error %d\n",
333 		       __func__, attrname, error);
334 		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
335 		*uelep = NULL;
336 		return error;
337 	}
338 
339 	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
340 	if (uele == NULL) {
341 		printf("%s: atttribute %s created but not found!\n",
342 		       __func__, attrname);
343 		vn_close(backing_vp, FREAD|FWRITE, l->l_cred);
344 		*uelep = NULL;
345 		return ESRCH; /* really internal error */
346 	}
347 
348 	printf("%s: EA backing store autocreated for %s\n",
349 	       mp->mnt_stat.f_mntonname, attrname);
350 
351 	*uelep = uele;
352 	return 0;
353 }
354 
355 /*
356  * Locate an attribute given a name and mountpoint.
357  * Must be holding uepm lock for the mount point.
358  */
359 static struct ufs_extattr_list_entry *
ufs_extattr_find_attr(struct ufsmount * ump,int attrnamespace,const char * attrname)360 ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
361     const char *attrname)
362 {
363 	struct ufs_extattr_list_entry *search_attribute;
364 
365 	for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
366 	    search_attribute != NULL;
367 	    search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
368 		if (!(strncmp(attrname, search_attribute->uele_attrname,
369 		    UFS_EXTATTR_MAXEXTATTRNAME)) &&
370 		    (attrnamespace == search_attribute->uele_attrnamespace)) {
371 			return (search_attribute);
372 		}
373 	}
374 
375 	return (0);
376 }
377 
378 /*
379  * Initialize per-FS structures supporting extended attributes.  Do not
380  * start extended attributes yet.
381  */
382 void
ufs_extattr_uepm_init(struct ufs_extattr_per_mount * uepm)383 ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
384 {
385 
386 	uepm->uepm_flags = 0;
387 	uepm->uepm_lockcnt = 0;
388 
389 	LIST_INIT(&uepm->uepm_list);
390 	mutex_init(&uepm->uepm_lock, MUTEX_DEFAULT, IPL_NONE);
391 	uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
392 }
393 
394 /*
395  * Destroy per-FS structures supporting extended attributes.  Assumes
396  * that EAs have already been stopped, and will panic if not.
397  */
398 void
ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount * uepm)399 ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
400 {
401 
402 	if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
403 		panic("ufs_extattr_uepm_destroy: not initialized");
404 
405 	if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
406 		panic("ufs_extattr_uepm_destroy: called while still started");
407 
408 	/*
409 	 * It's not clear that either order for the next three lines is
410 	 * ideal, and it should never be a problem if this is only called
411 	 * during unmount, and with vfs_busy().
412 	 */
413 	uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
414 	uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
415 	mutex_destroy(&uepm->uepm_lock);
416 }
417 
418 /*
419  * Start extended attribute support on an FS.
420  */
421 int
ufs_extattr_start(struct mount * mp,struct lwp * l)422 ufs_extattr_start(struct mount *mp, struct lwp *l)
423 {
424 	struct ufsmount *ump;
425 	int error = 0;
426 
427 	ump = VFSTOUFS(mp);
428 
429 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
430 		ufs_extattr_uepm_init(&ump->um_extattr);
431 
432 	ufs_extattr_uepm_lock(ump);
433 
434 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
435 		error = EOPNOTSUPP;
436 		goto unlock;
437 	}
438 	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
439 		error = EBUSY;
440 		goto unlock;
441 	}
442 
443 	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
444 
445 	ump->um_extattr.uepm_ucred = l->l_cred;
446 	kauth_cred_hold(ump->um_extattr.uepm_ucred);
447 
448  unlock:
449 	ufs_extattr_uepm_unlock(ump);
450 
451 	return (error);
452 }
453 
454 /*
455  * Helper routine: given a locked parent directory and filename, return
456  * the locked vnode of the inode associated with the name.  Will not
457  * follow symlinks, may return any type of vnode.  Lock on parent will
458  * be released even in the event of a failure.  In the event that the
459  * target is the parent (i.e., "."), there will be two references and
460  * one lock, requiring the caller to possibly special-case.
461  */
462 static int
ufs_extattr_lookup(struct vnode * start_dvp,int lockparent,const char * dirname,struct vnode ** vp,struct lwp * l)463 ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, const char *dirname,
464     struct vnode **vp, struct lwp *l)
465 {
466 	struct vop_lookup_v2_args vargs;
467 	struct componentname cnp;
468 	struct vnode *target_vp;
469 	char *pnbuf;
470 	int error;
471 
472 	KASSERT(VOP_ISLOCKED(start_dvp) == LK_EXCLUSIVE);
473 
474 	pnbuf = PNBUF_GET();
475 
476 	memset(&cnp, 0, sizeof(cnp));
477 	cnp.cn_nameiop = LOOKUP;
478 	cnp.cn_flags = ISLASTCN | lockparent;
479 	cnp.cn_cred = l->l_cred;
480 	cnp.cn_nameptr = pnbuf;
481 	error = copystr(dirname, pnbuf, MAXPATHLEN, &cnp.cn_namelen);
482 	if (error) {
483 		if (lockparent == 0) {
484 			VOP_UNLOCK(start_dvp);
485 		}
486 		PNBUF_PUT(pnbuf);
487 		printf("ufs_extattr_lookup: copystr failed\n");
488 		return (error);
489 	}
490 	cnp.cn_namelen--;	/* trim nul termination */
491 	vargs.a_desc = NULL;
492 	vargs.a_dvp = start_dvp;
493 	vargs.a_vpp = &target_vp;
494 	vargs.a_cnp = &cnp;
495 	error = ufs_lookup(&vargs);
496 	PNBUF_PUT(pnbuf);
497 	if (error) {
498 		if (lockparent == 0) {
499 			VOP_UNLOCK(start_dvp);
500 		}
501 		return (error);
502 	}
503 #if 0
504 	if (target_vp == start_dvp)
505 		panic("ufs_extattr_lookup: target_vp == start_dvp");
506 #endif
507 
508 	if (target_vp != start_dvp) {
509 		error = vn_lock(target_vp, LK_EXCLUSIVE);
510 		if (lockparent == 0)
511 			VOP_UNLOCK(start_dvp);
512 		if (error) {
513 			vrele(target_vp);
514 			return error;
515 		}
516 	}
517 
518 	KASSERT(VOP_ISLOCKED(target_vp) == LK_EXCLUSIVE);
519 	*vp = target_vp;
520 	return (0);
521 }
522 
523 /*
524  * Enable an EA using the passed filesystem, backing vnode, attribute name,
525  * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
526  * to be locked when passed in.  The vnode will be returned unlocked,
527  * regardless of success/failure of the function.  As a result, the caller
528  * will always need to vrele(), but not vput().
529  */
530 static int
ufs_extattr_enable_with_open(struct ufsmount * ump,struct vnode * vp,int attrnamespace,const char * attrname,struct lwp * l)531 ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
532     int attrnamespace, const char *attrname, struct lwp *l)
533 {
534 	int error;
535 
536 	error = VOP_OPEN(vp, FREAD|FWRITE, l->l_cred);
537 	if (error) {
538 		printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
539 		    "with %d\n", error);
540 		VOP_UNLOCK(vp);
541 		return (error);
542 	}
543 
544 	mutex_enter(vp->v_interlock);
545 	vp->v_writecount++;
546 	mutex_exit(vp->v_interlock);
547 
548 	vref(vp);
549 
550 	VOP_UNLOCK(vp);
551 
552 	error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, l);
553 	if (error != 0)
554 		vn_close(vp, FREAD|FWRITE, l->l_cred);
555 	return (error);
556 }
557 
558 /*
559  * Given a locked directory vnode, iterate over the names in the directory
560  * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
561  * attribute files.  Then invoke ufs_extattr_enable_with_open() on each
562  * to attempt to start the attribute.  Leaves the directory locked on
563  * exit.
564  */
565 static int
ufs_extattr_iterate_directory(struct ufsmount * ump,struct vnode * dvp,int attrnamespace,struct lwp * l)566 ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
567     int attrnamespace, struct lwp *l)
568 {
569 	struct vop_readdir_args vargs;
570 	struct statvfs *sbp = &ump->um_mountp->mnt_stat;
571 	struct dirent *dp, *edp;
572 	struct vnode *attr_vp;
573 	struct uio auio;
574 	struct iovec aiov;
575 	char *dirbuf;
576 	int error, eofflag = 0;
577 
578 	if (dvp->v_type != VDIR)
579 		return (ENOTDIR);
580 
581 	dirbuf = kmem_alloc(UFS_DIRBLKSIZ, KM_SLEEP);
582 
583 	auio.uio_iov = &aiov;
584 	auio.uio_iovcnt = 1;
585 	auio.uio_rw = UIO_READ;
586 	auio.uio_offset = 0;
587 	UIO_SETUP_SYSSPACE(&auio);
588 
589 	vargs.a_desc = NULL;
590 	vargs.a_vp = dvp;
591 	vargs.a_uio = &auio;
592 	vargs.a_cred = l->l_cred;
593 	vargs.a_eofflag = &eofflag;
594 	vargs.a_ncookies = NULL;
595 	vargs.a_cookies = NULL;
596 
597 	while (!eofflag) {
598 		auio.uio_resid = UFS_DIRBLKSIZ;
599 		aiov.iov_base = dirbuf;
600 		aiov.iov_len = UFS_DIRBLKSIZ;
601 		error = ufs_readdir(&vargs);
602 		if (error) {
603 			printf("ufs_extattr_iterate_directory: ufs_readdir "
604 			    "%d\n", error);
605 			return (error);
606 		}
607 
608 		/*
609 		 * XXXRW: While in UFS, we always get UFS_DIRBLKSIZ returns from
610 		 * the directory code on success, on other file systems this
611 		 * may not be the case.  For portability, we should check the
612 		 * read length on return from ufs_readdir().
613 		 */
614 		edp = (struct dirent *)&dirbuf[UFS_DIRBLKSIZ];
615 		for (dp = (struct dirent *)dirbuf; dp < edp; ) {
616 			if (dp->d_reclen == 0)
617 				break;
618 			/* Skip "." and ".." */
619 			if (dp->d_name[0] == '.' &&
620 			    (dp->d_name[1] == '\0' ||
621 			     (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
622 				goto next;
623 			error = ufs_extattr_lookup(dvp, LOCKPARENT,
624 			    dp->d_name, &attr_vp, l);
625 			if (error == ENOENT) {
626 				goto next; /* keep silent */
627 			} else if (error) {
628 				printf("ufs_extattr_iterate_directory: lookup "
629 				    "%s %d\n", dp->d_name, error);
630 			} else if (attr_vp == dvp) {
631 				vrele(attr_vp);
632 			} else if (attr_vp->v_type != VREG) {
633 				vput(attr_vp);
634 			} else {
635 				error = ufs_extattr_enable_with_open(ump,
636 				    attr_vp, attrnamespace, dp->d_name, l);
637 				vrele(attr_vp);
638 				if (error) {
639 					printf("ufs_extattr_iterate_directory: "
640 					    "enable %s %d\n", dp->d_name,
641 					    error);
642 				} else if (bootverbose) {
643 					printf("%s: EA %s loaded\n",
644 					       sbp->f_mntonname, dp->d_name);
645 				}
646 			}
647  next:
648 			dp = (struct dirent *) ((char *)dp + dp->d_reclen);
649 			if (dp >= edp)
650 				break;
651 		}
652 	}
653 	kmem_free(dirbuf, UFS_DIRBLKSIZ);
654 
655 	return (0);
656 }
657 
658 /*
659  * Auto-start of extended attributes, to be executed (optionally) at
660  * mount-time.
661  */
662 int
ufs_extattr_autostart(struct mount * mp,struct lwp * l)663 ufs_extattr_autostart(struct mount *mp, struct lwp *l)
664 {
665 	struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
666 	int error;
667 
668 	/*
669 	 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
670 	 * If so, automatically start EA's.
671 	 */
672 	error = VFS_ROOT(mp, &rvp);
673 	if (error) {
674 		printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
675 		    error);
676 		return (error);
677 	}
678 
679 	KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
680 
681 	error = ufs_extattr_lookup(rvp, 0,
682 	    UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, l);
683 	if (error) {
684 		/* rvp ref'd but now unlocked */
685 		KASSERT(VOP_ISLOCKED(rvp) == 0);
686 		vrele(rvp);
687 		return (error);
688 	}
689 	if (rvp == attr_dvp) {
690 		/* Should never happen. */
691 		KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
692 		vrele(attr_dvp);
693 		vput(rvp);
694 		return (EINVAL);
695 	}
696 	KASSERT(VOP_ISLOCKED(rvp) == 0);
697 	vrele(rvp);
698 
699 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
700 
701 	if (attr_dvp->v_type != VDIR) {
702 		printf("ufs_extattr_autostart: %s != VDIR\n",
703 		    UFS_EXTATTR_FSROOTSUBDIR);
704 		goto return_vput_attr_dvp;
705 	}
706 
707 	error = ufs_extattr_start(mp, l);
708 	if (error) {
709 		printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
710 		    error);
711 		goto return_vput_attr_dvp;
712 	}
713 
714 	/*
715 	 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
716 	 * UFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
717 	 * and start with appropriate type.  Failures in either don't
718 	 * result in an over-all failure.  attr_dvp is left locked to
719 	 * be cleaned up on exit.
720 	 */
721 	error = ufs_extattr_lookup(attr_dvp, LOCKPARENT,
722 	    UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, l);
723 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
724 	if (error == 0) {
725 		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
726 		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
727 		    attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, l);
728 		if (error)
729 			printf("ufs_extattr_iterate_directory returned %d\n",
730 			    error);
731 		KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
732 		vput(attr_system_dvp);
733 	}
734 
735 	error = ufs_extattr_lookup(attr_dvp, LOCKPARENT,
736 	    UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, l);
737 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
738 	if (error == 0) {
739 		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
740 		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
741 		    attr_user_dvp, EXTATTR_NAMESPACE_USER, l);
742 		if (error)
743 			printf("ufs_extattr_iterate_directory returned %d\n",
744 			    error);
745 		KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
746 		vput(attr_user_dvp);
747 	}
748 
749 	/* Mask startup failures in sub-directories. */
750 	error = 0;
751 
752  return_vput_attr_dvp:
753 	KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
754 	vput(attr_dvp);
755 
756 	return (error);
757 }
758 
759 /*
760  * Stop extended attribute support on an FS.
761  */
762 void
ufs_extattr_stop(struct mount * mp,struct lwp * l)763 ufs_extattr_stop(struct mount *mp, struct lwp *l)
764 {
765 	struct ufs_extattr_list_entry *uele;
766 	struct ufsmount *ump = VFSTOUFS(mp);
767 
768 	ufs_extattr_uepm_lock(ump);
769 
770 	/*
771 	 * If we haven't been started, no big deal.  Just short-circuit
772 	 * the processing work.
773 	 */
774 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
775 		goto unlock;
776 	}
777 
778 	while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
779 		uele = LIST_FIRST(&ump->um_extattr.uepm_list);
780 		ufs_extattr_disable(ump, uele->uele_attrnamespace,
781 		    uele->uele_attrname, l);
782 	}
783 
784 	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
785 
786 	kauth_cred_free(ump->um_extattr.uepm_ucred);
787 	ump->um_extattr.uepm_ucred = NULL;
788 
789  unlock:
790 	ufs_extattr_uepm_unlock(ump);
791 }
792 
793 /*
794  * Enable a named attribute on the specified filesystem; provide an
795  * unlocked backing vnode to hold the attribute data.
796  */
797 static int
ufs_extattr_enable(struct ufsmount * ump,int attrnamespace,const char * attrname,struct vnode * backing_vnode,struct lwp * l)798 ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
799     const char *attrname, struct vnode *backing_vnode, struct lwp *l)
800 {
801 	struct ufs_extattr_list_entry *attribute;
802 	struct iovec aiov;
803 	struct uio auio;
804 	int error = 0;
805 
806 	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
807 		return (EINVAL);
808 	if (backing_vnode->v_type != VREG)
809 		return (EINVAL);
810 
811 	attribute = kmem_zalloc(sizeof(*attribute), KM_SLEEP);
812 
813 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
814 		error = EOPNOTSUPP;
815 		goto free_exit;
816 	}
817 
818 	if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
819 		error = EEXIST;
820 		goto free_exit;
821 	}
822 
823 	strncpy(attribute->uele_attrname, attrname,
824 	    UFS_EXTATTR_MAXEXTATTRNAME);
825 	attribute->uele_attrnamespace = attrnamespace;
826 	memset(&attribute->uele_fileheader, 0,
827 	    sizeof(struct ufs_extattr_fileheader));
828 
829 	attribute->uele_backing_vnode = backing_vnode;
830 
831 	auio.uio_iov = &aiov;
832 	auio.uio_iovcnt = 1;
833 	aiov.iov_base = (void *) &attribute->uele_fileheader;
834 	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
835 	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
836 	auio.uio_offset = (off_t) 0;
837 	auio.uio_rw = UIO_READ;
838 	UIO_SETUP_SYSSPACE(&auio);
839 
840 	vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
841 	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
842 	    ump->um_extattr.uepm_ucred);
843 
844 	if (error)
845 		goto unlock_free_exit;
846 
847 	if (auio.uio_resid != 0) {
848 		printf("ufs_extattr_enable: malformed attribute header\n");
849 		error = EINVAL;
850 		goto unlock_free_exit;
851 	}
852 
853 	/*
854 	 * Try to determine the byte order of the attribute file.
855 	 */
856 	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
857 		attribute->uele_flags |= UELE_F_NEEDSWAP;
858 		attribute->uele_fileheader.uef_magic =
859 		    ufs_rw32(attribute->uele_fileheader.uef_magic,
860 			     UELE_NEEDSWAP(attribute));
861 		if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
862 			printf("ufs_extattr_enable: invalid attribute header "
863 			       "magic\n");
864 			error = EINVAL;
865 			goto unlock_free_exit;
866 		}
867 	}
868 	attribute->uele_fileheader.uef_version =
869 	    ufs_rw32(attribute->uele_fileheader.uef_version,
870 		     UELE_NEEDSWAP(attribute));
871 	attribute->uele_fileheader.uef_size =
872 	    ufs_rw32(attribute->uele_fileheader.uef_size,
873 		     UELE_NEEDSWAP(attribute));
874 
875 	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
876 		printf("ufs_extattr_enable: incorrect attribute header "
877 		    "version\n");
878 		error = EINVAL;
879 		goto unlock_free_exit;
880 	}
881 
882 	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
883 	    uele_entries);
884 
885 	VOP_UNLOCK(backing_vnode);
886 	return (0);
887 
888  unlock_free_exit:
889 	VOP_UNLOCK(backing_vnode);
890 
891  free_exit:
892 	kmem_free(attribute, sizeof(*attribute));
893 	return (error);
894 }
895 
896 /*
897  * Disable extended attribute support on an FS.
898  */
899 static int
ufs_extattr_disable(struct ufsmount * ump,int attrnamespace,const char * attrname,struct lwp * l)900 ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
901     const char *attrname, struct lwp *l)
902 {
903 	struct ufs_extattr_list_entry *uele;
904 	int error = 0;
905 
906 	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
907 		return (EINVAL);
908 
909 	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
910 	if (!uele)
911 		return (ENODATA);
912 
913 	LIST_REMOVE(uele, uele_entries);
914 
915 	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
916 	    l->l_cred);
917 
918 	kmem_free(uele, sizeof(*uele));
919 
920 	return (error);
921 }
922 
923 /*
924  * VFS call to manage extended attributes in UFS.  If filename_vp is
925  * non-NULL, it must be passed in locked, and regardless of errors in
926  * processing, will be unlocked.
927  */
928 int
ufs_extattrctl(struct mount * mp,int cmd,struct vnode * filename_vp,int attrnamespace,const char * attrname)929 ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
930     int attrnamespace, const char *attrname)
931 {
932 	struct lwp *l = curlwp;
933 	struct ufsmount *ump = VFSTOUFS(mp);
934 	int error;
935 
936 	/*
937 	 * Only privileged processes can configure extended attributes.
938 	 */
939 	error = kauth_authorize_system(l->l_cred, KAUTH_SYSTEM_FS_EXTATTR,
940 	    0, mp, NULL, NULL);
941 	if (error) {
942 		if (filename_vp != NULL)
943 			VOP_UNLOCK(filename_vp);
944 		return (error);
945 	}
946 
947 	switch(cmd) {
948 	case UFS_EXTATTR_CMD_START:
949 		if (filename_vp != NULL) {
950 			VOP_UNLOCK(filename_vp);
951 			return (EINVAL);
952 		}
953 		if (attrname != NULL)
954 			return (EINVAL);
955 
956 		error = ufs_extattr_autostart(mp, l);
957 		return (error);
958 
959 	case UFS_EXTATTR_CMD_STOP:
960 		if (filename_vp != NULL) {
961 			VOP_UNLOCK(filename_vp);
962 			return (EINVAL);
963 		}
964 		if (attrname != NULL)
965 			return (EINVAL);
966 
967 		ufs_extattr_stop(mp, l);
968 		return (0);
969 
970 	case UFS_EXTATTR_CMD_ENABLE:
971 		if (filename_vp == NULL)
972 			return (EINVAL);
973 		if (attrname == NULL) {
974 			VOP_UNLOCK(filename_vp);
975 			return (EINVAL);
976 		}
977 
978 		/*
979 		 * ufs_extattr_enable_with_open() will always unlock the
980 		 * vnode, regardless of failure.
981 		 */
982 		ufs_extattr_uepm_lock(ump);
983 		error = ufs_extattr_enable_with_open(ump, filename_vp,
984 		    attrnamespace, attrname, l);
985 		ufs_extattr_uepm_unlock(ump);
986 		return (error);
987 
988 	case UFS_EXTATTR_CMD_DISABLE:
989 		if (filename_vp != NULL) {
990 			VOP_UNLOCK(filename_vp);
991 			return (EINVAL);
992 		}
993 		if (attrname == NULL)
994 			return (EINVAL);
995 
996 		ufs_extattr_uepm_lock(ump);
997 		error = ufs_extattr_disable(ump, attrnamespace, attrname, l);
998 		ufs_extattr_uepm_unlock(ump);
999 		return (error);
1000 
1001 	default:
1002 		return (EINVAL);
1003 	}
1004 }
1005 
1006 /*
1007  * Read extended attribute header for a given vnode and attribute.
1008  * Backing vnode should be locked and unlocked by caller.
1009  */
1010 static int
ufs_extattr_get_header(struct vnode * vp,struct ufs_extattr_list_entry * uele,struct ufs_extattr_header * ueh,off_t * bap)1011 ufs_extattr_get_header(struct vnode *vp, struct ufs_extattr_list_entry *uele,
1012     struct ufs_extattr_header *ueh, off_t *bap)
1013 {
1014 	struct mount *mp = vp->v_mount;
1015 	struct ufsmount *ump = VFSTOUFS(mp);
1016 	struct inode *ip = VTOI(vp);
1017 	off_t base_offset;
1018 	struct iovec aiov;
1019 	struct uio aio;
1020 	int error;
1021 
1022 	/*
1023 	 * Find base offset of header in file based on file header size, and
1024 	 * data header size + maximum data size, indexed by inode number.
1025 	 */
1026 	base_offset = sizeof(struct ufs_extattr_fileheader) +
1027 	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1028 	    uele->uele_fileheader.uef_size);
1029 
1030 	/*
1031 	 * Read in the data header to see if the data is defined, and if so
1032 	 * how much.
1033 	 */
1034 	memset(ueh, 0, sizeof(struct ufs_extattr_header));
1035 	aiov.iov_base = ueh;
1036 	aiov.iov_len = sizeof(struct ufs_extattr_header);
1037 	aio.uio_iov = &aiov;
1038 	aio.uio_iovcnt = 1;
1039 	aio.uio_rw = UIO_READ;
1040 	aio.uio_offset = base_offset;
1041 	aio.uio_resid = sizeof(struct ufs_extattr_header);
1042 	UIO_SETUP_SYSSPACE(&aio);
1043 
1044 	error = VOP_READ(uele->uele_backing_vnode, &aio,
1045 	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1046 	if (error)
1047 		return error;
1048 
1049 	/*
1050 	 * Attribute headers are kept in file system byte order.
1051 	 * XXX What about the blob of data?
1052 	 */
1053 	ueh->ueh_flags = ufs_rw32(ueh->ueh_flags, UELE_NEEDSWAP(uele));
1054 	ueh->ueh_len   = ufs_rw32(ueh->ueh_len, UELE_NEEDSWAP(uele));
1055 	ueh->ueh_i_gen = ufs_rw32(ueh->ueh_i_gen, UELE_NEEDSWAP(uele));
1056 
1057 	/* Defined? */
1058 	if ((ueh->ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0)
1059 		return ENODATA;
1060 
1061 	/* Valid for the current inode generation? */
1062 	if (ueh->ueh_i_gen != ip->i_gen) {
1063 		/*
1064 		 * The inode itself has a different generation number
1065 		 * than the uele data.  For now, the best solution
1066 		 * is to coerce this to undefined, and let it get cleaned
1067 		 * up by the next write or extattrctl clean.
1068 		 */
1069 		printf("%s (%s): inode gen inconsistency (%u, %jd)\n",
1070 		       __func__,  mp->mnt_stat.f_mntonname, ueh->ueh_i_gen,
1071 		       (intmax_t)ip->i_gen);
1072 		return ENODATA;
1073 	}
1074 
1075 	/* Local size consistency check. */
1076 	if (ueh->ueh_len > uele->uele_fileheader.uef_size)
1077 		return ENXIO;
1078 
1079 	/* Return base offset */
1080 	if (bap != NULL)
1081 		*bap = base_offset;
1082 
1083 	return 0;
1084 }
1085 
1086 /*
1087  * Vnode operation to retrieve a named extended attribute.
1088  */
1089 int
ufs_getextattr(struct vop_getextattr_args * ap)1090 ufs_getextattr(struct vop_getextattr_args *ap)
1091 /*
1092 vop_getextattr {
1093 	IN struct vnode *a_vp;
1094 	IN int a_attrnamespace;
1095 	IN const char *a_name;
1096 	INOUT struct uio *a_uio;
1097 	OUT size_t *a_size;
1098 	IN kauth_cred_t a_cred;
1099 };
1100 */
1101 {
1102 	struct mount *mp = ap->a_vp->v_mount;
1103 	struct ufsmount *ump = VFSTOUFS(mp);
1104 	int error;
1105 
1106 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1107 		return (EOPNOTSUPP);
1108 
1109 	ufs_extattr_uepm_lock(ump);
1110 
1111 	error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1112 	    ap->a_uio, ap->a_size, ap->a_cred, curlwp);
1113 
1114 	ufs_extattr_uepm_unlock(ump);
1115 
1116 	return (error);
1117 }
1118 
1119 /*
1120  * Real work associated with retrieving a named attribute--assumes that
1121  * the attribute lock has already been grabbed.
1122  */
1123 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)1124 ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
1125     struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l)
1126 {
1127 	struct ufs_extattr_list_entry *attribute;
1128 	struct ufs_extattr_header ueh;
1129 	struct mount *mp = vp->v_mount;
1130 	struct ufsmount *ump = VFSTOUFS(mp);
1131 	off_t base_offset;
1132 	size_t len, old_len;
1133 	int error = 0;
1134 
1135 	if (strlen(name) == 0)
1136 		return (EINVAL);
1137 
1138 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1139 	    VREAD);
1140 	if (error)
1141 		return (error);
1142 
1143 	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1144 	if (!attribute)
1145 		return (ENODATA);
1146 
1147 	/*
1148 	 * Allow only offsets of zero to encourage the read/replace
1149 	 * extended attribute semantic.  Otherwise we can't guarantee
1150 	 * atomicity, as we don't provide locks for extended attributes.
1151 	 */
1152 	if (uio != NULL && uio->uio_offset != 0)
1153 		return (ENXIO);
1154 
1155 	/*
1156 	 * Don't need to get a lock on the backing file if the getattr is
1157 	 * being applied to the backing file, as the lock is already held.
1158 	 */
1159 	if (attribute->uele_backing_vnode != vp)
1160 		vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
1161 
1162 	error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1163 	if (error)
1164 		goto vopunlock_exit;
1165 
1166 	/* Return full data size if caller requested it. */
1167 	if (size != NULL)
1168 		*size = ueh.ueh_len;
1169 
1170 	/* Return data if the caller requested it. */
1171 	if (uio != NULL) {
1172 		/* Allow for offset into the attribute data. */
1173 		uio->uio_offset = base_offset + sizeof(struct
1174 		    ufs_extattr_header);
1175 
1176 		/*
1177 		 * Figure out maximum to transfer -- use buffer size and
1178 		 * local data limit.
1179 		 */
1180 		len = MIN(uio->uio_resid, ueh.ueh_len);
1181 		old_len = uio->uio_resid;
1182 		uio->uio_resid = len;
1183 
1184 		error = VOP_READ(attribute->uele_backing_vnode, uio,
1185 		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1186 		if (error)
1187 			goto vopunlock_exit;
1188 
1189 		uio->uio_resid = old_len - (len - uio->uio_resid);
1190 	}
1191 
1192  vopunlock_exit:
1193 
1194 	if (uio != NULL)
1195 		uio->uio_offset = 0;
1196 
1197 	if (attribute->uele_backing_vnode != vp)
1198 		VOP_UNLOCK(attribute->uele_backing_vnode);
1199 
1200 	return (error);
1201 }
1202 
1203 /*
1204  * Vnode operation to list extended attribute for a vnode
1205  */
1206 int
ufs_listextattr(struct vop_listextattr_args * ap)1207 ufs_listextattr(struct vop_listextattr_args *ap)
1208 /*
1209 vop_listextattr {
1210 	IN struct vnode *a_vp;
1211 	IN int a_attrnamespace;
1212 	INOUT struct uio *a_uio;
1213 	OUT size_t *a_size;
1214 	IN int flag;
1215 	IN kauth_cred_t a_cred;
1216 	struct proc *a_p;
1217 };
1218 */
1219 {
1220 	struct mount *mp = ap->a_vp->v_mount;
1221 	struct ufsmount *ump = VFSTOUFS(mp);
1222 	int error;
1223 
1224 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1225 		return (EOPNOTSUPP);
1226 
1227 	ufs_extattr_uepm_lock(ump);
1228 
1229 	error = ufs_extattr_list(ap->a_vp, ap->a_attrnamespace,
1230 	    ap->a_uio, ap->a_size, ap->a_flag, ap->a_cred, curlwp);
1231 
1232 	ufs_extattr_uepm_unlock(ump);
1233 
1234 	return (error);
1235 }
1236 
1237 /*
1238  * Real work associated with retrieving list of attributes--assumes that
1239  * the attribute lock has already been grabbed.
1240  */
1241 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)1242 ufs_extattr_list(struct vnode *vp, int attrnamespace,
1243     struct uio *uio, size_t *size, int flag,
1244     kauth_cred_t cred, struct lwp *l)
1245 {
1246 	struct ufs_extattr_list_entry *uele;
1247 	struct ufs_extattr_header ueh;
1248 	struct mount *mp = vp->v_mount;
1249 	struct ufsmount *ump = VFSTOUFS(mp);
1250 	size_t listsize = 0;
1251 	int error = 0;
1252 
1253 	/*
1254 	 * XXX: We can move this inside the loop and iterate on individual
1255 	 *	attributes.
1256 	 */
1257 	error = internal_extattr_check_cred(vp, attrnamespace, "", cred,
1258 	    VREAD);
1259 	if (error)
1260 		return (error);
1261 
1262 	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) {
1263 		unsigned char attrnamelen;
1264 
1265 		if (uele->uele_attrnamespace != attrnamespace)
1266 			continue;
1267 
1268 		error = ufs_extattr_get_header(vp, uele, &ueh, NULL);
1269 		if (error == ENODATA)
1270 			continue;
1271 		if (error != 0)
1272 			return error;
1273 
1274 		/*
1275 		 * Don't need to get a lock on the backing file if
1276 		 * the listattr is being applied to the backing file,
1277 		 * as the lock is already held.
1278 		 */
1279 		if (uele->uele_backing_vnode != vp)
1280 			vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
1281 
1282 		/*
1283 		 * +1 for trailing NUL (listxattr flavor)
1284 		 *  or leading name length (extattr_list_file flavor)
1285 	 	 */
1286 		attrnamelen = strlen(uele->uele_attrname);
1287 		listsize += attrnamelen + 1;
1288 
1289 		/* Return data if the caller requested it. */
1290 		if (uio != NULL) {
1291 			/*
1292 			 * We support two flavors. Either NUL-terminated
1293 			 * strings (a la listxattr), or non NUL-terminated,
1294 			 * one byte length prefixed strings (for
1295 			 * extattr_list_file). EXTATTR_LIST_LENPREFIX switches
1296 		 	 * that second behavior.
1297 			 */
1298 			if (flag & EXTATTR_LIST_LENPREFIX) {
1299 				uint8_t len = (uint8_t)attrnamelen;
1300 
1301 				/* Copy leading name length */
1302 				error = uiomove(&len, sizeof(len), uio);
1303 				if (error != 0)
1304 					break;
1305 			} else {
1306 				/* Include trailing NULL */
1307 				attrnamelen++;
1308 			}
1309 
1310 			error = uiomove(uele->uele_attrname,
1311 					(size_t)attrnamelen, uio);
1312 			if (error != 0)
1313 				break;
1314 		}
1315 
1316 		if (uele->uele_backing_vnode != vp)
1317 			VOP_UNLOCK(uele->uele_backing_vnode);
1318 
1319 		if (error != 0)
1320 			return error;
1321 	}
1322 
1323 	if (uio != NULL)
1324 		uio->uio_offset = 0;
1325 
1326 	/* Return full data size if caller requested it. */
1327 	if (size != NULL)
1328 		*size = listsize;
1329 
1330 	return 0;
1331 }
1332 
1333 /*
1334  * Vnode operation to remove a named attribute.
1335  */
1336 int
ufs_deleteextattr(struct vop_deleteextattr_args * ap)1337 ufs_deleteextattr(struct vop_deleteextattr_args *ap)
1338 /*
1339 vop_deleteextattr {
1340 	IN struct vnode *a_vp;
1341 	IN int a_attrnamespace;
1342 	IN const char *a_name;
1343 	IN kauth_cred_t a_cred;
1344 };
1345 */
1346 {
1347 	struct mount *mp = ap->a_vp->v_mount;
1348 	struct ufsmount *ump = VFSTOUFS(mp);
1349 	int error;
1350 
1351 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1352 		return (EOPNOTSUPP);
1353 
1354 	ufs_extattr_uepm_lock(ump);
1355 
1356 	error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1357 	    ap->a_cred, curlwp);
1358 
1359 	ufs_extattr_uepm_unlock(ump);
1360 
1361 	return (error);
1362 }
1363 
1364 /*
1365  * Vnode operation to set a named attribute.
1366  */
1367 int
ufs_setextattr(struct vop_setextattr_args * ap)1368 ufs_setextattr(struct vop_setextattr_args *ap)
1369 /*
1370 vop_setextattr {
1371 	IN struct vnode *a_vp;
1372 	IN int a_attrnamespace;
1373 	IN const char *a_name;
1374 	INOUT struct uio *a_uio;
1375 	IN kauth_cred_t a_cred;
1376 };
1377 */
1378 {
1379 	struct mount *mp = ap->a_vp->v_mount;
1380 	struct ufsmount *ump = VFSTOUFS(mp);
1381 	int error;
1382 
1383 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1384 		return (EOPNOTSUPP);
1385 
1386 	ufs_extattr_uepm_lock(ump);
1387 
1388 	/*
1389 	 * XXX: No longer a supported way to delete extended attributes.
1390 	 */
1391 	if (ap->a_uio == NULL) {
1392 		ufs_extattr_uepm_unlock(ump);
1393 		return (EINVAL);
1394 	}
1395 
1396 	error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1397 	    ap->a_uio, ap->a_cred, curlwp);
1398 
1399 	ufs_extattr_uepm_unlock(ump);
1400 
1401 	return (error);
1402 }
1403 
1404 /*
1405  * Real work associated with setting a vnode's extended attributes;
1406  * assumes that the attribute lock has already been grabbed.
1407  */
1408 static int
ufs_extattr_set(struct vnode * vp,int attrnamespace,const char * name,struct uio * uio,kauth_cred_t cred,struct lwp * l)1409 ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1410     struct uio *uio, kauth_cred_t cred, struct lwp *l)
1411 {
1412 	struct ufs_extattr_list_entry *attribute;
1413 	struct ufs_extattr_header ueh;
1414 	struct iovec local_aiov;
1415 	struct uio local_aio;
1416 	struct mount *mp = vp->v_mount;
1417 	struct ufsmount *ump = VFSTOUFS(mp);
1418 	struct inode *ip = VTOI(vp);
1419 	off_t base_offset;
1420 	int error = 0, ioflag;
1421 
1422 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1423 		return (EROFS);
1424 
1425 	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1426 		return (EINVAL);
1427 
1428 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1429 	    VWRITE);
1430 	if (error)
1431 		return (error);
1432 
1433 	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1434 	if (!attribute) {
1435 		error = ufs_extattr_autocreate_attr(vp, attrnamespace,
1436 						    name, l, &attribute);
1437 		if (error == EEXIST) {
1438 			/* Another thread raced us for backend creation */
1439 			error = 0;
1440 			attribute =
1441 			    ufs_extattr_find_attr(ump, attrnamespace, name);
1442 		}
1443 
1444 		if (error || !attribute)
1445 			return ENODATA;
1446 	}
1447 
1448 	/*
1449 	 * Early rejection of invalid offsets/length.
1450 	 * Reject: any offset but 0 (replace)
1451 	 *	 Any size greater than attribute size limit
1452  	 */
1453 	if (uio->uio_offset != 0 ||
1454 	    uio->uio_resid > attribute->uele_fileheader.uef_size)
1455 		return (ENXIO);
1456 
1457 	/*
1458 	 * Find base offset of header in file based on file header size, and
1459 	 * data header size + maximum data size, indexed by inode number.
1460 	 */
1461 	base_offset = sizeof(struct ufs_extattr_fileheader) +
1462 	    ip->i_number * (sizeof(struct ufs_extattr_header) +
1463 	    attribute->uele_fileheader.uef_size);
1464 
1465 	/*
1466 	 * Write out a data header for the data.
1467 	 */
1468 	ueh.ueh_len = ufs_rw32((uint32_t) uio->uio_resid,
1469 	    UELE_NEEDSWAP(attribute));
1470 	ueh.ueh_flags = ufs_rw32(UFS_EXTATTR_ATTR_FLAG_INUSE,
1471 				 UELE_NEEDSWAP(attribute));
1472 	ueh.ueh_i_gen = ufs_rw32(ip->i_gen, UELE_NEEDSWAP(attribute));
1473 	local_aiov.iov_base = &ueh;
1474 	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1475 	local_aio.uio_iov = &local_aiov;
1476 	local_aio.uio_iovcnt = 1;
1477 	local_aio.uio_rw = UIO_WRITE;
1478 	local_aio.uio_offset = base_offset;
1479 	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1480 	UIO_SETUP_SYSSPACE(&local_aio);
1481 
1482 	/*
1483 	 * Don't need to get a lock on the backing file if the setattr is
1484 	 * being applied to the backing file, as the lock is already held.
1485 	 */
1486 	if (attribute->uele_backing_vnode != vp)
1487 		vn_lock(attribute->uele_backing_vnode,
1488 		    LK_EXCLUSIVE | LK_RETRY);
1489 
1490 	ioflag = IO_NODELOCKED;
1491 	if (ufs_extattr_sync)
1492 		ioflag |= IO_SYNC;
1493 	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1494 	    ump->um_extattr.uepm_ucred);
1495 	if (error)
1496 		goto vopunlock_exit;
1497 
1498 	if (local_aio.uio_resid != 0) {
1499 		error = ENXIO;
1500 		goto vopunlock_exit;
1501 	}
1502 
1503 	/*
1504 	 * Write out user data.
1505 	 * XXX NOT ATOMIC WITH RESPECT TO THE HEADER.
1506 	 */
1507 	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1508 
1509 	ioflag = IO_NODELOCKED;
1510 	if (ufs_extattr_sync)
1511 		ioflag |= IO_SYNC;
1512 	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1513 	    ump->um_extattr.uepm_ucred);
1514 
1515  vopunlock_exit:
1516 	uio->uio_offset = 0;
1517 
1518 	if (attribute->uele_backing_vnode != vp)
1519 		VOP_UNLOCK(attribute->uele_backing_vnode);
1520 
1521 	return (error);
1522 }
1523 
1524 /*
1525  * Real work associated with removing an extended attribute from a vnode.
1526  * Assumes the attribute lock has already been grabbed.
1527  */
1528 static int
ufs_extattr_rm(struct vnode * vp,int attrnamespace,const char * name,kauth_cred_t cred,struct lwp * l)1529 ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1530     kauth_cred_t cred, struct lwp *l)
1531 {
1532 	struct ufs_extattr_list_entry *attribute;
1533 	struct ufs_extattr_header ueh;
1534 	struct mount *mp = vp->v_mount;
1535 	struct ufsmount *ump = VFSTOUFS(mp);
1536 	struct iovec local_aiov;
1537 	struct uio local_aio;
1538 	off_t base_offset;
1539 	int error = 0, ioflag;
1540 
1541 	if (vp->v_mount->mnt_flag & MNT_RDONLY)
1542 		return (EROFS);
1543 
1544 	if (!ufs_extattr_valid_attrname(attrnamespace, name))
1545 		return (EINVAL);
1546 
1547 	error = internal_extattr_check_cred(vp, attrnamespace, name, cred,
1548 	    VWRITE);
1549 	if (error)
1550 		return (error);
1551 
1552 	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1553 	if (!attribute)
1554 		return (ENODATA);
1555 
1556 	/*
1557 	 * Don't need to get a lock on the backing file if the getattr is
1558 	 * being applied to the backing file, as the lock is already held.
1559 	 */
1560 	if (attribute->uele_backing_vnode != vp)
1561 		vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
1562 
1563 	error = ufs_extattr_get_header(vp, attribute, &ueh, &base_offset);
1564 	if (error)
1565 		goto vopunlock_exit;
1566 
1567 	/* Flag it as not in use. */
1568 	ueh.ueh_flags = 0;		/* No need to byte swap 0 */
1569 	ueh.ueh_len = 0;		/* ...ditto... */
1570 
1571 	local_aiov.iov_base = &ueh;
1572 	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1573 	local_aio.uio_iov = &local_aiov;
1574 	local_aio.uio_iovcnt = 1;
1575 	local_aio.uio_rw = UIO_WRITE;
1576 	local_aio.uio_offset = base_offset;
1577 	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1578 	UIO_SETUP_SYSSPACE(&local_aio);
1579 
1580 	ioflag = IO_NODELOCKED;
1581 	if (ufs_extattr_sync)
1582 		ioflag |= IO_SYNC;
1583 	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1584 	    ump->um_extattr.uepm_ucred);
1585 	if (error)
1586 		goto vopunlock_exit;
1587 
1588 	if (local_aio.uio_resid != 0)
1589 		error = ENXIO;
1590 
1591  vopunlock_exit:
1592 	VOP_UNLOCK(attribute->uele_backing_vnode);
1593 
1594 	return (error);
1595 }
1596 
1597 /*
1598  * Called by UFS when an inode is no longer active and should have its
1599  * attributes stripped.
1600  */
1601 void
ufs_extattr_vnode_inactive(struct vnode * vp,struct lwp * l)1602 ufs_extattr_vnode_inactive(struct vnode *vp, struct lwp *l)
1603 {
1604 	struct ufs_extattr_list_entry *uele;
1605 	struct mount *mp = vp->v_mount;
1606 	struct ufsmount *ump = VFSTOUFS(mp);
1607 
1608 	/*
1609 	 * In that case, we cannot lock. We should not have any active vnodes
1610 	 * on the fs if this is not yet initialized but is going to be, so
1611 	 * this can go unlocked.
1612 	 */
1613 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1614 		return;
1615 
1616 	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1617 		return;
1618 
1619 	ufs_extattr_uepm_lock(ump);
1620 
1621 	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1622 		ufs_extattr_rm(vp, uele->uele_attrnamespace,
1623 		    uele->uele_attrname, lwp0.l_cred, l);
1624 
1625 	ufs_extattr_uepm_unlock(ump);
1626 }
1627 
1628 void
ufs_extattr_init(void)1629 ufs_extattr_init(void)
1630 {
1631 
1632 }
1633 
1634 void
ufs_extattr_done(void)1635 ufs_extattr_done(void)
1636 {
1637 
1638 }
1639