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