ufs_extattr.c revision 59241
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 59241 2000-04-15 03:34:27Z 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 /* local size consistency check */ 494 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 495 error = ENXIO; 496 goto vopunlock_exit; 497 } 498 499 if (ueh.ueh_len < uio->uio_offset) { 500 error = 0; 501 goto vopunlock_exit; 502 } 503 504 /* allow for offset into the attr data */ 505 offset = base_offset + sizeof(struct ufs_extattr_header) + 506 uio->uio_offset; 507 508 /* 509 * Figure out maximum to transfer -- use buffer size and local data 510 * limit. 511 */ 512 size = MIN(uio->uio_resid, ueh.ueh_len - uio->uio_offset); 513 514 old_offset = uio->uio_offset; 515 uio->uio_offset = offset; 516 old_size = uio->uio_resid; 517 uio->uio_resid = size; 518 519 error = VOP_READ(attribute->uele_backing_vnode, uio, 0, 520 ump->um_extattr.uepm_ucred); 521 if (error) { 522 uio->uio_offset = old_offset; 523 goto vopunlock_exit; 524 } 525 526 uio->uio_offset = old_offset; 527 uio->uio_resid = old_size - (size - uio->uio_resid); 528 529vopunlock_exit: 530 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 531 532 return (error); 533} 534 535/* 536 * Vnode operation to set a named attribute 537 */ 538int 539ufs_vop_setextattr(struct vop_setextattr_args *ap) 540/* 541vop_setextattr { 542 IN struct vnode *a_vp; 543 IN char *a_name; 544 INOUT struct uio *a_uio; 545 IN struct ucred *a_cred; 546 IN struct proc *a_p; 547}; 548*/ 549{ 550 struct mount *mp = ap->a_vp->v_mount; 551 struct ufsmount *ump = VFSTOUFS(mp); 552 553 int error; 554 555 ufs_extattr_uepm_lock(ump, ap->a_p); 556 557 if (ap->a_uio) 558 error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio, 559 ap->a_cred, ap->a_p); 560 else 561 error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred, 562 ap->a_p); 563 564 ufs_extattr_uepm_unlock(ump, ap->a_p); 565 566 return (error); 567} 568 569/* 570 * Real work associated with setting a vnode's extended attributes; 571 * assumes that the attribute lock has already been grabbed. 572 */ 573static int 574ufs_extattr_set(struct vnode *vp, char *name, struct uio *uio, 575 struct ucred *cred, struct proc *p) 576{ 577 struct ufs_extattr_list_entry *attribute; 578 struct ufs_extattr_header ueh; 579 struct iovec local_aiov; 580 struct uio local_aio; 581 struct mount *mp = vp->v_mount; 582 struct ufsmount *ump = VFSTOUFS(mp); 583 struct inode *ip = VTOI(vp); 584 off_t base_offset; 585 586 int error = 0; 587 588 if (vp->v_mount->mnt_flag & MNT_RDONLY) 589 return (EROFS); 590 591 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 592 return (EOPNOTSUPP); 593 594 attribute = ufs_exttatr_find_attr(ump, name); 595 if (!attribute) 596 return (ENOENT); 597 598 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, 599 p, IWRITE))) 600 return (error); 601 602 /* 603 * Early rejection of invalid offsets/lengths 604 * Reject: any offset but 0 (replace) 605 * Any size greater than attribute size limit 606 */ 607 if (uio->uio_offset != 0 || 608 uio->uio_resid > attribute->uele_fileheader.uef_size) 609 return (ENXIO); 610 611 /* 612 * Find base offset of header in file based on file header size, and 613 * data header size + maximum data size, indexed by inode number 614 */ 615 base_offset = sizeof(struct ufs_extattr_fileheader) + 616 ip->i_number * (sizeof(struct ufs_extattr_header) + 617 attribute->uele_fileheader.uef_size); 618 619 /* 620 * Write out a data header for the data 621 */ 622 ueh.ueh_len = uio->uio_resid; 623 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 624 local_aiov.iov_base = (caddr_t) &ueh; 625 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 626 local_aio.uio_iov = &local_aiov; 627 local_aio.uio_iovcnt = 1; 628 local_aio.uio_rw = UIO_WRITE; 629 local_aio.uio_segflg = UIO_SYSSPACE; 630 local_aio.uio_procp = p; 631 local_aio.uio_offset = base_offset; 632 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 633 634 /* 635 * Acquire locks 636 */ 637 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 638 639 /* 640 * Don't need to get a lock on the backing file if the setattr is 641 * being applied to the backing file, as the lock is already held 642 */ 643 if (attribute->uele_backing_vnode != vp) 644 vn_lock(attribute->uele_backing_vnode, 645 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 646 647 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, 648 ump->um_extattr.uepm_ucred); 649 if (error) 650 goto vopunlock_exit; 651 652 if (local_aio.uio_resid != 0) 653 error = ENXIO; 654 goto vopunlock_exit; 655 656 /* 657 * Write out user data 658 */ 659 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 660 661 error = VOP_WRITE(attribute->uele_backing_vnode, uio, 0, 662 ump->um_extattr.uepm_ucred); 663 664vopunlock_exit: 665 uio->uio_offset = 0; 666 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 667 668 return (error); 669} 670 671/* 672 * Real work associated with removing an extended attribute from a vnode. 673 * Assumes the attribute lock has already been grabbed. 674 */ 675static int 676ufs_extattr_rm(struct vnode *vp, char *name, struct ucred *cred, 677 struct proc *p) 678{ 679 struct ufs_extattr_list_entry *attribute; 680 struct ufs_extattr_header ueh; 681 struct iovec local_aiov; 682 struct uio local_aio; 683 struct mount *mp = vp->v_mount; 684 struct ufsmount *ump = VFSTOUFS(mp); 685 struct inode *ip = VTOI(vp); 686 off_t base_offset; 687 int error = 0; 688 689 if (vp->v_mount->mnt_flag & MNT_RDONLY) 690 return (EROFS); 691 692 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 693 return (EOPNOTSUPP); 694 695 attribute = ufs_exttatr_find_attr(ump, name); 696 if (!attribute) 697 return (ENOENT); 698 699 if ((error = ufs_extattr_credcheck(attribute, ip->i_uid, cred, p, 700 IWRITE))) 701 return (error); 702 703 /* 704 * Find base offset of header in file based on file header size, and 705 * data header size + maximum data size, indexed by inode number 706 */ 707 base_offset = sizeof(struct ufs_extattr_fileheader) + 708 ip->i_number * (sizeof(struct ufs_extattr_header) + 709 attribute->uele_fileheader.uef_size); 710 711 /* 712 * Read in the data header to see if the data is defined 713 */ 714 bzero(&ueh, sizeof(struct ufs_extattr_header)); 715 716 local_aiov.iov_base = (caddr_t) &ueh; 717 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 718 local_aio.uio_iov = &local_aiov; 719 local_aio.uio_iovcnt = 1; 720 local_aio.uio_rw = UIO_READ; 721 local_aio.uio_segflg = UIO_SYSSPACE; 722 local_aio.uio_procp = p; 723 local_aio.uio_offset = base_offset; 724 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 725 726 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 727 728 /* 729 * Don't need to get the lock on the backing vnode if the vnode we're 730 * modifying is it, as we already hold the lock. 731 */ 732 if (attribute->uele_backing_vnode != vp) 733 vn_lock(attribute->uele_backing_vnode, 734 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 735 736 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 0, 737 ump->um_extattr.uepm_ucred); 738 if (error) 739 goto vopunlock_exit; 740 741 /* defined? */ 742 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 743 error = ENOENT; 744 goto vopunlock_exit; 745 } 746 747 /* flag it as not in use */ 748 ueh.ueh_flags = 0; 749 750 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 0, 751 ump->um_extattr.uepm_ucred); 752 if (error) 753 goto vopunlock_exit; 754 755 if (local_aio.uio_resid != 0) 756 error = ENXIO; 757 758vopunlock_exit: 759 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 760 761 return (error); 762} 763 764/* 765 * Called by UFS when an inode is no longer active and should have its 766 * attributes stripped. 767 */ 768void 769ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) 770{ 771 struct ufs_extattr_list_entry *uele; 772 struct mount *mp = vp->v_mount; 773 struct ufsmount *ump = VFSTOUFS(mp); 774 775 ufs_extattr_uepm_lock(ump, p); 776 777 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 778 ufs_extattr_uepm_unlock(ump, p); 779 return; 780 } 781 782 for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL; 783 uele = uele->uele_entries.le_next) 784 ufs_extattr_rm(vp, uele->uele_attrname, 0, p); 785 786 ufs_extattr_uepm_unlock(ump, p); 787} 788