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