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