ufs_extattr.c revision 70773
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 70773 2001-01-07 23:27:58Z 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_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 * Destroy per-FS structures supporting extended attributes.  Assumes
149 * that EAs have already been stopped, and will panic if not.
150 */
151void
152ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
153{
154
155	if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
156		panic("ufs_extattr_uepm_destroy: not initialized");
157
158	if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
159		panic("ufs_extattr_uepm_destroy: called while still started");
160
161	/*
162	 * XXX: It's not clear that either order for the next two lines is
163	 * ideal, and it should never be a problem if this is only called
164	 * during unmount, and with vfs_busy().
165	 */
166	uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
167	lockdestroy(&uepm->uepm_lock);
168}
169
170/*
171 * Start extended attribute support on an FS
172 */
173int
174ufs_extattr_start(struct mount *mp, struct proc *p)
175{
176	struct ufsmount	*ump;
177	int	error = 0;
178
179	ump = VFSTOUFS(mp);
180
181	ufs_extattr_uepm_lock(ump, p);
182
183	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
184		error = EOPNOTSUPP;
185		goto unlock;
186	}
187	if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
188		error = EBUSY;
189		goto unlock;
190	}
191
192	ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
193
194	crhold(p->p_ucred);
195	ump->um_extattr.uepm_ucred = p->p_ucred;
196
197unlock:
198	ufs_extattr_uepm_unlock(ump, p);
199
200	return (error);
201}
202
203/*
204 * Stop extended attribute support on an FS
205 */
206int
207ufs_extattr_stop(struct mount *mp, struct proc *p)
208{
209	struct ufs_extattr_list_entry	*uele;
210	struct ufsmount	*ump = VFSTOUFS(mp);
211	int	error = 0;
212
213	ufs_extattr_uepm_lock(ump, p);
214
215	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
216		error = EOPNOTSUPP;
217		goto unlock;
218	}
219
220        while (ump->um_extattr.uepm_list.lh_first != NULL) {
221                uele = ump->um_extattr.uepm_list.lh_first;
222		ufs_extattr_disable(ump, uele->uele_attrname, p);
223        }
224
225	ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
226
227	crfree(ump->um_extattr.uepm_ucred);
228	ump->um_extattr.uepm_ucred = NULL;
229
230unlock:
231	ufs_extattr_uepm_unlock(ump, p);
232
233	return (error);
234}
235
236/*
237 * Enable a named attribute on the specified file system; provide a
238 * backing vnode to hold the attribute data.
239 */
240static int
241ufs_extattr_enable(struct ufsmount *ump, const char *attrname,
242    struct vnode *backing_vnode, struct proc *p)
243{
244	struct ufs_extattr_list_entry	*attribute;
245	struct iovec	aiov;
246	struct uio	auio;
247	int	error = 0;
248
249	if (!ufs_extattr_valid_attrname(attrname))
250		return (EINVAL);
251	if (backing_vnode->v_type != VREG)
252		return (EINVAL);
253
254	MALLOC(attribute, struct ufs_extattr_list_entry *,
255	    sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK);
256	if (attribute == NULL)
257		return (ENOMEM);
258
259	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
260		error = EOPNOTSUPP;
261		goto free_exit;
262	}
263
264	if (ufs_extattr_find_attr(ump, attrname)) {
265		error = EEXIST;
266		goto free_exit;
267	}
268
269	strncpy(attribute->uele_attrname, attrname, UFS_EXTATTR_MAXEXTATTRNAME);
270	bzero(&attribute->uele_fileheader,
271	    sizeof(struct ufs_extattr_fileheader));
272
273	attribute->uele_backing_vnode = backing_vnode;
274
275	auio.uio_iov = &aiov;
276	auio.uio_iovcnt = 1;
277	aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
278	aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
279	auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
280	auio.uio_offset = (off_t) 0;
281	auio.uio_segflg = UIO_SYSSPACE;
282	auio.uio_rw = UIO_READ;
283	auio.uio_procp = (struct proc *) p;
284
285	VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE);
286	vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p);
287	error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
288	    ump->um_extattr.uepm_ucred);
289	VOP_UNLOCK(backing_vnode, 0, p);
290
291	if (error) {
292		goto free_exit;
293	}
294
295	if (auio.uio_resid != 0) {
296		printf("ufs_extattr_enable: malformed attribute header\n");
297		error = EINVAL;
298		goto free_exit;
299	}
300
301	if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
302		printf("ufs_extattr_enable: invalid attribute header magic\n");
303		error = EINVAL;
304		goto free_exit;
305	}
306
307	if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
308		printf("ufs_extattr_enable: incorrect attribute header "
309		    "version\n");
310		error = EINVAL;
311		goto free_exit;
312	}
313
314	backing_vnode->v_flag |= VSYSTEM;
315	LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries);
316
317	return (0);
318
319free_exit:
320	FREE(attribute, M_UFS_EXTATTR);
321	return (error);
322}
323
324/*
325 * Disable extended attribute support on an FS
326 */
327static int
328ufs_extattr_disable(struct ufsmount *ump, const char *attrname, struct proc *p)
329{
330	struct ufs_extattr_list_entry	*uele;
331	int	error = 0;
332
333	if (!ufs_extattr_valid_attrname(attrname))
334		return (EINVAL);
335
336	uele = ufs_extattr_find_attr(ump, attrname);
337	if (!uele)
338		return (ENOENT);
339
340	LIST_REMOVE(uele, uele_entries);
341
342	uele->uele_backing_vnode->v_flag &= ~VSYSTEM;
343        error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p);
344
345	FREE(uele, M_UFS_EXTATTR);
346
347	return (error);
348}
349
350/*
351 * VFS call to manage extended attributes in UFS
352 * attrname, arg are userspace pointers from the syscall
353 */
354int
355ufs_extattrctl(struct mount *mp, int cmd, const char *attrname,
356	       caddr_t arg, struct proc *p)
357{
358	struct nameidata	nd;
359	struct ufsmount	*ump = VFSTOUFS(mp);
360	struct vnode	*vp;
361	char	local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* inc null */
362	char	*filename;
363	int	error, flags;
364	size_t	len;
365
366	/*
367	 * Processes with privilege, but in jail, are not allowed to
368	 * configure extended attributes.
369	 */
370	if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0)))
371		return (error);
372
373	switch(cmd) {
374	case UFS_EXTATTR_CMD_START:
375		error = ufs_extattr_start(mp, p);
376
377		return (error);
378
379	case UFS_EXTATTR_CMD_STOP:
380		return (ufs_extattr_stop(mp, p));
381
382	case UFS_EXTATTR_CMD_ENABLE:
383		error = copyinstr(attrname, local_attrname,
384		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
385		if (error)
386			return (error);
387
388		filename = (char *) arg;
389		NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p);
390		flags = FREAD | FWRITE;
391		error = vn_open(&nd, &flags, 0);
392		if (error)
393			return (error);
394
395		vp = nd.ni_vp;
396		VOP_UNLOCK(vp, 0, p);
397
398		ufs_extattr_uepm_lock(ump, p);
399		error = ufs_extattr_enable(ump, local_attrname, vp, p);
400		ufs_extattr_uepm_unlock(ump, p);
401
402		return (error);
403
404	case UFS_EXTATTR_CMD_DISABLE:
405		error = copyinstr(attrname, local_attrname,
406		    UFS_EXTATTR_MAXEXTATTRNAME, &len);
407
408		ufs_extattr_uepm_lock(ump, p);
409		error = ufs_extattr_disable(ump, local_attrname, p);
410		ufs_extattr_uepm_unlock(ump, p);
411
412		return (error);
413
414	default:
415		return (EINVAL);
416	}
417}
418
419/*
420 * Credential check based on process requesting service, and per-attribute
421 * permissions.
422 */
423static int
424ufs_extattr_credcheck(struct vnode *vp, struct ufs_extattr_list_entry *uele,
425    struct ucred *cred, struct proc *p, int access)
426{
427	int	system_namespace;
428
429	system_namespace = (strlen(uele->uele_attrname) >= 1 &&
430	    uele->uele_attrname[0] == '$');
431
432	/*
433	 * Kernel-invoked always succeeds
434	 */
435	if (cred == NULL)
436		return (0);
437
438	/*
439	 * Do not allow privileged processes in jail to directly
440	 * manipulate system attributes.
441	 *
442	 * XXX What capability should apply here?
443	 * Probably CAP_SYS_SETFFLAG.
444	 */
445	if (system_namespace)
446		return (suser_xxx(cred, p, 0));
447	else
448		return (VOP_ACCESS(vp, access, cred, p));
449}
450
451/*
452 * Vnode operating to retrieve a named extended attribute
453 */
454int
455ufs_vop_getextattr(struct vop_getextattr_args *ap)
456/*
457vop_getextattr {
458        IN struct vnode *a_vp;
459        IN const char *a_name;
460        INOUT struct uio *a_uio;
461        IN struct ucred *a_cred;
462        IN struct proc *a_p;
463};
464*/
465{
466	struct mount	*mp = ap->a_vp->v_mount;
467	struct ufsmount	*ump = VFSTOUFS(mp);
468	int	error;
469
470	ufs_extattr_uepm_lock(ump, ap->a_p);
471
472	error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred,
473	    ap->a_p);
474
475	ufs_extattr_uepm_unlock(ump, ap->a_p);
476
477	return (error);
478}
479
480/*
481 * Real work associated with retrieving a named attribute--assumes that
482 * the attribute lock has already been grabbed.
483 */
484static int
485ufs_extattr_get(struct vnode *vp, const char *name, struct uio *uio,
486    struct ucred *cred, struct proc *p)
487{
488	struct ufs_extattr_list_entry	*attribute;
489	struct ufs_extattr_header	ueh;
490	struct iovec	local_aiov;
491	struct uio	local_aio;
492	struct mount	*mp = vp->v_mount;
493	struct ufsmount	*ump = VFSTOUFS(mp);
494	struct inode	*ip = VTOI(vp);
495	off_t	base_offset;
496	size_t	size, old_size;
497	int	error = 0;
498
499	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
500		return (EOPNOTSUPP);
501
502	if (strlen(name) == 0 || (strlen(name) == 1 && name[0] == '$')) {
503		/* XXX retrieve attribute lists */
504		return (EINVAL);
505	}
506
507	attribute = ufs_extattr_find_attr(ump, name);
508	if (!attribute)
509		return (ENOENT);
510
511	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IREAD)))
512		return (error);
513
514	/*
515	 * Allow only offsets of zero to encourage the read/replace
516	 * extended attribute semantic.  Otherwise we can't guarantee
517	 * atomicity, as we don't provide locks for extended
518	 * attributes.
519	 */
520	if (uio->uio_offset != 0)
521		return (ENXIO);
522
523	/*
524	 * Find base offset of header in file based on file header size, and
525	 * data header size + maximum data size, indexed by inode number
526	 */
527	base_offset = sizeof(struct ufs_extattr_fileheader) +
528	    ip->i_number * (sizeof(struct ufs_extattr_header) +
529	    attribute->uele_fileheader.uef_size);
530
531	/*
532	 * Read in the data header to see if the data is defined, and if so
533	 * how much.
534	 */
535	bzero(&ueh, sizeof(struct ufs_extattr_header));
536	local_aiov.iov_base = (caddr_t) &ueh;
537	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
538	local_aio.uio_iov = &local_aiov;
539	local_aio.uio_iovcnt = 1;
540	local_aio.uio_rw = UIO_READ;
541	local_aio.uio_segflg = UIO_SYSSPACE;
542	local_aio.uio_procp = p;
543	local_aio.uio_offset = base_offset;
544	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
545
546	/*
547	 * Acquire locks.
548	 */
549	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ);
550	/*
551	 * Don't need to get a lock on the backing file if the getattr is
552	 * being applied to the backing file, as the lock is already held.
553	 */
554	if (attribute->uele_backing_vnode != vp)
555		vn_lock(attribute->uele_backing_vnode, LK_SHARED |
556		    LK_NOPAUSE | LK_RETRY, p);
557
558	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
559	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
560	if (error)
561		goto vopunlock_exit;
562
563	/* defined? */
564	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
565		error = ENOENT;
566		goto vopunlock_exit;
567	}
568
569	/* valid for the current inode generation? */
570	if (ueh.ueh_i_gen != ip->i_gen) {
571		/*
572		 * The inode itself has a different generation number
573		 * than the attribute data.  For now, the best solution
574		 * is to coerce this to undefined, and let it get cleaned
575		 * up by the next write or extattrctl clean.
576		 */
577		printf("ufs_extattr_get: inode %lu inconsistency (%d, %d)\n",
578		    (u_long)ip->i_number, ueh.ueh_i_gen, ip->i_gen);
579		error = ENOENT;
580		goto vopunlock_exit;
581	}
582
583	/* local size consistency check */
584	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
585		error = ENXIO;
586		goto vopunlock_exit;
587	}
588
589	/* allow for offset into the attr data */
590	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
591
592	/*
593	 * Figure out maximum to transfer -- use buffer size and local data
594	 * limit.
595	 */
596	size = MIN(uio->uio_resid, ueh.ueh_len);
597	old_size = uio->uio_resid;
598	uio->uio_resid = size;
599
600	error = VOP_READ(attribute->uele_backing_vnode, uio,
601	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
602	if (error)
603		goto vopunlock_exit;
604
605	uio->uio_resid = old_size - (size - uio->uio_resid);
606
607vopunlock_exit:
608
609	uio->uio_offset = 0;
610
611	if (attribute->uele_backing_vnode != vp)
612		VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
613
614	return (error);
615}
616
617/*
618 * Vnode operation to set a named attribute
619 */
620int
621ufs_vop_setextattr(struct vop_setextattr_args *ap)
622/*
623vop_setextattr {
624        IN struct vnode *a_vp;
625        IN const char *a_name;
626        INOUT struct uio *a_uio;
627        IN struct ucred *a_cred;
628        IN struct proc *a_p;
629};
630*/
631{
632	struct mount	*mp = ap->a_vp->v_mount;
633	struct ufsmount	*ump = VFSTOUFS(mp);
634
635	int	error;
636
637	ufs_extattr_uepm_lock(ump, ap->a_p);
638
639	if (ap->a_uio != NULL)
640		error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio,
641		    ap->a_cred, ap->a_p);
642	else
643		error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred,
644		    ap->a_p);
645
646	ufs_extattr_uepm_unlock(ump, ap->a_p);
647
648	return (error);
649}
650
651/*
652 * Real work associated with setting a vnode's extended attributes;
653 * assumes that the attribute lock has already been grabbed.
654 */
655static int
656ufs_extattr_set(struct vnode *vp, const char *name, struct uio *uio,
657    struct ucred *cred, struct proc *p)
658{
659	struct ufs_extattr_list_entry	*attribute;
660	struct ufs_extattr_header	ueh;
661	struct iovec	local_aiov;
662	struct uio	local_aio;
663	struct mount	*mp = vp->v_mount;
664	struct ufsmount	*ump = VFSTOUFS(mp);
665	struct inode	*ip = VTOI(vp);
666	off_t	base_offset;
667	int	error = 0;
668
669	if (vp->v_mount->mnt_flag & MNT_RDONLY)
670		return (EROFS);
671	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
672		return (EOPNOTSUPP);
673	if (!ufs_extattr_valid_attrname(name))
674		return (EINVAL);
675
676	attribute = ufs_extattr_find_attr(ump, name);
677	if (!attribute)
678		return (ENOENT);
679
680	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
681		return (error);
682
683	/*
684	 * Early rejection of invalid offsets/lengths
685	 * Reject: any offset but 0 (replace)
686	 *         Any size greater than attribute size limit
687 	 */
688	if (uio->uio_offset != 0 ||
689	    uio->uio_resid > attribute->uele_fileheader.uef_size)
690		return (ENXIO);
691
692	/*
693	 * Find base offset of header in file based on file header size, and
694	 * data header size + maximum data size, indexed by inode number
695	 */
696	base_offset = sizeof(struct ufs_extattr_fileheader) +
697	    ip->i_number * (sizeof(struct ufs_extattr_header) +
698	    attribute->uele_fileheader.uef_size);
699
700	/*
701	 * Write out a data header for the data
702	 */
703	ueh.ueh_len = uio->uio_resid;
704	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
705	ueh.ueh_i_gen = ip->i_gen;
706	local_aiov.iov_base = (caddr_t) &ueh;
707	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
708	local_aio.uio_iov = &local_aiov;
709	local_aio.uio_iovcnt = 1;
710	local_aio.uio_rw = UIO_WRITE;
711	local_aio.uio_segflg = UIO_SYSSPACE;
712	local_aio.uio_procp = p;
713	local_aio.uio_offset = base_offset;
714	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
715
716	/*
717	 * Acquire locks.
718	 */
719	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
720
721	/*
722	 * Don't need to get a lock on the backing file if the setattr is
723	 * being applied to the backing file, as the lock is already held.
724	 */
725	if (attribute->uele_backing_vnode != vp)
726		vn_lock(attribute->uele_backing_vnode,
727		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
728
729	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio,
730	    IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred);
731	if (error)
732		goto vopunlock_exit;
733
734	if (local_aio.uio_resid != 0) {
735		error = ENXIO;
736		goto vopunlock_exit;
737	}
738
739	/*
740	 * Write out user data
741	 */
742	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
743
744	error = VOP_WRITE(attribute->uele_backing_vnode, uio,
745	    IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred);
746
747vopunlock_exit:
748	uio->uio_offset = 0;
749
750	if (attribute->uele_backing_vnode != vp)
751		VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
752
753	return (error);
754}
755
756/*
757 * Real work associated with removing an extended attribute from a vnode.
758 * Assumes the attribute lock has already been grabbed.
759 */
760static int
761ufs_extattr_rm(struct vnode *vp, const char *name, struct ucred *cred,
762    struct proc *p)
763{
764	struct ufs_extattr_list_entry	*attribute;
765	struct ufs_extattr_header	ueh;
766	struct iovec	local_aiov;
767	struct uio	local_aio;
768	struct mount	*mp = vp->v_mount;
769	struct ufsmount	*ump = VFSTOUFS(mp);
770	struct inode	*ip = VTOI(vp);
771	off_t	base_offset;
772	int	error = 0;
773
774	if (vp->v_mount->mnt_flag & MNT_RDONLY)
775		return (EROFS);
776	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
777		return (EOPNOTSUPP);
778	if (!ufs_extattr_valid_attrname(name))
779		return (EINVAL);
780
781	attribute = ufs_extattr_find_attr(ump, name);
782	if (!attribute)
783		return (ENOENT);
784
785	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
786		return (error);
787
788	/*
789	 * Find base offset of header in file based on file header size, and
790	 * data header size + maximum data size, indexed by inode number
791	 */
792	base_offset = sizeof(struct ufs_extattr_fileheader) +
793	    ip->i_number * (sizeof(struct ufs_extattr_header) +
794	    attribute->uele_fileheader.uef_size);
795
796	/*
797	 * Check to see if currently defined.
798	 */
799	bzero(&ueh, sizeof(struct ufs_extattr_header));
800
801	local_aiov.iov_base = (caddr_t) &ueh;
802	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
803	local_aio.uio_iov = &local_aiov;
804	local_aio.uio_iovcnt = 1;
805	local_aio.uio_rw = UIO_READ;
806	local_aio.uio_segflg = UIO_SYSSPACE;
807	local_aio.uio_procp = p;
808	local_aio.uio_offset = base_offset;
809	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
810
811	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
812
813	/*
814	 * Don't need to get the lock on the backing vnode if the vnode we're
815	 * modifying is it, as we already hold the lock.
816	 */
817	if (attribute->uele_backing_vnode != vp)
818		vn_lock(attribute->uele_backing_vnode,
819		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
820
821	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
822	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
823	if (error)
824		goto vopunlock_exit;
825
826	/* defined? */
827	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
828		error = ENOENT;
829		goto vopunlock_exit;
830	}
831
832	/* Valid for the current inode generation? */
833	if (ueh.ueh_i_gen != ip->i_gen) {
834		/*
835		 * The inode itself has a different generation number than
836		 * the attribute data.  For now, the best solution is to
837		 * coerce this to undefined, and let it get cleaned up by
838		 * the next write or extattrctl clean.
839		 */
840		printf("ufs_extattr_rm: inode %lu inconsistency (%d, %d)\n",
841		    (u_long)ip->i_number, ueh.ueh_i_gen, ip->i_gen);
842		error = ENOENT;
843		goto vopunlock_exit;
844	}
845
846	/* flag it as not in use */
847	ueh.ueh_flags = 0;
848
849	local_aio.uio_offset = base_offset;
850	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
851	local_aio.uio_rw = UIO_WRITE;
852
853	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio,
854	    IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred);
855	if (error)
856		goto vopunlock_exit;
857
858	if (local_aio.uio_resid != 0)
859		error = ENXIO;
860
861vopunlock_exit:
862	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
863
864	return (error);
865}
866
867/*
868 * Called by UFS when an inode is no longer active and should have its
869 * attributes stripped.
870 */
871void
872ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p)
873{
874	struct ufs_extattr_list_entry	*uele;
875	struct mount	*mp = vp->v_mount;
876	struct ufsmount	*ump = VFSTOUFS(mp);
877
878	ufs_extattr_uepm_lock(ump, p);
879
880	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
881		ufs_extattr_uepm_unlock(ump, p);
882		return;
883	}
884
885	for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL;
886	    uele = uele->uele_entries.le_next)
887		ufs_extattr_rm(vp, uele->uele_attrname, NULL, p);
888
889	ufs_extattr_uepm_unlock(ump, p);
890}
891