ufs_extattr.c revision 175202
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 175202 2008-01-10 01:10:58Z attilio $"); 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, td); 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, td); 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, td); 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, td); 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, td); 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, td); 329 return (error); 330 } 331 332 vp->v_writecount++; 333 334 vref(vp); 335 336 VOP_UNLOCK(vp, 0, td); 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 int error; 452 453 /* 454 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root? 455 * If so, automatically start EA's. 456 */ 457 error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp, td); 458 if (error) { 459 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", 460 error); 461 return (error); 462 } 463 464 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, 465 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td); 466 if (error) { 467 /* rvp ref'd but now unlocked */ 468 vrele(rvp); 469 return (error); 470 } 471 if (rvp == attr_dvp) { 472 /* Should never happen. */ 473 vput(rvp); 474 vrele(attr_dvp); 475 return (EINVAL); 476 } 477 vrele(rvp); 478 479 if (attr_dvp->v_type != VDIR) { 480 printf("ufs_extattr_autostart: %s != VDIR\n", 481 UFS_EXTATTR_FSROOTSUBDIR); 482 goto return_vput_attr_dvp; 483 } 484 485 error = ufs_extattr_start(mp, td); 486 if (error) { 487 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", 488 error); 489 goto return_vput_attr_dvp; 490 } 491 492 /* 493 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, 494 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, 495 * and start with appropriate type. Failures in either don't 496 * result in an over-all failure. attr_dvp is left locked to 497 * be cleaned up on exit. 498 */ 499 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 500 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td); 501 if (!error) { 502 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 503 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td); 504 if (error) 505 printf("ufs_extattr_iterate_directory returned %d\n", 506 error); 507 vput(attr_system_dvp); 508 } 509 510 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 511 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td); 512 if (!error) { 513 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 514 attr_user_dvp, EXTATTR_NAMESPACE_USER, td); 515 if (error) 516 printf("ufs_extattr_iterate_directory returned %d\n", 517 error); 518 vput(attr_user_dvp); 519 } 520 521 /* Mask startup failures in sub-directories. */ 522 error = 0; 523 524return_vput_attr_dvp: 525 vput(attr_dvp); 526 527 return (error); 528} 529#endif /* !UFS_EXTATTR_AUTOSTART */ 530 531/* 532 * Stop extended attribute support on an FS. 533 */ 534int 535ufs_extattr_stop(struct mount *mp, struct thread *td) 536{ 537 struct ufs_extattr_list_entry *uele; 538 struct ufsmount *ump = VFSTOUFS(mp); 539 int error = 0; 540 541 ufs_extattr_uepm_lock(ump, td); 542 543 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 544 error = EOPNOTSUPP; 545 goto unlock; 546 } 547 548 while ((uele = LIST_FIRST(&ump->um_extattr.uepm_list)) != NULL) { 549 ufs_extattr_disable(ump, uele->uele_attrnamespace, 550 uele->uele_attrname, td); 551 } 552 553 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 554 555 crfree(ump->um_extattr.uepm_ucred); 556 ump->um_extattr.uepm_ucred = NULL; 557 558unlock: 559 ufs_extattr_uepm_unlock(ump, td); 560 561 return (error); 562} 563 564/* 565 * Enable a named attribute on the specified filesystem; provide an 566 * unlocked backing vnode to hold the attribute data. 567 */ 568static int 569ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 570 const char *attrname, struct vnode *backing_vnode, struct thread *td) 571{ 572 struct ufs_extattr_list_entry *attribute; 573 struct iovec aiov; 574 struct uio auio; 575 int error = 0; 576 577 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 578 return (EINVAL); 579 if (backing_vnode->v_type != VREG) 580 return (EINVAL); 581 582 MALLOC(attribute, struct ufs_extattr_list_entry *, 583 sizeof(struct ufs_extattr_list_entry), M_UFS_EXTATTR, M_WAITOK); 584 if (attribute == NULL) 585 return (ENOMEM); 586 587 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 588 error = EOPNOTSUPP; 589 goto free_exit; 590 } 591 592 if (ufs_extattr_find_attr(ump, attrnamespace, attrname)) { 593 error = EEXIST; 594 goto free_exit; 595 } 596 597 strncpy(attribute->uele_attrname, attrname, 598 UFS_EXTATTR_MAXEXTATTRNAME); 599 attribute->uele_attrnamespace = attrnamespace; 600 bzero(&attribute->uele_fileheader, 601 sizeof(struct ufs_extattr_fileheader)); 602 603 attribute->uele_backing_vnode = backing_vnode; 604 605 auio.uio_iov = &aiov; 606 auio.uio_iovcnt = 1; 607 aiov.iov_base = (caddr_t) &attribute->uele_fileheader; 608 aiov.iov_len = sizeof(struct ufs_extattr_fileheader); 609 auio.uio_resid = sizeof(struct ufs_extattr_fileheader); 610 auio.uio_offset = (off_t) 0; 611 auio.uio_segflg = UIO_SYSSPACE; 612 auio.uio_rw = UIO_READ; 613 auio.uio_td = td; 614 615 vn_lock(backing_vnode, LK_SHARED | LK_RETRY); 616 error = VOP_READ(backing_vnode, &auio, IO_NODELOCKED, 617 ump->um_extattr.uepm_ucred); 618 619 if (error) 620 goto unlock_free_exit; 621 622 if (auio.uio_resid != 0) { 623 printf("ufs_extattr_enable: malformed attribute header\n"); 624 error = EINVAL; 625 goto unlock_free_exit; 626 } 627 628 if (attribute->uele_fileheader.uef_magic != UFS_EXTATTR_MAGIC) { 629 printf("ufs_extattr_enable: invalid attribute header magic\n"); 630 error = EINVAL; 631 goto unlock_free_exit; 632 } 633 634 if (attribute->uele_fileheader.uef_version != UFS_EXTATTR_VERSION) { 635 printf("ufs_extattr_enable: incorrect attribute header " 636 "version\n"); 637 error = EINVAL; 638 goto unlock_free_exit; 639 } 640 641 ASSERT_VOP_LOCKED(backing_vnode, "ufs_extattr_enable"); 642 LIST_INSERT_HEAD(&ump->um_extattr.uepm_list, attribute, 643 uele_entries); 644 645 VOP_UNLOCK(backing_vnode, 0, td); 646 return (0); 647 648unlock_free_exit: 649 VOP_UNLOCK(backing_vnode, 0, td); 650 651free_exit: 652 FREE(attribute, M_UFS_EXTATTR); 653 return (error); 654} 655 656/* 657 * Disable extended attribute support on an FS. 658 */ 659static int 660ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 661 const char *attrname, struct thread *td) 662{ 663 struct ufs_extattr_list_entry *uele; 664 int error = 0; 665 666 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 667 return (EINVAL); 668 669 uele = ufs_extattr_find_attr(ump, attrnamespace, attrname); 670 if (!uele) 671 return (ENOATTR); 672 673 LIST_REMOVE(uele, uele_entries); 674 675 vn_lock(uele->uele_backing_vnode, LK_SHARED | LK_RETRY); 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 error = priv_check(td, PRIV_UFS_EXTATTRCTL); 703 if (error) { 704 if (filename_vp != NULL) 705 VOP_UNLOCK(filename_vp, 0, td); 706 return (error); 707 } 708 709 switch(cmd) { 710 case UFS_EXTATTR_CMD_START: 711 if (filename_vp != NULL) { 712 VOP_UNLOCK(filename_vp, 0, td); 713 return (EINVAL); 714 } 715 if (attrname != NULL) 716 return (EINVAL); 717 718 error = ufs_extattr_start(mp, td); 719 720 return (error); 721 722 case UFS_EXTATTR_CMD_STOP: 723 if (filename_vp != NULL) { 724 VOP_UNLOCK(filename_vp, 0, td); 725 return (EINVAL); 726 } 727 if (attrname != NULL) 728 return (EINVAL); 729 730 error = ufs_extattr_stop(mp, td); 731 732 return (error); 733 734 case UFS_EXTATTR_CMD_ENABLE: 735 736 if (filename_vp == NULL) 737 return (EINVAL); 738 if (attrname == NULL) { 739 VOP_UNLOCK(filename_vp, 0, td); 740 return (EINVAL); 741 } 742 743 /* 744 * ufs_extattr_enable_with_open() will always unlock the 745 * vnode, regardless of failure. 746 */ 747 ufs_extattr_uepm_lock(ump, td); 748 error = ufs_extattr_enable_with_open(ump, filename_vp, 749 attrnamespace, attrname, td); 750 ufs_extattr_uepm_unlock(ump, td); 751 752 return (error); 753 754 case UFS_EXTATTR_CMD_DISABLE: 755 756 if (filename_vp != NULL) { 757 VOP_UNLOCK(filename_vp, 0, td); 758 return (EINVAL); 759 } 760 if (attrname == NULL) 761 return (EINVAL); 762 763 ufs_extattr_uepm_lock(ump, td); 764 error = ufs_extattr_disable(ump, attrnamespace, attrname, 765 td); 766 ufs_extattr_uepm_unlock(ump, td); 767 768 return (error); 769 770 default: 771 return (EINVAL); 772 } 773} 774 775/* 776 * Vnode operating to retrieve a named extended attribute. 777 */ 778int 779ufs_getextattr(struct vop_getextattr_args *ap) 780/* 781vop_getextattr { 782 IN struct vnode *a_vp; 783 IN int a_attrnamespace; 784 IN const char *a_name; 785 INOUT struct uio *a_uio; 786 OUT size_t *a_size; 787 IN struct ucred *a_cred; 788 IN struct thread *a_td; 789}; 790*/ 791{ 792 struct mount *mp = ap->a_vp->v_mount; 793 struct ufsmount *ump = VFSTOUFS(mp); 794 int error; 795 796 ufs_extattr_uepm_lock(ump, ap->a_td); 797 798 error = ufs_extattr_get(ap->a_vp, ap->a_attrnamespace, ap->a_name, 799 ap->a_uio, ap->a_size, ap->a_cred, ap->a_td); 800 801 ufs_extattr_uepm_unlock(ump, ap->a_td); 802 803 return (error); 804} 805 806/* 807 * Real work associated with retrieving a named attribute--assumes that 808 * the attribute lock has already been grabbed. 809 */ 810static int 811ufs_extattr_get(struct vnode *vp, int attrnamespace, const char *name, 812 struct uio *uio, size_t *size, struct ucred *cred, struct thread *td) 813{ 814 struct ufs_extattr_list_entry *attribute; 815 struct ufs_extattr_header ueh; 816 struct iovec local_aiov; 817 struct uio local_aio; 818 struct mount *mp = vp->v_mount; 819 struct ufsmount *ump = VFSTOUFS(mp); 820 struct inode *ip = VTOI(vp); 821 off_t base_offset; 822 size_t len, old_len; 823 int error = 0; 824 825 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 826 return (EOPNOTSUPP); 827 828 if (strlen(name) == 0) 829 return (EINVAL); 830 831 error = extattr_check_cred(vp, attrnamespace, cred, td, IREAD); 832 if (error) 833 return (error); 834 835 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 836 if (!attribute) 837 return (ENOATTR); 838 839 /* 840 * Allow only offsets of zero to encourage the read/replace 841 * extended attribute semantic. Otherwise we can't guarantee 842 * atomicity, as we don't provide locks for extended attributes. 843 */ 844 if (uio != NULL && uio->uio_offset != 0) 845 return (ENXIO); 846 847 /* 848 * Find base offset of header in file based on file header size, and 849 * data header size + maximum data size, indexed by inode number. 850 */ 851 base_offset = sizeof(struct ufs_extattr_fileheader) + 852 ip->i_number * (sizeof(struct ufs_extattr_header) + 853 attribute->uele_fileheader.uef_size); 854 855 /* 856 * Read in the data header to see if the data is defined, and if so 857 * how much. 858 */ 859 bzero(&ueh, sizeof(struct ufs_extattr_header)); 860 local_aiov.iov_base = (caddr_t) &ueh; 861 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 862 local_aio.uio_iov = &local_aiov; 863 local_aio.uio_iovcnt = 1; 864 local_aio.uio_rw = UIO_READ; 865 local_aio.uio_segflg = UIO_SYSSPACE; 866 local_aio.uio_td = td; 867 local_aio.uio_offset = base_offset; 868 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 869 870 /* 871 * Acquire locks. 872 * 873 * Don't need to get a lock on the backing file if the getattr is 874 * being applied to the backing file, as the lock is already held. 875 */ 876 if (attribute->uele_backing_vnode != vp) 877 vn_lock(attribute->uele_backing_vnode, LK_SHARED | LK_RETRY); 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, LK_EXCLUSIVE | LK_RETRY); 1087 1088 ioflag = IO_NODELOCKED; 1089 if (ufs_extattr_sync) 1090 ioflag |= IO_SYNC; 1091 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1092 ump->um_extattr.uepm_ucred); 1093 if (error) 1094 goto vopunlock_exit; 1095 1096 if (local_aio.uio_resid != 0) { 1097 error = ENXIO; 1098 goto vopunlock_exit; 1099 } 1100 1101 /* 1102 * Write out user data. 1103 */ 1104 uio->uio_offset = base_offset + sizeof(struct ufs_extattr_header); 1105 1106 ioflag = IO_NODELOCKED; 1107 if (ufs_extattr_sync) 1108 ioflag |= IO_SYNC; 1109 error = VOP_WRITE(attribute->uele_backing_vnode, uio, ioflag, 1110 ump->um_extattr.uepm_ucred); 1111 1112vopunlock_exit: 1113 uio->uio_offset = 0; 1114 1115 if (attribute->uele_backing_vnode != vp) 1116 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1117 1118 return (error); 1119} 1120 1121/* 1122 * Real work associated with removing an extended attribute from a vnode. 1123 * Assumes the attribute lock has already been grabbed. 1124 */ 1125static int 1126ufs_extattr_rm(struct vnode *vp, int attrnamespace, const char *name, 1127 struct ucred *cred, struct thread *td) 1128{ 1129 struct ufs_extattr_list_entry *attribute; 1130 struct ufs_extattr_header ueh; 1131 struct iovec local_aiov; 1132 struct uio local_aio; 1133 struct mount *mp = vp->v_mount; 1134 struct ufsmount *ump = VFSTOUFS(mp); 1135 struct inode *ip = VTOI(vp); 1136 off_t base_offset; 1137 int error = 0, ioflag; 1138 1139 if (vp->v_mount->mnt_flag & MNT_RDONLY) 1140 return (EROFS); 1141 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 1142 return (EOPNOTSUPP); 1143 if (!ufs_extattr_valid_attrname(attrnamespace, name)) 1144 return (EINVAL); 1145 1146 error = extattr_check_cred(vp, attrnamespace, cred, td, IWRITE); 1147 if (error) 1148 return (error); 1149 1150 attribute = ufs_extattr_find_attr(ump, attrnamespace, name); 1151 if (!attribute) 1152 return (ENOATTR); 1153 1154 /* 1155 * Find base offset of header in file based on file header size, and 1156 * data header size + maximum data size, indexed by inode number. 1157 */ 1158 base_offset = sizeof(struct ufs_extattr_fileheader) + 1159 ip->i_number * (sizeof(struct ufs_extattr_header) + 1160 attribute->uele_fileheader.uef_size); 1161 1162 /* 1163 * Check to see if currently defined. 1164 */ 1165 bzero(&ueh, sizeof(struct ufs_extattr_header)); 1166 1167 local_aiov.iov_base = (caddr_t) &ueh; 1168 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1169 local_aio.uio_iov = &local_aiov; 1170 local_aio.uio_iovcnt = 1; 1171 local_aio.uio_rw = UIO_READ; 1172 local_aio.uio_segflg = UIO_SYSSPACE; 1173 local_aio.uio_td = td; 1174 local_aio.uio_offset = base_offset; 1175 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1176 1177 /* 1178 * Don't need to get the lock on the backing vnode if the vnode we're 1179 * modifying is it, as we already hold the lock. 1180 */ 1181 if (attribute->uele_backing_vnode != vp) 1182 vn_lock(attribute->uele_backing_vnode, LK_EXCLUSIVE | LK_RETRY); 1183 1184 error = VOP_READ(attribute->uele_backing_vnode, &local_aio, 1185 IO_NODELOCKED, ump->um_extattr.uepm_ucred); 1186 if (error) 1187 goto vopunlock_exit; 1188 1189 /* Defined? */ 1190 if ((ueh.ueh_flags & UFS_EXTATTR_ATTR_FLAG_INUSE) == 0) { 1191 error = ENOATTR; 1192 goto vopunlock_exit; 1193 } 1194 1195 /* Valid for the current inode generation? */ 1196 if (ueh.ueh_i_gen != ip->i_gen) { 1197 /* 1198 * The inode itself has a different generation number than 1199 * the attribute data. For now, the best solution is to 1200 * coerce this to undefined, and let it get cleaned up by 1201 * the next write or extattrctl clean. 1202 */ 1203 printf("ufs_extattr_rm (%s): inode number inconsistency (%d, %jd)\n", 1204 mp->mnt_stat.f_mntonname, ueh.ueh_i_gen, (intmax_t)ip->i_gen); 1205 error = ENOATTR; 1206 goto vopunlock_exit; 1207 } 1208 1209 /* Flag it as not in use. */ 1210 ueh.ueh_flags = 0; 1211 ueh.ueh_len = 0; 1212 1213 local_aiov.iov_base = (caddr_t) &ueh; 1214 local_aiov.iov_len = sizeof(struct ufs_extattr_header); 1215 local_aio.uio_iov = &local_aiov; 1216 local_aio.uio_iovcnt = 1; 1217 local_aio.uio_rw = UIO_WRITE; 1218 local_aio.uio_segflg = UIO_SYSSPACE; 1219 local_aio.uio_td = td; 1220 local_aio.uio_offset = base_offset; 1221 local_aio.uio_resid = sizeof(struct ufs_extattr_header); 1222 1223 ioflag = IO_NODELOCKED; 1224 if (ufs_extattr_sync) 1225 ioflag |= IO_SYNC; 1226 error = VOP_WRITE(attribute->uele_backing_vnode, &local_aio, ioflag, 1227 ump->um_extattr.uepm_ucred); 1228 if (error) 1229 goto vopunlock_exit; 1230 1231 if (local_aio.uio_resid != 0) 1232 error = ENXIO; 1233 1234vopunlock_exit: 1235 VOP_UNLOCK(attribute->uele_backing_vnode, 0, td); 1236 1237 return (error); 1238} 1239 1240/* 1241 * Called by UFS when an inode is no longer active and should have its 1242 * attributes stripped. 1243 */ 1244void 1245ufs_extattr_vnode_inactive(struct vnode *vp, struct thread *td) 1246{ 1247 struct ufs_extattr_list_entry *uele; 1248 struct mount *mp = vp->v_mount; 1249 struct ufsmount *ump = VFSTOUFS(mp); 1250 1251 /* 1252 * In that case, we cannot lock. We should not have any active vnodes 1253 * on the fs if this is not yet initialized but is going to be, so 1254 * this can go unlocked. 1255 */ 1256 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 1257 return; 1258 1259 ufs_extattr_uepm_lock(ump, td); 1260 1261 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 1262 ufs_extattr_uepm_unlock(ump, td); 1263 return; 1264 } 1265 1266 LIST_FOREACH(uele, &ump->um_extattr.uepm_list, uele_entries) 1267 ufs_extattr_rm(vp, uele->uele_attrnamespace, 1268 uele->uele_attrname, NULL, td); 1269 1270 ufs_extattr_uepm_unlock(ump, td); 1271} 1272 1273#endif /* !UFS_EXTATTR */ 1274