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