ufs_extattr.c revision 132023
1/*- 2 * Copyright (c) 1999-2002 Robert N. M. Watson 3 * Copyright (c) 2002-2003 Networks Associates Technology, Inc. 4 * All rights reserved. 5 * 6 * This software was developed by Robert Watson for the TrustedBSD Project. 7 * 8 * This software was developed for the FreeBSD Project in part by Network 9 * Associates Laboratories, the Security Research Division of Network 10 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 11 * as part of the DARPA CHATS research program. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 1. Redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer. 18 * 2. Redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 * 34 */ 35 36/* 37 * Support for filesystem extended attribute: UFS-specific support functions. 38 */ 39 40#include <sys/cdefs.h> 41__FBSDID("$FreeBSD: head/sys/ufs/ufs/ufs_extattr.c 132023 2004-07-12 08:14:09Z alfred $"); 42 43#include "opt_ufs.h" 44 45#include <sys/param.h> 46#include <sys/systm.h> 47#include <sys/kernel.h> 48#include <sys/namei.h> 49#include <sys/malloc.h> 50#include <sys/fcntl.h> 51#include <sys/proc.h> 52#include <sys/vnode.h> 53#include <sys/mount.h> 54#include <sys/lock.h> 55#include <sys/dirent.h> 56#include <sys/extattr.h> 57#include <sys/sysctl.h> 58 59#include <vm/uma.h> 60 61#include <ufs/ufs/dir.h> 62#include <ufs/ufs/extattr.h> 63#include <ufs/ufs/quota.h> 64#include <ufs/ufs/ufsmount.h> 65#include <ufs/ufs/inode.h> 66#include <ufs/ufs/ufs_extern.h> 67 68#ifdef UFS_EXTATTR 69 70static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); 71 72static int ufs_extattr_sync = 0; 73SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync, 74 0, ""); 75 76static int ufs_extattr_valid_attrname(int attrnamespace, 77 const char *attrname); 78static int ufs_extattr_enable_with_open(struct ufsmount *ump, 79 struct vnode *vp, int attrnamespace, const char *attrname, 80 struct thread *td); 81static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 82 const char *attrname, struct vnode *backing_vnode, 83 struct thread *td); 84static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 85 const char *attrname, struct thread *td); 86static int ufs_extattr_get(struct vnode *vp, int attrnamespace, 87 const char *name, struct uio *uio, size_t *size, 88 struct ucred *cred, struct thread *td); 89static int ufs_extattr_set(struct vnode *vp, int attrnamespace, 90 const char *name, struct uio *uio, struct ucred *cred, 91 struct thread *td); 92static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, 93 const char *name, struct ucred *cred, struct thread *td); 94 95/* 96 * Per-FS attribute lock protecting attribute operations. 97 * XXX Right now there is a lot of lock contention due to having a single 98 * lock per-FS; really, this should be far more fine-grained. 99 */ 100static void 101ufs_extattr_uepm_lock(struct ufsmount *ump, struct thread *td) 102{ 103 104 /* Ideally, LK_CANRECURSE would not be used, here. */ 105 lockmgr(&ump->um_extattr.uepm_lock, LK_EXCLUSIVE | LK_RETRY | 106 LK_CANRECURSE, 0, td); 107} 108 109static void 110ufs_extattr_uepm_unlock(struct ufsmount *ump, struct thread *td) 111{ 112 113 lockmgr(&ump->um_extattr.uepm_lock, LK_RELEASE, 0, td); 114} 115 116/*- 117 * Determine whether the name passed is a valid name for an actual 118 * attribute. 119 * 120 * Invalid currently consists of: 121 * NULL pointer for attrname 122 * zero-length attrname (used to retrieve application attribute list) 123 */ 124static int 125ufs_extattr_valid_attrname(int attrnamespace, const char *attrname) 126{ 127 128 if (attrname == NULL) 129 return (0); 130 if (strlen(attrname) == 0) 131 return (0); 132 return (1); 133} 134 135/* 136 * Locate an attribute given a name and mountpoint. 137 * Must be holding uepm lock for the mount point. 138 */ 139static struct ufs_extattr_list_entry * 140ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace, 141 const char *attrname) 142{ 143 struct ufs_extattr_list_entry *search_attribute; 144 145 for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list); 146 search_attribute != NULL; 147 search_attribute = LIST_NEXT(search_attribute, uele_entries)) { 148 if (!(strncmp(attrname, search_attribute->uele_attrname, 149 UFS_EXTATTR_MAXEXTATTRNAME)) && 150 (attrnamespace == search_attribute->uele_attrnamespace)) { 151 return (search_attribute); 152 } 153 } 154 155 return (0); 156} 157 158/* 159 * Initialize per-FS structures supporting extended attributes. Do not 160 * start extended attributes yet. 161 */ 162void 163ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) 164{ 165 166 uepm->uepm_flags = 0; 167 168 LIST_INIT(&uepm->uepm_list); 169 /* XXX is PVFS right, here? */ 170 lockinit(&uepm->uepm_lock, PVFS, "extattr", 0, 0); 171 uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; 172} 173 174/* 175 * Destroy per-FS structures supporting extended attributes. Assumes 176 * that EAs have already been stopped, and will panic if not. 177 */ 178void 179ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm) 180{ 181 182 if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 183 panic("ufs_extattr_uepm_destroy: not initialized"); 184 185 if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 186 panic("ufs_extattr_uepm_destroy: called while still started"); 187 188 /* 189 * It's not clear that either order for the next two lines is 190 * ideal, and it should never be a problem if this is only called 191 * during unmount, and with vfs_busy(). 192 */ 193 uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED; 194 lockdestroy(&uepm->uepm_lock); 195} 196 197/* 198 * Start extended attribute support on an FS. 199 */ 200int 201ufs_extattr_start(struct mount *mp, struct thread *td) 202{ 203 struct ufsmount *ump; 204 int error = 0; 205 206 ump = VFSTOUFS(mp); 207 208 ufs_extattr_uepm_lock(ump, td); 209 210 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) { 211 error = EOPNOTSUPP; 212 goto unlock; 213 } 214 if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) { 215 error = EBUSY; 216 goto unlock; 217 } 218 219 ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; 220 221 ump->um_extattr.uepm_ucred = crhold(td->td_ucred); 222 223unlock: 224 ufs_extattr_uepm_unlock(ump, td); 225 226 return (error); 227} 228 229#ifdef UFS_EXTATTR_AUTOSTART 230/* 231 * Helper routine: given a locked parent directory and filename, return 232 * the locked vnode of the inode associated with the name. Will not 233 * follow symlinks, may return any type of vnode. Lock on parent will 234 * be released even in the event of a failure. In the event that the 235 * target is the parent (i.e., "."), there will be two references and 236 * one lock, requiring the caller to possibly special-case. 237 */ 238#define UE_GETDIR_LOCKPARENT 1 239#define UE_GETDIR_LOCKPARENT_DONT 2 240static int 241ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, 242 struct vnode **vp, struct thread *td) 243{ 244 struct vop_cachedlookup_args vargs; 245 struct componentname cnp; 246 struct vnode *target_vp; 247 int error; 248 249 bzero(&cnp, sizeof(cnp)); 250 cnp.cn_nameiop = LOOKUP; 251 cnp.cn_flags = ISLASTCN; 252 if (lockparent == UE_GETDIR_LOCKPARENT) 253 cnp.cn_flags |= LOCKPARENT; 254 cnp.cn_thread = td; 255 cnp.cn_cred = td->td_ucred; 256 cnp.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 257 cnp.cn_nameptr = cnp.cn_pnbuf; 258 error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN, 259 (size_t *) &cnp.cn_namelen); 260 if (error) { 261 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) { 262 VOP_UNLOCK(start_dvp, 0, td); 263 } 264 uma_zfree(namei_zone, cnp.cn_pnbuf); 265 printf("ufs_extattr_lookup: copystr failed\n"); 266 return (error); 267 } 268 cnp.cn_namelen--; /* trim nul termination */ 269 vargs.a_desc = NULL; 270 vargs.a_dvp = start_dvp; 271 vargs.a_vpp = &target_vp; 272 vargs.a_cnp = &cnp; 273 error = ufs_lookup(&vargs); 274 uma_zfree(namei_zone, cnp.cn_pnbuf); 275 if (error) { 276 /* 277 * Error condition, may have to release the lock on the parent 278 * if ufs_lookup() didn't. 279 */ 280 if (!(cnp.cn_flags & PDIRUNLOCK) && 281 (lockparent == UE_GETDIR_LOCKPARENT_DONT)) 282 VOP_UNLOCK(start_dvp, 0, td); 283 284 /* 285 * Check that ufs_lookup() didn't release the lock when we 286 * didn't want it to. 287 */ 288 if ((cnp.cn_flags & PDIRUNLOCK) && 289 (lockparent == UE_GETDIR_LOCKPARENT)) 290 panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); 291 292 return (error); 293 } 294/* 295 if (target_vp == start_dvp) 296 panic("ufs_extattr_lookup: target_vp == start_dvp"); 297*/ 298 299 if (target_vp != start_dvp && 300 !(cnp.cn_flags & PDIRUNLOCK) && 301 (lockparent == UE_GETDIR_LOCKPARENT_DONT)) 302 panic("ufs_extattr_lookup: !lockparent but !PDIRUNLOCK"); 303 304 if ((cnp.cn_flags & PDIRUNLOCK) && 305 (lockparent == UE_GETDIR_LOCKPARENT)) 306 panic("ufs_extattr_lookup: lockparent but PDIRUNLOCK"); 307 308 /* printf("ufs_extattr_lookup: success\n"); */ 309 *vp = target_vp; 310 return (0); 311} 312#endif /* !UFS_EXTATTR_AUTOSTART */ 313 314/* 315 * Enable an EA using the passed filesystem, backing vnode, attribute name, 316 * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp 317 * to be locked when passed in. The vnode will be returned unlocked, 318 * regardless of success/failure of the function. As a result, the caller 319 * will always need to vrele(), but not vput(). 320 */ 321static int 322ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, 323 int attrnamespace, const char *attrname, struct thread *td) 324{ 325 int error; 326 327 error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, -1); 328 if (error) { 329 printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " 330 "with %d\n", error); 331 VOP_UNLOCK(vp, 0, td); 332 return (error); 333 } 334 335 /* 336 * XXX: Note, should VOP_CLOSE() if vfs_object_create() fails, but due 337 * to a similar piece of code in vn_open(), we don't. 338 */ 339 if (vn_canvmio(vp) == TRUE) 340 if ((error = vfs_object_create(vp, td, 341 td->td_ucred)) != 0) { 342 /* 343 * XXX: bug replicated from vn_open(): should 344 * VOP_CLOSE() here. 345 */ 346 VOP_UNLOCK(vp, 0, td); 347 return (error); 348 } 349 350 vp->v_writecount++; 351 352 vref(vp); 353 354 VOP_UNLOCK(vp, 0, td); 355 356 error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td); 357 if (error != 0) 358 vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 359 return (error); 360} 361 362#ifdef UFS_EXTATTR_AUTOSTART 363/* 364 * Given a locked directory vnode, iterate over the names in the directory 365 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential 366 * attribute files. Then invoke ufs_extattr_enable_with_open() on each 367 * to attempt to start the attribute. Leaves the directory locked on 368 * exit. 369 */ 370static int 371ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, 372 int attrnamespace, struct thread *td) 373{ 374 struct vop_readdir_args vargs; 375 struct dirent *dp, *edp; 376 struct vnode *attr_vp; 377 struct uio auio; 378 struct iovec aiov; 379 char *dirbuf; 380 int error, eofflag = 0; 381 382 if (dvp->v_type != VDIR) 383 return (ENOTDIR); 384 385 MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK); 386 387 auio.uio_iov = &aiov; 388 auio.uio_iovcnt = 1; 389 auio.uio_rw = UIO_READ; 390 auio.uio_segflg = UIO_SYSSPACE; 391 auio.uio_td = td; 392 auio.uio_offset = 0; 393 394 vargs.a_desc = NULL; 395 vargs.a_vp = dvp; 396 vargs.a_uio = &auio; 397 vargs.a_cred = td->td_ucred; 398 vargs.a_eofflag = &eofflag; 399 vargs.a_ncookies = NULL; 400 vargs.a_cookies = NULL; 401 402 while (!eofflag) { 403 auio.uio_resid = DIRBLKSIZ; 404 aiov.iov_base = dirbuf; 405 aiov.iov_len = DIRBLKSIZ; 406 error = ufs_readdir(&vargs); 407 if (error) { 408 printf("ufs_extattr_iterate_directory: ufs_readdir " 409 "%d\n", error); 410 return (error); 411 } 412 413 /* 414 * XXXRW: While in UFS, we always get DIRBLKSIZ returns from 415 * the directory code on success, on other file systems this 416 * may not be the case. For portability, we should check the 417 * read length on return from ufs_readdir(). 418 */ 419 edp = (struct dirent *)&dirbuf[DIRBLKSIZ]; 420 for (dp = (struct dirent *)dirbuf; dp < edp; ) { 421#if (BYTE_ORDER == LITTLE_ENDIAN) 422 dp->d_type = dp->d_namlen; 423 dp->d_namlen = 0; 424#else 425 dp->d_type = 0; 426#endif 427 if (dp->d_reclen == 0) 428 break; 429 error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, 430 dp->d_name, &attr_vp, td); 431 if (error) { 432 printf("ufs_extattr_iterate_directory: lookup " 433 "%s %d\n", dp->d_name, error); 434 } else if (attr_vp == dvp) { 435 vrele(attr_vp); 436 } else if (attr_vp->v_type != VREG) { 437 vput(attr_vp); 438 } else { 439 error = ufs_extattr_enable_with_open(ump, 440 attr_vp, attrnamespace, dp->d_name, td); 441 vrele(attr_vp); 442 if (error) { 443 printf("ufs_extattr_iterate_directory: " 444 "enable %s %d\n", dp->d_name, 445 error); 446 } else if (bootverbose) { 447 printf("UFS autostarted EA %s\n", 448 dp->d_name); 449 } 450 } 451 dp = (struct dirent *) ((char *)dp + dp->d_reclen); 452 if (dp >= edp) 453 break; 454 } 455 } 456 FREE(dirbuf, M_TEMP); 457 458 return (0); 459} 460 461/* 462 * Auto-start of extended attributes, to be executed (optionally) at 463 * mount-time. 464 */ 465int 466ufs_extattr_autostart(struct mount *mp, struct thread *td) 467{ 468 struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp; 469 int error; 470 471 /* 472 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root? 473 * If so, automatically start EA's. 474 */ 475 error = VFS_ROOT(mp, &rvp, td); 476 if (error) { 477 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", 478 error); 479 return (error); 480 } 481 482 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, 483 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td); 484 if (error) { 485 /* rvp ref'd but now unlocked */ 486 vrele(rvp); 487 return (error); 488 } 489 if (rvp == attr_dvp) { 490 /* Should never happen. */ 491 vrele(attr_dvp); 492 vput(rvp); 493 return (EINVAL); 494 } 495 vrele(rvp); 496 497 if (attr_dvp->v_type != VDIR) { 498 printf("ufs_extattr_autostart: %s != VDIR\n", 499 UFS_EXTATTR_FSROOTSUBDIR); 500 goto return_vput_attr_dvp; 501 } 502 503 error = ufs_extattr_start(mp, td); 504 if (error) { 505 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", 506 error); 507 goto return_vput_attr_dvp; 508 } 509 510 /* 511 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, 512 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, 513 * and start with appropriate type. Failures in either don't 514 * result in an over-all failure. attr_dvp is left locked to 515 * be cleaned up on exit. 516 */ 517 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 518 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td); 519 if (!error) { 520 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 521 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td); 522 if (error) 523 printf("ufs_extattr_iterate_directory returned %d\n", 524 error); 525 vput(attr_system_dvp); 526 } 527 528 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 529 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td); 530 if (!error) { 531 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 532 attr_user_dvp, EXTATTR_NAMESPACE_USER, td); 533 if (error) 534 printf("ufs_extattr_iterate_directory returned %d\n", 535 error); 536 vput(attr_user_dvp); 537 } 538 539 /* Mask startup failures in sub-directories. */ 540 error = 0; 541 542return_vput_attr_dvp: 543 vput(attr_dvp); 544 545 return (error); 546} 547#endif /* !UFS_EXTATTR_AUTOSTART */ 548 549/* 550 * Stop extended attribute support on an FS. 551 */ 552int 553ufs_extattr_stop(struct mount *mp, struct thread *td) 554{ 555 struct ufs_extattr_list_entry *uele; 556 struct ufsmount *ump = VFSTOUFS(mp); 557 int error = 0; 558 559 ufs_extattr_uepm_lock(ump, td); 560 561 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 562 error = EOPNOTSUPP; 563 goto unlock; 564 } 565 566 while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) { 567 uele = LIST_FIRST(&ump->um_extattr.uepm_list); 568 ufs_extattr_disable(ump, uele->uele_attrnamespace, 569 uele->uele_attrname, td); 570 } 571 572 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 573 574 crfree(ump->um_extattr.uepm_ucred); 575 ump->um_extattr.uepm_ucred = NULL; 576 577unlock: 578 ufs_extattr_uepm_unlock(ump, td); 579 580 return (error); 581} 582 583/* 584 * Enable a named attribute on the specified filesystem; provide an 585 * unlocked backing vnode to hold the attribute data. 586 */ 587static int 588ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 589 const char *attrname, struct vnode *backing_vnode, struct thread *td) 590{ 591 struct ufs_extattr_list_entry *attribute; 592 struct iovec aiov; 593 struct uio auio; 594 int error = 0; 595 596 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 597 return (EINVAL); 598 if (backing_vnode->v_type != VREG) 599 return (EINVAL); 600 601 MALLOC(attribute, struct ufs_extattr_list_entry *, 602 sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); 603 if (attribute == NULL) 604 return (ENOMEM); 605 606 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 607 error = EOPNOTSUPP; 608 goto free_exit; 609 } 610 611 if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) { 612 error = EEXIST; 613 goto free_exit; 614 } 615 616 strncpy(attribute->uele_attrname, attrname, 617 UFS_EXTATTR_MAXEXTATTRNAME); 618 attribute->uele_attrnamespace = attrnamespace; 619 bzero(&attribute->uele_fileheader, 620 sizeof(struct ufs_extattr_fileheader)); 621 622 attribute->uele_backing_vnode = backing_vnode; 623 624 auio.uio_iov = &aiov; 625 auio.uio_iovcnt = 1; 626 aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 627 aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 628 auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 629 auio.uio_offset = (off_t) 0; 630 auio.uio_segflg = UIO_SYSSPACE; 631 auio.uio_rw = UIO_READ; 632 auio.uio_td = td; 633 634 VOP_LEASE(backing_vnode, td, td->td_ucred, LEASE_WRITE); 635 vn_lock(backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, td); 636 error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, 637 ump->um_extattr.uepm_ucred); 638 639 if (error) 640 goto unlock_free_exit; 641 642 if (auio.uio_resid != 0) { 643 printf("ufs_extattr_enable: malformed attribute header\n"); 644 error = EINVAL; 645 goto unlock_free_exit; 646 } 647 648 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { 649 printf("ufs_extattr_enable: invalid attribute header magic\n"); 650 error = EINVAL; 651 goto unlock_free_exit; 652 } 653 654 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { 655 printf("ufs_extattr_enable: incorrect attribute header " 656 "version\n"); 657 error = EINVAL; 658 goto unlock_free_exit; 659 } 660 661 ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable"); 662 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, 663 uele_entries); 664 665 VOP_UNLOCK(backing_vnode, 0, td); 666 return (0); 667 668unlock_free_exit: 669 VOP_UNLOCK(backing_vnode, 0, td); 670 671free_exit: 672 FREE(attribute, M_UFS_EXTATTR); 673 return (error); 674} 675 676/* 677 * Disable extended attribute support on an FS. 678 */ 679static int 680ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 681 const char *attrname, struct thread *td) 682{ 683 struct ufs_extattr_list_entry *uele; 684 int error = 0; 685 686 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 687 return (EINVAL); 688 689 uele = ufs_extattr_find_attr(ump, attrnamespace, attrname); 690 if (!uele) 691 return (ENOATTR); 692 693 LIST_REMOVE(uele, uele_entries); 694 695 vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_NOPAUSE | LK_RETRY, 696 td); 697 ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable"); 698 VOP_UNLOCK(uele->uele_backing_vnode, 0, td); 699 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, 700 td->td_ucred, td); 701 702 FREE(uele, M_UFS_EXTATTR); 703 704 return (error); 705} 706 707/* 708 * VFS call to manage extended attributes in UFS. If filename_vp is 709 * non-NULL, it must be passed in locked, and regardless of errors in 710 * processing, will be unlocked. 711 */ 712int 713ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 714 int attrnamespace, const char *attrname, struct thread *td) 715{ 716 struct ufsmount *ump = VFSTOUFS(mp); 717 int error; 718 719 /* 720 * Processes with privilege, but in jail, are not allowed to 721 * configure extended attributes. 722 */ 723 if ((error = suser(td))) { 724 if (filename_vp != NULL) 725 VOP_UNLOCK(filename_vp, 0, td); 726 return (error); 727 } 728 729 switch(cmd) { 730 case UFS_EXTATTR_CMD_START: 731 if (filename_vp != NULL) { 732 VOP_UNLOCK(filename_vp, 0, td); 733 return (EINVAL); 734 } 735 if (attrname != NULL) 736 return (EINVAL); 737 738 error = ufs_extattr_start(mp, td); 739 740 return (error); 741 742 case UFS_EXTATTR_CMD_STOP: 743 if (filename_vp != NULL) { 744 VOP_UNLOCK(filename_vp, 0, td); 745 return (EINVAL); 746 } 747 if (attrname != NULL) 748 return (EINVAL); 749 750 error = ufs_extattr_stop(mp, td); 751 752 return (error); 753 754 case UFS_EXTATTR_CMD_ENABLE: 755 756 if (filename_vp == NULL) 757 return (EINVAL); 758 if (attrname == NULL) { 759 VOP_UNLOCK(filename_vp, 0, td); 760 return (EINVAL); 761 } 762 763 /* 764 * ufs_extattr_enable_with_open() will always unlock the 765 * vnode, regardless of failure. 766 */ 767 ufs_extattr_uepm_lock(ump, td); 768 error = ufs_extattr_enable_with_open(ump, filename_vp, 769 attrnamespace, attrname, td); 770 ufs_extattr_uepm_unlock(ump, td); 771 772 return (error); 773 774 case UFS_EXTATTR_CMD_DISABLE: 775 776 if (filename_vp != NULL) { 777 VOP_UNLOCK(filename_vp, 0, td); 778 return (EINVAL); 779 } 780 if (attrname == NULL) 781 return (EINVAL); 782 783 ufs_extattr_uepm_lock(ump, td); 784 error = ufs_extattr_disable(ump, attrnamespace, attrname, 785 td); 786 ufs_extattr_uepm_unlock(ump, td); 787 788 return (error); 789 790 default: 791 return (EINVAL); 792 } 793} 794 795/* 796 * Vnode operating to retrieve a named extended attribute. 797 */ 798int 799ufs_getextattr(struct vop_getextattr_args *ap) 800/* 801vop_getextattr { 802 IN struct vnode *a_vp; 803 IN int a_attrnamespace; 804 IN const char *a_name; 805 INOUT struct uio *a_uio; 806 OUT size_t *a_size; 807 IN struct ucred *a_cred; 808 IN struct thread *a_td; 809}; 810*/ 811{ 812 struct mount *mp = ap->a_vp->v_mount; 813 struct ufsmount *ump = VFSTOUFS(mp); 814 int error; 815 816 ufs_extattr_uepm_lock(ump, ap->a_td); 817 818 error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name, 819 ap->a_uio, ap->a_size, ap->a_cred, ap->a_td); 820 821 ufs_extattr_uepm_unlock(ump, ap->a_td); 822 823 return (error); 824} 825 826/* 827 * Real work associated with retrieving a named attribute--assumes that 828 * the attribute lock has already been grabbed. 829 */ 830static int 831ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, 832 struct uio *uio, size_t *size, struct ucred *cred, struct thread *td) 833{ 834 struct ufs_extattr_list_entry *attribute; 835 struct ufs_extattr_header ueh; 836 struct iovec local_aiov; 837 struct uio local_aio; 838 struct mount *mp = vp->v_mount; 839 struct ufsmount *ump = VFSTOUFS(mp); 840 struct inode *ip = VTOI(vp); 841 off_t base_offset; 842 size_t len, old_len; 843 int error = 0; 844 845 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 846 return (EOPNOTSUPP); 847 848 if (strlen(name) == 0) 849 return (EINVAL); 850 851 error = extattr_check_cred(vp, attrnamespace, cred, td, IREAD); 852 if (error) 853 return (error); 854 855 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 856 if (!attribute) 857 return (ENOATTR); 858 859 /* 860 * Allow only offsets of zero to encourage the read/replace 861 * extended attribute semantic. Otherwise we can't guarantee 862 * atomicity, as we don't provide locks for extended attributes. 863 */ 864 if (uio != NULL && uio->uio_offset != 0) 865 return (ENXIO); 866 867 /* 868 * Find base offset of header in file based on file header size, and 869 * data header size + maximum data size, indexed by inode number. 870 */ 871 base_offset = sizeof(struct ufs_extattr_fileheader) + 872 ip->i_number * (sizeof(struct ufs_extattr_header) + 873 attribute->uele_fileheader.uef_size); 874 875 /* 876 * Read in the data header to see if the data is defined, and if so 877 * how much. 878 */ 879 bzero(&ueh, sizeof(struct ufs_extattr_header)); 880 local_aiov.iov_base = (caddr_t) &ueh; 881 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 882 local_aio.uio_iov = &local_aiov; 883 local_aio.uio_iovcnt = 1; 884 local_aio.uio_rw = UIO_READ; 885 local_aio.uio_segflg = UIO_SYSSPACE; 886 local_aio.uio_td = td; 887 local_aio.uio_offset = base_offset; 888 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 889 890 /* 891 * Acquire locks. 892 */ 893 VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_READ); 894 /* 895 * Don't need to get a lock on the backing file if the getattr is 896 * being applied to the backing file, as the lock is already held. 897 */ 898 if (attribute->uele_backing_vnode != vp) 899 vn_lock(attribute->uele_backing_vnode, LK_SHARED | 900 LK_NOPAUSE | LK_RETRY, td); 901 902 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 903 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 904 if (error) 905 goto vopunlock_exit; 906 907 /* Defined? */ 908 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 909 error = ENOATTR; 910 goto vopunlock_exit; 911 } 912 913 /* Valid for the current inode generation? */ 914 if (ueh.ueh_i_gen != ip->i_gen) { 915 /* 916 * The inode itself has a different generation number 917 * than the attribute data. For now, the best solution 918 * is to coerce this to undefined, and let it get cleaned 919 * up by the next write or extattrctl clean. 920 */ 921 printf("ufs_extattr_get (%s): inode number inconsistency (%d, %jd)\n", 922 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 923 error = ENOATTR; 924 goto vopunlock_exit; 925 } 926 927 /* Local size consistency check. */ 928 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 929 error = ENXIO; 930 goto vopunlock_exit; 931 } 932 933 /* Return full data size if caller requested it. */ 934 if (size != NULL) 935 *size = ueh.ueh_len; 936 937 /* Return data if the caller requested it. */ 938 if (uio != NULL) { 939 /* Allow for offset into the attribute data. */ 940 uio->uio_offset = base_offset + sizeof(struct 941 ufs_extattr_header); 942 943 /* 944 * Figure out maximum to transfer -- use buffer size and 945 * local data limit. 946 */ 947 len = MIN(uio->uio_resid, ueh.ueh_len); 948 old_len = uio->uio_resid; 949 uio->uio_resid = len; 950 951 error = VOP_READ(attribute->uele_backing_vnode, uio, 952 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 953 if (error) 954 goto vopunlock_exit; 955 956 uio->uio_resid = old_len - (len - uio->uio_resid); 957 } 958 959vopunlock_exit: 960 961 if (uio != NULL) 962 uio->uio_offset = 0; 963 964 if (attribute->uele_backing_vnode != vp) 965 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 966 967 return (error); 968} 969 970/* 971 * Vnode operation to remove a named attribute. 972 */ 973int 974ufs_deleteextattr(struct vop_deleteextattr_args *ap) 975/* 976vop_deleteextattr { 977 IN struct vnode *a_vp; 978 IN int a_attrnamespace; 979 IN const char *a_name; 980 IN struct ucred *a_cred; 981 IN struct thread *a_td; 982}; 983*/ 984{ 985 struct mount *mp = ap->a_vp->v_mount; 986 struct ufsmount *ump = VFSTOUFS(mp); 987 int error; 988 989 ufs_extattr_uepm_lock(ump, ap->a_td); 990 991 error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name, 992 ap->a_cred, ap->a_td); 993 994 995 ufs_extattr_uepm_unlock(ump, ap->a_td); 996 997 return (error); 998} 999 1000/* 1001 * Vnode operation to set a named attribute. 1002 */ 1003int 1004ufs_setextattr(struct vop_setextattr_args *ap) 1005/* 1006vop_setextattr { 1007 IN struct vnode *a_vp; 1008 IN int a_attrnamespace; 1009 IN const char *a_name; 1010 INOUT struct uio *a_uio; 1011 IN struct ucred *a_cred; 1012 IN struct thread *a_td; 1013}; 1014*/ 1015{ 1016 struct mount *mp = ap->a_vp->v_mount; 1017 struct ufsmount *ump = VFSTOUFS(mp); 1018 int error; 1019 1020 ufs_extattr_uepm_lock(ump, ap->a_td); 1021 1022 /* 1023 * XXX: No longer a supported way to delete extended attributes. 1024 */ 1025 if (ap->a_uio == NULL) 1026 return (EINVAL); 1027 1028 error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name, 1029 ap->a_uio, ap->a_cred, ap->a_td); 1030 1031 ufs_extattr_uepm_unlock(ump, ap->a_td); 1032 1033 return (error); 1034} 1035 1036/* 1037 * Real work associated with setting a vnode's extended attributes; 1038 * assumes that the attribute lock has already been grabbed. 1039 */ 1040static int 1041ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, 1042 struct uio *uio, struct ucred *cred, struct thread *td) 1043{ 1044 struct ufs_extattr_list_entry *attribute; 1045 struct ufs_extattr_header ueh; 1046 struct iovec local_aiov; 1047 struct uio local_aio; 1048 struct mount *mp = vp->v_mount; 1049 struct ufsmount *ump = VFSTOUFS(mp); 1050 struct inode *ip = VTOI(vp); 1051 off_t base_offset; 1052 int error = 0, ioflag; 1053 1054 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1055 return (EROFS); 1056 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1057 return (EOPNOTSUPP); 1058 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1059 return (EINVAL); 1060 1061 error = extattr_check_cred(vp, attrnamespace, cred, td, IWRITE); 1062 if (error) 1063 return (error); 1064 1065 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1066 if (!attribute) 1067 return (ENOATTR); 1068 1069 /* 1070 * Early rejection of invalid offsets/length. 1071 * Reject: any offset but 0 (replace) 1072 * Any size greater than attribute size limit 1073 */ 1074 if (uio->uio_offset != 0 || 1075 uio->uio_resid > attribute->uele_fileheader.uef_size) 1076 return (ENXIO); 1077 1078 /* 1079 * Find base offset of header in file based on file header size, and 1080 * data header size + maximum data size, indexed by inode number. 1081 */ 1082 base_offset = sizeof(struct ufs_extattr_fileheader) + 1083 ip->i_number * (sizeof(struct ufs_extattr_header) + 1084 attribute->uele_fileheader.uef_size); 1085 1086 /* 1087 * Write out a data header for the data. 1088 */ 1089 ueh.ueh_len = uio->uio_resid; 1090 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 1091 ueh.ueh_i_gen = ip->i_gen; 1092 local_aiov.iov_base = (caddr_t) &ueh; 1093 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1094 local_aio.uio_iov = &local_aiov; 1095 local_aio.uio_iovcnt = 1; 1096 local_aio.uio_rw = UIO_WRITE; 1097 local_aio.uio_segflg = UIO_SYSSPACE; 1098 local_aio.uio_td = td; 1099 local_aio.uio_offset = base_offset; 1100 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1101 1102 /* 1103 * Acquire locks. 1104 */ 1105 VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE); 1106 1107 /* 1108 * Don't need to get a lock on the backing file if the setattr is 1109 * being applied to the backing file, as the lock is already held. 1110 */ 1111 if (attribute->uele_backing_vnode != vp) 1112 vn_lock(attribute->uele_backing_vnode, 1113 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td); 1114 1115 ioflag = IO_NODELOCKED; 1116 if (ufs_extattr_sync) 1117 ioflag |= IO_SYNC; 1118 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1119 ump->um_extattr.uepm_ucred); 1120 if (error) 1121 goto vopunlock_exit; 1122 1123 if (local_aio.uio_resid != 0) { 1124 error = ENXIO; 1125 goto vopunlock_exit; 1126 } 1127 1128 /* 1129 * Write out user data. 1130 */ 1131 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 1132 1133 ioflag = IO_NODELOCKED; 1134 if (ufs_extattr_sync) 1135 ioflag |= IO_SYNC; 1136 error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, 1137 ump->um_extattr.uepm_ucred); 1138 1139vopunlock_exit: 1140 uio->uio_offset = 0; 1141 1142 if (attribute->uele_backing_vnode != vp) 1143 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1144 1145 return (error); 1146} 1147 1148/* 1149 * Real work associated with removing an extended attribute from a vnode. 1150 * Assumes the attribute lock has already been grabbed. 1151 */ 1152static int 1153ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, 1154 struct ucred *cred, struct thread *td) 1155{ 1156 struct ufs_extattr_list_entry *attribute; 1157 struct ufs_extattr_header ueh; 1158 struct iovec local_aiov; 1159 struct uio local_aio; 1160 struct mount *mp = vp->v_mount; 1161 struct ufsmount *ump = VFSTOUFS(mp); 1162 struct inode *ip = VTOI(vp); 1163 off_t base_offset; 1164 int error = 0, ioflag; 1165 1166 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1167 return (EROFS); 1168 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1169 return (EOPNOTSUPP); 1170 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1171 return (EINVAL); 1172 1173 error = extattr_check_cred(vp, attrnamespace, cred, td, IWRITE); 1174 if (error) 1175 return (error); 1176 1177 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1178 if (!attribute) 1179 return (ENOATTR); 1180 1181 /* 1182 * Find base offset of header in file based on file header size, and 1183 * data header size + maximum data size, indexed by inode number. 1184 */ 1185 base_offset = sizeof(struct ufs_extattr_fileheader) + 1186 ip->i_number * (sizeof(struct ufs_extattr_header) + 1187 attribute->uele_fileheader.uef_size); 1188 1189 /* 1190 * Check to see if currently defined. 1191 */ 1192 bzero(&ueh, sizeof(struct ufs_extattr_header)); 1193 1194 local_aiov.iov_base = (caddr_t) &ueh; 1195 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1196 local_aio.uio_iov = &local_aiov; 1197 local_aio.uio_iovcnt = 1; 1198 local_aio.uio_rw = UIO_READ; 1199 local_aio.uio_segflg = UIO_SYSSPACE; 1200 local_aio.uio_td = td; 1201 local_aio.uio_offset = base_offset; 1202 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1203 1204 VOP_LEASE(attribute->uele_backing_vnode, td, cred, LEASE_WRITE); 1205 1206 /* 1207 * Don't need to get the lock on the backing vnode if the vnode we're 1208 * modifying is it, as we already hold the lock. 1209 */ 1210 if (attribute->uele_backing_vnode != vp) 1211 vn_lock(attribute->uele_backing_vnode, 1212 LK_EXCLUSIVE | LK_NOPAUSE | LK_RETRY, td); 1213 1214 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 1215 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 1216 if (error) 1217 goto vopunlock_exit; 1218 1219 /* Defined? */ 1220 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 1221 error = ENOATTR; 1222 goto vopunlock_exit; 1223 } 1224 1225 /* Valid for the current inode generation? */ 1226 if (ueh.ueh_i_gen != ip->i_gen) { 1227 /* 1228 * The inode itself has a different generation number than 1229 * the attribute data. For now, the best solution is to 1230 * coerce this to undefined, and let it get cleaned up by 1231 * the next write or extattrctl clean. 1232 */ 1233 printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n", 1234 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 1235 error = ENOATTR; 1236 goto vopunlock_exit; 1237 } 1238 1239 /* Flag it as not in use. */ 1240 ueh.ueh_flags = 0; 1241 ueh.ueh_len = 0; 1242 1243 local_aiov.iov_base = (caddr_t) &ueh; 1244 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1245 local_aio.uio_iov = &local_aiov; 1246 local_aio.uio_iovcnt = 1; 1247 local_aio.uio_rw = UIO_WRITE; 1248 local_aio.uio_segflg = UIO_SYSSPACE; 1249 local_aio.uio_td = td; 1250 local_aio.uio_offset = base_offset; 1251 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1252 1253 ioflag = IO_NODELOCKED; 1254 if (ufs_extattr_sync) 1255 ioflag |= IO_SYNC; 1256 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1257 ump->um_extattr.uepm_ucred); 1258 if (error) 1259 goto vopunlock_exit; 1260 1261 if (local_aio.uio_resid != 0) 1262 error = ENXIO; 1263 1264vopunlock_exit: 1265 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1266 1267 return (error); 1268} 1269 1270/* 1271 * Called by UFS when an inode is no longer active and should have its 1272 * attributes stripped. 1273 */ 1274void 1275ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td) 1276{ 1277 struct ufs_extattr_list_entry *uele; 1278 struct mount *mp = vp->v_mount; 1279 struct ufsmount *ump = VFSTOUFS(mp); 1280 1281 /* 1282 * In that case, we cannot lock. We should not have any active vnodes 1283 * on the fs if this is not yet initialized but is going to be, so 1284 * this can go unlocked. 1285 */ 1286 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 1287 return; 1288 1289 ufs_extattr_uepm_lock(ump, td); 1290 1291 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 1292 ufs_extattr_uepm_unlock(ump, td); 1293 return; 1294 } 1295 1296 LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) 1297 ufs_extattr_rm(vp, uele->uele_attrnamespace, 1298 uele->uele_attrname, NULL, td); 1299 1300 ufs_extattr_uepm_unlock(ump, td); 1301} 1302 1303#endif /* !UFS_EXTATTR */ 1304