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