ufs_extattr.c revision 59241
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 59241 2000-04-15 03:34:27Z rwatson $
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_credcheck(struct ufs_extattr_list_entry *uele,
53    u_int32_t fowner, struct ucred *cred, struct proc *p, int access);
54static int	ufs_extattr_enable(struct ufsmount *ump, char *attrname,
55    struct vnode *backing_vnode, struct proc *p);
56static int	ufs_extattr_disable(struct ufsmount *ump, char *attrname,
57    struct proc *p);
58static int	ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio,
59    struct ucred *cred, struct proc *p);
60static int	ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio,
61    struct ucred *cred, struct proc *p);
62static int	ufs_extattr_rm(struct vnode *vp, char *name,
63    struct ucred *cred, struct proc *p);
64
65/*
66 * Per-FS attribute lock protecting attribute operations
67 * XXX Right now there is a lot of lock contention due to having a single
68 * lock per-FS; really, this should be far more fine-grained.
69 */
70static void
71ufs_extattr_uepm_lock(struct ufsmount *ump, struct proc *p)
72{
73
74	/* ideally, LK_CANRECURSE would not be used, here */
75	lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY |
76	    LK_CANRECURSE, 0, p);
77}
78
79static void
80ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p)
81{
82
83	lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, p);
84}
85
86/*
87 * Locate an attribute given a name and mountpoint.
88 * Must be holding uepm lock for the mount point.
89 */
90static struct ufs_extattr_list_entry *
91ufs_exttatr_find_attr(struct ufsmount *ump, char *attrname)
92{
93        struct ufs_extattr_list_entry   *search_attribute;
94
95        for (search_attribute = ump->um_extattr.uepm_list.lh_first;
96	    search_attribute;
97	    search_attribute = search_attribute->uele_entries.le_next) {
98                if (!(strncmp(attrname, search_attribute->uele_attrname,
99                    UFS_EXTATTR_MAXEXTATTRNAME))) {
100                        return (search_attribute);
101                }
102        }
103
104        return (0);
105}
106
107/*
108 * Initialize per-FS structures supporting extended attributes.  Do not
109 * start extended attributes yet.
110 */
111void
112ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
113{
114
115	uepm->uepm_flags = 0;
116
117	LIST_INIT(&uepm->uepm_list);
118	/* XXX is PVFS right, here? */
119	lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0);
120	uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
121}
122
123/*
124 * Start extended attribute support on an FS
125 */
126int
127ufs_extattr_start(struct mount *mp, struct proc *p)
128{
129	struct ufsmount	*ump;
130	int	error = 0;
131
132	ump = VFSTOUFS(mp);
133
134	ufs_extattr_uepm_lock(ump, p);
135
136	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
137		error = EOPNOTSUPP;
138		goto unlock;
139	}
140	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
141		error = EBUSY;
142		goto unlock;
143	}
144
145	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
146
147	crhold(p->p_ucred);
148	ump->um_extattr.uepm_ucred = p->p_ucred;
149
150unlock:
151	ufs_extattr_uepm_unlock(ump, p);
152
153	return (error);
154}
155
156/*
157 * Stop extended attribute support on an FS
158 */
159int
160ufs_extattr_stop(struct mount *mp, struct proc *p)
161{
162	struct ufs_extattr_list_entry	*uele;
163	struct ufsmount	*ump = VFSTOUFS(mp);
164	int	error = 0;
165
166	ufs_extattr_uepm_lock(ump, p);
167
168	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
169		error = EOPNOTSUPP;
170		goto unlock;
171	}
172
173        while (ump->um_extattr.uepm_list.lh_first != NULL) {
174                uele = ump->um_extattr.uepm_list.lh_first;
175		ufs_extattr_disable(ump, uele->uele_attrname, p);
176        }
177
178	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
179
180	crfree(ump->um_extattr.uepm_ucred);
181	ump->um_extattr.uepm_ucred = NULL;
182
183unlock:
184	ufs_extattr_uepm_unlock(ump, p);
185
186	return (error);
187}
188
189/*
190 * Enable a named attribute on the specified file system; provide a
191 * backing vnode to hold the attribute data.
192 */
193static int
194ufs_extattr_enable(struct ufsmount *ump, char *attrname,
195    struct vnode *backing_vnode, struct proc *p)
196{
197	struct ufs_extattr_list_entry	*attribute;
198	struct iovec	aiov;
199	struct uio	auio;
200	int	error = 0;
201
202	if (backing_vnode->v_type != VREG)
203		return (EINVAL);
204
205	MALLOC(attribute, struct ufs_extattr_list_entry *,
206	    sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK);
207	if (attribute == NULL)
208		return (ENOMEM);
209
210	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
211		error = EOPNOTSUPP;
212		goto free_exit;
213	}
214
215	if (ufs_exttatr_find_attr(ump, attrname)) {
216		error = EOPNOTSUPP;
217		goto free_exit;
218	}
219
220	strncpy(attribute->uele_attrname, attrname, UFS_EXTATTR_MAXEXTATTRNAME);
221	bzero(&attribute->uele_fileheader,
222	    sizeof(struct ufs_extattr_fileheader));
223
224	attribute->uele_backing_vnode = backing_vnode;
225	backing_vnode->v_flag |= VSYSTEM;
226
227	auio.uio_iov = &aiov;
228	auio.uio_iovcnt = 1;
229	aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
230	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
231	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
232	auio.uio_offset = (off_t) 0;
233	auio.uio_segflg = UIO_SYSSPACE;
234	auio.uio_rw = UIO_READ;
235	auio.uio_procp = (struct proc *) p;
236
237	VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE);
238	vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p);
239	error = VOP_READ(backing_vnode, &auio, 0, ump->um_extattr.uepm_ucred);
240	VOP_UNLOCK(backing_vnode, 0, p);
241
242	if (error) {
243		goto free_exit;
244	}
245
246	if (auio.uio_resid != 0) {
247		printf("ufs_extattr_enable: malformed attribute header\n");
248		error = EINVAL;
249		goto free_exit;
250	}
251
252	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries);
253
254	return (0);
255
256free_exit:
257	FREE(attribute, M_UFS_EXTATTR);
258	return (error);
259}
260
261/*
262 * Disable extended attribute support on an FS
263 */
264static int
265ufs_extattr_disable(struct ufsmount *ump, char *attrname, struct proc *p)
266{
267	struct ufs_extattr_list_entry	*uele;
268	int	error = 0;
269
270	uele = ufs_exttatr_find_attr(ump, attrname);
271	if (!uele)
272		return (ENOENT);
273
274	LIST_REMOVE(uele, uele_entries);
275
276	uele->uele_backing_vnode->v_flag &= ~VSYSTEM;
277        error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p);
278
279	FREE(uele, M_UFS_EXTATTR);
280
281	return (error);
282}
283
284/*
285 * VFS call to manage extended attributes in UFS
286 * attrname, arg are userspace pointers from the syscall
287 */
288int
289ufs_extattrctl(struct mount *mp, int cmd, char *attrname,
290	       caddr_t arg, struct proc *p)
291{
292	struct nameidata	nd;
293	struct ufsmount	*ump = VFSTOUFS(mp);
294	struct vnode	*vp;
295	char	local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* inc null */
296	char	*filename;
297	int	error, len;
298
299	if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0)))
300		return (error);
301
302	switch(cmd) {
303	case UFS_EXTATTR_CMD_START:
304		error = ufs_extattr_start(mp, p);
305
306		return (error);
307
308	case UFS_EXTATTR_CMD_STOP:
309		return (ufs_extattr_stop(mp, p));
310
311	case UFS_EXTATTR_CMD_ENABLE:
312		error = copyinstr(attrname, local_attrname,
313		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
314		if (error)
315			return (error);
316
317		filename = (char *) arg;
318		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p);
319		error = vn_open(&nd, FREAD|FWRITE, 0);
320		if (error)
321			return (error);
322
323		vp = nd.ni_vp;
324		VOP_UNLOCK(vp, 0, p);
325
326		ufs_extattr_uepm_lock(ump, p);
327		error = ufs_extattr_enable(ump, local_attrname, vp, p);
328		ufs_extattr_uepm_unlock(ump, p);
329
330		return (error);
331
332	case UFS_EXTATTR_CMD_DISABLE:
333		error = copyinstr(attrname, local_attrname,
334		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
335
336		ufs_extattr_uepm_lock(ump, p);
337		error = ufs_extattr_disable(ump, local_attrname, p);
338		ufs_extattr_uepm_unlock(ump, p);
339
340		return (error);
341
342	default:
343		return (EINVAL);
344	}
345}
346
347/*
348 * Credential check based on process requesting service, and per-attribute
349 * permissions.
350 */
351static int
352ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, u_int32_t fowner,
353		      struct ucred *cred, struct proc *p, int access)
354{
355	u_int	uef_perm;
356
357	switch(access) {
358	case IREAD:
359		uef_perm = uele->uele_fileheader.uef_read_perm;
360		break;
361	case IWRITE:
362		uef_perm = uele->uele_fileheader.uef_write_perm;
363		break;
364	default:
365		return (EACCES);
366	}
367
368	/* Kernel sponsoring request does so without passing a cred */
369	if (!cred)
370		return (0);
371
372	/* XXX there might eventually be a capability check here */
373
374	/* If it's set to root-only, check for suser(p) */
375	if (uef_perm == UFS_EXTATTR_PERM_ROOT && !suser(p))
376		return (0);
377
378	/* Allow the owner if appropriate */
379	if (uef_perm == UFS_EXTATTR_PERM_OWNER && cred->cr_uid == fowner)
380		return (0);
381
382	/* Allow anyone if appropriate */
383	if (uef_perm == UFS_EXTATTR_PERM_ANYONE)
384		return (0);
385
386	return (EACCES);
387}
388
389/*
390 * Vnode operating to retrieve a named extended attribute
391 */
392int
393ufs_vop_getextattr(struct vop_getextattr_args *ap)
394/*
395vop_getextattr {
396        IN struct vnode *a_vp;
397        IN char *a_name;
398        INOUT struct uio *a_uio;
399        IN struct ucred *a_cred;
400        IN struct proc *a_p;
401};
402*/
403{
404	struct mount	*mp = ap->a_vp->v_mount;
405	struct ufsmount	*ump = VFSTOUFS(mp);
406	int	error;
407
408	ufs_extattr_uepm_lock(ump, ap->a_p);
409
410	error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred,
411	    ap->a_p);
412
413	ufs_extattr_uepm_unlock(ump, ap->a_p);
414
415	return (error);
416}
417
418/*
419 * Real work associated with retrieving a named attribute--assumes that
420 * the attribute lock has already been grabbed.
421 */
422static int
423ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio,
424    struct ucred *cred, struct proc *p)
425{
426	struct ufs_extattr_list_entry	*attribute;
427	struct ufs_extattr_header	ueh;
428	struct iovec	local_aiov;
429	struct uio	local_aio;
430	struct mount	*mp = vp->v_mount;
431	struct ufsmount	*ump = VFSTOUFS(mp);
432	struct inode	*ip = VTOI(vp);
433	off_t	base_offset, old_offset, offset;
434	size_t	size, old_size;
435	int	error = 0;
436
437	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
438		return (EOPNOTSUPP);
439
440	attribute = ufs_exttatr_find_attr(ump, name);
441	if (!attribute)
442		return (ENOENT);
443
444	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p,
445	    IREAD)))
446		return (error);
447
448	/*
449	 * Early rejection of offsets that are invalid
450	 */
451	if (uio->uio_offset >= attribute->uele_fileheader.uef_size ||
452	    uio->uio_offset < 0)
453		return (ENXIO);
454
455	/*
456	 * Find base offset of header in file based on file header size, and
457	 * data header size + maximum data size, indexed by inode number
458	 */
459	base_offset = sizeof(struct ufs_extattr_fileheader) +
460	    ip->i_number * (sizeof(struct ufs_extattr_header) +
461	    attribute->uele_fileheader.uef_size);
462
463	/*
464	 * Read in the data header to see if the data is defined, and if so
465	 * how much.
466	 */
467	bzero(&ueh, sizeof(struct ufs_extattr_header));
468	local_aiov.iov_base = (caddr_t) &ueh;
469	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
470	local_aio.uio_iov = &local_aiov;
471	local_aio.uio_iovcnt = 1;
472	local_aio.uio_rw = UIO_READ;
473	local_aio.uio_segflg = UIO_SYSSPACE;
474	local_aio.uio_procp = p;
475	local_aio.uio_offset = base_offset;
476	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
477
478	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ);
479	vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_NOPAUSE |
480	    LK_RETRY, p);
481
482	error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0,
483	    ump->um_extattr.uepm_ucred);
484	if (error)
485		goto vopunlock_exit;
486
487	/* defined? */
488	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
489		error = ENOENT;
490		goto vopunlock_exit;
491	}
492
493	/* local size consistency check */
494	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
495		error = ENXIO;
496		goto vopunlock_exit;
497	}
498
499	if (ueh.ueh_len < uio->uio_offset) {
500		error = 0;
501		goto vopunlock_exit;
502	}
503
504	/* allow for offset into the attr data */
505	offset = base_offset + sizeof(struct ufs_extattr_header) +
506	    uio->uio_offset;
507
508	/*
509	 * Figure out maximum to transfer -- use buffer size and local data
510	 * limit.
511	 */
512	size = MIN(uio->uio_resid, ueh.ueh_len - uio->uio_offset);
513
514	old_offset = uio->uio_offset;
515	uio->uio_offset = offset;
516	old_size = uio->uio_resid;
517	uio->uio_resid = size;
518
519	error = VOP_READ(attribute->uele_backing_vnode, uio, 0,
520	    ump->um_extattr.uepm_ucred);
521	if (error) {
522		uio->uio_offset = old_offset;
523		goto vopunlock_exit;
524	}
525
526	uio->uio_offset = old_offset;
527	uio->uio_resid = old_size - (size - uio->uio_resid);
528
529vopunlock_exit:
530	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
531
532	return (error);
533}
534
535/*
536 * Vnode operation to set a named attribute
537 */
538int
539ufs_vop_setextattr(struct vop_setextattr_args *ap)
540/*
541vop_setextattr {
542        IN struct vnode *a_vp;
543        IN char *a_name;
544        INOUT struct uio *a_uio;
545        IN struct ucred *a_cred;
546        IN struct proc *a_p;
547};
548*/
549{
550	struct mount	*mp = ap->a_vp->v_mount;
551	struct ufsmount	*ump = VFSTOUFS(mp);
552
553	int	error;
554
555	ufs_extattr_uepm_lock(ump, ap->a_p);
556
557	if (ap->a_uio)
558		error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio,
559		    ap->a_cred, ap->a_p);
560	else
561		error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred,
562		    ap->a_p);
563
564	ufs_extattr_uepm_unlock(ump, ap->a_p);
565
566	return (error);
567}
568
569/*
570 * Real work associated with setting a vnode's extended attributes;
571 * assumes that the attribute lock has already been grabbed.
572 */
573static int
574ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio,
575    struct ucred *cred, struct proc *p)
576{
577	struct ufs_extattr_list_entry	*attribute;
578	struct ufs_extattr_header	ueh;
579	struct iovec	local_aiov;
580	struct uio	local_aio;
581	struct mount	*mp = vp->v_mount;
582	struct ufsmount	*ump = VFSTOUFS(mp);
583	struct inode	*ip = VTOI(vp);
584	off_t	base_offset;
585
586	int	error = 0;
587
588	if (vp->v_mount->mnt_flag & MNT_RDONLY)
589		return (EROFS);
590
591	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
592		return (EOPNOTSUPP);
593
594	attribute = ufs_exttatr_find_attr(ump, name);
595	if (!attribute)
596		return (ENOENT);
597
598	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred,
599	    p, IWRITE)))
600		return (error);
601
602	/*
603	 * Early rejection of invalid offsets/lengths
604	 * Reject: any offset but 0 (replace)
605	 *         Any size greater than attribute size limit
606 	 */
607	if (uio->uio_offset != 0 ||
608	    uio->uio_resid > attribute->uele_fileheader.uef_size)
609		return (ENXIO);
610
611	/*
612	 * Find base offset of header in file based on file header size, and
613	 * data header size + maximum data size, indexed by inode number
614	 */
615	base_offset = sizeof(struct ufs_extattr_fileheader) +
616	    ip->i_number * (sizeof(struct ufs_extattr_header) +
617	    attribute->uele_fileheader.uef_size);
618
619	/*
620	 * Write out a data header for the data
621	 */
622	ueh.ueh_len = uio->uio_resid;
623	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
624	local_aiov.iov_base = (caddr_t) &ueh;
625	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
626	local_aio.uio_iov = &local_aiov;
627	local_aio.uio_iovcnt = 1;
628	local_aio.uio_rw = UIO_WRITE;
629	local_aio.uio_segflg = UIO_SYSSPACE;
630	local_aio.uio_procp = p;
631	local_aio.uio_offset = base_offset;
632	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
633
634	/*
635	 * Acquire locks
636	 */
637	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
638
639	/*
640	 * Don't need to get a lock on the backing file if the setattr is
641	 * being applied to the backing file, as the lock is already held
642	 */
643	if (attribute->uele_backing_vnode != vp)
644		vn_lock(attribute->uele_backing_vnode,
645		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
646
647	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
648	    ump->um_extattr.uepm_ucred);
649	if (error)
650		goto vopunlock_exit;
651
652	if (local_aio.uio_resid != 0)
653		error = ENXIO;
654		goto vopunlock_exit;
655
656	/*
657	 * Write out user data
658	 */
659	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
660
661	error = VOP_WRITE(attribute->uele_backing_vnode, uio, 0,
662	    ump->um_extattr.uepm_ucred);
663
664vopunlock_exit:
665	uio->uio_offset = 0;
666	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
667
668	return (error);
669}
670
671/*
672 * Real work associated with removing an extended attribute from a vnode.
673 * Assumes the attribute lock has already been grabbed.
674 */
675static int
676ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred,
677    struct proc *p)
678{
679	struct ufs_extattr_list_entry	*attribute;
680	struct ufs_extattr_header	ueh;
681	struct iovec	local_aiov;
682	struct uio	local_aio;
683	struct mount	*mp = vp->v_mount;
684	struct ufsmount	*ump = VFSTOUFS(mp);
685	struct inode	*ip = VTOI(vp);
686	off_t	base_offset;
687	int	error = 0;
688
689	if (vp->v_mount->mnt_flag & MNT_RDONLY)
690		return (EROFS);
691
692	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
693		return (EOPNOTSUPP);
694
695	attribute = ufs_exttatr_find_attr(ump, name);
696	if (!attribute)
697		return (ENOENT);
698
699	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p,
700	    IWRITE)))
701		return (error);
702
703	/*
704	 * Find base offset of header in file based on file header size, and
705	 * data header size + maximum data size, indexed by inode number
706	 */
707	base_offset = sizeof(struct ufs_extattr_fileheader) +
708	    ip->i_number * (sizeof(struct ufs_extattr_header) +
709	    attribute->uele_fileheader.uef_size);
710
711	/*
712	 * Read in the data header to see if the data is defined
713	 */
714	bzero(&ueh, sizeof(struct ufs_extattr_header));
715
716	local_aiov.iov_base = (caddr_t) &ueh;
717	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
718	local_aio.uio_iov = &local_aiov;
719	local_aio.uio_iovcnt = 1;
720	local_aio.uio_rw = UIO_READ;
721	local_aio.uio_segflg = UIO_SYSSPACE;
722	local_aio.uio_procp = p;
723	local_aio.uio_offset = base_offset;
724	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
725
726	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
727
728	/*
729	 * Don't need to get the lock on the backing vnode if the vnode we're
730	 * modifying is it, as we already hold the lock.
731	 */
732	if (attribute->uele_backing_vnode != vp)
733		vn_lock(attribute->uele_backing_vnode,
734		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
735
736	error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0,
737	    ump->um_extattr.uepm_ucred);
738	if (error)
739		goto vopunlock_exit;
740
741	/* defined? */
742	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
743		error = ENOENT;
744		goto vopunlock_exit;
745	}
746
747	/* flag it as not in use */
748	ueh.ueh_flags = 0;
749
750	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
751	    ump->um_extattr.uepm_ucred);
752	if (error)
753		goto vopunlock_exit;
754
755	if (local_aio.uio_resid != 0)
756		error = ENXIO;
757
758vopunlock_exit:
759	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
760
761	return (error);
762}
763
764/*
765 * Called by UFS when an inode is no longer active and should have its
766 * attributes stripped.
767 */
768void
769ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p)
770{
771	struct ufs_extattr_list_entry	*uele;
772	struct mount	*mp = vp->v_mount;
773	struct ufsmount	*ump = VFSTOUFS(mp);
774
775	ufs_extattr_uepm_lock(ump, p);
776
777	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
778		ufs_extattr_uepm_unlock(ump, p);
779		return;
780	}
781
782	for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL;
783	    uele = uele->uele_entries.le_next)
784		ufs_extattr_rm(vp, uele->uele_attrname, 0, p);
785
786	ufs_extattr_uepm_unlock(ump, p);
787}
788