ufs_extattr.c revision 144367
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 144367 2005-03-31 04:37:09Z jeff $"); 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_gen.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 (lockparent == UE_GETDIR_LOCKPARENT_DONT) 281 VOP_UNLOCK(start_dvp, 0, td); 282 283 /* 284 * Check that ufs_lookup() didn't release the lock when we 285 * didn't want it to. 286 */ 287 if (lockparent == UE_GETDIR_LOCKPARENT) 288 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 289 290 return (error); 291 } 292/* 293 if (target_vp == start_dvp) 294 panic("ufs_extattr_lookup: target_vp == start_dvp"); 295*/ 296 297 if (target_vp != start_dvp && lockparent == UE_GETDIR_LOCKPARENT_DONT) 298 VOP_UNLOCK(start_dvp, 0, td); 299 300 if (lockparent == UE_GETDIR_LOCKPARENT) 301 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 302 303 /* printf("ufs_extattr_lookup: success\n"); */ 304 *vp = target_vp; 305 return (0); 306} 307#endif /* !UFS_EXTATTR_AUTOSTART */ 308 309/* 310 * Enable an EA using the passed filesystem, backing vnode, attribute name, 311 * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp 312 * to be locked when passed in. The vnode will be returned unlocked, 313 * regardless of success/failure of the function. As a result, the caller 314 * will always need to vrele(), but not vput(). 315 */ 316static int 317ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, 318 int attrnamespace, const char *attrname, struct thread *td) 319{ 320 int error; 321 322 error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, -1); 323 if (error) { 324 printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " 325 "with %d\n", error); 326 VOP_UNLOCK(vp, 0, td); 327 return (error); 328 } 329 330 vp->v_writecount++; 331 332 vref(vp); 333 334 VOP_UNLOCK(vp, 0, td); 335 336 error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td); 337 if (error != 0) 338 vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 339 return (error); 340} 341 342#ifdef UFS_EXTATTR_AUTOSTART 343/* 344 * Given a locked directory vnode, iterate over the names in the directory 345 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential 346 * attribute files. Then invoke ufs_extattr_enable_with_open() on each 347 * to attempt to start the attribute. Leaves the directory locked on 348 * exit. 349 */ 350static int 351ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, 352 int attrnamespace, struct thread *td) 353{ 354 struct vop_readdir_args vargs; 355 struct dirent *dp, *edp; 356 struct vnode *attr_vp; 357 struct uio auio; 358 struct iovec aiov; 359 char *dirbuf; 360 int error, eofflag = 0; 361 362 if (dvp->v_type != VDIR) 363 return (ENOTDIR); 364 365 MALLOC(dirbuf, char *, DIRBLKSIZ, M_TEMP, M_WAITOK); 366 367 auio.uio_iov = &aiov; 368 auio.uio_iovcnt = 1; 369 auio.uio_rw = UIO_READ; 370 auio.uio_segflg = UIO_SYSSPACE; 371 auio.uio_td = td; 372 auio.uio_offset = 0; 373 374 vargs.a_gen.a_desc = NULL; 375 vargs.a_vp = dvp; 376 vargs.a_uio = &auio; 377 vargs.a_cred = td->td_ucred; 378 vargs.a_eofflag = &eofflag; 379 vargs.a_ncookies = NULL; 380 vargs.a_cookies = NULL; 381 382 while (!eofflag) { 383 auio.uio_resid = DIRBLKSIZ; 384 aiov.iov_base = dirbuf; 385 aiov.iov_len = DIRBLKSIZ; 386 error = ufs_readdir(&vargs); 387 if (error) { 388 printf("ufs_extattr_iterate_directory: ufs_readdir " 389 "%d\n", error); 390 return (error); 391 } 392 393 /* 394 * XXXRW: While in UFS, we always get DIRBLKSIZ returns from 395 * the directory code on success, on other file systems this 396 * may not be the case. For portability, we should check the 397 * read length on return from ufs_readdir(). 398 */ 399 edp = (struct dirent *)&dirbuf[DIRBLKSIZ]; 400 for (dp = (struct dirent *)dirbuf; dp < edp; ) { 401#if (BYTE_ORDER == LITTLE_ENDIAN) 402 dp->d_type = dp->d_namlen; 403 dp->d_namlen = 0; 404#else 405 dp->d_type = 0; 406#endif 407 if (dp->d_reclen == 0) 408 break; 409 error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, 410 dp->d_name, &attr_vp, td); 411 if (error) { 412 printf("ufs_extattr_iterate_directory: lookup " 413 "%s %d\n", dp->d_name, error); 414 } else if (attr_vp == dvp) { 415 vrele(attr_vp); 416 } else if (attr_vp->v_type != VREG) { 417 vput(attr_vp); 418 } else { 419 error = ufs_extattr_enable_with_open(ump, 420 attr_vp, attrnamespace, dp->d_name, td); 421 vrele(attr_vp); 422 if (error) { 423 printf("ufs_extattr_iterate_directory: " 424 "enable %s %d\n", dp->d_name, 425 error); 426 } else if (bootverbose) { 427 printf("UFS autostarted EA %s\n", 428 dp->d_name); 429 } 430 } 431 dp = (struct dirent *) ((char *)dp + dp->d_reclen); 432 if (dp >= edp) 433 break; 434 } 435 } 436 FREE(dirbuf, M_TEMP); 437 438 return (0); 439} 440 441/* 442 * Auto-start of extended attributes, to be executed (optionally) at 443 * mount-time. 444 */ 445int 446ufs_extattr_autostart(struct mount *mp, struct thread *td) 447{ 448 struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp; 449 int error; 450 451 /* 452 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root? 453 * If so, automatically start EA's. 454 */ 455 error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp, td); 456 if (error) { 457 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", 458 error); 459 return (error); 460 } 461 462 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, 463 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td); 464 if (error) { 465 /* rvp ref'd but now unlocked */ 466 vrele(rvp); 467 return (error); 468 } 469 if (rvp == attr_dvp) { 470 /* Should never happen. */ 471 vrele(attr_dvp); 472 vput(rvp); 473 return (EINVAL); 474 } 475 vrele(rvp); 476 477 if (attr_dvp->v_type != VDIR) { 478 printf("ufs_extattr_autostart: %s != VDIR\n", 479 UFS_EXTATTR_FSROOTSUBDIR); 480 goto return_vput_attr_dvp; 481 } 482 483 error = ufs_extattr_start(mp, td); 484 if (error) { 485 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", 486 error); 487 goto return_vput_attr_dvp; 488 } 489 490 /* 491 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, 492 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, 493 * and start with appropriate type. Failures in either don't 494 * result in an over-all failure. attr_dvp is left locked to 495 * be cleaned up on exit. 496 */ 497 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 498 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td); 499 if (!error) { 500 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 501 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td); 502 if (error) 503 printf("ufs_extattr_iterate_directory returned %d\n", 504 error); 505 vput(attr_system_dvp); 506 } 507 508 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 509 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td); 510 if (!error) { 511 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 512 attr_user_dvp, EXTATTR_NAMESPACE_USER, td); 513 if (error) 514 printf("ufs_extattr_iterate_directory returned %d\n", 515 error); 516 vput(attr_user_dvp); 517 } 518 519 /* Mask startup failures in sub-directories. */ 520 error = 0; 521 522return_vput_attr_dvp: 523 vput(attr_dvp); 524 525 return (error); 526} 527#endif /* !UFS_EXTATTR_AUTOSTART */ 528 529/* 530 * Stop extended attribute support on an FS. 531 */ 532int 533ufs_extattr_stop(struct mount *mp, struct thread *td) 534{ 535 struct ufs_extattr_list_entry *uele; 536 struct ufsmount *ump = VFSTOUFS(mp); 537 int error = 0; 538 539 ufs_extattr_uepm_lock(ump, td); 540 541 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 542 error = EOPNOTSUPP; 543 goto unlock; 544 } 545 546 while (LIST_FIRST(&ump->um_extattr.uepm_list) != NULL) { 547 uele = LIST_FIRST(&ump->um_extattr.uepm_list); 548 ufs_extattr_disable(ump, uele->uele_attrnamespace, 549 uele->uele_attrname, td); 550 } 551 552 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 553 554 crfree(ump->um_extattr.uepm_ucred); 555 ump->um_extattr.uepm_ucred = NULL; 556 557unlock: 558 ufs_extattr_uepm_unlock(ump, td); 559 560 return (error); 561} 562 563/* 564 * Enable a named attribute on the specified filesystem; provide an 565 * unlocked backing vnode to hold the attribute data. 566 */ 567static int 568ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 569 const char *attrname, struct vnode *backing_vnode, struct thread *td) 570{ 571 struct ufs_extattr_list_entry *attribute; 572 struct iovec aiov; 573 struct uio auio; 574 int error = 0; 575 576 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 577 return (EINVAL); 578 if (backing_vnode->v_type != VREG) 579 return (EINVAL); 580 581 MALLOC(attribute, struct ufs_extattr_list_entry *, 582 sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); 583 if (attribute == NULL) 584 return (ENOMEM); 585 586 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 587 error = EOPNOTSUPP; 588 goto free_exit; 589 } 590 591 if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) { 592 error = EEXIST; 593 goto free_exit; 594 } 595 596 strncpy(attribute->uele_attrname, attrname, 597 UFS_EXTATTR_MAXEXTATTRNAME); 598 attribute->uele_attrnamespace = attrnamespace; 599 bzero(&attribute->uele_fileheader, 600 sizeof(struct ufs_extattr_fileheader)); 601 602 attribute->uele_backing_vnode = backing_vnode; 603 604 auio.uio_iov = &aiov; 605 auio.uio_iovcnt = 1; 606 aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 607 aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 608 auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 609 auio.uio_offset = (off_t) 0; 610 auio.uio_segflg = UIO_SYSSPACE; 611 auio.uio_rw = UIO_READ; 612 auio.uio_td = td; 613 614 vn_lock(backing_vnode, LK_SHARED | LK_RETRY, td); 615 error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, 616 ump->um_extattr.uepm_ucred); 617 618 if (error) 619 goto unlock_free_exit; 620 621 if (auio.uio_resid != 0) { 622 printf("ufs_extattr_enable: malformed attribute header\n"); 623 error = EINVAL; 624 goto unlock_free_exit; 625 } 626 627 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { 628 printf("ufs_extattr_enable: invalid attribute header magic\n"); 629 error = EINVAL; 630 goto unlock_free_exit; 631 } 632 633 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { 634 printf("ufs_extattr_enable: incorrect attribute header " 635 "version\n"); 636 error = EINVAL; 637 goto unlock_free_exit; 638 } 639 640 ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable"); 641 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, 642 uele_entries); 643 644 VOP_UNLOCK(backing_vnode, 0, td); 645 return (0); 646 647unlock_free_exit: 648 VOP_UNLOCK(backing_vnode, 0, td); 649 650free_exit: 651 FREE(attribute, M_UFS_EXTATTR); 652 return (error); 653} 654 655/* 656 * Disable extended attribute support on an FS. 657 */ 658static int 659ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 660 const char *attrname, struct thread *td) 661{ 662 struct ufs_extattr_list_entry *uele; 663 int error = 0; 664 665 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 666 return (EINVAL); 667 668 uele = ufs_extattr_find_attr(ump, attrnamespace, attrname); 669 if (!uele) 670 return (ENOATTR); 671 672 LIST_REMOVE(uele, uele_entries); 673 674 vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY, 675 td); 676 ASSERT_VOP_LOCKED(uele->uele_backing_vnode, "ufs_extattr_disable"); 677 VOP_UNLOCK(uele->uele_backing_vnode, 0, td); 678 error = vn_close(uele->uele_backing_vnode, FREAD|FWRITE, 679 td->td_ucred, td); 680 681 FREE(uele, M_UFS_EXTATTR); 682 683 return (error); 684} 685 686/* 687 * VFS call to manage extended attributes in UFS. If filename_vp is 688 * non-NULL, it must be passed in locked, and regardless of errors in 689 * processing, will be unlocked. 690 */ 691int 692ufs_extattrctl(struct mount *mp, int cmd, struct vnode *filename_vp, 693 int attrnamespace, const char *attrname, struct thread *td) 694{ 695 struct ufsmount *ump = VFSTOUFS(mp); 696 int error; 697 698 /* 699 * Processes with privilege, but in jail, are not allowed to 700 * configure extended attributes. 701 */ 702 if ((error = suser(td))) { 703 if (filename_vp != NULL) 704 VOP_UNLOCK(filename_vp, 0, td); 705 return (error); 706 } 707 708 switch(cmd) { 709 case UFS_EXTATTR_CMD_START: 710 if (filename_vp != NULL) { 711 VOP_UNLOCK(filename_vp, 0, td); 712 return (EINVAL); 713 } 714 if (attrname != NULL) 715 return (EINVAL); 716 717 error = ufs_extattr_start(mp, td); 718 719 return (error); 720 721 case UFS_EXTATTR_CMD_STOP: 722 if (filename_vp != NULL) { 723 VOP_UNLOCK(filename_vp, 0, td); 724 return (EINVAL); 725 } 726 if (attrname != NULL) 727 return (EINVAL); 728 729 error = ufs_extattr_stop(mp, td); 730 731 return (error); 732 733 case UFS_EXTATTR_CMD_ENABLE: 734 735 if (filename_vp == NULL) 736 return (EINVAL); 737 if (attrname == NULL) { 738 VOP_UNLOCK(filename_vp, 0, td); 739 return (EINVAL); 740 } 741 742 /* 743 * ufs_extattr_enable_with_open() will always unlock the 744 * vnode, regardless of failure. 745 */ 746 ufs_extattr_uepm_lock(ump, td); 747 error = ufs_extattr_enable_with_open(ump, filename_vp, 748 attrnamespace, attrname, td); 749 ufs_extattr_uepm_unlock(ump, td); 750 751 return (error); 752 753 case UFS_EXTATTR_CMD_DISABLE: 754 755 if (filename_vp != NULL) { 756 VOP_UNLOCK(filename_vp, 0, td); 757 return (EINVAL); 758 } 759 if (attrname == NULL) 760 return (EINVAL); 761 762 ufs_extattr_uepm_lock(ump, td); 763 error = ufs_extattr_disable(ump, attrnamespace, attrname, 764 td); 765 ufs_extattr_uepm_unlock(ump, td); 766 767 return (error); 768 769 default: 770 return (EINVAL); 771 } 772} 773 774/* 775 * Vnode operating to retrieve a named extended attribute. 776 */ 777int 778ufs_getextattr(struct vop_getextattr_args *ap) 779/* 780vop_getextattr { 781 IN struct vnode *a_vp; 782 IN int a_attrnamespace; 783 IN const char *a_name; 784 INOUT struct uio *a_uio; 785 OUT size_t *a_size; 786 IN struct ucred *a_cred; 787 IN struct thread *a_td; 788}; 789*/ 790{ 791 struct mount *mp = ap->a_vp->v_mount; 792 struct ufsmount *ump = VFSTOUFS(mp); 793 int error; 794 795 ufs_extattr_uepm_lock(ump, ap->a_td); 796 797 error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name, 798 ap->a_uio, ap->a_size, ap->a_cred, ap->a_td); 799 800 ufs_extattr_uepm_unlock(ump, ap->a_td); 801 802 return (error); 803} 804 805/* 806 * Real work associated with retrieving a named attribute--assumes that 807 * the attribute lock has already been grabbed. 808 */ 809static int 810ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, 811 struct uio *uio, size_t *size, struct ucred *cred, struct thread *td) 812{ 813 struct ufs_extattr_list_entry *attribute; 814 struct ufs_extattr_header ueh; 815 struct iovec local_aiov; 816 struct uio local_aio; 817 struct mount *mp = vp->v_mount; 818 struct ufsmount *ump = VFSTOUFS(mp); 819 struct inode *ip = VTOI(vp); 820 off_t base_offset; 821 size_t len, old_len; 822 int error = 0; 823 824 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 825 return (EOPNOTSUPP); 826 827 if (strlen(name) == 0) 828 return (EINVAL); 829 830 error = extattr_check_cred(vp, attrnamespace, cred, td, IREAD); 831 if (error) 832 return (error); 833 834 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 835 if (!attribute) 836 return (ENOATTR); 837 838 /* 839 * Allow only offsets of zero to encourage the read/replace 840 * extended attribute semantic. Otherwise we can't guarantee 841 * atomicity, as we don't provide locks for extended attributes. 842 */ 843 if (uio != NULL && uio->uio_offset != 0) 844 return (ENXIO); 845 846 /* 847 * Find base offset of header in file based on file header size, and 848 * data header size + maximum data size, indexed by inode number. 849 */ 850 base_offset = sizeof(struct ufs_extattr_fileheader) + 851 ip->i_number * (sizeof(struct ufs_extattr_header) + 852 attribute->uele_fileheader.uef_size); 853 854 /* 855 * Read in the data header to see if the data is defined, and if so 856 * how much. 857 */ 858 bzero(&ueh, sizeof(struct ufs_extattr_header)); 859 local_aiov.iov_base = (caddr_t) &ueh; 860 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 861 local_aio.uio_iov = &local_aiov; 862 local_aio.uio_iovcnt = 1; 863 local_aio.uio_rw = UIO_READ; 864 local_aio.uio_segflg = UIO_SYSSPACE; 865 local_aio.uio_td = td; 866 local_aio.uio_offset = base_offset; 867 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 868 869 /* 870 * Acquire locks. 871 * 872 * Don't need to get a lock on the backing file if the getattr is 873 * being applied to the backing file, as the lock is already held. 874 */ 875 if (attribute->uele_backing_vnode != vp) 876 vn_lock(attribute->uele_backing_vnode, LK_SHARED | 877 LK_RETRY, td); 878 879 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 880 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 881 if (error) 882 goto vopunlock_exit; 883 884 /* Defined? */ 885 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 886 error = ENOATTR; 887 goto vopunlock_exit; 888 } 889 890 /* Valid for the current inode generation? */ 891 if (ueh.ueh_i_gen != ip->i_gen) { 892 /* 893 * The inode itself has a different generation number 894 * than the attribute data. For now, the best solution 895 * is to coerce this to undefined, and let it get cleaned 896 * up by the next write or extattrctl clean. 897 */ 898 printf("ufs_extattr_get (%s): inode number inconsistency (%d, %jd)\n", 899 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 900 error = ENOATTR; 901 goto vopunlock_exit; 902 } 903 904 /* Local size consistency check. */ 905 if (ueh.ueh_len > attribute->uele_fileheader.uef_size) { 906 error = ENXIO; 907 goto vopunlock_exit; 908 } 909 910 /* Return full data size if caller requested it. */ 911 if (size != NULL) 912 *size = ueh.ueh_len; 913 914 /* Return data if the caller requested it. */ 915 if (uio != NULL) { 916 /* Allow for offset into the attribute data. */ 917 uio->uio_offset = base_offset + sizeof(struct 918 ufs_extattr_header); 919 920 /* 921 * Figure out maximum to transfer -- use buffer size and 922 * local data limit. 923 */ 924 len = MIN(uio->uio_resid, ueh.ueh_len); 925 old_len = uio->uio_resid; 926 uio->uio_resid = len; 927 928 error = VOP_READ(attribute->uele_backing_vnode, uio, 929 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 930 if (error) 931 goto vopunlock_exit; 932 933 uio->uio_resid = old_len - (len - uio->uio_resid); 934 } 935 936vopunlock_exit: 937 938 if (uio != NULL) 939 uio->uio_offset = 0; 940 941 if (attribute->uele_backing_vnode != vp) 942 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 943 944 return (error); 945} 946 947/* 948 * Vnode operation to remove a named attribute. 949 */ 950int 951ufs_deleteextattr(struct vop_deleteextattr_args *ap) 952/* 953vop_deleteextattr { 954 IN struct vnode *a_vp; 955 IN int a_attrnamespace; 956 IN const char *a_name; 957 IN struct ucred *a_cred; 958 IN struct thread *a_td; 959}; 960*/ 961{ 962 struct mount *mp = ap->a_vp->v_mount; 963 struct ufsmount *ump = VFSTOUFS(mp); 964 int error; 965 966 ufs_extattr_uepm_lock(ump, ap->a_td); 967 968 error = ufs_extattr_rm(ap->a_vp, ap->a_attrnamespace, ap->a_name, 969 ap->a_cred, ap->a_td); 970 971 972 ufs_extattr_uepm_unlock(ump, ap->a_td); 973 974 return (error); 975} 976 977/* 978 * Vnode operation to set a named attribute. 979 */ 980int 981ufs_setextattr(struct vop_setextattr_args *ap) 982/* 983vop_setextattr { 984 IN struct vnode *a_vp; 985 IN int a_attrnamespace; 986 IN const char *a_name; 987 INOUT struct uio *a_uio; 988 IN struct ucred *a_cred; 989 IN struct thread *a_td; 990}; 991*/ 992{ 993 struct mount *mp = ap->a_vp->v_mount; 994 struct ufsmount *ump = VFSTOUFS(mp); 995 int error; 996 997 ufs_extattr_uepm_lock(ump, ap->a_td); 998 999 /* 1000 * XXX: No longer a supported way to delete extended attributes. 1001 */ 1002 if (ap->a_uio == NULL) 1003 return (EINVAL); 1004 1005 error = ufs_extattr_set(ap->a_vp, ap->a_attrnamespace, ap->a_name, 1006 ap->a_uio, ap->a_cred, ap->a_td); 1007 1008 ufs_extattr_uepm_unlock(ump, ap->a_td); 1009 1010 return (error); 1011} 1012 1013/* 1014 * Real work associated with setting a vnode's extended attributes; 1015 * assumes that the attribute lock has already been grabbed. 1016 */ 1017static int 1018ufs_extattr_set(struct vnode *vp, int attrnamespace, const char *name, 1019 struct uio *uio, struct ucred *cred, struct thread *td) 1020{ 1021 struct ufs_extattr_list_entry *attribute; 1022 struct ufs_extattr_header ueh; 1023 struct iovec local_aiov; 1024 struct uio local_aio; 1025 struct mount *mp = vp->v_mount; 1026 struct ufsmount *ump = VFSTOUFS(mp); 1027 struct inode *ip = VTOI(vp); 1028 off_t base_offset; 1029 int error = 0, ioflag; 1030 1031 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1032 return (EROFS); 1033 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1034 return (EOPNOTSUPP); 1035 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1036 return (EINVAL); 1037 1038 error = extattr_check_cred(vp, attrnamespace, cred, td, IWRITE); 1039 if (error) 1040 return (error); 1041 1042 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1043 if (!attribute) 1044 return (ENOATTR); 1045 1046 /* 1047 * Early rejection of invalid offsets/length. 1048 * Reject: any offset but 0 (replace) 1049 * Any size greater than attribute size limit 1050 */ 1051 if (uio->uio_offset != 0 || 1052 uio->uio_resid > attribute->uele_fileheader.uef_size) 1053 return (ENXIO); 1054 1055 /* 1056 * Find base offset of header in file based on file header size, and 1057 * data header size + maximum data size, indexed by inode number. 1058 */ 1059 base_offset = sizeof(struct ufs_extattr_fileheader) + 1060 ip->i_number * (sizeof(struct ufs_extattr_header) + 1061 attribute->uele_fileheader.uef_size); 1062 1063 /* 1064 * Write out a data header for the data. 1065 */ 1066 ueh.ueh_len = uio->uio_resid; 1067 ueh.ueh_flags = UFS_EXTATTR_ATTR_FLAG_INUSE; 1068 ueh.ueh_i_gen = ip->i_gen; 1069 local_aiov.iov_base = (caddr_t) &ueh; 1070 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1071 local_aio.uio_iov = &local_aiov; 1072 local_aio.uio_iovcnt = 1; 1073 local_aio.uio_rw = UIO_WRITE; 1074 local_aio.uio_segflg = UIO_SYSSPACE; 1075 local_aio.uio_td = td; 1076 local_aio.uio_offset = base_offset; 1077 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1078 1079 /* 1080 * Acquire locks. 1081 * 1082 * Don't need to get a lock on the backing file if the setattr is 1083 * being applied to the backing file, as the lock is already held. 1084 */ 1085 if (attribute->uele_backing_vnode != vp) 1086 vn_lock(attribute->uele_backing_vnode, 1087 LK_EXCLUSIVE LK_RETRY, td); 1088 1089 ioflag = IO_NODELOCKED; 1090 if (ufs_extattr_sync) 1091 ioflag |= IO_SYNC; 1092 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1093 ump->um_extattr.uepm_ucred); 1094 if (error) 1095 goto vopunlock_exit; 1096 1097 if (local_aio.uio_resid != 0) { 1098 error = ENXIO; 1099 goto vopunlock_exit; 1100 } 1101 1102 /* 1103 * Write out user data. 1104 */ 1105 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 1106 1107 ioflag = IO_NODELOCKED; 1108 if (ufs_extattr_sync) 1109 ioflag |= IO_SYNC; 1110 error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, 1111 ump->um_extattr.uepm_ucred); 1112 1113vopunlock_exit: 1114 uio->uio_offset = 0; 1115 1116 if (attribute->uele_backing_vnode != vp) 1117 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1118 1119 return (error); 1120} 1121 1122/* 1123 * Real work associated with removing an extended attribute from a vnode. 1124 * Assumes the attribute lock has already been grabbed. 1125 */ 1126static int 1127ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, 1128 struct ucred *cred, struct thread *td) 1129{ 1130 struct ufs_extattr_list_entry *attribute; 1131 struct ufs_extattr_header ueh; 1132 struct iovec local_aiov; 1133 struct uio local_aio; 1134 struct mount *mp = vp->v_mount; 1135 struct ufsmount *ump = VFSTOUFS(mp); 1136 struct inode *ip = VTOI(vp); 1137 off_t base_offset; 1138 int error = 0, ioflag; 1139 1140 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1141 return (EROFS); 1142 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1143 return (EOPNOTSUPP); 1144 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1145 return (EINVAL); 1146 1147 error = extattr_check_cred(vp, attrnamespace, cred, td, IWRITE); 1148 if (error) 1149 return (error); 1150 1151 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1152 if (!attribute) 1153 return (ENOATTR); 1154 1155 /* 1156 * Find base offset of header in file based on file header size, and 1157 * data header size + maximum data size, indexed by inode number. 1158 */ 1159 base_offset = sizeof(struct ufs_extattr_fileheader) + 1160 ip->i_number * (sizeof(struct ufs_extattr_header) + 1161 attribute->uele_fileheader.uef_size); 1162 1163 /* 1164 * Check to see if currently defined. 1165 */ 1166 bzero(&ueh, sizeof(struct ufs_extattr_header)); 1167 1168 local_aiov.iov_base = (caddr_t) &ueh; 1169 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1170 local_aio.uio_iov = &local_aiov; 1171 local_aio.uio_iovcnt = 1; 1172 local_aio.uio_rw = UIO_READ; 1173 local_aio.uio_segflg = UIO_SYSSPACE; 1174 local_aio.uio_td = td; 1175 local_aio.uio_offset = base_offset; 1176 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1177 1178 /* 1179 * Don't need to get the lock on the backing vnode if the vnode we're 1180 * modifying is it, as we already hold the lock. 1181 */ 1182 if (attribute->uele_backing_vnode != vp) 1183 vn_lock(attribute->uele_backing_vnode, 1184 LK_EXCLUSIVE | LK_RETRY, td); 1185 1186 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 1187 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 1188 if (error) 1189 goto vopunlock_exit; 1190 1191 /* Defined? */ 1192 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 1193 error = ENOATTR; 1194 goto vopunlock_exit; 1195 } 1196 1197 /* Valid for the current inode generation? */ 1198 if (ueh.ueh_i_gen != ip->i_gen) { 1199 /* 1200 * The inode itself has a different generation number than 1201 * the attribute data. For now, the best solution is to 1202 * coerce this to undefined, and let it get cleaned up by 1203 * the next write or extattrctl clean. 1204 */ 1205 printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n", 1206 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 1207 error = ENOATTR; 1208 goto vopunlock_exit; 1209 } 1210 1211 /* Flag it as not in use. */ 1212 ueh.ueh_flags = 0; 1213 ueh.ueh_len = 0; 1214 1215 local_aiov.iov_base = (caddr_t) &ueh; 1216 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1217 local_aio.uio_iov = &local_aiov; 1218 local_aio.uio_iovcnt = 1; 1219 local_aio.uio_rw = UIO_WRITE; 1220 local_aio.uio_segflg = UIO_SYSSPACE; 1221 local_aio.uio_td = td; 1222 local_aio.uio_offset = base_offset; 1223 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1224 1225 ioflag = IO_NODELOCKED; 1226 if (ufs_extattr_sync) 1227 ioflag |= IO_SYNC; 1228 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1229 ump->um_extattr.uepm_ucred); 1230 if (error) 1231 goto vopunlock_exit; 1232 1233 if (local_aio.uio_resid != 0) 1234 error = ENXIO; 1235 1236vopunlock_exit: 1237 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1238 1239 return (error); 1240} 1241 1242/* 1243 * Called by UFS when an inode is no longer active and should have its 1244 * attributes stripped. 1245 */ 1246void 1247ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td) 1248{ 1249 struct ufs_extattr_list_entry *uele; 1250 struct mount *mp = vp->v_mount; 1251 struct ufsmount *ump = VFSTOUFS(mp); 1252 1253 /* 1254 * In that case, we cannot lock. We should not have any active vnodes 1255 * on the fs if this is not yet initialized but is going to be, so 1256 * this can go unlocked. 1257 */ 1258 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 1259 return; 1260 1261 ufs_extattr_uepm_lock(ump, td); 1262 1263 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 1264 ufs_extattr_uepm_unlock(ump, td); 1265 return; 1266 } 1267 1268 LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) 1269 ufs_extattr_rm(vp, uele->uele_attrnamespace, 1270 uele->uele_attrname, NULL, td); 1271 1272 ufs_extattr_uepm_unlock(ump, td); 1273} 1274 1275#endif /* !UFS_EXTATTR */ 1276