1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 1999-2002 Robert N. M. Watson 5 * Copyright (c) 2002-2003 Networks Associates Technology, Inc. 6 * All rights reserved. 7 * 8 * This software was developed by Robert Watson for the TrustedBSD Project. 9 * 10 * This software was developed for the FreeBSD Project in part by Network 11 * Associates Laboratories, the Security Research Division of Network 12 * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), 13 * as part of the DARPA CHATS research program. 14 * 15 * Redistribution and use in source and binary forms, with or without 16 * modification, are permitted provided that the following conditions 17 * are met: 18 * 1. Redistributions of source code must retain the above copyright 19 * notice, this list of conditions and the following disclaimer. 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in the 22 * documentation and/or other materials provided with the distribution. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 */ 37 38 /* 39 * Support for filesystem extended attribute: UFS-specific support functions. 40 */ 41 42 #include <sys/cdefs.h> 43 __FBSDID("$FreeBSD$"); 44 45 #include "opt_ufs.h" 46 47 #include <sys/param.h> 48 #include <sys/systm.h> 49 #include <sys/kernel.h> 50 #include <sys/namei.h> 51 #include <sys/malloc.h> 52 #include <sys/fcntl.h> 53 #include <sys/priv.h> 54 #include <sys/proc.h> 55 #include <sys/vnode.h> 56 #include <sys/mount.h> 57 #include <sys/lock.h> 58 #include <sys/dirent.h> 59 #include <sys/extattr.h> 60 #include <sys/sx.h> 61 #include <sys/sysctl.h> 62 63 #include <vm/uma.h> 64 65 #include <ufs/ufs/dir.h> 66 #include <ufs/ufs/extattr.h> 67 #include <ufs/ufs/quota.h> 68 #include <ufs/ufs/ufsmount.h> 69 #include <ufs/ufs/inode.h> 70 #include <ufs/ufs/ufs_extern.h> 71 72 #ifdef UFS_EXTATTR 73 74 FEATURE(ufs_extattr, "ufs extended attribute support"); 75 76 static MALLOC_DEFINE(M_UFS_EXTATTR, "ufs_extattr", "ufs extended attribute"); 77 78 static int ufs_extattr_sync = 0; 79 SYSCTL_INT(_debug, OID_AUTO, ufs_extattr_sync, CTLFLAG_RW, &ufs_extattr_sync, 80 0, ""); 81 82 static int ufs_extattr_valid_attrname(int attrnamespace, 83 const char *attrname); 84 static int ufs_extattr_enable_with_open(struct ufsmount *ump, 85 struct vnode *vp, int attrnamespace, const char *attrname, 86 struct thread *td); 87 static int ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 88 const char *attrname, struct vnode *backing_vnode, 89 struct thread *td); 90 static int ufs_extattr_disable(struct ufsmount *ump, int attrnamespace, 91 const char *attrname, struct thread *td); 92 static int ufs_extattr_get(struct vnode *vp, int attrnamespace, 93 const char *name, struct uio *uio, size_t *size, 94 struct ucred *cred, struct thread *td); 95 static int ufs_extattr_set(struct vnode *vp, int attrnamespace, 96 const char *name, struct uio *uio, struct ucred *cred, 97 struct thread *td); 98 static int ufs_extattr_rm(struct vnode *vp, int attrnamespace, 99 const char *name, struct ucred *cred, struct thread *td); 100 #ifdef UFS_EXTATTR_AUTOSTART 101 static int ufs_extattr_autostart_locked(struct mount *mp, 102 struct thread *td); 103 #endif 104 static int ufs_extattr_start_locked(struct ufsmount *ump, 105 struct thread *td); 106 107 /* 108 * Per-FS attribute lock protecting attribute operations. 109 * 110 * XXXRW: Perhaps something more fine-grained would be appropriate, but at 111 * the end of the day we're going to contend on the vnode lock for the 112 * backing file anyway. 113 */ 114 static void 115 ufs_extattr_uepm_lock(struct ufsmount *ump) 116 { 117 118 sx_xlock(&ump->um_extattr.uepm_lock); 119 } 120 121 static void 122 ufs_extattr_uepm_unlock(struct ufsmount *ump) 123 { 124 125 sx_xunlock(&ump->um_extattr.uepm_lock); 126 } 127 128 /*- 129 * Determine whether the name passed is a valid name for an actual 130 * attribute. 131 * 132 * Invalid currently consists of: 133 * NULL pointer for attrname 134 * zero-length attrname (used to retrieve application attribute list) 135 */ 136 static int 137 ufs_extattr_valid_attrname(int attrnamespace, const char *attrname) 138 { 139 140 if (attrname == NULL) 141 return (0); 142 if (strlen(attrname) == 0) 143 return (0); 144 return (1); 145 } 146 147 /* 148 * Locate an attribute given a name and mountpoint. 149 * Must be holding uepm lock for the mount point. 150 */ 151 static struct ufs_extattr_list_entry * 152 ufs_extattr_find_attr(struct ufsmount *ump, int attrnamespace, 153 const char *attrname) 154 { 155 struct ufs_extattr_list_entry *search_attribute; 156 157 sx_assert(&ump->um_extattr.uepm_lock, SA_XLOCKED); 158 159 for (search_attribute = LIST_FIRST(&ump->um_extattr.uepm_list); 160 search_attribute != NULL; 161 search_attribute = LIST_NEXT(search_attribute, uele_entries)) { 162 if (!(strncmp(attrname, search_attribute->uele_attrname, 163 UFS_EXTATTR_MAXEXTATTRNAME)) && 164 (attrnamespace == search_attribute->uele_attrnamespace)) { 165 return (search_attribute); 166 } 167 } 168 169 return (0); 170 } 171 172 /* 173 * Initialize per-FS structures supporting extended attributes. Do not 174 * start extended attributes yet. 175 */ 176 void 177 ufs_extattr_uepm_init(struct ufs_extattr_per_mount *uepm) 178 { 179 180 uepm->uepm_flags = 0; 181 LIST_INIT(&uepm->uepm_list); 182 sx_init(&uepm->uepm_lock, "ufs_extattr_sx"); 183 uepm->uepm_flags |= UFS_EXTATTR_UEPM_INITIALIZED; 184 } 185 186 /* 187 * Destroy per-FS structures supporting extended attributes. Assumes 188 * that EAs have already been stopped, and will panic if not. 189 */ 190 void 191 ufs_extattr_uepm_destroy(struct ufs_extattr_per_mount *uepm) 192 { 193 194 if (!(uepm->uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 195 panic("ufs_extattr_uepm_destroy: not initialized"); 196 197 if ((uepm->uepm_flags & UFS_EXTATTR_UEPM_STARTED)) 198 panic("ufs_extattr_uepm_destroy: called while still started"); 199 200 /* 201 * It's not clear that either order for the next two lines is 202 * ideal, and it should never be a problem if this is only called 203 * during unmount, and with vfs_busy(). 204 */ 205 uepm->uepm_flags &= ~UFS_EXTATTR_UEPM_INITIALIZED; 206 sx_destroy(&uepm->uepm_lock); 207 } 208 209 /* 210 * Start extended attribute support on an FS. 211 */ 212 int 213 ufs_extattr_start(struct mount *mp, struct thread *td) 214 { 215 struct ufsmount *ump; 216 int error = 0; 217 218 ump = VFSTOUFS(mp); 219 220 ufs_extattr_uepm_lock(ump); 221 error = ufs_extattr_start_locked(ump, td); 222 ufs_extattr_uepm_unlock(ump); 223 return (error); 224 } 225 226 static int 227 ufs_extattr_start_locked(struct ufsmount *ump, struct thread *td) 228 { 229 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_INITIALIZED)) 230 return (EOPNOTSUPP); 231 if (ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED) 232 return (EBUSY); 233 234 ump->um_extattr.uepm_flags |= UFS_EXTATTR_UEPM_STARTED; 235 ump->um_extattr.uepm_ucred = crhold(td->td_ucred); 236 return (0); 237 } 238 239 #ifdef UFS_EXTATTR_AUTOSTART 240 /* 241 * Helper routine: given a locked parent directory and filename, return 242 * the locked vnode of the inode associated with the name. Will not 243 * follow symlinks, may return any type of vnode. Lock on parent will 244 * be released even in the event of a failure. In the event that the 245 * target is the parent (i.e., "."), there will be two references and 246 * one lock, requiring the caller to possibly special-case. 247 */ 248 #define UE_GETDIR_LOCKPARENT 1 249 #define UE_GETDIR_LOCKPARENT_DONT 2 250 static int 251 ufs_extattr_lookup(struct vnode *start_dvp, int lockparent, char *dirname, 252 struct vnode **vp, struct thread *td) 253 { 254 struct vop_cachedlookup_args vargs; 255 struct componentname cnp; 256 struct vnode *target_vp; 257 int error; 258 259 bzero(&cnp, sizeof(cnp)); 260 cnp.cn_nameiop = LOOKUP; 261 cnp.cn_flags = ISLASTCN; 262 if (lockparent == UE_GETDIR_LOCKPARENT) 263 cnp.cn_flags |= LOCKPARENT; 264 cnp.cn_lkflags = LK_EXCLUSIVE; 265 cnp.cn_thread = td; 266 cnp.cn_cred = td->td_ucred; 267 cnp.cn_pnbuf = uma_zalloc(namei_zone, M_WAITOK); 268 cnp.cn_nameptr = cnp.cn_pnbuf; 269 error = copystr(dirname, cnp.cn_pnbuf, MAXPATHLEN, 270 (size_t *) &cnp.cn_namelen); 271 if (error) { 272 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) { 273 VOP_UNLOCK(start_dvp, 0); 274 } 275 uma_zfree(namei_zone, cnp.cn_pnbuf); 276 printf("ufs_extattr_lookup: copystr failed\n"); 277 return (error); 278 } 279 cnp.cn_namelen--; /* trim nul termination */ 280 vargs.a_gen.a_desc = NULL; 281 vargs.a_dvp = start_dvp; 282 vargs.a_vpp = &target_vp; 283 vargs.a_cnp = &cnp; 284 error = ufs_lookup(&vargs); 285 uma_zfree(namei_zone, cnp.cn_pnbuf); 286 if (error) { 287 /* 288 * Error condition, may have to release the lock on the parent 289 * if ufs_lookup() didn't. 290 */ 291 if (lockparent == UE_GETDIR_LOCKPARENT_DONT) 292 VOP_UNLOCK(start_dvp, 0); 293 294 /* 295 * Check that ufs_lookup() didn't release the lock when we 296 * didn't want it to. 297 */ 298 if (lockparent == UE_GETDIR_LOCKPARENT) 299 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 300 301 return (error); 302 } 303 /* 304 if (target_vp == start_dvp) 305 panic("ufs_extattr_lookup: target_vp == start_dvp"); 306 */ 307 308 if (target_vp != start_dvp && lockparent == UE_GETDIR_LOCKPARENT_DONT) 309 VOP_UNLOCK(start_dvp, 0); 310 311 if (lockparent == UE_GETDIR_LOCKPARENT) 312 ASSERT_VOP_LOCKED(start_dvp, "ufs_extattr_lookup"); 313 314 /* printf("ufs_extattr_lookup: success\n"); */ 315 *vp = target_vp; 316 return (0); 317 } 318 #endif /* !UFS_EXTATTR_AUTOSTART */ 319 320 /* 321 * Enable an EA using the passed filesystem, backing vnode, attribute name, 322 * namespace, and proc. Will perform a VOP_OPEN() on the vp, so expects vp 323 * to be locked when passed in. The vnode will be returned unlocked, 324 * regardless of success/failure of the function. As a result, the caller 325 * will always need to vrele(), but not vput(). 326 */ 327 static int 328 ufs_extattr_enable_with_open(struct ufsmount *ump, struct vnode *vp, 329 int attrnamespace, const char *attrname, struct thread *td) 330 { 331 int error; 332 333 error = VOP_OPEN(vp, FREAD|FWRITE, td->td_ucred, td, NULL); 334 if (error) { 335 printf("ufs_extattr_enable_with_open.VOP_OPEN(): failed " 336 "with %d\n", error); 337 VOP_UNLOCK(vp, 0); 338 return (error); 339 } 340 341 VOP_ADD_WRITECOUNT(vp, 1); 342 CTR3(KTR_VFS, "%s: vp %p v_writecount increased to %d", __func__, vp, 343 vp->v_writecount); 344 345 vref(vp); 346 347 VOP_UNLOCK(vp, 0); 348 349 error = ufs_extattr_enable(ump, attrnamespace, attrname, vp, td); 350 if (error != 0) 351 vn_close(vp, FREAD|FWRITE, td->td_ucred, td); 352 return (error); 353 } 354 355 #ifdef UFS_EXTATTR_AUTOSTART 356 /* 357 * Given a locked directory vnode, iterate over the names in the directory 358 * and use ufs_extattr_lookup() to retrieve locked vnodes of potential 359 * attribute files. Then invoke ufs_extattr_enable_with_open() on each 360 * to attempt to start the attribute. Leaves the directory locked on 361 * exit. 362 */ 363 static int 364 ufs_extattr_iterate_directory(struct ufsmount *ump, struct vnode *dvp, 365 int attrnamespace, struct thread *td) 366 { 367 struct vop_readdir_args vargs; 368 struct dirent *dp, *edp; 369 struct vnode *attr_vp; 370 struct uio auio; 371 struct iovec aiov; 372 char *dirbuf; 373 int error, eofflag = 0; 374 375 if (dvp->v_type != VDIR) 376 return (ENOTDIR); 377 378 dirbuf = malloc(DIRBLKSIZ, M_TEMP, M_WAITOK); 379 380 auio.uio_iov = &aiov; 381 auio.uio_iovcnt = 1; 382 auio.uio_rw = UIO_READ; 383 auio.uio_segflg = UIO_SYSSPACE; 384 auio.uio_td = td; 385 auio.uio_offset = 0; 386 387 vargs.a_gen.a_desc = NULL; 388 vargs.a_vp = dvp; 389 vargs.a_uio = &auio; 390 vargs.a_cred = td->td_ucred; 391 vargs.a_eofflag = &eofflag; 392 vargs.a_ncookies = NULL; 393 vargs.a_cookies = NULL; 394 395 while (!eofflag) { 396 auio.uio_resid = DIRBLKSIZ; 397 aiov.iov_base = dirbuf; 398 aiov.iov_len = DIRBLKSIZ; 399 error = ufs_readdir(&vargs); 400 if (error) { 401 printf("ufs_extattr_iterate_directory: ufs_readdir " 402 "%d\n", error); 403 return (error); 404 } 405 406 edp = (struct dirent *)&dirbuf[DIRBLKSIZ - auio.uio_resid]; 407 for (dp = (struct dirent *)dirbuf; dp < edp; ) { 408 if (dp->d_reclen == 0) 409 break; 410 error = ufs_extattr_lookup(dvp, UE_GETDIR_LOCKPARENT, 411 dp->d_name, &attr_vp, td); 412 if (error) { 413 printf("ufs_extattr_iterate_directory: lookup " 414 "%s %d\n", dp->d_name, error); 415 } else if (attr_vp == dvp) { 416 vrele(attr_vp); 417 } else if (attr_vp->v_type != VREG) { 418 vput(attr_vp); 419 } else { 420 error = ufs_extattr_enable_with_open(ump, 421 attr_vp, attrnamespace, dp->d_name, td); 422 vrele(attr_vp); 423 if (error) { 424 printf("ufs_extattr_iterate_directory: " 425 "enable %s %d\n", dp->d_name, 426 error); 427 } else if (bootverbose) { 428 printf("UFS autostarted EA %s\n", 429 dp->d_name); 430 } 431 } 432 dp = (struct dirent *) ((char *)dp + dp->d_reclen); 433 if (dp >= edp) 434 break; 435 } 436 } 437 free(dirbuf, M_TEMP); 438 439 return (0); 440 } 441 442 /* 443 * Auto-start of extended attributes, to be executed (optionally) at 444 * mount-time. 445 */ 446 int 447 ufs_extattr_autostart(struct mount *mp, struct thread *td) 448 { 449 struct ufsmount *ump; 450 int error; 451 452 ump = VFSTOUFS(mp); 453 ufs_extattr_uepm_lock(ump); 454 error = ufs_extattr_autostart_locked(mp, td); 455 ufs_extattr_uepm_unlock(ump); 456 return (error); 457 } 458 459 static int 460 ufs_extattr_autostart_locked(struct mount *mp, struct thread *td) 461 { 462 struct vnode *rvp, *attr_dvp, *attr_system_dvp, *attr_user_dvp; 463 struct ufsmount *ump = VFSTOUFS(mp); 464 int error; 465 466 /* 467 * UFS_EXTATTR applies only to UFS1, as UFS2 uses native extended 468 * attributes, so don't autostart. 469 */ 470 if (ump->um_fstype != UFS1) 471 return (0); 472 473 /* 474 * Does UFS_EXTATTR_FSROOTSUBDIR exist off the filesystem root? 475 * If so, automatically start EA's. 476 */ 477 error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp); 478 if (error) { 479 printf("ufs_extattr_autostart.VFS_ROOT() returned %d\n", 480 error); 481 return (error); 482 } 483 484 error = ufs_extattr_lookup(rvp, UE_GETDIR_LOCKPARENT_DONT, 485 UFS_EXTATTR_FSROOTSUBDIR, &attr_dvp, td); 486 if (error) { 487 /* rvp ref'd but now unlocked */ 488 vrele(rvp); 489 return (error); 490 } 491 if (rvp == attr_dvp) { 492 /* Should never happen. */ 493 vput(rvp); 494 vrele(attr_dvp); 495 return (EINVAL); 496 } 497 vrele(rvp); 498 499 if (attr_dvp->v_type != VDIR) { 500 printf("ufs_extattr_autostart: %s != VDIR\n", 501 UFS_EXTATTR_FSROOTSUBDIR); 502 goto return_vput_attr_dvp; 503 } 504 505 error = ufs_extattr_start_locked(ump, td); 506 if (error) { 507 printf("ufs_extattr_autostart: ufs_extattr_start failed (%d)\n", 508 error); 509 goto return_vput_attr_dvp; 510 } 511 512 /* 513 * Look for two subdirectories: UFS_EXTATTR_SUBDIR_SYSTEM, 514 * UFS_EXTATTR_SUBDIR_USER. For each, iterate over the sub-directory, 515 * and start with appropriate type. Failures in either don't 516 * result in an over-all failure. attr_dvp is left locked to 517 * be cleaned up on exit. 518 */ 519 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 520 UFS_EXTATTR_SUBDIR_SYSTEM, &attr_system_dvp, td); 521 if (!error) { 522 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 523 attr_system_dvp, EXTATTR_NAMESPACE_SYSTEM, td); 524 if (error) 525 printf("ufs_extattr_iterate_directory returned %d\n", 526 error); 527 vput(attr_system_dvp); 528 } 529 530 error = ufs_extattr_lookup(attr_dvp, UE_GETDIR_LOCKPARENT, 531 UFS_EXTATTR_SUBDIR_USER, &attr_user_dvp, td); 532 if (!error) { 533 error = ufs_extattr_iterate_directory(VFSTOUFS(mp), 534 attr_user_dvp, EXTATTR_NAMESPACE_USER, td); 535 if (error) 536 printf("ufs_extattr_iterate_directory returned %d\n", 537 error); 538 vput(attr_user_dvp); 539 } 540 541 /* Mask startup failures in sub-directories. */ 542 error = 0; 543 544 return_vput_attr_dvp: 545 vput(attr_dvp); 546 547 return (error); 548 } 549 #endif /* !UFS_EXTATTR_AUTOSTART */ 550 551 /* 552 * Stop extended attribute support on an FS. 553 */ 554 int 555 ufs_extattr_stop(struct mount *mp, struct thread *td) 556 { 557 struct ufs_extattr_list_entry *uele; 558 struct ufsmount *ump = VFSTOUFS(mp); 559 int error = 0; 560 561 ufs_extattr_uepm_lock(ump); 562 563 if (!(ump->um_extattr.uepm_flags & UFS_EXTATTR_UEPM_STARTED)) { 564 error = EOPNOTSUPP; 565 goto unlock; 566 } 567 568 while ((uele = LIST_FIRST(&ump->um_extattr.uepm_list)) != NULL) { 569 ufs_extattr_disable(ump, uele->uele_attrnamespace, 570 uele->uele_attrname, td); 571 } 572 573 ump->um_extattr.uepm_flags &= ~UFS_EXTATTR_UEPM_STARTED; 574 575 crfree(ump->um_extattr.uepm_ucred); 576 ump->um_extattr.uepm_ucred = NULL; 577 578 unlock: 579 ufs_extattr_uepm_unlock(ump); 580 581 return (error); 582 } 583 584 /* 585 * Enable a named attribute on the specified filesystem; provide an 586 * unlocked backing vnode to hold the attribute data. 587 */ 588 static int 589 ufs_extattr_enable(struct ufsmount *ump, int attrnamespace, 590 const char *attrname, struct vnode *backing_vnode, struct thread *td) 591 { 592 struct ufs_extattr_list_entry *attribute; 593 struct iovec aiov; 594 struct uio auio; 595 int error = 0; 596 597 if (!ufs_extattr_valid_attrname(attrnamespace, attrname)) 598 return (EINVAL); 599 if (backing_vnode->v_type != VREG) 600 return (EINVAL); 601 602 attribute = malloc(sizeof(struct ufs_extattr_list_entry), 603 M_UFS_EXTATTR, M_WAITOK); 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 666 unlock_free_exit: 667 VOP_UNLOCK(backing_vnode, 0); 668 669 free_exit: 670 free(attribute, M_UFS_EXTATTR); 671 return (error); 672 } 673 674 /* 675 * Disable extended attribute support on an FS. 676 */ 677 static int 678 ufs_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 */ 709 int 710 ufs_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 */ 807 int 808 ufs_getextattr(struct vop_getextattr_args *ap) 809 /* 810 vop_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 */ 839 static int 840 ufs_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 965 vopunlock_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 */ 979 int 980 ufs_deleteextattr(struct vop_deleteextattr_args *ap) 981 /* 982 vop_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 */ 1009 int 1010 ufs_setextattr(struct vop_setextattr_args *ap) 1011 /* 1012 vop_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 */ 1046 static int 1047 ufs_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 1141 vopunlock_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 */ 1154 static int 1155 ufs_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 1263 vopunlock_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 */ 1273 void 1274 ufs_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