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