1 /* $FreeBSD$ */ 2 /* $NetBSD: msdosfs_denode.c,v 1.28 1998/02/10 14:10:00 mrg Exp $ */ 3 4 /*- 5 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 6 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 7 * All rights reserved. 8 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by TooLs GmbH. 21 * 4. The name of TooLs GmbH may not be used to endorse or promote products 22 * derived from this software without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 25 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 28 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 29 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 30 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 31 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 32 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 33 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 */ 35 /*- 36 * Written by Paul Popelka (paulp@uts.amdahl.com) 37 * 38 * You can do anything you want with this software, just don't say you wrote 39 * it, and don't remove this notice. 40 * 41 * This software is provided "as is". 42 * 43 * The author supplies this software to be publicly redistributed on the 44 * understanding that the author is not responsible for the correct 45 * functioning of this software in any circumstances and is not liable for 46 * any damages caused by this software. 47 * 48 * October 1992 49 */ 50 51 #include <sys/param.h> 52 #include <sys/systm.h> 53 #include <sys/kernel.h> 54 #include <sys/mount.h> 55 #include <sys/malloc.h> 56 #include <sys/bio.h> 57 #include <sys/buf.h> 58 #include <sys/clock.h> 59 #include <sys/vnode.h> 60 #include <sys/mutex.h> 61 62 #include <vm/vm.h> 63 #include <vm/vm_extern.h> 64 65 #include <fs/msdosfs/bpb.h> 66 #include <fs/msdosfs/msdosfsmount.h> 67 #include <fs/msdosfs/direntry.h> 68 #include <fs/msdosfs/denode.h> 69 #include <fs/msdosfs/fat.h> 70 71 static MALLOC_DEFINE(M_MSDOSFSNODE, "msdosfs_node", "MSDOSFS vnode private part"); 72 73 static int 74 de_vncmpf(struct vnode *vp, void *arg) 75 { 76 struct denode *de; 77 uint64_t *a; 78 79 a = arg; 80 de = VTODE(vp); 81 return (de->de_inode != *a); 82 } 83 84 /* 85 * If deget() succeeds it returns with the gotten denode locked(). 86 * 87 * pmp - address of msdosfsmount structure of the filesystem containing 88 * the denode of interest. The address of 89 * the msdosfsmount structure are used. 90 * dirclust - which cluster bp contains, if dirclust is 0 (root directory) 91 * diroffset is relative to the beginning of the root directory, 92 * otherwise it is cluster relative. 93 * diroffset - offset past begin of cluster of denode we want 94 * depp - returns the address of the gotten denode. 95 */ 96 int 97 deget(pmp, dirclust, diroffset, depp) 98 struct msdosfsmount *pmp; /* so we know the maj/min number */ 99 u_long dirclust; /* cluster this dir entry came from */ 100 u_long diroffset; /* index of entry within the cluster */ 101 struct denode **depp; /* returns the addr of the gotten denode */ 102 { 103 int error; 104 uint64_t inode; 105 struct mount *mntp = pmp->pm_mountp; 106 struct direntry *direntptr; 107 struct denode *ldep; 108 struct vnode *nvp, *xvp; 109 struct buf *bp; 110 111 #ifdef MSDOSFS_DEBUG 112 printf("deget(pmp %p, dirclust %lu, diroffset %lx, depp %p)\n", 113 pmp, dirclust, diroffset, depp); 114 #endif 115 116 /* 117 * On FAT32 filesystems, root is a (more or less) normal 118 * directory 119 */ 120 if (FAT32(pmp) && dirclust == MSDOSFSROOT) 121 dirclust = pmp->pm_rootdirblk; 122 123 /* 124 * See if the denode is in the denode cache. Use the location of 125 * the directory entry to compute the hash value. For subdir use 126 * address of "." entry. For root dir (if not FAT32) use cluster 127 * MSDOSFSROOT, offset MSDOSFSROOT_OFS 128 * 129 * NOTE: The check for de_refcnt > 0 below insures the denode being 130 * examined does not represent an unlinked but still open file. 131 * These files are not to be accessible even when the directory 132 * entry that represented the file happens to be reused while the 133 * deleted file is still open. 134 */ 135 inode = (uint64_t)pmp->pm_bpcluster * dirclust + diroffset; 136 137 error = vfs_hash_get(mntp, inode, LK_EXCLUSIVE, curthread, &nvp, 138 de_vncmpf, &inode); 139 if (error) 140 return(error); 141 if (nvp != NULL) { 142 *depp = VTODE(nvp); 143 KASSERT((*depp)->de_dirclust == dirclust, ("wrong dirclust")); 144 KASSERT((*depp)->de_diroffset == diroffset, ("wrong diroffset")); 145 return (0); 146 } 147 148 /* 149 * Do the MALLOC before the getnewvnode since doing so afterward 150 * might cause a bogus v_data pointer to get dereferenced 151 * elsewhere if MALLOC should block. 152 */ 153 MALLOC(ldep, struct denode *, sizeof(struct denode), M_MSDOSFSNODE, M_WAITOK); 154 155 /* 156 * Directory entry was not in cache, have to create a vnode and 157 * copy it from the passed disk buffer. 158 */ 159 /* getnewvnode() does a VREF() on the vnode */ 160 error = getnewvnode("msdosfs", mntp, &msdosfs_vnodeops, &nvp); 161 if (error) { 162 *depp = NULL; 163 FREE(ldep, M_MSDOSFSNODE); 164 return error; 165 } 166 bzero((caddr_t)ldep, sizeof *ldep); 167 nvp->v_data = ldep; 168 ldep->de_vnode = nvp; 169 ldep->de_flag = 0; 170 ldep->de_dirclust = dirclust; 171 ldep->de_diroffset = diroffset; 172 ldep->de_inode = inode; 173 fc_purge(ldep, 0); /* init the fat cache for this denode */ 174 175 error = vfs_hash_insert(nvp, inode, LK_EXCLUSIVE, curthread, &xvp, 176 de_vncmpf, &inode); 177 if (error) { 178 *depp = NULL; 179 return (error); 180 } 181 if (xvp != NULL) { 182 /* XXX: Not sure this is right */ 183 nvp = xvp; 184 ldep->de_vnode = nvp; 185 } 186 187 ldep->de_pmp = pmp; 188 ldep->de_refcnt = 1; 189 /* 190 * Copy the directory entry into the denode area of the vnode. 191 */ 192 if ((dirclust == MSDOSFSROOT 193 || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) 194 && diroffset == MSDOSFSROOT_OFS) { 195 /* 196 * Directory entry for the root directory. There isn't one, 197 * so we manufacture one. We should probably rummage 198 * through the root directory and find a label entry (if it 199 * exists), and then use the time and date from that entry 200 * as the time and date for the root denode. 201 */ 202 nvp->v_vflag |= VV_ROOT; /* should be further down XXX */ 203 204 ldep->de_Attributes = ATTR_DIRECTORY; 205 ldep->de_LowerCase = 0; 206 if (FAT32(pmp)) 207 ldep->de_StartCluster = pmp->pm_rootdirblk; 208 /* de_FileSize will be filled in further down */ 209 else { 210 ldep->de_StartCluster = MSDOSFSROOT; 211 ldep->de_FileSize = pmp->pm_rootdirsize * DEV_BSIZE; 212 } 213 /* 214 * fill in time and date so that fattime2timespec() doesn't 215 * spit up when called from msdosfs_getattr() with root 216 * denode 217 */ 218 ldep->de_CHun = 0; 219 ldep->de_CTime = 0x0000; /* 00:00:00 */ 220 ldep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) 221 | (1 << DD_DAY_SHIFT); 222 /* Jan 1, 1980 */ 223 ldep->de_ADate = ldep->de_CDate; 224 ldep->de_MTime = ldep->de_CTime; 225 ldep->de_MDate = ldep->de_CDate; 226 /* leave the other fields as garbage */ 227 } else { 228 error = readep(pmp, dirclust, diroffset, &bp, &direntptr); 229 if (error) { 230 /* 231 * The denode does not contain anything useful, so 232 * it would be wrong to leave it on its hash chain. 233 * Arrange for vput() to just forget about it. 234 */ 235 ldep->de_Name[0] = SLOT_DELETED; 236 237 vput(nvp); 238 *depp = NULL; 239 return (error); 240 } 241 DE_INTERNALIZE(ldep, direntptr); 242 brelse(bp); 243 } 244 245 /* 246 * Fill in a few fields of the vnode and finish filling in the 247 * denode. Then return the address of the found denode. 248 */ 249 if (ldep->de_Attributes & ATTR_DIRECTORY) { 250 /* 251 * Since DOS directory entries that describe directories 252 * have 0 in the filesize field, we take this opportunity 253 * to find out the length of the directory and plug it into 254 * the denode structure. 255 */ 256 u_long size; 257 258 /* 259 * XXX Sometimes, these arrives that . entry have cluster 260 * number 0, when it shouldn't. Use real cluster number 261 * instead of what is written in directory entry. 262 */ 263 if ((diroffset == 0) && (ldep->de_StartCluster != dirclust)) { 264 printf("deget(): . entry at clust %ld != %ld\n", 265 dirclust, ldep->de_StartCluster); 266 ldep->de_StartCluster = dirclust; 267 } 268 269 nvp->v_type = VDIR; 270 if (ldep->de_StartCluster != MSDOSFSROOT) { 271 error = pcbmap(ldep, 0xffff, 0, &size, 0); 272 if (error == E2BIG) { 273 ldep->de_FileSize = de_cn2off(pmp, size); 274 error = 0; 275 } else 276 printf("deget(): pcbmap returned %d\n", error); 277 } 278 } else 279 nvp->v_type = VREG; 280 ldep->de_modrev = init_va_filerev(); 281 *depp = ldep; 282 return (0); 283 } 284 285 int 286 deupdat(dep, waitfor) 287 struct denode *dep; 288 int waitfor; 289 { 290 int error; 291 struct buf *bp; 292 struct direntry *dirp; 293 struct timespec ts; 294 295 if (DETOV(dep)->v_mount->mnt_flag & MNT_RDONLY) 296 return (0); 297 getnanotime(&ts); 298 DETIMES(dep, &ts, &ts, &ts); 299 if ((dep->de_flag & DE_MODIFIED) == 0) 300 return (0); 301 dep->de_flag &= ~DE_MODIFIED; 302 if (dep->de_Attributes & ATTR_DIRECTORY) 303 return (0); 304 if (dep->de_refcnt <= 0) 305 return (0); 306 error = readde(dep, &bp, &dirp); 307 if (error) 308 return (error); 309 DE_EXTERNALIZE(dirp, dep); 310 if (waitfor) 311 return (bwrite(bp)); 312 else { 313 bdwrite(bp); 314 return (0); 315 } 316 } 317 318 /* 319 * Truncate the file described by dep to the length specified by length. 320 */ 321 int 322 detrunc(dep, length, flags, cred, td) 323 struct denode *dep; 324 u_long length; 325 int flags; 326 struct ucred *cred; 327 struct thread *td; 328 { 329 int error; 330 int allerror; 331 u_long eofentry; 332 u_long chaintofree; 333 daddr_t bn; 334 int boff; 335 int isadir = dep->de_Attributes & ATTR_DIRECTORY; 336 struct buf *bp; 337 struct msdosfsmount *pmp = dep->de_pmp; 338 339 #ifdef MSDOSFS_DEBUG 340 printf("detrunc(): file %s, length %lu, flags %x\n", dep->de_Name, length, flags); 341 #endif 342 343 /* 344 * Disallow attempts to truncate the root directory since it is of 345 * fixed size. That's just the way dos filesystems are. We use 346 * the VROOT bit in the vnode because checking for the directory 347 * bit and a startcluster of 0 in the denode is not adequate to 348 * recognize the root directory at this point in a file or 349 * directory's life. 350 */ 351 if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) { 352 printf("detrunc(): can't truncate root directory, clust %ld, offset %ld\n", 353 dep->de_dirclust, dep->de_diroffset); 354 return (EINVAL); 355 } 356 357 358 if (dep->de_FileSize < length) { 359 vnode_pager_setsize(DETOV(dep), length); 360 return deextend(dep, length, cred); 361 } 362 363 /* 364 * If the desired length is 0 then remember the starting cluster of 365 * the file and set the StartCluster field in the directory entry 366 * to 0. If the desired length is not zero, then get the number of 367 * the last cluster in the shortened file. Then get the number of 368 * the first cluster in the part of the file that is to be freed. 369 * Then set the next cluster pointer in the last cluster of the 370 * file to CLUST_EOFE. 371 */ 372 if (length == 0) { 373 chaintofree = dep->de_StartCluster; 374 dep->de_StartCluster = 0; 375 eofentry = ~0; 376 } else { 377 error = pcbmap(dep, de_clcount(pmp, length) - 1, 0, 378 &eofentry, 0); 379 if (error) { 380 #ifdef MSDOSFS_DEBUG 381 printf("detrunc(): pcbmap fails %d\n", error); 382 #endif 383 return (error); 384 } 385 } 386 387 fc_purge(dep, de_clcount(pmp, length)); 388 389 /* 390 * If the new length is not a multiple of the cluster size then we 391 * must zero the tail end of the new last cluster in case it 392 * becomes part of the file again because of a seek. 393 */ 394 if ((boff = length & pmp->pm_crbomask) != 0) { 395 if (isadir) { 396 bn = cntobn(pmp, eofentry); 397 error = bread(pmp->pm_devvp, bn, pmp->pm_bpcluster, 398 NOCRED, &bp); 399 if (error) { 400 brelse(bp); 401 #ifdef MSDOSFS_DEBUG 402 printf("detrunc(): bread fails %d\n", error); 403 #endif 404 return (error); 405 } 406 bzero(bp->b_data + boff, pmp->pm_bpcluster - boff); 407 if (flags & IO_SYNC) 408 bwrite(bp); 409 else 410 bdwrite(bp); 411 } 412 } 413 414 /* 415 * Write out the updated directory entry. Even if the update fails 416 * we free the trailing clusters. 417 */ 418 dep->de_FileSize = length; 419 if (!isadir) 420 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 421 allerror = vtruncbuf(DETOV(dep), cred, td, length, pmp->pm_bpcluster); 422 #ifdef MSDOSFS_DEBUG 423 if (allerror) 424 printf("detrunc(): vtruncbuf error %d\n", allerror); 425 #endif 426 error = deupdat(dep, 1); 427 if (error && (allerror == 0)) 428 allerror = error; 429 #ifdef MSDOSFS_DEBUG 430 printf("detrunc(): allerror %d, eofentry %lu\n", 431 allerror, eofentry); 432 #endif 433 434 /* 435 * If we need to break the cluster chain for the file then do it 436 * now. 437 */ 438 if (eofentry != ~0) { 439 error = fatentry(FAT_GET_AND_SET, pmp, eofentry, 440 &chaintofree, CLUST_EOFE); 441 if (error) { 442 #ifdef MSDOSFS_DEBUG 443 printf("detrunc(): fatentry errors %d\n", error); 444 #endif 445 return (error); 446 } 447 fc_setcache(dep, FC_LASTFC, de_cluster(pmp, length - 1), 448 eofentry); 449 } 450 451 /* 452 * Now free the clusters removed from the file because of the 453 * truncation. 454 */ 455 if (chaintofree != 0 && !MSDOSFSEOF(pmp, chaintofree)) 456 freeclusterchain(pmp, chaintofree); 457 458 return (allerror); 459 } 460 461 /* 462 * Extend the file described by dep to length specified by length. 463 */ 464 int 465 deextend(dep, length, cred) 466 struct denode *dep; 467 u_long length; 468 struct ucred *cred; 469 { 470 struct msdosfsmount *pmp = dep->de_pmp; 471 u_long count; 472 int error; 473 474 /* 475 * The root of a DOS filesystem cannot be extended. 476 */ 477 if ((DETOV(dep)->v_vflag & VV_ROOT) && !FAT32(pmp)) 478 return (EINVAL); 479 480 /* 481 * Directories cannot be extended. 482 */ 483 if (dep->de_Attributes & ATTR_DIRECTORY) 484 return (EISDIR); 485 486 if (length <= dep->de_FileSize) 487 panic("deextend: file too large"); 488 489 /* 490 * Compute the number of clusters to allocate. 491 */ 492 count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); 493 if (count > 0) { 494 if (count > pmp->pm_freeclustercount) 495 return (ENOSPC); 496 error = extendfile(dep, count, NULL, NULL, DE_CLEAR); 497 if (error) { 498 /* truncate the added clusters away again */ 499 (void) detrunc(dep, dep->de_FileSize, 0, cred, NULL); 500 return (error); 501 } 502 } 503 dep->de_FileSize = length; 504 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 505 return (deupdat(dep, 1)); 506 } 507 508 /* 509 * Move a denode to its correct hash queue after the file it represents has 510 * been moved to a new directory. 511 */ 512 void 513 reinsert(dep) 514 struct denode *dep; 515 { 516 struct vnode *vp; 517 518 /* 519 * Fix up the denode cache. If the denode is for a directory, 520 * there is nothing to do since the hash is based on the starting 521 * cluster of the directory file and that hasn't changed. If for a 522 * file the hash is based on the location of the directory entry, 523 * so we must remove it from the cache and re-enter it with the 524 * hash based on the new location of the directory entry. 525 */ 526 #if 0 527 if (dep->de_Attributes & ATTR_DIRECTORY) 528 return; 529 #endif 530 vp = DETOV(dep); 531 dep->de_inode = (uint64_t)dep->de_pmp->pm_bpcluster * dep->de_dirclust + 532 dep->de_diroffset; 533 vfs_hash_rehash(vp, dep->de_inode); 534 } 535 536 int 537 msdosfs_reclaim(ap) 538 struct vop_reclaim_args /* { 539 struct vnode *a_vp; 540 } */ *ap; 541 { 542 struct vnode *vp = ap->a_vp; 543 struct denode *dep = VTODE(vp); 544 545 #ifdef MSDOSFS_DEBUG 546 printf("msdosfs_reclaim(): dep %p, file %s, refcnt %ld\n", 547 dep, dep->de_Name, dep->de_refcnt); 548 #endif 549 550 if (prtactive && vrefcnt(vp) != 0) 551 vprint("msdosfs_reclaim(): pushing active", vp); 552 /* 553 * Destroy the vm object and flush associated pages. 554 */ 555 vnode_destroy_vobject(vp); 556 /* 557 * Remove the denode from its hash chain. 558 */ 559 vfs_hash_remove(vp); 560 /* 561 * Purge old data structures associated with the denode. 562 */ 563 #if 0 /* XXX */ 564 dep->de_flag = 0; 565 #endif 566 FREE(dep, M_MSDOSFSNODE); 567 vp->v_data = NULL; 568 569 return (0); 570 } 571 572 int 573 msdosfs_inactive(ap) 574 struct vop_inactive_args /* { 575 struct vnode *a_vp; 576 struct thread *a_td; 577 } */ *ap; 578 { 579 struct vnode *vp = ap->a_vp; 580 struct denode *dep = VTODE(vp); 581 struct thread *td = ap->a_td; 582 int error = 0; 583 584 #ifdef MSDOSFS_DEBUG 585 printf("msdosfs_inactive(): dep %p, de_Name[0] %x\n", dep, dep->de_Name[0]); 586 #endif 587 588 if (prtactive && vrefcnt(vp) != 0) 589 vprint("msdosfs_inactive(): pushing active", vp); 590 591 /* 592 * Ignore denodes related to stale file handles. 593 */ 594 if (dep->de_Name[0] == SLOT_DELETED) 595 goto out; 596 597 /* 598 * If the file has been deleted and it is on a read/write 599 * filesystem, then truncate the file, and mark the directory slot 600 * as empty. (This may not be necessary for the dos filesystem.) 601 */ 602 #ifdef MSDOSFS_DEBUG 603 printf("msdosfs_inactive(): dep %p, refcnt %ld, mntflag %x, MNT_RDONLY %x\n", 604 dep, dep->de_refcnt, vp->v_mount->mnt_flag, MNT_RDONLY); 605 #endif 606 if (dep->de_refcnt <= 0 && (vp->v_mount->mnt_flag & MNT_RDONLY) == 0) { 607 error = detrunc(dep, (u_long) 0, 0, NOCRED, td); 608 dep->de_flag |= DE_UPDATE; 609 dep->de_Name[0] = SLOT_DELETED; 610 } 611 deupdat(dep, 0); 612 613 out: 614 /* 615 * If we are done with the denode, reclaim it 616 * so that it can be reused immediately. 617 */ 618 #ifdef MSDOSFS_DEBUG 619 printf("msdosfs_inactive(): v_usecount %d, de_Name[0] %x\n", 620 vrefcnt(vp), dep->de_Name[0]); 621 #endif 622 if (dep->de_Name[0] == SLOT_DELETED) 623 vrecycle(vp, td); 624 return (error); 625 } 626