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>
59 #include <sys/sysctl.h>
63 #include <ufs/ufs/dir.h>
64 #include <ufs/ufs/extattr.h>
65 #include <ufs/ufs/quota.h>
66 #include <ufs/ufs/ufsmount.h>
67 #include <ufs/ufs/inode.h>
68 #include <ufs/ufs/ufs_extern.h>
72 static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
74 static int ufs_extattr_sync = 0;
75 SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync,
78 static int ufs_extattr_valid_attrname(int attrnamespace,
79 const char *attrname);
80 static int ufs_extattr_enable_with_open(struct ufsmount *ump,
81 struct vnode *vp, int attrnamespace, const char *attrname,
83 static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
84 const char *attrname, struct vnode *backing_vnode,
86 static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
87 const char *attrname, struct thread *td);
88 static int ufs_extattr_get(struct vnode *vp, int attrnamespace,
89 const char *name, struct uio *uio, size_t *size,
90 struct ucred *cred, struct thread *td);
91 static int ufs_extattr_set(struct vnode *vp, int attrnamespace,
92 const char *name, struct uio *uio, struct ucred *cred,
94 static int ufs_extattr_rm(struct vnode *vp, int attrnamespace,
95 const char *name, struct ucred *cred, struct thread *td);
98 * Per-FS attribute lock protecting attribute operations.
100 * XXXRW: Perhaps something more fine-grained would be appropriate, but at
101 * the end of the day we're going to contend on the vnode lock for the
102 * backing file anyway.
105 ufs_extattr_uepm_lock(struct ufsmount *ump, struct thread *td)
108 sx_xlock(&ump->um_extattr.uepm_lock);
112 ufs_extattr_uepm_unlock(struct ufsmount *ump, struct thread *td)
115 sx_xunlock(&ump->um_extattr.uepm_lock);
119 * Determine whether the name passed is a valid name for an actual
122 * Invalid currently consists of:
123 * NULL pointer for attrname
124 * zero-length attrname (used to retrieve application attribute list)
127 ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
130 if (attrname == NULL)
132 if (strlen(attrname) == 0)
138 * Locate an attribute given a name and mountpoint.
139 * Must be holding uepm lock for the mount point.
141 static struct ufs_extattr_list_entry *
142 ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
143 const char *attrname)
145 struct ufs_extattr_list_entry *search_attribute;
147 sx_assert(&ump->um_extattr.uepm_lock, SA_XLOCKED);
149 for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
150 search_attribute != NULL;
151 search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
152 if (!(strncmp(attrname, search_attribute->uele_attrname,
153 UFS_EXTATTR_MAXEXTATTRNAME)) &&
154 (attrnamespace == search_attribute->uele_attrnamespace)) {
155 return (search_attribute);
163 * Initialize per-FS structures supporting extended attributes. Do not
164 * start extended attributes yet.
167 ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
170 uepm->uepm_flags = 0;
171 LIST_INIT(&uepm->uepm_list);
172 sx_init(&uepm->uepm_lock, "ufs_extattr_sx");
173 uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
177 * Destroy per-FS structures supporting extended attributes. Assumes
178 * that EAs have already been stopped, and will panic if not.
181 ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
184 if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
185 panic("ufs_extattr_uepm_destroy: not initialized");
187 if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
188 panic("ufs_extattr_uepm_destroy: called while still started");
191 * It's not clear that either order for the next two lines is
192 * ideal, and it should never be a problem if this is only called
193 * during unmount, and with vfs_busy().
195 uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
196 sx_destroy(&uepm->uepm_lock);
200 * Start extended attribute support on an FS.
203 ufs_extattr_start(struct mount *mp, struct thread *td)
205 struct ufsmount *ump;
210 ufs_extattr_uepm_lock(ump, td);
212 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
216 if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
221 ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
223 ump->um_extattr.uepm_ucred = crhold(td->td_ucred);
226 ufs_extattr_uepm_unlock(ump, td);
231 #ifdef UFS_EXTATTR_AUTOSTART
233 * Helper routine: given a locked parent directory and filename, return
234 * the locked vnode of the inode associated with the name. Will not
235 * follow symlinks, may return any type of vnode. Lock on parent will
236 * be released even in the event of a failure. In the event that the
237 * target is the parent (i.e., "."), there will be two references and
238 * one lock, requiring the caller to possibly special-case.
240 #define UE_GETDIR_LOCKPARENT 1
241 #define UE_GETDIR_LOCKPARENT_DONT 2
243 ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname,
244 struct vnode **vp, struct thread *td)
246 struct vop_cachedlookup_args vargs;
247 struct componentname cnp;
248 struct vnode *target_vp;
251 bzero(&cnp, sizeof(cnp));
252 cnp.cn_nameiop = LOOKUP;
253 cnp.cn_flags = ISLASTCN;
254 if (lockparent == UE_GETDIR_LOCKPARENT)
255 cnp.cn_flags |= LOCKPARENT;
256 cnp.cn_lkflags = LK_EXCLUSIVE;
258 cnp.cn_cred = td->td_ucred;
259 cnp.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK);
260 cnp.cn_nameptr = cnp.cn_pnbuf;
261 error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN,
262 (size_t *) &cnp.cn_namelen);
264 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) {
265 VOP_UNLOCK(start_dvp, 0);
267 uma_zfree(namei_zone, cnp.cn_pnbuf);
268 printf("ufs_extattr_lookup: copystr failed\n");
271 cnp.cn_namelen--; /* trim nul termination */
272 vargs.a_gen.a_desc = NULL;
273 vargs.a_dvp = start_dvp;
274 vargs.a_vpp = &target_vp;
276 error = ufs_lookup(&vargs);
277 uma_zfree(namei_zone, cnp.cn_pnbuf);
280 * Error condition, may have to release the lock on the parent
281 * if ufs_lookup() didn't.
283 if (lockparent == UE_GETDIR_LOCKPARENT_DONT)
284 VOP_UNLOCK(start_dvp, 0);
287 * Check that ufs_lookup() didn't release the lock when we
290 if (lockparent == UE_GETDIR_LOCKPARENT)
291 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup");
296 if (target_vp == start_dvp)
297 panic("ufs_extattr_lookup: target_vp == start_dvp");
300 if (target_vp != start_dvp && lockparent == UE_GETDIR_LOCKPARENT_DONT)
301 VOP_UNLOCK(start_dvp, 0);
303 if (lockparent == UE_GETDIR_LOCKPARENT)
304 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup");
306 /* printf("ufs_extattr_lookup: success\n"); */
310 #endif /* !UFS_EXTATTR_AUTOSTART */
313 * Enable an EA using the passed filesystem, backing vnode, attribute name,
314 * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp
315 * to be locked when passed in. The vnode will be returned unlocked,
316 * regardless of success/failure of the function. As a result, the caller
317 * will always need to vrele(), but not vput().
320 ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
321 int attrnamespace, const char *attrname, struct thread *td)
325 error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, NULL);
327 printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
339 error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td);
341 vn_close(vp, FREAD|FWRITE, td->td_ucred, td);
345 #ifdef UFS_EXTATTR_AUTOSTART
347 * Given a locked directory vnode, iterate over the names in the directory
348 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
349 * attribute files. Then invoke ufs_extattr_enable_with_open() on each
350 * to attempt to start the attribute. Leaves the directory locked on
354 ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
355 int attrnamespace, struct thread *td)
357 struct vop_readdir_args vargs;
358 struct dirent *dp, *edp;
359 struct vnode *attr_vp;
363 int error, eofflag = 0;
365 if (dvp->v_type != VDIR)
368 MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK);
370 auio.uio_iov = &aiov;
372 auio.uio_rw = UIO_READ;
373 auio.uio_segflg = UIO_SYSSPACE;
377 vargs.a_gen.a_desc = NULL;
380 vargs.a_cred = td->td_ucred;
381 vargs.a_eofflag = &eofflag;
382 vargs.a_ncookies = NULL;
383 vargs.a_cookies = NULL;
386 auio.uio_resid = DIRBLKSIZ;
387 aiov.iov_base = dirbuf;
388 aiov.iov_len = DIRBLKSIZ;
389 error = ufs_readdir(&vargs);
391 printf("ufs_extattr_iterate_directory: ufs_readdir "
397 * XXXRW: While in UFS, we always get DIRBLKSIZ returns from
398 * the directory code on success, on other file systems this
399 * may not be the case. For portability, we should check the
400 * read length on return from ufs_readdir().
402 edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
403 for (dp = (struct dirent *)dirbuf; dp < edp; ) {
404 #if (BYTE_ORDER == LITTLE_ENDIAN)
405 dp->d_type = dp->d_namlen;
410 if (dp->d_reclen == 0)
412 error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT,
413 dp->d_name, &attr_vp, td);
415 printf("ufs_extattr_iterate_directory: lookup "
416 "%s %d\n", dp->d_name, error);
417 } else if (attr_vp == dvp) {
419 } else if (attr_vp->v_type != VREG) {
422 error = ufs_extattr_enable_with_open(ump,
423 attr_vp, attrnamespace, dp->d_name, td);
426 printf("ufs_extattr_iterate_directory: "
427 "enable %s %d\n", dp->d_name,
429 } else if (bootverbose) {
430 printf("UFS autostarted EA %s\n",
434 dp = (struct dirent *) ((char *)dp + dp->d_reclen);
439 FREE(dirbuf, M_TEMP);
445 * Auto-start of extended attributes, to be executed (optionally) at
449 ufs_extattr_autostart(struct mount *mp, struct thread *td)
451 struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
452 struct ufsmount *ump = VFSTOUFS(mp);
456 * UFS_EXTATTR applies only to UFS1, as UFS2 uses native extended
457 * attributes, so don't autostart.
459 if (ump->um_fstype != UFS1)
463 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
464 * If so, automatically start EA's.
466 error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp, td);
468 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
473 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT,
474 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td);
476 /* rvp ref'd but now unlocked */
480 if (rvp == attr_dvp) {
481 /* Should never happen. */
488 if (attr_dvp->v_type != VDIR) {
489 printf("ufs_extattr_autostart: %s != VDIR\n",
490 UFS_EXTATTR_FSROOTSUBDIR);
491 goto return_vput_attr_dvp;
494 error = ufs_extattr_start(mp, td);
496 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
498 goto return_vput_attr_dvp;
502 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
503 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory,
504 * and start with appropriate type. Failures in either don't
505 * result in an over-all failure. attr_dvp is left locked to
506 * be cleaned up on exit.
508 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
509 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td);
511 error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
512 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td);
514 printf("ufs_extattr_iterate_directory returned %d\n",
516 vput(attr_system_dvp);
519 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT,
520 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td);
522 error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
523 attr_user_dvp, EXTATTR_NAMESPACE_USER, td);
525 printf("ufs_extattr_iterate_directory returned %d\n",
530 /* Mask startup failures in sub-directories. */
533 return_vput_attr_dvp:
538 #endif /* !UFS_EXTATTR_AUTOSTART */
541 * Stop extended attribute support on an FS.
544 ufs_extattr_stop(struct mount *mp, struct thread *td)
546 struct ufs_extattr_list_entry *uele;
547 struct ufsmount *ump = VFSTOUFS(mp);
550 ufs_extattr_uepm_lock(ump, td);
552 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
557 while ((uele = LIST_FIRST(&ump->um_extattr.uepm_list)) != NULL) {
558 ufs_extattr_disable(ump, uele->uele_attrnamespace,
559 uele->uele_attrname, td);
562 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
564 crfree(ump->um_extattr.uepm_ucred);
565 ump->um_extattr.uepm_ucred = NULL;
568 ufs_extattr_uepm_unlock(ump, td);
574 * Enable a named attribute on the specified filesystem; provide an
575 * unlocked backing vnode to hold the attribute data.
578 ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
579 const char *attrname, struct vnode *backing_vnode, struct thread *td)
581 struct ufs_extattr_list_entry *attribute;
586 if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
588 if (backing_vnode->v_type != VREG)
591 MALLOC(attribute, struct ufs_extattr_list_entry *,
592 sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK);
593 if (attribute == NULL)
596 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
601 if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
606 strncpy(attribute->uele_attrname, attrname,
607 UFS_EXTATTR_MAXEXTATTRNAME);
608 attribute->uele_attrnamespace = attrnamespace;
609 bzero(&attribute->uele_fileheader,
610 sizeof(struct ufs_extattr_fileheader));
612 attribute->uele_backing_vnode = backing_vnode;
614 auio.uio_iov = &aiov;
616 aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
617 aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
618 auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
619 auio.uio_offset = (off_t) 0;
620 auio.uio_segflg = UIO_SYSSPACE;
621 auio.uio_rw = UIO_READ;
624 vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
625 error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
626 ump->um_extattr.uepm_ucred);
629 goto unlock_free_exit;
631 if (auio.uio_resid != 0) {
632 printf("ufs_extattr_enable: malformed attribute header\n");
634 goto unlock_free_exit;
637 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
638 printf("ufs_extattr_enable: invalid attribute header magic\n");
640 goto unlock_free_exit;
643 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
644 printf("ufs_extattr_enable: incorrect attribute header "
647 goto unlock_free_exit;
650 ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable");
651 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
654 VOP_UNLOCK(backing_vnode, 0);
658 VOP_UNLOCK(backing_vnode, 0);
661 FREE(attribute, M_UFS_EXTATTR);
666 * Disable extended attribute support on an FS.
669 ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
670 const char *attrname, struct thread *td)
672 struct ufs_extattr_list_entry *uele;
675 if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
678 uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
682 LIST_REMOVE(uele, uele_entries);
684 vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY);
685 ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable");
686 VOP_UNLOCK(uele->uele_backing_vnode, 0);
687 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
690 FREE(uele, M_UFS_EXTATTR);
696 * VFS call to manage extended attributes in UFS. If filename_vp is
697 * non-NULL, it must be passed in locked, and regardless of errors in
698 * processing, will be unlocked.
701 ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
702 int attrnamespace, const char *attrname, struct thread *td)
704 struct ufsmount *ump = VFSTOUFS(mp);
708 * Processes with privilege, but in jail, are not allowed to
709 * configure extended attributes.
711 error = priv_check(td, PRIV_UFS_EXTATTRCTL);
713 if (filename_vp != NULL)
714 VOP_UNLOCK(filename_vp, 0);
719 * We only allow extattrctl(2) on UFS1 file systems, as UFS2 uses
720 * native extended attributes.
722 if (ump->um_fstype != UFS1) {
723 if (filename_vp != NULL)
724 VOP_UNLOCK(filename_vp, 0);
729 case UFS_EXTATTR_CMD_START:
730 if (filename_vp != NULL) {
731 VOP_UNLOCK(filename_vp, 0);
734 if (attrname != NULL)
737 error = ufs_extattr_start(mp, td);
741 case UFS_EXTATTR_CMD_STOP:
742 if (filename_vp != NULL) {
743 VOP_UNLOCK(filename_vp, 0);
746 if (attrname != NULL)
749 error = ufs_extattr_stop(mp, td);
753 case UFS_EXTATTR_CMD_ENABLE:
755 if (filename_vp == NULL)
757 if (attrname == NULL) {
758 VOP_UNLOCK(filename_vp, 0);
763 * ufs_extattr_enable_with_open() will always unlock the
764 * vnode, regardless of failure.
766 ufs_extattr_uepm_lock(ump, td);
767 error = ufs_extattr_enable_with_open(ump, filename_vp,
768 attrnamespace, attrname, td);
769 ufs_extattr_uepm_unlock(ump, td);
773 case UFS_EXTATTR_CMD_DISABLE:
775 if (filename_vp != NULL) {
776 VOP_UNLOCK(filename_vp, 0);
779 if (attrname == NULL)
782 ufs_extattr_uepm_lock(ump, td);
783 error = ufs_extattr_disable(ump, attrnamespace, attrname,
785 ufs_extattr_uepm_unlock(ump, td);
795 * Vnode operating to retrieve a named extended attribute.
798 ufs_getextattr(struct vop_getextattr_args *ap)
801 IN struct vnode *a_vp;
802 IN int a_attrnamespace;
803 IN const char *a_name;
804 INOUT struct uio *a_uio;
806 IN struct ucred *a_cred;
807 IN struct thread *a_td;
811 struct mount *mp = ap->a_vp->v_mount;
812 struct ufsmount *ump = VFSTOUFS(mp);
815 ufs_extattr_uepm_lock(ump, ap->a_td);
817 error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
818 ap->a_uio, ap->a_size, ap->a_cred, ap->a_td);
820 ufs_extattr_uepm_unlock(ump, ap->a_td);
826 * Real work associated with retrieving a named attribute--assumes that
827 * the attribute lock has already been grabbed.
830 ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
831 struct uio *uio, size_t *size, struct ucred *cred, struct thread *td)
833 struct ufs_extattr_list_entry *attribute;
834 struct ufs_extattr_header ueh;
835 struct iovec local_aiov;
836 struct uio local_aio;
837 struct mount *mp = vp->v_mount;
838 struct ufsmount *ump = VFSTOUFS(mp);
839 struct inode *ip = VTOI(vp);
844 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
847 if (strlen(name) == 0)
850 error = extattr_check_cred(vp, attrnamespace, cred, td, VREAD);
854 attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
859 * Allow only offsets of zero to encourage the read/replace
860 * extended attribute semantic. Otherwise we can't guarantee
861 * atomicity, as we don't provide locks for extended attributes.
863 if (uio != NULL && uio->uio_offset != 0)
867 * Find base offset of header in file based on file header size, and
868 * data header size + maximum data size, indexed by inode number.
870 base_offset = sizeof(struct ufs_extattr_fileheader) +
871 ip->i_number * (sizeof(struct ufs_extattr_header) +
872 attribute->uele_fileheader.uef_size);
875 * Read in the data header to see if the data is defined, and if so
878 bzero(&ueh, sizeof(struct ufs_extattr_header));
879 local_aiov.iov_base = (caddr_t) &ueh;
880 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
881 local_aio.uio_iov = &local_aiov;
882 local_aio.uio_iovcnt = 1;
883 local_aio.uio_rw = UIO_READ;
884 local_aio.uio_segflg = UIO_SYSSPACE;
885 local_aio.uio_td = td;
886 local_aio.uio_offset = base_offset;
887 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
892 * Don't need to get a lock on the backing file if the getattr is
893 * being applied to the backing file, as the lock is already held.
895 if (attribute->uele_backing_vnode != vp)
896 vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY);
898 error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
899 IO_NODELOCKED, ump->um_extattr.uepm_ucred);
904 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
909 /* Valid for the current inode generation? */
910 if (ueh.ueh_i_gen != ip->i_gen) {
912 * The inode itself has a different generation number
913 * than the attribute data. For now, the best solution
914 * is to coerce this to undefined, and let it get cleaned
915 * up by the next write or extattrctl clean.
917 printf("ufs_extattr_get (%s): inode number inconsistency (%d, %jd)\n",
918 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen);
923 /* Local size consistency check. */
924 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
929 /* Return full data size if caller requested it. */
933 /* Return data if the caller requested it. */
935 /* Allow for offset into the attribute data. */
936 uio->uio_offset = base_offset + sizeof(struct
940 * Figure out maximum to transfer -- use buffer size and
943 len = MIN(uio->uio_resid, ueh.ueh_len);
944 old_len = uio->uio_resid;
945 uio->uio_resid = len;
947 error = VOP_READ(attribute->uele_backing_vnode, uio,
948 IO_NODELOCKED, ump->um_extattr.uepm_ucred);
952 uio->uio_resid = old_len - (len - uio->uio_resid);
960 if (attribute->uele_backing_vnode != vp)
961 VOP_UNLOCK(attribute->uele_backing_vnode, 0);
967 * Vnode operation to remove a named attribute.
970 ufs_deleteextattr(struct vop_deleteextattr_args *ap)
973 IN struct vnode *a_vp;
974 IN int a_attrnamespace;
975 IN const char *a_name;
976 IN struct ucred *a_cred;
977 IN struct thread *a_td;
981 struct mount *mp = ap->a_vp->v_mount;
982 struct ufsmount *ump = VFSTOUFS(mp);
985 ufs_extattr_uepm_lock(ump, ap->a_td);
987 error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
988 ap->a_cred, ap->a_td);
991 ufs_extattr_uepm_unlock(ump, ap->a_td);
997 * Vnode operation to set a named attribute.
1000 ufs_setextattr(struct vop_setextattr_args *ap)
1003 IN struct vnode *a_vp;
1004 IN int a_attrnamespace;
1005 IN const char *a_name;
1006 INOUT struct uio *a_uio;
1007 IN struct ucred *a_cred;
1008 IN struct thread *a_td;
1012 struct mount *mp = ap->a_vp->v_mount;
1013 struct ufsmount *ump = VFSTOUFS(mp);
1016 ufs_extattr_uepm_lock(ump, ap->a_td);
1019 * XXX: No longer a supported way to delete extended attributes.
1021 if (ap->a_uio == NULL)
1024 error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
1025 ap->a_uio, ap->a_cred, ap->a_td);
1027 ufs_extattr_uepm_unlock(ump, ap->a_td);
1033 * Real work associated with setting a vnode's extended attributes;
1034 * assumes that the attribute lock has already been grabbed.
1037 ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
1038 struct uio *uio, struct ucred *cred, struct thread *td)
1040 struct ufs_extattr_list_entry *attribute;
1041 struct ufs_extattr_header ueh;
1042 struct iovec local_aiov;
1043 struct uio local_aio;
1044 struct mount *mp = vp->v_mount;
1045 struct ufsmount *ump = VFSTOUFS(mp);
1046 struct inode *ip = VTOI(vp);
1048 int error = 0, ioflag;
1050 if (vp->v_mount->mnt_flag & MNT_RDONLY)
1052 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1053 return (EOPNOTSUPP);
1054 if (!ufs_extattr_valid_attrname(attrnamespace, name))
1057 error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE);
1061 attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1066 * Early rejection of invalid offsets/length.
1067 * Reject: any offset but 0 (replace)
1068 * Any size greater than attribute size limit
1070 if (uio->uio_offset != 0 ||
1071 uio->uio_resid > attribute->uele_fileheader.uef_size)
1075 * Find base offset of header in file based on file header size, and
1076 * data header size + maximum data size, indexed by inode number.
1078 base_offset = sizeof(struct ufs_extattr_fileheader) +
1079 ip->i_number * (sizeof(struct ufs_extattr_header) +
1080 attribute->uele_fileheader.uef_size);
1083 * Write out a data header for the data.
1085 ueh.ueh_len = uio->uio_resid;
1086 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE;
1087 ueh.ueh_i_gen = ip->i_gen;
1088 local_aiov.iov_base = (caddr_t) &ueh;
1089 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1090 local_aio.uio_iov = &local_aiov;
1091 local_aio.uio_iovcnt = 1;
1092 local_aio.uio_rw = UIO_WRITE;
1093 local_aio.uio_segflg = UIO_SYSSPACE;
1094 local_aio.uio_td = td;
1095 local_aio.uio_offset = base_offset;
1096 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1101 * Don't need to get a lock on the backing file if the setattr is
1102 * being applied to the backing file, as the lock is already held.
1104 if (attribute->uele_backing_vnode != vp)
1105 vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
1107 ioflag = IO_NODELOCKED;
1108 if (ufs_extattr_sync)
1110 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1111 ump->um_extattr.uepm_ucred);
1113 goto vopunlock_exit;
1115 if (local_aio.uio_resid != 0) {
1117 goto vopunlock_exit;
1121 * Write out user data.
1123 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
1125 ioflag = IO_NODELOCKED;
1126 if (ufs_extattr_sync)
1128 error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
1129 ump->um_extattr.uepm_ucred);
1132 uio->uio_offset = 0;
1134 if (attribute->uele_backing_vnode != vp)
1135 VOP_UNLOCK(attribute->uele_backing_vnode, 0);
1141 * Real work associated with removing an extended attribute from a vnode.
1142 * Assumes the attribute lock has already been grabbed.
1145 ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
1146 struct ucred *cred, struct thread *td)
1148 struct ufs_extattr_list_entry *attribute;
1149 struct ufs_extattr_header ueh;
1150 struct iovec local_aiov;
1151 struct uio local_aio;
1152 struct mount *mp = vp->v_mount;
1153 struct ufsmount *ump = VFSTOUFS(mp);
1154 struct inode *ip = VTOI(vp);
1156 int error = 0, ioflag;
1158 if (vp->v_mount->mnt_flag & MNT_RDONLY)
1160 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
1161 return (EOPNOTSUPP);
1162 if (!ufs_extattr_valid_attrname(attrnamespace, name))
1165 error = extattr_check_cred(vp, attrnamespace, cred, td, VWRITE);
1169 attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
1174 * Find base offset of header in file based on file header size, and
1175 * data header size + maximum data size, indexed by inode number.
1177 base_offset = sizeof(struct ufs_extattr_fileheader) +
1178 ip->i_number * (sizeof(struct ufs_extattr_header) +
1179 attribute->uele_fileheader.uef_size);
1182 * Check to see if currently defined.
1184 bzero(&ueh, sizeof(struct ufs_extattr_header));
1186 local_aiov.iov_base = (caddr_t) &ueh;
1187 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1188 local_aio.uio_iov = &local_aiov;
1189 local_aio.uio_iovcnt = 1;
1190 local_aio.uio_rw = UIO_READ;
1191 local_aio.uio_segflg = UIO_SYSSPACE;
1192 local_aio.uio_td = td;
1193 local_aio.uio_offset = base_offset;
1194 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1197 * Don't need to get the lock on the backing vnode if the vnode we're
1198 * modifying is it, as we already hold the lock.
1200 if (attribute->uele_backing_vnode != vp)
1201 vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY);
1203 error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
1204 IO_NODELOCKED, ump->um_extattr.uepm_ucred);
1206 goto vopunlock_exit;
1209 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
1211 goto vopunlock_exit;
1214 /* Valid for the current inode generation? */
1215 if (ueh.ueh_i_gen != ip->i_gen) {
1217 * The inode itself has a different generation number than
1218 * the attribute data. For now, the best solution is to
1219 * coerce this to undefined, and let it get cleaned up by
1220 * the next write or extattrctl clean.
1222 printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n",
1223 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen);
1225 goto vopunlock_exit;
1228 /* Flag it as not in use. */
1232 local_aiov.iov_base = (caddr_t) &ueh;
1233 local_aiov.iov_len = sizeof(struct ufs_extattr_header);
1234 local_aio.uio_iov = &local_aiov;
1235 local_aio.uio_iovcnt = 1;
1236 local_aio.uio_rw = UIO_WRITE;
1237 local_aio.uio_segflg = UIO_SYSSPACE;
1238 local_aio.uio_td = td;
1239 local_aio.uio_offset = base_offset;
1240 local_aio.uio_resid = sizeof(struct ufs_extattr_header);
1242 ioflag = IO_NODELOCKED;
1243 if (ufs_extattr_sync)
1245 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
1246 ump->um_extattr.uepm_ucred);
1248 goto vopunlock_exit;
1250 if (local_aio.uio_resid != 0)
1254 VOP_UNLOCK(attribute->uele_backing_vnode, 0);
1260 * Called by UFS when an inode is no longer active and should have its
1261 * attributes stripped.
1264 ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td)
1266 struct ufs_extattr_list_entry *uele;
1267 struct mount *mp = vp->v_mount;
1268 struct ufsmount *ump = VFSTOUFS(mp);
1271 * In that case, we cannot lock. We should not have any active vnodes
1272 * on the fs if this is not yet initialized but is going to be, so
1273 * this can go unlocked.
1275 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
1278 ufs_extattr_uepm_lock(ump, td);
1280 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
1281 ufs_extattr_uepm_unlock(ump, td);
1285 LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
1286 ufs_extattr_rm(vp, uele->uele_attrnamespace,
1287 uele->uele_attrname, NULL, td);
1289 ufs_extattr_uepm_unlock(ump, td);
1292 #endif /* !UFS_EXTATTR */