1 /* $NetBSD: msdosfs_denode.c,v 1.56 2017/05/26 14:34:19 riastradh Exp $ */ 2 3 /*- 4 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 5 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 6 * All rights reserved. 7 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. All advertising materials mentioning features or use of this software 18 * must display the following acknowledgement: 19 * This product includes software developed by TooLs GmbH. 20 * 4. The name of TooLs GmbH may not be used to endorse or promote products 21 * derived from this software without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 24 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 25 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 26 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 28 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 29 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 31 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 32 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 */ 34 /* 35 * Written by Paul Popelka (paulp@uts.amdahl.com) 36 * 37 * You can do anything you want with this software, just don't say you wrote 38 * it, and don't remove this notice. 39 * 40 * This software is provided "as is". 41 * 42 * The author supplies this software to be publicly redistributed on the 43 * understanding that the author is not responsible for the correct 44 * functioning of this software in any circumstances and is not liable for 45 * any damages caused by this software. 46 * 47 * October 1992 48 */ 49 50 #include <sys/cdefs.h> 51 __KERNEL_RCSID(0, "$NetBSD: msdosfs_denode.c,v 1.56 2017/05/26 14:34:19 riastradh Exp $"); 52 53 #include <sys/param.h> 54 #include <sys/systm.h> 55 #include <sys/mount.h> 56 #include <sys/malloc.h> 57 #include <sys/pool.h> 58 #include <sys/proc.h> 59 #include <sys/buf.h> 60 #include <sys/vnode.h> 61 #include <sys/kernel.h> /* defines "time" */ 62 #include <sys/dirent.h> 63 #include <sys/namei.h> 64 #include <sys/kauth.h> 65 66 #include <uvm/uvm_extern.h> 67 68 #include <fs/msdosfs/bpb.h> 69 #include <fs/msdosfs/msdosfsmount.h> 70 #include <fs/msdosfs/direntry.h> 71 #include <fs/msdosfs/denode.h> 72 #include <fs/msdosfs/fat.h> 73 74 struct pool msdosfs_denode_pool; 75 76 struct fh_key { 77 struct msdosfsmount *fhk_mount; 78 uint32_t fhk_dircluster; 79 uint32_t fhk_diroffset; 80 }; 81 struct fh_node { 82 struct rb_node fh_rbnode; 83 struct fh_key fh_key; 84 #define fh_mount fh_key.fhk_mount 85 #define fh_dircluster fh_key.fhk_dircluster 86 #define fh_diroffset fh_key.fhk_diroffset 87 uint32_t fh_gen; 88 }; 89 90 static int 91 fh_compare_node_fh(void *ctx, const void *b, const void *key) 92 { 93 const struct fh_node * const pnp = b; 94 const struct fh_key * const fhp = key; 95 96 /* msdosfs_fh_destroy() below depends on first sorting on fh_mount. */ 97 if (pnp->fh_mount != fhp->fhk_mount) 98 return (intptr_t)pnp->fh_mount - (intptr_t)fhp->fhk_mount; 99 if (pnp->fh_dircluster != fhp->fhk_dircluster) 100 return pnp->fh_dircluster - fhp->fhk_dircluster; 101 return pnp->fh_diroffset - fhp->fhk_diroffset; 102 } 103 104 static int 105 fh_compare_nodes(void *ctx, const void *parent, const void *node) 106 { 107 const struct fh_node * const np = node; 108 109 return fh_compare_node_fh(ctx, parent, &np->fh_key); 110 } 111 112 static uint32_t fh_generation; 113 static kmutex_t fh_lock; 114 static struct pool fh_pool; 115 static rb_tree_t fh_rbtree; 116 static const rb_tree_ops_t fh_rbtree_ops = { 117 .rbto_compare_nodes = fh_compare_nodes, 118 .rbto_compare_key = fh_compare_node_fh, 119 .rbto_node_offset = offsetof(struct fh_node, fh_rbnode), 120 .rbto_context = NULL 121 }; 122 123 static const struct genfs_ops msdosfs_genfsops = { 124 .gop_size = genfs_size, 125 .gop_alloc = msdosfs_gop_alloc, 126 .gop_write = genfs_gop_write, 127 .gop_markupdate = msdosfs_gop_markupdate, 128 }; 129 130 MALLOC_DECLARE(M_MSDOSFSFAT); 131 132 void 133 msdosfs_init(void) 134 { 135 136 malloc_type_attach(M_MSDOSFSMNT); 137 malloc_type_attach(M_MSDOSFSFAT); 138 malloc_type_attach(M_MSDOSFSTMP); 139 pool_init(&msdosfs_denode_pool, sizeof(struct denode), 0, 0, 0, 140 "msdosnopl", &pool_allocator_nointr, IPL_NONE); 141 pool_init(&fh_pool, sizeof(struct fh_node), 0, 0, 0, 142 "msdosfhpl", &pool_allocator_nointr, IPL_NONE); 143 rb_tree_init(&fh_rbtree, &fh_rbtree_ops); 144 mutex_init(&fh_lock, MUTEX_DEFAULT, IPL_NONE); 145 } 146 147 /* 148 * Reinitialize. 149 */ 150 151 void 152 msdosfs_reinit(void) 153 { 154 155 } 156 157 void 158 msdosfs_done(void) 159 { 160 pool_destroy(&msdosfs_denode_pool); 161 pool_destroy(&fh_pool); 162 mutex_destroy(&fh_lock); 163 malloc_type_detach(M_MSDOSFSTMP); 164 malloc_type_detach(M_MSDOSFSFAT); 165 malloc_type_detach(M_MSDOSFSMNT); 166 } 167 168 /* 169 * If deget() succeeds it returns with the gotten denode unlocked. 170 * 171 * pmp - address of msdosfsmount structure of the filesystem containing 172 * the denode of interest. The pm_dev field and the address of 173 * the msdosfsmount structure are used. 174 * dirclust - which cluster bp contains, if dirclust is 0 (root directory) 175 * diroffset is relative to the beginning of the root directory, 176 * otherwise it is cluster relative. 177 * diroffset - offset past begin of cluster of denode we want 178 * vpp - returns the address of the gotten vnode. 179 */ 180 int 181 deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset, 182 struct vnode **vpp) 183 /* pmp: so we know the maj/min number */ 184 /* dirclust: cluster this dir entry came from */ 185 /* diroffset: index of entry within the cluster */ 186 /* vpp: returns the addr of the gotten vnode */ 187 { 188 int error; 189 struct denode_key key; 190 191 /* 192 * On FAT32 filesystems, root is a (more or less) normal 193 * directory 194 */ 195 if (FAT32(pmp) && dirclust == MSDOSFSROOT) 196 dirclust = pmp->pm_rootdirblk; 197 198 memset(&key, 0, sizeof(key)); 199 key.dk_dirclust = dirclust; 200 key.dk_diroffset = diroffset; 201 /* key.dk_dirgen = NULL; */ 202 203 error = vcache_get(pmp->pm_mountp, &key, sizeof(key), vpp); 204 return error; 205 } 206 207 int 208 msdosfs_loadvnode(struct mount *mp, struct vnode *vp, 209 const void *key, size_t key_len, const void **new_key) 210 { 211 bool is_root; 212 int error; 213 extern int (**msdosfs_vnodeop_p)(void *); 214 struct msdosfsmount *pmp; 215 struct direntry *direntptr; 216 struct denode *ldep; 217 struct buf *bp; 218 struct denode_key dkey; 219 220 KASSERT(key_len == sizeof(dkey)); 221 memcpy(&dkey, key, key_len); 222 KASSERT(dkey.dk_dirgen == NULL); 223 224 pmp = VFSTOMSDOSFS(mp); 225 is_root = ((dkey.dk_dirclust == MSDOSFSROOT || 226 (FAT32(pmp) && dkey.dk_dirclust == pmp->pm_rootdirblk)) && 227 dkey.dk_diroffset == MSDOSFSROOT_OFS); 228 229 #ifdef MSDOSFS_DEBUG 230 printf("loadvnode(pmp %p, dirclust %lu, diroffset %lx, vp %p)\n", 231 pmp, dkey.dk_dirclust, dkey.dk_diroffset, vp); 232 #endif 233 234 ldep = pool_get(&msdosfs_denode_pool, PR_WAITOK); 235 memset(ldep, 0, sizeof *ldep); 236 /* ldep->de_flag = 0; */ 237 /* ldep->de_devvp = 0; */ 238 /* ldep->de_lockf = 0; */ 239 ldep->de_dev = pmp->pm_dev; 240 ldep->de_dirclust = dkey.dk_dirclust; 241 ldep->de_diroffset = dkey.dk_diroffset; 242 ldep->de_pmp = pmp; 243 ldep->de_devvp = pmp->pm_devvp; 244 ldep->de_refcnt = 1; 245 fc_purge(ldep, 0); /* init the FAT cache for this denode */ 246 247 /* 248 * Copy the directory entry into the denode area of the vnode. 249 */ 250 if (is_root) { 251 /* 252 * Directory entry for the root directory. There isn't one, 253 * so we manufacture one. We should probably rummage 254 * through the root directory and find a label entry (if it 255 * exists), and then use the time and date from that entry 256 * as the time and date for the root denode. 257 */ 258 ldep->de_Attributes = ATTR_DIRECTORY; 259 if (FAT32(pmp)) 260 ldep->de_StartCluster = pmp->pm_rootdirblk; 261 /* de_FileSize will be filled in further down */ 262 else { 263 ldep->de_StartCluster = MSDOSFSROOT; 264 ldep->de_FileSize = pmp->pm_rootdirsize * 265 pmp->pm_BytesPerSec; 266 } 267 /* 268 * fill in time and date so that dos2unixtime() doesn't 269 * spit up when called from msdosfs_getattr() with root 270 * denode 271 */ 272 ldep->de_CHun = 0; 273 ldep->de_CTime = 0x0000; /* 00:00:00 */ 274 ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) 275 | (1 << DD_DAY_SHIFT); 276 /* Jan 1, 1980 */ 277 ldep->de_ADate = ldep->de_CDate; 278 ldep->de_MTime = ldep->de_CTime; 279 ldep->de_MDate = ldep->de_CDate; 280 /* leave the other fields as garbage */ 281 } else { 282 error = readep(pmp, ldep->de_dirclust, ldep->de_diroffset, 283 &bp, &direntptr); 284 if (error) { 285 pool_put(&msdosfs_denode_pool, ldep); 286 return error; 287 } 288 DE_INTERNALIZE(ldep, direntptr); 289 brelse(bp, 0); 290 } 291 292 /* 293 * Fill in a few fields of the vnode and finish filling in the 294 * denode. 295 */ 296 if (ldep->de_Attributes & ATTR_DIRECTORY) { 297 /* 298 * Since DOS directory entries that describe directories 299 * have 0 in the filesize field, we take this opportunity 300 * to find out the length of the directory and plug it into 301 * the denode structure. 302 */ 303 u_long size; 304 305 vp->v_type = VDIR; 306 if (ldep->de_StartCluster != MSDOSFSROOT) { 307 error = pcbmap(ldep, CLUST_END, 0, &size, 0); 308 if (error == E2BIG) { 309 ldep->de_FileSize = de_cn2off(pmp, size); 310 error = 0; 311 } else 312 printf("loadvnode(): pcbmap returned %d\n", 313 error); 314 } 315 } else 316 vp->v_type = VREG; 317 vref(ldep->de_devvp); 318 if (is_root) 319 vp->v_vflag |= VV_ROOT; 320 vp->v_tag = VT_MSDOSFS; 321 vp->v_op = msdosfs_vnodeop_p; 322 vp->v_data = ldep; 323 ldep->de_vnode = vp; 324 genfs_node_init(vp, &msdosfs_genfsops); 325 uvm_vnp_setsize(vp, ldep->de_FileSize); 326 *new_key = &ldep->de_key; 327 328 return 0; 329 } 330 331 int 332 deupdat(struct denode *dep, int waitfor) 333 { 334 335 return (msdosfs_update(DETOV(dep), NULL, NULL, 336 waitfor ? UPDATE_WAIT : 0)); 337 } 338 339 /* 340 * Truncate the file described by dep to the length specified by length. 341 */ 342 int 343 detrunc(struct denode *dep, u_long length, int flags, kauth_cred_t cred) 344 { 345 int error; 346 int allerror; 347 u_long eofentry; 348 u_long chaintofree = 0; 349 daddr_t bn, lastblock; 350 int boff; 351 int isadir = dep->de_Attributes & ATTR_DIRECTORY; 352 struct buf *bp; 353 struct msdosfsmount *pmp = dep->de_pmp; 354 355 #ifdef MSDOSFS_DEBUG 356 printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); 357 #endif 358 359 /* 360 * Disallow attempts to truncate the root directory since it is of 361 * fixed size. That's just the way dos filesystems are. We use 362 * the VROOT bit in the vnode because checking for the directory 363 * bit and a startcluster of 0 in the denode is not adequate to 364 * recognize the root directory at this point in a file or 365 * directory's life. 366 */ 367 if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) { 368 printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", 369 dep->de_dirclust, dep->de_diroffset); 370 return (EINVAL); 371 } 372 373 uvm_vnp_setsize(DETOV(dep), length); 374 375 if (dep->de_FileSize < length) 376 return (deextend(dep, length, cred)); 377 lastblock = de_clcount(pmp, length) - 1; 378 379 /* 380 * If the desired length is 0 then remember the starting cluster of 381 * the file and set the StartCluster field in the directory entry 382 * to 0. If the desired length is not zero, then get the number of 383 * the last cluster in the shortened file. Then get the number of 384 * the first cluster in the part of the file that is to be freed. 385 * Then set the next cluster pointer in the last cluster of the 386 * file to CLUST_EOFE. 387 */ 388 if (length == 0) { 389 chaintofree = dep->de_StartCluster; 390 dep->de_StartCluster = 0; 391 eofentry = ~0; 392 } else { 393 error = pcbmap(dep, lastblock, 0, &eofentry, 0); 394 if (error) { 395 #ifdef MSDOSFS_DEBUG 396 printf("detrunc(): pcbmap fails %d\n", error); 397 #endif 398 return (error); 399 } 400 } 401 402 /* 403 * If the new length is not a multiple of the cluster size then we 404 * must zero the tail end of the new last cluster in case it 405 * becomes part of the file again because of a seek. 406 */ 407 if ((boff = length & pmp->pm_crbomask) != 0) { 408 if (isadir) { 409 bn = cntobn(pmp, eofentry); 410 error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), 411 pmp->pm_bpcluster, B_MODIFY, &bp); 412 if (error) { 413 #ifdef MSDOSFS_DEBUG 414 printf("detrunc(): bread fails %d\n", error); 415 #endif 416 return (error); 417 } 418 memset((char *)bp->b_data + boff, 0, 419 pmp->pm_bpcluster - boff); 420 if (flags & IO_SYNC) 421 bwrite(bp); 422 else 423 bdwrite(bp); 424 } else { 425 ubc_zerorange(&DETOV(dep)->v_uobj, length, 426 pmp->pm_bpcluster - boff, 427 UBC_UNMAP_FLAG(DETOV(dep))); 428 } 429 } 430 431 /* 432 * Write out the updated directory entry. Even if the update fails 433 * we free the trailing clusters. 434 */ 435 dep->de_FileSize = length; 436 if (!isadir) 437 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 438 vtruncbuf(DETOV(dep), lastblock + 1, 0, 0); 439 allerror = deupdat(dep, 1); 440 #ifdef MSDOSFS_DEBUG 441 printf("detrunc(): allerror %d, eofentry %lu\n", 442 allerror, eofentry); 443 #endif 444 445 fc_purge(dep, lastblock + 1); 446 447 /* 448 * If we need to break the cluster chain for the file then do it 449 * now. 450 */ 451 if (eofentry != ~0) { 452 error = fatentry(FAT_GET_AND_SET, pmp, eofentry, 453 &chaintofree, CLUST_EOFE); 454 if (error) { 455 #ifdef MSDOSFS_DEBUG 456 printf("detrunc(): fatentry errors %d\n", error); 457 #endif 458 return (error); 459 } 460 fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1), 461 eofentry); 462 } 463 464 /* 465 * Now free the clusters removed from the file because of the 466 * truncation. 467 */ 468 if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask)) 469 freeclusterchain(pmp, chaintofree); 470 471 return (allerror); 472 } 473 474 /* 475 * Extend the file described by dep to length specified by length. 476 */ 477 int 478 deextend(struct denode *dep, u_long length, kauth_cred_t cred) 479 { 480 struct msdosfsmount *pmp = dep->de_pmp; 481 u_long count, osize; 482 int error; 483 484 /* 485 * The root of a DOS filesystem cannot be extended. 486 */ 487 if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) 488 return (EINVAL); 489 490 /* 491 * Directories cannot be extended. 492 */ 493 if (dep->de_Attributes & ATTR_DIRECTORY) 494 return (EISDIR); 495 496 if (length <= dep->de_FileSize) 497 panic("deextend: file too large"); 498 499 /* 500 * Compute the number of clusters to allocate. 501 */ 502 count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); 503 if (count > 0) { 504 if (count > pmp->pm_freeclustercount) 505 return (ENOSPC); 506 error = extendfile(dep, count, NULL, NULL, DE_CLEAR); 507 if (error) { 508 /* truncate the added clusters away again */ 509 (void) detrunc(dep, dep->de_FileSize, 0, cred); 510 return (error); 511 } 512 } 513 514 /* 515 * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a 516 * memset(); we set the write size so ubc won't read in file data that 517 * is zero'd later. 518 */ 519 osize = dep->de_FileSize; 520 dep->de_FileSize = length; 521 uvm_vnp_setwritesize(DETOV(dep), (voff_t)dep->de_FileSize); 522 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 523 ubc_zerorange(&DETOV(dep)->v_uobj, (off_t)osize, 524 (size_t)(round_page(dep->de_FileSize) - osize), 525 UBC_UNMAP_FLAG(DETOV(dep))); 526 uvm_vnp_setsize(DETOV(dep), (voff_t)dep->de_FileSize); 527 return (deupdat(dep, 1)); 528 } 529 530 int 531 msdosfs_reclaim(void *v) 532 { 533 struct vop_reclaim_v2_args /* { 534 struct vnode *a_vp; 535 } */ *ap = v; 536 struct vnode *vp = ap->a_vp; 537 struct denode *dep = VTODE(vp); 538 539 VOP_UNLOCK(vp); 540 541 #ifdef MSDOSFS_DEBUG 542 printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n", 543 dep, dep->de_Name, dep->de_refcnt); 544 #endif 545 546 /* 547 * Purge old data structures associated with the denode. 548 */ 549 if (dep->de_devvp) { 550 vrele(dep->de_devvp); 551 dep->de_devvp = 0; 552 } 553 #if 0 /* XXX */ 554 dep->de_flag = 0; 555 #endif 556 /* 557 * To interlock with msdosfs_sync(). 558 */ 559 genfs_node_destroy(vp); 560 mutex_enter(vp->v_interlock); 561 vp->v_data = NULL; 562 mutex_exit(vp->v_interlock); 563 pool_put(&msdosfs_denode_pool, dep); 564 return (0); 565 } 566 567 int 568 msdosfs_inactive(void *v) 569 { 570 struct vop_inactive_v2_args /* { 571 struct vnode *a_vp; 572 bool *a_recycle; 573 } */ *ap = v; 574 struct vnode *vp = ap->a_vp; 575 struct denode *dep = VTODE(vp); 576 int error = 0; 577 578 #ifdef MSDOSFS_DEBUG 579 printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep, dep->de_Name[0]); 580 #endif 581 582 /* 583 * Get rid of denodes related to stale file handles. 584 */ 585 if (dep->de_Name[0] == SLOT_DELETED) 586 goto out; 587 588 /* 589 * If the file has been deleted and it is on a read/write 590 * filesystem, then truncate the file, and mark the directory slot 591 * as empty. (This may not be necessary for the dos filesystem.) 592 */ 593 #ifdef MSDOSFS_DEBUG 594 printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %x %s\n", 595 dep, dep->de_refcnt, vp->v_mount->mnt_flag, 596 (vp->v_mount->mnt_flag & MNT_RDONLY) ? "MNT_RDONLY" : ""); 597 #endif 598 if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 599 if (dep->de_FileSize != 0) { 600 error = detrunc(dep, (u_long)0, 0, NOCRED); 601 } 602 dep->de_Name[0] = SLOT_DELETED; 603 msdosfs_fh_remove(dep->de_pmp, 604 dep->de_dirclust, dep->de_diroffset); 605 } 606 deupdat(dep, 0); 607 out: 608 /* 609 * If we are done with the denode, reclaim it 610 * so that it can be reused immediately. 611 */ 612 #ifdef MSDOSFS_DEBUG 613 printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", 614 vp->v_usecount, dep->de_Name[0]); 615 #endif 616 *ap->a_recycle = (dep->de_Name[0] == SLOT_DELETED); 617 618 return (error); 619 } 620 621 int 622 msdosfs_gop_alloc(struct vnode *vp, off_t off, 623 off_t len, int flags, kauth_cred_t cred) 624 { 625 return 0; 626 } 627 628 void 629 msdosfs_gop_markupdate(struct vnode *vp, int flags) 630 { 631 u_long mask = 0; 632 633 if ((flags & GOP_UPDATE_ACCESSED) != 0) { 634 mask = DE_ACCESS; 635 } 636 if ((flags & GOP_UPDATE_MODIFIED) != 0) { 637 mask |= DE_UPDATE; 638 } 639 if (mask) { 640 struct denode *dep = VTODE(vp); 641 642 dep->de_flag |= mask; 643 } 644 } 645 646 int 647 msdosfs_fh_enter(struct msdosfsmount *pmp, 648 uint32_t dircluster, uint32_t diroffset, uint32_t *genp) 649 { 650 struct fh_key fhkey; 651 struct fh_node *fhp; 652 653 fhkey.fhk_mount = pmp; 654 fhkey.fhk_dircluster = dircluster; 655 fhkey.fhk_diroffset = diroffset; 656 657 mutex_enter(&fh_lock); 658 fhp = rb_tree_find_node(&fh_rbtree, &fhkey); 659 if (fhp == NULL) { 660 mutex_exit(&fh_lock); 661 fhp = pool_get(&fh_pool, PR_WAITOK); 662 mutex_enter(&fh_lock); 663 fhp->fh_key = fhkey; 664 fhp->fh_gen = fh_generation++; 665 rb_tree_insert_node(&fh_rbtree, fhp); 666 } 667 *genp = fhp->fh_gen; 668 mutex_exit(&fh_lock); 669 return 0; 670 } 671 672 int 673 msdosfs_fh_remove(struct msdosfsmount *pmp, 674 uint32_t dircluster, uint32_t diroffset) 675 { 676 struct fh_key fhkey; 677 struct fh_node *fhp; 678 679 fhkey.fhk_mount = pmp; 680 fhkey.fhk_dircluster = dircluster; 681 fhkey.fhk_diroffset = diroffset; 682 683 mutex_enter(&fh_lock); 684 fhp = rb_tree_find_node(&fh_rbtree, &fhkey); 685 if (fhp == NULL) { 686 mutex_exit(&fh_lock); 687 return ENOENT; 688 } 689 rb_tree_remove_node(&fh_rbtree, fhp); 690 mutex_exit(&fh_lock); 691 pool_put(&fh_pool, fhp); 692 return 0; 693 } 694 695 int 696 msdosfs_fh_lookup(struct msdosfsmount *pmp, 697 uint32_t dircluster, uint32_t diroffset, uint32_t *genp) 698 { 699 struct fh_key fhkey; 700 struct fh_node *fhp; 701 702 fhkey.fhk_mount = pmp; 703 fhkey.fhk_dircluster = dircluster; 704 fhkey.fhk_diroffset = diroffset; 705 706 mutex_enter(&fh_lock); 707 fhp = rb_tree_find_node(&fh_rbtree, &fhkey); 708 if (fhp == NULL) { 709 mutex_exit(&fh_lock); 710 return ESTALE; 711 } 712 *genp = fhp->fh_gen; 713 mutex_exit(&fh_lock); 714 return 0; 715 } 716 717 void 718 msdosfs_fh_destroy(struct msdosfsmount *pmp) 719 { 720 struct fh_key fhkey; 721 struct fh_node *fhp, *nfhp; 722 723 fhkey.fhk_mount = pmp; 724 fhkey.fhk_dircluster = 0; 725 fhkey.fhk_diroffset = 0; 726 727 mutex_enter(&fh_lock); 728 for (fhp = rb_tree_find_node_geq(&fh_rbtree, &fhkey); 729 fhp != NULL && fhp->fh_mount == pmp; fhp = nfhp) { 730 nfhp = rb_tree_iterate(&fh_rbtree, fhp, RB_DIR_RIGHT); 731 rb_tree_remove_node(&fh_rbtree, fhp); 732 pool_put(&fh_pool, fhp); 733 } 734 #ifdef DIAGNOSTIC 735 RB_TREE_FOREACH(fhp, &fh_rbtree) { 736 KASSERT(fhp->fh_mount != pmp); 737 } 738 #endif 739 mutex_exit(&fh_lock); 740 } 741