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