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