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