ufs_extattr.c revision 70764
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 70764 2001-01-07 21:07:22Z 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 /* allow for offset into the attr data */ 590 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 591 592 /* 593 * Figure out maximum to transfer -- use buffer size and local data 594 * limit. 595 */ 596 size = MIN(uio->uio_resid, ueh.ueh_len); 597 old_size = uio->uio_resid; 598 uio->uio_resid = size; 599 600 error = VOP_READ(attribute->uele_backing_vnode, uio, 601 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 602 if (error) 603 goto vopunlock_exit; 604 605 uio->uio_resid = old_size - (size - uio->uio_resid); 606 607vopunlock_exit: 608 609 uio->uio_offset = 0; 610 611 if (attribute->uele_backing_vnode != vp) 612 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 613 614 return (error); 615} 616 617/* 618 * Vnode operation to set a named attribute 619 */ 620int 621ufs_vop_setextattr(struct vop_setextattr_args *ap) 622/* 623vop_setextattr { 624 IN struct vnode *a_vp; 625 IN const char *a_name; 626 INOUT struct uio *a_uio; 627 IN struct ucred *a_cred; 628 IN struct proc *a_p; 629}; 630*/ 631{ 632 struct mount *mp = ap->a_vp->v_mount; 633 struct ufsmount *ump = VFSTOUFS(mp); 634 635 int error; 636 637 ufs_extattr_uepm_lock(ump, ap->a_p); 638 639 if (ap->a_uio != NULL) 640 error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio, 641 ap->a_cred, ap->a_p); 642 else 643 error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred, 644 ap->a_p); 645 646 ufs_extattr_uepm_unlock(ump, ap->a_p); 647 648 return (error); 649} 650 651/* 652 * Real work associated with setting a vnode's extended attributes; 653 * assumes that the attribute lock has already been grabbed. 654 */ 655static int 656ufs_extattr_set(struct vnode *vp, const char *name, struct uio *uio, 657 struct ucred *cred, struct proc *p) 658{ 659 struct ufs_extattr_list_entry *attribute; 660 struct ufs_extattr_header ueh; 661 struct iovec local_aiov; 662 struct uio local_aio; 663 struct mount *mp = vp->v_mount; 664 struct ufsmount *ump = VFSTOUFS(mp); 665 struct inode *ip = VTOI(vp); 666 off_t base_offset; 667 int error = 0; 668 669 if (vp->v_mount->mnt_flag & MNT_RDONLY) 670 return (EROFS); 671 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 672 return (EOPNOTSUPP); 673 if (!ufs_extattr_valid_attrname(name)) 674 return (EINVAL); 675 676 attribute = ufs_extattr_find_attr(ump, name); 677 if (!attribute) 678 return (ENOENT); 679 680 if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) 681 return (error); 682 683 /* 684 * Early rejection of invalid offsets/lengths 685 * Reject: any offset but 0 (replace) 686 * Any size greater than attribute size limit 687 */ 688 if (uio->uio_offset != 0 || 689 uio->uio_resid > attribute->uele_fileheader.uef_size) 690 return (ENXIO); 691 692 /* 693 * Find base offset of header in file based on file header size, and 694 * data header size + maximum data size, indexed by inode number 695 */ 696 base_offset = sizeof(struct ufs_extattr_fileheader) + 697 ip->i_number * (sizeof(struct ufs_extattr_header) + 698 attribute->uele_fileheader.uef_size); 699 700 /* 701 * Write out a data header for the data 702 */ 703 ueh.ueh_len = uio->uio_resid; 704 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 705 ueh.ueh_i_gen = ip->i_gen; 706 local_aiov.iov_base = (caddr_t) &ueh; 707 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 708 local_aio.uio_iov = &local_aiov; 709 local_aio.uio_iovcnt = 1; 710 local_aio.uio_rw = UIO_WRITE; 711 local_aio.uio_segflg = UIO_SYSSPACE; 712 local_aio.uio_procp = p; 713 local_aio.uio_offset = base_offset; 714 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 715 716 /* 717 * Acquire locks. 718 */ 719 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 720 721 /* 722 * Don't need to get a lock on the backing file if the setattr is 723 * being applied to the backing file, as the lock is already held. 724 */ 725 if (attribute->uele_backing_vnode != vp) 726 vn_lock(attribute->uele_backing_vnode, 727 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 728 729 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 730 IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); 731 if (error) 732 goto vopunlock_exit; 733 734 if (local_aio.uio_resid != 0) { 735 error = ENXIO; 736 goto vopunlock_exit; 737 } 738 739 /* 740 * Write out user data 741 */ 742 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 743 744 error = VOP_WRITE(attribute->uele_backing_vnode, uio, 745 IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); 746 747vopunlock_exit: 748 uio->uio_offset = 0; 749 750 if (attribute->uele_backing_vnode != vp) 751 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 752 753 return (error); 754} 755 756/* 757 * Real work associated with removing an extended attribute from a vnode. 758 * Assumes the attribute lock has already been grabbed. 759 */ 760static int 761ufs_extattr_rm(struct vnode *vp, const char *name, struct ucred *cred, 762 struct proc *p) 763{ 764 struct ufs_extattr_list_entry *attribute; 765 struct ufs_extattr_header ueh; 766 struct iovec local_aiov; 767 struct uio local_aio; 768 struct mount *mp = vp->v_mount; 769 struct ufsmount *ump = VFSTOUFS(mp); 770 struct inode *ip = VTOI(vp); 771 off_t base_offset; 772 int error = 0; 773 774 if (vp->v_mount->mnt_flag & MNT_RDONLY) 775 return (EROFS); 776 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 777 return (EOPNOTSUPP); 778 if (!ufs_extattr_valid_attrname(name)) 779 return (EINVAL); 780 781 attribute = ufs_extattr_find_attr(ump, name); 782 if (!attribute) 783 return (ENOENT); 784 785 if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) 786 return (error); 787 788 /* 789 * Find base offset of header in file based on file header size, and 790 * data header size + maximum data size, indexed by inode number 791 */ 792 base_offset = sizeof(struct ufs_extattr_fileheader) + 793 ip->i_number * (sizeof(struct ufs_extattr_header) + 794 attribute->uele_fileheader.uef_size); 795 796 /* 797 * Check to see if currently defined. 798 */ 799 bzero(&ueh, sizeof(struct ufs_extattr_header)); 800 801 local_aiov.iov_base = (caddr_t) &ueh; 802 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 803 local_aio.uio_iov = &local_aiov; 804 local_aio.uio_iovcnt = 1; 805 local_aio.uio_rw = UIO_READ; 806 local_aio.uio_segflg = UIO_SYSSPACE; 807 local_aio.uio_procp = p; 808 local_aio.uio_offset = base_offset; 809 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 810 811 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 812 813 /* 814 * Don't need to get the lock on the backing vnode if the vnode we're 815 * modifying is it, as we already hold the lock. 816 */ 817 if (attribute->uele_backing_vnode != vp) 818 vn_lock(attribute->uele_backing_vnode, 819 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 820 821 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 822 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 823 if (error) 824 goto vopunlock_exit; 825 826 /* defined? */ 827 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 828 error = ENOENT; 829 goto vopunlock_exit; 830 } 831 832 /* flag it as not in use */ 833 ueh.ueh_flags = 0; 834 835 local_aio.uio_offset = base_offset; 836 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 837 local_aio.uio_rw = UIO_WRITE; 838 839 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 840 IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); 841 if (error) 842 goto vopunlock_exit; 843 844 if (local_aio.uio_resid != 0) 845 error = ENXIO; 846 847vopunlock_exit: 848 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 849 850 return (error); 851} 852 853/* 854 * Called by UFS when an inode is no longer active and should have its 855 * attributes stripped. 856 */ 857void 858ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) 859{ 860 struct ufs_extattr_list_entry *uele; 861 struct mount *mp = vp->v_mount; 862 struct ufsmount *ump = VFSTOUFS(mp); 863 864 ufs_extattr_uepm_lock(ump, p); 865 866 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 867 ufs_extattr_uepm_unlock(ump, p); 868 return; 869 } 870 871 for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL; 872 uele = uele->uele_entries.le_next) 873 ufs_extattr_rm(vp, uele->uele_attrname, 0, p); 874 875 ufs_extattr_uepm_unlock(ump, p); 876} 877