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