diff --git a/headers/private/fs_shell/fssh_kernel_priv.h b/headers/private/fs_shell/fssh_kernel_priv.h index db7df042af..7999235214 100644 --- a/headers/private/fs_shell/fssh_kernel_priv.h +++ b/headers/private/fs_shell/fssh_kernel_priv.h @@ -46,6 +46,7 @@ #define ROUNDUP(a, b) (((a) + ((b)-1)) & ~((b)-1)) #define ROUNDDOWN(a, b) (((a) / (b)) * (b)) +#define HOWMANY(a, b) (((a) + ((b) - 1)) / (b)) #define CHECK_BIT(a, b) ((a) & (1 << (b))) diff --git a/src/add-ons/kernel/file_systems/fat/bsd/kern/vfs_bio.c b/src/add-ons/kernel/file_systems/fat/bsd/kern/vfs_bio.c index 5495ea35fa..4f1663a0cb 100644 --- a/src/add-ons/kernel/file_systems/fat/bsd/kern/vfs_bio.c +++ b/src/add-ons/kernel/file_systems/fat/bsd/kern/vfs_bio.c @@ -138,15 +138,16 @@ _bwrite(struct buf* buf) return EIO; } else if (buf->b_owned == false) { // 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 { + 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 - uint32 cBlockCount = buf->b_bufsize / CACHED_BLOCK_SIZE; + uint32 cBlockCount = buf->b_bufsize / cBlockSize; uint32 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), - CACHED_BLOCK_SIZE); - block_cache_put(blockCache, buf->b_blkno + i); + memcpy((caddr_t)buf->b_bcpointers[i], buf->b_data + (i * cBlockSize), cBlockSize); + block_cache_put(blockCache, cachedBlock + i); buf->b_bcpointers[i] = NULL; } } @@ -184,13 +185,16 @@ bawrite(struct buf* bp) if (bp->b_vreg->v_resizing == false) file_cache_sync(bp->b_vreg->v_cache); } 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) { - block_cache_sync_etc(blockCache, bp->b_blkno, 1); + block_cache_sync_etc(blockCache, cachedBlock, 1); } else { - block_cache_sync_etc(blockCache, bp->b_blkno, - howmany(bp->b_bufsize, CACHED_BLOCK_SIZE)); + block_cache_sync_etc(blockCache, cachedBlock, + 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 msdosfsmount* fatVolume = (struct msdosfsmount*)bsdVolume->mnt_data; void* blockCache = bsdVolume->mnt_cache; + off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, bp->b_blkno); bool readOnly = MOUNTED_READ_ONLY(VFSTOMSDOSFS(bsdVolume)); if (bp->b_owned == false) { if (readOnly == true) - block_cache_set_dirty(blockCache, bp->b_blkno, false, -1); - block_cache_put(blockCache, bp->b_blkno); + block_cache_set_dirty(blockCache, cachedBlock, false, -1); + block_cache_put(blockCache, cachedBlock); put_buf(bp); } else { - uint32 cBlockCount = bp->b_bufsize / CACHED_BLOCK_SIZE; + uint32 cBlockCount = bp->b_bufsize / fatVolume->pm_BytesPerSec; uint32 i; for (i = 0; i < cBlockCount && bp->b_bcpointers[i] != NULL; ++i) { if (readOnly == true) - block_cache_set_dirty(blockCache, bp->b_blkno + i, false, -1); - block_cache_put(blockCache, bp->b_blkno + i); + block_cache_set_dirty(blockCache, cachedBlock + i, false, -1); + block_cache_put(blockCache, cachedBlock + i); 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; void* blockCache = NULL; + size_t cBlockSize = 0; uint32 cBlockCount; // the number of block cache blocks spanned by the client's request struct buf* newBuf = NULL; @@ -304,11 +311,12 @@ getblkx(struct vnode* vp, daddr_t blkno, daddr_t dblkno, int size, int slpflag, } else { return ENOTSUP; } + cBlockSize = fatVolume->pm_BytesPerSec; // Before allocating memory for a new struct buf, try to reuse an existing one // in the device vnode's lists. 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) { // Get a buf with no data space. It will just point to a block cache block. 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; } else if (size == (int)fatVolume->pm_fatblocksize && SLIST_EMPTY(&deviceNode->v_bufobj.bo_fatbufs) == false) { - // This branch will never be reached in FAT16 or FAT32 so long as pm_fatblocksize and - // CACHED_BLOCK_SIZE are both 512. + // This branch is only relevant for FAT12 volumes with 512-byte sectors. newBuf = SLIST_FIRST(&deviceNode->v_bufobj.bo_fatbufs); SLIST_REMOVE_HEAD(&deviceNode->v_bufobj.bo_fatbufs, link); --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; ASSERT(size == newBuf->b_resid); - cBlockCount = howmany(size, CACHED_BLOCK_SIZE); + cBlockCount = howmany(size, cBlockSize); // Three branches: // 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); 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) - newBuf->b_data = (void*)block_cache_get(blockCache, dblkno); + newBuf->b_data = (void*)block_cache_get(blockCache, cachedBlock); 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) { put_buf(newBuf); return EIO; } - newBuf->b_bufsize = CACHED_BLOCK_SIZE; + newBuf->b_bufsize = cBlockSize; } else { // 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); if (status != 0) { 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 if (cBlockCount > 4) { size_t prefetchBlocks = cBlockCount; - block_cache_prefetch(blockCache, dblkno, &prefetchBlocks); + block_cache_prefetch(blockCache, cachedBlock, &prefetchBlocks); } #endif // _KERNEL_MODE for (i = 0; i < cBlockCount; i++) { 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 - 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) { put_buf(newBuf); 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++) { - memcpy(newBuf->b_data + (i * CACHED_BLOCK_SIZE), (caddr_t)newBuf->b_bcpointers[i], - CACHED_BLOCK_SIZE); + memcpy(newBuf->b_data + (i * cBlockSize), (caddr_t)newBuf->b_bcpointers[i], + cBlockSize); } } @@ -492,15 +502,19 @@ bwrite(struct buf* bp) } } else { // 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) { // single block - status = block_cache_sync_etc(blockCache, bp->b_blkno, 1); + status = block_cache_sync_etc(blockCache, cachedBlock, 1); } else { // multiple blocks - status = block_cache_sync_etc(blockCache, bp->b_blkno, - howmany(bp->b_bufsize, CACHED_BLOCK_SIZE)); + status = block_cache_sync_etc(blockCache, cachedBlock, + howmany(bp->b_bufsize, fatVolume->pm_BytesPerSec)); } } diff --git a/src/add-ons/kernel/file_systems/fat/dosfs.h b/src/add-ons/kernel/file_systems/fat/dosfs.h index f53f280f9e..81c04d6a72 100644 --- a/src/add-ons/kernel/file_systems/fat/dosfs.h +++ b/src/add-ons/kernel/file_systems/fat/dosfs.h @@ -14,7 +14,6 @@ #endif -#define CACHED_BLOCK_SIZE 512 #define BUF_CACHE_SIZE 20 // notify every second if the file size has changed @@ -34,6 +33,9 @@ ((fat_volume->pm_bpcluster) / (fat_volume->pm_BlkPerSec * 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) \ (((cluster) >= 2) && ((uint32)(cluster) <= fatVolume->pm_maxcluster)) #define IS_DATA_CLUSTER(cluster) vIS_DATA_CLUSTER(fatVolume, cluster) diff --git a/src/add-ons/kernel/file_systems/fat/kernel_interface.cpp b/src/add-ons/kernel/file_systems/fat/kernel_interface.cpp index 76e05d46f4..86cff0403e 100644 --- a/src/add-ons/kernel/file_systems/fat/kernel_interface.cpp +++ b/src/add-ons/kernel/file_systems/fat/kernel_interface.cpp @@ -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 if (bsdVolume->mnt_volentry >= 0) { uint8* rootDirBuffer; - daddr_t rootDirBlock = fatVolume->pm_rootdirblk; + daddr_t rootDirSector = fatVolume->pm_rootdirblk; 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); - 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(&rootDirBuffer)); if (status == B_OK) { direntry* label_direntry = reinterpret_cast(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); } else { 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; } - block_cache_put(blockCache, rootDirBlock); + block_cache_put(blockCache, rootDirSector); } } else { // 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) REPORT_ERROR(externStatus); } 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 - = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_fatblk, fatBlocks); + = block_cache_sync_etc(bsdVolume->mnt_cache, fatFirstSector, fatSectors); if (fatStatus != B_OK) { externStatus = fatStatus; REPORT_ERROR(fatStatus); @@ -3309,14 +3312,6 @@ bsd_device_init(mount* bsdVolume, const dev_t devID, const char* deviceFile, cde *_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) { // reopen it with read/write permissions 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; else 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_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"); 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; // 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"); 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? uint32 fatSectors = fatVolume->pm_FATsecs / fatVolume->pm_BlkPerSec; // 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 - // 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 minFatEntries = minUsedFatSectors * fatEntriesPerSector - (fatEntriesPerSector - 1); // 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) RETURN_ERROR(status); - // Set up the block cache. - // 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). + // set up the block cache to use sector-size blocks 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) return B_ERROR; diff --git a/src/add-ons/kernel/file_systems/fat/mkdos.cpp b/src/add-ons/kernel/file_systems/fat/mkdos.cpp index 5a32fb27c8..6b34f3065e 100644 --- a/src/add-ons/kernel/file_systems/fat/mkdos.cpp +++ b/src/add-ons/kernel/file_systems/fat/mkdos.cpp @@ -154,12 +154,10 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char } if (hasPartitionInfo) { dprintf("dosfs: partition info: start at %" B_PRIdOFF " bytes " - "(%" B_PRIdOFF " sectors), " "%" B_PRIdOFF " KB, " "%" B_PRIdOFF " MB, " "%" B_PRIdOFF " GB\n", partitionInfo.offset, - partitionInfo.offset / 512, partitionInfo.offset / 1024, partitionInfo.offset / (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 * 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) 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) { dprintf("dosfs Error: this is a read-only device\n"); return B_ERROR; @@ -195,17 +187,26 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char return B_ERROR; } uint64 size = 0; + uint32 sectorSize = 512; +#ifndef FS_SHELL if (hasPartitionInfo) { size = partitionInfo.size; + sectorSize = partitionInfo.physical_block_size; +#else + ASSERT(hasPartitionInfo == false); + if (0) { +#endif // !FS_SHELL } else if (hasDeviceGeometry) { size = uint64(deviceGeometry.bytes_per_sector) * deviceGeometry.sectors_per_track * deviceGeometry.cylinder_count * deviceGeometry.head_count; + sectorSize = deviceGeometry.bytes_per_sector; } else if (hasBiosGeometry) { size = uint64(biosGeometry.bytes_per_sector) * biosGeometry.sectors_per_track * biosGeometry.cylinder_count * biosGeometry.head_count; + sectorSize = biosGeometry.bytes_per_sector; } else { // maybe it's just a file struct stat stat; @@ -215,6 +216,12 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char return B_ERROR; } 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 " @@ -223,18 +230,19 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char "%" B_PRIu64 " MB, " "%" B_PRIu64 " GB\n", size, - size / 512, + size / sectorSize, size / 1024, size / (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) { // 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 " "first %u sectors will be used\n", sectorCount, UINT_MAX); sectorCount = UINT_MAX; - size = sectorCount * 512; + size = sectorCount * sectorSize; } if (fatbits == 0) { @@ -254,72 +262,75 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char 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; sectorPerCluster = 0; if (fatbits == 12) { sectorPerCluster = 0; - if (size < 16777216LL) + if (adjustedSize < 16777216LL) sectorPerCluster = 8; - if (size <= 2949120) + if (adjustedSize <= 2949120) sectorPerCluster = 2; - if (size <= 1474560) + if (adjustedSize <= 1474560) sectorPerCluster = 1; - if (size <= 737280) { + if (adjustedSize <= 737280) { // 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. sectorPerCluster = 2; } } else if (fatbits == 16) { 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; - if (size <= (1024 * 1024 * 1024LL)) // up to 1GB, use 16k clusters + if (adjustedSize <= (1024 * 1024 * 1024LL)) // up to 1GB, use 16k clusters 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; - if (size <= (256 * 1024 * 1024LL)) // up to 256MB, use 4k clusters + if (adjustedSize <= (256 * 1024 * 1024LL)) // up to 256MB, use 4k clusters 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; - if (size <= (16 * 1024 * 1024LL)) // up to 16MB, use 1k clusters + if (adjustedSize <= (16 * 1024 * 1024LL)) // up to 16MB, use 1k clusters sectorPerCluster = 2; - if (size <= 4182016LL) // smaller than 4.1 MB must fail + if (adjustedSize <= FLOPPY_MAX_SIZE) // smaller than this must fail sectorPerCluster = 0; } else if (fatbits == 32) { sectorPerCluster = 64; // default is 32k clusters - if (size <= (32 * 1024 * 1024 * 1024LL)) { + if (adjustedSize <= (32 * 1024 * 1024 * 1024LL)) { // up to 32GB, use 16k clusters sectorPerCluster = 32; } - if (size <= (16 * 1024 * 1024 * 1024LL)) { + if (adjustedSize <= (16 * 1024 * 1024 * 1024LL)) { // up to 16GB, use 8k clusters sectorPerCluster = 16; } - if (size <= (8 * 1024 * 1024 * 1024LL)) { + if (adjustedSize <= (8 * 1024 * 1024 * 1024LL)) { // up to 8GB, use 4k clusters sectorPerCluster = 8; } - if (size <= (532480 * 512LL)) { + if (adjustedSize <= (532480 * 512LL)) { // up to 260 MB, use 0.5k clusters sectorPerCluster = 1; } - if (size <= (66600 * 512LL)) { + if (adjustedSize <= (66600 * 512LL)) { // smaller than 32.5 MB must fail sectorPerCluster = 0; } } if (sectorPerCluster == 0) { - dprintf("dosfs Error: failed to determine sector per cluster value, " - "partition too large for %d bit fat\n",fatbits); + dprintf("dosfs Error: failed to determine sector per cluster value, %" B_PRIu64 + " partition with %" B_PRIu32 "-byte sectors too large for %d bit fat\n", + size, sectorSize, fatbits); return B_ERROR; } int reservedSectorCount = 0; // avoid compiler warning int rootEntryCount = 0; // avoid compiler warning int numFATs; - int sectorSize; uint8 biosDriveId; // 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 numFATs = 2; - sectorSize = 512; if (fatbits == 12 || fatbits == 16) reservedSectorCount = 1; if (fatbits == 32) @@ -345,12 +355,12 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char rootEntryCount = 0; // 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; uint32 rootDirSectors = ((rootEntryCount * 32) + (sectorSize - 1)) / sectorSize; uint64 tmpVal1 = dskSize - (reservedSectorCount + rootDirSectors); - uint64 tmpVal2 = (256 * sectorPerCluster) + numFATs; + uint64 tmpVal2 = (256 * sectorPerCluster * sectorSize / 512) + numFATs; if (fatbits == 32) tmpVal2 = tmpVal2 / 2; 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); return B_BAD_VALUE; } - if (fatbits == 16 && clusterCount > FAT16_MAX_CLUSTER_COUNT) { - dprintf("dosfs Error: cluster count (%" B_PRIu64 ") exceeds FAT16 limit.\n", clusterCount); + if (fatbits == 16 && (clusterCount <= FAT12_MAX_CLUSTER_COUNT + || clusterCount > FAT16_MAX_CLUSTER_COUNT)) { + dprintf("dosfs Error: cluster count (%" B_PRIu64 ") not valid for FAT16.\n", clusterCount); return B_BAD_VALUE; } - if (fatbits == 32 && clusterCount > FAT32_MAX_CLUSTER_COUNT) { - dprintf("dosfs Error: cluster count (%" B_PRIu64 ") exceeds FAT32 limit.\n", clusterCount); + if (fatbits == 32 && (clusterCount <= FAT16_MAX_CLUSTER_COUNT + || clusterCount > FAT32_MAX_CLUSTER_COUNT)) { + dprintf("dosfs Error: cluster count (%" B_PRIu64 ") not valid for FAT32.\n", clusterCount); 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: 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"); char * zerobuffer = (char *)malloc(65536); memset(zerobuffer,0,65536); - int64 bytes_to_write = 512LL * (reservedSectorCount + (numFATs * FATSize) - + rootDirSectors); + int64 bytes_to_write = static_cast(sectorSize) + * (reservedSectorCount + (numFATs * FATSize) + rootDirSectors); int64 pos = 0; while (bytes_to_write > 0) { ssize_t writesize = min_c(bytes_to_write, 65536); written = write_pos(fd, pos, zerobuffer, 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); return B_ERROR; } @@ -491,14 +509,15 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char //write boot sector 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) { dprintf("dosfs Error: write error at sector %d\n", BOOT_SECTOR_NUM); return B_ERROR; } 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) { dprintf("dosfs Error: write error at sector %d\n", BACKUP_SECTOR_NUM); return B_ERROR; @@ -539,13 +558,13 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char sec[10] = 0xFF; sec[11] = 0x0F; } - written = write_pos(fd, reservedSectorCount * 512, sec, 512); + written = write_pos(fd, reservedSectorCount * sectorSize, sec, 512); if (written != 512) { dprintf("dosfs Error: write error at sector %d\n", reservedSectorCount); return B_ERROR; } if (numFATs > 1) { - written = write_pos(fd, (reservedSectorCount + FATSize) * 512,sec,512); + written = write_pos(fd, (reservedSectorCount + FATSize) * sectorSize, sec, 512); if (written != 512) { dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n", 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); fsinfosector.FSI_Nxt_Free = B_HOST_TO_LENDIAN_INT32(3); 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) { dprintf("dosfs Error: write error at sector %d\n", FSINFO_SECTOR_NUM); return B_ERROR; @@ -580,20 +599,20 @@ _dosfs_initialize(int fd, partition_id partitionID, const char* name, const char memset(data, 0, 512); create_volume_label_sector(data, label); uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize); - written = write_pos(fd, rootDirSector * 512, data, 512); + written = write_pos(fd, rootDirSector * sectorSize, data, 512); if (written != 512) { dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n", rootDirSector); return B_ERROR; } } else if (fatbits == 32) { - int size = 512 * sectorPerCluster; + int size = sectorSize * sectorPerCluster; uint8 *cluster = (uint8*)malloc(size); memset(cluster, 0, size); create_volume_label_sector(cluster, label); uint32 rootDirSector = reservedSectorCount + (numFATs * FATSize) + rootDirSectors; - written = write_pos(fd, rootDirSector * 512, cluster, size); + written = write_pos(fd, rootDirSector * sectorSize, cluster, size); free(cluster); if (written != size) { dprintf("dosfs Error: write error at sector %" B_PRIu32 "\n", rootDirSector); diff --git a/src/add-ons/kernel/file_systems/fat/support.cpp b/src/add-ons/kernel/file_systems/fat/support.cpp index f8aa33db41..07bd46afcf 100644 --- a/src/add-ons/kernel/file_systems/fat/support.cpp +++ b/src/add-ons/kernel/file_systems/fat/support.cpp @@ -66,8 +66,11 @@ #include #include +#include #include #include +#else +#include #endif // !FS_SHELL #include "debug.h" @@ -557,7 +560,8 @@ read_fsinfo(msdosfsmount* volume, const vnode* devNode) const uint8* buffer; 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(&buffer)); if (status != B_OK) RETURN_ERROR(status); @@ -575,7 +579,7 @@ read_fsinfo(msdosfsmount* volume, const vnode* devNode) 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; } - 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) RETURN_ERROR(B_ERROR); struct fsinfo* fsInfo = reinterpret_cast(buffer); if (memcmp(fsInfo->fsisig1, "RRaA", 4) != 0 || memcmp(fsInfo->fsisig2, "rrAa", 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_put(volume->pm_mountp->mnt_cache, volume->pm_fsinfo); + block_cache_set_dirty(volume->pm_mountp->mnt_cache, cachedBlock, false, -1); + block_cache_put(volume->pm_mountp->mnt_cache, cachedBlock); RETURN_ERROR(B_ERROR); } @@ -619,7 +624,7 @@ write_fsinfo(msdosfsmount* volume) putulong(fsInfo->fsinxtfree, volume->pm_nxtfree); 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; } @@ -632,32 +637,36 @@ write_fsinfo(msdosfsmount* volume) status_t check_fat(const msdosfsmount* volume) { - uint8 fatBuffer[512]; - uint8 mirrorBuffer[512]; + uint32 bytesPerSec = volume->pm_BytesPerSec; + 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 large FATs, that takes too long, so just check the first block of each FAT. - uint32 checkBlocks = volume->pm_FATsecs > 4096 ? 1 : volume->pm_FATsecs; - PRINT("check_fat checking %" B_PRIu32 " blocks\n", checkBlocks); + // For large FATs, that takes too long, so just check the first sector of each FAT. + uint32 checkSectors = fatSectors > 4096 ? 1 : fatSectors; + PRINT("check_fat checking %" B_PRIu32 " sectors\n", checkSectors); - // for each block - for (uint32 i = 0; i < checkBlocks; ++i) { - // read a block from the first/active fat - uint32 resBlocks = volume->pm_ResSectors * volume->pm_BlkPerSec; - off_t position = 512 * (resBlocks + volume->pm_curfat * volume->pm_FATsecs + i); + // for each sector + for (uint32 i = 0; i < checkSectors; ++i) { + // read a sector from the first/active fat + uint32 resSectors = volume->pm_ResSectors; + off_t position = bytesPerSec * (resSectors + volume->pm_curfat * fatSectors + i); ssize_t bytes_read - = read_pos(volume->pm_dev->si_fd, position, reinterpret_cast(fatBuffer), 0x200); - if (bytes_read != 0x200) + = read_pos(volume->pm_dev->si_fd, position, reinterpret_cast(fatBuffer), bytesPerSec); + if (bytes_read != static_cast(bytesPerSec)) RETURN_ERROR(B_IO_ERROR); // for each mirror for (uint32 j = 0; j < volume->pm_FATs; ++j) { if (j == volume->pm_curfat) 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, - reinterpret_cast(mirrorBuffer), 0x200); - if (bytes_read != 0x200) + reinterpret_cast(mirrorBuffer), bytesPerSec); + if (bytes_read != static_cast(bytesPerSec)) RETURN_ERROR(B_IO_ERROR); 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 // restrictive; allow these to go through in // 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" "Install dosfstools and use fsck.fat to inspect %s.\n", 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; if (cluster == MSDOSFSROOT) { - status = block_cache_sync_etc(bsdVolume->mnt_cache, fatVolume->pm_rootdirblk, - fatVolume->pm_rootdirsize); + off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, fatVolume->pm_rootdirblk); + size_t numBlocks = + HOWMANY(fatVolume->pm_rootdirsize * DEV_BSIZE, fatVolume->pm_BytesPerSec); + status = block_cache_sync_etc(bsdVolume->mnt_cache, cachedBlock, numBlocks); } else { status_t 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), - BLOCKS_PER_CLUSTER(fatVolume)); + off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, cntobn(fatVolume, cluster)); + // 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)); } 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 // the case of an error, the driver might shrink a directory to undo an attempted expansion, // 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--) { status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); } while ((IS_DATA_CLUSTER(cluster)) && status == B_OK) { - block_cache_discard(bsdVolume->mnt_cache, de_cn2bn(fatVolume, cluster), - BLOCKS_PER_CLUSTER(fatVolume)); + off_t cachedBlock = BLOCK_TO_SECTOR(fatVolume, cntobn(fatVolume, cluster)); + block_cache_discard(bsdVolume->mnt_cache, cachedBlock, SECTORS_PER_CLUSTER(fatVolume)); status = B_FROM_POSIX_ERROR(fatentry(FAT_GET, fatVolume, cluster, &cluster, 0)); }