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