ufs_extattr.c revision 71999
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 71999 2001-02-04 13:13:25Z phk $
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 attribute list)
95 *	 attrname consisting of "$" (used to treive system attribute 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 = LIST_FIRST(&ump->um_extattr.uepm_list);
120	    search_attribute;
121	    search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
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 (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
221		uele = LIST_FIRST(&ump->um_extattr.uepm_list);
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]; /* Incl. 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 attributes.
518	 */
519	if (uio->uio_offset != 0)
520		return (ENXIO);
521
522	/*
523	 * Find base offset of header in file based on file header size, and
524	 * data header size + maximum data size, indexed by inode number.
525	 */
526	base_offset = sizeof(struct ufs_extattr_fileheader) +
527	    ip->i_number * (sizeof(struct ufs_extattr_header) +
528	    attribute->uele_fileheader.uef_size);
529
530	/*
531	 * Read in the data header to see if the data is defined, and if so
532	 * how much.
533	 */
534	bzero(&ueh, sizeof(struct ufs_extattr_header));
535	local_aiov.iov_base = (caddr_t) &ueh;
536	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
537	local_aio.uio_iov = &local_aiov;
538	local_aio.uio_iovcnt = 1;
539	local_aio.uio_rw = UIO_READ;
540	local_aio.uio_segflg = UIO_SYSSPACE;
541	local_aio.uio_procp = p;
542	local_aio.uio_offset = base_offset;
543	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
544
545	/*
546	 * Acquire locks.
547	 */
548	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ);
549	/*
550	 * Don't need to get a lock on the backing file if the getattr is
551	 * being applied to the backing file, as the lock is already held.
552	 */
553	if (attribute->uele_backing_vnode != vp)
554		vn_lock(attribute->uele_backing_vnode, LK_SHARED |
555		    LK_NOPAUSE | LK_RETRY, p);
556
557	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
558	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
559	if (error)
560		goto vopunlock_exit;
561
562	/* Defined? */
563	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
564		error = ENOENT;
565		goto vopunlock_exit;
566	}
567
568	/* Valid for the current inode generation? */
569	if (ueh.ueh_i_gen != ip->i_gen) {
570		/*
571		 * The inode itself has a different generation number
572		 * than the attribute data.  For now, the best solution
573		 * is to coerce this to undefined, and let it get cleaned
574		 * up by the next write or extattrctl clean.
575		 */
576		printf("ufs_extattr_get: inode number inconsistency (%d, %d)\n",
577		    ueh.ueh_i_gen, ip->i_gen);
578		error = ENOENT;
579		goto vopunlock_exit;
580	}
581
582	/* Local size consistency check. */
583	if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
584		error = ENXIO;
585		goto vopunlock_exit;
586	}
587
588	/* Allow for offset into the attribute data. */
589	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
590
591	/*
592	 * Figure out maximum to transfer -- use buffer size and local data
593	 * limit.
594	 */
595	size = MIN(uio->uio_resid, ueh.ueh_len);
596	old_size = uio->uio_resid;
597	uio->uio_resid = size;
598
599	error = VOP_READ(attribute->uele_backing_vnode, uio,
600	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
601	if (error)
602		goto vopunlock_exit;
603
604	uio->uio_resid = old_size - (size - uio->uio_resid);
605
606vopunlock_exit:
607
608	uio->uio_offset = 0;
609
610	if (attribute->uele_backing_vnode != vp)
611		VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
612
613	return (error);
614}
615
616/*
617 * Vnode operation to set a named attribute.
618 */
619int
620ufs_vop_setextattr(struct vop_setextattr_args *ap)
621/*
622vop_setextattr {
623	IN struct vnode *a_vp;
624	IN const char *a_name;
625	INOUT struct uio *a_uio;
626	IN struct ucred *a_cred;
627	IN struct proc *a_p;
628};
629*/
630{
631	struct mount	*mp = ap->a_vp->v_mount;
632	struct ufsmount	*ump = VFSTOUFS(mp);
633
634	int	error;
635
636	ufs_extattr_uepm_lock(ump, ap->a_p);
637
638	if (ap->a_uio != NULL)
639		error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio,
640		    ap->a_cred, ap->a_p);
641	else
642		error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred,
643		    ap->a_p);
644
645	ufs_extattr_uepm_unlock(ump, ap->a_p);
646
647	return (error);
648}
649
650/*
651 * Real work associated with setting a vnode's extended attributes;
652 * assumes that the attribute lock has already been grabbed.
653 */
654static int
655ufs_extattr_set(struct vnode *vp, const char *name, struct uio *uio,
656    struct ucred *cred, struct proc *p)
657{
658	struct ufs_extattr_list_entry	*attribute;
659	struct ufs_extattr_header	ueh;
660	struct iovec	local_aiov;
661	struct uio	local_aio;
662	struct mount	*mp = vp->v_mount;
663	struct ufsmount	*ump = VFSTOUFS(mp);
664	struct inode	*ip = VTOI(vp);
665	off_t	base_offset;
666	int	error = 0;
667
668	if (vp->v_mount->mnt_flag & MNT_RDONLY)
669		return (EROFS);
670	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
671		return (EOPNOTSUPP);
672	if (!ufs_extattr_valid_attrname(name))
673		return (EINVAL);
674
675	attribute = ufs_extattr_find_attr(ump, name);
676	if (!attribute)
677		return (ENOENT);
678
679	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
680		return (error);
681
682	/*
683	 * Early rejection of invalid offsets/length.
684	 * Reject: any offset but 0 (replace)
685	 *	 Any size greater than attribute size limit
686 	 */
687	if (uio->uio_offset != 0 ||
688	    uio->uio_resid > attribute->uele_fileheader.uef_size)
689		return (ENXIO);
690
691	/*
692	 * Find base offset of header in file based on file header size, and
693	 * data header size + maximum data size, indexed by inode number.
694	 */
695	base_offset = sizeof(struct ufs_extattr_fileheader) +
696	    ip->i_number * (sizeof(struct ufs_extattr_header) +
697	    attribute->uele_fileheader.uef_size);
698
699	/*
700	 * Write out a data header for the data.
701	 */
702	ueh.ueh_len = uio->uio_resid;
703	ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
704	ueh.ueh_i_gen = ip->i_gen;
705	local_aiov.iov_base = (caddr_t) &ueh;
706	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
707	local_aio.uio_iov = &local_aiov;
708	local_aio.uio_iovcnt = 1;
709	local_aio.uio_rw = UIO_WRITE;
710	local_aio.uio_segflg = UIO_SYSSPACE;
711	local_aio.uio_procp = p;
712	local_aio.uio_offset = base_offset;
713	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
714
715	/*
716	 * Acquire locks.
717	 */
718	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
719
720	/*
721	 * Don't need to get a lock on the backing file if the setattr is
722	 * being applied to the backing file, as the lock is already held.
723	 */
724	if (attribute->uele_backing_vnode != vp)
725		vn_lock(attribute->uele_backing_vnode,
726		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
727
728	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio,
729	    IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred);
730	if (error)
731		goto vopunlock_exit;
732
733	if (local_aio.uio_resid != 0) {
734		error = ENXIO;
735		goto vopunlock_exit;
736	}
737
738	/*
739	 * Write out user data.
740	 */
741	uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
742
743	error = VOP_WRITE(attribute->uele_backing_vnode, uio,
744	    IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred);
745
746vopunlock_exit:
747	uio->uio_offset = 0;
748
749	if (attribute->uele_backing_vnode != vp)
750		VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
751
752	return (error);
753}
754
755/*
756 * Real work associated with removing an extended attribute from a vnode.
757 * Assumes the attribute lock has already been grabbed.
758 */
759static int
760ufs_extattr_rm(struct vnode *vp, const char *name, struct ucred *cred,
761    struct proc *p)
762{
763	struct ufs_extattr_list_entry	*attribute;
764	struct ufs_extattr_header	ueh;
765	struct iovec	local_aiov;
766	struct uio	local_aio;
767	struct mount	*mp = vp->v_mount;
768	struct ufsmount	*ump = VFSTOUFS(mp);
769	struct inode	*ip = VTOI(vp);
770	off_t	base_offset;
771	int	error = 0;
772
773	if (vp->v_mount->mnt_flag & MNT_RDONLY)
774		return (EROFS);
775	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
776		return (EOPNOTSUPP);
777	if (!ufs_extattr_valid_attrname(name))
778		return (EINVAL);
779
780	attribute = ufs_extattr_find_attr(ump, name);
781	if (!attribute)
782		return (ENOENT);
783
784	if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE)))
785		return (error);
786
787	/*
788	 * Find base offset of header in file based on file header size, and
789	 * data header size + maximum data size, indexed by inode number.
790	 */
791	base_offset = sizeof(struct ufs_extattr_fileheader) +
792	    ip->i_number * (sizeof(struct ufs_extattr_header) +
793	    attribute->uele_fileheader.uef_size);
794
795	/*
796	 * Check to see if currently defined.
797	 */
798	bzero(&ueh, sizeof(struct ufs_extattr_header));
799
800	local_aiov.iov_base = (caddr_t) &ueh;
801	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
802	local_aio.uio_iov = &local_aiov;
803	local_aio.uio_iovcnt = 1;
804	local_aio.uio_rw = UIO_READ;
805	local_aio.uio_segflg = UIO_SYSSPACE;
806	local_aio.uio_procp = p;
807	local_aio.uio_offset = base_offset;
808	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
809
810	VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE);
811
812	/*
813	 * Don't need to get the lock on the backing vnode if the vnode we're
814	 * modifying is it, as we already hold the lock.
815	 */
816	if (attribute->uele_backing_vnode != vp)
817		vn_lock(attribute->uele_backing_vnode,
818		    LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p);
819
820	error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
821	    IO_NODELOCKED, ump->um_extattr.uepm_ucred);
822	if (error)
823		goto vopunlock_exit;
824
825	/* Defined? */
826	if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
827		error = ENOENT;
828		goto vopunlock_exit;
829	}
830
831	/* Valid for the current inode generation? */
832	if (ueh.ueh_i_gen != ip->i_gen) {
833		/*
834		 * The inode itself has a different generation number than
835		 * the attribute data.  For now, the best solution is to
836		 * coerce this to undefined, and let it get cleaned up by
837		 * the next write or extattrctl clean.
838		 */
839		printf("ufs_extattr_rm: inode number inconsistency (%d, %d)\n",
840		    ueh.ueh_i_gen, ip->i_gen);
841		error = ENOENT;
842		goto vopunlock_exit;
843	}
844
845	/* Flag it as not in use. */
846	ueh.ueh_flags = 0;
847	ueh.ueh_len = 0;
848
849	local_aiov.iov_base = (caddr_t) &ueh;
850	local_aiov.iov_len = sizeof(struct ufs_extattr_header);
851	local_aio.uio_iov = &local_aiov;
852	local_aio.uio_iovcnt = 1;
853	local_aio.uio_rw = UIO_WRITE;
854	local_aio.uio_segflg = UIO_SYSSPACE;
855	local_aio.uio_procp = p;
856	local_aio.uio_offset = base_offset;
857	local_aio.uio_resid = sizeof(struct ufs_extattr_header);
858
859	error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio,
860	    IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred);
861	if (error)
862		goto vopunlock_exit;
863
864	if (local_aio.uio_resid != 0)
865		error = ENXIO;
866
867vopunlock_exit:
868	VOP_UNLOCK(attribute->uele_backing_vnode, 0, p);
869
870	return (error);
871}
872
873/*
874 * Called by UFS when an inode is no longer active and should have its
875 * attributes stripped.
876 */
877void
878ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p)
879{
880	struct ufs_extattr_list_entry	*uele;
881	struct mount	*mp = vp->v_mount;
882	struct ufsmount	*ump = VFSTOUFS(mp);
883
884	ufs_extattr_uepm_lock(ump, p);
885
886	if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
887		ufs_extattr_uepm_unlock(ump, p);
888		return;
889	}
890
891	for (uele = LIST_FIRST(&ump->um_extattr.uepm_list); uele != NULL;
892	    uele = LIST_NEXT(uele, uele_entries))
893		ufs_extattr_rm(vp, uele->uele_attrname, NULL, p);
894
895	ufs_extattr_uepm_unlock(ump, p);
896}
897