1
0
Fork 0
forked from len0rd/rockbox

fat: Allow use of variable logical sector sizes

Only used if MAX_LOG_SECTOR_SIZE is defined

This allows a single build to seamlessly work with (eg) 512B or 4K sectors.

Change-Id: I85d2a6612afca6a1d7a3bd49c588b5745ab2b220
This commit is contained in:
Solomon Peachy 2024-11-04 19:48:17 -05:00
parent e519356c47
commit 7ecab006c0
3 changed files with 84 additions and 41 deletions

View file

@ -103,12 +103,12 @@ static int alloc_filestr(struct filestr_desc **filep)
} }
/* return the file size in sectors */ /* return the file size in sectors */
static inline unsigned long filesize_sectors(file_size_t size) static inline unsigned long filesize_sectors(uint16_t sector_size, file_size_t size)
{ {
/* overflow proof whereas "(x + y - 1) / y" is not */ /* overflow proof whereas "(x + y - 1) / y" is not */
unsigned long numsectors = size / SECTOR_SIZE; unsigned long numsectors = size / sector_size;
if (size % SECTOR_SIZE) if (size % sector_size)
numsectors++; numsectors++;
return numsectors; return numsectors;
@ -196,10 +196,11 @@ file_error:
return rc; return rc;
} }
/* Handle syncing all file's streams to the truncation */ /* Handle syncing all file's streams to the truncation */
static void handle_truncate(struct filestr_desc * const file, file_size_t size) static void handle_truncate(struct filestr_desc * const file, file_size_t size)
{ {
unsigned long filesectors = filesize_sectors(size); uint16_t sector_size = fat_file_sector_size(file->stream.fatstr.fatfilep);
unsigned long filesectors = filesize_sectors(sector_size, size);
struct filestr_base *s = NULL; struct filestr_base *s = NULL;
while ((s = fileobj_get_next_stream(&file->stream, s))) while ((s = fileobj_get_next_stream(&file->stream, s)))
@ -229,9 +230,11 @@ static int ftruncate_internal(struct filestr_desc *file, file_size_t size,
file_size_t cursize = *file->sizep; file_size_t cursize = *file->sizep;
file_size_t truncsize = MIN(size, cursize); file_size_t truncsize = MIN(size, cursize);
uint16_t sector_size = fat_file_sector_size(file->stream.fatstr.fatfilep);
if (write_now) if (write_now)
{ {
unsigned long sector = filesize_sectors(truncsize); unsigned long sector = filesize_sectors(sector_size, truncsize);
struct filestr_cache *const cachep = file->stream.cachep; struct filestr_cache *const cachep = file->stream.cachep;
if (cachep->flags == (FSC_NEW|FSC_DIRTY) && if (cachep->flags == (FSC_NEW|FSC_DIRTY) &&
@ -244,7 +247,7 @@ static int ftruncate_internal(struct filestr_desc *file, file_size_t size,
{ {
/* no space left on device; further truncation needed */ /* no space left on device; further truncation needed */
discard_cache(file); discard_cache(file);
truncsize = ALIGN_DOWN(truncsize - 1, SECTOR_SIZE); truncsize = ALIGN_DOWN(truncsize - 1, sector_size);
sector--; sector--;
rc = rc2; rc = rc2;
} }
@ -292,6 +295,7 @@ static int fsync_internal(struct filestr_desc *file)
file_size_t size = *file->sizep; file_size_t size = *file->sizep;
unsigned int foflags = fileobj_get_flags(&file->stream); unsigned int foflags = fileobj_get_flags(&file->stream);
uint16_t sector_size = fat_file_sector_size(file->stream.fatstr.fatfilep);
/* flush sector cache? */ /* flush sector cache? */
struct filestr_cache *const cachep = file->stream.cachep; struct filestr_cache *const cachep = file->stream.cachep;
@ -302,7 +306,7 @@ static int fsync_internal(struct filestr_desc *file)
{ {
/* no space left on device so this must be dropped */ /* no space left on device so this must be dropped */
discard_cache(file); discard_cache(file);
size = ALIGN_DOWN(size - 1, SECTOR_SIZE); size = ALIGN_DOWN(size - 1, sector_size);
foflags |= FO_TRUNC; foflags |= FO_TRUNC;
rc = rc2; rc = rc2;
} }
@ -407,7 +411,7 @@ static int open_internal_inner2(const char *path,
/* not found; try to create it */ /* not found; try to create it */
callflags &= ~FO_TRUNC; callflags &= ~FO_TRUNC;
rc = create_stream_internal(&compinfo.parentinfo, compinfo.name, rc = create_stream_internal(&compinfo.parentinfo, compinfo.name,
compinfo.length, ATTR_NEW_FILE, callflags, compinfo.length, ATTR_NEW_FILE, callflags,
&file->stream); &file->stream);
if (rc < 0) if (rc < 0)
@ -649,15 +653,16 @@ static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
struct filestr_cache * const cachep = file->stream.cachep; struct filestr_cache * const cachep = file->stream.cachep;
void * const bufstart = buf; void * const bufstart = buf;
uint16_t sector_size = fat_file_sector_size(file->stream.fatstr.fatfilep);
const unsigned long filesectors = filesize_sectors(size); const unsigned long filesectors = filesize_sectors(sector_size, size);
unsigned long sector = file->offset / SECTOR_SIZE; unsigned long sector = file->offset / sector_size;
unsigned long sectoroffs = file->offset % SECTOR_SIZE; unsigned long sectoroffs = file->offset % sector_size;
/* any head bytes? */ /* any head bytes? */
if (sectoroffs) if (sectoroffs)
{ {
size_t headbytes = MIN(nbyte, SECTOR_SIZE - sectoroffs); size_t headbytes = MIN(nbyte, sector_size - sectoroffs);
rc = readwrite_partial(file, cachep, sector, sectoroffs, buf, headbytes, rc = readwrite_partial(file, cachep, sector, sectoroffs, buf, headbytes,
filesectors, write); filesectors, write);
if (rc <= 0) if (rc <= 0)
@ -676,7 +681,7 @@ static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
} }
/* read/write whole sectors right into/from the supplied buffer */ /* read/write whole sectors right into/from the supplied buffer */
unsigned long sectorcount = nbyte / SECTOR_SIZE; unsigned long sectorcount = nbyte / sector_size;
while (sectorcount) while (sectorcount)
{ {
@ -728,8 +733,8 @@ static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
} }
else else
{ {
buf += rc * SECTOR_SIZE; buf += rc * sector_size;
nbyte -= rc * SECTOR_SIZE; nbyte -= rc * sector_size;
sector += rc; sector += rc;
sectorcount -= rc; sectorcount -= rc;
@ -745,9 +750,9 @@ static ssize_t readwrite(struct filestr_desc *file, void *buf, size_t nbyte,
if (UNLIKELY(sectorcount && sector == cachep->sector)) if (UNLIKELY(sectorcount && sector == cachep->sector))
{ {
/* do this one sector with the cache */ /* do this one sector with the cache */
readwrite_cache(cachep, buf, 0, SECTOR_SIZE, write); readwrite_cache(cachep, buf, 0, sector_size, write);
buf += SECTOR_SIZE; buf += sector_size;
nbyte -= SECTOR_SIZE; nbyte -= sector_size;
sector++; sector++;
sectorcount--; sectorcount--;
} }

View file

@ -35,6 +35,7 @@
#include "rbunicode.h" #include "rbunicode.h"
#include "debug.h" #include "debug.h"
#include "panic.h" #include "panic.h"
#include "disk.h"
/*#define LOGF_ENABLE*/ /*#define LOGF_ENABLE*/
#include "logf.h" #include "logf.h"
@ -162,9 +163,15 @@ union raw_dirent
#define FAT_NTRES_LC_NAME 0x08 #define FAT_NTRES_LC_NAME 0x08
#define FAT_NTRES_LC_EXT 0x10 #define FAT_NTRES_LC_EXT 0x10
#define CLUSTERS_PER_FAT_SECTOR (SECTOR_SIZE / 4) #ifdef MAX_LOG_SECTOR_SIZE
#define CLUSTERS_PER_FAT16_SECTOR (SECTOR_SIZE / 2) #define LOG_SECTOR_SIZE(bpb) fat_bpb->sector_size
#define DIR_ENTRIES_PER_SECTOR (SECTOR_SIZE / DIR_ENTRY_SIZE) #else
#define LOG_SECTOR_SIZE(bpb) SECTOR_SIZE
#endif
#define CLUSTERS_PER_FAT_SECTOR (LOG_SECTOR_SIZE(fat_bpb) / 4)
#define CLUSTERS_PER_FAT16_SECTOR (LOG_SECTOR_SIZE(fat_bpb) / 2)
#define DIR_ENTRIES_PER_SECTOR (LOG_SECTOR_SIZE(fat_bpb) / DIR_ENTRY_SIZE)
#define DIR_ENTRY_SIZE 32 #define DIR_ENTRY_SIZE 32
#define FAT_BAD_MARK 0x0ffffff7 #define FAT_BAD_MARK 0x0ffffff7
#define FAT_EOF_MARK 0x0ffffff8 #define FAT_EOF_MARK 0x0ffffff8
@ -208,8 +215,10 @@ struct bpb;
static void update_fsinfo32(struct bpb *fat_bpb); static void update_fsinfo32(struct bpb *fat_bpb);
/* Note: This struct doesn't hold the raw values after mounting if /* Note: This struct doesn't hold the raw values after mounting if
* bpb_bytspersec isn't 512. All sector counts are normalized to 512 byte * bpb_bytspersec isn't the same as the underlying device's logical
* physical sectors. */ sector size. Sector counts are then normalized to that sector
size.
*/
static struct bpb static struct bpb
{ {
unsigned long bpb_bytspersec; /* Bytes per sector, typically 512 */ unsigned long bpb_bytspersec; /* Bytes per sector, typically 512 */
@ -263,12 +272,19 @@ static struct bpb
int BPB_FN_DECL(update_fat_entry, unsigned long, unsigned long); int BPB_FN_DECL(update_fat_entry, unsigned long, unsigned long);
void BPB_FN_DECL(fat_recalc_free_internal); void BPB_FN_DECL(fat_recalc_free_internal);
#endif /* HAVE_FAT16SUPPORT */ #endif /* HAVE_FAT16SUPPORT */
#ifdef MAX_LOG_SECTOR_SIZE
uint16_t sector_size;
#endif
} fat_bpbs[NUM_VOLUMES]; /* mounted partition info */ } fat_bpbs[NUM_VOLUMES]; /* mounted partition info */
#ifdef STORAGE_NEEDS_BOUNCE_BUFFER #ifdef STORAGE_NEEDS_BOUNCE_BUFFER
#ifdef MAX_LOG_SECTOR_SIZE
#define BOUNCE_SECTOR_SIZE MAX_LOG_SECTOR_SIZE
#else
#define BOUNCE_SECTOR_SIZE SECTOR_SIZE
#endif
#define FAT_BOUNCE_SECTORS 10 #define FAT_BOUNCE_SECTORS 10
static uint8_t fat_bounce_buffers[NUM_VOLUMES][SECTOR_SIZE*FAT_BOUNCE_SECTORS] STORAGE_ALIGN_ATTR; static uint8_t fat_bounce_buffers[NUM_VOLUMES][BOUNCE_SECTOR_SIZE*FAT_BOUNCE_SECTORS] STORAGE_ALIGN_ATTR;
#define FAT_BOUNCE_BUFFER(bpb) \ #define FAT_BOUNCE_BUFFER(bpb) \
(fat_bounce_buffers[IF_MV_VOL((bpb)->volume)]) (fat_bounce_buffers[IF_MV_VOL((bpb)->volume)])
#endif #endif
@ -394,7 +410,7 @@ static void raw_dirent_set_fstclus(union raw_dirent *ent, long fstclus)
static int bpb_is_sane(struct bpb *fat_bpb) static int bpb_is_sane(struct bpb *fat_bpb)
{ {
if (fat_bpb->bpb_bytspersec % SECTOR_SIZE) if (fat_bpb->bpb_bytspersec % LOG_SECTOR_SIZE(fat_bpb))
{ {
DEBUGF("%s() - Error: sector size is not sane (%lu)\n", DEBUGF("%s() - Error: sector size is not sane (%lu)\n",
__func__, fat_bpb->bpb_bytspersec); __func__, fat_bpb->bpb_bytspersec);
@ -402,9 +418,9 @@ static int bpb_is_sane(struct bpb *fat_bpb)
} }
/* The fat_bpb struct does not hold the raw value of bpb_bytspersec, the /* The fat_bpb struct does not hold the raw value of bpb_bytspersec, the
* value is multiplied in cases where bpb_bytspersec != SECTOR_SIZE. We need * value is multiplied in cases where bpb_bytspersec != sector_size. We need
* to undo that multiplication before we do the sanity check. */ * to undo that multiplication before we do the sanity check. */
unsigned long secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; unsigned long secmult = fat_bpb->bpb_bytspersec / LOG_SECTOR_SIZE(fat_bpb);
if (fat_bpb->bpb_secperclus * fat_bpb->bpb_bytspersec / secmult > 128*1024ul) if (fat_bpb->bpb_secperclus * fat_bpb->bpb_bytspersec / secmult > 128*1024ul)
{ {
@ -1141,7 +1157,7 @@ static int fat_mount_internal(struct bpb *fat_bpb)
} }
fat_bpb->bpb_bytspersec = BYTES2INT16(buf, BPB_BYTSPERSEC); fat_bpb->bpb_bytspersec = BYTES2INT16(buf, BPB_BYTSPERSEC);
unsigned long secmult = fat_bpb->bpb_bytspersec / SECTOR_SIZE; unsigned long secmult = fat_bpb->bpb_bytspersec / LOG_SECTOR_SIZE(fat_bpb);
/* Sanity check is performed later */ /* Sanity check is performed later */
fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS]; fat_bpb->bpb_secperclus = secmult * buf[BPB_SECPERCLUS];
@ -1426,7 +1442,7 @@ static int fat_extend_dir(struct bpb *fat_bpb, struct fat_filestr *dirstr)
FAT_ERROR(-2); FAT_ERROR(-2);
} }
memset(sec, 0, SECTOR_SIZE); memset(sec, 0, LOG_SECTOR_SIZE(fat_bpb));
dc_dirty_buf(sec); dc_dirty_buf(sec);
dc_unlock_cache(); dc_unlock_cache();
} }
@ -1981,6 +1997,15 @@ static int free_cluster_chain(struct bpb *fat_bpb, long startcluster)
/** File entity functions **/ /** File entity functions **/
int fat_file_sector_size(const struct fat_file *file)
{
#ifdef MAX_LOG_SECTOR_SIZE
const struct bpb *fat_bpb = FAT_BPB(file->volume);
#endif
return LOG_SECTOR_SIZE(fat_bpb);
}
int fat_create_file(struct fat_file *parent, const char *name, int fat_create_file(struct fat_file *parent, const char *name,
uint8_t attr, struct fat_file *file, uint8_t attr, struct fat_file *file,
struct fat_direntry *fatent) struct fat_direntry *fatent)
@ -2362,11 +2387,11 @@ int fat_closewrite(struct fat_filestr *filestr, uint32_t size,
count++; count++;
} }
uint32_t len = count * fat_bpb->bpb_secperclus * SECTOR_SIZE; uint32_t len = count * fat_bpb->bpb_secperclus * LOG_SECTOR_SIZE(fat_bpb);
DEBUGF("File is %lu clusters (chainlen=%lu, size=%lu)\n", DEBUGF("File is %lu clusters (chainlen=%lu, size=%lu)\n",
count, len, size ); count, len, size );
if (len > size + fat_bpb->bpb_secperclus * SECTOR_SIZE) if (len > size + fat_bpb->bpb_secperclus * LOG_SECTOR_SIZE(fat_bpb))
panicf("Cluster chain is too long\n"); panicf("Cluster chain is too long\n");
if (len < size) if (len < size)
@ -2429,22 +2454,22 @@ static long transfer(struct bpb *fat_bpb, sector_t start, long count,
if(UNLIKELY(STORAGE_OVERLAP((uintptr_t)buf))) { if(UNLIKELY(STORAGE_OVERLAP((uintptr_t)buf))) {
void* xfer_buf = FAT_BOUNCE_BUFFER(fat_bpb); void* xfer_buf = FAT_BOUNCE_BUFFER(fat_bpb);
while(count > 0) { while(count > 0) {
int xfer_count = MIN(count, FAT_BOUNCE_SECTORS); int xfer_count = MIN(count*LOG_SECTOR_SIZE(fat_bpb), (FAT_BOUNCE_SECTORS * BOUNCE_SECTOR_SIZE)) / LOG_SECTOR_SIZE(fat_bpb);
if(write) { if(write) {
memcpy(xfer_buf, buf, xfer_count * SECTOR_SIZE); memcpy(xfer_buf, buf, xfer_count * LOG_SECTOR_SIZE(fat_bpb));
rc = storage_write_sectors(IF_MD(fat_bpb->drive,) rc = storage_write_sectors(IF_MD(fat_bpb->drive,)
start + fat_bpb->startsector, xfer_count, xfer_buf); start + fat_bpb->startsector, xfer_count, xfer_buf);
} else { } else {
rc = storage_read_sectors(IF_MD(fat_bpb->drive,) rc = storage_read_sectors(IF_MD(fat_bpb->drive,)
start + fat_bpb->startsector, xfer_count, xfer_buf); start + fat_bpb->startsector, xfer_count, xfer_buf);
memcpy(buf, xfer_buf, xfer_count * SECTOR_SIZE); memcpy(buf, xfer_buf, xfer_count * LOG_SECTOR_SIZE(fat_bpb));
} }
if(rc < 0) if(rc < 0)
break; break;
buf += xfer_count * SECTOR_SIZE; buf += xfer_count * LOG_SECTOR_SIZE(fat_bpb);
start += xfer_count; start += xfer_count;
count -= xfer_count; count -= xfer_count;
} }
@ -2571,7 +2596,7 @@ long fat_readwrite(struct fat_filestr *filestr, unsigned long sectorcount,
FAT_ERROR(rc * 10 - 2); FAT_ERROR(rc * 10 - 2);
transferred += count; transferred += count;
buf += count * SECTOR_SIZE; buf += count * LOG_SECTOR_SIZE(fat_bpb);
count = 0; count = 0;
} }
@ -2744,6 +2769,11 @@ int fat_readdir(struct fat_filestr *dirstr, struct fat_dirscan_info *scan,
scan->entries = 0; scan->entries = 0;
#ifdef MAX_LOG_SECTOR_SIZE
struct fat_file *file = dirstr->fatfilep;
const struct bpb *fat_bpb = FAT_BPB(file->volume);
#endif
while (1) while (1)
{ {
unsigned int direntry = ++scan->entry; unsigned int direntry = ++scan->entry;
@ -2863,7 +2893,6 @@ void fat_rewinddir(struct fat_dirscan_info *scan)
scan->entries = 0; scan->entries = 0;
} }
/** Mounting and unmounting functions **/ /** Mounting and unmounting functions **/
bool fat_ismounted(IF_MV_NONVOID(int volume)) bool fat_ismounted(IF_MV_NONVOID(int volume))
@ -2888,6 +2917,10 @@ int fat_mount(IF_MV(int volume,) IF_MD(int drive,) unsigned long startsector)
fat_bpb->drive = drive; fat_bpb->drive = drive;
#endif #endif
#ifdef MAX_LOG_SECTOR_SIZE
fat_bpb->sector_size = disk_get_log_sector_size(IF_MD(drive));
#endif
rc = fat_mount_internal(fat_bpb); rc = fat_mount_internal(fat_bpb);
if (rc < 0) if (rc < 0)
FAT_ERROR(rc * 10 - 2); FAT_ERROR(rc * 10 - 2);
@ -2927,6 +2960,9 @@ int fat_unmount(IF_MV_NONVOID(int volume))
/** Debug screen stuff **/ /** Debug screen stuff **/
#ifdef MAX_LOG_SECTOR_SIZE #ifdef MAX_LOG_SECTOR_SIZE
/* This isn't necessarily the same as storage's logical sector size;
we can have situations where the filesystem (and partition table)
uses a larger "virtual sector" than the underlying storage device */
int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume)) int fat_get_bytes_per_sector(IF_MV_NONVOID(int volume))
{ {
int bytes = 0; int bytes = 0;
@ -2945,7 +2981,7 @@ unsigned int fat_get_cluster_size(IF_MV_NONVOID(int volume))
struct bpb * const fat_bpb = FAT_BPB(volume); struct bpb * const fat_bpb = FAT_BPB(volume);
if (fat_bpb) if (fat_bpb)
size = fat_bpb->bpb_secperclus * SECTOR_SIZE; size = fat_bpb->bpb_secperclus * LOG_SECTOR_SIZE(fat_bpb);
return size; return size;
} }
@ -2967,7 +3003,7 @@ bool fat_size(IF_MV(int volume,) sector_t *size, sector_t *free)
if (!fat_bpb) if (!fat_bpb)
return false; return false;
unsigned long factor = fat_bpb->bpb_secperclus * SECTOR_SIZE / 1024; unsigned long factor = fat_bpb->bpb_secperclus * LOG_SECTOR_SIZE(fat_bpb) / 1024;
if (size) *size = fat_bpb->dataclusters * factor; if (size) *size = fat_bpb->dataclusters * factor;
if (free) *free = fat_bpb->fsinfo.freecount * factor; if (free) *free = fat_bpb->fsinfo.freecount * factor;

View file

@ -143,6 +143,8 @@ int fat_rename(struct fat_file *parent, struct fat_file *file,
int fat_modtime(struct fat_file *parent, struct fat_file *file, int fat_modtime(struct fat_file *parent, struct fat_file *file,
time_t modtime); time_t modtime);
int fat_file_sector_size(const struct fat_file *file);
/** File stream functions **/ /** File stream functions **/
int fat_closewrite(struct fat_filestr *filestr, uint32_t size, int fat_closewrite(struct fat_filestr *filestr, uint32_t size,
struct fat_direntry *fatentp); struct fat_direntry *fatentp);