ufs_extattr.c revision 70776
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 70776 2001-01-07 23:45:56Z 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 attribute list) 95 * attrname consisting of "$" (used to treive system attribute 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]; /* Incl. 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 attributes. 518 */ 519 if (uio->uio_offset != 0) 520 return (ENXIO); 521 522 /* 523 * Find base offset of header in file based on file header size, and 524 * data header size + maximum data size, indexed by inode number. 525 */ 526 base_offset = sizeof(struct ufs_extattr_fileheader) + 527 ip->i_number * (sizeof(struct ufs_extattr_header) + 528 attribute->uele_fileheader.uef_size); 529 530 /* 531 * Read in the data header to see if the data is defined, and if so 532 * how much. 533 */ 534 bzero(&ueh, sizeof(struct ufs_extattr_header)); 535 local_aiov.iov_base = (caddr_t) &ueh; 536 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 537 local_aio.uio_iov = &local_aiov; 538 local_aio.uio_iovcnt = 1; 539 local_aio.uio_rw = UIO_READ; 540 local_aio.uio_segflg = UIO_SYSSPACE; 541 local_aio.uio_procp = p; 542 local_aio.uio_offset = base_offset; 543 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 544 545 /* 546 * Acquire locks. 547 */ 548 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_READ); 549 /* 550 * Don't need to get a lock on the backing file if the getattr is 551 * being applied to the backing file, as the lock is already held. 552 */ 553 if (attribute->uele_backing_vnode != vp) 554 vn_lock(attribute->uele_backing_vnode, LK_SHARED | 555 LK_NOPAUSE | LK_RETRY, p); 556 557 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 558 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 559 if (error) 560 goto vopunlock_exit; 561 562 /* Defined? */ 563 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 564 error = ENOENT; 565 goto vopunlock_exit; 566 } 567 568 /* Valid for the current inode generation? */ 569 if (ueh.ueh_i_gen != ip->i_gen) { 570 /* 571 * The inode itself has a different generation number 572 * than the attribute data. For now, the best solution 573 * is to coerce this to undefined, and let it get cleaned 574 * up by the next write or extattrctl clean. 575 */ 576 printf("ufs_extattr_get: inode number inconsistency (%d, %d)\n", 577 ueh.ueh_i_gen, ip->i_gen); 578 error = ENOENT; 579 goto vopunlock_exit; 580 } 581 582 /* Local size consistency check. */ 583 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 584 error = ENXIO; 585 goto vopunlock_exit; 586 } 587 588 /* Allow for offset into the attribute data. */ 589 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 590 591 /* 592 * Figure out maximum to transfer -- use buffer size and local data 593 * limit. 594 */ 595 size = MIN(uio->uio_resid, ueh.ueh_len); 596 old_size = uio->uio_resid; 597 uio->uio_resid = size; 598 599 error = VOP_READ(attribute->uele_backing_vnode, uio, 600 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 601 if (error) 602 goto vopunlock_exit; 603 604 uio->uio_resid = old_size - (size - uio->uio_resid); 605 606vopunlock_exit: 607 608 uio->uio_offset = 0; 609 610 if (attribute->uele_backing_vnode != vp) 611 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 612 613 return (error); 614} 615 616/* 617 * Vnode operation to set a named attribute. 618 */ 619int 620ufs_vop_setextattr(struct vop_setextattr_args *ap) 621/* 622vop_setextattr { 623 IN struct vnode *a_vp; 624 IN const char *a_name; 625 INOUT struct uio *a_uio; 626 IN struct ucred *a_cred; 627 IN struct proc *a_p; 628}; 629*/ 630{ 631 struct mount *mp = ap->a_vp->v_mount; 632 struct ufsmount *ump = VFSTOUFS(mp); 633 634 int error; 635 636 ufs_extattr_uepm_lock(ump, ap->a_p); 637 638 if (ap->a_uio != NULL) 639 error = ufs_extattr_set(ap->a_vp, ap->a_name, ap->a_uio, 640 ap->a_cred, ap->a_p); 641 else 642 error = ufs_extattr_rm(ap->a_vp, ap->a_name, ap->a_cred, 643 ap->a_p); 644 645 ufs_extattr_uepm_unlock(ump, ap->a_p); 646 647 return (error); 648} 649 650/* 651 * Real work associated with setting a vnode's extended attributes; 652 * assumes that the attribute lock has already been grabbed. 653 */ 654static int 655ufs_extattr_set(struct vnode *vp, const char *name, struct uio *uio, 656 struct ucred *cred, struct proc *p) 657{ 658 struct ufs_extattr_list_entry *attribute; 659 struct ufs_extattr_header ueh; 660 struct iovec local_aiov; 661 struct uio local_aio; 662 struct mount *mp = vp->v_mount; 663 struct ufsmount *ump = VFSTOUFS(mp); 664 struct inode *ip = VTOI(vp); 665 off_t base_offset; 666 int error = 0; 667 668 if (vp->v_mount->mnt_flag & MNT_RDONLY) 669 return (EROFS); 670 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 671 return (EOPNOTSUPP); 672 if (!ufs_extattr_valid_attrname(name)) 673 return (EINVAL); 674 675 attribute = ufs_extattr_find_attr(ump, name); 676 if (!attribute) 677 return (ENOENT); 678 679 if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) 680 return (error); 681 682 /* 683 * Early rejection of invalid offsets/length. 684 * Reject: any offset but 0 (replace) 685 * Any size greater than attribute size limit 686 */ 687 if (uio->uio_offset != 0 || 688 uio->uio_resid > attribute->uele_fileheader.uef_size) 689 return (ENXIO); 690 691 /* 692 * Find base offset of header in file based on file header size, and 693 * data header size + maximum data size, indexed by inode number. 694 */ 695 base_offset = sizeof(struct ufs_extattr_fileheader) + 696 ip->i_number * (sizeof(struct ufs_extattr_header) + 697 attribute->uele_fileheader.uef_size); 698 699 /* 700 * Write out a data header for the data. 701 */ 702 ueh.ueh_len = uio->uio_resid; 703 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 704 ueh.ueh_i_gen = ip->i_gen; 705 local_aiov.iov_base = (caddr_t) &ueh; 706 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 707 local_aio.uio_iov = &local_aiov; 708 local_aio.uio_iovcnt = 1; 709 local_aio.uio_rw = UIO_WRITE; 710 local_aio.uio_segflg = UIO_SYSSPACE; 711 local_aio.uio_procp = p; 712 local_aio.uio_offset = base_offset; 713 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 714 715 /* 716 * Acquire locks. 717 */ 718 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 719 720 /* 721 * Don't need to get a lock on the backing file if the setattr is 722 * being applied to the backing file, as the lock is already held. 723 */ 724 if (attribute->uele_backing_vnode != vp) 725 vn_lock(attribute->uele_backing_vnode, 726 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 727 728 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 729 IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); 730 if (error) 731 goto vopunlock_exit; 732 733 if (local_aio.uio_resid != 0) { 734 error = ENXIO; 735 goto vopunlock_exit; 736 } 737 738 /* 739 * Write out user data. 740 */ 741 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 742 743 error = VOP_WRITE(attribute->uele_backing_vnode, uio, 744 IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); 745 746vopunlock_exit: 747 uio->uio_offset = 0; 748 749 if (attribute->uele_backing_vnode != vp) 750 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 751 752 return (error); 753} 754 755/* 756 * Real work associated with removing an extended attribute from a vnode. 757 * Assumes the attribute lock has already been grabbed. 758 */ 759static int 760ufs_extattr_rm(struct vnode *vp, const char *name, struct ucred *cred, 761 struct proc *p) 762{ 763 struct ufs_extattr_list_entry *attribute; 764 struct ufs_extattr_header ueh; 765 struct iovec local_aiov; 766 struct uio local_aio; 767 struct mount *mp = vp->v_mount; 768 struct ufsmount *ump = VFSTOUFS(mp); 769 struct inode *ip = VTOI(vp); 770 off_t base_offset; 771 int error = 0; 772 773 if (vp->v_mount->mnt_flag & MNT_RDONLY) 774 return (EROFS); 775 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 776 return (EOPNOTSUPP); 777 if (!ufs_extattr_valid_attrname(name)) 778 return (EINVAL); 779 780 attribute = ufs_extattr_find_attr(ump, name); 781 if (!attribute) 782 return (ENOENT); 783 784 if ((error = ufs_extattr_credcheck(vp, attribute, cred, p, IWRITE))) 785 return (error); 786 787 /* 788 * Find base offset of header in file based on file header size, and 789 * data header size + maximum data size, indexed by inode number. 790 */ 791 base_offset = sizeof(struct ufs_extattr_fileheader) + 792 ip->i_number * (sizeof(struct ufs_extattr_header) + 793 attribute->uele_fileheader.uef_size); 794 795 /* 796 * Check to see if currently defined. 797 */ 798 bzero(&ueh, sizeof(struct ufs_extattr_header)); 799 800 local_aiov.iov_base = (caddr_t) &ueh; 801 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 802 local_aio.uio_iov = &local_aiov; 803 local_aio.uio_iovcnt = 1; 804 local_aio.uio_rw = UIO_READ; 805 local_aio.uio_segflg = UIO_SYSSPACE; 806 local_aio.uio_procp = p; 807 local_aio.uio_offset = base_offset; 808 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 809 810 VOP_LEASE(attribute->uele_backing_vnode, p, cred, LEASE_WRITE); 811 812 /* 813 * Don't need to get the lock on the backing vnode if the vnode we're 814 * modifying is it, as we already hold the lock. 815 */ 816 if (attribute->uele_backing_vnode != vp) 817 vn_lock(attribute->uele_backing_vnode, 818 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, p); 819 820 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 821 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 822 if (error) 823 goto vopunlock_exit; 824 825 /* Defined? */ 826 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 827 error = ENOENT; 828 goto vopunlock_exit; 829 } 830 831 /* Valid for the current inode generation? */ 832 if (ueh.ueh_i_gen != ip->i_gen) { 833 /* 834 * The inode itself has a different generation number than 835 * the attribute data. For now, the best solution is to 836 * coerce this to undefined, and let it get cleaned up by 837 * the next write or extattrctl clean. 838 */ 839 printf("ufs_extattr_rm: inode number inconsistency (%d, %d)\n", 840 ueh.ueh_i_gen, ip->i_gen); 841 error = ENOENT; 842 goto vopunlock_exit; 843 } 844 845 /* Flag it as not in use. */ 846 ueh.ueh_flags = 0; 847 ueh.ueh_len = 0; 848 849 local_aiov.iov_base = (caddr_t) &ueh; 850 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 851 local_aio.uio_iov = &local_aiov; 852 local_aio.uio_iovcnt = 1; 853 local_aio.uio_rw = UIO_WRITE; 854 local_aio.uio_segflg = UIO_SYSSPACE; 855 local_aio.uio_procp = p; 856 local_aio.uio_offset = base_offset; 857 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 858 859 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, 860 IO_NODELOCKED | IO_SYNC, ump->um_extattr.uepm_ucred); 861 if (error) 862 goto vopunlock_exit; 863 864 if (local_aio.uio_resid != 0) 865 error = ENXIO; 866 867vopunlock_exit: 868 VOP_UNLOCK(attribute->uele_backing_vnode, 0, p); 869 870 return (error); 871} 872 873/* 874 * Called by UFS when an inode is no longer active and should have its 875 * attributes stripped. 876 */ 877void 878ufs_extattr_vnode_inactive(struct vnode *vp, struct proc *p) 879{ 880 struct ufs_extattr_list_entry *uele; 881 struct mount *mp = vp->v_mount; 882 struct ufsmount *ump = VFSTOUFS(mp); 883 884 ufs_extattr_uepm_lock(ump, p); 885 886 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 887 ufs_extattr_uepm_unlock(ump, p); 888 return; 889 } 890 891 for (uele = ump->um_extattr.uepm_list.lh_first; uele != NULL; 892 uele = uele->uele_entries.le_next) 893 ufs_extattr_rm(vp, uele->uele_attrname, NULL, p); 894 895 ufs_extattr_uepm_unlock(ump, p); 896} 897