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