1/* 2 * Copyright (c) 2000-2008,2010-2011,2013 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* $FreeBSD: src/sys/msdosfs/msdosfs_denode.c,v 1.48 2000/05/05 09:58:34 phk Exp $ */ 24/* $NetBSD: msdosfs_denode.c,v 1.28 1998/02/10 14:10:00 mrg Exp $ */ 25 26/*- 27 * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. 28 * Copyright (C) 1994, 1995, 1997 TooLs GmbH. 29 * All rights reserved. 30 * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 3. All advertising materials mentioning features or use of this software 41 * must display the following acknowledgement: 42 * This product includes software developed by TooLs GmbH. 43 * 4. The name of TooLs GmbH may not be used to endorse or promote products 44 * derived from this software without specific prior written permission. 45 * 46 * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR 47 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 48 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 49 * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 50 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 51 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 52 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 53 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 54 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 55 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 56 */ 57/* 58 * Written by Paul Popelka (paulp@uts.amdahl.com) 59 * 60 * You can do anything you want with this software, just don't say you wrote 61 * it, and don't remove this notice. 62 * 63 * This software is provided "as is". 64 * 65 * The author supplies this software to be publicly redistributed on the 66 * understanding that the author is not responsible for the correct 67 * functioning of this software in any circumstances and is not liable for 68 * any damages caused by this software. 69 * 70 * October 1992 71 */ 72 73#include <sys/param.h> 74#include <sys/systm.h> 75#include <sys/kernel.h> 76#include <sys/mount.h> 77#include <sys/malloc.h> 78#include <sys/proc.h> 79#include <sys/buf.h> 80#include <sys/vnode.h> 81#include <sys/ubc.h> 82#include <sys/namei.h> 83#include <mach/boolean.h> 84#include <libkern/OSMalloc.h> 85 86#include "bpb.h" 87#include "msdosfsmount.h" 88#include "direntry.h" 89#include "denode.h" 90#include "fat.h" 91#include "msdosfs_kdebug.h" 92 93#ifndef DEBUG 94#define DEBUG 0 95#endif 96 97OSMallocTag msdosfs_node_tag = NULL; 98static lck_mtx_t *msdosfs_hash_lock = NULL; 99 100static struct denode **dehashtbl = NULL; 101static unsigned long dehash; /* size of hash table - 1 */ 102#define DEHASH(dev, dcl, doff) (dehashtbl[(minor(dev) + (dcl) + (doff)) & dehash]) 103 104union _qcvt { 105 quad_t qcvt; 106 int32_t val[2]; 107}; 108#define SETHIGH(q, h) { \ 109 union _qcvt tmp; \ 110 tmp.qcvt = (q); \ 111 tmp.val[_QUAD_HIGHWORD] = (h); \ 112 (q) = tmp.qcvt; \ 113} 114#define SETLOW(q, l) { \ 115 union _qcvt tmp; \ 116 tmp.qcvt = (q); \ 117 tmp.val[_QUAD_LOWWORD] = (l); \ 118 (q) = tmp.qcvt; \ 119} 120 121struct denode *msdosfs_hash_get(dev_t dev, uint32_t dirclust, uint32_t diroff); 122void msdosfs_hash_insert(struct denode *dep); 123void msdosfs_hash_remove(struct denode *dep); 124 125void msdosfs_hash_init(void) 126{ 127 msdosfs_hash_lock = lck_mtx_alloc_init(msdosfs_lck_grp, msdosfs_lck_attr); 128 msdosfs_node_tag = OSMalloc_Tagalloc("msdosfs denode", OSMT_DEFAULT); 129 dehashtbl = hashinit(desiredvnodes, M_TEMP, &dehash); 130} 131 132void msdosfs_hash_uninit(void) 133{ 134 if (dehashtbl) 135 FREE(dehashtbl, M_TEMP); 136 if (msdosfs_node_tag) 137 OSMalloc_Tagfree(msdosfs_node_tag); 138 if (msdosfs_hash_lock) 139 lck_mtx_free(msdosfs_hash_lock, msdosfs_lck_grp); 140} 141 142/* 143 * Look for a given denode in the denode hash. If found, 144 * return with a use_count reference on the vnode. 145 * 146 * Assumes the msdosfs_hash_lock has already been acquired. 147 */ 148struct denode *msdosfs_hash_get(dev_t dev, uint32_t dirclust, uint32_t diroff) 149{ 150 int error; 151 struct denode *dep; 152 vnode_t vp; 153 u_int32_t vid; 154 struct denode *found = 0; /* Only used if DEBUG is non-zero */ 155 156loop: 157 for (dep = DEHASH(dev, dirclust, diroff); dep; dep = dep->de_next) 158 { 159 if (dirclust == dep->de_dirclust 160 && diroff == dep->de_diroffset 161 && dev == dep->de_dev 162 && dep->de_refcnt != 0) 163 { 164 if (ISSET(dep->de_flag, DE_INIT)) 165 { 166 /* 167 * Denode is being initialized. Wait for it to complete. 168 * We unlock the hash lock while sleeping to avoid deadlock. 169 */ 170 SET(dep->de_flag, DE_WAITINIT); 171 msleep(dep, msdosfs_hash_lock, PINOD, "msdosfs_hash_get", 0); 172 goto loop; 173 } 174 175 /* If the vnode has been deleted, ignore it */ 176 if (dep->de_refcnt <= 0) 177 { 178 if (DEBUG) 179 printf("msdosfs_hash_get: found deleted object\n"); 180 msdosfs_hash_remove(dep); 181 goto loop; 182 } 183 184 /* 185 * Make sure the vnode isn't being terminated. NOTE: we have to 186 * drop the hash lock to avoid deadlock with other threads that 187 * may be trying to terminate this vnode. 188 */ 189 vp = DETOV(dep); 190 vid = vnode_vid(vp); 191 lck_mtx_unlock(msdosfs_hash_lock); 192 error = vnode_getwithvid(vp, vid); 193 lck_mtx_lock(msdosfs_hash_lock); 194 if (error) 195 goto loop; 196 197 if (DEBUG) 198 { 199 if (found) 200 panic("msdosfs_hash_get: multiple denodes"); 201 found = dep; 202 continue; 203 } 204 return dep; 205 } 206 } 207 if (DEBUG) 208 return found; 209 210 return NULL; 211} 212 213/* 214 * Insert a given denode in the denode hash. 215 * 216 * Assumes the msdosfs_hash_lock has already been acquired. Assumes the 217 * denode is not currently in the hash. 218 */ 219void msdosfs_hash_insert(struct denode *dep) 220{ 221 struct denode **depp, *deq; 222 223 if (DEBUG) 224 { 225 struct denode *found; 226 found = msdosfs_hash_get(dep->de_dev, dep->de_dirclust, dep->de_diroffset); 227 if (found) 228 panic("msdosfs_hash_insert: denode already in hash? found=%p", found); 229 } 230 231 depp = &DEHASH(dep->de_dev, dep->de_dirclust, dep->de_diroffset); 232 deq = *depp; 233 if (deq) 234 deq->de_prev = &dep->de_next; 235 dep->de_next = deq; 236 dep->de_prev = depp; 237 *depp = dep; 238} 239 240/* 241 * Remove a given denode in the denode hash. 242 * 243 * Assumes the msdosfs_hash_lock has already been acquired. 244 */ 245void msdosfs_hash_remove(struct denode *dep) 246{ 247 struct denode *deq; 248 249 deq = dep->de_next; 250 if (deq) 251 deq->de_prev = dep->de_prev; 252 *dep->de_prev = deq; 253} 254 255/* 256 * If msdosfs_deget() succeeds it returns with an io_count reference on the denode's 257 * corresponding vnode. If not returning that vnode to VFS, then be sure 258 * to vnode_put it! 259 * 260 * pmp - address of msdosfsmount structure of the filesystem containing 261 * the denode of interest. The pm_dev field and the address of 262 * the msdosfsmount structure are used. 263 * dirclust - which cluster bp contains, if dirclust is 0 (root directory) 264 * diroffset is relative to the beginning of the root directory, 265 * otherwise it is cluster relative. 266 * diroffset - offset from start of parent directory to the directory entry we want 267 * dvp - parent directory 268 * cnp - name used to look up the denode 269 * depp - returns the address of the gotten denode. 270 */ 271int msdosfs_deget( 272 struct msdosfsmount *pmp, /* so we know the maj/min number */ 273 uint32_t dirclust, /* cluster this dir entry came from */ 274 uint32_t diroffset, /* index of entry within the cluster */ 275 vnode_t dvp, /* parent directory */ 276 struct componentname *cnp, /* name used to find this node */ 277 struct denode **depp, /* returns the addr of the gotten denode */ 278 vfs_context_t context) 279{ 280 int error; 281 dev_t dev = pmp->pm_dev; 282 struct mount *mntp = pmp->pm_mountp; 283 struct denode *dep; 284 struct dosdirentry *direntptr; 285 struct buf *bp = NULL; 286 struct timeval tv; 287 struct vnode_fsparam vfsp; 288 enum vtype vtype; 289 290 /* 291 * On FAT32 filesystems, root is a (more or less) normal 292 * directory 293 */ 294 if (FAT32(pmp) && dirclust == MSDOSFSROOT) 295 dirclust = pmp->pm_rootdirblk; 296 297 /* 298 * Lock the hash so that we can't race against another msdosfs_deget(), 299 * VNOP_RECLAIM, etc. 300 */ 301 lck_mtx_lock(msdosfs_hash_lock); 302 303 /* 304 * See if the denode is already in our hash. If so, 305 * just return it. 306 */ 307 dep = msdosfs_hash_get(dev, dirclust, diroffset); 308 if (dep != NULL) { 309 *depp = dep; 310 if (dvp && cnp && (cnp->cn_flags & MAKEENTRY) && (dep->de_flag & DE_ROOT) == 0) 311 cache_enter(dvp, DETOV(dep), cnp); 312 if (dep->de_parent == NULL && dvp != NULLVP && (dep->de_flag & DE_ROOT) == 0) 313 { 314 if (DEBUG) printf("msdosfs_deget: fixing de_parent\n"); 315 dep->de_parent = VTODE(dvp); 316 } 317 lck_mtx_unlock(msdosfs_hash_lock); 318 return 0; 319 } 320 321 /* 322 * There was nothing in the hash. Before we block on I/O, 323 * we need to insert a matching denode marked as being 324 * initialized. Any other msdosfs_deget() will block until we're 325 * finished here, and either find the fully initialized 326 * denode, or none at all. 327 */ 328 dep = OSMalloc(sizeof(struct denode), msdosfs_node_tag); 329 if (dep == NULL) { 330 *depp = NULL; 331 lck_mtx_unlock(msdosfs_hash_lock); 332 return ENOMEM; 333 } 334 bzero(dep, sizeof *dep); 335 dep->de_lock = lck_mtx_alloc_init(msdosfs_lck_grp, msdosfs_lck_attr); 336 dep->de_cluster_lock = lck_mtx_alloc_init(msdosfs_lck_grp, msdosfs_lck_attr); 337 dep->de_pmp = pmp; 338 dep->de_devvp = pmp->pm_devvp; 339 dep->de_dev = dev; 340 dep->de_dirclust = dirclust; 341 dep->de_diroffset = diroffset; 342 dep->de_refcnt = 1; 343 SET(dep->de_flag, DE_INIT); 344 msdosfs_hash_insert(dep); 345 lck_mtx_unlock(msdosfs_hash_lock); 346 347 vfsp.vnfs_markroot = 0; /* Assume not the root */ 348 349 /* 350 * Copy the directory entry into the denode 351 */ 352 if ((dirclust == MSDOSFSROOT 353 || (FAT32(pmp) && dirclust == pmp->pm_rootdirblk)) 354 && diroffset == MSDOSFSROOT_OFS) { 355 /* 356 * Directory entry for the root directory. There isn't one, 357 * so we manufacture one. We should probably rummage 358 * through the root directory and find a label entry (if it 359 * exists), and then use the time and date from that entry 360 * as the time and date for the root denode. 361 */ 362 vfsp.vnfs_markroot = 1; 363 SET(dep->de_flag, DE_ROOT); 364 365 bcopy(" ", dep->de_Name, 11); 366 dep->de_Attributes = ATTR_DIRECTORY; 367 dep->de_LowerCase = 0; 368 if (FAT32(pmp)) 369 dep->de_StartCluster = pmp->pm_rootdirblk; 370 /* de_FileSize will be filled in further down */ 371 else { 372 dep->de_StartCluster = MSDOSFSROOT; 373 dep->de_FileSize = pmp->pm_rootdirsize * pmp->pm_BlockSize; 374 } 375 /* 376 * fill in time and date so that msdosfs_dos2unixtime() doesn't 377 * spit up when called from msdosfs_vnop_getattr() with root 378 * denode 379 */ 380 dep->de_CHun = 0; 381 dep->de_CTime = 0x0000; /* 00:00:00 */ 382 dep->de_CDate = (0 << DD_YEAR_SHIFT) | (1 << DD_MONTH_SHIFT) 383 | (1 << DD_DAY_SHIFT); 384 /* Jan 1, 1980 */ 385 dep->de_ADate = dep->de_CDate; 386 dep->de_MTime = dep->de_CTime; 387 dep->de_MDate = dep->de_CDate; 388 /* leave the other fields as garbage */ 389 390 /* 391 * If there is a volume label entry, then grab the times from it instead. 392 */ 393 if (pmp->pm_label_cluster != CLUST_EOFE) { 394 error = msdosfs_readep(pmp, pmp->pm_label_cluster, pmp->pm_label_offset, 395 &bp, &direntptr, context); 396 if (!error) { 397 dep->de_CHun = direntptr->deCHundredth; 398 dep->de_CTime = getuint16(direntptr->deCTime); 399 dep->de_CDate = getuint16(direntptr->deCDate); 400 dep->de_ADate = getuint16(direntptr->deADate); 401 dep->de_MTime = getuint16(direntptr->deMTime); 402 dep->de_MDate = getuint16(direntptr->deMDate); 403 buf_brelse(bp); 404 bp = NULL; 405 } 406 } 407 } else { 408 /* Not the root directory */ 409 410 /* Get the non-date values from the given directory entry */ 411 error = msdosfs_readep(pmp, dirclust, diroffset, &bp, &direntptr, context); 412 if (error) goto fail; 413 bcopy(direntptr->deName, dep->de_Name, 11); 414 dep->de_Attributes = direntptr->deAttributes; 415 dep->de_LowerCase = direntptr->deLowerCase; 416 dep->de_StartCluster = getuint16(direntptr->deStartCluster); 417 if (FAT32(pmp)) 418 dep->de_StartCluster |= getuint16(direntptr->deHighClust) << 16; 419 dep->de_FileSize = getuint32(direntptr->deFileSize); 420 421 /* For directories, the dates/times come from its "." entry */ 422 if (direntptr->deAttributes & ATTR_DIRECTORY) 423 { 424 buf_brelse(bp); 425 bp = NULL; 426 if (DEBUG) 427 { 428 if (dep->de_StartCluster < CLUST_FIRST || dep->de_StartCluster > pmp->pm_maxcluster) 429 panic("msdosfs_deget: directory de_StartCluster=%u", dep->de_StartCluster); 430 } 431 error = msdosfs_readep(pmp, dep->de_StartCluster, 0, &bp, &direntptr, context); 432 if (error) goto fail; 433 } 434 435 /* Copy the dates and times */ 436 dep->de_CHun = direntptr->deCHundredth; 437 dep->de_CTime = getuint16(direntptr->deCTime); 438 dep->de_CDate = getuint16(direntptr->deCDate); 439 dep->de_ADate = getuint16(direntptr->deADate); 440 dep->de_MTime = getuint16(direntptr->deMTime); 441 dep->de_MDate = getuint16(direntptr->deMDate); 442 443 buf_brelse(bp); 444 bp = NULL; 445 } 446 447 /* 448 * Determine initial values for vnode fields, and finish 449 * populating the denode. 450 */ 451 if (dep->de_Attributes & ATTR_DIRECTORY) { 452 /* 453 * Since DOS directory entries that describe directories 454 * have 0 in the filesize field, we take this opportunity 455 * to find out the length of the directory and plug it into 456 * the denode structure. 457 */ 458 uint32_t size; 459 460 vtype = VDIR; 461 if (dep->de_StartCluster != MSDOSFSROOT) { 462 error = msdosfs_pcbmap(dep, 0xFFFFFFFF, 1, NULL, &size, &dep->de_LastCluster); 463 if (error == E2BIG) { 464 dep->de_FileSize = de_cn2off(pmp, size); 465 error = 0; 466 } 467 if (error) 468 goto fail; 469 } 470 } else { 471 /* 472 * We found a regular file. See if it is really a symlink. 473 */ 474 vtype = msdosfs_check_link(dep, context); 475 } 476 getmicrouptime(&tv); 477 SETHIGH(dep->de_modrev, (int32_t)tv.tv_sec); 478 SETLOW(dep->de_modrev, tv.tv_usec * 4294); 479 480 /* Remember the parent denode */ 481 dep->de_parent = (dvp != NULLVP) ? VTODE(dvp) : NULL; 482 483 /* 484 * Create the vnode 485 */ 486 vfsp.vnfs_mp = mntp; 487 vfsp.vnfs_vtype = vtype; 488 vfsp.vnfs_str = "msdosfs"; 489 vfsp.vnfs_dvp = dvp; 490 vfsp.vnfs_fsnode = dep; 491 vfsp.vnfs_cnp = cnp; 492 vfsp.vnfs_vops = msdosfs_vnodeop_p; 493 vfsp.vnfs_rdev = 0; /* msdosfs doesn't support block devices */ 494 vfsp.vnfs_filesize = dep->de_FileSize; 495 if (dvp && cnp && (cnp->cn_flags & MAKEENTRY)) 496 vfsp.vnfs_flags = 0; 497 else 498 vfsp.vnfs_flags = VNFS_NOCACHE; 499 /* vfsp.vnfs_markroot was set or cleared above */ 500 vfsp.vnfs_marksystem = 0; /* msdosfs has no "system" vnodes */ 501 502 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &dep->de_vnode); 503 if (error) 504 goto fail; 505 506 /* 507 * Take an "fs" reference on the new vnode on 508 * behalf of the denode. 509 */ 510 vnode_addfsref(dep->de_vnode); 511 512 /* 513 * Return it. We're done. 514 */ 515 *depp = dep; 516 517 CLR(dep->de_flag, DE_INIT); 518 if (ISSET(dep->de_flag, DE_WAITINIT)) 519 wakeup(dep); 520 521 return 0; 522 523fail: 524 if (bp) 525 buf_brelse(bp); 526 527 lck_mtx_lock(msdosfs_hash_lock); 528 msdosfs_hash_remove(dep); 529 lck_mtx_unlock(msdosfs_hash_lock); 530 531 if (ISSET(dep->de_flag, DE_WAITINIT)) 532 wakeup(dep); 533 534 OSFree(dep, sizeof *dep, msdosfs_node_tag); 535 536 return error; 537} 538 539int msdosfs_deupdat(struct denode *dep, int waitfor, vfs_context_t context) 540{ 541#pragma unused (waitfor) 542 int error = 0; 543 int isRoot = 0; 544 int isDir = 0; 545 uint32_t dirclust, diroffset; 546 struct buf *bp; 547 struct dosdirentry *dirp; 548 struct timespec ts; 549 struct msdosfsmount *pmp = dep->de_pmp; 550 551 if (vnode_vfsisrdonly(DETOV(dep))) 552 return (0); 553 getnanotime(&ts); 554 DETIMES(dep, &ts, &ts, &ts); 555 if ((dep->de_flag & DE_MODIFIED) == 0) 556 return 0; 557 558 isRoot = dep->de_flag & DE_ROOT; 559 isDir = dep->de_Attributes & ATTR_DIRECTORY; 560 561 if (isRoot && pmp->pm_label_cluster == CLUST_EOFE) 562 return 0; /* There is no volume label entry to update. */ 563 if (dep->de_refcnt <= 0) 564 return 0; /* Object was deleted from the namespace. */ 565 566 /* 567 * Update the fields, like times, which go in the directory entry. 568 * 569 * Windows never updates the times in a directory's directory entry. 570 * Since we wish to update them persistently, we use the times from 571 * the "." entry in the directory. Since the root doesn't have a "." 572 * entry, we use the times in the volume label entry (if there is one). 573 */ 574 if (dep->de_flag & DE_MODIFIED) 575 { 576 dep->de_flag &= ~DE_MODIFIED; 577 578 /* 579 * Read in the directory entry to be updated. 580 * For files, this is just the file's directory entry (in its parent). 581 * For directories, this is the "." entry inside the directory. 582 * For the root, this is the volume label's directory entry. 583 */ 584 if (isDir) 585 { 586 if (isRoot) 587 { 588 dirclust = pmp->pm_label_cluster; 589 diroffset = pmp->pm_label_offset; 590 } 591 else 592 { 593 dirclust = dep->de_StartCluster; 594 diroffset = 0; 595 } 596 } 597 else 598 { 599 dirclust = dep->de_dirclust; 600 diroffset = dep->de_diroffset; 601 } 602 error = msdosfs_readep(pmp, dirclust, diroffset, &bp, &dirp, context); 603 if (error) return (error); 604 605 if (isRoot) 606 DE_EXTERNALIZE_ROOT(dirp, dep); 607 else 608 { 609 if (DEBUG) 610 { 611 if ((dirp->deAttributes ^ dep->de_Attributes) & ATTR_DIRECTORY) 612 panic("deupdate: attributes are wrong"); 613 if (!isDir && bcmp(dirp->deName, dep->de_Name, SHORT_NAME_LEN)) 614 { 615 panic("msdosfs_deupdat: file name is wrong"); 616 } 617 } 618 DE_EXTERNALIZE(dirp, dep); 619 } 620 621 error = buf_bdwrite(bp); 622 bp = NULL; 623 } 624 625 /* 626 * We need to be able to update the de_Attributes for a directory (in the 627 * directory's own entry, not the "." or volume label entry) so that we can 628 * change its hidden bit. For files, we've already updated the de_Attributes 629 * above. 630 */ 631 if (error == 0 && (dep->de_flag & DE_ATTR_MOD)) 632 { 633 dep->de_flag &= ~DE_ATTR_MOD; 634 635 if (isDir && !isRoot) 636 { 637 error = msdosfs_readep(pmp, dep->de_dirclust, dep->de_diroffset, &bp, &dirp, context); 638 if (error) return error; 639 dirp->deAttributes = dep->de_Attributes; 640 error = buf_bdwrite(bp); 641 bp = NULL; 642 } 643 } 644 return error; 645} 646 647/* 648 * Truncate the file described by dep to the length specified by length. 649 * 650 * NOTE: This function takes care of updating dep->de_FileSize and calling 651 * ubc_setsize with new length. 652 */ 653int msdosfs_detrunc(struct denode *dep, uint32_t length, int flags, vfs_context_t context) 654{ 655 int error; 656 int allerror; 657 int cluster_locked = 0; 658 uint32_t eofentry; 659 uint32_t chaintofree; 660 int isadir = dep->de_Attributes & ATTR_DIRECTORY; 661 struct msdosfsmount *pmp = dep->de_pmp; 662 vnode_t vp = DETOV(dep); 663 664 /* 665 * Disallow attempts to truncate the root directory since it is of 666 * fixed size. That's just the way dos filesystems are. We use 667 * the VROOT bit in the vnode because checking for the directory 668 * bit and a startcluster of 0 in the denode is not adequate to 669 * recognize the root directory at this point in a file or 670 * directory's life. 671 */ 672 if (vnode_isvroot(vp) && !FAT32(pmp)) { 673 printf("msdosfs_detrunc(): can't truncate root directory, clust %d, offset %d\n", 674 dep->de_dirclust, dep->de_diroffset); 675 return (EINVAL); 676 } 677 678 if (dep->de_FileSize < length) { 679 return msdosfs_deextend(dep, length, flags, context); 680 } 681 682 lck_mtx_lock(dep->de_cluster_lock); 683 cluster_locked = 1; 684 685 /* 686 * If the desired length is 0 then remember the starting cluster of 687 * the file and set the StartCluster field in the directory entry 688 * to 0. If the desired length is not zero, then get the number of 689 * the last cluster in the shortened file. Then get the number of 690 * the first cluster in the part of the file that is to be freed. 691 * Then set the next cluster pointer in the last cluster of the 692 * file to CLUST_EOFE. 693 */ 694 if (length == 0) { 695 chaintofree = dep->de_StartCluster; 696 dep->de_StartCluster = 0; 697 dep->de_LastCluster = 0; 698 dep->de_cluster_count = 0; 699 } else { 700 uint32_t length_clusters; 701 702 length_clusters = de_clcount(pmp, length); 703 704 /* 705 * Determine the new last cluster of the file. 706 * 707 * Note: this has a side effect of updating the cluster extent cache 708 * to be the extent containing the new end of file. We must do this 709 * before updating the cluster extent cache; if not, msdosfs_pcbmap_internal 710 * could update the cluster extent cache (if the previously cached 711 * extent didn't contain the new last cluster), leaving it inconsistent 712 * once we actually truncate the extra clusters. 713 */ 714 lck_mtx_lock(dep->de_pmp->pm_fat_lock); 715 error = msdosfs_pcbmap_internal(dep, length_clusters - 1, 1, NULL, 716 &eofentry, NULL); 717 lck_mtx_unlock(dep->de_pmp->pm_fat_lock); 718 719 if (error) 720 { 721 allerror = error; 722 goto exit; 723 } 724 725 /* 726 * Update the cluster extent cache. We have to be careful that we 727 * don't call msdosfs_pcbmap_internal until after the cluster chain has actually 728 * been truncated. (Otherwise our cluster extent cache would be 729 * inconsistent with the actual cluster chain on disk.) 730 * 731 * We could also move this code below, after the msdosfs_fatentry() call that 732 * truncates the chain. 733 */ 734 if (length_clusters >= dep->de_cluster_logical) 735 { 736 /* 737 * New length is in or after the currently cached extent. 738 * If within the cached extent, then truncate it. 739 */ 740 if (length_clusters < (dep->de_cluster_logical + dep->de_cluster_count)) 741 dep->de_cluster_count = length_clusters - dep->de_cluster_logical; 742 } 743 else 744 { 745 /* 746 * The new length is before the currently cached extent, 747 * so completely invalidate the cached extent (none of it 748 * exists any more). 749 */ 750 dep->de_cluster_count = 0; 751 } 752 } 753 754 allerror = buf_invalidateblks(vp, ((length > 0) ? BUF_WRITE_DATA : 0), 0, 0); 755 756 dep->de_FileSize = length; 757 758 /* 759 * Write out the updated directory entry. Even if the update fails 760 * we free the trailing clusters. 761 */ 762 if (!isadir) 763 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 764 765 error = msdosfs_deupdat(dep, 1, context); 766 if (error && (allerror == 0)) 767 allerror = error; 768 769 /* 770 * If we need to break the cluster chain for the file then do it 771 * now. 772 */ 773 if (length != 0) { 774 error = msdosfs_fatentry(FAT_GET_AND_SET, pmp, eofentry, 775 &chaintofree, CLUST_EOFE); 776 if (error) 777 { 778 dep->de_cluster_count = 0; 779 allerror = error; 780 goto exit; 781 } 782 783 dep->de_LastCluster = eofentry; 784 } 785 786 /* 787 * Now free the clusters removed from the file because of the 788 * truncation. 789 */ 790 if (chaintofree != 0 && !MSDOSFSEOF(pmp, chaintofree)) 791 msdosfs_freeclusterchain(pmp, chaintofree); 792 793 /* 794 * If "length" is not a multiple of the page size, ubc_setsize will 795 * cause the page containing offset "length" to be flushed. This will 796 * call VNOP_BLOCKMAP, which will need the de_cluster_lock. Since 797 * we're done manipulating the cached cluster extent, release the 798 * de_cluster_lock now. 799 */ 800 lck_mtx_unlock(dep->de_cluster_lock); 801 cluster_locked = 0; 802 803 ubc_setsize(vp, (off_t)length); /* XXX check errors */ 804 805exit: 806 if (cluster_locked) 807 lck_mtx_unlock(dep->de_cluster_lock); 808 809 return (allerror); 810} 811 812/* 813 * Extend the file described by dep to length specified by length. 814 * We must be able to allocate all of the new space, else fail this request. 815 */ 816int msdosfs_deextend(struct denode *dep, uint32_t length, int flags, vfs_context_t context) 817{ 818 struct msdosfsmount *pmp = dep->de_pmp; 819 uint32_t count; 820 int error; 821 822 /* 823 * The root of a DOS filesystem cannot be extended. 824 */ 825 if (vnode_isvroot(DETOV(dep)) && !FAT32(pmp)) 826 return (EINVAL); 827 828 /* 829 * Directories cannot be extended. 830 */ 831 if (dep->de_Attributes & ATTR_DIRECTORY) 832 return (EISDIR); 833 834 if (length <= dep->de_FileSize) 835 panic("msdosfs_deextend: file too large"); 836 837 /* 838 * Compute the number of clusters to allocate. 839 */ 840 count = de_clcount(pmp, length) - de_clcount(pmp, dep->de_FileSize); 841 if (count > 0) { 842 if (count > pmp->pm_freeclustercount) 843 return (ENOSPC); 844 error = msdosfs_extendfile(dep, count, NULL); 845 if (error) { 846 /* truncate the added clusters away again */ 847 (void) msdosfs_detrunc(dep, dep->de_FileSize, 0, context); 848 return (error); 849 } 850 } 851 852 /* Zero fill the newly allocated bytes, except if IO_NOZEROFILL was given. */ 853 if (!(flags & IO_NOZEROFILL)) { 854 error = cluster_write(DETOV(dep), (struct uio *) 0, 855 (off_t)dep->de_FileSize, (off_t)(length), 856 (off_t)dep->de_FileSize, (off_t)0, 857 (flags | IO_HEADZEROFILL)); 858 if (error) 859 return (error); 860 } 861 862 ubc_setsize(DETOV(dep), (off_t)length); /* XXX check errors */ 863 864 dep->de_FileSize = length; 865 866 dep->de_flag |= DE_UPDATE|DE_MODIFIED; 867 return (msdosfs_deupdat(dep, 1, context)); 868} 869 870/* 871 * Move a denode to its correct hash queue after the file it represents has 872 * been moved to a new directory. 873 * 874 * Assumes the msdosfs_hash_lock has NOT been acquired. 875 */ 876void msdosfs_hash_reinsert(struct denode *dep) 877{ 878 /* 879 * Fix up the denode cache. The hash is based on the location of the 880 * directory entry, so we must remove it from the cache and re-enter it 881 * with the hash based on the new location of the directory entry. 882 */ 883 lck_mtx_lock(msdosfs_hash_lock); 884 msdosfs_hash_remove(dep); 885 msdosfs_hash_insert(dep); 886 lck_mtx_unlock(msdosfs_hash_lock); 887} 888 889int msdosfs_vnop_reclaim(struct vnop_reclaim_args *ap) 890/* { 891 vnode_t a_vp; 892 vfs_context_t a_context; 893 } */ 894{ 895 vnode_t vp = ap->a_vp; 896 struct denode *dep = VTODE(vp); 897 898 KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_RECLAIM|DBG_FUNC_START, dep, 0, 0, 0, 0); 899 /* 900 * Skip everything if there was no denode. This can happen 901 * If the vnode was temporarily created in msdosfs_check_link. 902 */ 903 if (dep == NULL) 904 goto done; 905 906 /* 907 * Remove the denode from its hash chain. 908 */ 909 lck_mtx_lock(msdosfs_hash_lock); 910 msdosfs_hash_remove(dep); 911 lck_mtx_unlock(msdosfs_hash_lock); 912 913 /* 914 * Purge old data structures associated with the denode. 915 */ 916 cache_purge(vp); 917 918 lck_mtx_free(dep->de_cluster_lock, msdosfs_lck_grp); 919 lck_mtx_free(dep->de_lock, msdosfs_lck_grp); 920 OSFree(dep, sizeof(struct denode), msdosfs_node_tag); 921 vnode_clearfsnode(vp); 922 vnode_removefsref(vp); 923 924done: 925 KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_RECLAIM|DBG_FUNC_END, 0, 0, 0, 0, 0); 926 return 0; 927} 928 929int msdosfs_vnop_inactive(struct vnop_inactive_args *ap) 930/* { 931 vnode_t a_vp; 932 vfs_context_t a_context; 933 } */ 934{ 935 vnode_t vp = ap->a_vp; 936 vfs_context_t context = ap->a_context; 937 struct denode *dep = VTODE(vp); 938 int error = 0; 939 int needs_flush = 0; 940 941 KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_INACTIVE|DBG_FUNC_START, dep, 0, 0, 0, 0); 942 943 /* 944 * Skip everything if there was no denode. This can happen 945 * If the vnode was temporarily created in msdosfs_check_link. 946 */ 947 if (dep == NULL) 948 goto done; 949 950 lck_mtx_lock(dep->de_lock); 951 952 /* 953 * Ignore denodes related to stale file handles. 954 */ 955 if (dep->de_Name[0] == SLOT_DELETED) 956 goto out; 957 958 /* 959 * If the file has been deleted and it is on a read/write 960 * filesystem, then truncate the file, and mark the directory slot 961 * as empty. (This may not be necessary for the dos filesystem.) 962 */ 963 if (dep->de_refcnt <= 0 && ( !vnode_vfsisrdonly(vp))) { 964 error = msdosfs_detrunc(dep, (uint32_t) 0, 0, context); 965 needs_flush = 1; 966 dep->de_flag |= DE_UPDATE; 967 dep->de_Name[0] = SLOT_DELETED; 968 vnode_recycle(vp); 969 } 970 msdosfs_deupdat(dep, 1, context); 971 972out: 973 if (needs_flush) 974 msdosfs_meta_flush(dep->de_pmp, FALSE); 975 976 /* 977 * We used to do a vnode_recycle(vp) here if the file/dir had 978 * been deleted. That's good for reducing the pressure for vnodes, 979 * but bad for delayed writes (including async mounts) because VFS 980 * calls VNOP_FSYNC(..., MNT_WAIT,...) on the vnode during vclean(), 981 * which causes us to synchronously write all of the volume's metadata. 982 * And that means that mass deletions become totally synchronous. 983 */ 984 985 lck_mtx_unlock(dep->de_lock); 986 987done: 988 KERNEL_DEBUG_CONSTANT(MSDOSFS_VNOP_INACTIVE|DBG_FUNC_END, error, 0, 0, 0, 0); 989 return (error); 990} 991 992 993/* 994 * msdosfs_defileid -- Return the file ID (inode number) for a given denode. This routine 995 * is used by msdosfs_vnop_getattr and msdosfs_vnop_readdir to ensure a consistent file 996 * ID space. 997 * 998 * In older versions, the file ID was based on the location of the directory entry 999 * on disk (essentially the byte offset of the entry divided by the size of an entry). 1000 * The file ID of a directory was the ID of the "." entry (which is the first entry 1001 * in the directory). The file ID of the root of a FAT12 or FAT16 volume (whose root 1002 * directory is not in an allocated cluster) is 1. 1003 * 1004 * We now use the starting cluster number of the file or directory, or 1 for the root 1005 * of a FAT12 or FAT16 volume. Note that empty files have no starting cluster number, 1006 * and their file ID is a constant that is out of range for any FAT volume (since 1007 * FAT32 really only uses 28 bits for cluster number). Directories (other than the root) 1008 * always contain at least one cluster for their "." and ".." entries. 1009 */ 1010uint32_t msdosfs_defileid(struct denode *dep) 1011{ 1012 uint32_t fileid; 1013 1014 fileid = dep->de_StartCluster; 1015 1016 if ((dep->de_Attributes & ATTR_DIRECTORY) && (dep->de_StartCluster == MSDOSFSROOT)) 1017 fileid = FILENO_ROOT; /* root of FAT12 or FAT16 */ 1018 1019 if (fileid == 0) /* empty? */ 1020 fileid = FILENO_EMPTY; /* use an out-of-range cluster number */ 1021 1022 return fileid; 1023} 1024