ufs_extattr.c revision 59388
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 59388 2000-04-19 07:38:20Z 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 backing_vnode->v_flag |= VSYSTEM; 226 227 auio.uio_iov = &aiov; 228 auio.uio_iovcnt = 1; 229 aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 230 aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 231 auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 232 auio.uio_offset = (off_t) 0; 233 auio.uio_segflg = UIO_SYSSPACE; 234 auio.uio_rw = UIO_READ; 235 auio.uio_procp = (struct proc *) p; 236 237 VOP_LEASE(backing_vnode, p, p->p_cred->pc_ucred, LEASE_WRITE); 238 vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, p); 239 error = VOP_READ(backing_vnode, &auio, 0, ump->um_extattr.uepm_ucred); 240 VOP_UNLOCK(backing_vnode, 0, p); 241 242 if (error) { 243 goto free_exit; 244 } 245 246 if (auio.uio_resid != 0) { 247 printf("ufs_extattr_enable: malformed attribute header\n"); 248 error = EINVAL; 249 goto free_exit; 250 } 251 252 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, uele_entries); 253 254 return (0); 255 256free_exit: 257 FREE(attribute, M_UFS_EXTATTR); 258 return (error); 259} 260 261/* 262 * Disable extended attribute support on an FS 263 */ 264static int 265ufs_extattr_disable(struct ufsmount *ump, char *attrname, struct proc *p) 266{ 267 struct ufs_extattr_list_entry *uele; 268 int error = 0; 269 270 uele = ufs_exttatr_find_attr(ump, attrname); 271 if (!uele) 272 return (ENOENT); 273 274 LIST_REMOVE(uele, uele_entries); 275 276 uele->uele_backing_vnode->v_flag &= ~VSYSTEM; 277 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, p->p_ucred, p); 278 279 FREE(uele, M_UFS_EXTATTR); 280 281 return (error); 282} 283 284/* 285 * VFS call to manage extended attributes in UFS 286 * attrname, arg are userspace pointers from the syscall 287 */ 288int 289ufs_extattrctl(struct mount *mp, int cmd, char *attrname, 290 caddr_t arg, struct proc *p) 291{ 292 struct nameidata nd; 293 struct ufsmount *ump = VFSTOUFS(mp); 294 struct vnode *vp; 295 char local_attrname[UFS_EXTATTR_MAXEXTATTRNAME]; /* inc null */ 296 char *filename; 297 int error, len; 298 299 if ((error = suser_xxx(p->p_cred->pc_ucred, p, 0))) 300 return (error); 301 302 switch(cmd) { 303 case UFS_EXTATTR_CMD_START: 304 error = ufs_extattr_start(mp, p); 305 306 return (error); 307 308 case UFS_EXTATTR_CMD_STOP: 309 return (ufs_extattr_stop(mp, p)); 310 311 case UFS_EXTATTR_CMD_ENABLE: 312 error = copyinstr(attrname, local_attrname, 313 UFS_EXTATTR_MAXEXTATTRNAME, &len); 314 if (error) 315 return (error); 316 317 filename = (char *) arg; 318 NDINIT(&nd, LOOKUP, FOLLOW, UIO_USERSPACE, filename, p); 319 error = vn_open(&nd, FREAD|FWRITE, 0); 320 if (error) 321 return (error); 322 323 vp = nd.ni_vp; 324 VOP_UNLOCK(vp, 0, p); 325 326 ufs_extattr_uepm_lock(ump, p); 327 error = ufs_extattr_enable(ump, local_attrname, vp, p); 328 ufs_extattr_uepm_unlock(ump, p); 329 330 return (error); 331 332 case UFS_EXTATTR_CMD_DISABLE: 333 error = copyinstr(attrname, local_attrname, 334 UFS_EXTATTR_MAXEXTATTRNAME, &len); 335 336 ufs_extattr_uepm_lock(ump, p); 337 error = ufs_extattr_disable(ump, local_attrname, p); 338 ufs_extattr_uepm_unlock(ump, p); 339 340 return (error); 341 342 default: 343 return (EINVAL); 344 } 345} 346 347/* 348 * Credential check based on process requesting service, and per-attribute 349 * permissions. 350 */ 351static int 352ufs_extattr_credcheck(struct ufs_extattr_list_entry *uele, u_int32_t fowner, 353 struct ucred *cred, struct proc *p, int access) 354{ 355 u_int uef_perm; 356 357 switch(access) { 358 case IREAD: 359 uef_perm = uele->uele_fileheader.uef_read_perm; 360 break; 361 case IWRITE: 362 uef_perm = uele->uele_fileheader.uef_write_perm; 363 break; 364 default: 365 return (EACCES); 366 } 367 368 /* Kernel sponsoring request does so without passing a cred */ 369 if (!cred) 370 return (0); 371 372 /* XXX there might eventually be a capability check here */ 373 374 /* If it's set to root-only, check for suser(p) */ 375 if (uef_perm == UFS_EXTATTR_PERM_ROOT && !suser(p)) 376 return (0); 377 378 /* Allow the owner if appropriate */ 379 if (uef_perm == UFS_EXTATTR_PERM_OWNER && cred->cr_uid == fowner) 380 return (0); 381 382 /* Allow anyone if appropriate */ 383 if (uef_perm == UFS_EXTATTR_PERM_ANYONE) 384 return (0); 385 386 return (EACCES); 387} 388 389/* 390 * Vnode operating to retrieve a named extended attribute 391 */ 392int 393ufs_vop_getextattr(struct vop_getextattr_args *ap) 394/* 395vop_getextattr { 396 IN struct vnode *a_vp; 397 IN char *a_name; 398 INOUT struct uio *a_uio; 399 IN struct ucred *a_cred; 400 IN struct proc *a_p; 401}; 402*/ 403{ 404 struct mount *mp = ap->a_vp->v_mount; 405 struct ufsmount *ump = VFSTOUFS(mp); 406 int error; 407 408 ufs_extattr_uepm_lock(ump, ap->a_p); 409 410 error = ufs_extattr_get(ap->a_vp, ap->a_name, ap->a_uio, ap->a_cred, 411 ap->a_p); 412 413 ufs_extattr_uepm_unlock(ump, ap->a_p); 414 415 return (error); 416} 417 418/* 419 * Real work associated with retrieving a named attribute--assumes that 420 * the attribute lock has already been grabbed. 421 */ 422static int 423ufs_extattr_get(struct vnode *vp, char *name, struct uio *uio, 424 struct ucred *cred, struct proc *p) 425{ 426 struct ufs_extattr_list_entry *attribute; 427 struct ufs_extattr_header ueh; 428 struct iovec local_aiov; 429 struct uio local_aio; 430 struct mount *mp = vp->v_mount; 431 struct ufsmount *ump = VFSTOUFS(mp); 432 struct inode *ip = VTOI(vp); 433 off_t base_offset, old_offset, offset; 434 size_t size, old_size; 435 int error = 0; 436 437 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 438 return (EOPNOTSUPP); 439 440 attribute = ufs_exttatr_find_attr(ump, name); 441 if (!attribute) 442 return (ENOENT); 443 444 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, 445 IREAD))) 446 return (error); 447 448 /* 449 * Early rejection of offsets that are invalid 450 */ 451 if (uio->uio_offset >= attribute->uele_fileheader.uef_size || 452 uio->uio_offset < 0) 453 return (ENXIO); 454 455 /* 456 * Find base offset of header in file based on file header size, and 457 * data header size + maximum data size, indexed by inode number 458 */ 459 base_offset = sizeof(struct ufs_extattr_fileheader) + 460 ip->i_number * (sizeof(struct ufs_extattr_header) + 461 attribute->uele_fileheader.uef_size); 462 463 /* 464 * Read in the data header to see if the data is defined, and if so 465 * how much. 466 */ 467 bzero(&ueh, sizeof(struct ufs_extattr_header)); 468 local_aiov.iov_base = (caddr_t) &ueh; 469 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 470 local_aio.uio_iov = &local_aiov; 471 local_aio.uio_iovcnt = 1; 472 local_aio.uio_rw = UIO_READ; 473 local_aio.uio_segflg = UIO_SYSSPACE; 474 local_aio.uio_procp = p; 475 local_aio.uio_offset = base_offset; 476 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 477 478 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ); 479 vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_NOPAUSE | 480 LK_RETRY, p); 481 482 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0, 483 ump->um_extattr.uepm_ucred); 484 if (error) 485 goto vopunlock_exit; 486 487 /* defined? */ 488 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 489 error = ENOENT; 490 goto vopunlock_exit; 491 } 492 493 /* valid for the current inode generation? */ 494 if (ueh.ueh_i_gen != ip->i_gen) { 495 /* 496 * The inode itself has a different generation number 497 * than the attribute data. For now, the best solution 498 * is to coerce this to undefined, and let it get cleaned 499 * up by the next write or extattrctl clean. 500 */ 501 printf("ufs_extattr: inode number inconsistency (%d, %d)\n", 502 ueh.ueh_i_gen, ip->i_gen); 503 error = ENOENT; 504 goto vopunlock_exit; 505 } 506 507 /* local size consistency check */ 508 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 509 error = ENXIO; 510 goto vopunlock_exit; 511 } 512 513 if (ueh.ueh_len < uio->uio_offset) { 514 error = 0; 515 goto vopunlock_exit; 516 } 517 518 /* allow for offset into the attr data */ 519 offset = base_offset + sizeof(struct ufs_extattr_header) + 520 uio->uio_offset; 521 522 /* 523 * Figure out maximum to transfer -- use buffer size and local data 524 * limit. 525 */ 526 size = MIN(uio->uio_resid, ueh.ueh_len - uio->uio_offset); 527 528 old_offset = uio->uio_offset; 529 uio->uio_offset = offset; 530 old_size = uio->uio_resid; 531 uio->uio_resid = size; 532 533 error = VOP_READ(attribute->uele_backing_vnode, uio, 0, 534 ump->um_extattr.uepm_ucred); 535 if (error) { 536 uio->uio_offset = old_offset; 537 goto vopunlock_exit; 538 } 539 540 uio->uio_offset = old_offset; 541 uio->uio_resid = old_size - (size - uio->uio_resid); 542 543vopunlock_exit: 544 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 545 546 return (error); 547} 548 549/* 550 * Vnode operation to set a named attribute 551 */ 552int 553ufs_vop_setextattr(struct vop_setextattr_args *ap) 554/* 555vop_setextattr { 556 IN struct vnode *a_vp; 557 IN char *a_name; 558 INOUT struct uio *a_uio; 559 IN struct ucred *a_cred; 560 IN struct proc *a_p; 561}; 562*/ 563{ 564 struct mount *mp = ap->a_vp->v_mount; 565 struct ufsmount *ump = VFSTOUFS(mp); 566 567 int error; 568 569 ufs_extattr_uepm_lock(ump, ap->a_p); 570 571 if (ap->a_uio) 572 error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio, 573 ap->a_cred, ap->a_p); 574 else 575 error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred, 576 ap->a_p); 577 578 ufs_extattr_uepm_unlock(ump, ap->a_p); 579 580 return (error); 581} 582 583/* 584 * Real work associated with setting a vnode's extended attributes; 585 * assumes that the attribute lock has already been grabbed. 586 */ 587static int 588ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio, 589 struct ucred *cred, struct proc *p) 590{ 591 struct ufs_extattr_list_entry *attribute; 592 struct ufs_extattr_header ueh; 593 struct iovec local_aiov; 594 struct uio local_aio; 595 struct mount *mp = vp->v_mount; 596 struct ufsmount *ump = VFSTOUFS(mp); 597 struct inode *ip = VTOI(vp); 598 off_t base_offset; 599 600 int error = 0; 601 602 if (vp->v_mount->mnt_flag & MNT_RDONLY) 603 return (EROFS); 604 605 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 606 return (EOPNOTSUPP); 607 608 attribute = ufs_exttatr_find_attr(ump, name); 609 if (!attribute) 610 return (ENOENT); 611 612 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, 613 p, IWRITE))) 614 return (error); 615 616 /* 617 * Early rejection of invalid offsets/lengths 618 * Reject: any offset but 0 (replace) 619 * Any size greater than attribute size limit 620 */ 621 if (uio->uio_offset != 0 || 622 uio->uio_resid > attribute->uele_fileheader.uef_size) 623 return (ENXIO); 624 625 /* 626 * Find base offset of header in file based on file header size, and 627 * data header size + maximum data size, indexed by inode number 628 */ 629 base_offset = sizeof(struct ufs_extattr_fileheader) + 630 ip->i_number * (sizeof(struct ufs_extattr_header) + 631 attribute->uele_fileheader.uef_size); 632 633 /* 634 * Write out a data header for the data 635 */ 636 ueh.ueh_len = uio->uio_resid; 637 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 638 ueh.ueh_i_gen = ip->i_gen; 639 local_aiov.iov_base = (caddr_t) &ueh; 640 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 641 local_aio.uio_iov = &local_aiov; 642 local_aio.uio_iovcnt = 1; 643 local_aio.uio_rw = UIO_WRITE; 644 local_aio.uio_segflg = UIO_SYSSPACE; 645 local_aio.uio_procp = p; 646 local_aio.uio_offset = base_offset; 647 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 648 649 /* 650 * Acquire locks 651 */ 652 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 653 654 /* 655 * Don't need to get a lock on the backing file if the setattr is 656 * being applied to the backing file, as the lock is already held 657 */ 658 if (attribute->uele_backing_vnode != vp) 659 vn_lock(attribute->uele_backing_vnode, 660 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 661 662 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, 663 ump->um_extattr.uepm_ucred); 664 if (error) 665 goto vopunlock_exit; 666 667 if (local_aio.uio_resid != 0) { 668 error = ENXIO; 669 goto vopunlock_exit; 670 } 671 672 /* 673 * Write out user data 674 */ 675 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 676 677 error = VOP_WRITE(attribute->uele_backing_vnode, uio, IO_SYNC, 678 ump->um_extattr.uepm_ucred); 679 680vopunlock_exit: 681 uio->uio_offset = 0; 682 683 if (attribute->uele_backing_vnode != vp) 684 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 685 686 return (error); 687} 688 689/* 690 * Real work associated with removing an extended attribute from a vnode. 691 * Assumes the attribute lock has already been grabbed. 692 */ 693static int 694ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred, 695 struct proc *p) 696{ 697 struct ufs_extattr_list_entry *attribute; 698 struct ufs_extattr_header ueh; 699 struct iovec local_aiov; 700 struct uio local_aio; 701 struct mount *mp = vp->v_mount; 702 struct ufsmount *ump = VFSTOUFS(mp); 703 struct inode *ip = VTOI(vp); 704 off_t base_offset; 705 int error = 0; 706 707 if (vp->v_mount->mnt_flag & MNT_RDONLY) 708 return (EROFS); 709 710 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 711 return (EOPNOTSUPP); 712 713 attribute = ufs_exttatr_find_attr(ump, name); 714 if (!attribute) 715 return (ENOENT); 716 717 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, 718 IWRITE))) 719 return (error); 720 721 /* 722 * Find base offset of header in file based on file header size, and 723 * data header size + maximum data size, indexed by inode number 724 */ 725 base_offset = sizeof(struct ufs_extattr_fileheader) + 726 ip->i_number * (sizeof(struct ufs_extattr_header) + 727 attribute->uele_fileheader.uef_size); 728 729 /* 730 * Read in the data header to see if the data is defined 731 */ 732 bzero(&ueh, sizeof(struct ufs_extattr_header)); 733 734 local_aiov.iov_base = (caddr_t) &ueh; 735 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 736 local_aio.uio_iov = &local_aiov; 737 local_aio.uio_iovcnt = 1; 738 local_aio.uio_rw = UIO_READ; 739 local_aio.uio_segflg = UIO_SYSSPACE; 740 local_aio.uio_procp = p; 741 local_aio.uio_offset = base_offset; 742 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 743 744 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 745 746 /* 747 * Don't need to get the lock on the backing vnode if the vnode we're 748 * modifying is it, as we already hold the lock. 749 */ 750 if (attribute->uele_backing_vnode != vp) 751 vn_lock(attribute->uele_backing_vnode, 752 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 753 754 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0, 755 ump->um_extattr.uepm_ucred); 756 if (error) 757 goto vopunlock_exit; 758 759 /* defined? */ 760 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 761 error = ENOENT; 762 goto vopunlock_exit; 763 } 764 765 /* flag it as not in use */ 766 ueh.ueh_flags = 0; 767 768 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, 769 ump->um_extattr.uepm_ucred); 770 if (error) 771 goto vopunlock_exit; 772 773 if (local_aio.uio_resid != 0) 774 error = ENXIO; 775 776vopunlock_exit: 777 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 778 779 return (error); 780} 781 782/* 783 * Called by UFS when an inode is no longer active and should have its 784 * attributes stripped. 785 */ 786void 787ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) 788{ 789 struct ufs_extattr_list_entry *uele; 790 struct mount *mp = vp->v_mount; 791 struct ufsmount *ump = VFSTOUFS(mp); 792 793 ufs_extattr_uepm_lock(ump, p); 794 795 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 796 ufs_extattr_uepm_unlock(ump, p); 797 return; 798 } 799 800 for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL; 801 uele = uele->uele_entries.le_next) 802 ufs_extattr_rm(vp, uele->uele_attrname, 0, p); 803 804 ufs_extattr_uepm_unlock(ump, p); 805} 806