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