fat: Enable 4096-byte sector sizes

* Correct code that only works when sector size is 512 bytes.
* For devices with 4096-byte sectors, use 4096 as the size of blocks
  in the block cache, and as the unit of IO requests in FAT
  operations.
* Some values continue to be stored in 512-byte units, regardless of
  sector size, in keeping with the original BSD driver.
* Fixes #19686.

Change-Id: I8fa77aeab1bc93a5465134018c3113afb2a80b8b
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9542
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
Jim906
2025-08-04 15:06:48 +00:00
committed by waddlesplash
parent 5e21788f2a
commit 22375f018d
6 changed files with 190 additions and 140 deletions
@@ -46,6 +46,7 @@
#define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1))
#define ROUNDDOWN(a, b) (((a) / (b)) * (b)) #define ROUNDDOWN(a, b) (((a) / (b)) * (b))
#define HOWMANY(a, b) (((a) + ((b) - 1)) / (b))
#define CHECK_BIT(a, b) ((a) & (1 << (b))) #define CHECK_BIT(a, b) ((a) & (1 << (b)))
@@ -138,15 +138,16 @@ _bwrite(struct buf* buf)
return EIO; return EIO;
} else if (buf->b_owned == false) { } else if (buf->b_owned == false) {
// put the single block cache block that was modified // put the single block cache block that was modified
block_cache_put(blockCache, buf->b_blkno); block_cache_put(blockCache, BLOCK_TO_SECTOR(fatVolume, buf->b_blkno));
} else { } else {
size_t cBlockSize = fatVolume->pm_BytesPerSec;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, buf->b_blkno);
// copy b_data into mutiple block cache blocks and put them // copy b_data into mutiple block cache blocks and put them
uint32 cBlockCount = buf->b_bufsize / CACHED_BLOCK_SIZE; uint32 cBlockCount = buf->b_bufsize / cBlockSize;
uint32 i; uint32 i;
for (i = 0; i < cBlockCount && buf->b_bcpointers[i] != NULL; ++i) { for (i = 0; i < cBlockCount && buf->b_bcpointers[i] != NULL; ++i) {
memcpy((caddr_t)buf->b_bcpointers[i], buf->b_data + (i * CACHED_BLOCK_SIZE), memcpy((caddr_t)buf->b_bcpointers[i], buf->b_data + (i * cBlockSize), cBlockSize);
CACHED_BLOCK_SIZE); block_cache_put(blockCache, cachedBlock + i);
block_cache_put(blockCache, buf->b_blkno + i);
buf->b_bcpointers[i] = NULL; buf->b_bcpointers[i] = NULL;
} }
} }
@@ -184,13 +185,16 @@ bawrite(struct buf* bp)
if (bp->b_vreg->v_resizing == false) if (bp->b_vreg->v_resizing == false)
file_cache_sync(bp->b_vreg->v_cache); file_cache_sync(bp->b_vreg->v_cache);
} else { } else {
void* blockCache = bp->b_vp->v_rdev->si_mountpt->mnt_cache; struct mount* bsdVolume = bp->b_vp->v_rdev->si_mountpt;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data;
void* blockCache = bsdVolume->mnt_cache;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, bp->b_blkno);
if (bp->b_owned == false) { if (bp->b_owned == false) {
block_cache_sync_etc(blockCache, bp->b_blkno, 1); block_cache_sync_etc(blockCache, cachedBlock, 1);
} else { } else {
block_cache_sync_etc(blockCache, bp->b_blkno, block_cache_sync_etc(blockCache, cachedBlock,
howmany(bp->b_bufsize, CACHED_BLOCK_SIZE)); howmany(bp->b_bufsize, fatVolume->pm_BytesPerSec));
} }
} }
@@ -215,21 +219,23 @@ brelse(struct buf* bp)
} }
struct mount* bsdVolume = bp->b_vp->v_rdev->si_mountpt; struct mount* bsdVolume = bp->b_vp->v_rdev->si_mountpt;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data;
void* blockCache = bsdVolume->mnt_cache; void* blockCache = bsdVolume->mnt_cache;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, bp->b_blkno);
bool readOnly = MOUNTED_READ_ONLY(VFSTOMSDOSFS(bsdVolume)); bool readOnly = MOUNTED_READ_ONLY(VFSTOMSDOSFS(bsdVolume));
if (bp->b_owned == false) { if (bp->b_owned == false) {
if (readOnly == true) if (readOnly == true)
block_cache_set_dirty(blockCache, bp->b_blkno, false, -1); block_cache_set_dirty(blockCache, cachedBlock, false, -1);
block_cache_put(blockCache, bp->b_blkno); block_cache_put(blockCache, cachedBlock);
put_buf(bp); put_buf(bp);
} else { } else {
uint32 cBlockCount = bp->b_bufsize / CACHED_BLOCK_SIZE; uint32 cBlockCount = bp->b_bufsize / fatVolume->pm_BytesPerSec;
uint32 i; uint32 i;
for (i = 0; i < cBlockCount && bp->b_bcpointers[i] != NULL; ++i) { for (i = 0; i < cBlockCount && bp->b_bcpointers[i] != NULL; ++i) {
if (readOnly == true) if (readOnly == true)
block_cache_set_dirty(blockCache, bp->b_blkno + i, false, -1); block_cache_set_dirty(blockCache, cachedBlock + i, false, -1);
block_cache_put(blockCache, bp->b_blkno + i); block_cache_put(blockCache, cachedBlock + i);
bp->b_bcpointers[i] = NULL; bp->b_bcpointers[i] = NULL;
} }
@@ -282,6 +288,7 @@ getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
uint32 i; uint32 i;
void* blockCache = NULL; void* blockCache = NULL;
size_t cBlockSize = 0;
uint32 cBlockCount; uint32 cBlockCount;
// the number of block cache blocks spanned by the client's request // the number of block cache blocks spanned by the client's request
struct buf* newBuf = NULL; struct buf* newBuf = NULL;
@@ -304,11 +311,12 @@ getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
} else { } else {
return ENOTSUP; return ENOTSUP;
} }
cBlockSize = fatVolume->pm_BytesPerSec;
// Before allocating memory for a new struct buf, try to reuse an existing one // Before allocating memory for a new struct buf, try to reuse an existing one
// in the device vnode's lists. // in the device vnode's lists.
rw_lock_write_lock(&deviceNode->v_bufobj.bo_lock.haikuRW); rw_lock_write_lock(&deviceNode->v_bufobj.bo_lock.haikuRW);
if (size == CACHED_BLOCK_SIZE && vp->v_type != VREG if ((size_t)size == cBlockSize && vp->v_type != VREG
&& SLIST_EMPTY(&deviceNode->v_bufobj.bo_emptybufs) == false) { && SLIST_EMPTY(&deviceNode->v_bufobj.bo_emptybufs) == false) {
// Get a buf with no data space. It will just point to a block cache block. // Get a buf with no data space. It will just point to a block cache block.
newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_emptybufs); newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_emptybufs);
@@ -324,8 +332,7 @@ getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
foundExisting = true; foundExisting = true;
} else if (size == (int)fatVolume->pm_fatblocksize } else if (size == (int)fatVolume->pm_fatblocksize
&& SLIST_EMPTY(&deviceNode->v_bufobj.bo_fatbufs) == false) { && SLIST_EMPTY(&deviceNode->v_bufobj.bo_fatbufs) == false) {
// This branch will never be reached in FAT16 or FAT32 so long as pm_fatblocksize and // This branch is only relevant for FAT12 volumes with 512-byte sectors.
// CACHED_BLOCK_SIZE are both 512.
newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_fatbufs); newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_fatbufs);
SLIST_REMOVE_HEAD(&deviceNode->v_bufobj.bo_fatbufs, link); SLIST_REMOVE_HEAD(&deviceNode->v_bufobj.bo_fatbufs, link);
--deviceNode->v_bufobj.bo_fatblocks; --deviceNode->v_bufobj.bo_fatblocks;
@@ -355,7 +362,7 @@ getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
newBuf->b_vreg = vp->v_type == VREG ? vp : NULL; newBuf->b_vreg = vp->v_type == VREG ? vp : NULL;
ASSERT(size == newBuf->b_resid); ASSERT(size == newBuf->b_resid);
cBlockCount = howmany(size, CACHED_BLOCK_SIZE); cBlockCount = howmany(size, cBlockSize);
// Three branches: // Three branches:
// For regular files, copy from file cache into b_data. // For regular files, copy from file cache into b_data.
@@ -397,18 +404,20 @@ getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
put_buf(newBuf); put_buf(newBuf);
return EIO; return EIO;
} }
} else if (size == CACHED_BLOCK_SIZE && vp->v_type != VREG) { } else if ((size_t)size == cBlockSize && vp->v_type != VREG) {
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, dblkno);
if (readOnly == true) if (readOnly == true)
newBuf->b_data = (void*)block_cache_get(blockCache, dblkno); newBuf->b_data = (void*)block_cache_get(blockCache, cachedBlock);
else else
newBuf->b_data = block_cache_get_writable(blockCache, dblkno, -1); newBuf->b_data = block_cache_get_writable(blockCache, cachedBlock, -1);
if (newBuf->b_data == NULL) { if (newBuf->b_data == NULL) {
put_buf(newBuf); put_buf(newBuf);
return EIO; return EIO;
} }
newBuf->b_bufsize = CACHED_BLOCK_SIZE; newBuf->b_bufsize = cBlockSize;
} else { } else {
// need to get more than one cached block and copy them to make a continuous buffer // need to get more than one cached block and copy them to make a continuous buffer
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, dblkno);
status = allocate_data(newBuf, size); status = allocate_data(newBuf, size);
if (status != 0) { if (status != 0) {
put_buf(newBuf); put_buf(newBuf);
@@ -419,25 +428,26 @@ getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag,
// for high block counts, try to get all blocks in one disk read // for high block counts, try to get all blocks in one disk read
if (cBlockCount > 4) { if (cBlockCount > 4) {
size_t prefetchBlocks = cBlockCount; size_t prefetchBlocks = cBlockCount;
block_cache_prefetch(blockCache, dblkno, &prefetchBlocks); block_cache_prefetch(blockCache, cachedBlock, &prefetchBlocks);
} }
#endif // _KERNEL_MODE #endif // _KERNEL_MODE
for (i = 0; i < cBlockCount; i++) { for (i = 0; i < cBlockCount; i++) {
if (readOnly == true) if (readOnly == true)
newBuf->b_bcpointers[i] = (void*)block_cache_get(blockCache, dblkno + i); newBuf->b_bcpointers[i] = (void*)block_cache_get(blockCache, cachedBlock + i);
else else
newBuf->b_bcpointers[i] = block_cache_get_writable(blockCache, dblkno + i, -1); newBuf->b_bcpointers[i] = block_cache_get_writable(blockCache, cachedBlock + i,
-1);
if (newBuf->b_bcpointers[i] == NULL) { if (newBuf->b_bcpointers[i] == NULL) {
put_buf(newBuf); put_buf(newBuf);
return EIO; return EIO;
} }
} }
ASSERT(cBlockCount * CACHED_BLOCK_SIZE == (u_long)newBuf->b_bufsize); ASSERT(cBlockCount * cBlockSize == (u_long)newBuf->b_bufsize);
for (i = 0; i < cBlockCount; i++) { for (i = 0; i < cBlockCount; i++) {
memcpy(newBuf->b_data + (i * CACHED_BLOCK_SIZE), (caddr_t)newBuf->b_bcpointers[i], memcpy(newBuf->b_data + (i * cBlockSize), (caddr_t)newBuf->b_bcpointers[i],
CACHED_BLOCK_SIZE); cBlockSize);
} }
} }
@@ -492,15 +502,19 @@ bwrite(struct buf* bp)
} }
} else { } else {
// block cache // block cache
void* blockCache = bp->b_vp->v_rdev->si_mountpt->mnt_cache; struct mount* bsdVolume = bp->b_vp->v_rdev->si_mountpt;
struct msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data;
void* blockCache = bsdVolume->mnt_cache;
off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, bp->b_blkno);
if (bp->b_owned == false) { if (bp->b_owned == false) {
// single block // single block
status = block_cache_sync_etc(blockCache, bp->b_blkno, 1); status = block_cache_sync_etc(blockCache, cachedBlock, 1);
} else { } else {
// multiple blocks // multiple blocks
status = block_cache_sync_etc(blockCache, bp->b_blkno, status = block_cache_sync_etc(blockCache, cachedBlock,
howmany(bp->b_bufsize, CACHED_BLOCK_SIZE)); howmany(bp->b_bufsize, fatVolume->pm_BytesPerSec));
} }
} }
+3 -1
View File
@@ -14,7 +14,6 @@
#endif #endif
#define CACHED_BLOCK_SIZE 512
#define BUF_CACHE_SIZE 20 #define BUF_CACHE_SIZE 20
// notify every second if the file size has changed // notify every second if the file size has changed
@@ -34,6 +33,9 @@
((fat_volume->pm_bpcluster) / (fat_volume->pm_BlkPerSec * DEV_BSIZE)) ((fat_volume->pm_bpcluster) / (fat_volume->pm_BlkPerSec * DEV_BSIZE))
#define BLOCKS_PER_CLUSTER(fatVolume) (fatVolume->pm_bpcluster / DEV_BSIZE) #define BLOCKS_PER_CLUSTER(fatVolume) (fatVolume->pm_bpcluster / DEV_BSIZE)
// convert a block number from DEV_BSIZE units to volume-specific sector units
#define BLOCK_TO_SECTOR(fat_volume, block) ((block * DEV_BSIZE) / fat_volume->pm_BytesPerSec)
#define vIS_DATA_CLUSTER(fatVolume, cluster) \ #define vIS_DATA_CLUSTER(fatVolume, cluster) \
(((cluster) >= 2) && ((uint32)(cluster) <= fatVolume->pm_maxcluster)) (((cluster) >= 2) && ((uint32)(cluster) <= fatVolume->pm_maxcluster))
#define IS_DATA_CLUSTER(cluster) vIS_DATA_CLUSTER(fatVolume, cluster) #define IS_DATA_CLUSTER(cluster) vIS_DATA_CLUSTER(fatVolume, cluster)
@@ -575,13 +575,14 @@ dosfs_write_fs_stat(fs_volume* volume, const struct fs_info* info, uint32 mask)
// update the label file if there is one // update the label file if there is one
if (bsdVolume->mnt_volentry >= 0) { if (bsdVolume->mnt_volentry >= 0) {
uint8* rootDirBuffer; uint8* rootDirBuffer;
daddr_t rootDirBlock = fatVolume->pm_rootdirblk; daddr_t rootDirSector = fatVolume->pm_rootdirblk;
if (FAT32(fatVolume) == true) if (FAT32(fatVolume) == true)
rootDirBlock = cntobn(fatVolume, fatVolume->pm_rootdirblk); rootDirSector = cntobn(fatVolume, fatVolume->pm_rootdirblk);
rootDirSector = BLOCK_TO_SECTOR(fatVolume, rootDirSector);
daddr_t dirOffset = bsdVolume->mnt_volentry * sizeof(direntry); daddr_t dirOffset = bsdVolume->mnt_volentry * sizeof(direntry);
rootDirBlock += dirOffset / DEV_BSIZE; rootDirSector += dirOffset / fatVolume->pm_BytesPerSec;
status = block_cache_get_writable_etc(blockCache, rootDirBlock, -1, status = block_cache_get_writable_etc(blockCache, rootDirSector, -1,
reinterpret_cast<void**>(&rootDirBuffer)); reinterpret_cast<void**>(&rootDirBuffer));
if (status == B_OK) { if (status == B_OK) {
direntry* label_direntry = reinterpret_cast<direntry*>(rootDirBuffer + dirOffset); direntry* label_direntry = reinterpret_cast<direntry*>(rootDirBuffer + dirOffset);
@@ -593,10 +594,10 @@ dosfs_write_fs_stat(fs_volume* volume, const struct fs_info* info, uint32 mask)
memcpy(label_direntry->deName, name, LABEL_LENGTH); memcpy(label_direntry->deName, name, LABEL_LENGTH);
} else { } else {
INFORM("wfsstat: root directory position check failed\n"); INFORM("wfsstat: root directory position check failed\n");
block_cache_set_dirty(blockCache, rootDirBlock, false, -1); block_cache_set_dirty(blockCache, rootDirSector, false, -1);
status = B_ERROR; status = B_ERROR;
} }
block_cache_put(blockCache, rootDirBlock); block_cache_put(blockCache, rootDirSector);
} }
} else { } else {
// A future enhancement could be to create a label direntry if none exists already. // A future enhancement could be to create a label direntry if none exists already.
@@ -1213,9 +1214,11 @@ _dosfs_fsync(struct vnode* bsdNode)
if (externStatus != B_OK) if (externStatus != B_OK)
REPORT_ERROR(externStatus); REPORT_ERROR(externStatus);
} else { } else {
size_t fatBlocks = (fatVolume->pm_fatsize * fatVolume->pm_FATs) / DEV_BSIZE; off_t fatFirstSector = fatVolume->pm_fatblk / fatVolume->pm_BytesPerSec;
size_t fatSectors = (fatVolume->pm_fatsize * fatVolume->pm_FATs)
/ fatVolume->pm_BytesPerSec;
status_t fatStatus status_t fatStatus
= block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_fatblk, fatBlocks); = block_cache_sync_etc(bsdVolume->mnt_cache, fatFirstSector, fatSectors);
if (fatStatus != B_OK) { if (fatStatus != B_OK) {
externStatus = fatStatus; externStatus = fatStatus;
REPORT_ERROR(fatStatus); REPORT_ERROR(fatStatus);
@@ -3309,14 +3312,6 @@ bsd_device_init(mount* bsdVolume, const dev_t devID, const char* deviceFile, cde
*_readOnly = true; *_readOnly = true;
} }
if (geometry->bytes_per_sector != 0x200) {
// FAT is compatible with 0x400, 0x800, and 0x1000 as well, but this driver has not
// been tested with those values
INFORM("The FAT driver does not currently support write access to volumes with > 1 block "
"per sector\n");
*_readOnly = true;
}
if (*_readOnly == false) { if (*_readOnly == false) {
// reopen it with read/write permissions // reopen it with read/write permissions
close(device->si_fd); close(device->si_fd);
@@ -3652,6 +3647,7 @@ fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags, const c
fatVolume->pm_fatblocksize = 3 * 512; fatVolume->pm_fatblocksize = 3 * 512;
else else
fatVolume->pm_fatblocksize = DEV_BSIZE; fatVolume->pm_fatblocksize = DEV_BSIZE;
fatVolume->pm_fatblocksize = roundup(fatVolume->pm_fatblocksize, fatVolume->pm_BytesPerSec);
fatVolume->pm_fatblocksec = fatVolume->pm_fatblocksize / DEV_BSIZE; fatVolume->pm_fatblocksec = fatVolume->pm_fatblocksize / DEV_BSIZE;
fatVolume->pm_bnshift = ffs(DEV_BSIZE) - 1; fatVolume->pm_bnshift = ffs(DEV_BSIZE) - 1;
@@ -3679,9 +3675,17 @@ fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags, const c
INFORM("si_geometry not initialized\n"); INFORM("si_geometry not initialized\n");
return B_ERROR; return B_ERROR;
} }
// mkfs.fat sets the BytesPerSec value in the BPB based on the -S command line parameter,
// not on the properties of the underlying device.
if (dev->si_geometry->bytes_per_sector > fatVolume->pm_BytesPerSec) {
INFORM("Volume was formatted with %u-byte sectors, but device can support %" B_PRIu32
"-byte sectors.\n", fatVolume->pm_BytesPerSec, dev->si_geometry->bytes_per_sector);
}
uint32 fsSectors = fatVolume->pm_HugeSectors / fatVolume->pm_BlkPerSec; uint32 fsSectors = fatVolume->pm_HugeSectors / fatVolume->pm_BlkPerSec;
// convert back from 512-byte blocks to sectors // convert back from 512-byte blocks to sectors
if (fsSectors > dev->si_mediasize / dev->si_geometry->bytes_per_sector) { if (fsSectors > dev->si_mediasize / fatVolume->pm_BytesPerSec) {
INFORM("dosfs: volume extends past end of partition, mounting read-only\n"); INFORM("dosfs: volume extends past end of partition, mounting read-only\n");
readOnly = true; readOnly = true;
} }
@@ -3691,9 +3695,10 @@ fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags, const c
// given the size of the FAT table, how many sectors do we expect to have in the volume? // given the size of the FAT table, how many sectors do we expect to have in the volume?
uint32 fatSectors = fatVolume->pm_FATsecs / fatVolume->pm_BlkPerSec; uint32 fatSectors = fatVolume->pm_FATsecs / fatVolume->pm_BlkPerSec;
// convert back from 512-byte blocks to sectors // convert back from 512-byte blocks to sectors
uint32 minUsedFatSectors = fatSectors - 8 - (SECTORS_PER_CLUSTER(fatVolume) - 1); uint32 minUsedFatSectors = fatSectors - (fatSectors / 64)
- (SECTORS_PER_CLUSTER(fatVolume) - 1);
// The math recommended by Microsoft to estimate required FAT sectors at initialization // The math recommended by Microsoft to estimate required FAT sectors at initialization
// may overestimate by up to 8 sectors; fsck.fat also aligns the FAT to cluster size // may overestimate the requirement; mkfs.fat also aligns the FAT to cluster size
uint32 fatEntriesPerSector = fatVolume->pm_BytesPerSec / 4; uint32 fatEntriesPerSector = fatVolume->pm_BytesPerSec / 4;
uint32 minFatEntries = minUsedFatSectors * fatEntriesPerSector - (fatEntriesPerSector - 1); uint32 minFatEntries = minUsedFatSectors * fatEntriesPerSector - (fatEntriesPerSector - 1);
// the last utilized sector of a FAT contains at least one entry // the last utilized sector of a FAT contains at least one entry
@@ -3712,13 +3717,9 @@ fat_volume_init(vnode* devvp, mount* bsdVolume, const uint64_t fatFlags, const c
if (status != B_OK) if (status != B_OK)
RETURN_ERROR(status); RETURN_ERROR(status);
// Set up the block cache. // set up the block cache to use sector-size blocks
// If the cached block size is ever changed, functions that work with the block cache
// will need to be re-examined because they assume a size of 512 bytes
// (e.g. dosfs_fsync, read_fsinfo, write_fsinfo, sync_clusters, discard_clusters,
// dosfs_write_fs_stat, and the functions defined in vfs_bio.c).
bsdVolume->mnt_cache bsdVolume->mnt_cache
= block_cache_create(dev->si_fd, fatVolume->pm_HugeSectors, CACHED_BLOCK_SIZE, readOnly); = block_cache_create(dev->si_fd, fsSectors, fatVolume->pm_BytesPerSec, readOnly);
if (bsdVolume->mnt_cache == NULL) if (bsdVolume->mnt_cache == NULL)
return B_ERROR; return B_ERROR;
+72 -53
View File
@@ -154,12 +154,10 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
} }
if (hasPartitionInfo) { if (hasPartitionInfo) {
dprintf("dosfs: partition info: start at %" B_PRIdOFF " bytes " dprintf("dosfs: partition info: start at %" B_PRIdOFF " bytes "
"(%" B_PRIdOFF " sectors), "
"%" B_PRIdOFF " KB, " "%" B_PRIdOFF " KB, "
"%" B_PRIdOFF " MB, " "%" B_PRIdOFF " MB, "
"%" B_PRIdOFF " GB\n", "%" B_PRIdOFF " GB\n",
partitionInfo.offset, partitionInfo.offset,
partitionInfo.offset / 512,
partitionInfo.offset / 1024, partitionInfo.offset / 1024,
partitionInfo.offset / (1024 * 1024), partitionInfo.offset / (1024 * 1024),
partitionInfo.offset / (1024 * 1024 * 1024)); partitionInfo.offset / (1024 * 1024 * 1024));
@@ -171,21 +169,15 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
partitionInfo.size / 1024, partitionInfo.size / 1024,
partitionInfo.size / (1024 * 1024), partitionInfo.size / (1024 * 1024),
partitionInfo.size / (1024 * 1024 * 1024)); partitionInfo.size / (1024 * 1024 * 1024));
#ifndef FS_SHELL
dprintf("dosfs: partition info: physical block size %" B_PRId32 " bytes\n",
partitionInfo.physical_block_size);
#endif
} }
if (!isRawDevice && !hasPartitionInfo) if (!isRawDevice && !hasPartitionInfo)
dprintf("dosfs Warning: couldn't get partition information\n"); dprintf("dosfs Warning: couldn't get partition information\n");
if ((hasBiosGeometry && biosGeometry.bytes_per_sector != 512)
|| (hasDeviceGeometry && deviceGeometry.bytes_per_sector != 512)) {
dprintf("dosfs Error: geometry block size not 512 bytes\n");
return B_ERROR;
} else if (hasPartitionInfo && partitionInfo.logical_block_size != 512) {
dprintf("dosfs: partition logical block size is not 512, "
"it's %" B_PRId32 " bytes\n",
partitionInfo.logical_block_size);
}
if (hasDeviceGeometry && deviceGeometry.read_only) { if (hasDeviceGeometry && deviceGeometry.read_only) {
dprintf("dosfs Error: this is a read-only device\n"); dprintf("dosfs Error: this is a read-only device\n");
return B_ERROR; return B_ERROR;
@@ -195,17 +187,26 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
return B_ERROR; return B_ERROR;
} }
uint64 size = 0; uint64 size = 0;
uint32 sectorSize = 512;
#ifndef FS_SHELL
if (hasPartitionInfo) { if (hasPartitionInfo) {
size = partitionInfo.size; size = partitionInfo.size;
sectorSize = partitionInfo.physical_block_size;
#else
ASSERT(hasPartitionInfo == false);
if (0) {
#endif // !FS_SHELL
} else if (hasDeviceGeometry) { } else if (hasDeviceGeometry) {
size = uint64(deviceGeometry.bytes_per_sector) size = uint64(deviceGeometry.bytes_per_sector)
* deviceGeometry.sectors_per_track * deviceGeometry.cylinder_count * deviceGeometry.sectors_per_track * deviceGeometry.cylinder_count
* deviceGeometry.head_count; * deviceGeometry.head_count;
sectorSize = deviceGeometry.bytes_per_sector;
} else if (hasBiosGeometry) { } else if (hasBiosGeometry) {
size = uint64(biosGeometry.bytes_per_sector) size = uint64(biosGeometry.bytes_per_sector)
* biosGeometry.sectors_per_track * biosGeometry.cylinder_count * biosGeometry.sectors_per_track * biosGeometry.cylinder_count
* biosGeometry.head_count; * biosGeometry.head_count;
sectorSize = biosGeometry.bytes_per_sector;
} else { } else {
// maybe it's just a file // maybe it's just a file
struct stat stat; struct stat stat;
@@ -215,6 +216,12 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
return B_ERROR; return B_ERROR;
} }
size = stat.st_size; size = stat.st_size;
#ifndef FS_SHELL
fs_info parentInfo;
if (fs_stat_dev(stat.st_dev, &parentInfo) == 0)
sectorSize = parentInfo.block_size;
#endif
} }
dprintf("dosfs: size = %" B_PRIu64 " bytes " dprintf("dosfs: size = %" B_PRIu64 " bytes "
@@ -223,18 +230,19 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
"%" B_PRIu64 " MB, " "%" B_PRIu64 " MB, "
"%" B_PRIu64 " GB\n", "%" B_PRIu64 " GB\n",
size, size,
size / 512, size / sectorSize,
size / 1024, size / 1024,
size / (1024 * 1024), size / (1024 * 1024),
size / (1024 * 1024 * 1024)); size / (1024 * 1024 * 1024));
dprintf("dosfs: sector size = %" B_PRIu32 " bytes\n", sectorSize);
uint64 sectorCount = size / 512; uint64 sectorCount = size / sectorSize;
if (sectorCount > UINT_MAX) { if (sectorCount > UINT_MAX) {
// The FAT spec only provides 32 bits to store the sector count on disk. // The FAT spec only provides 32 bits to store the sector count on disk.
dprintf("dosfs Warning: sector count %" B_PRIu64 " won't fit in the FAT BPB. Only the " dprintf("dosfs Warning: sector count %" B_PRIu64 " won't fit in the FAT BPB. Only the "
"first %u sectors will be used\n", sectorCount, UINT_MAX); "first %u sectors will be used\n", sectorCount, UINT_MAX);
sectorCount = UINT_MAX; sectorCount = UINT_MAX;
size = sectorCount * 512; size = sectorCount * sectorSize;
} }
if (fatbits == 0) { if (fatbits == 0) {
@@ -254,72 +262,75 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
return B_ERROR; return B_ERROR;
} }
uint64 adjustedSize = (size * 512) / sectorSize;
// The volume size cutoffs recommended by Microsoft to determine sectors per cluster
// assume 512-byte sectors.
int sectorPerCluster; int sectorPerCluster;
sectorPerCluster = 0; sectorPerCluster = 0;
if (fatbits == 12) { if (fatbits == 12) {
sectorPerCluster = 0; sectorPerCluster = 0;
if (size < 16777216LL) if (adjustedSize < 16777216LL)
sectorPerCluster = 8; sectorPerCluster = 8;
if (size <= 2949120) if (adjustedSize <= 2949120)
sectorPerCluster = 2; sectorPerCluster = 2;
if (size <= 1474560) if (adjustedSize <= 1474560)
sectorPerCluster = 1; sectorPerCluster = 1;
if (size <= 737280) { if (adjustedSize <= 737280) {
// We follow Microsoft guidance in increasing cluster size for the smallest disks. // We follow Microsoft guidance in increasing cluster size for the smallest disks.
// The idea was probably to keep the FAT from taking up a too much of a small disk. // The idea was probably to keep the FAT from taking up a too much of a small disk.
sectorPerCluster = 2; sectorPerCluster = 2;
} }
} else if (fatbits == 16) { } else if (fatbits == 16) {
sectorPerCluster = 0; //larger than 2 GB must fail sectorPerCluster = 0; //larger than 2 GB must fail
if (size <= (2048 * 1024 * 1024LL)) // up to 2GB, use 32k clusters if (adjustedSize <= (2048 * 1024 * 1024LL)) // up to 2GB, use 32k clusters
sectorPerCluster = 64; sectorPerCluster = 64;
if (size <= (1024 * 1024 * 1024LL)) // up to 1GB, use 16k clusters if (adjustedSize <= (1024 * 1024 * 1024LL)) // up to 1GB, use 16k clusters
sectorPerCluster = 32; sectorPerCluster = 32;
if (size <= (512 * 1024 * 1024LL)) // up to 512MB, use 8k clusters if (adjustedSize <= (512 * 1024 * 1024LL)) // up to 512MB, use 8k clusters
sectorPerCluster = 16; sectorPerCluster = 16;
if (size <= (256 * 1024 * 1024LL)) // up to 256MB, use 4k clusters if (adjustedSize <= (256 * 1024 * 1024LL)) // up to 256MB, use 4k clusters
sectorPerCluster = 8; sectorPerCluster = 8;
if (size <= (128 * 1024 * 1024LL)) // up to 128MB, use 2k clusters if (adjustedSize <= (128 * 1024 * 1024LL)) // up to 128MB, use 2k clusters
sectorPerCluster = 4; sectorPerCluster = 4;
if (size <= (16 * 1024 * 1024LL)) // up to 16MB, use 1k clusters if (adjustedSize <= (16 * 1024 * 1024LL)) // up to 16MB, use 1k clusters
sectorPerCluster = 2; sectorPerCluster = 2;
if (size <= 4182016LL) // smaller than 4.1 MB must fail if (adjustedSize <= FLOPPY_MAX_SIZE) // smaller than this must fail
sectorPerCluster = 0; sectorPerCluster = 0;
} else if (fatbits == 32) { } else if (fatbits == 32) {
sectorPerCluster = 64; // default is 32k clusters sectorPerCluster = 64; // default is 32k clusters
if (size <= (32 * 1024 * 1024 * 1024LL)) { if (adjustedSize <= (32 * 1024 * 1024 * 1024LL)) {
// up to 32GB, use 16k clusters // up to 32GB, use 16k clusters
sectorPerCluster = 32; sectorPerCluster = 32;
} }
if (size <= (16 * 1024 * 1024 * 1024LL)) { if (adjustedSize <= (16 * 1024 * 1024 * 1024LL)) {
// up to 16GB, use 8k clusters // up to 16GB, use 8k clusters
sectorPerCluster = 16; sectorPerCluster = 16;
} }
if (size <= (8 * 1024 * 1024 * 1024LL)) { if (adjustedSize <= (8 * 1024 * 1024 * 1024LL)) {
// up to 8GB, use 4k clusters // up to 8GB, use 4k clusters
sectorPerCluster = 8; sectorPerCluster = 8;
} }
if (size <= (532480 * 512LL)) { if (adjustedSize <= (532480 * 512LL)) {
// up to 260 MB, use 0.5k clusters // up to 260 MB, use 0.5k clusters
sectorPerCluster = 1; sectorPerCluster = 1;
} }
if (size <= (66600 * 512LL)) { if (adjustedSize <= (66600 * 512LL)) {
// smaller than 32.5 MB must fail // smaller than 32.5 MB must fail
sectorPerCluster = 0; sectorPerCluster = 0;
} }
} }
if (sectorPerCluster == 0) { if (sectorPerCluster == 0) {
dprintf("dosfs Error: failed to determine sector per cluster value, " dprintf("dosfs Error: failed to determine sector per cluster value, %" B_PRIu64
"partition too large for %d bit fat\n",fatbits); " partition with %" B_PRIu32 "-byte sectors too large for %d bit fat\n",
size, sectorSize, fatbits);
return B_ERROR; return B_ERROR;
} }
int reservedSectorCount = 0; // avoid compiler warning int reservedSectorCount = 0; // avoid compiler warning
int rootEntryCount = 0; // avoid compiler warning int rootEntryCount = 0; // avoid compiler warning
int numFATs; int numFATs;
int sectorSize;
uint8 biosDriveId; uint8 biosDriveId;
// get bios drive-id, or use 0x80 // get bios drive-id, or use 0x80
@@ -332,7 +343,6 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
// default parameters for the bootsector // default parameters for the bootsector
numFATs = 2; numFATs = 2;
sectorSize = 512;
if (fatbits == 12 || fatbits == 16) if (fatbits == 12 || fatbits == 16)
reservedSectorCount = 1; reservedSectorCount = 1;
if (fatbits == 32) if (fatbits == 32)
@@ -345,12 +355,12 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
rootEntryCount = 0; rootEntryCount = 0;
// Determine FATSize // Determine FATSize
// calculation done as MS recommends // calculation done as MS recommends (with adjustments to account for sector sizes > 512 bytes)
uint64 dskSize = size / sectorSize; uint64 dskSize = size / sectorSize;
uint32 rootDirSectors = ((rootEntryCount * 32) + (sectorSize - 1)) uint32 rootDirSectors = ((rootEntryCount * 32) + (sectorSize - 1))
/ sectorSize; / sectorSize;
uint64 tmpVal1 = dskSize - (reservedSectorCount + rootDirSectors); uint64 tmpVal1 = dskSize - (reservedSectorCount + rootDirSectors);
uint64 tmpVal2 = (256 * sectorPerCluster) + numFATs; uint64 tmpVal2 = (256 * sectorPerCluster * sectorSize / 512) + numFATs;
if (fatbits == 32) if (fatbits == 32)
tmpVal2 = tmpVal2 / 2; tmpVal2 = tmpVal2 / 2;
uint32 FATSize = (tmpVal1 + (tmpVal2 - 1)) / tmpVal2; uint32 FATSize = (tmpVal1 + (tmpVal2 - 1)) / tmpVal2;
@@ -365,16 +375,24 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
dprintf("dosfs Error: cluster count (%" B_PRIu64 ") exceeds FAT12 limit.\n", clusterCount); dprintf("dosfs Error: cluster count (%" B_PRIu64 ") exceeds FAT12 limit.\n", clusterCount);
return B_BAD_VALUE; return B_BAD_VALUE;
} }
if (fatbits == 16 && clusterCount > FAT16_MAX_CLUSTER_COUNT) { if (fatbits == 16 && (clusterCount <= FAT12_MAX_CLUSTER_COUNT
dprintf("dosfs Error: cluster count (%" B_PRIu64 ") exceeds FAT16 limit.\n", clusterCount); || clusterCount > FAT16_MAX_CLUSTER_COUNT)) {
dprintf("dosfs Error: cluster count (%" B_PRIu64 ") not valid for FAT16.\n", clusterCount);
return B_BAD_VALUE; return B_BAD_VALUE;
} }
if (fatbits == 32 && clusterCount > FAT32_MAX_CLUSTER_COUNT) { if (fatbits == 32 && (clusterCount <= FAT16_MAX_CLUSTER_COUNT
dprintf("dosfs Error: cluster count (%" B_PRIu64 ") exceeds FAT32 limit.\n", clusterCount); || clusterCount > FAT32_MAX_CLUSTER_COUNT)) {
dprintf("dosfs Error: cluster count (%" B_PRIu64 ") not valid for FAT32.\n", clusterCount);
return B_BAD_VALUE; return B_BAD_VALUE;
} }
dprintf("dosfs: fatbits = %d, clustersize = %d\n", fatbits, sectorPerCluster * 512); // Verify the calculated FATSize is large enough
if (clusterCount * fatbits / 8 > FATSize * sectorSize) {
dprintf("dosfs Error: FAT size of %" B_PRIu32 " not sufficient for %" B_PRIu64
" %d-bit entries.\n", FATSize, clusterCount, fatbits);
}
dprintf("dosfs: fatbits = %d, clustersize = %d\n", fatbits, sectorPerCluster * sectorSize);
dprintf("dosfs: FAT size is %" B_PRIu32 " sectors\n", FATSize); dprintf("dosfs: FAT size is %" B_PRIu32 " sectors\n", FATSize);
dprintf("dosfs: disk label: %s\n", label); dprintf("dosfs: disk label: %s\n", label);
@@ -473,14 +491,14 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
dprintf("dosfs: Writing FAT\n"); dprintf("dosfs: Writing FAT\n");
char * zerobuffer = (char *)malloc(65536); char * zerobuffer = (char *)malloc(65536);
memset(zerobuffer,0,65536); memset(zerobuffer,0,65536);
int64 bytes_to_write = 512LL * (reservedSectorCount + (numFATs * FATSize) int64 bytes_to_write = static_cast<int64>(sectorSize)
+ rootDirSectors); * (reservedSectorCount + (numFATs * FATSize) + rootDirSectors);
int64 pos = 0; int64 pos = 0;
while (bytes_to_write > 0) { while (bytes_to_write > 0) {
ssize_t writesize = min_c(bytes_to_write, 65536); ssize_t writesize = min_c(bytes_to_write, 65536);
written = write_pos(fd, pos, zerobuffer, writesize); written = write_pos(fd, pos, zerobuffer, writesize);
if (written != writesize) { if (written != writesize) {
dprintf("dosfs Error: write error near sector %" B_PRId64 "\n", pos / 512); dprintf("dosfs Error: write error near sector %" B_PRId64 "\n", pos / sectorSize);
free(zerobuffer); free(zerobuffer);
return B_ERROR; return B_ERROR;
} }
@@ -491,14 +509,15 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
//write boot sector //write boot sector
dprintf("dosfs: Writing boot block\n"); dprintf("dosfs: Writing boot block\n");
written = write_pos(fd, BOOT_SECTOR_NUM * 512, bootsector, 512); written = write_pos(fd, BOOT_SECTOR_NUM * sectorSize, bootsector, 512);
// even if the boot sector is 4096 bytes, we only need to write the first 512
if (written != 512) { if (written != 512) {
dprintf("dosfs Error: write error at sector %d\n", BOOT_SECTOR_NUM); dprintf("dosfs Error: write error at sector %d\n", BOOT_SECTOR_NUM);
return B_ERROR; return B_ERROR;
} }
if (fatbits == 32) { if (fatbits == 32) {
written = write_pos(fd, BACKUP_SECTOR_NUM * 512, bootsector, 512); written = write_pos(fd, BACKUP_SECTOR_NUM * sectorSize, bootsector, 512);
if (written != 512) { if (written != 512) {
dprintf("dosfs Error: write error at sector %d\n", BACKUP_SECTOR_NUM); dprintf("dosfs Error: write error at sector %d\n", BACKUP_SECTOR_NUM);
return B_ERROR; return B_ERROR;
@@ -539,13 +558,13 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
sec[10] = 0xFF; sec[10] = 0xFF;
sec[11] = 0x0F; sec[11] = 0x0F;
} }
written = write_pos(fd, reservedSectorCount * 512, sec, 512); written = write_pos(fd, reservedSectorCount * sectorSize, sec, 512);
if (written != 512) { if (written != 512) {
dprintf("dosfs Error: write error at sector %d\n", reservedSectorCount); dprintf("dosfs Error: write error at sector %d\n", reservedSectorCount);
return B_ERROR; return B_ERROR;
} }
if (numFATs > 1) { if (numFATs > 1) {
written = write_pos(fd, (reservedSectorCount + FATSize) * 512,sec,512); written = write_pos(fd, (reservedSectorCount + FATSize) * sectorSize, sec, 512);
if (written != 512) { if (written != 512) {
dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n", dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n",
reservedSectorCount + FATSize); reservedSectorCount + FATSize);
@@ -566,7 +585,7 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
= B_HOST_TO_LENDIAN_INT32((uint32)free_count); = B_HOST_TO_LENDIAN_INT32((uint32)free_count);
fsinfosector.FSI_Nxt_Free = B_HOST_TO_LENDIAN_INT32(3); fsinfosector.FSI_Nxt_Free = B_HOST_TO_LENDIAN_INT32(3);
fsinfosector.FSI_TrailSig = B_HOST_TO_LENDIAN_INT32(0xAA550000); fsinfosector.FSI_TrailSig = B_HOST_TO_LENDIAN_INT32(0xAA550000);
written = write_pos(fd, FSINFO_SECTOR_NUM * 512, &fsinfosector, 512); written = write_pos(fd, FSINFO_SECTOR_NUM * sectorSize, &fsinfosector, 512);
if (written != 512) { if (written != 512) {
dprintf("dosfs Error: write error at sector %d\n", FSINFO_SECTOR_NUM); dprintf("dosfs Error: write error at sector %d\n", FSINFO_SECTOR_NUM);
return B_ERROR; return B_ERROR;
@@ -580,20 +599,20 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char
memset(data, 0, 512); memset(data, 0, 512);
create_volume_label_sector(data, label); create_volume_label_sector(data, label);
uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize); uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize);
written = write_pos(fd, rootDirSector * 512, data, 512); written = write_pos(fd, rootDirSector * sectorSize, data, 512);
if (written != 512) { if (written != 512) {
dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n", dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n",
rootDirSector); rootDirSector);
return B_ERROR; return B_ERROR;
} }
} else if (fatbits == 32) { } else if (fatbits == 32) {
int size = 512 * sectorPerCluster; int size = sectorSize * sectorPerCluster;
uint8 *cluster = (uint8*)malloc(size); uint8 *cluster = (uint8*)malloc(size);
memset(cluster, 0, size); memset(cluster, 0, size);
create_volume_label_sector(cluster, label); create_volume_label_sector(cluster, label);
uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize) uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize)
+ rootDirSectors; + rootDirSectors;
written = write_pos(fd, rootDirSector * 512, cluster, size); written = write_pos(fd, rootDirSector * sectorSize, cluster, size);
free(cluster); free(cluster);
if (written != size) { if (written != size) {
dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n", rootDirSector); dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n", rootDirSector);
+42 -29
View File
@@ -66,8 +66,11 @@
#include <AutoDeleter.h> #include <AutoDeleter.h>
#include <file_systems/mime_ext_table.h> #include <file_systems/mime_ext_table.h>
#include <kernel.h>
#include <real_time_clock.h> #include <real_time_clock.h>
#include <util/AutoLock.h> #include <util/AutoLock.h>
#else
#include <fssh_kernel_priv.h>
#endif // !FS_SHELL #endif // !FS_SHELL
#include "debug.h" #include "debug.h"
@@ -557,7 +560,8 @@ read_fsinfo(msdosfsmount* volume, const vnode* devNode)
const uint8* buffer; const uint8* buffer;
const struct fsinfo* fsInfo; const struct fsinfo* fsInfo;
status = block_cache_get_etc(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, off_t cachedBlock = BLOCK_TO_SECTOR(volume, volume->pm_fsinfo);
status = block_cache_get_etc(volume->pm_mountp->mnt_cache, cachedBlock,
reinterpret_cast<const void**>(&buffer)); reinterpret_cast<const void**>(&buffer));
if (status != B_OK) if (status != B_OK)
RETURN_ERROR(status); RETURN_ERROR(status);
@@ -575,7 +579,7 @@ read_fsinfo(msdosfsmount* volume, const vnode* devNode)
volume->pm_fsinfo = 0; volume->pm_fsinfo = 0;
} }
block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); block_cache_put(volume->pm_mountp->mnt_cache, cachedBlock);
} }
/* /*
@@ -603,15 +607,16 @@ write_fsinfo(msdosfsmount* volume)
return B_OK; return B_OK;
} }
void* buffer = block_cache_get_writable(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, -1); off_t cachedBlock = BLOCK_TO_SECTOR(volume, volume->pm_fsinfo);
void* buffer = block_cache_get_writable(volume->pm_mountp->mnt_cache, cachedBlock, -1);
if (buffer == NULL) if (buffer == NULL)
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
struct fsinfo* fsInfo = reinterpret_cast<struct fsinfo*>(buffer); struct fsinfo* fsInfo = reinterpret_cast<struct fsinfo*>(buffer);
if (memcmp(fsInfo->fsisig1, "RRaA", 4) != 0 || memcmp(fsInfo->fsisig2, "rrAa", 4) != 0 if (memcmp(fsInfo->fsisig1, "RRaA", 4) != 0 || memcmp(fsInfo->fsisig2, "rrAa", 4) != 0
|| memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) != 0) { || memcmp(fsInfo->fsisig3, "\0\0\125\252", 4) != 0) {
block_cache_set_dirty(volume->pm_mountp->mnt_cache, volume->pm_fsinfo, false, -1); block_cache_set_dirty(volume->pm_mountp->mnt_cache, cachedBlock, false, -1);
block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); block_cache_put(volume->pm_mountp->mnt_cache, cachedBlock);
RETURN_ERROR(B_ERROR); RETURN_ERROR(B_ERROR);
} }
@@ -619,7 +624,7 @@ write_fsinfo(msdosfsmount* volume)
putulong(fsInfo->fsinxtfree, volume->pm_nxtfree); putulong(fsInfo->fsinxtfree, volume->pm_nxtfree);
volume->pm_flags &= ~MSDOSFS_FSIMOD; volume->pm_flags &= ~MSDOSFS_FSIMOD;
block_cache_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); block_cache_put(volume->pm_mountp->mnt_cache, cachedBlock);
return B_OK; return B_OK;
} }
@@ -632,32 +637,36 @@ write_fsinfo(msdosfsmount* volume)
status_t status_t
check_fat(const msdosfsmount* volume) check_fat(const msdosfsmount* volume)
{ {
uint8 fatBuffer[512]; uint32 bytesPerSec = volume->pm_BytesPerSec;
uint8 mirrorBuffer[512]; uint32 fatSectors = volume->pm_FATsecs / volume->pm_BlkPerSec;
// pm_FATsecs is always in units of DEV_BSIZE
uint8 fatBuffer[bytesPerSec];
uint8 mirrorBuffer[bytesPerSec];
// For small FATs, check whether each FAT mirror matches the active FAT. // For small FATs, check whether each FAT mirror matches the active FAT.
// For large FATs, that takes too long, so just check the first block of each FAT. // For large FATs, that takes too long, so just check the first sector of each FAT.
uint32 checkBlocks = volume->pm_FATsecs > 4096 ? 1 : volume->pm_FATsecs; uint32 checkSectors = fatSectors > 4096 ? 1 : fatSectors;
PRINT("check_fat checking %" B_PRIu32 " blocks\n", checkBlocks); PRINT("check_fat checking %" B_PRIu32 " sectors\n", checkSectors);
// for each block // for each sector
for (uint32 i = 0; i < checkBlocks; ++i) { for (uint32 i = 0; i < checkSectors; ++i) {
// read a block from the first/active fat // read a sector from the first/active fat
uint32 resBlocks = volume->pm_ResSectors * volume->pm_BlkPerSec; uint32 resSectors = volume->pm_ResSectors;
off_t position = 512 * (resBlocks + volume->pm_curfat * volume->pm_FATsecs + i); off_t position = bytesPerSec * (resSectors + volume->pm_curfat * fatSectors + i);
ssize_t bytes_read ssize_t bytes_read
= read_pos(volume->pm_dev->si_fd, position, reinterpret_cast<void*>(fatBuffer), 0x200); = read_pos(volume->pm_dev->si_fd, position, reinterpret_cast<void*>(fatBuffer), bytesPerSec);
if (bytes_read != 0x200) if (bytes_read != static_cast<ssize_t>(bytesPerSec))
RETURN_ERROR(B_IO_ERROR); RETURN_ERROR(B_IO_ERROR);
// for each mirror // for each mirror
for (uint32 j = 0; j < volume->pm_FATs; ++j) { for (uint32 j = 0; j < volume->pm_FATs; ++j) {
if (j == volume->pm_curfat) if (j == volume->pm_curfat)
continue; continue;
position = 512 * (resBlocks + volume->pm_FATsecs * j + i); position = bytesPerSec * (resSectors + fatSectors * j + i);
bytes_read = read_pos(volume->pm_dev->si_fd, position, bytes_read = read_pos(volume->pm_dev->si_fd, position,
reinterpret_cast<void*>(mirrorBuffer), 0x200); reinterpret_cast<void*>(mirrorBuffer), bytesPerSec);
if (bytes_read != 0x200) if (bytes_read != static_cast<ssize_t>(bytesPerSec))
RETURN_ERROR(B_IO_ERROR); RETURN_ERROR(B_IO_ERROR);
if (i == 0 && mirrorBuffer[0] != volume->pm_Media) { if (i == 0 && mirrorBuffer[0] != volume->pm_Media) {
@@ -670,7 +679,7 @@ check_fat(const msdosfsmount* volume)
// checking for exact matches of fats is too // checking for exact matches of fats is too
// restrictive; allow these to go through in // restrictive; allow these to go through in
// case the fat is corrupted for some reason // case the fat is corrupted for some reason
if (memcmp(fatBuffer, mirrorBuffer, 0x200)) { if (memcmp(fatBuffer, mirrorBuffer, bytesPerSec)) {
INFORM("FAT %" B_PRIu32 " doesn't match active FAT (%u) on %s.\n" INFORM("FAT %" B_PRIu32 " doesn't match active FAT (%u) on %s.\n"
"Install dosfstools and use fsck.fat to inspect %s.\n", "Install dosfstools and use fsck.fat to inspect %s.\n",
j, volume->pm_curfat, volume->pm_dev->si_device, volume->pm_dev->si_device); j, volume->pm_curfat, volume->pm_dev->si_device, volume->pm_dev->si_device);
@@ -1081,13 +1090,17 @@ sync_clusters(vnode* bsdNode)
u_long cluster = fatNode->de_dirclust; u_long cluster = fatNode->de_dirclust;
if (cluster == MSDOSFSROOT) { if (cluster == MSDOSFSROOT) {
status = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_rootdirblk, off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, fatVolume->pm_rootdirblk);
fatVolume->pm_rootdirsize); size_t numBlocks =
HOWMANY(fatVolume->pm_rootdirsize * DEV_BSIZE, fatVolume->pm_BytesPerSec);
status = block_cache_sync_etc(bsdVolume->mnt_cache, cachedBlock, numBlocks);
} else { } else {
status_t fatStatus = B_OK; status_t fatStatus = B_OK;
while ((IS_DATA_CLUSTER(cluster)) && status == B_OK && fatStatus == B_OK) { while ((IS_DATA_CLUSTER(cluster)) && status == B_OK && fatStatus == B_OK) {
status = block_cache_sync_etc(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster), off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, cntobn(fatVolume, cluster));
BLOCKS_PER_CLUSTER(fatVolume)); // changed from de_cn2bn
status = block_cache_sync_etc(bsdVolume->mnt_cache, cachedBlock,
SECTORS_PER_CLUSTER(fatVolume));
fatStatus = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); fatStatus = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
} }
if (fatStatus != B_OK) if (fatStatus != B_OK)
@@ -1117,14 +1130,14 @@ discard_clusters(vnode* bsdNode, off_t newLength)
// Typically we are discarding all clusters associated with a directory. However, in // Typically we are discarding all clusters associated with a directory. However, in
// the case of an error, the driver might shrink a directory to undo an attempted expansion, // the case of an error, the driver might shrink a directory to undo an attempted expansion,
// as in createde. // as in createde.
for (uint32 skip = howmany(newLength, fatVolume->pm_bpcluster); skip > 0 && status == B_OK; for (uint32 skip = HOWMANY(newLength, fatVolume->pm_bpcluster); skip > 0 && status == B_OK;
skip--) { skip--) {
status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
} }
while ((IS_DATA_CLUSTER(cluster)) && status == B_OK) { while ((IS_DATA_CLUSTER(cluster)) && status == B_OK) {
block_cache_discard(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster), off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, cntobn(fatVolume, cluster));
BLOCKS_PER_CLUSTER(fatVolume)); block_cache_discard(bsdVolume->mnt_cache, cachedBlock, SECTORS_PER_CLUSTER(fatVolume));
status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0));
} }