159241Srwatson/*-
2126097Srwatson * Copyright (c) 1999-2002 Robert N. M. Watson
3126097Srwatson * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
459241Srwatson * All rights reserved.
559241Srwatson *
685845Srwatson * This software was developed by Robert Watson for the TrustedBSD Project.
785845Srwatson *
8106394Srwatson * This software was developed for the FreeBSD Project in part by Network
9106394Srwatson * Associates Laboratories, the Security Research Division of Network
10106394Srwatson * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
11106394Srwatson * as part of the DARPA CHATS research program.
1290452Srwatson *
1359241Srwatson * Redistribution and use in source and binary forms, with or without
1459241Srwatson * modification, are permitted provided that the following conditions
1559241Srwatson * are met:
1659241Srwatson * 1. Redistributions of source code must retain the above copyright
1759241Srwatson *    notice, this list of conditions and the following disclaimer.
1859241Srwatson * 2. Redistributions in binary form must reproduce the above copyright
1959241Srwatson *    notice, this list of conditions and the following disclaimer in the
2059241Srwatson *    documentation and/or other materials provided with the distribution.
2159241Srwatson *
2259241Srwatson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2359241Srwatson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2459241Srwatson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2559241Srwatson * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2659241Srwatson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2759241Srwatson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2859241Srwatson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2959241Srwatson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3059241Srwatson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3159241Srwatson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3259241Srwatson * SUCH DAMAGE.
3359241Srwatson *
3459241Srwatson */
35116192Sobrien
3659241Srwatson/*
3796755Strhodes * Support for filesystem extended attribute: UFS-specific support functions.
3859241Srwatson */
3959241Srwatson
40116192Sobrien#include <sys/cdefs.h>
41116192Sobrien__FBSDID("$FreeBSD: releng/10.3/sys/ufs/ufs/ufs_extattr.c 252438 2013-07-01 04:06:40Z gleb $");
42116192Sobrien
4390453Srwatson#include "opt_ufs.h"
4490453Srwatson
4559241Srwatson#include <sys/param.h>
4659241Srwatson#include <sys/systm.h>
4759241Srwatson#include <sys/kernel.h>
4859241Srwatson#include <sys/namei.h>
4959241Srwatson#include <sys/malloc.h>
5059241Srwatson#include <sys/fcntl.h>
51164033Srwatson#include <sys/priv.h>
5259241Srwatson#include <sys/proc.h>
5359241Srwatson#include <sys/vnode.h>
5459241Srwatson#include <sys/mount.h>
5559241Srwatson#include <sys/lock.h>
5674234Srwatson#include <sys/dirent.h>
5774273Srwatson#include <sys/extattr.h>
58176797Srwatson#include <sys/sx.h>
5975106Srwatson#include <sys/sysctl.h>
6059241Srwatson
6192768Sjeff#include <vm/uma.h>
6274234Srwatson
6374234Srwatson#include <ufs/ufs/dir.h>
6459241Srwatson#include <ufs/ufs/extattr.h>
6559241Srwatson#include <ufs/ufs/quota.h>
6659241Srwatson#include <ufs/ufs/ufsmount.h>
6759241Srwatson#include <ufs/ufs/inode.h>
6874234Srwatson#include <ufs/ufs/ufs_extern.h>
6959241Srwatson
7074433Srwatson#ifdef UFS_EXTATTR
7174273Srwatson
7259241Srwatsonstatic MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
7359241Srwatson
7475106Srwatsonstatic int ufs_extattr_sync = 0;
7575106SrwatsonSYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync,
7675106Srwatson    0, "");
7775106Srwatson
7885577Srwatsonstatic int	ufs_extattr_valid_attrname(int attrnamespace,
7990453Srwatson		    const char *attrname);
8074273Srwatsonstatic int	ufs_extattr_enable_with_open(struct ufsmount *ump,
8190453Srwatson		    struct vnode *vp, int attrnamespace, const char *attrname,
8290453Srwatson		    struct thread *td);
8374437Srwatsonstatic int	ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
8490453Srwatson		    const char *attrname, struct vnode *backing_vnode,
8590453Srwatson		    struct thread *td);
8674437Srwatsonstatic int	ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
8790453Srwatson		    const char *attrname, struct thread *td);
8874437Srwatsonstatic int	ufs_extattr_get(struct vnode *vp, int attrnamespace,
8990453Srwatson		    const char *name, struct uio *uio, size_t *size,
9090453Srwatson		    struct ucred *cred, struct thread *td);
9174437Srwatsonstatic int	ufs_extattr_set(struct vnode *vp, int attrnamespace,
9290453Srwatson		    const char *name, struct uio *uio, struct ucred *cred,
9390453Srwatson		    struct thread *td);
9474437Srwatsonstatic int	ufs_extattr_rm(struct vnode *vp, int attrnamespace,
9590453Srwatson		    const char *name, struct ucred *cred, struct thread *td);
96191990Sattilio#ifdef UFS_EXTATTR_AUTOSTART
97186898Skibstatic int	ufs_extattr_autostart_locked(struct mount *mp,
98186898Skib		    struct thread *td);
99191990Sattilio#endif
100186898Skibstatic int	ufs_extattr_start_locked(struct ufsmount *ump,
101186898Skib		    struct thread *td);
10259241Srwatson
10359241Srwatson/*
10470776Srwatson * Per-FS attribute lock protecting attribute operations.
105176797Srwatson *
106176797Srwatson * XXXRW: Perhaps something more fine-grained would be appropriate, but at
107176797Srwatson * the end of the day we're going to contend on the vnode lock for the
108176797Srwatson * backing file anyway.
10959241Srwatson */
11059241Srwatsonstatic void
111234613Straszufs_extattr_uepm_lock(struct ufsmount *ump)
11259241Srwatson{
11359241Srwatson
114176797Srwatson	sx_xlock(&ump->um_extattr.uepm_lock);
11559241Srwatson}
11659241Srwatson
11759241Srwatsonstatic void
118234613Straszufs_extattr_uepm_unlock(struct ufsmount *ump)
11959241Srwatson{
12059241Srwatson
121176797Srwatson	sx_xunlock(&ump->um_extattr.uepm_lock);
12259241Srwatson}
12359241Srwatson
124131067Srwatson/*-
12565377Srwatson * Determine whether the name passed is a valid name for an actual
12665377Srwatson * attribute.
12765377Srwatson *
12865377Srwatson * Invalid currently consists of:
12970776Srwatson *	 NULL pointer for attrname
13070776Srwatson *	 zero-length attrname (used to retrieve application attribute list)
13165377Srwatson */
13265377Srwatsonstatic int
13385577Srwatsonufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
13465377Srwatson{
13565377Srwatson
13665377Srwatson	if (attrname == NULL)
13765377Srwatson		return (0);
13865377Srwatson	if (strlen(attrname) == 0)
13965377Srwatson		return (0);
14065377Srwatson	return (1);
14165377Srwatson}
14265377Srwatson
14365377Srwatson/*
14459241Srwatson * Locate an attribute given a name and mountpoint.
14559241Srwatson * Must be holding uepm lock for the mount point.
14659241Srwatson */
14759241Srwatsonstatic struct ufs_extattr_list_entry *
14874437Srwatsonufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
14974273Srwatson    const char *attrname)
15059241Srwatson{
151131066Srwatson	struct ufs_extattr_list_entry *search_attribute;
15259241Srwatson
153176797Srwatson	sx_assert(&ump->um_extattr.uepm_lock, SA_XLOCKED);
154176797Srwatson
15571999Sphk	for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
156131066Srwatson	    search_attribute != NULL;
15771999Sphk	    search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
15870776Srwatson		if (!(strncmp(attrname, search_attribute->uele_attrname,
15974273Srwatson		    UFS_EXTATTR_MAXEXTATTRNAME)) &&
16074437Srwatson		    (attrnamespace == search_attribute->uele_attrnamespace)) {
16170776Srwatson			return (search_attribute);
16270776Srwatson		}
16370776Srwatson	}
16459241Srwatson
16570776Srwatson	return (0);
16659241Srwatson}
16759241Srwatson
16859241Srwatson/*
16959241Srwatson * Initialize per-FS structures supporting extended attributes.  Do not
17059241Srwatson * start extended attributes yet.
17159241Srwatson */
17259241Srwatsonvoid
17359241Srwatsonufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
17459241Srwatson{
17559241Srwatson
17659241Srwatson	uepm->uepm_flags = 0;
17759241Srwatson	LIST_INIT(&uepm->uepm_list);
178176797Srwatson	sx_init(&uepm->uepm_lock, "ufs_extattr_sx");
17959241Srwatson	uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
18059241Srwatson}
18159241Srwatson
18259241Srwatson/*
18366616Srwatson * Destroy per-FS structures supporting extended attributes.  Assumes
18466616Srwatson * that EAs have already been stopped, and will panic if not.
18566616Srwatson */
18666616Srwatsonvoid
18766616Srwatsonufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
18866616Srwatson{
18966616Srwatson
19066616Srwatson	if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
19166616Srwatson		panic("ufs_extattr_uepm_destroy: not initialized");
19266616Srwatson
19366893Srwatson	if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
19466616Srwatson		panic("ufs_extattr_uepm_destroy: called while still started");
19566616Srwatson
19666616Srwatson	/*
19785579Srwatson	 * It's not clear that either order for the next two lines is
19866616Srwatson	 * ideal, and it should never be a problem if this is only called
19966616Srwatson	 * during unmount, and with vfs_busy().
20066616Srwatson	 */
20166616Srwatson	uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
202176797Srwatson	sx_destroy(&uepm->uepm_lock);
20366616Srwatson}
20466616Srwatson
20566616Srwatson/*
20670776Srwatson * Start extended attribute support on an FS.
20759241Srwatson */
20859241Srwatsonint
20983366Sjulianufs_extattr_start(struct mount *mp, struct thread *td)
21059241Srwatson{
211131066Srwatson	struct ufsmount *ump;
212131066Srwatson	int error = 0;
21359241Srwatson
21459241Srwatson	ump = VFSTOUFS(mp);
21559241Srwatson
216234613Strasz	ufs_extattr_uepm_lock(ump);
217186898Skib	error = ufs_extattr_start_locked(ump, td);
218234613Strasz	ufs_extattr_uepm_unlock(ump);
219186898Skib	return (error);
220186898Skib}
22159241Srwatson
222186898Skibstatic int
223186898Skibufs_extattr_start_locked(struct ufsmount *ump, struct thread *td)
224186898Skib{
225186898Skib	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
226186898Skib		return (EOPNOTSUPP);
227186898Skib	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)
228186898Skib		return (EBUSY);
22959241Srwatson
23059241Srwatson	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
23191406Sjhb	ump->um_extattr.uepm_ucred = crhold(td->td_ucred);
232186898Skib	return (0);
23359241Srwatson}
23459241Srwatson
23574433Srwatson#ifdef UFS_EXTATTR_AUTOSTART
23659241Srwatson/*
23774234Srwatson * Helper routine: given a locked parent directory and filename, return
23874234Srwatson * the locked vnode of the inode associated with the name.  Will not
23974234Srwatson * follow symlinks, may return any type of vnode.  Lock on parent will
24074234Srwatson * be released even in the event of a failure.  In the event that the
24174234Srwatson * target is the parent (i.e., "."), there will be two references and
24274234Srwatson * one lock, requiring the caller to possibly special-case.
24374234Srwatson */
24474234Srwatson#define	UE_GETDIR_LOCKPARENT	1
24574234Srwatson#define	UE_GETDIR_LOCKPARENT_DONT	2
24674234Srwatsonstatic int
24774234Srwatsonufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname,
24883366Sjulian    struct vnode **vp, struct thread *td)
24974234Srwatson{
25074234Srwatson	struct vop_cachedlookup_args vargs;
25174234Srwatson	struct componentname cnp;
25274234Srwatson	struct vnode *target_vp;
25374234Srwatson	int error;
25474234Srwatson
25574234Srwatson	bzero(&cnp, sizeof(cnp));
25674234Srwatson	cnp.cn_nameiop = LOOKUP;
25774234Srwatson	cnp.cn_flags = ISLASTCN;
25874234Srwatson	if (lockparent == UE_GETDIR_LOCKPARENT)
25974234Srwatson		cnp.cn_flags |= LOCKPARENT;
260151258Srwatson	cnp.cn_lkflags = LK_EXCLUSIVE;
26183366Sjulian	cnp.cn_thread = td;
26291406Sjhb	cnp.cn_cred = td->td_ucred;
263111119Simp	cnp.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
26474234Srwatson	cnp.cn_nameptr = cnp.cn_pnbuf;
26574234Srwatson	error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN,
26674234Srwatson	    (size_t *) &cnp.cn_namelen);
26774234Srwatson	if (error) {
26874234Srwatson		if (lockparent == UE_GETDIR_LOCKPARENT_DONT) {
269175294Sattilio			VOP_UNLOCK(start_dvp, 0);
27074234Srwatson		}
27192768Sjeff		uma_zfree(namei_zone, cnp.cn_pnbuf);
27274234Srwatson		printf("ufs_extattr_lookup: copystr failed\n");
27374234Srwatson		return (error);
27474234Srwatson	}
27574234Srwatson	cnp.cn_namelen--;	/* trim nul termination */
276138814Simp	vargs.a_gen.a_desc = NULL;
27774234Srwatson	vargs.a_dvp = start_dvp;
27874234Srwatson	vargs.a_vpp = &target_vp;
27974234Srwatson	vargs.a_cnp = &cnp;
28074234Srwatson	error = ufs_lookup(&vargs);
28192768Sjeff	uma_zfree(namei_zone, cnp.cn_pnbuf);
28274234Srwatson	if (error) {
28374234Srwatson		/*
28474234Srwatson		 * Error condition, may have to release the lock on the parent
28574234Srwatson		 * if ufs_lookup() didn't.
28674234Srwatson		 */
287144209Sjeff		if (lockparent == UE_GETDIR_LOCKPARENT_DONT)
288175294Sattilio			VOP_UNLOCK(start_dvp, 0);
28974234Srwatson
29074234Srwatson		/*
29174234Srwatson		 * Check that ufs_lookup() didn't release the lock when we
29274234Srwatson		 * didn't want it to.
29374234Srwatson		 */
294144209Sjeff		if (lockparent == UE_GETDIR_LOCKPARENT)
295144209Sjeff			ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup");
29674234Srwatson
29774234Srwatson		return (error);
29874234Srwatson	}
29974234Srwatson/*
30074234Srwatson	if (target_vp == start_dvp)
30174234Srwatson		panic("ufs_extattr_lookup: target_vp == start_dvp");
30274234Srwatson*/
30374234Srwatson
304144209Sjeff	if (target_vp != start_dvp && lockparent == UE_GETDIR_LOCKPARENT_DONT)
305175294Sattilio		VOP_UNLOCK(start_dvp, 0);
30674234Srwatson
307144209Sjeff	if (lockparent == UE_GETDIR_LOCKPARENT)
308144209Sjeff		ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup");
30974234Srwatson
31074234Srwatson	/* printf("ufs_extattr_lookup: success\n"); */
31174234Srwatson	*vp = target_vp;
31274234Srwatson	return (0);
31374234Srwatson}
31474433Srwatson#endif /* !UFS_EXTATTR_AUTOSTART */
31574234Srwatson
31674234Srwatson/*
31796755Strhodes * Enable an EA using the passed filesystem, backing vnode, attribute name,
31874273Srwatson * namespace, and proc.  Will perform a VOP_OPEN() on the vp, so expects vp
31985580Srwatson * to be locked when passed in.  The vnode will be returned unlocked,
32085580Srwatson * regardless of success/failure of the function.  As a result, the caller
32185580Srwatson * will always need to vrele(), but not vput().
32274234Srwatson */
32374234Srwatsonstatic int
32474234Srwatsonufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
32583366Sjulian    int attrnamespace, const char *attrname, struct thread *td)
32674234Srwatson{
32774234Srwatson	int error;
32874234Srwatson
329170183Skib	error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, NULL);
33074234Srwatson	if (error) {
33174234Srwatson		printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
33274234Srwatson		    "with %d\n", error);
333175294Sattilio		VOP_UNLOCK(vp, 0);
33474234Srwatson		return (error);
33574234Srwatson	}
33674234Srwatson
337242476Skib	VOP_ADD_WRITECOUNT(vp, 1);
338232701Sjhb	CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d", __func__, vp,
339232701Sjhb	    vp->v_writecount);
34074234Srwatson
34174234Srwatson	vref(vp);
34274234Srwatson
343175294Sattilio	VOP_UNLOCK(vp, 0);
34474234Srwatson
34583366Sjulian	error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td);
34677847Stmm	if (error != 0)
34791406Sjhb		vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
34877847Stmm	return (error);
34974234Srwatson}
35074234Srwatson
35174433Srwatson#ifdef UFS_EXTATTR_AUTOSTART
35274234Srwatson/*
35374234Srwatson * Given a locked directory vnode, iterate over the names in the directory
35474234Srwatson * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
35574234Srwatson * attribute files.  Then invoke ufs_extattr_enable_with_open() on each
35674234Srwatson * to attempt to start the attribute.  Leaves the directory locked on
35774234Srwatson * exit.
35874234Srwatson */
35974234Srwatsonstatic int
36074234Srwatsonufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
36183366Sjulian    int attrnamespace, struct thread *td)
36274234Srwatson{
36374234Srwatson	struct vop_readdir_args vargs;
36474234Srwatson	struct dirent *dp, *edp;
36574234Srwatson	struct vnode *attr_vp;
36674234Srwatson	struct uio auio;
36774234Srwatson	struct iovec aiov;
36874234Srwatson	char *dirbuf;
36974234Srwatson	int error, eofflag = 0;
37074234Srwatson
37174234Srwatson	if (dvp->v_type != VDIR)
37274234Srwatson		return (ENOTDIR);
37374234Srwatson
374184205Sdes	dirbuf = malloc(DIRBLKSIZ, M_TEMP, M_WAITOK);
37574234Srwatson
37674234Srwatson	auio.uio_iov = &aiov;
37774234Srwatson	auio.uio_iovcnt = 1;
37874234Srwatson	auio.uio_rw = UIO_READ;
37974234Srwatson	auio.uio_segflg = UIO_SYSSPACE;
38083366Sjulian	auio.uio_td = td;
38174234Srwatson	auio.uio_offset = 0;
38274234Srwatson
383138814Simp	vargs.a_gen.a_desc = NULL;
38474234Srwatson	vargs.a_vp = dvp;
38574234Srwatson	vargs.a_uio = &auio;
38691406Sjhb	vargs.a_cred = td->td_ucred;
38774234Srwatson	vargs.a_eofflag = &eofflag;
38874234Srwatson	vargs.a_ncookies = NULL;
38974234Srwatson	vargs.a_cookies = NULL;
39074234Srwatson
39174234Srwatson	while (!eofflag) {
39274234Srwatson		auio.uio_resid = DIRBLKSIZ;
39374234Srwatson		aiov.iov_base = dirbuf;
39474234Srwatson		aiov.iov_len = DIRBLKSIZ;
39574234Srwatson		error = ufs_readdir(&vargs);
39674234Srwatson		if (error) {
39774234Srwatson			printf("ufs_extattr_iterate_directory: ufs_readdir "
39874234Srwatson			    "%d\n", error);
39974234Srwatson			return (error);
40074234Srwatson		}
40174234Srwatson
402252438Sgleb		edp = (struct dirent *)&dirbuf[DIRBLKSIZ - auio.uio_resid];
40374234Srwatson		for (dp = (struct dirent *)dirbuf; dp < edp; ) {
40474234Srwatson			if (dp->d_reclen == 0)
40574234Srwatson				break;
40674234Srwatson			error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT,
40783366Sjulian			    dp->d_name, &attr_vp, td);
40874234Srwatson			if (error) {
40974234Srwatson				printf("ufs_extattr_iterate_directory: lookup "
41074234Srwatson				    "%s %d\n", dp->d_name, error);
41174234Srwatson			} else if (attr_vp == dvp) {
41274234Srwatson				vrele(attr_vp);
41374234Srwatson			} else if (attr_vp->v_type != VREG) {
41474234Srwatson				vput(attr_vp);
41574234Srwatson			} else {
41674234Srwatson				error = ufs_extattr_enable_with_open(ump,
41783366Sjulian				    attr_vp, attrnamespace, dp->d_name, td);
41874273Srwatson				vrele(attr_vp);
41974234Srwatson				if (error) {
42074234Srwatson					printf("ufs_extattr_iterate_directory: "
42174234Srwatson					    "enable %s %d\n", dp->d_name,
42274234Srwatson					    error);
42385580Srwatson				} else if (bootverbose) {
42485580Srwatson					printf("UFS autostarted EA %s\n",
42585580Srwatson					    dp->d_name);
42674234Srwatson				}
42774234Srwatson			}
42874234Srwatson			dp = (struct dirent *) ((char *)dp + dp->d_reclen);
42974234Srwatson			if (dp >= edp)
43074234Srwatson				break;
43174234Srwatson		}
43274234Srwatson	}
433184205Sdes	free(dirbuf, M_TEMP);
43474234Srwatson
43574234Srwatson	return (0);
43674234Srwatson}
43774234Srwatson
43874234Srwatson/*
43974234Srwatson * Auto-start of extended attributes, to be executed (optionally) at
44074234Srwatson * mount-time.
44174234Srwatson */
44274234Srwatsonint
44383366Sjulianufs_extattr_autostart(struct mount *mp, struct thread *td)
44474234Srwatson{
445186898Skib	struct ufsmount *ump;
446186898Skib	int error;
447186898Skib
448186898Skib	ump = VFSTOUFS(mp);
449234613Strasz	ufs_extattr_uepm_lock(ump);
450186898Skib	error = ufs_extattr_autostart_locked(mp, td);
451234613Strasz	ufs_extattr_uepm_unlock(ump);
452186898Skib	return (error);
453186898Skib}
454186898Skib
455186898Skibstatic int
456186898Skibufs_extattr_autostart_locked(struct mount *mp, struct thread *td)
457186898Skib{
45874404Srwatson	struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
459176752Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
46074234Srwatson	int error;
46174234Srwatson
46274234Srwatson	/*
463176752Srwatson	 * UFS_EXTATTR applies only to UFS1, as UFS2 uses native extended
464176752Srwatson	 * attributes, so don't autostart.
465176752Srwatson	 */
466176752Srwatson	if (ump->um_fstype != UFS1)
467176752Srwatson		return (0);
468176752Srwatson
469176752Srwatson	/*
47096755Strhodes	 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
47174273Srwatson	 * If so, automatically start EA's.
47274234Srwatson	 */
473191990Sattilio	error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp);
47474234Srwatson	if (error) {
47585578Srwatson		printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
47685578Srwatson		    error);
47774234Srwatson		return (error);
47874234Srwatson	}
47974234Srwatson
48074234Srwatson	error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT,
48183366Sjulian	    UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td);
48274234Srwatson	if (error) {
48374234Srwatson		/* rvp ref'd but now unlocked */
48474234Srwatson		vrele(rvp);
48574234Srwatson		return (error);
48674234Srwatson	}
48774234Srwatson	if (rvp == attr_dvp) {
48874234Srwatson		/* Should never happen. */
489155160Sjeff		vput(rvp);
49074234Srwatson		vrele(attr_dvp);
49174234Srwatson		return (EINVAL);
49274234Srwatson	}
49374234Srwatson	vrele(rvp);
49474234Srwatson
49574234Srwatson	if (attr_dvp->v_type != VDIR) {
49674273Srwatson		printf("ufs_extattr_autostart: %s != VDIR\n",
49774273Srwatson		    UFS_EXTATTR_FSROOTSUBDIR);
49874404Srwatson		goto return_vput_attr_dvp;
49974234Srwatson	}
50074234Srwatson
501186898Skib	error = ufs_extattr_start_locked(ump, td);
50274234Srwatson	if (error) {
50374234Srwatson		printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
50474234Srwatson		    error);
50574404Srwatson		goto return_vput_attr_dvp;
50674234Srwatson	}
50774234Srwatson
50874234Srwatson	/*
50974404Srwatson	 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
51074404Srwatson	 * UFS_EXTATTR_SUBDIR_USER.  For each, iterate over the sub-directory,
51174404Srwatson	 * and start with appropriate type.  Failures in either don't
51274404Srwatson	 * result in an over-all failure.  attr_dvp is left locked to
51374404Srwatson	 * be cleaned up on exit.
51474234Srwatson	 */
51574404Srwatson	error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
51683366Sjulian	    UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td);
51774404Srwatson	if (!error) {
51874404Srwatson		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
51983366Sjulian		    attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td);
52074404Srwatson		if (error)
52174404Srwatson			printf("ufs_extattr_iterate_directory returned %d\n",
52274404Srwatson			    error);
52374404Srwatson		vput(attr_system_dvp);
52474404Srwatson	}
52574234Srwatson
52674404Srwatson	error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
52783366Sjulian	    UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td);
52874404Srwatson	if (!error) {
52974404Srwatson		error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
53083366Sjulian		    attr_user_dvp, EXTATTR_NAMESPACE_USER, td);
53174404Srwatson		if (error)
53274404Srwatson			printf("ufs_extattr_iterate_directory returned %d\n",
53374404Srwatson			    error);
53474404Srwatson		vput(attr_user_dvp);
53574404Srwatson	}
53674404Srwatson
53774404Srwatson	/* Mask startup failures in sub-directories. */
53874234Srwatson	error = 0;
53974234Srwatson
54074404Srwatsonreturn_vput_attr_dvp:
54174234Srwatson	vput(attr_dvp);
54274234Srwatson
54374234Srwatson	return (error);
54474234Srwatson}
54574433Srwatson#endif /* !UFS_EXTATTR_AUTOSTART */
54674234Srwatson
54774234Srwatson/*
54870776Srwatson * Stop extended attribute support on an FS.
54959241Srwatson */
55059241Srwatsonint
55183366Sjulianufs_extattr_stop(struct mount *mp, struct thread *td)
55259241Srwatson{
553131066Srwatson	struct ufs_extattr_list_entry *uele;
554131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
555131066Srwatson	int error = 0;
55659241Srwatson
557234613Strasz	ufs_extattr_uepm_lock(ump);
55859241Srwatson
55959241Srwatson	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
56059241Srwatson		error = EOPNOTSUPP;
56159241Srwatson		goto unlock;
56259241Srwatson	}
56359241Srwatson
564152163Sdelphij	while ((uele = LIST_FIRST(&ump->um_extattr.uepm_list)) != NULL) {
56574437Srwatson		ufs_extattr_disable(ump, uele->uele_attrnamespace,
56683366Sjulian		    uele->uele_attrname, td);
56770776Srwatson	}
56859241Srwatson
56959241Srwatson	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
57059241Srwatson
57159241Srwatson	crfree(ump->um_extattr.uepm_ucred);
57259241Srwatson	ump->um_extattr.uepm_ucred = NULL;
57359241Srwatson
57459241Srwatsonunlock:
575234613Strasz	ufs_extattr_uepm_unlock(ump);
57659241Srwatson
57759241Srwatson	return (error);
57859241Srwatson}
57959241Srwatson
58059241Srwatson/*
58196755Strhodes * Enable a named attribute on the specified filesystem; provide an
58274273Srwatson * unlocked backing vnode to hold the attribute data.
58359241Srwatson */
58459241Srwatsonstatic int
58574437Srwatsonufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
58683366Sjulian    const char *attrname, struct vnode *backing_vnode, struct thread *td)
58759241Srwatson{
588131066Srwatson	struct ufs_extattr_list_entry *attribute;
589131066Srwatson	struct iovec aiov;
590131066Srwatson	struct uio auio;
591131066Srwatson	int error = 0;
59259241Srwatson
59385577Srwatson	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
59465377Srwatson		return (EINVAL);
59559241Srwatson	if (backing_vnode->v_type != VREG)
59659241Srwatson		return (EINVAL);
59759241Srwatson
598184214Sdes	attribute = malloc(sizeof(struct ufs_extattr_list_entry),
599184214Sdes	    M_UFS_EXTATTR, M_WAITOK);
60059241Srwatson	if (attribute == NULL)
60159241Srwatson		return (ENOMEM);
60259241Srwatson
60359241Srwatson	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
60459241Srwatson		error = EOPNOTSUPP;
60559241Srwatson		goto free_exit;
60659241Srwatson	}
60759241Srwatson
60874437Srwatson	if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
60965768Srwatson		error = EEXIST;
61059241Srwatson		goto free_exit;
61159241Srwatson	}
61259241Srwatson
61385578Srwatson	strncpy(attribute->uele_attrname, attrname,
61485578Srwatson	    UFS_EXTATTR_MAXEXTATTRNAME);
61574437Srwatson	attribute->uele_attrnamespace = attrnamespace;
61659241Srwatson	bzero(&attribute->uele_fileheader,
61759241Srwatson	    sizeof(struct ufs_extattr_fileheader));
61859241Srwatson
61959241Srwatson	attribute->uele_backing_vnode = backing_vnode;
62059241Srwatson
62159241Srwatson	auio.uio_iov = &aiov;
62259241Srwatson	auio.uio_iovcnt = 1;
62359241Srwatson	aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
62459241Srwatson	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
62559241Srwatson	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
62659241Srwatson	auio.uio_offset = (off_t) 0;
62759241Srwatson	auio.uio_segflg = UIO_SYSSPACE;
62859241Srwatson	auio.uio_rw = UIO_READ;
62983366Sjulian	auio.uio_td = td;
63059241Srwatson
631175202Sattilio	vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
63265768Srwatson	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
63365768Srwatson	    ump->um_extattr.uepm_ucred);
63459241Srwatson
63574273Srwatson	if (error)
636115040Srwatson		goto unlock_free_exit;
63759241Srwatson
63859241Srwatson	if (auio.uio_resid != 0) {
63959241Srwatson		printf("ufs_extattr_enable: malformed attribute header\n");
64059241Srwatson		error = EINVAL;
641115040Srwatson		goto unlock_free_exit;
64259241Srwatson	}
64359241Srwatson
64459400Srwatson	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
64559400Srwatson		printf("ufs_extattr_enable: invalid attribute header magic\n");
64659400Srwatson		error = EINVAL;
647115040Srwatson		goto unlock_free_exit;
64859400Srwatson	}
64959400Srwatson
65059400Srwatson	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
65159400Srwatson		printf("ufs_extattr_enable: incorrect attribute header "
65259400Srwatson		    "version\n");
65359400Srwatson		error = EINVAL;
654115040Srwatson		goto unlock_free_exit;
65559400Srwatson	}
65659400Srwatson
657101308Sjeff	ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable");
65885578Srwatson	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
65985578Srwatson	    uele_entries);
66059241Srwatson
661175294Sattilio	VOP_UNLOCK(backing_vnode, 0);
66259241Srwatson	return (0);
66359241Srwatson
664115040Srwatsonunlock_free_exit:
665175294Sattilio	VOP_UNLOCK(backing_vnode, 0);
666115040Srwatson
66759241Srwatsonfree_exit:
668184205Sdes	free(attribute, M_UFS_EXTATTR);
66959241Srwatson	return (error);
67059241Srwatson}
67159241Srwatson
67259241Srwatson/*
67370776Srwatson * Disable extended attribute support on an FS.
67459241Srwatson */
67559241Srwatsonstatic int
67674437Srwatsonufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
67783366Sjulian    const char *attrname, struct thread *td)
67859241Srwatson{
679131066Srwatson	struct ufs_extattr_list_entry *uele;
680131066Srwatson	int error = 0;
68159241Srwatson
68285577Srwatson	if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
68365377Srwatson		return (EINVAL);
68465377Srwatson
68574437Srwatson	uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
68659241Srwatson	if (!uele)
68791814Sgreen		return (ENOATTR);
68859241Srwatson
68959241Srwatson	LIST_REMOVE(uele, uele_entries);
69059241Srwatson
691175202Sattilio	vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
692101308Sjeff	ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable");
693175294Sattilio	VOP_UNLOCK(uele->uele_backing_vnode, 0);
69485578Srwatson	error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
69591406Sjhb	    td->td_ucred, td);
69659241Srwatson
697184205Sdes	free(uele, M_UFS_EXTATTR);
69859241Srwatson
69959241Srwatson	return (error);
70059241Srwatson}
70159241Srwatson
70259241Srwatson/*
70374273Srwatson * VFS call to manage extended attributes in UFS.  If filename_vp is
70474273Srwatson * non-NULL, it must be passed in locked, and regardless of errors in
70574273Srwatson * processing, will be unlocked.
70659241Srwatson */
70759241Srwatsonint
70874273Srwatsonufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
709191990Sattilio    int attrnamespace, const char *attrname)
71059241Srwatson{
711131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
712191990Sattilio	struct thread *td = curthread;
713131066Srwatson	int error;
71459241Srwatson
71566041Srwatson	/*
71666041Srwatson	 * Processes with privilege, but in jail, are not allowed to
71766041Srwatson	 * configure extended attributes.
71866041Srwatson	 */
719164033Srwatson	error = priv_check(td, PRIV_UFS_EXTATTRCTL);
720164033Srwatson	if (error) {
72174273Srwatson		if (filename_vp != NULL)
722175294Sattilio			VOP_UNLOCK(filename_vp, 0);
72359241Srwatson		return (error);
72474273Srwatson	}
72559241Srwatson
726176752Srwatson	/*
727176752Srwatson	 * We only allow extattrctl(2) on UFS1 file systems, as UFS2 uses
728176752Srwatson	 * native extended attributes.
729176752Srwatson	 */
730176752Srwatson	if (ump->um_fstype != UFS1) {
731176752Srwatson		if (filename_vp != NULL)
732176752Srwatson			VOP_UNLOCK(filename_vp, 0);
733176752Srwatson		return (EOPNOTSUPP);
734176752Srwatson	}
735176752Srwatson
73659241Srwatson	switch(cmd) {
73759241Srwatson	case UFS_EXTATTR_CMD_START:
73874273Srwatson		if (filename_vp != NULL) {
739175294Sattilio			VOP_UNLOCK(filename_vp, 0);
74074273Srwatson			return (EINVAL);
74174273Srwatson		}
74274273Srwatson		if (attrname != NULL)
74374273Srwatson			return (EINVAL);
74474273Srwatson
74583366Sjulian		error = ufs_extattr_start(mp, td);
74659241Srwatson
74759241Srwatson		return (error);
74859241Srwatson
74959241Srwatson	case UFS_EXTATTR_CMD_STOP:
75074273Srwatson		if (filename_vp != NULL) {
751175294Sattilio			VOP_UNLOCK(filename_vp, 0);
75274273Srwatson			return (EINVAL);
75374273Srwatson		}
75474273Srwatson		if (attrname != NULL)
75574273Srwatson			return (EINVAL);
75659241Srwatson
75783366Sjulian		error = ufs_extattr_stop(mp, td);
75874273Srwatson
75974273Srwatson		return (error);
76074273Srwatson
76159241Srwatson	case UFS_EXTATTR_CMD_ENABLE:
76259241Srwatson
76374273Srwatson		if (filename_vp == NULL)
76474273Srwatson			return (EINVAL);
76574273Srwatson		if (attrname == NULL) {
766175294Sattilio			VOP_UNLOCK(filename_vp, 0);
76774273Srwatson			return (EINVAL);
76874273Srwatson		}
76959241Srwatson
77074273Srwatson		/*
77174273Srwatson		 * ufs_extattr_enable_with_open() will always unlock the
77274273Srwatson		 * vnode, regardless of failure.
77374273Srwatson		 */
774234613Strasz		ufs_extattr_uepm_lock(ump);
77574273Srwatson		error = ufs_extattr_enable_with_open(ump, filename_vp,
77683366Sjulian		    attrnamespace, attrname, td);
777234613Strasz		ufs_extattr_uepm_unlock(ump);
77859241Srwatson
77959241Srwatson		return (error);
78059241Srwatson
78159241Srwatson	case UFS_EXTATTR_CMD_DISABLE:
78259241Srwatson
78374273Srwatson		if (filename_vp != NULL) {
784175294Sattilio			VOP_UNLOCK(filename_vp, 0);
78574273Srwatson			return (EINVAL);
78674273Srwatson		}
78774273Srwatson		if (attrname == NULL)
78874273Srwatson			return (EINVAL);
78974273Srwatson
790234613Strasz		ufs_extattr_uepm_lock(ump);
79185578Srwatson		error = ufs_extattr_disable(ump, attrnamespace, attrname,
79285578Srwatson		    td);
793234613Strasz		ufs_extattr_uepm_unlock(ump);
79459241Srwatson
79559241Srwatson		return (error);
79659241Srwatson
79759241Srwatson	default:
79859241Srwatson		return (EINVAL);
79959241Srwatson	}
80059241Srwatson}
80159241Srwatson
80259241Srwatson/*
80370776Srwatson * Vnode operating to retrieve a named extended attribute.
80459241Srwatson */
80559241Srwatsonint
80695974Sphkufs_getextattr(struct vop_getextattr_args *ap)
80759241Srwatson/*
80859241Srwatsonvop_getextattr {
80970776Srwatson	IN struct vnode *a_vp;
81074437Srwatson	IN int a_attrnamespace;
81170776Srwatson	IN const char *a_name;
81270776Srwatson	INOUT struct uio *a_uio;
813104346Sdd	OUT size_t *a_size;
81470776Srwatson	IN struct ucred *a_cred;
81583366Sjulian	IN struct thread *a_td;
81659241Srwatson};
81759241Srwatson*/
81859241Srwatson{
819131066Srwatson	struct mount *mp = ap->a_vp->v_mount;
820131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
821131066Srwatson	int error;
82259241Srwatson
823234613Strasz	ufs_extattr_uepm_lock(ump);
82459241Srwatson
82574437Srwatson	error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
82690448Srwatson	    ap->a_uio, ap->a_size, ap->a_cred, ap->a_td);
82759241Srwatson
828234613Strasz	ufs_extattr_uepm_unlock(ump);
82959241Srwatson
83059241Srwatson	return (error);
83159241Srwatson}
83259241Srwatson
83359241Srwatson/*
83459241Srwatson * Real work associated with retrieving a named attribute--assumes that
83559241Srwatson * the attribute lock has already been grabbed.
83659241Srwatson */
83759241Srwatsonstatic int
83874437Srwatsonufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
83990448Srwatson    struct uio *uio, size_t *size, struct ucred *cred, struct thread *td)
84059241Srwatson{
841131066Srwatson	struct ufs_extattr_list_entry *attribute;
842131066Srwatson	struct ufs_extattr_header ueh;
843131066Srwatson	struct iovec local_aiov;
844131066Srwatson	struct uio local_aio;
845131066Srwatson	struct mount *mp = vp->v_mount;
846131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
847131066Srwatson	struct inode *ip = VTOI(vp);
848131066Srwatson	off_t base_offset;
849131066Srwatson	size_t len, old_len;
850131066Srwatson	int error = 0;
85159241Srwatson
85259241Srwatson	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
85359241Srwatson		return (EOPNOTSUPP);
85459241Srwatson
855115865Srwatson	if (strlen(name) == 0)
85665377Srwatson		return (EINVAL);
85765377Srwatson
858182721Strasz	error = extattr_check_cred(vp, attrnamespace, cred, td, VREAD);
859102985Sphk	if (error)
860102985Sphk		return (error);
861102985Sphk
86274437Srwatson	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
86359241Srwatson	if (!attribute)
86491814Sgreen		return (ENOATTR);
86559241Srwatson
86659241Srwatson	/*
86759913Srwatson	 * Allow only offsets of zero to encourage the read/replace
86859913Srwatson	 * extended attribute semantic.  Otherwise we can't guarantee
86970776Srwatson	 * atomicity, as we don't provide locks for extended attributes.
87059241Srwatson	 */
87190448Srwatson	if (uio != NULL && uio->uio_offset != 0)
87259241Srwatson		return (ENXIO);
87359241Srwatson
87459241Srwatson	/*
87559241Srwatson	 * Find base offset of header in file based on file header size, and
87670776Srwatson	 * data header size + maximum data size, indexed by inode number.
87759241Srwatson	 */
87859241Srwatson	base_offset = sizeof(struct ufs_extattr_fileheader) +
87959241Srwatson	    ip->i_number * (sizeof(struct ufs_extattr_header) +
88059241Srwatson	    attribute->uele_fileheader.uef_size);
88159241Srwatson
88259241Srwatson	/*
88359241Srwatson	 * Read in the data header to see if the data is defined, and if so
88459241Srwatson	 * how much.
88559241Srwatson	 */
88659241Srwatson	bzero(&ueh, sizeof(struct ufs_extattr_header));
88759241Srwatson	local_aiov.iov_base = (caddr_t) &ueh;
88859241Srwatson	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
88959241Srwatson	local_aio.uio_iov = &local_aiov;
89059241Srwatson	local_aio.uio_iovcnt = 1;
89159241Srwatson	local_aio.uio_rw = UIO_READ;
89259241Srwatson	local_aio.uio_segflg = UIO_SYSSPACE;
89383366Sjulian	local_aio.uio_td = td;
89459241Srwatson	local_aio.uio_offset = base_offset;
89559241Srwatson	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
89659241Srwatson
89765768Srwatson	/*
89865768Srwatson	 * Acquire locks.
899141523Srwatson	 *
90065768Srwatson	 * Don't need to get a lock on the backing file if the getattr is
90165768Srwatson	 * being applied to the backing file, as the lock is already held.
90265768Srwatson	 */
90365768Srwatson	if (attribute->uele_backing_vnode != vp)
904175202Sattilio		vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
90559241Srwatson
90665768Srwatson	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
90765768Srwatson	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
90859241Srwatson	if (error)
90959241Srwatson		goto vopunlock_exit;
91059241Srwatson
91170776Srwatson	/* Defined? */
91259241Srwatson	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
91391814Sgreen		error = ENOATTR;
91459241Srwatson		goto vopunlock_exit;
91559241Srwatson	}
91659241Srwatson
91770776Srwatson	/* Valid for the current inode generation? */
91859388Srwatson	if (ueh.ueh_i_gen != ip->i_gen) {
91959388Srwatson		/*
92059388Srwatson		 * The inode itself has a different generation number
92159388Srwatson		 * than the attribute data.  For now, the best solution
92259388Srwatson		 * is to coerce this to undefined, and let it get cleaned
92359388Srwatson		 * up by the next write or extattrctl clean.
92459388Srwatson		 */
925252437Spfg		printf("ufs_extattr_get (%s): inode number inconsistency (%d, %ju)\n",
926252435Spfg		    mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (uintmax_t)ip->i_gen);
92791814Sgreen		error = ENOATTR;
92859388Srwatson		goto vopunlock_exit;
92959388Srwatson	}
93059388Srwatson
93170776Srwatson	/* Local size consistency check. */
93259241Srwatson	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
93359241Srwatson		error = ENXIO;
93459241Srwatson		goto vopunlock_exit;
93559241Srwatson	}
93659241Srwatson
93790448Srwatson	/* Return full data size if caller requested it. */
93890448Srwatson	if (size != NULL)
93990448Srwatson		*size = ueh.ueh_len;
94059241Srwatson
94190448Srwatson	/* Return data if the caller requested it. */
94290448Srwatson	if (uio != NULL) {
94390448Srwatson		/* Allow for offset into the attribute data. */
94490448Srwatson		uio->uio_offset = base_offset + sizeof(struct
94590448Srwatson		    ufs_extattr_header);
94659241Srwatson
94790448Srwatson		/*
94890448Srwatson		 * Figure out maximum to transfer -- use buffer size and
94990448Srwatson		 * local data limit.
95090448Srwatson		 */
95190448Srwatson		len = MIN(uio->uio_resid, ueh.ueh_len);
95290448Srwatson		old_len = uio->uio_resid;
95390448Srwatson		uio->uio_resid = len;
95459241Srwatson
95590448Srwatson		error = VOP_READ(attribute->uele_backing_vnode, uio,
95690448Srwatson		    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
95790448Srwatson		if (error)
95890448Srwatson			goto vopunlock_exit;
95959241Srwatson
96090448Srwatson		uio->uio_resid = old_len - (len - uio->uio_resid);
96190448Srwatson	}
96290448Srwatson
96359241Srwatsonvopunlock_exit:
96459241Srwatson
96590448Srwatson	if (uio != NULL)
96690448Srwatson		uio->uio_offset = 0;
96765768Srwatson
96865768Srwatson	if (attribute->uele_backing_vnode != vp)
969175294Sattilio		VOP_UNLOCK(attribute->uele_backing_vnode, 0);
97065768Srwatson
97159241Srwatson	return (error);
97259241Srwatson}
97359241Srwatson
97459241Srwatson/*
975118131Srwatson * Vnode operation to remove a named attribute.
976118131Srwatson */
977118131Srwatsonint
978118131Srwatsonufs_deleteextattr(struct vop_deleteextattr_args *ap)
979118131Srwatson/*
980118131Srwatsonvop_deleteextattr {
981118131Srwatson	IN struct vnode *a_vp;
982118131Srwatson	IN int a_attrnamespace;
983118131Srwatson	IN const char *a_name;
984118131Srwatson	IN struct ucred *a_cred;
985118131Srwatson	IN struct thread *a_td;
986118131Srwatson};
987118131Srwatson*/
988118131Srwatson{
989131066Srwatson	struct mount *mp = ap->a_vp->v_mount;
990131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
991131066Srwatson	int error;
992118131Srwatson
993234613Strasz	ufs_extattr_uepm_lock(ump);
994118131Srwatson
995118131Srwatson	error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
996118131Srwatson	    ap->a_cred, ap->a_td);
997118131Srwatson
998118131Srwatson
999234613Strasz	ufs_extattr_uepm_unlock(ump);
1000118131Srwatson
1001118131Srwatson	return (error);
1002118131Srwatson}
1003118131Srwatson
1004118131Srwatson/*
100570776Srwatson * Vnode operation to set a named attribute.
100659241Srwatson */
100759241Srwatsonint
100895974Sphkufs_setextattr(struct vop_setextattr_args *ap)
100959241Srwatson/*
101059241Srwatsonvop_setextattr {
101170776Srwatson	IN struct vnode *a_vp;
101274437Srwatson	IN int a_attrnamespace;
101370776Srwatson	IN const char *a_name;
101470776Srwatson	INOUT struct uio *a_uio;
101570776Srwatson	IN struct ucred *a_cred;
101683366Sjulian	IN struct thread *a_td;
101759241Srwatson};
101859241Srwatson*/
101959241Srwatson{
1020131066Srwatson	struct mount *mp = ap->a_vp->v_mount;
1021131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
1022131066Srwatson	int error;
102359241Srwatson
1024118131Srwatson	/*
1025118131Srwatson	 * XXX: No longer a supported way to delete extended attributes.
1026118131Srwatson	 */
1027118131Srwatson	if (ap->a_uio == NULL)
1028118131Srwatson		return (EINVAL);
102959241Srwatson
1030234613Strasz	ufs_extattr_uepm_lock(ump);
1031225104Sae
1032118131Srwatson	error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1033118131Srwatson	    ap->a_uio, ap->a_cred, ap->a_td);
1034118131Srwatson
1035234613Strasz	ufs_extattr_uepm_unlock(ump);
103659241Srwatson
103759241Srwatson	return (error);
103859241Srwatson}
103959241Srwatson
104059241Srwatson/*
104159241Srwatson * Real work associated with setting a vnode's extended attributes;
104259241Srwatson * assumes that the attribute lock has already been grabbed.
104359241Srwatson */
104459241Srwatsonstatic int
104574437Srwatsonufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
104683366Sjulian    struct uio *uio, struct ucred *cred, struct thread *td)
104759241Srwatson{
1048131066Srwatson	struct ufs_extattr_list_entry *attribute;
1049131066Srwatson	struct ufs_extattr_header ueh;
1050131066Srwatson	struct iovec local_aiov;
1051131066Srwatson	struct uio local_aio;
1052131066Srwatson	struct mount *mp = vp->v_mount;
1053131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
1054131066Srwatson	struct inode *ip = VTOI(vp);
1055131066Srwatson	off_t base_offset;
1056131066Srwatson	int error = 0, ioflag;
105759241Srwatson
105859241Srwatson	if (vp->v_mount->mnt_flag & MNT_RDONLY)
105959241Srwatson		return (EROFS);
106059241Srwatson	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
106159241Srwatson		return (EOPNOTSUPP);
106285577Srwatson	if (!ufs_extattr_valid_attrname(attrnamespace, name))
106365377Srwatson		return (EINVAL);
106459241Srwatson
1065182721Strasz	error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE);
1066102985Sphk	if (error)
1067102985Sphk		return (error);
1068102985Sphk
106974437Srwatson	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
107059241Srwatson	if (!attribute)
107191814Sgreen		return (ENOATTR);
107259241Srwatson
107359241Srwatson	/*
107470776Srwatson	 * Early rejection of invalid offsets/length.
107559241Srwatson	 * Reject: any offset but 0 (replace)
107670776Srwatson	 *	 Any size greater than attribute size limit
107759241Srwatson 	 */
107859241Srwatson	if (uio->uio_offset != 0 ||
107959241Srwatson	    uio->uio_resid > attribute->uele_fileheader.uef_size)
108059241Srwatson		return (ENXIO);
108159241Srwatson
108259241Srwatson	/*
108359241Srwatson	 * Find base offset of header in file based on file header size, and
108470776Srwatson	 * data header size + maximum data size, indexed by inode number.
108559241Srwatson	 */
108659241Srwatson	base_offset = sizeof(struct ufs_extattr_fileheader) +
108759241Srwatson	    ip->i_number * (sizeof(struct ufs_extattr_header) +
108859241Srwatson	    attribute->uele_fileheader.uef_size);
108959241Srwatson
109059241Srwatson	/*
109170776Srwatson	 * Write out a data header for the data.
109259241Srwatson	 */
109359241Srwatson	ueh.ueh_len = uio->uio_resid;
109459241Srwatson	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
109559388Srwatson	ueh.ueh_i_gen = ip->i_gen;
109659241Srwatson	local_aiov.iov_base = (caddr_t) &ueh;
109759241Srwatson	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
109859241Srwatson	local_aio.uio_iov = &local_aiov;
109959241Srwatson	local_aio.uio_iovcnt = 1;
110059241Srwatson	local_aio.uio_rw = UIO_WRITE;
110159241Srwatson	local_aio.uio_segflg = UIO_SYSSPACE;
110283366Sjulian	local_aio.uio_td = td;
110359241Srwatson	local_aio.uio_offset = base_offset;
110459241Srwatson	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
110559241Srwatson
110659241Srwatson	/*
110765768Srwatson	 * Acquire locks.
1108141523Srwatson	 *
110959241Srwatson	 * Don't need to get a lock on the backing file if the setattr is
111065768Srwatson	 * being applied to the backing file, as the lock is already held.
111159241Srwatson	 */
111259241Srwatson	if (attribute->uele_backing_vnode != vp)
1113175202Sattilio		vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
111459241Srwatson
111575106Srwatson	ioflag = IO_NODELOCKED;
111675106Srwatson	if (ufs_extattr_sync)
111775106Srwatson		ioflag |= IO_SYNC;
111875106Srwatson	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
111975106Srwatson	    ump->um_extattr.uepm_ucred);
112059241Srwatson	if (error)
112159241Srwatson		goto vopunlock_exit;
112259241Srwatson
112359268Srwatson	if (local_aio.uio_resid != 0) {
112459241Srwatson		error = ENXIO;
112559241Srwatson		goto vopunlock_exit;
112659268Srwatson	}
112759241Srwatson
112859241Srwatson	/*
112970776Srwatson	 * Write out user data.
113059241Srwatson	 */
113159241Srwatson	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
113259241Srwatson
113375106Srwatson	ioflag = IO_NODELOCKED;
113475106Srwatson	if (ufs_extattr_sync)
113575106Srwatson		ioflag |= IO_SYNC;
113675106Srwatson	error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
113775106Srwatson	    ump->um_extattr.uepm_ucred);
113875106Srwatson
113959241Srwatsonvopunlock_exit:
114059241Srwatson	uio->uio_offset = 0;
114159241Srwatson
114259268Srwatson	if (attribute->uele_backing_vnode != vp)
1143175294Sattilio		VOP_UNLOCK(attribute->uele_backing_vnode, 0);
114459268Srwatson
114559241Srwatson	return (error);
114659241Srwatson}
114759241Srwatson
114859241Srwatson/*
114959241Srwatson * Real work associated with removing an extended attribute from a vnode.
115059241Srwatson * Assumes the attribute lock has already been grabbed.
115159241Srwatson */
115259241Srwatsonstatic int
115374437Srwatsonufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
115483366Sjulian    struct ucred *cred, struct thread *td)
115559241Srwatson{
1156131066Srwatson	struct ufs_extattr_list_entry *attribute;
1157131066Srwatson	struct ufs_extattr_header ueh;
1158131066Srwatson	struct iovec local_aiov;
1159131066Srwatson	struct uio local_aio;
1160131066Srwatson	struct mount *mp = vp->v_mount;
1161131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
1162131066Srwatson	struct inode *ip = VTOI(vp);
1163131066Srwatson	off_t base_offset;
1164131066Srwatson	int error = 0, ioflag;
116559241Srwatson
116659241Srwatson	if (vp->v_mount->mnt_flag & MNT_RDONLY)
116759241Srwatson		return (EROFS);
116859241Srwatson	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
116959241Srwatson		return (EOPNOTSUPP);
117085577Srwatson	if (!ufs_extattr_valid_attrname(attrnamespace, name))
117165377Srwatson		return (EINVAL);
117259241Srwatson
1173182721Strasz	error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE);
1174102985Sphk	if (error)
1175102985Sphk		return (error);
1176102985Sphk
117774437Srwatson	attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
117859241Srwatson	if (!attribute)
117991814Sgreen		return (ENOATTR);
118059241Srwatson
118159241Srwatson	/*
118259241Srwatson	 * Find base offset of header in file based on file header size, and
118370776Srwatson	 * data header size + maximum data size, indexed by inode number.
118459241Srwatson	 */
118559241Srwatson	base_offset = sizeof(struct ufs_extattr_fileheader) +
118659241Srwatson	    ip->i_number * (sizeof(struct ufs_extattr_header) +
118759241Srwatson	    attribute->uele_fileheader.uef_size);
118859241Srwatson
118959241Srwatson	/*
119065768Srwatson	 * Check to see if currently defined.
119159241Srwatson	 */
119259241Srwatson	bzero(&ueh, sizeof(struct ufs_extattr_header));
119359241Srwatson
119459241Srwatson	local_aiov.iov_base = (caddr_t) &ueh;
119559241Srwatson	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
119659241Srwatson	local_aio.uio_iov = &local_aiov;
119759241Srwatson	local_aio.uio_iovcnt = 1;
119859241Srwatson	local_aio.uio_rw = UIO_READ;
119959241Srwatson	local_aio.uio_segflg = UIO_SYSSPACE;
120083366Sjulian	local_aio.uio_td = td;
120159241Srwatson	local_aio.uio_offset = base_offset;
120259241Srwatson	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
120359241Srwatson
120459241Srwatson	/*
120559241Srwatson	 * Don't need to get the lock on the backing vnode if the vnode we're
120659241Srwatson	 * modifying is it, as we already hold the lock.
120759241Srwatson	 */
120859241Srwatson	if (attribute->uele_backing_vnode != vp)
1209175202Sattilio		vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
121059241Srwatson
121165768Srwatson	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
121265768Srwatson	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
121359241Srwatson	if (error)
121459241Srwatson		goto vopunlock_exit;
121559241Srwatson
121670776Srwatson	/* Defined? */
121759241Srwatson	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
121891814Sgreen		error = ENOATTR;
121959241Srwatson		goto vopunlock_exit;
122059241Srwatson	}
122159241Srwatson
122270773Srwatson	/* Valid for the current inode generation? */
122370773Srwatson	if (ueh.ueh_i_gen != ip->i_gen) {
122470773Srwatson		/*
122570773Srwatson		 * The inode itself has a different generation number than
122670776Srwatson		 * the attribute data.  For now, the best solution is to
122770773Srwatson		 * coerce this to undefined, and let it get cleaned up by
122870773Srwatson		 * the next write or extattrctl clean.
122970773Srwatson		 */
1230106673Sjhb		printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n",
1231106673Sjhb		    mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen);
123291814Sgreen		error = ENOATTR;
123370773Srwatson		goto vopunlock_exit;
123470773Srwatson	}
123570773Srwatson
123670776Srwatson	/* Flag it as not in use. */
123759241Srwatson	ueh.ueh_flags = 0;
123870774Srwatson	ueh.ueh_len = 0;
123959241Srwatson
124070774Srwatson	local_aiov.iov_base = (caddr_t) &ueh;
124170774Srwatson	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
124270774Srwatson	local_aio.uio_iov = &local_aiov;
124370774Srwatson	local_aio.uio_iovcnt = 1;
124470774Srwatson	local_aio.uio_rw = UIO_WRITE;
124570774Srwatson	local_aio.uio_segflg = UIO_SYSSPACE;
124683366Sjulian	local_aio.uio_td = td;
124765768Srwatson	local_aio.uio_offset = base_offset;
124865768Srwatson	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
124965768Srwatson
125075106Srwatson	ioflag = IO_NODELOCKED;
125175106Srwatson	if (ufs_extattr_sync)
125275106Srwatson		ioflag |= IO_SYNC;
125375106Srwatson	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
125475106Srwatson	    ump->um_extattr.uepm_ucred);
125559241Srwatson	if (error)
125659241Srwatson		goto vopunlock_exit;
125759241Srwatson
125859241Srwatson	if (local_aio.uio_resid != 0)
125959241Srwatson		error = ENXIO;
126059241Srwatson
126159241Srwatsonvopunlock_exit:
1262175294Sattilio	VOP_UNLOCK(attribute->uele_backing_vnode, 0);
126359241Srwatson
126459241Srwatson	return (error);
126559241Srwatson}
126659241Srwatson
126759241Srwatson/*
126859241Srwatson * Called by UFS when an inode is no longer active and should have its
126959241Srwatson * attributes stripped.
127059241Srwatson */
127159241Srwatsonvoid
127283366Sjulianufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td)
127359241Srwatson{
1274131066Srwatson	struct ufs_extattr_list_entry *uele;
1275131066Srwatson	struct mount *mp = vp->v_mount;
1276131066Srwatson	struct ufsmount *ump = VFSTOUFS(mp);
127759241Srwatson
127877190Stmm	/*
127977190Stmm	 * In that case, we cannot lock. We should not have any active vnodes
128077190Stmm	 * on the fs if this is not yet initialized but is going to be, so
128177190Stmm	 * this can go unlocked.
128277190Stmm	 */
128377190Stmm	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
128477190Stmm		return;
128577190Stmm
1286234613Strasz	ufs_extattr_uepm_lock(ump);
128770776Srwatson
128859241Srwatson	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1289234613Strasz		ufs_extattr_uepm_unlock(ump);
129059241Srwatson		return;
129159241Srwatson	}
129259241Srwatson
129372012Sphk	LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
129474437Srwatson		ufs_extattr_rm(vp, uele->uele_attrnamespace,
129583366Sjulian		    uele->uele_attrname, NULL, td);
129659241Srwatson
1297234613Strasz	ufs_extattr_uepm_unlock(ump);
129859241Srwatson}
129974273Srwatson
130074433Srwatson#endif /* !UFS_EXTATTR */
1301