1 /* $NetBSD: msdosfs_denode.c,v 1.60 2021/10/23 16:58:17 thorpej 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.60 2021/10/23 16:58:17 thorpej 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 .gop_putrange = genfs_gop_putrange, 129 }; 130 131 MALLOC_DECLARE(M_MSDOSFSFAT); 132 133 void 134 msdosfs_init(void) 135 { 136 137 malloc_type_attach(M_MSDOSFSMNT); 138 malloc_type_attach(M_MSDOSFSFAT); 139 malloc_type_attach(M_MSDOSFSTMP); 140 pool_init(&msdosfs_denode_pool, sizeof(struct denode), 0, 0, 0, 141 "msdosnopl", &pool_allocator_nointr, IPL_NONE); 142 pool_init(&fh_pool, sizeof(struct fh_node), 0, 0, 0, 143 "msdosfhpl", &pool_allocator_nointr, IPL_NONE); 144 rb_tree_init(&fh_rbtree, &fh_rbtree_ops); 145 mutex_init(&fh_lock, MUTEX_DEFAULT, IPL_NONE); 146 } 147 148 /* 149 * Reinitialize. 150 */ 151 152 void 153 msdosfs_reinit(void) 154 { 155 156 } 157 158 void 159 msdosfs_done(void) 160 { 161 pool_destroy(&msdosfs_denode_pool); 162 pool_destroy(&fh_pool); 163 mutex_destroy(&fh_lock); 164 malloc_type_detach(M_MSDOSFSTMP); 165 malloc_type_detach(M_MSDOSFSFAT); 166 malloc_type_detach(M_MSDOSFSMNT); 167 } 168 169 /* 170 * If deget() succeeds it returns with the gotten denode unlocked. 171 * 172 * pmp - address of msdosfsmount structure of the filesystem containing 173 * the denode of interest. The pm_dev field and the address of 174 * the msdosfsmount structure are used. 175 * dirclust - which cluster bp contains, if dirclust is 0 (root directory) 176 * diroffset is relative to the beginning of the root directory, 177 * otherwise it is cluster relative. 178 * diroffset - offset past begin of cluster of denode we want 179 * vpp - returns the address of the gotten vnode. 180 */ 181 int 182 msdosfs_deget(struct msdosfsmount *pmp, u_long dirclust, u_long diroffset, 183 struct vnode **vpp) 184 /* pmp: so we know the maj/min number */ 185 /* dirclust: cluster this dir entry came from */ 186 /* diroffset: index of entry within the cluster */ 187 /* vpp: returns the addr of the gotten vnode */ 188 { 189 int error; 190 struct denode_key key; 191 192 /* 193 * On FAT32 filesystems, root is a (more or less) normal 194 * directory 195 */ 196 if (FAT32(pmp) && dirclust == MSDOSFSROOT) 197 dirclust = pmp->pm_rootdirblk; 198 199 memset(&key, 0, sizeof(key)); 200 key.dk_dirclust = dirclust; 201 key.dk_diroffset = diroffset; 202 /* key.dk_dirgen = NULL; */ 203 204 error = vcache_get(pmp->pm_mountp, &key, sizeof(key), vpp); 205 return error; 206 } 207 208 int 209 msdosfs_loadvnode(struct mount *mp, struct vnode *vp, 210 const void *key, size_t key_len, const void **new_key) 211 { 212 bool is_root; 213 int error; 214 extern int (**msdosfs_vnodeop_p)(void *); 215 struct msdosfsmount *pmp; 216 struct direntry *direntptr; 217 struct denode *ldep; 218 struct buf *bp; 219 struct denode_key dkey; 220 221 KASSERT(key_len == sizeof(dkey)); 222 memcpy(&dkey, key, key_len); 223 KASSERT(dkey.dk_dirgen == NULL); 224 225 pmp = VFSTOMSDOSFS(mp); 226 is_root = ((dkey.dk_dirclust == MSDOSFSROOT || 227 (FAT32(pmp) && dkey.dk_dirclust == pmp->pm_rootdirblk)) && 228 dkey.dk_diroffset == MSDOSFSROOT_OFS); 229 230 #ifdef MSDOSFS_DEBUG 231 printf("loadvnode(pmp %p, dirclust %lu, diroffset %lx, vp %p)\n", 232 pmp, dkey.dk_dirclust, dkey.dk_diroffset, vp); 233 #endif 234 235 ldep = pool_get(&msdosfs_denode_pool, PR_WAITOK); 236 memset(ldep, 0, sizeof *ldep); 237 /* ldep->de_flag = 0; */ 238 /* ldep->de_devvp = 0; */ 239 /* ldep->de_lockf = 0; */ 240 ldep->de_dev = pmp->pm_dev; 241 ldep->de_dirclust = dkey.dk_dirclust; 242 ldep->de_diroffset = dkey.dk_diroffset; 243 ldep->de_pmp = pmp; 244 ldep->de_devvp = pmp->pm_devvp; 245 ldep->de_refcnt = 1; 246 msdosfs_fc_purge(ldep, 0); /* init the FAT cache for this denode */ 247 248 /* 249 * Copy the directory entry into the denode area of the vnode. 250 */ 251 if (is_root) { 252 /* 253 * Directory entry for the root directory. There isn't one, 254 * so we manufacture one. We should probably rummage 255 * through the root directory and find a label entry (if it 256 * exists), and then use the time and date from that entry 257 * as the time and date for the root denode. 258 */ 259 ldep->de_Attributes = ATTR_DIRECTORY; 260 if (FAT32(pmp)) 261 ldep->de_StartCluster = pmp->pm_rootdirblk; 262 /* de_FileSize will be filled in further down */ 263 else { 264 ldep->de_StartCluster = MSDOSFSROOT; 265 ldep->de_FileSize = pmp->pm_rootdirsize * 266 pmp->pm_BytesPerSec; 267 } 268 /* 269 * fill in time and date so that dos2unixtime() doesn't 270 * spit up when called from msdosfs_getattr() with root 271 * denode 272 */ 273 ldep->de_CHun = 0; 274 ldep->de_CTime = 0x0000; /* 00:00:00 */ 275 ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) 276 | (1 << DD_DAY_SHIFT); 277 /* Jan 1, 1980 */ 278 ldep->de_ADate = ldep->de_CDate; 279 ldep->de_MTime = ldep->de_CTime; 280 ldep->de_MDate = ldep->de_CDate; 281 /* leave the other fields as garbage */ 282 } else { 283 error = msdosfs_readep(pmp, ldep->de_dirclust, 284 ldep->de_diroffset, &bp, &direntptr); 285 if (error) { 286 pool_put(&msdosfs_denode_pool, ldep); 287 return error; 288 } 289 DE_INTERNALIZE(ldep, direntptr); 290 brelse(bp, 0); 291 } 292 293 /* 294 * Fill in a few fields of the vnode and finish filling in the 295 * denode. 296 */ 297 if (ldep->de_Attributes & ATTR_DIRECTORY) { 298 /* 299 * Since DOS directory entries that describe directories 300 * have 0 in the filesize field, we take this opportunity 301 * to find out the length of the directory and plug it into 302 * the denode structure. 303 */ 304 u_long size; 305 306 vp->v_type = VDIR; 307 if (ldep->de_StartCluster != MSDOSFSROOT) { 308 error = msdosfs_pcbmap(ldep, CLUST_END, 0, &size, 0); 309 if (error == E2BIG) { 310 ldep->de_FileSize = de_cn2off(pmp, size); 311 error = 0; 312 } else 313 printf("loadvnode(): pcbmap returned %d\n", 314 error); 315 } 316 } else 317 vp->v_type = VREG; 318 vref(ldep->de_devvp); 319 if (is_root) 320 vp->v_vflag |= VV_ROOT; 321 vp->v_tag = VT_MSDOSFS; 322 vp->v_op = msdosfs_vnodeop_p; 323 vp->v_data = ldep; 324 ldep->de_vnode = vp; 325 genfs_node_init(vp, &msdosfs_genfsops); 326 uvm_vnp_setsize(vp, ldep->de_FileSize); 327 *new_key = &ldep->de_key; 328 329 return 0; 330 } 331 332 int 333 msdosfs_deupdat(struct denode *dep, int waitfor) 334 { 335 336 return (msdosfs_update(DETOV(dep), NULL, NULL, 337 waitfor ? UPDATE_WAIT : 0)); 338 } 339 340 /* 341 * Truncate the file described by dep to the length specified by length. 342 */ 343 int 344 msdosfs_detrunc(struct denode *dep, u_long length, int flags, kauth_cred_t cred) 345 { 346 int error; 347 int allerror; 348 u_long eofentry; 349 u_long chaintofree = 0; 350 daddr_t bn, lastblock; 351 int boff; 352 int isadir = dep->de_Attributes & ATTR_DIRECTORY; 353 struct buf *bp; 354 struct msdosfsmount *pmp = dep->de_pmp; 355 356 #ifdef MSDOSFS_DEBUG 357 printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); 358 #endif 359 360 /* 361 * Disallow attempts to truncate the root directory since it is of 362 * fixed size. That's just the way dos filesystems are. We use 363 * the VROOT bit in the vnode because checking for the directory 364 * bit and a startcluster of 0 in the denode is not adequate to 365 * recognize the root directory at this point in a file or 366 * directory's life. 367 */ 368 if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) { 369 printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", 370 dep->de_dirclust, dep->de_diroffset); 371 return (EINVAL); 372 } 373 374 uvm_vnp_setsize(DETOV(dep), length); 375 376 if (dep->de_FileSize < length) 377 return (msdosfs_deextend(dep, length, cred)); 378 lastblock = de_clcount(pmp, length) - 1; 379 380 /* 381 * If the desired length is 0 then remember the starting cluster of 382 * the file and set the StartCluster field in the directory entry 383 * to 0. If the desired length is not zero, then get the number of 384 * the last cluster in the shortened file. Then get the number of 385 * the first cluster in the part of the file that is to be freed. 386 * Then set the next cluster pointer in the last cluster of the 387 * file to CLUST_EOFE. 388 */ 389 if (length == 0) { 390 chaintofree = dep->de_StartCluster; 391 dep->de_StartCluster = 0; 392 eofentry = ~0; 393 } else { 394 error = msdosfs_pcbmap(dep, lastblock, 0, &eofentry, 0); 395 if (error) { 396 #ifdef MSDOSFS_DEBUG 397 printf("detrunc(): pcbmap fails %d\n", error); 398 #endif 399 return (error); 400 } 401 } 402 403 /* 404 * If the new length is not a multiple of the cluster size then we 405 * must zero the tail end of the new last cluster in case it 406 * becomes part of the file again because of a seek. 407 */ 408 if ((boff = length & pmp->pm_crbomask) != 0) { 409 if (isadir) { 410 bn = cntobn(pmp, eofentry); 411 error = bread(pmp->pm_devvp, de_bn2kb(pmp, bn), 412 pmp->pm_bpcluster, B_MODIFY, &bp); 413 if (error) { 414 #ifdef MSDOSFS_DEBUG 415 printf("detrunc(): bread fails %d\n", error); 416 #endif 417 return (error); 418 } 419 memset((char *)bp->b_data + boff, 0, 420 pmp->pm_bpcluster - boff); 421 if (flags & IO_SYNC) 422 bwrite(bp); 423 else 424 bdwrite(bp); 425 } else { 426 ubc_zerorange(&DETOV(dep)->v_uobj, length, 427 pmp->pm_bpcluster - boff, 428 UBC_VNODE_FLAGS(DETOV(dep))); 429 } 430 } 431 432 /* 433 * Write out the updated directory entry. Even if the update fails 434 * we free the trailing clusters. 435 */ 436 dep->de_FileSize = length; 437 if (!isadir) 438 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 439 vtruncbuf(DETOV(dep), lastblock + 1, 0, 0); 440 allerror = msdosfs_deupdat(dep, 1); 441 #ifdef MSDOSFS_DEBUG 442 printf("detrunc(): allerror %d, eofentry %lu\n", 443 allerror, eofentry); 444 #endif 445 446 msdosfs_fc_purge(dep, lastblock + 1); 447 448 /* 449 * If we need to break the cluster chain for the file then do it 450 * now. 451 */ 452 if (eofentry != ~0) { 453 error = msdosfs_fatentry(FAT_GET_AND_SET, pmp, eofentry, 454 &chaintofree, CLUST_EOFE); 455 if (error) { 456 #ifdef MSDOSFS_DEBUG 457 printf("detrunc(): fatentry errors %d\n", error); 458 #endif 459 return (error); 460 } 461 fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1), 462 eofentry); 463 } 464 465 /* 466 * Now free the clusters removed from the file because of the 467 * truncation. 468 */ 469 if (chaintofree != 0 && !MSDOSFSEOF(chaintofree, pmp->pm_fatmask)) 470 msdosfs_freeclusterchain(pmp, chaintofree); 471 472 return (allerror); 473 } 474 475 /* 476 * Extend the file described by dep to length specified by length. 477 */ 478 int 479 msdosfs_deextend(struct denode *dep, u_long length, kauth_cred_t cred) 480 { 481 struct msdosfsmount *pmp = dep->de_pmp; 482 u_long count, osize; 483 int error; 484 485 /* 486 * The root of a DOS filesystem cannot be extended. 487 */ 488 if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) 489 return (EINVAL); 490 491 /* 492 * Directories cannot be extended. 493 */ 494 if (dep->de_Attributes & ATTR_DIRECTORY) 495 return (EISDIR); 496 497 if (length <= dep->de_FileSize) 498 panic("deextend: file too large"); 499 500 /* 501 * Compute the number of clusters to allocate. 502 */ 503 count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); 504 if (count > 0) { 505 if (count > pmp->pm_freeclustercount) 506 return (ENOSPC); 507 error = msdosfs_extendfile(dep, count, NULL, NULL, DE_CLEAR); 508 if (error) { 509 /* truncate the added clusters away again */ 510 (void) msdosfs_detrunc(dep, dep->de_FileSize, 0, cred); 511 return (error); 512 } 513 } 514 515 /* 516 * Zero extend file range; ubc_zerorange() uses ubc_alloc() and a 517 * memset(); we set the write size so ubc won't read in file data that 518 * is zero'd later. 519 */ 520 osize = dep->de_FileSize; 521 dep->de_FileSize = length; 522 uvm_vnp_setwritesize(DETOV(dep), (voff_t)dep->de_FileSize); 523 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 524 ubc_zerorange(&DETOV(dep)->v_uobj, (off_t)osize, 525 (size_t)(round_page(dep->de_FileSize) - osize), 526 UBC_VNODE_FLAGS(DETOV(dep))); 527 uvm_vnp_setsize(DETOV(dep), (voff_t)dep->de_FileSize); 528 return (msdosfs_deupdat(dep, 1)); 529 } 530 531 int 532 msdosfs_reclaim(void *v) 533 { 534 struct vop_reclaim_v2_args /* { 535 struct vnode *a_vp; 536 } */ *ap = v; 537 struct vnode *vp = ap->a_vp; 538 struct denode *dep = VTODE(vp); 539 540 VOP_UNLOCK(vp); 541 542 #ifdef MSDOSFS_DEBUG 543 printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n", 544 dep, dep->de_Name, dep->de_refcnt); 545 #endif 546 547 /* 548 * Purge old data structures associated with the denode. 549 */ 550 if (dep->de_devvp) { 551 vrele(dep->de_devvp); 552 dep->de_devvp = 0; 553 } 554 #if 0 /* XXX */ 555 dep->de_flag = 0; 556 #endif 557 /* 558 * To interlock with msdosfs_sync(). 559 */ 560 genfs_node_destroy(vp); 561 mutex_enter(vp->v_interlock); 562 vp->v_data = NULL; 563 mutex_exit(vp->v_interlock); 564 pool_put(&msdosfs_denode_pool, dep); 565 return (0); 566 } 567 568 int 569 msdosfs_inactive(void *v) 570 { 571 struct vop_inactive_v2_args /* { 572 struct vnode *a_vp; 573 bool *a_recycle; 574 } */ *ap = v; 575 struct vnode *vp = ap->a_vp; 576 struct denode *dep = VTODE(vp); 577 int error = 0; 578 579 #ifdef MSDOSFS_DEBUG 580 printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep, dep->de_Name[0]); 581 #endif 582 583 /* 584 * Get rid of denodes related to stale file handles. 585 */ 586 if (dep->de_Name[0] == SLOT_DELETED) 587 goto out; 588 589 /* 590 * If the file has been deleted and it is on a read/write 591 * filesystem, then truncate the file, and mark the directory slot 592 * as empty. (This may not be necessary for the dos filesystem.) 593 */ 594 #ifdef MSDOSFS_DEBUG 595 printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %x %s\n", 596 dep, dep->de_refcnt, vp->v_mount->mnt_flag, 597 (vp->v_mount->mnt_flag & MNT_RDONLY) ? "MNT_RDONLY" : ""); 598 #endif 599 if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 600 if (dep->de_FileSize != 0) { 601 error = msdosfs_detrunc(dep, (u_long)0, 0, NOCRED); 602 } 603 dep->de_Name[0] = SLOT_DELETED; 604 msdosfs_fh_remove(dep->de_pmp, 605 dep->de_dirclust, dep->de_diroffset); 606 } 607 msdosfs_deupdat(dep, 0); 608 out: 609 /* 610 * If we are done with the denode, reclaim it 611 * so that it can be reused immediately. 612 */ 613 #ifdef MSDOSFS_DEBUG 614 printf("msdosfs_inactive(): usecount %d, de_Name[0] %x\n", 615 vrefcnt(vp), dep->de_Name[0]); 616 #endif 617 *ap->a_recycle = (dep->de_Name[0] == SLOT_DELETED); 618 619 return (error); 620 } 621 622 int 623 msdosfs_gop_alloc(struct vnode *vp, off_t off, 624 off_t len, int flags, kauth_cred_t cred) 625 { 626 return 0; 627 } 628 629 void 630 msdosfs_gop_markupdate(struct vnode *vp, int flags) 631 { 632 u_long mask = 0; 633 634 if ((flags & GOP_UPDATE_ACCESSED) != 0) { 635 mask = DE_ACCESS; 636 } 637 if ((flags & GOP_UPDATE_MODIFIED) != 0) { 638 mask |= DE_UPDATE; 639 } 640 if (mask) { 641 struct denode *dep = VTODE(vp); 642 643 dep->de_flag |= mask; 644 } 645 } 646 647 int 648 msdosfs_fh_enter(struct msdosfsmount *pmp, 649 uint32_t dircluster, uint32_t diroffset, uint32_t *genp) 650 { 651 struct fh_key fhkey; 652 struct fh_node *fhp; 653 654 fhkey.fhk_mount = pmp; 655 fhkey.fhk_dircluster = dircluster; 656 fhkey.fhk_diroffset = diroffset; 657 658 mutex_enter(&fh_lock); 659 fhp = rb_tree_find_node(&fh_rbtree, &fhkey); 660 if (fhp == NULL) { 661 mutex_exit(&fh_lock); 662 fhp = pool_get(&fh_pool, PR_WAITOK); 663 mutex_enter(&fh_lock); 664 fhp->fh_key = fhkey; 665 fhp->fh_gen = fh_generation++; 666 rb_tree_insert_node(&fh_rbtree, fhp); 667 } 668 *genp = fhp->fh_gen; 669 mutex_exit(&fh_lock); 670 return 0; 671 } 672 673 int 674 msdosfs_fh_remove(struct msdosfsmount *pmp, 675 uint32_t dircluster, uint32_t diroffset) 676 { 677 struct fh_key fhkey; 678 struct fh_node *fhp; 679 680 fhkey.fhk_mount = pmp; 681 fhkey.fhk_dircluster = dircluster; 682 fhkey.fhk_diroffset = diroffset; 683 684 mutex_enter(&fh_lock); 685 fhp = rb_tree_find_node(&fh_rbtree, &fhkey); 686 if (fhp == NULL) { 687 mutex_exit(&fh_lock); 688 return ENOENT; 689 } 690 rb_tree_remove_node(&fh_rbtree, fhp); 691 mutex_exit(&fh_lock); 692 pool_put(&fh_pool, fhp); 693 return 0; 694 } 695 696 int 697 msdosfs_fh_lookup(struct msdosfsmount *pmp, 698 uint32_t dircluster, uint32_t diroffset, uint32_t *genp) 699 { 700 struct fh_key fhkey; 701 struct fh_node *fhp; 702 703 fhkey.fhk_mount = pmp; 704 fhkey.fhk_dircluster = dircluster; 705 fhkey.fhk_diroffset = diroffset; 706 707 mutex_enter(&fh_lock); 708 fhp = rb_tree_find_node(&fh_rbtree, &fhkey); 709 if (fhp == NULL) { 710 mutex_exit(&fh_lock); 711 return ESTALE; 712 } 713 *genp = fhp->fh_gen; 714 mutex_exit(&fh_lock); 715 return 0; 716 } 717 718 void 719 msdosfs_fh_destroy(struct msdosfsmount *pmp) 720 { 721 struct fh_key fhkey; 722 struct fh_node *fhp, *nfhp; 723 724 fhkey.fhk_mount = pmp; 725 fhkey.fhk_dircluster = 0; 726 fhkey.fhk_diroffset = 0; 727 728 mutex_enter(&fh_lock); 729 for (fhp = rb_tree_find_node_geq(&fh_rbtree, &fhkey); 730 fhp != NULL && fhp->fh_mount == pmp; fhp = nfhp) { 731 nfhp = rb_tree_iterate(&fh_rbtree, fhp, RB_DIR_RIGHT); 732 rb_tree_remove_node(&fh_rbtree, fhp); 733 pool_put(&fh_pool, fhp); 734 } 735 #ifdef DIAGNOSTIC 736 RB_TREE_FOREACH(fhp, &fh_rbtree) { 737 KASSERT(fhp->fh_mount != pmp); 738 } 739 #endif 740 mutex_exit(&fh_lock); 741 } 742