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