2 * Copyright (c) 1999-2002 Robert N. M. Watson
3 * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
6 * This software was developed by Robert Watson for the TrustedBSD Project.
8 * This software was developed for the FreeBSD Project in part by Network
9 * Associates Laboratories, the Security Research Division of Network
10 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
11 * as part of the DARPA CHATS research program.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
37 * Support for filesystem extended attribute: UFS-specific support functions.
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/namei.h>
49 #include <sys/malloc.h>
50 #include <sys/fcntl.h>
53 #include <sys/vnode.h>
54 #include <sys/mount.h>
56 #include <sys/dirent.h>
57 #include <sys/extattr.h>
58 #include <sys/sysctl.h>
62 #include <ufs/ufs/dir.h>
63 #include <ufs/ufs/extattr.h>
64 #include <ufs/ufs/quota.h>
65 #include <ufs/ufs/ufsmount.h>
66 #include <ufs/ufs/inode.h>
67 #include <ufs/ufs/ufs_extern.h>
71 static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
73 static int ufs_extattr_sync = 0;
74 SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync,
77 static int ufs_extattr_valid_attrname(int attrnamespace,
78 const char *attrname);
79 static int ufs_extattr_enable_with_open(struct ufsmount *ump,
80 struct vnode *vp, int attrnamespace, const char *attrname,
82 static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
83 const char *attrname, struct vnode *backing_vnode,
85 static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
86 const char *attrname, struct thread *td);
87 static int ufs_extattr_get(struct vnode *vp, int attrnamespace,
88 const char *name, struct uio *uio, size_t *size,
89 struct ucred *cred, struct thread *td);
90 static int ufs_extattr_set(struct vnode *vp, int attrnamespace,
91 const char *name, struct uio *uio, struct ucred *cred,
93 static int ufs_extattr_rm(struct vnode *vp, int attrnamespace,
94 const char *name, struct ucred *cred, struct thread *td);
97 * Per-FS attribute lock protecting attribute operations.
98 * XXX Right now there is a lot of lock contention due to having a single
99 * lock per-FS; really, this should be far more fine-grained.
102 ufs_extattr_uepm_lock(struct ufsmount *ump, struct thread *td)
105 /* Ideally, LK_CANRECURSE would not be used, here. */
106 lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY |
107 LK_CANRECURSE, 0, td);
111 ufs_extattr_uepm_unlock(struct ufsmount *ump, struct thread *td)
114 lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, td);
118 * Determine whether the name passed is a valid name for an actual
121 * Invalid currently consists of:
122 * NULL pointer for attrname
123 * zero-length attrname (used to retrieve application attribute list)
126 ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
129 if (attrname == NULL)
131 if (strlen(attrname) == 0)
137 * Locate an attribute given a name and mountpoint.
138 * Must be holding uepm lock for the mount point.
140 static struct ufs_extattr_list_entry *
141 ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
142 const char *attrname)
144 struct ufs_extattr_list_entry *search_attribute;
146 for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
147 search_attribute != NULL;
148 search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
149 if (!(strncmp(attrname, search_attribute->uele_attrname,
150 UFS_EXTATTR_MAXEXTATTRNAME)) &&
151 (attrnamespace == search_attribute->uele_attrnamespace)) {
152 return (search_attribute);
160 * Initialize per-FS structures supporting extended attributes. Do not
161 * start extended attributes yet.
164 ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
167 uepm->uepm_flags = 0;
169 LIST_INIT(&uepm->uepm_list);
170 /* XXX is PVFS right, here? */
171 lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0);
172 uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
176 * Destroy per-FS structures supporting extended attributes. Assumes
177 * that EAs have already been stopped, and will panic if not.
180 ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
183 if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
184 panic("ufs_extattr_uepm_destroy: not initialized");
186 if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
187 panic("ufs_extattr_uepm_destroy: called while still started");
190 * It's not clear that either order for the next two lines is
191 * ideal, and it should never be a problem if this is only called
192 * during unmount, and with vfs_busy().
194 uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
195 lockdestroy(&uepm->uepm_lock);
199 * Start extended attribute support on an FS.
202 ufs_extattr_start(struct mount *mp, struct thread *td)
204 struct ufsmount *ump;
209 ufs_extattr_uepm_lock(ump, td);
211 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
215 if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
220 ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
222 ump->um_extattr.uepm_ucred = crhold(td->td_ucred);
225 ufs_extattr_uepm_unlock(ump, td);
230 #ifdef UFS_EXTATTR_AUTOSTART
232 * Helper routine: given a locked parent directory and filename, return
233 * the locked vnode of the inode associated with the name. Will not
234 * follow symlinks, may return any type of vnode. Lock on parent will
235 * be released even in the event of a failure. In the event that the
236 * target is the parent (i.e., "."), there will be two references and
237 * one lock, requiring the caller to possibly special-case.
239 #define UE_GETDIR_LOCKPARENT 1
240 #define UE_GETDIR_LOCKPARENT_DONT 2
242 ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname,
243 struct vnode **vp, struct thread *td)
245 struct vop_cachedlookup_args vargs;
246 struct componentname cnp;
247 struct vnode *target_vp;
250 bzero(&cnp, sizeof(cnp));
251 cnp.cn_nameiop = LOOKUP;
252 cnp.cn_flags = ISLASTCN;
253 if (lockparent == UE_GETDIR_LOCKPARENT)
254 cnp.cn_flags |= LOCKPARENT;
255 cnp.cn_lkflags = LK_EXCLUSIVE;
257 cnp.cn_cred = td->td_ucred;
258 cnp.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
259 cnp.cn_nameptr = cnp.cn_pnbuf;
260 error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN,
261 (size_t *) &cnp.cn_namelen);
263 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) {
264 VOP_UNLOCK(start_dvp, 0, td);
266 uma_zfree(namei_zone, cnp.cn_pnbuf);
267 printf("ufs_extattr_lookup: copystr failed\n");
270 cnp.cn_namelen--; /* trim nul termination */
271 vargs.a_gen.a_desc = NULL;
272 vargs.a_dvp = start_dvp;
273 vargs.a_vpp = &target_vp;
275 error = ufs_lookup(&vargs);
276 uma_zfree(namei_zone, cnp.cn_pnbuf);
279 * Error condition, may have to release the lock on the parent
280 * if ufs_lookup() didn't.
282 if (lockparent == UE_GETDIR_LOCKPARENT_DONT)
283 VOP_UNLOCK(start_dvp, 0, td);
286 * Check that ufs_lookup() didn't release the lock when we
289 if (lockparent == UE_GETDIR_LOCKPARENT)
290 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup");
295 if (target_vp == start_dvp)
296 panic("ufs_extattr_lookup: target_vp == start_dvp");
299 if (target_vp != start_dvp && lockparent == UE_GETDIR_LOCKPARENT_DONT)
300 VOP_UNLOCK(start_dvp, 0, td);
302 if (lockparent == UE_GETDIR_LOCKPARENT)
303 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup");
305 /* printf("ufs_extattr_lookup: success\n"); */
309 #endif /* !UFS_EXTATTR_AUTOSTART */
312 * Enable an EA using the passed filesystem, backing vnode, attribute name,
313 * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp
314 * to be locked when passed in. The vnode will be returned unlocked,
315 * regardless of success/failure of the function. As a result, the caller
316 * will always need to vrele(), but not vput().
319 ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
320 int attrnamespace, const char *attrname, struct thread *td)
324 error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, NULL);
326 printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
328 VOP_UNLOCK(vp, 0, td);
336 VOP_UNLOCK(vp, 0, td);
338 error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td);
340 vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
344 #ifdef UFS_EXTATTR_AUTOSTART
346 * Given a locked directory vnode, iterate over the names in the directory
347 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
348 * attribute files. Then invoke ufs_extattr_enable_with_open() on each
349 * to attempt to start the attribute. Leaves the directory locked on
353 ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
354 int attrnamespace, struct thread *td)
356 struct vop_readdir_args vargs;
357 struct dirent *dp, *edp;
358 struct vnode *attr_vp;
362 int error, eofflag = 0;
364 if (dvp->v_type != VDIR)
367 MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK);
369 auio.uio_iov = &aiov;
371 auio.uio_rw = UIO_READ;
372 auio.uio_segflg = UIO_SYSSPACE;
376 vargs.a_gen.a_desc = NULL;
379 vargs.a_cred = td->td_ucred;
380 vargs.a_eofflag = &eofflag;
381 vargs.a_ncookies = NULL;
382 vargs.a_cookies = NULL;
385 auio.uio_resid = DIRBLKSIZ;
386 aiov.iov_base = dirbuf;
387 aiov.iov_len = DIRBLKSIZ;
388 error = ufs_readdir(&vargs);
390 printf("ufs_extattr_iterate_directory: ufs_readdir "
396 * XXXRW: While in UFS, we always get DIRBLKSIZ returns from
397 * the directory code on success, on other file systems this
398 * may not be the case. For portability, we should check the
399 * read length on return from ufs_readdir().
401 edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
402 for (dp = (struct dirent *)dirbuf; dp < edp; ) {
403 #if (BYTE_ORDER == LITTLE_ENDIAN)
404 dp->d_type = dp->d_namlen;
409 if (dp->d_reclen == 0)
411 error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT,
412 dp->d_name, &attr_vp, td);
414 printf("ufs_extattr_iterate_directory: lookup "
415 "%s %d\n", dp->d_name, error);
416 } else if (attr_vp == dvp) {
418 } else if (attr_vp->v_type != VREG) {
421 error = ufs_extattr_enable_with_open(ump,
422 attr_vp, attrnamespace, dp->d_name, td);
425 printf("ufs_extattr_iterate_directory: "
426 "enable %s %d\n", dp->d_name,
428 } else if (bootverbose) {
429 printf("UFS autostarted EA %s\n",
433 dp = (struct dirent *) ((char *)dp + dp->d_reclen);
438 FREE(dirbuf, M_TEMP);
444 * Auto-start of extended attributes, to be executed (optionally) at
448 ufs_extattr_autostart(struct mount *mp, struct thread *td)
450 struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
454 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
455 * If so, automatically start EA's.
457 error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp, td);
459 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
464 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT,
465 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td);
467 /* rvp ref'd but now unlocked */
471 if (rvp == attr_dvp) {
472 /* Should never happen. */
479 if (attr_dvp->v_type != VDIR) {
480 printf("ufs_extattr_autostart: %s != VDIR\n",
481 UFS_EXTATTR_FSROOTSUBDIR);
482 goto return_vput_attr_dvp;
485 error = ufs_extattr_start(mp, td);
487 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
489 goto return_vput_attr_dvp;
493 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
494 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory,
495 * and start with appropriate type. Failures in either don't
496 * result in an over-all failure. attr_dvp is left locked to
497 * be cleaned up on exit.
499 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
500 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td);
502 error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
503 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td);
505 printf("ufs_extattr_iterate_directory returned %d\n",
507 vput(attr_system_dvp);
510 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
511 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td);
513 error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
514 attr_user_dvp, EXTATTR_NAMESPACE_USER, td);
516 printf("ufs_extattr_iterate_directory returned %d\n",
521 /* Mask startup failures in sub-directories. */
524 return_vput_attr_dvp:
529 #endif /* !UFS_EXTATTR_AUTOSTART */
532 * Stop extended attribute support on an FS.
535 ufs_extattr_stop(struct mount *mp, struct thread *td)
537 struct ufs_extattr_list_entry *uele;
538 struct ufsmount *ump = VFSTOUFS(mp);
541 ufs_extattr_uepm_lock(ump, td);
543 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
548 while ((uele = LIST_FIRST(&ump->um_extattr.uepm_list)) != NULL) {
549 ufs_extattr_disable(ump, uele->uele_attrnamespace,
550 uele->uele_attrname, td);
553 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
555 crfree(ump->um_extattr.uepm_ucred);
556 ump->um_extattr.uepm_ucred = NULL;
559 ufs_extattr_uepm_unlock(ump, td);
565 * Enable a named attribute on the specified filesystem; provide an
566 * unlocked backing vnode to hold the attribute data.
569 ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
570 const char *attrname, struct vnode *backing_vnode, struct thread *td)
572 struct ufs_extattr_list_entry *attribute;
577 if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
579 if (backing_vnode->v_type != VREG)
582 MALLOC(attribute, struct ufs_extattr_list_entry *,
583 sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK);
584 if (attribute == NULL)
587 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
592 if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
597 strncpy(attribute->uele_attrname, attrname,
598 UFS_EXTATTR_MAXEXTATTRNAME);
599 attribute->uele_attrnamespace = attrnamespace;
600 bzero(&attribute->uele_fileheader,
601 sizeof(struct ufs_extattr_fileheader));
603 attribute->uele_backing_vnode = backing_vnode;
605 auio.uio_iov = &aiov;
607 aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
608 aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
609 auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
610 auio.uio_offset = (off_t) 0;
611 auio.uio_segflg = UIO_SYSSPACE;
612 auio.uio_rw = UIO_READ;
615 vn_lock(backing_vnode, LK_SHARED | LK_RETRY, td);
616 error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
617 ump->um_extattr.uepm_ucred);
620 goto unlock_free_exit;
622 if (auio.uio_resid != 0) {
623 printf("ufs_extattr_enable: malformed attribute header\n");
625 goto unlock_free_exit;
628 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
629 printf("ufs_extattr_enable: invalid attribute header magic\n");
631 goto unlock_free_exit;
634 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
635 printf("ufs_extattr_enable: incorrect attribute header "
638 goto unlock_free_exit;
641 ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable");
642 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
645 VOP_UNLOCK(backing_vnode, 0, td);
649 VOP_UNLOCK(backing_vnode, 0, td);
652 FREE(attribute, M_UFS_EXTATTR);
657 * Disable extended attribute support on an FS.
660 ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
661 const char *attrname, struct thread *td)
663 struct ufs_extattr_list_entry *uele;
666 if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
669 uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
673 LIST_REMOVE(uele, uele_entries);
675 vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY,
677 ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable");
678 VOP_UNLOCK(uele->uele_backing_vnode, 0, td);
679 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
682 FREE(uele, M_UFS_EXTATTR);
688 * VFS call to manage extended attributes in UFS. If filename_vp is
689 * non-NULL, it must be passed in locked, and regardless of errors in
690 * processing, will be unlocked.
693 ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
694 int attrnamespace, const char *attrname, struct thread *td)
696 struct ufsmount *ump = VFSTOUFS(mp);
700 * Processes with privilege, but in jail, are not allowed to
701 * configure extended attributes.
703 error = priv_check(td, PRIV_UFS_EXTATTRCTL);
705 if (filename_vp != NULL)
706 VOP_UNLOCK(filename_vp, 0, td);
711 case UFS_EXTATTR_CMD_START:
712 if (filename_vp != NULL) {
713 VOP_UNLOCK(filename_vp, 0, td);
716 if (attrname != NULL)
719 error = ufs_extattr_start(mp, td);
723 case UFS_EXTATTR_CMD_STOP:
724 if (filename_vp != NULL) {
725 VOP_UNLOCK(filename_vp, 0, td);
728 if (attrname != NULL)
731 error = ufs_extattr_stop(mp, td);
735 case UFS_EXTATTR_CMD_ENABLE:
737 if (filename_vp == NULL)
739 if (attrname == NULL) {
740 VOP_UNLOCK(filename_vp, 0, td);
745 * ufs_extattr_enable_with_open() will always unlock the
746 * vnode, regardless of failure.
748 ufs_extattr_uepm_lock(ump, td);
749 error = ufs_extattr_enable_with_open(ump, filename_vp,
750 attrnamespace, attrname, td);
751 ufs_extattr_uepm_unlock(ump, td);
755 case UFS_EXTATTR_CMD_DISABLE:
757 if (filename_vp != NULL) {
758 VOP_UNLOCK(filename_vp, 0, td);
761 if (attrname == NULL)
764 ufs_extattr_uepm_lock(ump, td);
765 error = ufs_extattr_disable(ump, attrnamespace, attrname,
767 ufs_extattr_uepm_unlock(ump, td);
777 * Vnode operating to retrieve a named extended attribute.
780 ufs_getextattr(struct vop_getextattr_args *ap)
783 IN struct vnode *a_vp;
784 IN int a_attrnamespace;
785 IN const char *a_name;
786 INOUT struct uio *a_uio;
788 IN struct ucred *a_cred;
789 IN struct thread *a_td;
793 struct mount *mp = ap->a_vp->v_mount;
794 struct ufsmount *ump = VFSTOUFS(mp);
797 ufs_extattr_uepm_lock(ump, ap->a_td);
799 error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
800 ap->a_uio, ap->a_size, ap->a_cred, ap->a_td);
802 ufs_extattr_uepm_unlock(ump, ap->a_td);
808 * Real work associated with retrieving a named attribute--assumes that
809 * the attribute lock has already been grabbed.
812 ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
813 struct uio *uio, size_t *size, struct ucred *cred, struct thread *td)
815 struct ufs_extattr_list_entry *attribute;
816 struct ufs_extattr_header ueh;
817 struct iovec local_aiov;
818 struct uio local_aio;
819 struct mount *mp = vp->v_mount;
820 struct ufsmount *ump = VFSTOUFS(mp);
821 struct inode *ip = VTOI(vp);
826 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
829 if (strlen(name) == 0)
832 error = extattr_check_cred(vp, attrnamespace, cred, td, IREAD);
836 attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
841 * Allow only offsets of zero to encourage the read/replace
842 * extended attribute semantic. Otherwise we can't guarantee
843 * atomicity, as we don't provide locks for extended attributes.
845 if (uio != NULL && uio->uio_offset != 0)
849 * Find base offset of header in file based on file header size, and
850 * data header size + maximum data size, indexed by inode number.
852 base_offset = sizeof(struct ufs_extattr_fileheader) +
853 ip->i_number * (sizeof(struct ufs_extattr_header) +
854 attribute->uele_fileheader.uef_size);
857 * Read in the data header to see if the data is defined, and if so
860 bzero(&ueh, sizeof(struct ufs_extattr_header));
861 local_aiov.iov_base = (caddr_t) &ueh;
862 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
863 local_aio.uio_iov = &local_aiov;
864 local_aio.uio_iovcnt = 1;
865 local_aio.uio_rw = UIO_READ;
866 local_aio.uio_segflg = UIO_SYSSPACE;
867 local_aio.uio_td = td;
868 local_aio.uio_offset = base_offset;
869 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
874 * Don't need to get a lock on the backing file if the getattr is
875 * being applied to the backing file, as the lock is already held.
877 if (attribute->uele_backing_vnode != vp)
878 vn_lock(attribute->uele_backing_vnode, LK_SHARED |
881 error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
882 IO_NODELOCKED, ump->um_extattr.uepm_ucred);
887 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
892 /* Valid for the current inode generation? */
893 if (ueh.ueh_i_gen != ip->i_gen) {
895 * The inode itself has a different generation number
896 * than the attribute data. For now, the best solution
897 * is to coerce this to undefined, and let it get cleaned
898 * up by the next write or extattrctl clean.
900 printf("ufs_extattr_get (%s): inode number inconsistency (%d, %jd)\n",
901 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen);
906 /* Local size consistency check. */
907 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
912 /* Return full data size if caller requested it. */
916 /* Return data if the caller requested it. */
918 /* Allow for offset into the attribute data. */
919 uio->uio_offset = base_offset + sizeof(struct
923 * Figure out maximum to transfer -- use buffer size and
926 len = MIN(uio->uio_resid, ueh.ueh_len);
927 old_len = uio->uio_resid;
928 uio->uio_resid = len;
930 error = VOP_READ(attribute->uele_backing_vnode, uio,
931 IO_NODELOCKED, ump->um_extattr.uepm_ucred);
935 uio->uio_resid = old_len - (len - uio->uio_resid);
943 if (attribute->uele_backing_vnode != vp)
944 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
950 * Vnode operation to remove a named attribute.
953 ufs_deleteextattr(struct vop_deleteextattr_args *ap)
956 IN struct vnode *a_vp;
957 IN int a_attrnamespace;
958 IN const char *a_name;
959 IN struct ucred *a_cred;
960 IN struct thread *a_td;
964 struct mount *mp = ap->a_vp->v_mount;
965 struct ufsmount *ump = VFSTOUFS(mp);
968 ufs_extattr_uepm_lock(ump, ap->a_td);
970 error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
971 ap->a_cred, ap->a_td);
974 ufs_extattr_uepm_unlock(ump, ap->a_td);
980 * Vnode operation to set a named attribute.
983 ufs_setextattr(struct vop_setextattr_args *ap)
986 IN struct vnode *a_vp;
987 IN int a_attrnamespace;
988 IN const char *a_name;
989 INOUT struct uio *a_uio;
990 IN struct ucred *a_cred;
991 IN struct thread *a_td;
995 struct mount *mp = ap->a_vp->v_mount;
996 struct ufsmount *ump = VFSTOUFS(mp);
999 ufs_extattr_uepm_lock(ump, ap->a_td);
1002 * XXX: No longer a supported way to delete extended attributes.
1004 if (ap->a_uio == NULL)
1007 error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1008 ap->a_uio, ap->a_cred, ap->a_td);
1010 ufs_extattr_uepm_unlock(ump, ap->a_td);
1016 * Real work associated with setting a vnode's extended attributes;
1017 * assumes that the attribute lock has already been grabbed.
1020 ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1021 struct uio *uio, struct ucred *cred, struct thread *td)
1023 struct ufs_extattr_list_entry *attribute;
1024 struct ufs_extattr_header ueh;
1025 struct iovec local_aiov;
1026 struct uio local_aio;
1027 struct mount *mp = vp->v_mount;
1028 struct ufsmount *ump = VFSTOUFS(mp);
1029 struct inode *ip = VTOI(vp);
1031 int error = 0, ioflag;
1033 if (vp->v_mount->mnt_flag & MNT_RDONLY)
1035 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1036 return (EOPNOTSUPP);
1037 if (!ufs_extattr_valid_attrname(attrnamespace, name))
1040 error = extattr_check_cred(vp, attrnamespace, cred, td, IWRITE);
1044 attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1049 * Early rejection of invalid offsets/length.
1050 * Reject: any offset but 0 (replace)
1051 * Any size greater than attribute size limit
1053 if (uio->uio_offset != 0 ||
1054 uio->uio_resid > attribute->uele_fileheader.uef_size)
1058 * Find base offset of header in file based on file header size, and
1059 * data header size + maximum data size, indexed by inode number.
1061 base_offset = sizeof(struct ufs_extattr_fileheader) +
1062 ip->i_number * (sizeof(struct ufs_extattr_header) +
1063 attribute->uele_fileheader.uef_size);
1066 * Write out a data header for the data.
1068 ueh.ueh_len = uio->uio_resid;
1069 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
1070 ueh.ueh_i_gen = ip->i_gen;
1071 local_aiov.iov_base = (caddr_t) &ueh;
1072 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1073 local_aio.uio_iov = &local_aiov;
1074 local_aio.uio_iovcnt = 1;
1075 local_aio.uio_rw = UIO_WRITE;
1076 local_aio.uio_segflg = UIO_SYSSPACE;
1077 local_aio.uio_td = td;
1078 local_aio.uio_offset = base_offset;
1079 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1084 * Don't need to get a lock on the backing file if the setattr is
1085 * being applied to the backing file, as the lock is already held.
1087 if (attribute->uele_backing_vnode != vp)
1088 vn_lock(attribute->uele_backing_vnode,
1089 LK_EXCLUSIVE | LK_RETRY, td);
1091 ioflag = IO_NODELOCKED;
1092 if (ufs_extattr_sync)
1094 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1095 ump->um_extattr.uepm_ucred);
1097 goto vopunlock_exit;
1099 if (local_aio.uio_resid != 0) {
1101 goto vopunlock_exit;
1105 * Write out user data.
1107 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1109 ioflag = IO_NODELOCKED;
1110 if (ufs_extattr_sync)
1112 error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1113 ump->um_extattr.uepm_ucred);
1116 uio->uio_offset = 0;
1118 if (attribute->uele_backing_vnode != vp)
1119 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1125 * Real work associated with removing an extended attribute from a vnode.
1126 * Assumes the attribute lock has already been grabbed.
1129 ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1130 struct ucred *cred, struct thread *td)
1132 struct ufs_extattr_list_entry *attribute;
1133 struct ufs_extattr_header ueh;
1134 struct iovec local_aiov;
1135 struct uio local_aio;
1136 struct mount *mp = vp->v_mount;
1137 struct ufsmount *ump = VFSTOUFS(mp);
1138 struct inode *ip = VTOI(vp);
1140 int error = 0, ioflag;
1142 if (vp->v_mount->mnt_flag & MNT_RDONLY)
1144 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1145 return (EOPNOTSUPP);
1146 if (!ufs_extattr_valid_attrname(attrnamespace, name))
1149 error = extattr_check_cred(vp, attrnamespace, cred, td, IWRITE);
1153 attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1158 * Find base offset of header in file based on file header size, and
1159 * data header size + maximum data size, indexed by inode number.
1161 base_offset = sizeof(struct ufs_extattr_fileheader) +
1162 ip->i_number * (sizeof(struct ufs_extattr_header) +
1163 attribute->uele_fileheader.uef_size);
1166 * Check to see if currently defined.
1168 bzero(&ueh, sizeof(struct ufs_extattr_header));
1170 local_aiov.iov_base = (caddr_t) &ueh;
1171 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1172 local_aio.uio_iov = &local_aiov;
1173 local_aio.uio_iovcnt = 1;
1174 local_aio.uio_rw = UIO_READ;
1175 local_aio.uio_segflg = UIO_SYSSPACE;
1176 local_aio.uio_td = td;
1177 local_aio.uio_offset = base_offset;
1178 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1181 * Don't need to get the lock on the backing vnode if the vnode we're
1182 * modifying is it, as we already hold the lock.
1184 if (attribute->uele_backing_vnode != vp)
1185 vn_lock(attribute->uele_backing_vnode,
1186 LK_EXCLUSIVE | LK_RETRY, td);
1188 error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
1189 IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1191 goto vopunlock_exit;
1194 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
1196 goto vopunlock_exit;
1199 /* Valid for the current inode generation? */
1200 if (ueh.ueh_i_gen != ip->i_gen) {
1202 * The inode itself has a different generation number than
1203 * the attribute data. For now, the best solution is to
1204 * coerce this to undefined, and let it get cleaned up by
1205 * the next write or extattrctl clean.
1207 printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n",
1208 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen);
1210 goto vopunlock_exit;
1213 /* Flag it as not in use. */
1217 local_aiov.iov_base = (caddr_t) &ueh;
1218 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1219 local_aio.uio_iov = &local_aiov;
1220 local_aio.uio_iovcnt = 1;
1221 local_aio.uio_rw = UIO_WRITE;
1222 local_aio.uio_segflg = UIO_SYSSPACE;
1223 local_aio.uio_td = td;
1224 local_aio.uio_offset = base_offset;
1225 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1227 ioflag = IO_NODELOCKED;
1228 if (ufs_extattr_sync)
1230 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1231 ump->um_extattr.uepm_ucred);
1233 goto vopunlock_exit;
1235 if (local_aio.uio_resid != 0)
1239 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td);
1245 * Called by UFS when an inode is no longer active and should have its
1246 * attributes stripped.
1249 ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td)
1251 struct ufs_extattr_list_entry *uele;
1252 struct mount *mp = vp->v_mount;
1253 struct ufsmount *ump = VFSTOUFS(mp);
1256 * In that case, we cannot lock. We should not have any active vnodes
1257 * on the fs if this is not yet initialized but is going to be, so
1258 * this can go unlocked.
1260 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1263 ufs_extattr_uepm_lock(ump, td);
1265 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1266 ufs_extattr_uepm_unlock(ump, td);
1270 LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1271 ufs_extattr_rm(vp, uele->uele_attrnamespace,
1272 uele->uele_attrname, NULL, td);
1274 ufs_extattr_uepm_unlock(ump, td);
1277 #endif /* !UFS_EXTATTR */