ufs_extattr.c revision 59388
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 59388 2000-04-19 07:38:20Z 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	/* valid for the current inode generation? */
494	if (ueh.ueh_i_gen != ip->i_gen) {
495		/*
496		 * The inode itself has a different generation number
497		 * than the attribute data.  For now, the best solution
498		 * is to coerce this to undefined, and let it get cleaned
499		 * up by the next write or extattrctl clean.
500		 */
501		printf("ufs_extattr: inode number inconsistency (%d, %d)\n",
502		    ueh.ueh_i_gen, ip->i_gen);
503		error = ENOENT;
504		goto vopunlock_exit;
505	}
506
507	/* local size consistency check */
508	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
509		error = ENXIO;
510		goto vopunlock_exit;
511	}
512
513	if (ueh.ueh_len < uio->uio_offset) {
514		error = 0;
515		goto vopunlock_exit;
516	}
517
518	/* allow for offset into the attr data */
519	offset = base_offset + sizeof(struct ufs_extattr_header) +
520	    uio->uio_offset;
521
522	/*
523	 * Figure out maximum to transfer -- use buffer size and local data
524	 * limit.
525	 */
526	size = MIN(uio->uio_resid, ueh.ueh_len - uio->uio_offset);
527
528	old_offset = uio->uio_offset;
529	uio->uio_offset = offset;
530	old_size = uio->uio_resid;
531	uio->uio_resid = size;
532
533	error = VOP_READ(attribute->uele_backing_vnode, uio, 0,
534	    ump->um_extattr.uepm_ucred);
535	if (error) {
536		uio->uio_offset = old_offset;
537		goto vopunlock_exit;
538	}
539
540	uio->uio_offset = old_offset;
541	uio->uio_resid = old_size - (size - uio->uio_resid);
542
543vopunlock_exit:
544	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
545
546	return (error);
547}
548
549/*
550 * Vnode operation to set a named attribute
551 */
552int
553ufs_vop_setextattr(struct vop_setextattr_args *ap)
554/*
555vop_setextattr {
556        IN struct vnode *a_vp;
557        IN char *a_name;
558        INOUT struct uio *a_uio;
559        IN struct ucred *a_cred;
560        IN struct proc *a_p;
561};
562*/
563{
564	struct mount	*mp = ap->a_vp->v_mount;
565	struct ufsmount	*ump = VFSTOUFS(mp);
566
567	int	error;
568
569	ufs_extattr_uepm_lock(ump, ap->a_p);
570
571	if (ap->a_uio)
572		error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio,
573		    ap->a_cred, ap->a_p);
574	else
575		error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred,
576		    ap->a_p);
577
578	ufs_extattr_uepm_unlock(ump, ap->a_p);
579
580	return (error);
581}
582
583/*
584 * Real work associated with setting a vnode's extended attributes;
585 * assumes that the attribute lock has already been grabbed.
586 */
587static int
588ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio,
589    struct ucred *cred, struct proc *p)
590{
591	struct ufs_extattr_list_entry	*attribute;
592	struct ufs_extattr_header	ueh;
593	struct iovec	local_aiov;
594	struct uio	local_aio;
595	struct mount	*mp = vp->v_mount;
596	struct ufsmount	*ump = VFSTOUFS(mp);
597	struct inode	*ip = VTOI(vp);
598	off_t	base_offset;
599
600	int	error = 0;
601
602	if (vp->v_mount->mnt_flag & MNT_RDONLY)
603		return (EROFS);
604
605	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
606		return (EOPNOTSUPP);
607
608	attribute = ufs_exttatr_find_attr(ump, name);
609	if (!attribute)
610		return (ENOENT);
611
612	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred,
613	    p, IWRITE)))
614		return (error);
615
616	/*
617	 * Early rejection of invalid offsets/lengths
618	 * Reject: any offset but 0 (replace)
619	 *         Any size greater than attribute size limit
620 	 */
621	if (uio->uio_offset != 0 ||
622	    uio->uio_resid > attribute->uele_fileheader.uef_size)
623		return (ENXIO);
624
625	/*
626	 * Find base offset of header in file based on file header size, and
627	 * data header size + maximum data size, indexed by inode number
628	 */
629	base_offset = sizeof(struct ufs_extattr_fileheader) +
630	    ip->i_number * (sizeof(struct ufs_extattr_header) +
631	    attribute->uele_fileheader.uef_size);
632
633	/*
634	 * Write out a data header for the data
635	 */
636	ueh.ueh_len = uio->uio_resid;
637	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
638	ueh.ueh_i_gen = ip->i_gen;
639	local_aiov.iov_base = (caddr_t) &ueh;
640	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
641	local_aio.uio_iov = &local_aiov;
642	local_aio.uio_iovcnt = 1;
643	local_aio.uio_rw = UIO_WRITE;
644	local_aio.uio_segflg = UIO_SYSSPACE;
645	local_aio.uio_procp = p;
646	local_aio.uio_offset = base_offset;
647	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
648
649	/*
650	 * Acquire locks
651	 */
652	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
653
654	/*
655	 * Don't need to get a lock on the backing file if the setattr is
656	 * being applied to the backing file, as the lock is already held
657	 */
658	if (attribute->uele_backing_vnode != vp)
659		vn_lock(attribute->uele_backing_vnode,
660		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
661
662	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
663	    ump->um_extattr.uepm_ucred);
664	if (error)
665		goto vopunlock_exit;
666
667	if (local_aio.uio_resid != 0) {
668		error = ENXIO;
669		goto vopunlock_exit;
670	}
671
672	/*
673	 * Write out user data
674	 */
675	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
676
677	error = VOP_WRITE(attribute->uele_backing_vnode, uio, IO_SYNC,
678	    ump->um_extattr.uepm_ucred);
679
680vopunlock_exit:
681	uio->uio_offset = 0;
682
683	if (attribute->uele_backing_vnode != vp)
684		VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
685
686	return (error);
687}
688
689/*
690 * Real work associated with removing an extended attribute from a vnode.
691 * Assumes the attribute lock has already been grabbed.
692 */
693static int
694ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred,
695    struct proc *p)
696{
697	struct ufs_extattr_list_entry	*attribute;
698	struct ufs_extattr_header	ueh;
699	struct iovec	local_aiov;
700	struct uio	local_aio;
701	struct mount	*mp = vp->v_mount;
702	struct ufsmount	*ump = VFSTOUFS(mp);
703	struct inode	*ip = VTOI(vp);
704	off_t	base_offset;
705	int	error = 0;
706
707	if (vp->v_mount->mnt_flag & MNT_RDONLY)
708		return (EROFS);
709
710	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
711		return (EOPNOTSUPP);
712
713	attribute = ufs_exttatr_find_attr(ump, name);
714	if (!attribute)
715		return (ENOENT);
716
717	if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p,
718	    IWRITE)))
719		return (error);
720
721	/*
722	 * Find base offset of header in file based on file header size, and
723	 * data header size + maximum data size, indexed by inode number
724	 */
725	base_offset = sizeof(struct ufs_extattr_fileheader) +
726	    ip->i_number * (sizeof(struct ufs_extattr_header) +
727	    attribute->uele_fileheader.uef_size);
728
729	/*
730	 * Read in the data header to see if the data is defined
731	 */
732	bzero(&ueh, sizeof(struct ufs_extattr_header));
733
734	local_aiov.iov_base = (caddr_t) &ueh;
735	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
736	local_aio.uio_iov = &local_aiov;
737	local_aio.uio_iovcnt = 1;
738	local_aio.uio_rw = UIO_READ;
739	local_aio.uio_segflg = UIO_SYSSPACE;
740	local_aio.uio_procp = p;
741	local_aio.uio_offset = base_offset;
742	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
743
744	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
745
746	/*
747	 * Don't need to get the lock on the backing vnode if the vnode we're
748	 * modifying is it, as we already hold the lock.
749	 */
750	if (attribute->uele_backing_vnode != vp)
751		vn_lock(attribute->uele_backing_vnode,
752		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
753
754	error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0,
755	    ump->um_extattr.uepm_ucred);
756	if (error)
757		goto vopunlock_exit;
758
759	/* defined? */
760	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
761		error = ENOENT;
762		goto vopunlock_exit;
763	}
764
765	/* flag it as not in use */
766	ueh.ueh_flags = 0;
767
768	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0,
769	    ump->um_extattr.uepm_ucred);
770	if (error)
771		goto vopunlock_exit;
772
773	if (local_aio.uio_resid != 0)
774		error = ENXIO;
775
776vopunlock_exit:
777	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
778
779	return (error);
780}
781
782/*
783 * Called by UFS when an inode is no longer active and should have its
784 * attributes stripped.
785 */
786void
787ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p)
788{
789	struct ufs_extattr_list_entry	*uele;
790	struct mount	*mp = vp->v_mount;
791	struct ufsmount	*ump = VFSTOUFS(mp);
792
793	ufs_extattr_uepm_lock(ump, p);
794
795	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
796		ufs_extattr_uepm_unlock(ump, p);
797		return;
798	}
799
800	for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL;
801	    uele = uele->uele_entries.le_next)
802		ufs_extattr_rm(vp, uele->uele_attrname, 0, p);
803
804	ufs_extattr_uepm_unlock(ump, p);
805}
806