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