ufs_extattr.c revision 65721
1/*-
2 * Copyright (c) 1999, 2000 Robert N. M. Watson
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$FreeBSD: head/sys/ufs/ufs/ufs_extattr.c 65721 2000-09-11 05:43:02Z jhb $
27 */
28/*
29 * TrustedBSD Project - extended attribute support for UFS-like file systems
30 */
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/namei.h>
36#include <sys/malloc.h>
37#include <sys/fcntl.h>
38#include <sys/proc.h>
39#include <sys/vnode.h>
40#include <sys/mount.h>
41#include <sys/lock.h>
42
43#include <ufs/ufs/extattr.h>
44#include <ufs/ufs/quota.h>
45#include <ufs/ufs/ufsmount.h>
46#include <ufs/ufs/inode.h>
47
48#define MIN(a,b) (((a)<(b))?(a):(b))
49
50static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
51
52static int	ufs_extattr_valid_attrname(const char *attrname);
53static int	ufs_extattr_credcheck(struct vnode *vp,
54    struct ufs_extattr_list_entry *uele, struct ucred *cred, struct proc *p,
55    int access);
56static int	ufs_extattr_enable(struct ufsmount *ump, const char *attrname,
57    struct vnode *backing_vnode, struct proc *p);
58static int	ufs_extattr_disable(struct ufsmount *ump, const char *attrname,
59    struct proc *p);
60static int	ufs_extattr_get(struct vnode *vp, const char *name,
61    struct uio *uio, struct ucred *cred, struct proc *p);
62static int	ufs_extattr_set(struct vnode *vp, const char *name,
63    struct uio *uio, struct ucred *cred, struct proc *p);
64static int	ufs_extattr_rm(struct vnode *vp, const char *name,
65    struct ucred *cred, struct proc *p);
66
67/*
68 * Per-FS attribute lock protecting attribute operations
69 * XXX Right now there is a lot of lock contention due to having a single
70 * lock per-FS; really, this should be far more fine-grained.
71 */
72static void
73ufs_extattr_uepm_lock(struct ufsmount *ump, struct proc *p)
74{
75
76	/* ideally, LK_CANRECURSE would not be used, here */
77	lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY |
78	    LK_CANRECURSE, 0, p);
79}
80
81static void
82ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p)
83{
84
85	lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, p);
86}
87
88/*
89 * Determine whether the name passed is a valid name for an actual
90 * attribute.
91 *
92 * Invalid currently consists of:
93 *         NULL pointer for attrname
94 *         zero-length attrname (used to retrieve application attr list)
95 *         attrname consisting of "$" (used to treive system attr list)
96 */
97static int
98ufs_extattr_valid_attrname(const char *attrname)
99{
100
101	if (attrname == NULL)
102		return (0);
103	if (strlen(attrname) == 0)
104		return (0);
105	if (strlen(attrname) == 1 && attrname[0] == '$')
106		return (0);
107	return (1);
108}
109
110/*
111 * Locate an attribute given a name and mountpoint.
112 * Must be holding uepm lock for the mount point.
113 */
114static struct ufs_extattr_list_entry *
115ufs_extattr_find_attr(struct ufsmount *ump, const char *attrname)
116{
117        struct ufs_extattr_list_entry   *search_attribute;
118
119        for (search_attribute = ump->um_extattr.uepm_list.lh_first;
120	    search_attribute;
121	    search_attribute = search_attribute->uele_entries.le_next) {
122                if (!(strncmp(attrname, search_attribute->uele_attrname,
123                    UFS_EXTATTR_MAXEXTATTRNAME))) {
124                        return (search_attribute);
125                }
126        }
127
128        return (0);
129}
130
131/*
132 * Initialize per-FS structures supporting extended attributes.  Do not
133 * start extended attributes yet.
134 */
135void
136ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
137{
138
139	uepm->uepm_flags = 0;
140
141	LIST_INIT(&uepm->uepm_list);
142	/* XXX is PVFS right, here? */
143	lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0);
144	uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
145}
146
147/*
148 * Start extended attribute support on an FS
149 */
150int
151ufs_extattr_start(struct mount *mp, struct proc *p)
152{
153	struct ufsmount	*ump;
154	int	error = 0;
155
156	ump = VFSTOUFS(mp);
157
158	ufs_extattr_uepm_lock(ump, p);
159
160	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
161		error = EOPNOTSUPP;
162		goto unlock;
163	}
164	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
165		error = EBUSY;
166		goto unlock;
167	}
168
169	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
170
171	crhold(p->p_ucred);
172	ump->um_extattr.uepm_ucred = p->p_ucred;
173
174unlock:
175	ufs_extattr_uepm_unlock(ump, p);
176
177	return (error);
178}
179
180/*
181 * Stop extended attribute support on an FS
182 */
183int
184ufs_extattr_stop(struct mount *mp, struct proc *p)
185{
186	struct ufs_extattr_list_entry	*uele;
187	struct ufsmount	*ump = VFSTOUFS(mp);
188	int	error = 0;
189
190	ufs_extattr_uepm_lock(ump, p);
191
192	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
193		error = EOPNOTSUPP;
194		goto unlock;
195	}
196
197        while (ump->um_extattr.uepm_list.lh_first != NULL) {
198                uele = ump->um_extattr.uepm_list.lh_first;
199		ufs_extattr_disable(ump, uele->uele_attrname, p);
200        }
201
202	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
203
204	crfree(ump->um_extattr.uepm_ucred);
205	ump->um_extattr.uepm_ucred = NULL;
206
207unlock:
208	ufs_extattr_uepm_unlock(ump, p);
209
210	return (error);
211}
212
213/*
214 * Enable a named attribute on the specified file system; provide a
215 * backing vnode to hold the attribute data.
216 */
217static int
218ufs_extattr_enable(struct ufsmount *ump, const char *attrname,
219    struct vnode *backing_vnode, struct proc *p)
220{
221	struct ufs_extattr_list_entry	*attribute;
222	struct iovec	aiov;
223	struct uio	auio;
224	int	error = 0;
225
226	if (!ufs_extattr_valid_attrname(attrname))
227		return (EINVAL);
228	if (backing_vnode->v_type != VREG)
229		return (EINVAL);
230
231	MALLOC(attribute, struct ufs_extattr_list_entry *,
232	    sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK);
233	if (attribute == NULL)
234		return (ENOMEM);
235
236	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
237		error = EOPNOTSUPP;
238		goto free_exit;
239	}
240
241	if (ufs_extattr_find_attr(ump, attrname)) {
242		error = EOPNOTSUPP;
243		goto free_exit;
244	}
245
246	strncpy(attribute->uele_attrname, attrname, UFS_EXTATTR_MAXEXTATTRNAME);
247	bzero(&attribute->uele_fileheader,
248	    sizeof(struct ufs_extattr_fileheader));
249
250	attribute->uele_backing_vnode = backing_vnode;
251
252	auio.uio_iov = &aiov;
253	auio.uio_iovcnt = 1;
254	aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
255	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
256	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
257	auio.uio_offset = (off_t) 0;
258	auio.uio_segflg = UIO_SYSSPACE;
259	auio.uio_rw = UIO_READ;
260	auio.uio_procp = (struct proc *) p;
261
262	VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE);
263	vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p);
264	error = VOP_READ(backing_vnode, &auio, 0, ump->um_extattr.uepm_ucred);
265	VOP_UNLOCK(backing_vnode, 0, p);
266
267	if (error) {
268		goto free_exit;
269	}
270
271	if (auio.uio_resid != 0) {
272		printf("ufs_extattr_enable: malformed attribute header\n");
273		error = EINVAL;
274		goto free_exit;
275	}
276
277	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
278		printf("ufs_extattr_enable: invalid attribute header magic\n");
279		error = EINVAL;
280		goto free_exit;
281	}
282
283	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
284		printf("ufs_extattr_enable: incorrect attribute header "
285		    "version\n");
286		error = EINVAL;
287		goto free_exit;
288	}
289
290	backing_vnode->v_flag |= VSYSTEM;
291	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries);
292
293	return (0);
294
295free_exit:
296	FREE(attribute, M_UFS_EXTATTR);
297	return (error);
298}
299
300/*
301 * Disable extended attribute support on an FS
302 */
303static int
304ufs_extattr_disable(struct ufsmount *ump, const char *attrname, struct proc *p)
305{
306	struct ufs_extattr_list_entry	*uele;
307	int	error = 0;
308
309	if (!ufs_extattr_valid_attrname(attrname))
310		return (EINVAL);
311
312	uele = ufs_extattr_find_attr(ump, attrname);
313	if (!uele)
314		return (ENOENT);
315
316	LIST_REMOVE(uele, uele_entries);
317
318	uele->uele_backing_vnode->v_flag &= ~VSYSTEM;
319        error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p);
320
321	FREE(uele, M_UFS_EXTATTR);
322
323	return (error);
324}
325
326/*
327 * VFS call to manage extended attributes in UFS
328 * attrname, arg are userspace pointers from the syscall
329 */
330int
331ufs_extattrctl(struct mount *mp, int cmd, const char *attrname,
332	       caddr_t arg, struct proc *p)
333{
334	struct nameidata	nd;
335	struct ufsmount	*ump = VFSTOUFS(mp);
336	struct vnode	*vp;
337	char	local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* inc null */
338	char	*filename;
339	int	error, flags;
340	size_t	len;
341
342	if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0)))
343		return (error);
344
345	switch(cmd) {
346	case UFS_EXTATTR_CMD_START:
347		error = ufs_extattr_start(mp, p);
348
349		return (error);
350
351	case UFS_EXTATTR_CMD_STOP:
352		return (ufs_extattr_stop(mp, p));
353
354	case UFS_EXTATTR_CMD_ENABLE:
355		error = copyinstr(attrname, local_attrname,
356		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
357		if (error)
358			return (error);
359
360		filename = (char *) arg;
361		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p);
362		flags = FREAD | FWRITE;
363		error = vn_open(&nd, &flags, 0);
364		if (error)
365			return (error);
366
367		vp = nd.ni_vp;
368		VOP_UNLOCK(vp, 0, p);
369
370		ufs_extattr_uepm_lock(ump, p);
371		error = ufs_extattr_enable(ump, local_attrname, vp, p);
372		ufs_extattr_uepm_unlock(ump, p);
373
374		return (error);
375
376	case UFS_EXTATTR_CMD_DISABLE:
377		error = copyinstr(attrname, local_attrname,
378		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
379
380		ufs_extattr_uepm_lock(ump, p);
381		error = ufs_extattr_disable(ump, local_attrname, p);
382		ufs_extattr_uepm_unlock(ump, p);
383
384		return (error);
385
386	default:
387		return (EINVAL);
388	}
389}
390
391/*
392 * Credential check based on process requesting service, and per-attribute
393 * permissions.
394 */
395static int
396ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele,
397    struct ucred *cred, struct proc *p, int access)
398{
399	int	system_namespace;
400
401	system_namespace = (strlen(uele->uele_attrname) >= 1 &&
402	    uele->uele_attrname[0] == '$');
403
404	/*
405	 * Kernel-invoked always succeeds
406	 */
407	if (cred == NULL)
408		return (0);
409
410	/*
411	 * XXX What capability should apply here?
412	 */
413	if (system_namespace)
414		return (suser_xxx(cred, p, PRISON_ROOT));
415	else
416		return (VOP_ACCESS(vp, access, cred, p));
417}
418
419/*
420 * Vnode operating to retrieve a named extended attribute
421 */
422int
423ufs_vop_getextattr(struct vop_getextattr_args *ap)
424/*
425vop_getextattr {
426        IN struct vnode *a_vp;
427        IN const char *a_name;
428        INOUT struct uio *a_uio;
429        IN struct ucred *a_cred;
430        IN struct proc *a_p;
431};
432*/
433{
434	struct mount	*mp = ap->a_vp->v_mount;
435	struct ufsmount	*ump = VFSTOUFS(mp);
436	int	error;
437
438	ufs_extattr_uepm_lock(ump, ap->a_p);
439
440	error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred,
441	    ap->a_p);
442
443	ufs_extattr_uepm_unlock(ump, ap->a_p);
444
445	return (error);
446}
447
448/*
449 * Real work associated with retrieving a named attribute--assumes that
450 * the attribute lock has already been grabbed.
451 */
452static int
453ufs_extattr_get(struct vnode *vp, const char *name, struct uio *uio,
454    struct ucred *cred, struct proc *p)
455{
456	struct ufs_extattr_list_entry	*attribute;
457	struct ufs_extattr_header	ueh;
458	struct iovec	local_aiov;
459	struct uio	local_aio;
460	struct mount	*mp = vp->v_mount;
461	struct ufsmount	*ump = VFSTOUFS(mp);
462	struct inode	*ip = VTOI(vp);
463	off_t	base_offset;
464	size_t	size, old_size;
465	int	error = 0;
466
467	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
468		return (EOPNOTSUPP);
469
470	if (strlen(name) == 0 || (strlen(name) == 1 && name[0] == '$')) {
471		/* XXX retrieve attribute lists */
472		return (EINVAL);
473	}
474
475	attribute = ufs_extattr_find_attr(ump, name);
476	if (!attribute)
477		return (ENOENT);
478
479	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IREAD)))
480		return (error);
481
482	/*
483	 * Allow only offsets of zero to encourage the read/replace
484	 * extended attribute semantic.  Otherwise we can't guarantee
485	 * atomicity, as we don't provide locks for extended
486	 * attributes.
487	 */
488	if (uio->uio_offset != 0)
489		return (ENXIO);
490
491	/*
492	 * Find base offset of header in file based on file header size, and
493	 * data header size + maximum data size, indexed by inode number
494	 */
495	base_offset = sizeof(struct ufs_extattr_fileheader) +
496	    ip->i_number * (sizeof(struct ufs_extattr_header) +
497	    attribute->uele_fileheader.uef_size);
498
499	/*
500	 * Read in the data header to see if the data is defined, and if so
501	 * how much.
502	 */
503	bzero(&ueh, sizeof(struct ufs_extattr_header));
504	local_aiov.iov_base = (caddr_t) &ueh;
505	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
506	local_aio.uio_iov = &local_aiov;
507	local_aio.uio_iovcnt = 1;
508	local_aio.uio_rw = UIO_READ;
509	local_aio.uio_segflg = UIO_SYSSPACE;
510	local_aio.uio_procp = p;
511	local_aio.uio_offset = base_offset;
512	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
513
514	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ);
515	vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_NOPAUSE |
516	    LK_RETRY, p);
517
518	error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0,
519	    ump->um_extattr.uepm_ucred);
520	if (error)
521		goto vopunlock_exit;
522
523	/* defined? */
524	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
525		error = ENOENT;
526		goto vopunlock_exit;
527	}
528
529	/* valid for the current inode generation? */
530	if (ueh.ueh_i_gen != ip->i_gen) {
531		/*
532		 * The inode itself has a different generation number
533		 * than the attribute data.  For now, the best solution
534		 * is to coerce this to undefined, and let it get cleaned
535		 * up by the next write or extattrctl clean.
536		 */
537		printf("ufs_extattr: inode number inconsistency (%d, %d)\n",
538		    ueh.ueh_i_gen, ip->i_gen);
539		error = ENOENT;
540		goto vopunlock_exit;
541	}
542
543	/* local size consistency check */
544	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
545		error = ENXIO;
546		goto vopunlock_exit;
547	}
548
549	if (ueh.ueh_len < uio->uio_offset) {
550		error = 0;
551		goto vopunlock_exit;
552	}
553
554	/* allow for offset into the attr data */
555	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
556
557	/*
558	 * Figure out maximum to transfer -- use buffer size and local data
559	 * limit.
560	 */
561	size = MIN(uio->uio_resid, ueh.ueh_len);
562	old_size = uio->uio_resid;
563	uio->uio_resid = size;
564
565	error = VOP_READ(attribute->uele_backing_vnode, uio, 0,
566	    ump->um_extattr.uepm_ucred);
567	if (error) {
568		uio->uio_offset = 0;
569		goto vopunlock_exit;
570	}
571
572	uio->uio_offset = 0;
573	uio->uio_resid = old_size - (size - uio->uio_resid);
574
575vopunlock_exit:
576	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
577
578	return (error);
579}
580
581/*
582 * Vnode operation to set a named attribute
583 */
584int
585ufs_vop_setextattr(struct vop_setextattr_args *ap)
586/*
587vop_setextattr {
588        IN struct vnode *a_vp;
589        IN const char *a_name;
590        INOUT struct uio *a_uio;
591        IN struct ucred *a_cred;
592        IN struct proc *a_p;
593};
594*/
595{
596	struct mount	*mp = ap->a_vp->v_mount;
597	struct ufsmount	*ump = VFSTOUFS(mp);
598
599	int	error;
600
601	ufs_extattr_uepm_lock(ump, ap->a_p);
602
603	if (ap->a_uio)
604		error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio,
605		    ap->a_cred, ap->a_p);
606	else
607		error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred,
608		    ap->a_p);
609
610	ufs_extattr_uepm_unlock(ump, ap->a_p);
611
612	return (error);
613}
614
615/*
616 * Real work associated with setting a vnode's extended attributes;
617 * assumes that the attribute lock has already been grabbed.
618 */
619static int
620ufs_extattr_set(struct vnode *vp, const char *name, struct uio *uio,
621    struct ucred *cred, struct proc *p)
622{
623	struct ufs_extattr_list_entry	*attribute;
624	struct ufs_extattr_header	ueh;
625	struct iovec	local_aiov;
626	struct uio	local_aio;
627	struct mount	*mp = vp->v_mount;
628	struct ufsmount	*ump = VFSTOUFS(mp);
629	struct inode	*ip = VTOI(vp);
630	off_t	base_offset;
631
632	int	error = 0;
633
634	if (vp->v_mount->mnt_flag & MNT_RDONLY)
635		return (EROFS);
636	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
637		return (EOPNOTSUPP);
638	if (!ufs_extattr_valid_attrname(name))
639		return (EINVAL);
640
641	attribute = ufs_extattr_find_attr(ump, name);
642	if (!attribute)
643		return (ENOENT);
644
645	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
646		return (error);
647
648	/*
649	 * Early rejection of invalid offsets/lengths
650	 * Reject: any offset but 0 (replace)
651	 *         Any size greater than attribute size limit
652 	 */
653	if (uio->uio_offset != 0 ||
654	    uio->uio_resid > attribute->uele_fileheader.uef_size)
655		return (ENXIO);
656
657	/*
658	 * Find base offset of header in file based on file header size, and
659	 * data header size + maximum data size, indexed by inode number
660	 */
661	base_offset = sizeof(struct ufs_extattr_fileheader) +
662	    ip->i_number * (sizeof(struct ufs_extattr_header) +
663	    attribute->uele_fileheader.uef_size);
664
665	/*
666	 * Write out a data header for the data
667	 */
668	ueh.ueh_len = uio->uio_resid;
669	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
670	ueh.ueh_i_gen = ip->i_gen;
671	local_aiov.iov_base = (caddr_t) &ueh;
672	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
673	local_aio.uio_iov = &local_aiov;
674	local_aio.uio_iovcnt = 1;
675	local_aio.uio_rw = UIO_WRITE;
676	local_aio.uio_segflg = UIO_SYSSPACE;
677	local_aio.uio_procp = p;
678	local_aio.uio_offset = base_offset;
679	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
680
681	/*
682	 * Acquire locks
683	 */
684	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
685
686	/*
687	 * Don't need to get a lock on the backing file if the setattr is
688	 * being applied to the backing file, as the lock is already held
689	 */
690	if (attribute->uele_backing_vnode != vp)
691		vn_lock(attribute->uele_backing_vnode,
692		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
693
694	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
695	    ump->um_extattr.uepm_ucred);
696	if (error)
697		goto vopunlock_exit;
698
699	if (local_aio.uio_resid != 0) {
700		error = ENXIO;
701		goto vopunlock_exit;
702	}
703
704	/*
705	 * Write out user data
706	 */
707	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
708
709	error = VOP_WRITE(attribute->uele_backing_vnode, uio, IO_SYNC,
710	    ump->um_extattr.uepm_ucred);
711
712vopunlock_exit:
713	uio->uio_offset = 0;
714
715	if (attribute->uele_backing_vnode != vp)
716		VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
717
718	return (error);
719}
720
721/*
722 * Real work associated with removing an extended attribute from a vnode.
723 * Assumes the attribute lock has already been grabbed.
724 */
725static int
726ufs_extattr_rm(struct vnode *vp, const char *name, struct ucred *cred,
727    struct proc *p)
728{
729	struct ufs_extattr_list_entry	*attribute;
730	struct ufs_extattr_header	ueh;
731	struct iovec	local_aiov;
732	struct uio	local_aio;
733	struct mount	*mp = vp->v_mount;
734	struct ufsmount	*ump = VFSTOUFS(mp);
735	struct inode	*ip = VTOI(vp);
736	off_t	base_offset;
737	int	error = 0;
738
739	if (vp->v_mount->mnt_flag & MNT_RDONLY)
740		return (EROFS);
741	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
742		return (EOPNOTSUPP);
743	if (!ufs_extattr_valid_attrname(name))
744		return (EINVAL);
745
746	attribute = ufs_extattr_find_attr(ump, name);
747	if (!attribute)
748		return (ENOENT);
749
750	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
751		return (error);
752
753	/*
754	 * Find base offset of header in file based on file header size, and
755	 * data header size + maximum data size, indexed by inode number
756	 */
757	base_offset = sizeof(struct ufs_extattr_fileheader) +
758	    ip->i_number * (sizeof(struct ufs_extattr_header) +
759	    attribute->uele_fileheader.uef_size);
760
761	/*
762	 * Read in the data header to see if the data is defined
763	 */
764	bzero(&ueh, sizeof(struct ufs_extattr_header));
765
766	local_aiov.iov_base = (caddr_t) &ueh;
767	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
768	local_aio.uio_iov = &local_aiov;
769	local_aio.uio_iovcnt = 1;
770	local_aio.uio_rw = UIO_READ;
771	local_aio.uio_segflg = UIO_SYSSPACE;
772	local_aio.uio_procp = p;
773	local_aio.uio_offset = base_offset;
774	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
775
776	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
777
778	/*
779	 * Don't need to get the lock on the backing vnode if the vnode we're
780	 * modifying is it, as we already hold the lock.
781	 */
782	if (attribute->uele_backing_vnode != vp)
783		vn_lock(attribute->uele_backing_vnode,
784		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
785
786	error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0,
787	    ump->um_extattr.uepm_ucred);
788	if (error)
789		goto vopunlock_exit;
790
791	/* defined? */
792	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
793		error = ENOENT;
794		goto vopunlock_exit;
795	}
796
797	/* flag it as not in use */
798	ueh.ueh_flags = 0;
799
800	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
801	    ump->um_extattr.uepm_ucred);
802	if (error)
803		goto vopunlock_exit;
804
805	if (local_aio.uio_resid != 0)
806		error = ENXIO;
807
808vopunlock_exit:
809	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
810
811	return (error);
812}
813
814/*
815 * Called by UFS when an inode is no longer active and should have its
816 * attributes stripped.
817 */
818void
819ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p)
820{
821	struct ufs_extattr_list_entry	*uele;
822	struct mount	*mp = vp->v_mount;
823	struct ufsmount	*ump = VFSTOUFS(mp);
824
825	ufs_extattr_uepm_lock(ump, p);
826
827	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
828		ufs_extattr_uepm_unlock(ump, p);
829		return;
830	}
831
832	for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL;
833	    uele = uele->uele_entries.le_next)
834		ufs_extattr_rm(vp, uele->uele_attrname, 0, p);
835
836	ufs_extattr_uepm_unlock(ump, p);
837}
838