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