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