Annotation of src/sys/ufs/ufs/ufs_extattr.c, Revision 1.9.4.2
1.9.4.2 ! yamt 1: /* $NetBSD: ufs_extattr.c,v 1.9.4.1 2006/06/21 15:12:39 yamt Exp $ */
! 2:
! 3: /*-
! 4: * Copyright (c) 1999-2002 Robert N. M. Watson
! 5: * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
! 6: * All rights reserved.
! 7: *
! 8: * This software was developed by Robert Watson for the TrustedBSD Project.
! 9: *
! 10: * This software was developed for the FreeBSD Project in part by Network
! 11: * Associates Laboratories, the Security Research Division of Network
! 12: * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"),
! 13: * as part of the DARPA CHATS research program.
! 14: *
! 15: * Redistribution and use in source and binary forms, with or without
! 16: * modification, are permitted provided that the following conditions
! 17: * are met:
! 18: * 1. Redistributions of source code must retain the above copyright
! 19: * notice, this list of conditions and the following disclaimer.
! 20: * 2. Redistributions in binary form must reproduce the above copyright
! 21: * notice, this list of conditions and the following disclaimer in the
! 22: * documentation and/or other materials provided with the distribution.
! 23: *
! 24: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
! 25: * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
! 26: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
! 27: * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
! 28: * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
! 29: * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
! 30: * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 31: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
! 32: * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
! 33: * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
! 34: * SUCH DAMAGE.
! 35: *
! 36: */
! 37:
! 38: /*
! 39: * Support for file system extended attributes on the UFS1 file system.
! 40: *
! 41: * Extended attributes are defined in the form name=value, where name is
! 42: * a nul-terminated string in the style of a file name, and value is a
! 43: * binary blob of zero or more bytes. The UFS1 extended attribute service
! 44: * layers support for extended attributes onto a backing file, in the style
! 45: * of the quota implementation, meaning that it requires no underlying format
! 46: * changes to the file system. This design choice exchanges simplicity,
! 47: * usability, and easy deployment for performance.
! 48: */
! 49:
! 50: #include <sys/cdefs.h>
! 51: __RCSID("$NetBSD: ufs_extattr.c,v 1.9.4.1 2006/06/21 15:12:39 yamt Exp $");
! 52:
! 53: #include "opt_ffs.h"
! 54:
! 55: #include <sys/param.h>
! 56: #include <sys/systm.h>
! 57: #include <sys/reboot.h>
! 58: #include <sys/kauth.h>
! 59: #include <sys/kernel.h>
! 60: #include <sys/namei.h>
! 61: #include <sys/malloc.h>
! 62: #include <sys/fcntl.h>
! 63: #include <sys/lwp.h>
! 64: #include <sys/vnode.h>
! 65: #include <sys/mount.h>
! 66: #include <sys/lock.h>
! 67: #include <sys/dirent.h>
! 68: #include <sys/extattr.h>
! 69: #include <sys/sysctl.h>
! 70:
! 71: #include <ufs/ufs/dir.h>
! 72: #include <ufs/ufs/extattr.h>
! 73: #include <ufs/ufs/quota.h>
! 74: #include <ufs/ufs/ufsmount.h>
! 75: #include <ufs/ufs/inode.h>
! 76: #include <ufs/ufs/ufs_bswap.h>
! 77: #include <ufs/ufs/ufs_extern.h>
! 78:
! 79: static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute");
! 80:
! 81: int ufs_extattr_sync = 1;
! 82:
! 83: static int ufs_extattr_valid_attrname(int attrnamespace,
! 84: const char *attrname);
! 85: static int ufs_extattr_enable_with_open(struct ufsmount *ump,
! 86: struct vnode *vp, int attrnamespace, const char *attrname,
! 87: struct lwp *l);
! 88: static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
! 89: const char *attrname, struct vnode *backing_vnode,
! 90: struct lwp *l);
! 91: static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
! 92: const char *attrname, struct lwp *l);
! 93: static int ufs_extattr_get(struct vnode *vp, int attrnamespace,
! 94: const char *name, struct uio *uio, size_t *size,
! 95: kauth_cred_t cred, struct lwp *l);
! 96: static int ufs_extattr_set(struct vnode *vp, int attrnamespace,
! 97: const char *name, struct uio *uio, kauth_cred_t cred,
! 98: struct lwp *l);
! 99: static int ufs_extattr_rm(struct vnode *vp, int attrnamespace,
! 100: const char *name, kauth_cred_t cred, struct lwp *l);
! 101:
! 102: /*
! 103: * Per-FS attribute lock protecting attribute operations.
! 104: * XXX Right now there is a lot of lock contention due to having a single
! 105: * lock per-FS; really, this should be far more fine-grained.
! 106: */
! 107: static void
! 108: ufs_extattr_uepm_lock(struct ufsmount *ump)
! 109: {
! 110:
! 111: /* Ideally, LK_CANRECURSE would not be used, here. */
! 112: lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE |
! 113: LK_CANRECURSE, NULL);
! 114: }
! 115:
! 116: static void
! 117: ufs_extattr_uepm_unlock(struct ufsmount *ump)
! 118: {
! 119:
! 120: lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, NULL);
! 121: }
! 122:
! 123: /*-
! 124: * Determine whether the name passed is a valid name for an actual
! 125: * attribute.
! 126: *
! 127: * Invalid currently consists of:
! 128: * NULL pointer for attrname
! 129: * zero-length attrname (used to retrieve application attribute list)
! 130: */
! 131: static int
! 132: ufs_extattr_valid_attrname(int attrnamespace, const char *attrname)
! 133: {
! 134:
! 135: if (attrname == NULL)
! 136: return (0);
! 137: if (strlen(attrname) == 0)
! 138: return (0);
! 139: return (1);
! 140: }
! 141:
! 142: /*
! 143: * Locate an attribute given a name and mountpoint.
! 144: * Must be holding uepm lock for the mount point.
! 145: */
! 146: static struct ufs_extattr_list_entry *
! 147: ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace,
! 148: const char *attrname)
! 149: {
! 150: struct ufs_extattr_list_entry *search_attribute;
! 151:
! 152: for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list);
! 153: search_attribute != NULL;
! 154: search_attribute = LIST_NEXT(search_attribute, uele_entries)) {
! 155: if (!(strncmp(attrname, search_attribute->uele_attrname,
! 156: UFS_EXTATTR_MAXEXTATTRNAME)) &&
! 157: (attrnamespace == search_attribute->uele_attrnamespace)) {
! 158: return (search_attribute);
! 159: }
! 160: }
! 161:
! 162: return (0);
! 163: }
! 164:
! 165: /*
! 166: * Initialize per-FS structures supporting extended attributes. Do not
! 167: * start extended attributes yet.
! 168: */
! 169: void
! 170: ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm)
! 171: {
! 172:
! 173: uepm->uepm_flags = 0;
! 174:
! 175: LIST_INIT(&uepm->uepm_list);
! 176: /* XXX is PVFS right, here? */
! 177: lockinit(&uepm->uepm_lock, PVFS, "ufsea", 0, 0);
! 178: uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED;
! 179: }
! 180:
! 181: /*
! 182: * Destroy per-FS structures supporting extended attributes. Assumes
! 183: * that EAs have already been stopped, and will panic if not.
! 184: */
! 185: void
! 186: ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm)
! 187: {
! 188:
! 189: if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
! 190: panic("ufs_extattr_uepm_destroy: not initialized");
! 191:
! 192: if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED))
! 193: panic("ufs_extattr_uepm_destroy: called while still started");
! 194:
! 195: /*
! 196: * It's not clear that either order for the next two lines is
! 197: * ideal, and it should never be a problem if this is only called
! 198: * during unmount, and with vfs_busy().
! 199: */
! 200: uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED;
! 201: lockmgr(&uepm->uepm_lock, LK_DRAIN, NULL);
! 202: }
! 203:
! 204: /*
! 205: * Start extended attribute support on an FS.
! 206: */
! 207: int
! 208: ufs_extattr_start(struct mount *mp, struct lwp *l)
! 209: {
! 210: struct ufsmount *ump;
! 211: int error = 0;
! 212:
! 213: ump = VFSTOUFS(mp);
! 214:
! 215: ufs_extattr_uepm_lock(ump);
! 216:
! 217: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) {
! 218: error = EOPNOTSUPP;
! 219: goto unlock;
! 220: }
! 221: if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) {
! 222: error = EBUSY;
! 223: goto unlock;
! 224: }
! 225:
! 226: ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED;
! 227:
! 228: ump->um_extattr.uepm_ucred = l->l_proc->p_cred;
! 229: kauth_cred_hold(ump->um_extattr.uepm_ucred);
! 230:
! 231: unlock:
! 232: ufs_extattr_uepm_unlock(ump);
! 233:
! 234: return (error);
! 235: }
! 236:
! 237: #ifdef UFS_EXTATTR_AUTOSTART
! 238: /*
! 239: * Helper routine: given a locked parent directory and filename, return
! 240: * the locked vnode of the inode associated with the name. Will not
! 241: * follow symlinks, may return any type of vnode. Lock on parent will
! 242: * be released even in the event of a failure. In the event that the
! 243: * target is the parent (i.e., "."), there will be two references and
! 244: * one lock, requiring the caller to possibly special-case.
! 245: */
! 246: static int
! 247: ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, const char *dirname,
! 248: struct vnode **vp, struct lwp *l)
! 249: {
! 250: struct vop_lookup_args vargs;
! 251: struct componentname cnp;
! 252: struct vnode *target_vp;
! 253: int error;
! 254:
! 255: KASSERT(VOP_ISLOCKED(start_dvp) == LK_EXCLUSIVE);
! 256:
! 257: memset(&cnp, 0, sizeof(cnp));
! 258: cnp.cn_nameiop = LOOKUP;
! 259: cnp.cn_flags = ISLASTCN | lockparent;
! 260: cnp.cn_lwp = l;
! 261: cnp.cn_cred = l->l_proc->p_cred;
! 262: cnp.cn_pnbuf = PNBUF_GET();
! 263: cnp.cn_nameptr = cnp.cn_pnbuf;
! 264: error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN,
! 265: (size_t *) &cnp.cn_namelen);
! 266: if (error) {
! 267: if (lockparent == 0) {
! 268: VOP_UNLOCK(start_dvp, 0);
! 269: }
! 270: PNBUF_PUT(cnp.cn_pnbuf);
! 271: printf("ufs_extattr_lookup: copystr failed\n");
! 272: return (error);
! 273: }
! 274: cnp.cn_namelen--; /* trim nul termination */
! 275: vargs.a_desc = NULL;
! 276: vargs.a_dvp = start_dvp;
! 277: vargs.a_vpp = &target_vp;
! 278: vargs.a_cnp = &cnp;
! 279: error = ufs_lookup(&vargs);
! 280: PNBUF_PUT(cnp.cn_pnbuf);
! 281: if (error) {
! 282: /*
! 283: * Error condition, may have to release the lock on the parent
! 284: * if ufs_lookup() didn't.
! 285: */
! 286: if ((cnp.cn_flags & PDIRUNLOCK) == 0) {
! 287: KASSERT(VOP_ISLOCKED(start_dvp) == LK_EXCLUSIVE);
! 288: if (lockparent == 0)
! 289: VOP_UNLOCK(start_dvp, 0);
! 290: }
! 291:
! 292: /*
! 293: * Check that ufs_lookup() didn't release the lock when we
! 294: * didn't want it to.
! 295: */
! 296: if ((cnp.cn_flags & PDIRUNLOCK) && lockparent)
! 297: panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK");
! 298:
! 299: return (error);
! 300: }
! 301: #if 0
! 302: if (target_vp == start_dvp)
! 303: panic("ufs_extattr_lookup: target_vp == start_dvp");
! 304: #endif
! 305:
! 306: KASSERT(VOP_ISLOCKED(target_vp) == LK_EXCLUSIVE);
! 307:
! 308: if (target_vp != start_dvp &&
! 309: (cnp.cn_flags & PDIRUNLOCK) == 0 && lockparent == 0)
! 310: panic("ufs_extattr_lookup: !lockparent but !PDIRUNLOCK");
! 311:
! 312: if ((cnp.cn_flags & PDIRUNLOCK) && lockparent)
! 313: panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK");
! 314:
! 315: *vp = target_vp;
! 316: return (0);
! 317: }
! 318: #endif /* !UFS_EXTATTR_AUTOSTART */
! 319:
! 320: /*
! 321: * Enable an EA using the passed filesystem, backing vnode, attribute name,
! 322: * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp
! 323: * to be locked when passed in. The vnode will be returned unlocked,
! 324: * regardless of success/failure of the function. As a result, the caller
! 325: * will always need to vrele(), but not vput().
! 326: */
! 327: static int
! 328: ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp,
! 329: int attrnamespace, const char *attrname, struct lwp *l)
! 330: {
! 331: int error;
! 332:
! 333: error = VOP_OPEN(vp, FREAD|FWRITE, l->l_proc->p_cred, l);
! 334: if (error) {
! 335: printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed "
! 336: "with %d\n", error);
! 337: VOP_UNLOCK(vp, 0);
! 338: return (error);
! 339: }
! 340:
! 341: vp->v_writecount++;
! 342:
! 343: vref(vp);
! 344:
! 345: VOP_UNLOCK(vp, 0);
! 346:
! 347: error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, l);
! 348: if (error != 0)
! 349: vn_close(vp, FREAD|FWRITE, l->l_proc->p_cred, l);
! 350: return (error);
! 351: }
! 352:
! 353: #ifdef UFS_EXTATTR_AUTOSTART
! 354: /*
! 355: * Given a locked directory vnode, iterate over the names in the directory
! 356: * and use ufs_extattr_lookup() to retrieve locked vnodes of potential
! 357: * attribute files. Then invoke ufs_extattr_enable_with_open() on each
! 358: * to attempt to start the attribute. Leaves the directory locked on
! 359: * exit.
! 360: */
! 361: static int
! 362: ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp,
! 363: int attrnamespace, struct lwp *l)
! 364: {
! 365: struct vop_readdir_args vargs;
! 366: struct dirent *dp, *edp;
! 367: struct vnode *attr_vp;
! 368: struct uio auio;
! 369: struct iovec aiov;
! 370: char *dirbuf;
! 371: int error, eofflag = 0;
! 372:
! 373: if (dvp->v_type != VDIR)
! 374: return (ENOTDIR);
! 375:
! 376: MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK);
! 377:
! 378: auio.uio_iov = &aiov;
! 379: auio.uio_iovcnt = 1;
! 380: auio.uio_rw = UIO_READ;
! 381: auio.uio_offset = 0;
! 382: UIO_SETUP_SYSSPACE(&auio);
! 383:
! 384: vargs.a_desc = NULL;
! 385: vargs.a_vp = dvp;
! 386: vargs.a_uio = &auio;
! 387: vargs.a_cred = l->l_proc->p_cred;
! 388: vargs.a_eofflag = &eofflag;
! 389: vargs.a_ncookies = NULL;
! 390: vargs.a_cookies = NULL;
! 391:
! 392: while (!eofflag) {
! 393: auio.uio_resid = DIRBLKSIZ;
! 394: aiov.iov_base = dirbuf;
! 395: aiov.iov_len = DIRBLKSIZ;
! 396: error = ufs_readdir(&vargs);
! 397: if (error) {
! 398: printf("ufs_extattr_iterate_directory: ufs_readdir "
! 399: "%d\n", error);
! 400: return (error);
! 401: }
! 402:
! 403: /*
! 404: * XXXRW: While in UFS, we always get DIRBLKSIZ returns from
! 405: * the directory code on success, on other file systems this
! 406: * may not be the case. For portability, we should check the
! 407: * read length on return from ufs_readdir().
! 408: */
! 409: edp = (struct dirent *)&dirbuf[DIRBLKSIZ];
! 410: for (dp = (struct dirent *)dirbuf; dp < edp; ) {
! 411: if (dp->d_reclen == 0)
! 412: break;
! 413: /* Skip "." and ".." */
! 414: if (dp->d_name[0] == '.' &&
! 415: (dp->d_name[1] == '\0' ||
! 416: (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
! 417: goto next;
! 418: error = ufs_extattr_lookup(dvp, LOCKPARENT,
! 419: dp->d_name, &attr_vp, l);
! 420: if (error) {
! 421: printf("ufs_extattr_iterate_directory: lookup "
! 422: "%s %d\n", dp->d_name, error);
! 423: } else if (attr_vp == dvp) {
! 424: vrele(attr_vp);
! 425: } else if (attr_vp->v_type != VREG) {
! 426: vput(attr_vp);
! 427: } else {
! 428: error = ufs_extattr_enable_with_open(ump,
! 429: attr_vp, attrnamespace, dp->d_name, l);
! 430: vrele(attr_vp);
! 431: if (error) {
! 432: printf("ufs_extattr_iterate_directory: "
! 433: "enable %s %d\n", dp->d_name,
! 434: error);
! 435: } else if (1 || bootverbose) {
! 436: printf("UFS autostarted EA %s\n",
! 437: dp->d_name);
! 438: }
! 439: }
! 440: next:
! 441: dp = (struct dirent *) ((char *)dp + dp->d_reclen);
! 442: if (dp >= edp)
! 443: break;
! 444: }
! 445: }
! 446: FREE(dirbuf, M_TEMP);
! 447:
! 448: return (0);
! 449: }
! 450:
! 451: /*
! 452: * Auto-start of extended attributes, to be executed (optionally) at
! 453: * mount-time.
! 454: */
! 455: int
! 456: ufs_extattr_autostart(struct mount *mp, struct lwp *l)
! 457: {
! 458: struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp;
! 459: int error;
! 460:
! 461: /*
! 462: * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root?
! 463: * If so, automatically start EA's.
! 464: */
! 465: error = VFS_ROOT(mp, &rvp);
! 466: if (error) {
! 467: printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n",
! 468: error);
! 469: return (error);
! 470: }
! 471:
! 472: KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
! 473:
! 474: error = ufs_extattr_lookup(rvp, 0,
! 475: UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, l);
! 476: if (error) {
! 477: /* rvp ref'd but now unlocked */
! 478: KASSERT(VOP_ISLOCKED(rvp) == 0);
! 479: vrele(rvp);
! 480: return (error);
! 481: }
! 482: if (rvp == attr_dvp) {
! 483: /* Should never happen. */
! 484: KASSERT(VOP_ISLOCKED(rvp) == LK_EXCLUSIVE);
! 485: vrele(attr_dvp);
! 486: vput(rvp);
! 487: return (EINVAL);
! 488: }
! 489: KASSERT(VOP_ISLOCKED(rvp) == 0);
! 490: vrele(rvp);
! 491:
! 492: KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
! 493:
! 494: if (attr_dvp->v_type != VDIR) {
! 495: printf("ufs_extattr_autostart: %s != VDIR\n",
! 496: UFS_EXTATTR_FSROOTSUBDIR);
! 497: goto return_vput_attr_dvp;
! 498: }
! 499:
! 500: error = ufs_extattr_start(mp, l);
! 501: if (error) {
! 502: printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n",
! 503: error);
! 504: goto return_vput_attr_dvp;
! 505: }
! 506:
! 507: /*
! 508: * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM,
! 509: * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory,
! 510: * and start with appropriate type. Failures in either don't
! 511: * result in an over-all failure. attr_dvp is left locked to
! 512: * be cleaned up on exit.
! 513: */
! 514: error = ufs_extattr_lookup(attr_dvp, LOCKPARENT,
! 515: UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, l);
! 516: KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
! 517: if (error == 0) {
! 518: KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
! 519: error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
! 520: attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, l);
! 521: if (error)
! 522: printf("ufs_extattr_iterate_directory returned %d\n",
! 523: error);
! 524: KASSERT(VOP_ISLOCKED(attr_system_dvp) == LK_EXCLUSIVE);
! 525: vput(attr_system_dvp);
! 526: }
! 527:
! 528: error = ufs_extattr_lookup(attr_dvp, LOCKPARENT,
! 529: UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, l);
! 530: KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
! 531: if (error == 0) {
! 532: KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
! 533: error = ufs_extattr_iterate_directory(VFSTOUFS(mp),
! 534: attr_user_dvp, EXTATTR_NAMESPACE_USER, l);
! 535: if (error)
! 536: printf("ufs_extattr_iterate_directory returned %d\n",
! 537: error);
! 538: KASSERT(VOP_ISLOCKED(attr_user_dvp) == LK_EXCLUSIVE);
! 539: vput(attr_user_dvp);
! 540: }
! 541:
! 542: /* Mask startup failures in sub-directories. */
! 543: error = 0;
! 544:
! 545: return_vput_attr_dvp:
! 546: KASSERT(VOP_ISLOCKED(attr_dvp) == LK_EXCLUSIVE);
! 547: vput(attr_dvp);
! 548:
! 549: return (error);
! 550: }
! 551: #endif /* !UFS_EXTATTR_AUTOSTART */
! 552:
! 553: /*
! 554: * Stop extended attribute support on an FS.
! 555: */
! 556: int
! 557: ufs_extattr_stop(struct mount *mp, struct lwp *l)
! 558: {
! 559: struct ufs_extattr_list_entry *uele;
! 560: struct ufsmount *ump = VFSTOUFS(mp);
! 561: int error = 0;
! 562:
! 563: ufs_extattr_uepm_lock(ump);
! 564:
! 565: /*
! 566: * If we haven't been started, no big deal. Just short-circuit
! 567: * the processing work.
! 568: */
! 569: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
! 570: error = EOPNOTSUPP;
! 571: goto unlock;
! 572: }
! 573:
! 574: while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) {
! 575: uele = LIST_FIRST(&ump->um_extattr.uepm_list);
! 576: ufs_extattr_disable(ump, uele->uele_attrnamespace,
! 577: uele->uele_attrname, l);
! 578: }
! 579:
! 580: ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED;
! 581:
! 582: kauth_cred_free(ump->um_extattr.uepm_ucred);
! 583: ump->um_extattr.uepm_ucred = NULL;
! 584:
! 585: unlock:
! 586: ufs_extattr_uepm_unlock(ump);
! 587:
! 588: return (error);
! 589: }
! 590:
! 591: /*
! 592: * Enable a named attribute on the specified filesystem; provide an
! 593: * unlocked backing vnode to hold the attribute data.
! 594: */
! 595: static int
! 596: ufs_extattr_enable(struct ufsmount *ump, int attrnamespace,
! 597: const char *attrname, struct vnode *backing_vnode, struct lwp *l)
! 598: {
! 599: struct ufs_extattr_list_entry *attribute;
! 600: struct iovec aiov;
! 601: struct uio auio;
! 602: int error = 0;
! 603:
! 604: if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
! 605: return (EINVAL);
! 606: if (backing_vnode->v_type != VREG)
! 607: return (EINVAL);
! 608:
! 609: attribute = malloc(sizeof(*attribute), M_UFS_EXTATTR,
! 610: M_WAITOK | M_ZERO);
! 611:
! 612: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
! 613: error = EOPNOTSUPP;
! 614: goto free_exit;
! 615: }
! 616:
! 617: if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) {
! 618: error = EEXIST;
! 619: goto free_exit;
! 620: }
! 621:
! 622: strncpy(attribute->uele_attrname, attrname,
! 623: UFS_EXTATTR_MAXEXTATTRNAME);
! 624: attribute->uele_attrnamespace = attrnamespace;
! 625: bzero(&attribute->uele_fileheader,
! 626: sizeof(struct ufs_extattr_fileheader));
! 627:
! 628: attribute->uele_backing_vnode = backing_vnode;
! 629:
! 630: auio.uio_iov = &aiov;
! 631: auio.uio_iovcnt = 1;
! 632: aiov.iov_base = (caddr_t) &attribute->uele_fileheader;
! 633: aiov.iov_len = sizeof(struct ufs_extattr_fileheader);
! 634: auio.uio_resid = sizeof(struct ufs_extattr_fileheader);
! 635: auio.uio_offset = (off_t) 0;
! 636: auio.uio_rw = UIO_READ;
! 637: UIO_SETUP_SYSSPACE(&auio);
! 638:
! 639: VOP_LEASE(backing_vnode, l, l->l_proc->p_cred, LEASE_WRITE);
! 640: vn_lock(backing_vnode, LK_SHARED | LK_RETRY);
! 641: error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED,
! 642: ump->um_extattr.uepm_ucred);
! 643:
! 644: if (error)
! 645: goto unlock_free_exit;
! 646:
! 647: if (auio.uio_resid != 0) {
! 648: printf("ufs_extattr_enable: malformed attribute header\n");
! 649: error = EINVAL;
! 650: goto unlock_free_exit;
! 651: }
! 652:
! 653: /*
! 654: * Try to determine the byte order of the attribute file.
! 655: */
! 656: if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
! 657: attribute->uele_flags |= UELE_F_NEEDSWAP;
! 658: attribute->uele_fileheader.uef_magic =
! 659: ufs_rw32(attribute->uele_fileheader.uef_magic,
! 660: UELE_NEEDSWAP(attribute));
! 661: if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) {
! 662: printf("ufs_extattr_enable: invalid attribute header "
! 663: "magic\n");
! 664: error = EINVAL;
! 665: goto unlock_free_exit;
! 666: }
! 667: }
! 668: attribute->uele_fileheader.uef_version =
! 669: ufs_rw32(attribute->uele_fileheader.uef_version,
! 670: UELE_NEEDSWAP(attribute));
! 671: attribute->uele_fileheader.uef_size =
! 672: ufs_rw32(attribute->uele_fileheader.uef_size,
! 673: UELE_NEEDSWAP(attribute));
! 674:
! 675: if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) {
! 676: printf("ufs_extattr_enable: incorrect attribute header "
! 677: "version\n");
! 678: error = EINVAL;
! 679: goto unlock_free_exit;
! 680: }
! 681:
! 682: LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute,
! 683: uele_entries);
! 684:
! 685: VOP_UNLOCK(backing_vnode, 0);
! 686: return (0);
! 687:
! 688: unlock_free_exit:
! 689: VOP_UNLOCK(backing_vnode, 0);
! 690:
! 691: free_exit:
! 692: free(attribute, M_UFS_EXTATTR);
! 693: return (error);
! 694: }
! 695:
! 696: /*
! 697: * Disable extended attribute support on an FS.
! 698: */
! 699: static int
! 700: ufs_extattr_disable(struct ufsmount *ump, int attrnamespace,
! 701: const char *attrname, struct lwp *l)
! 702: {
! 703: struct ufs_extattr_list_entry *uele;
! 704: int error = 0;
! 705:
! 706: if (!ufs_extattr_valid_attrname(attrnamespace, attrname))
! 707: return (EINVAL);
! 708:
! 709: uele = ufs_extattr_find_attr(ump, attrnamespace, attrname);
! 710: if (!uele)
! 711: return (ENOATTR);
! 712:
! 713: LIST_REMOVE(uele, uele_entries);
! 714:
! 715: error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE,
! 716: l->l_proc->p_cred, l);
! 717:
! 718: free(uele, M_UFS_EXTATTR);
! 719:
! 720: return (error);
! 721: }
! 722:
! 723: /*
! 724: * VFS call to manage extended attributes in UFS. If filename_vp is
! 725: * non-NULL, it must be passed in locked, and regardless of errors in
! 726: * processing, will be unlocked.
! 727: */
! 728: int
! 729: ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp,
! 730: int attrnamespace, const char *attrname, struct lwp *l)
! 731: {
! 732: struct ufsmount *ump = VFSTOUFS(mp);
! 733: int error;
! 734:
! 735: /*
! 736: * Only privileged processes can configure extended attributes.
! 737: */
! 738: if ((error = kauth_authorize_generic(l->l_proc->p_cred, KAUTH_GENERIC_ISSUSER,
! 739: &l->l_proc->p_acflag)) != 0) {
! 740: if (filename_vp != NULL)
! 741: VOP_UNLOCK(filename_vp, 0);
! 742: return (error);
! 743: }
! 744:
! 745: switch(cmd) {
! 746: case UFS_EXTATTR_CMD_START:
! 747: if (filename_vp != NULL) {
! 748: VOP_UNLOCK(filename_vp, 0);
! 749: return (EINVAL);
! 750: }
! 751: if (attrname != NULL)
! 752: return (EINVAL);
! 753:
! 754: error = ufs_extattr_start(mp, l);
! 755: return (error);
! 756:
! 757: case UFS_EXTATTR_CMD_STOP:
! 758: if (filename_vp != NULL) {
! 759: VOP_UNLOCK(filename_vp, 0);
! 760: return (EINVAL);
! 761: }
! 762: if (attrname != NULL)
! 763: return (EINVAL);
! 764:
! 765: error = ufs_extattr_stop(mp, l);
! 766: return (error);
! 767:
! 768: case UFS_EXTATTR_CMD_ENABLE:
! 769: if (filename_vp == NULL)
! 770: return (EINVAL);
! 771: if (attrname == NULL) {
! 772: VOP_UNLOCK(filename_vp, 0);
! 773: return (EINVAL);
! 774: }
! 775:
! 776: /*
! 777: * ufs_extattr_enable_with_open() will always unlock the
! 778: * vnode, regardless of failure.
! 779: */
! 780: ufs_extattr_uepm_lock(ump);
! 781: error = ufs_extattr_enable_with_open(ump, filename_vp,
! 782: attrnamespace, attrname, l);
! 783: ufs_extattr_uepm_unlock(ump);
! 784: return (error);
! 785:
! 786: case UFS_EXTATTR_CMD_DISABLE:
! 787: if (filename_vp != NULL) {
! 788: VOP_UNLOCK(filename_vp, 0);
! 789: return (EINVAL);
! 790: }
! 791: if (attrname == NULL)
! 792: return (EINVAL);
! 793:
! 794: ufs_extattr_uepm_lock(ump);
! 795: error = ufs_extattr_disable(ump, attrnamespace, attrname, l);
! 796: ufs_extattr_uepm_unlock(ump);
! 797: return (error);
! 798:
! 799: default:
! 800: return (EINVAL);
! 801: }
! 802: }
! 803:
! 804: /*
! 805: * Vnode operation to retrieve a named extended attribute.
! 806: */
! 807: int
! 808: ufs_getextattr(struct vop_getextattr_args *ap)
! 809: /*
! 810: vop_getextattr {
! 811: IN struct vnode *a_vp;
! 812: IN int a_attrnamespace;
! 813: IN const char *a_name;
! 814: INOUT struct uio *a_uio;
! 815: OUT size_t *a_size;
! 816: IN kauth_cred_t a_cred;
! 817: IN struct lwp *a_l;
! 818: };
! 819: */
! 820: {
! 821: struct mount *mp = ap->a_vp->v_mount;
! 822: struct ufsmount *ump = VFSTOUFS(mp);
! 823: int error;
! 824:
! 825: ufs_extattr_uepm_lock(ump);
! 826:
! 827: error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name,
! 828: ap->a_uio, ap->a_size, ap->a_cred, ap->a_l);
! 829:
! 830: ufs_extattr_uepm_unlock(ump);
! 831:
! 832: return (error);
! 833: }
! 834:
! 835: /*
! 836: * Real work associated with retrieving a named attribute--assumes that
! 837: * the attribute lock has already been grabbed.
! 838: */
! 839: static int
! 840: ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name,
! 841: struct uio *uio, size_t *size, kauth_cred_t cred, struct lwp *l)
! 842: {
! 843: struct ufs_extattr_list_entry *attribute;
! 844: struct ufs_extattr_header ueh;
! 845: struct iovec local_aiov;
! 846: struct uio local_aio;
! 847: struct mount *mp = vp->v_mount;
! 848: struct ufsmount *ump = VFSTOUFS(mp);
! 849: struct inode *ip = VTOI(vp);
! 850: off_t base_offset;
! 851: size_t len, old_len;
! 852: int error = 0;
! 853:
! 854: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
! 855: return (EOPNOTSUPP);
! 856:
! 857: if (strlen(name) == 0)
! 858: return (EINVAL);
! 859:
! 860: error = extattr_check_cred(vp, attrnamespace, cred, l, IREAD);
! 861: if (error)
! 862: return (error);
! 863:
! 864: attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
! 865: if (!attribute)
! 866: return (ENOATTR);
! 867:
! 868: /*
! 869: * Allow only offsets of zero to encourage the read/replace
! 870: * extended attribute semantic. Otherwise we can't guarantee
! 871: * atomicity, as we don't provide locks for extended attributes.
! 872: */
! 873: if (uio != NULL && uio->uio_offset != 0)
! 874: return (ENXIO);
! 875:
! 876: /*
! 877: * Find base offset of header in file based on file header size, and
! 878: * data header size + maximum data size, indexed by inode number.
! 879: */
! 880: base_offset = sizeof(struct ufs_extattr_fileheader) +
! 881: ip->i_number * (sizeof(struct ufs_extattr_header) +
! 882: attribute->uele_fileheader.uef_size);
! 883:
! 884: /*
! 885: * Read in the data header to see if the data is defined, and if so
! 886: * how much.
! 887: */
! 888: memset(&ueh, 0, sizeof(struct ufs_extattr_header));
! 889: local_aiov.iov_base = &ueh;
! 890: local_aiov.iov_len = sizeof(struct ufs_extattr_header);
! 891: local_aio.uio_iov = &local_aiov;
! 892: local_aio.uio_iovcnt = 1;
! 893: local_aio.uio_rw = UIO_READ;
! 894: local_aio.uio_offset = base_offset;
! 895: local_aio.uio_resid = sizeof(struct ufs_extattr_header);
! 896: UIO_SETUP_SYSSPACE(&local_aio);
! 897:
! 898: /*
! 899: * Acquire locks.
! 900: */
! 901: VOP_LEASE(attribute->uele_backing_vnode, l, cred, LEASE_READ);
! 902: /*
! 903: * Don't need to get a lock on the backing file if the getattr is
! 904: * being applied to the backing file, as the lock is already held.
! 905: */
! 906: if (attribute->uele_backing_vnode != vp)
! 907: vn_lock(attribute->uele_backing_vnode, LK_SHARED |
! 908: LK_RETRY);
! 909:
! 910: error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
! 911: IO_NODELOCKED, ump->um_extattr.uepm_ucred);
! 912: if (error)
! 913: goto vopunlock_exit;
! 914:
! 915: /*
! 916: * Attribute headers are kept in file system byte order.
! 917: * XXX What about the blob of data?
! 918: */
! 919: ueh.ueh_flags = ufs_rw32(ueh.ueh_flags, UELE_NEEDSWAP(attribute));
! 920: ueh.ueh_len = ufs_rw32(ueh.ueh_len, UELE_NEEDSWAP(attribute));
! 921: ueh.ueh_i_gen = ufs_rw32(ueh.ueh_i_gen, UELE_NEEDSWAP(attribute));
! 922:
! 923: /* Defined? */
! 924: if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
! 925: error = ENOATTR;
! 926: goto vopunlock_exit;
! 927: }
! 928:
! 929: /* Valid for the current inode generation? */
! 930: if (ueh.ueh_i_gen != ip->i_gen) {
! 931: /*
! 932: * The inode itself has a different generation number
! 933: * than the attribute data. For now, the best solution
! 934: * is to coerce this to undefined, and let it get cleaned
! 935: * up by the next write or extattrctl clean.
! 936: */
! 937: printf("ufs_extattr_get (%s): inode gen inconsistency (%u, %jd)\n",
! 938: mp->mnt_stat.f_mntonname, ueh.ueh_i_gen,
! 939: (intmax_t)ip->i_gen);
! 940: error = ENOATTR;
! 941: goto vopunlock_exit;
! 942: }
! 943:
! 944: /* Local size consistency check. */
! 945: if (ueh.ueh_len > attribute->uele_fileheader.uef_size) {
! 946: error = ENXIO;
! 947: goto vopunlock_exit;
! 948: }
! 949:
! 950: /* Return full data size if caller requested it. */
! 951: if (size != NULL)
! 952: *size = ueh.ueh_len;
! 953:
! 954: /* Return data if the caller requested it. */
! 955: if (uio != NULL) {
! 956: /* Allow for offset into the attribute data. */
! 957: uio->uio_offset = base_offset + sizeof(struct
! 958: ufs_extattr_header);
! 959:
! 960: /*
! 961: * Figure out maximum to transfer -- use buffer size and
! 962: * local data limit.
! 963: */
! 964: len = MIN(uio->uio_resid, ueh.ueh_len);
! 965: old_len = uio->uio_resid;
! 966: uio->uio_resid = len;
! 967:
! 968: error = VOP_READ(attribute->uele_backing_vnode, uio,
! 969: IO_NODELOCKED, ump->um_extattr.uepm_ucred);
! 970: if (error)
! 971: goto vopunlock_exit;
! 972:
! 973: uio->uio_resid = old_len - (len - uio->uio_resid);
! 974: }
! 975:
! 976: vopunlock_exit:
! 977:
! 978: if (uio != NULL)
! 979: uio->uio_offset = 0;
! 980:
! 981: if (attribute->uele_backing_vnode != vp)
! 982: VOP_UNLOCK(attribute->uele_backing_vnode, 0);
! 983:
! 984: return (error);
! 985: }
! 986:
! 987: /*
! 988: * Vnode operation to remove a named attribute.
! 989: */
! 990: int
! 991: ufs_deleteextattr(struct vop_deleteextattr_args *ap)
! 992: /*
! 993: vop_deleteextattr {
! 994: IN struct vnode *a_vp;
! 995: IN int a_attrnamespace;
! 996: IN const char *a_name;
! 997: IN kauth_cred_t a_cred;
! 998: IN struct lwp *a_l;
! 999: };
! 1000: */
! 1001: {
! 1002: struct mount *mp = ap->a_vp->v_mount;
! 1003: struct ufsmount *ump = VFSTOUFS(mp);
! 1004: int error;
! 1005:
! 1006: ufs_extattr_uepm_lock(ump);
! 1007:
! 1008: error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name,
! 1009: ap->a_cred, ap->a_l);
! 1010:
! 1011: ufs_extattr_uepm_unlock(ump);
! 1012:
! 1013: return (error);
! 1014: }
! 1015:
! 1016: /*
! 1017: * Vnode operation to set a named attribute.
! 1018: */
! 1019: int
! 1020: ufs_setextattr(struct vop_setextattr_args *ap)
! 1021: /*
! 1022: vop_setextattr {
! 1023: IN struct vnode *a_vp;
! 1024: IN int a_attrnamespace;
! 1025: IN const char *a_name;
! 1026: INOUT struct uio *a_uio;
! 1027: IN kauth_cred_t a_cred;
! 1028: IN struct lwp *a_l;
! 1029: };
! 1030: */
! 1031: {
! 1032: struct mount *mp = ap->a_vp->v_mount;
! 1033: struct ufsmount *ump = VFSTOUFS(mp);
! 1034: int error;
! 1035:
! 1036: ufs_extattr_uepm_lock(ump);
! 1037:
! 1038: /*
! 1039: * XXX: No longer a supported way to delete extended attributes.
! 1040: */
! 1041: if (ap->a_uio == NULL)
! 1042: return (EINVAL);
! 1043:
! 1044: error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name,
! 1045: ap->a_uio, ap->a_cred, ap->a_l);
! 1046:
! 1047: ufs_extattr_uepm_unlock(ump);
! 1048:
! 1049: return (error);
! 1050: }
! 1051:
! 1052: /*
! 1053: * Real work associated with setting a vnode's extended attributes;
! 1054: * assumes that the attribute lock has already been grabbed.
! 1055: */
! 1056: static int
! 1057: ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name,
! 1058: struct uio *uio, kauth_cred_t cred, struct lwp *l)
! 1059: {
! 1060: struct ufs_extattr_list_entry *attribute;
! 1061: struct ufs_extattr_header ueh;
! 1062: struct iovec local_aiov;
! 1063: struct uio local_aio;
! 1064: struct mount *mp = vp->v_mount;
! 1065: struct ufsmount *ump = VFSTOUFS(mp);
! 1066: struct inode *ip = VTOI(vp);
! 1067: off_t base_offset;
! 1068: int error = 0, ioflag;
! 1069:
! 1070: if (vp->v_mount->mnt_flag & MNT_RDONLY)
! 1071: return (EROFS);
! 1072: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
! 1073: return (EOPNOTSUPP);
! 1074: if (!ufs_extattr_valid_attrname(attrnamespace, name))
! 1075: return (EINVAL);
! 1076:
! 1077: error = extattr_check_cred(vp, attrnamespace, cred, l, IWRITE);
! 1078: if (error)
! 1079: return (error);
! 1080:
! 1081: attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
! 1082: if (!attribute)
! 1083: return (ENOATTR);
! 1084:
! 1085: /*
! 1086: * Early rejection of invalid offsets/length.
! 1087: * Reject: any offset but 0 (replace)
! 1088: * Any size greater than attribute size limit
! 1089: */
! 1090: if (uio->uio_offset != 0 ||
! 1091: uio->uio_resid > attribute->uele_fileheader.uef_size)
! 1092: return (ENXIO);
! 1093:
! 1094: /*
! 1095: * Find base offset of header in file based on file header size, and
! 1096: * data header size + maximum data size, indexed by inode number.
! 1097: */
! 1098: base_offset = sizeof(struct ufs_extattr_fileheader) +
! 1099: ip->i_number * (sizeof(struct ufs_extattr_header) +
! 1100: attribute->uele_fileheader.uef_size);
! 1101:
! 1102: /*
! 1103: * Write out a data header for the data.
! 1104: */
! 1105: ueh.ueh_len = ufs_rw32((uint32_t) uio->uio_resid,
! 1106: UELE_NEEDSWAP(attribute));
! 1107: ueh.ueh_flags = ufs_rw32(UFS_EXTATTR_ATTR_FLAG_INUSE,
! 1108: UELE_NEEDSWAP(attribute));
! 1109: ueh.ueh_i_gen = ufs_rw32(ip->i_gen, UELE_NEEDSWAP(attribute));
! 1110: local_aiov.iov_base = &ueh;
! 1111: local_aiov.iov_len = sizeof(struct ufs_extattr_header);
! 1112: local_aio.uio_iov = &local_aiov;
! 1113: local_aio.uio_iovcnt = 1;
! 1114: local_aio.uio_rw = UIO_WRITE;
! 1115: local_aio.uio_offset = base_offset;
! 1116: local_aio.uio_resid = sizeof(struct ufs_extattr_header);
! 1117: UIO_SETUP_SYSSPACE(&local_aio);
! 1118:
! 1119: /*
! 1120: * Acquire locks.
! 1121: */
! 1122: VOP_LEASE(attribute->uele_backing_vnode, l, cred, LEASE_WRITE);
! 1123:
! 1124: /*
! 1125: * Don't need to get a lock on the backing file if the setattr is
! 1126: * being applied to the backing file, as the lock is already held.
! 1127: */
! 1128: if (attribute->uele_backing_vnode != vp)
! 1129: vn_lock(attribute->uele_backing_vnode,
! 1130: LK_EXCLUSIVE | LK_RETRY);
! 1131:
! 1132: ioflag = IO_NODELOCKED;
! 1133: if (ufs_extattr_sync)
! 1134: ioflag |= IO_SYNC;
! 1135: error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
! 1136: ump->um_extattr.uepm_ucred);
! 1137: if (error)
! 1138: goto vopunlock_exit;
! 1139:
! 1140: if (local_aio.uio_resid != 0) {
! 1141: error = ENXIO;
! 1142: goto vopunlock_exit;
! 1143: }
! 1144:
! 1145: /*
! 1146: * Write out user data.
! 1147: * XXX NOT ATOMIC WITH RESPECT TO THE HEADER.
! 1148: */
! 1149: uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header);
! 1150:
! 1151: ioflag = IO_NODELOCKED;
! 1152: if (ufs_extattr_sync)
! 1153: ioflag |= IO_SYNC;
! 1154: error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag,
! 1155: ump->um_extattr.uepm_ucred);
! 1156:
! 1157: vopunlock_exit:
! 1158: uio->uio_offset = 0;
! 1159:
! 1160: if (attribute->uele_backing_vnode != vp)
! 1161: VOP_UNLOCK(attribute->uele_backing_vnode, 0);
! 1162:
! 1163: return (error);
! 1164: }
! 1165:
! 1166: /*
! 1167: * Real work associated with removing an extended attribute from a vnode.
! 1168: * Assumes the attribute lock has already been grabbed.
! 1169: */
! 1170: static int
! 1171: ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name,
! 1172: kauth_cred_t cred, struct lwp *l)
! 1173: {
! 1174: struct ufs_extattr_list_entry *attribute;
! 1175: struct ufs_extattr_header ueh;
! 1176: struct iovec local_aiov;
! 1177: struct uio local_aio;
! 1178: struct mount *mp = vp->v_mount;
! 1179: struct ufsmount *ump = VFSTOUFS(mp);
! 1180: struct inode *ip = VTOI(vp);
! 1181: off_t base_offset;
! 1182: int error = 0, ioflag;
! 1183:
! 1184: if (vp->v_mount->mnt_flag & MNT_RDONLY)
! 1185: return (EROFS);
! 1186: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED))
! 1187: return (EOPNOTSUPP);
! 1188: if (!ufs_extattr_valid_attrname(attrnamespace, name))
! 1189: return (EINVAL);
! 1190:
! 1191: error = extattr_check_cred(vp, attrnamespace, cred, l, IWRITE);
! 1192: if (error)
! 1193: return (error);
! 1194:
! 1195: attribute = ufs_extattr_find_attr(ump, attrnamespace, name);
! 1196: if (!attribute)
! 1197: return (ENOATTR);
! 1198:
! 1199: /*
! 1200: * Find base offset of header in file based on file header size, and
! 1201: * data header size + maximum data size, indexed by inode number.
! 1202: */
! 1203: base_offset = sizeof(struct ufs_extattr_fileheader) +
! 1204: ip->i_number * (sizeof(struct ufs_extattr_header) +
! 1205: attribute->uele_fileheader.uef_size);
! 1206:
! 1207: /*
! 1208: * Check to see if currently defined.
! 1209: */
! 1210: memset(&ueh, 0, sizeof(struct ufs_extattr_header));
! 1211:
! 1212: local_aiov.iov_base = &ueh;
! 1213: local_aiov.iov_len = sizeof(struct ufs_extattr_header);
! 1214: local_aio.uio_iov = &local_aiov;
! 1215: local_aio.uio_iovcnt = 1;
! 1216: local_aio.uio_rw = UIO_READ;
! 1217: local_aio.uio_offset = base_offset;
! 1218: local_aio.uio_resid = sizeof(struct ufs_extattr_header);
! 1219: UIO_SETUP_SYSSPACE(&local_aio);
! 1220:
! 1221: VOP_LEASE(attribute->uele_backing_vnode, l, cred, LEASE_WRITE);
! 1222:
! 1223: /*
! 1224: * Don't need to get the lock on the backing vnode if the vnode we're
! 1225: * modifying is it, as we already hold the lock.
! 1226: */
! 1227: if (attribute->uele_backing_vnode != vp)
! 1228: vn_lock(attribute->uele_backing_vnode,
! 1229: LK_EXCLUSIVE | LK_RETRY);
! 1230:
! 1231: error = VOP_READ(attribute->uele_backing_vnode, &local_aio,
! 1232: IO_NODELOCKED, ump->um_extattr.uepm_ucred);
! 1233: if (error)
! 1234: goto vopunlock_exit;
! 1235:
! 1236: /*
! 1237: * Attribute headers are kept in file system byte order.
! 1238: */
! 1239: ueh.ueh_flags = ufs_rw32(ueh.ueh_flags, UELE_NEEDSWAP(attribute));
! 1240: ueh.ueh_len = ufs_rw32(ueh.ueh_len, UELE_NEEDSWAP(attribute));
! 1241: ueh.ueh_i_gen = ufs_rw32(ueh.ueh_i_gen, UELE_NEEDSWAP(attribute));
! 1242:
! 1243: /* Defined? */
! 1244: if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) {
! 1245: error = ENOATTR;
! 1246: goto vopunlock_exit;
! 1247: }
! 1248:
! 1249: /* Valid for the current inode generation? */
! 1250: if (ueh.ueh_i_gen != ip->i_gen) {
! 1251: /*
! 1252: * The inode itself has a different generation number than
! 1253: * the attribute data. For now, the best solution is to
! 1254: * coerce this to undefined, and let it get cleaned up by
! 1255: * the next write or extattrctl clean.
! 1256: */
! 1257: printf("ufs_extattr_rm (%s): inode number inconsistency (%u, %jd)\n",
! 1258: mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen);
! 1259: error = ENOATTR;
! 1260: goto vopunlock_exit;
! 1261: }
! 1262:
! 1263: /* Flag it as not in use. */
! 1264: ueh.ueh_flags = 0; /* No need to byte swap 0 */
! 1265: ueh.ueh_len = 0; /* ...ditto... */
! 1266:
! 1267: local_aiov.iov_base = &ueh;
! 1268: local_aiov.iov_len = sizeof(struct ufs_extattr_header);
! 1269: local_aio.uio_iov = &local_aiov;
! 1270: local_aio.uio_iovcnt = 1;
! 1271: local_aio.uio_rw = UIO_WRITE;
! 1272: local_aio.uio_offset = base_offset;
! 1273: local_aio.uio_resid = sizeof(struct ufs_extattr_header);
! 1274: UIO_SETUP_SYSSPACE(&local_aio);
! 1275:
! 1276: ioflag = IO_NODELOCKED;
! 1277: if (ufs_extattr_sync)
! 1278: ioflag |= IO_SYNC;
! 1279: error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag,
! 1280: ump->um_extattr.uepm_ucred);
! 1281: if (error)
! 1282: goto vopunlock_exit;
! 1283:
! 1284: if (local_aio.uio_resid != 0)
! 1285: error = ENXIO;
! 1286:
! 1287: vopunlock_exit:
! 1288: VOP_UNLOCK(attribute->uele_backing_vnode, 0);
! 1289:
! 1290: return (error);
! 1291: }
! 1292:
! 1293: /*
! 1294: * Called by UFS when an inode is no longer active and should have its
! 1295: * attributes stripped.
! 1296: */
! 1297: void
! 1298: ufs_extattr_vnode_inactive(struct vnode *vp, struct lwp *l)
! 1299: {
! 1300: struct ufs_extattr_list_entry *uele;
! 1301: struct mount *mp = vp->v_mount;
! 1302: struct ufsmount *ump = VFSTOUFS(mp);
! 1303:
! 1304: /*
! 1305: * In that case, we cannot lock. We should not have any active vnodes
! 1306: * on the fs if this is not yet initialized but is going to be, so
! 1307: * this can go unlocked.
! 1308: */
! 1309: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED))
! 1310: return;
! 1311:
! 1312: ufs_extattr_uepm_lock(ump);
! 1313:
! 1314: if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) {
! 1315: ufs_extattr_uepm_unlock(ump);
! 1316: return;
! 1317: }
! 1318:
! 1319: LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries)
! 1320: ufs_extattr_rm(vp, uele->uele_attrnamespace,
! 1321: uele->uele_attrname, proc0.p_cred, l);
! 1322:
! 1323: ufs_extattr_uepm_unlock(ump);
! 1324: }
CVSweb <webmaster@jp.NetBSD.org>