ufs_extattr.c revision 61281
1/*- 2 * Copyright (c) 1999, 2000 Robert N. M. Watson 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/ufs/ufs/ufs_extattr.c 61281 2000-06-05 14:22:51Z rwatson $ 27 */ 28/* 29 * TrustedBSD Project - extended attribute support for UFS-like file systems 30 */ 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35#include <sys/namei.h> 36#include <sys/malloc.h> 37#include <sys/fcntl.h> 38#include <sys/proc.h> 39#include <sys/vnode.h> 40#include <sys/mount.h> 41#include <sys/lock.h> 42 43#include <ufs/ufs/extattr.h> 44#include <ufs/ufs/quota.h> 45#include <ufs/ufs/ufsmount.h> 46#include <ufs/ufs/inode.h> 47 48#define MIN(a,b) (((a)<(b))?(a):(b)) 49 50static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); 51 52static int ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, 53 u_int32_t fowner, struct ucred *cred, struct proc *p, int access); 54static int ufs_extattr_enable(struct ufsmount *ump, char *attrname, 55 struct vnode *backing_vnode, struct proc *p); 56static int ufs_extattr_disable(struct ufsmount *ump, char *attrname, 57 struct proc *p); 58static int ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio, 59 struct ucred *cred, struct proc *p); 60static int ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio, 61 struct ucred *cred, struct proc *p); 62static int ufs_extattr_rm(struct vnode *vp, char *name, 63 struct ucred *cred, struct proc *p); 64 65/* 66 * Per-FS attribute lock protecting attribute operations 67 * XXX Right now there is a lot of lock contention due to having a single 68 * lock per-FS; really, this should be far more fine-grained. 69 */ 70static void 71ufs_extattr_uepm_lock(struct ufsmount *ump, struct proc *p) 72{ 73 74 /* ideally, LK_CANRECURSE would not be used, here */ 75 lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY | 76 LK_CANRECURSE, 0, p); 77} 78 79static void 80ufs_extattr_uepm_unlock(struct ufsmount *ump, struct proc *p) 81{ 82 83 lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, p); 84} 85 86/* 87 * Locate an attribute given a name and mountpoint. 88 * Must be holding uepm lock for the mount point. 89 */ 90static struct ufs_extattr_list_entry * 91ufs_exttatr_find_attr(struct ufsmount *ump, char *attrname) 92{ 93 struct ufs_extattr_list_entry *search_attribute; 94 95 for (search_attribute = ump->um_extattr.uepm_list.lh_first; 96 search_attribute; 97 search_attribute = search_attribute->uele_entries.le_next) { 98 if (!(strncmp(attrname, search_attribute->uele_attrname, 99 UFS_EXTATTR_MAXEXTATTRNAME))) { 100 return (search_attribute); 101 } 102 } 103 104 return (0); 105} 106 107/* 108 * Initialize per-FS structures supporting extended attributes. Do not 109 * start extended attributes yet. 110 */ 111void 112ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) 113{ 114 115 uepm->uepm_flags = 0; 116 117 LIST_INIT(&uepm->uepm_list); 118 /* XXX is PVFS right, here? */ 119 lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0); 120 uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; 121} 122 123/* 124 * Start extended attribute support on an FS 125 */ 126int 127ufs_extattr_start(struct mount *mp, struct proc *p) 128{ 129 struct ufsmount *ump; 130 int error = 0; 131 132 ump = VFSTOUFS(mp); 133 134 ufs_extattr_uepm_lock(ump, p); 135 136 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) { 137 error = EOPNOTSUPP; 138 goto unlock; 139 } 140 if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) { 141 error = EBUSY; 142 goto unlock; 143 } 144 145 ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; 146 147 crhold(p->p_ucred); 148 ump->um_extattr.uepm_ucred = p->p_ucred; 149 150unlock: 151 ufs_extattr_uepm_unlock(ump, p); 152 153 return (error); 154} 155 156/* 157 * Stop extended attribute support on an FS 158 */ 159int 160ufs_extattr_stop(struct mount *mp, struct proc *p) 161{ 162 struct ufs_extattr_list_entry *uele; 163 struct ufsmount *ump = VFSTOUFS(mp); 164 int error = 0; 165 166 ufs_extattr_uepm_lock(ump, p); 167 168 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 169 error = EOPNOTSUPP; 170 goto unlock; 171 } 172 173 while (ump->um_extattr.uepm_list.lh_first != NULL) { 174 uele = ump->um_extattr.uepm_list.lh_first; 175 ufs_extattr_disable(ump, uele->uele_attrname, p); 176 } 177 178 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 179 180 crfree(ump->um_extattr.uepm_ucred); 181 ump->um_extattr.uepm_ucred = NULL; 182 183unlock: 184 ufs_extattr_uepm_unlock(ump, p); 185 186 return (error); 187} 188 189/* 190 * Enable a named attribute on the specified file system; provide a 191 * backing vnode to hold the attribute data. 192 */ 193static int 194ufs_extattr_enable(struct ufsmount *ump, char *attrname, 195 struct vnode *backing_vnode, struct proc *p) 196{ 197 struct ufs_extattr_list_entry *attribute; 198 struct iovec aiov; 199 struct uio auio; 200 int error = 0; 201 202 if (backing_vnode->v_type != VREG) 203 return (EINVAL); 204 205 MALLOC(attribute, struct ufs_extattr_list_entry *, 206 sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); 207 if (attribute == NULL) 208 return (ENOMEM); 209 210 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 211 error = EOPNOTSUPP; 212 goto free_exit; 213 } 214 215 if (ufs_exttatr_find_attr(ump, attrname)) { 216 error = EOPNOTSUPP; 217 goto free_exit; 218 } 219 220 strncpy(attribute->uele_attrname, attrname, UFS_EXTATTR_MAXEXTATTRNAME); 221 bzero(&attribute->uele_fileheader, 222 sizeof(struct ufs_extattr_fileheader)); 223 224 attribute->uele_backing_vnode = backing_vnode; 225 226 auio.uio_iov = &aiov; 227 auio.uio_iovcnt = 1; 228 aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 229 aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 230 auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 231 auio.uio_offset = (off_t) 0; 232 auio.uio_segflg = UIO_SYSSPACE; 233 auio.uio_rw = UIO_READ; 234 auio.uio_procp = (struct proc *) p; 235 236 VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE); 237 vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); 238 error = VOP_READ(backing_vnode, &auio, 0, ump->um_extattr.uepm_ucred); 239 VOP_UNLOCK(backing_vnode, 0, p); 240 241 if (error) { 242 goto free_exit; 243 } 244 245 if (auio.uio_resid != 0) { 246 printf("ufs_extattr_enable: malformed attribute header\n"); 247 error = EINVAL; 248 goto free_exit; 249 } 250 251 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { 252 printf("ufs_extattr_enable: invalid attribute header magic\n"); 253 error = EINVAL; 254 goto free_exit; 255 } 256 257 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { 258 printf("ufs_extattr_enable: incorrect attribute header " 259 "version\n"); 260 error = EINVAL; 261 goto free_exit; 262 } 263 264 backing_vnode->v_flag |= VSYSTEM; 265 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries); 266 267 return (0); 268 269free_exit: 270 FREE(attribute, M_UFS_EXTATTR); 271 return (error); 272} 273 274/* 275 * Disable extended attribute support on an FS 276 */ 277static int 278ufs_extattr_disable(struct ufsmount *ump, char *attrname, struct proc *p) 279{ 280 struct ufs_extattr_list_entry *uele; 281 int error = 0; 282 283 uele = ufs_exttatr_find_attr(ump, attrname); 284 if (!uele) 285 return (ENOENT); 286 287 LIST_REMOVE(uele, uele_entries); 288 289 uele->uele_backing_vnode->v_flag &= ~VSYSTEM; 290 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p); 291 292 FREE(uele, M_UFS_EXTATTR); 293 294 return (error); 295} 296 297/* 298 * VFS call to manage extended attributes in UFS 299 * attrname, arg are userspace pointers from the syscall 300 */ 301int 302ufs_extattrctl(struct mount *mp, int cmd, char *attrname, 303 caddr_t arg, struct proc *p) 304{ 305 struct nameidata nd; 306 struct ufsmount *ump = VFSTOUFS(mp); 307 struct vnode *vp; 308 char local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* inc null */ 309 char *filename; 310 int error, len; 311 312 if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0))) 313 return (error); 314 315 switch(cmd) { 316 case UFS_EXTATTR_CMD_START: 317 error = ufs_extattr_start(mp, p); 318 319 return (error); 320 321 case UFS_EXTATTR_CMD_STOP: 322 return (ufs_extattr_stop(mp, p)); 323 324 case UFS_EXTATTR_CMD_ENABLE: 325 error = copyinstr(attrname, local_attrname, 326 UFS_EXTATTR_MAXEXTATTRNAME, &len); 327 if (error) 328 return (error); 329 330 filename = (char *) arg; 331 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p); 332 error = vn_open(&nd, FREAD|FWRITE, 0); 333 if (error) 334 return (error); 335 336 vp = nd.ni_vp; 337 VOP_UNLOCK(vp, 0, p); 338 339 ufs_extattr_uepm_lock(ump, p); 340 error = ufs_extattr_enable(ump, local_attrname, vp, p); 341 ufs_extattr_uepm_unlock(ump, p); 342 343 return (error); 344 345 case UFS_EXTATTR_CMD_DISABLE: 346 error = copyinstr(attrname, local_attrname, 347 UFS_EXTATTR_MAXEXTATTRNAME, &len); 348 349 ufs_extattr_uepm_lock(ump, p); 350 error = ufs_extattr_disable(ump, local_attrname, p); 351 ufs_extattr_uepm_unlock(ump, p); 352 353 return (error); 354 355 default: 356 return (EINVAL); 357 } 358} 359 360/* 361 * Credential check based on process requesting service, and per-attribute 362 * permissions. 363 */ 364static int 365ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, u_int32_t fowner, 366 struct ucred *cred, struct proc *p, int access) 367{ 368 u_int uef_perm; 369 370 switch(access) { 371 case IREAD: 372 uef_perm = uele->uele_fileheader.uef_read_perm; 373 break; 374 case IWRITE: 375 uef_perm = uele->uele_fileheader.uef_write_perm; 376 break; 377 default: 378 return (EACCES); 379 } 380 381 /* Kernel sponsoring request does so without passing a cred */ 382 if (!cred) 383 return (0); 384 385 /* XXX there might eventually be a capability check here */ 386 387 /* If it's set to root-only, check for suser(p) */ 388 if (uef_perm == UFS_EXTATTR_PERM_ROOT && !suser(p)) 389 return (0); 390 391 /* Allow the owner if appropriate */ 392 if (uef_perm == UFS_EXTATTR_PERM_OWNER && cred->cr_uid == fowner) 393 return (0); 394 395 /* Allow anyone if appropriate */ 396 if (uef_perm == UFS_EXTATTR_PERM_ANYONE) 397 return (0); 398 399 return (EACCES); 400} 401 402/* 403 * Vnode operating to retrieve a named extended attribute 404 */ 405int 406ufs_vop_getextattr(struct vop_getextattr_args *ap) 407/* 408vop_getextattr { 409 IN struct vnode *a_vp; 410 IN char *a_name; 411 INOUT struct uio *a_uio; 412 IN struct ucred *a_cred; 413 IN struct proc *a_p; 414}; 415*/ 416{ 417 struct mount *mp = ap->a_vp->v_mount; 418 struct ufsmount *ump = VFSTOUFS(mp); 419 int error; 420 421 ufs_extattr_uepm_lock(ump, ap->a_p); 422 423 error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred, 424 ap->a_p); 425 426 ufs_extattr_uepm_unlock(ump, ap->a_p); 427 428 return (error); 429} 430 431/* 432 * Real work associated with retrieving a named attribute--assumes that 433 * the attribute lock has already been grabbed. 434 */ 435static int 436ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio, 437 struct ucred *cred, struct proc *p) 438{ 439 struct ufs_extattr_list_entry *attribute; 440 struct ufs_extattr_header ueh; 441 struct iovec local_aiov; 442 struct uio local_aio; 443 struct mount *mp = vp->v_mount; 444 struct ufsmount *ump = VFSTOUFS(mp); 445 struct inode *ip = VTOI(vp); 446 off_t base_offset; 447 size_t size, old_size; 448 int error = 0; 449 450 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 451 return (EOPNOTSUPP); 452 453 attribute = ufs_exttatr_find_attr(ump, name); 454 if (!attribute) 455 return (ENOENT); 456 457 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, 458 IREAD))) 459 return (error); 460 461 /* 462 * Allow only offsets of zero to encourage the read/replace 463 * extended attribute semantic. Otherwise we can't guarantee 464 * atomicity, as we don't provide locks for extended 465 * attributes. 466 */ 467 if (uio->uio_offset != 0) 468 return (ENXIO); 469 470 /* 471 * Find base offset of header in file based on file header size, and 472 * data header size + maximum data size, indexed by inode number 473 */ 474 base_offset = sizeof(struct ufs_extattr_fileheader) + 475 ip->i_number * (sizeof(struct ufs_extattr_header) + 476 attribute->uele_fileheader.uef_size); 477 478 /* 479 * Read in the data header to see if the data is defined, and if so 480 * how much. 481 */ 482 bzero(&ueh, sizeof(struct ufs_extattr_header)); 483 local_aiov.iov_base = (caddr_t) &ueh; 484 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 485 local_aio.uio_iov = &local_aiov; 486 local_aio.uio_iovcnt = 1; 487 local_aio.uio_rw = UIO_READ; 488 local_aio.uio_segflg = UIO_SYSSPACE; 489 local_aio.uio_procp = p; 490 local_aio.uio_offset = base_offset; 491 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 492 493 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ); 494 vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_NOPAUSE | 495 LK_RETRY, p); 496 497 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0, 498 ump->um_extattr.uepm_ucred); 499 if (error) 500 goto vopunlock_exit; 501 502 /* defined? */ 503 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 504 error = ENOENT; 505 goto vopunlock_exit; 506 } 507 508 /* valid for the current inode generation? */ 509 if (ueh.ueh_i_gen != ip->i_gen) { 510 /* 511 * The inode itself has a different generation number 512 * than the attribute data. For now, the best solution 513 * is to coerce this to undefined, and let it get cleaned 514 * up by the next write or extattrctl clean. 515 */ 516 printf("ufs_extattr: inode number inconsistency (%d, %d)\n", 517 ueh.ueh_i_gen, ip->i_gen); 518 error = ENOENT; 519 goto vopunlock_exit; 520 } 521 522 /* local size consistency check */ 523 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 524 error = ENXIO; 525 goto vopunlock_exit; 526 } 527 528 if (ueh.ueh_len < uio->uio_offset) { 529 error = 0; 530 goto vopunlock_exit; 531 } 532 533 /* allow for offset into the attr data */ 534 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 535 536 /* 537 * Figure out maximum to transfer -- use buffer size and local data 538 * limit. 539 */ 540 size = MIN(uio->uio_resid, ueh.ueh_len); 541 old_size = uio->uio_resid; 542 uio->uio_resid = size; 543 544 error = VOP_READ(attribute->uele_backing_vnode, uio, 0, 545 ump->um_extattr.uepm_ucred); 546 if (error) { 547 uio->uio_offset = 0; 548 goto vopunlock_exit; 549 } 550 551 uio->uio_offset = 0; 552 uio->uio_resid = old_size - (size - uio->uio_resid); 553 554vopunlock_exit: 555 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 556 557 return (error); 558} 559 560/* 561 * Vnode operation to set a named attribute 562 */ 563int 564ufs_vop_setextattr(struct vop_setextattr_args *ap) 565/* 566vop_setextattr { 567 IN struct vnode *a_vp; 568 IN char *a_name; 569 INOUT struct uio *a_uio; 570 IN struct ucred *a_cred; 571 IN struct proc *a_p; 572}; 573*/ 574{ 575 struct mount *mp = ap->a_vp->v_mount; 576 struct ufsmount *ump = VFSTOUFS(mp); 577 578 int error; 579 580 ufs_extattr_uepm_lock(ump, ap->a_p); 581 582 if (ap->a_uio) 583 error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio, 584 ap->a_cred, ap->a_p); 585 else 586 error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred, 587 ap->a_p); 588 589 ufs_extattr_uepm_unlock(ump, ap->a_p); 590 591 return (error); 592} 593 594/* 595 * Real work associated with setting a vnode's extended attributes; 596 * assumes that the attribute lock has already been grabbed. 597 */ 598static int 599ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio, 600 struct ucred *cred, struct proc *p) 601{ 602 struct ufs_extattr_list_entry *attribute; 603 struct ufs_extattr_header ueh; 604 struct iovec local_aiov; 605 struct uio local_aio; 606 struct mount *mp = vp->v_mount; 607 struct ufsmount *ump = VFSTOUFS(mp); 608 struct inode *ip = VTOI(vp); 609 off_t base_offset; 610 611 int error = 0; 612 613 if (vp->v_mount->mnt_flag & MNT_RDONLY) 614 return (EROFS); 615 616 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 617 return (EOPNOTSUPP); 618 619 attribute = ufs_exttatr_find_attr(ump, name); 620 if (!attribute) 621 return (ENOENT); 622 623 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, 624 p, IWRITE))) 625 return (error); 626 627 /* 628 * Early rejection of invalid offsets/lengths 629 * Reject: any offset but 0 (replace) 630 * Any size greater than attribute size limit 631 */ 632 if (uio->uio_offset != 0 || 633 uio->uio_resid > attribute->uele_fileheader.uef_size) 634 return (ENXIO); 635 636 /* 637 * Find base offset of header in file based on file header size, and 638 * data header size + maximum data size, indexed by inode number 639 */ 640 base_offset = sizeof(struct ufs_extattr_fileheader) + 641 ip->i_number * (sizeof(struct ufs_extattr_header) + 642 attribute->uele_fileheader.uef_size); 643 644 /* 645 * Write out a data header for the data 646 */ 647 ueh.ueh_len = uio->uio_resid; 648 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 649 ueh.ueh_i_gen = ip->i_gen; 650 local_aiov.iov_base = (caddr_t) &ueh; 651 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 652 local_aio.uio_iov = &local_aiov; 653 local_aio.uio_iovcnt = 1; 654 local_aio.uio_rw = UIO_WRITE; 655 local_aio.uio_segflg = UIO_SYSSPACE; 656 local_aio.uio_procp = p; 657 local_aio.uio_offset = base_offset; 658 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 659 660 /* 661 * Acquire locks 662 */ 663 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 664 665 /* 666 * Don't need to get a lock on the backing file if the setattr is 667 * being applied to the backing file, as the lock is already held 668 */ 669 if (attribute->uele_backing_vnode != vp) 670 vn_lock(attribute->uele_backing_vnode, 671 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 672 673 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, 674 ump->um_extattr.uepm_ucred); 675 if (error) 676 goto vopunlock_exit; 677 678 if (local_aio.uio_resid != 0) { 679 error = ENXIO; 680 goto vopunlock_exit; 681 } 682 683 /* 684 * Write out user data 685 */ 686 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 687 688 error = VOP_WRITE(attribute->uele_backing_vnode, uio, IO_SYNC, 689 ump->um_extattr.uepm_ucred); 690 691vopunlock_exit: 692 uio->uio_offset = 0; 693 694 if (attribute->uele_backing_vnode != vp) 695 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 696 697 return (error); 698} 699 700/* 701 * Real work associated with removing an extended attribute from a vnode. 702 * Assumes the attribute lock has already been grabbed. 703 */ 704static int 705ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred, 706 struct proc *p) 707{ 708 struct ufs_extattr_list_entry *attribute; 709 struct ufs_extattr_header ueh; 710 struct iovec local_aiov; 711 struct uio local_aio; 712 struct mount *mp = vp->v_mount; 713 struct ufsmount *ump = VFSTOUFS(mp); 714 struct inode *ip = VTOI(vp); 715 off_t base_offset; 716 int error = 0; 717 718 if (vp->v_mount->mnt_flag & MNT_RDONLY) 719 return (EROFS); 720 721 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 722 return (EOPNOTSUPP); 723 724 attribute = ufs_exttatr_find_attr(ump, name); 725 if (!attribute) 726 return (ENOENT); 727 728 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, 729 IWRITE))) 730 return (error); 731 732 /* 733 * Find base offset of header in file based on file header size, and 734 * data header size + maximum data size, indexed by inode number 735 */ 736 base_offset = sizeof(struct ufs_extattr_fileheader) + 737 ip->i_number * (sizeof(struct ufs_extattr_header) + 738 attribute->uele_fileheader.uef_size); 739 740 /* 741 * Read in the data header to see if the data is defined 742 */ 743 bzero(&ueh, sizeof(struct ufs_extattr_header)); 744 745 local_aiov.iov_base = (caddr_t) &ueh; 746 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 747 local_aio.uio_iov = &local_aiov; 748 local_aio.uio_iovcnt = 1; 749 local_aio.uio_rw = UIO_READ; 750 local_aio.uio_segflg = UIO_SYSSPACE; 751 local_aio.uio_procp = p; 752 local_aio.uio_offset = base_offset; 753 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 754 755 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 756 757 /* 758 * Don't need to get the lock on the backing vnode if the vnode we're 759 * modifying is it, as we already hold the lock. 760 */ 761 if (attribute->uele_backing_vnode != vp) 762 vn_lock(attribute->uele_backing_vnode, 763 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 764 765 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0, 766 ump->um_extattr.uepm_ucred); 767 if (error) 768 goto vopunlock_exit; 769 770 /* defined? */ 771 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 772 error = ENOENT; 773 goto vopunlock_exit; 774 } 775 776 /* flag it as not in use */ 777 ueh.ueh_flags = 0; 778 779 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, 780 ump->um_extattr.uepm_ucred); 781 if (error) 782 goto vopunlock_exit; 783 784 if (local_aio.uio_resid != 0) 785 error = ENXIO; 786 787vopunlock_exit: 788 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 789 790 return (error); 791} 792 793/* 794 * Called by UFS when an inode is no longer active and should have its 795 * attributes stripped. 796 */ 797void 798ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) 799{ 800 struct ufs_extattr_list_entry *uele; 801 struct mount *mp = vp->v_mount; 802 struct ufsmount *ump = VFSTOUFS(mp); 803 804 ufs_extattr_uepm_lock(ump, p); 805 806 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 807 ufs_extattr_uepm_unlock(ump, p); 808 return; 809 } 810 811 for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL; 812 uele = uele->uele_entries.le_next) 813 ufs_extattr_rm(vp, uele->uele_attrname, 0, p); 814 815 ufs_extattr_uepm_unlock(ump, p); 816} 817