ufs_extattr.c revision 61281
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 61281 2000-06-05 14:22:51Z 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
226	auio.uio_iov = &aiov;
227	auio.uio_iovcnt = 1;
228	aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
229	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
230	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
231	auio.uio_offset = (off_t) 0;
232	auio.uio_segflg = UIO_SYSSPACE;
233	auio.uio_rw = UIO_READ;
234	auio.uio_procp = (struct proc *) p;
235
236	VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE);
237	vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p);
238	error = VOP_READ(backing_vnode, &auio, 0, ump->um_extattr.uepm_ucred);
239	VOP_UNLOCK(backing_vnode, 0, p);
240
241	if (error) {
242		goto free_exit;
243	}
244
245	if (auio.uio_resid != 0) {
246		printf("ufs_extattr_enable: malformed attribute header\n");
247		error = EINVAL;
248		goto free_exit;
249	}
250
251	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
252		printf("ufs_extattr_enable: invalid attribute header magic\n");
253		error = EINVAL;
254		goto free_exit;
255	}
256
257	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
258		printf("ufs_extattr_enable: incorrect attribute header "
259		    "version\n");
260		error = EINVAL;
261		goto free_exit;
262	}
263
264	backing_vnode->v_flag |= VSYSTEM;
265	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries);
266
267	return (0);
268
269free_exit:
270	FREE(attribute, M_UFS_EXTATTR);
271	return (error);
272}
273
274/*
275 * Disable extended attribute support on an FS
276 */
277static int
278ufs_extattr_disable(struct ufsmount *ump, char *attrname, struct proc *p)
279{
280	struct ufs_extattr_list_entry	*uele;
281	int	error = 0;
282
283	uele = ufs_exttatr_find_attr(ump, attrname);
284	if (!uele)
285		return (ENOENT);
286
287	LIST_REMOVE(uele, uele_entries);
288
289	uele->uele_backing_vnode->v_flag &= ~VSYSTEM;
290        error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p);
291
292	FREE(uele, M_UFS_EXTATTR);
293
294	return (error);
295}
296
297/*
298 * VFS call to manage extended attributes in UFS
299 * attrname, arg are userspace pointers from the syscall
300 */
301int
302ufs_extattrctl(struct mount *mp, int cmd, char *attrname,
303	       caddr_t arg, struct proc *p)
304{
305	struct nameidata	nd;
306	struct ufsmount	*ump = VFSTOUFS(mp);
307	struct vnode	*vp;
308	char	local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* inc null */
309	char	*filename;
310	int	error, len;
311
312	if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0)))
313		return (error);
314
315	switch(cmd) {
316	case UFS_EXTATTR_CMD_START:
317		error = ufs_extattr_start(mp, p);
318
319		return (error);
320
321	case UFS_EXTATTR_CMD_STOP:
322		return (ufs_extattr_stop(mp, p));
323
324	case UFS_EXTATTR_CMD_ENABLE:
325		error = copyinstr(attrname, local_attrname,
326		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
327		if (error)
328			return (error);
329
330		filename = (char *) arg;
331		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p);
332		error = vn_open(&nd, FREAD|FWRITE, 0);
333		if (error)
334			return (error);
335
336		vp = nd.ni_vp;
337		VOP_UNLOCK(vp, 0, p);
338
339		ufs_extattr_uepm_lock(ump, p);
340		error = ufs_extattr_enable(ump, local_attrname, vp, p);
341		ufs_extattr_uepm_unlock(ump, p);
342
343		return (error);
344
345	case UFS_EXTATTR_CMD_DISABLE:
346		error = copyinstr(attrname, local_attrname,
347		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
348
349		ufs_extattr_uepm_lock(ump, p);
350		error = ufs_extattr_disable(ump, local_attrname, p);
351		ufs_extattr_uepm_unlock(ump, p);
352
353		return (error);
354
355	default:
356		return (EINVAL);
357	}
358}
359
360/*
361 * Credential check based on process requesting service, and per-attribute
362 * permissions.
363 */
364static int
365ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, u_int32_t fowner,
366		      struct ucred *cred, struct proc *p, int access)
367{
368	u_int	uef_perm;
369
370	switch(access) {
371	case IREAD:
372		uef_perm = uele->uele_fileheader.uef_read_perm;
373		break;
374	case IWRITE:
375		uef_perm = uele->uele_fileheader.uef_write_perm;
376		break;
377	default:
378		return (EACCES);
379	}
380
381	/* Kernel sponsoring request does so without passing a cred */
382	if (!cred)
383		return (0);
384
385	/* XXX there might eventually be a capability check here */
386
387	/* If it's set to root-only, check for suser(p) */
388	if (uef_perm == UFS_EXTATTR_PERM_ROOT && !suser(p))
389		return (0);
390
391	/* Allow the owner if appropriate */
392	if (uef_perm == UFS_EXTATTR_PERM_OWNER && cred->cr_uid == fowner)
393		return (0);
394
395	/* Allow anyone if appropriate */
396	if (uef_perm == UFS_EXTATTR_PERM_ANYONE)
397		return (0);
398
399	return (EACCES);
400}
401
402/*
403 * Vnode operating to retrieve a named extended attribute
404 */
405int
406ufs_vop_getextattr(struct vop_getextattr_args *ap)
407/*
408vop_getextattr {
409        IN struct vnode *a_vp;
410        IN char *a_name;
411        INOUT struct uio *a_uio;
412        IN struct ucred *a_cred;
413        IN struct proc *a_p;
414};
415*/
416{
417	struct mount	*mp = ap->a_vp->v_mount;
418	struct ufsmount	*ump = VFSTOUFS(mp);
419	int	error;
420
421	ufs_extattr_uepm_lock(ump, ap->a_p);
422
423	error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred,
424	    ap->a_p);
425
426	ufs_extattr_uepm_unlock(ump, ap->a_p);
427
428	return (error);
429}
430
431/*
432 * Real work associated with retrieving a named attribute--assumes that
433 * the attribute lock has already been grabbed.
434 */
435static int
436ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio,
437    struct ucred *cred, struct proc *p)
438{
439	struct ufs_extattr_list_entry	*attribute;
440	struct ufs_extattr_header	ueh;
441	struct iovec	local_aiov;
442	struct uio	local_aio;
443	struct mount	*mp = vp->v_mount;
444	struct ufsmount	*ump = VFSTOUFS(mp);
445	struct inode	*ip = VTOI(vp);
446	off_t	base_offset;
447	size_t	size, old_size;
448	int	error = 0;
449
450	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
451		return (EOPNOTSUPP);
452
453	attribute = ufs_exttatr_find_attr(ump, name);
454	if (!attribute)
455		return (ENOENT);
456
457	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p,
458	    IREAD)))
459		return (error);
460
461	/*
462	 * Allow only offsets of zero to encourage the read/replace
463	 * extended attribute semantic.  Otherwise we can't guarantee
464	 * atomicity, as we don't provide locks for extended
465	 * attributes.
466	 */
467	if (uio->uio_offset != 0)
468		return (ENXIO);
469
470	/*
471	 * Find base offset of header in file based on file header size, and
472	 * data header size + maximum data size, indexed by inode number
473	 */
474	base_offset = sizeof(struct ufs_extattr_fileheader) +
475	    ip->i_number * (sizeof(struct ufs_extattr_header) +
476	    attribute->uele_fileheader.uef_size);
477
478	/*
479	 * Read in the data header to see if the data is defined, and if so
480	 * how much.
481	 */
482	bzero(&ueh, sizeof(struct ufs_extattr_header));
483	local_aiov.iov_base = (caddr_t) &ueh;
484	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
485	local_aio.uio_iov = &local_aiov;
486	local_aio.uio_iovcnt = 1;
487	local_aio.uio_rw = UIO_READ;
488	local_aio.uio_segflg = UIO_SYSSPACE;
489	local_aio.uio_procp = p;
490	local_aio.uio_offset = base_offset;
491	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
492
493	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ);
494	vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_NOPAUSE |
495	    LK_RETRY, p);
496
497	error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0,
498	    ump->um_extattr.uepm_ucred);
499	if (error)
500		goto vopunlock_exit;
501
502	/* defined? */
503	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
504		error = ENOENT;
505		goto vopunlock_exit;
506	}
507
508	/* valid for the current inode generation? */
509	if (ueh.ueh_i_gen != ip->i_gen) {
510		/*
511		 * The inode itself has a different generation number
512		 * than the attribute data.  For now, the best solution
513		 * is to coerce this to undefined, and let it get cleaned
514		 * up by the next write or extattrctl clean.
515		 */
516		printf("ufs_extattr: inode number inconsistency (%d, %d)\n",
517		    ueh.ueh_i_gen, ip->i_gen);
518		error = ENOENT;
519		goto vopunlock_exit;
520	}
521
522	/* local size consistency check */
523	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
524		error = ENXIO;
525		goto vopunlock_exit;
526	}
527
528	if (ueh.ueh_len < uio->uio_offset) {
529		error = 0;
530		goto vopunlock_exit;
531	}
532
533	/* allow for offset into the attr data */
534	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
535
536	/*
537	 * Figure out maximum to transfer -- use buffer size and local data
538	 * limit.
539	 */
540	size = MIN(uio->uio_resid, ueh.ueh_len);
541	old_size = uio->uio_resid;
542	uio->uio_resid = size;
543
544	error = VOP_READ(attribute->uele_backing_vnode, uio, 0,
545	    ump->um_extattr.uepm_ucred);
546	if (error) {
547		uio->uio_offset = 0;
548		goto vopunlock_exit;
549	}
550
551	uio->uio_offset = 0;
552	uio->uio_resid = old_size - (size - uio->uio_resid);
553
554vopunlock_exit:
555	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
556
557	return (error);
558}
559
560/*
561 * Vnode operation to set a named attribute
562 */
563int
564ufs_vop_setextattr(struct vop_setextattr_args *ap)
565/*
566vop_setextattr {
567        IN struct vnode *a_vp;
568        IN char *a_name;
569        INOUT struct uio *a_uio;
570        IN struct ucred *a_cred;
571        IN struct proc *a_p;
572};
573*/
574{
575	struct mount	*mp = ap->a_vp->v_mount;
576	struct ufsmount	*ump = VFSTOUFS(mp);
577
578	int	error;
579
580	ufs_extattr_uepm_lock(ump, ap->a_p);
581
582	if (ap->a_uio)
583		error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio,
584		    ap->a_cred, ap->a_p);
585	else
586		error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred,
587		    ap->a_p);
588
589	ufs_extattr_uepm_unlock(ump, ap->a_p);
590
591	return (error);
592}
593
594/*
595 * Real work associated with setting a vnode's extended attributes;
596 * assumes that the attribute lock has already been grabbed.
597 */
598static int
599ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio,
600    struct ucred *cred, struct proc *p)
601{
602	struct ufs_extattr_list_entry	*attribute;
603	struct ufs_extattr_header	ueh;
604	struct iovec	local_aiov;
605	struct uio	local_aio;
606	struct mount	*mp = vp->v_mount;
607	struct ufsmount	*ump = VFSTOUFS(mp);
608	struct inode	*ip = VTOI(vp);
609	off_t	base_offset;
610
611	int	error = 0;
612
613	if (vp->v_mount->mnt_flag & MNT_RDONLY)
614		return (EROFS);
615
616	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
617		return (EOPNOTSUPP);
618
619	attribute = ufs_exttatr_find_attr(ump, name);
620	if (!attribute)
621		return (ENOENT);
622
623	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred,
624	    p, IWRITE)))
625		return (error);
626
627	/*
628	 * Early rejection of invalid offsets/lengths
629	 * Reject: any offset but 0 (replace)
630	 *         Any size greater than attribute size limit
631 	 */
632	if (uio->uio_offset != 0 ||
633	    uio->uio_resid > attribute->uele_fileheader.uef_size)
634		return (ENXIO);
635
636	/*
637	 * Find base offset of header in file based on file header size, and
638	 * data header size + maximum data size, indexed by inode number
639	 */
640	base_offset = sizeof(struct ufs_extattr_fileheader) +
641	    ip->i_number * (sizeof(struct ufs_extattr_header) +
642	    attribute->uele_fileheader.uef_size);
643
644	/*
645	 * Write out a data header for the data
646	 */
647	ueh.ueh_len = uio->uio_resid;
648	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
649	ueh.ueh_i_gen = ip->i_gen;
650	local_aiov.iov_base = (caddr_t) &ueh;
651	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
652	local_aio.uio_iov = &local_aiov;
653	local_aio.uio_iovcnt = 1;
654	local_aio.uio_rw = UIO_WRITE;
655	local_aio.uio_segflg = UIO_SYSSPACE;
656	local_aio.uio_procp = p;
657	local_aio.uio_offset = base_offset;
658	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
659
660	/*
661	 * Acquire locks
662	 */
663	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
664
665	/*
666	 * Don't need to get a lock on the backing file if the setattr is
667	 * being applied to the backing file, as the lock is already held
668	 */
669	if (attribute->uele_backing_vnode != vp)
670		vn_lock(attribute->uele_backing_vnode,
671		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
672
673	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
674	    ump->um_extattr.uepm_ucred);
675	if (error)
676		goto vopunlock_exit;
677
678	if (local_aio.uio_resid != 0) {
679		error = ENXIO;
680		goto vopunlock_exit;
681	}
682
683	/*
684	 * Write out user data
685	 */
686	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
687
688	error = VOP_WRITE(attribute->uele_backing_vnode, uio, IO_SYNC,
689	    ump->um_extattr.uepm_ucred);
690
691vopunlock_exit:
692	uio->uio_offset = 0;
693
694	if (attribute->uele_backing_vnode != vp)
695		VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
696
697	return (error);
698}
699
700/*
701 * Real work associated with removing an extended attribute from a vnode.
702 * Assumes the attribute lock has already been grabbed.
703 */
704static int
705ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred,
706    struct proc *p)
707{
708	struct ufs_extattr_list_entry	*attribute;
709	struct ufs_extattr_header	ueh;
710	struct iovec	local_aiov;
711	struct uio	local_aio;
712	struct mount	*mp = vp->v_mount;
713	struct ufsmount	*ump = VFSTOUFS(mp);
714	struct inode	*ip = VTOI(vp);
715	off_t	base_offset;
716	int	error = 0;
717
718	if (vp->v_mount->mnt_flag & MNT_RDONLY)
719		return (EROFS);
720
721	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
722		return (EOPNOTSUPP);
723
724	attribute = ufs_exttatr_find_attr(ump, name);
725	if (!attribute)
726		return (ENOENT);
727
728	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p,
729	    IWRITE)))
730		return (error);
731
732	/*
733	 * Find base offset of header in file based on file header size, and
734	 * data header size + maximum data size, indexed by inode number
735	 */
736	base_offset = sizeof(struct ufs_extattr_fileheader) +
737	    ip->i_number * (sizeof(struct ufs_extattr_header) +
738	    attribute->uele_fileheader.uef_size);
739
740	/*
741	 * Read in the data header to see if the data is defined
742	 */
743	bzero(&ueh, sizeof(struct ufs_extattr_header));
744
745	local_aiov.iov_base = (caddr_t) &ueh;
746	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
747	local_aio.uio_iov = &local_aiov;
748	local_aio.uio_iovcnt = 1;
749	local_aio.uio_rw = UIO_READ;
750	local_aio.uio_segflg = UIO_SYSSPACE;
751	local_aio.uio_procp = p;
752	local_aio.uio_offset = base_offset;
753	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
754
755	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
756
757	/*
758	 * Don't need to get the lock on the backing vnode if the vnode we're
759	 * modifying is it, as we already hold the lock.
760	 */
761	if (attribute->uele_backing_vnode != vp)
762		vn_lock(attribute->uele_backing_vnode,
763		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
764
765	error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0,
766	    ump->um_extattr.uepm_ucred);
767	if (error)
768		goto vopunlock_exit;
769
770	/* defined? */
771	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
772		error = ENOENT;
773		goto vopunlock_exit;
774	}
775
776	/* flag it as not in use */
777	ueh.ueh_flags = 0;
778
779	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
780	    ump->um_extattr.uepm_ucred);
781	if (error)
782		goto vopunlock_exit;
783
784	if (local_aio.uio_resid != 0)
785		error = ENXIO;
786
787vopunlock_exit:
788	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
789
790	return (error);
791}
792
793/*
794 * Called by UFS when an inode is no longer active and should have its
795 * attributes stripped.
796 */
797void
798ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p)
799{
800	struct ufs_extattr_list_entry	*uele;
801	struct mount	*mp = vp->v_mount;
802	struct ufsmount	*ump = VFSTOUFS(mp);
803
804	ufs_extattr_uepm_lock(ump, p);
805
806	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
807		ufs_extattr_uepm_unlock(ump, p);
808		return;
809	}
810
811	for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL;
812	    uele = uele->uele_entries.le_next)
813		ufs_extattr_rm(vp, uele->uele_attrname, 0, p);
814
815	ufs_extattr_uepm_unlock(ump, p);
816}
817