Skip to content

Commit

Permalink
Add basic support for Apricot-variant FAT filesystems.
Browse files Browse the repository at this point in the history
  • Loading branch information
davidgiven committed Sep 6, 2023
1 parent 9e61670 commit 6ae4d8c
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 67 deletions.
145 changes: 78 additions & 67 deletions dep/fatfs/source/ff.c
Original file line number Diff line number Diff line change
Expand Up @@ -3264,6 +3264,7 @@ static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS,
{
WORD w, sign;
BYTE b;
BYTE bpboffset = fs->bpboffset;


fs->wflag = 0; fs->winsect = (LBA_t)0 - 1; /* Invaidate window */
Expand All @@ -3273,20 +3274,23 @@ static UINT check_fs ( /* 0:FAT/FAT32 VBR, 1:exFAT VBR, 2:Not FAT and valid BS,
if (sign == 0xAA55 && !memcmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; /* It is an exFAT VBR */
#endif
b = fs->win[BS_JmpBoot];
if (b == 0xEB || b == 0xE9 || b == 0xE8) { /* Valid JumpBoot code? (short jump, near jump or near call) */
if (b == 0xEB || b == 0xE9 || b == 0xE8 || b == 0x56) { /* Valid JumpBoot code? (short jump, near jump or near call, Apricot) */
if (sign == 0xAA55 && !memcmp(fs->win + BS_FilSysType32, "FAT32 ", 8)) {
return 0; /* It is an FAT32 VBR */
}
if (b == 0x56)
bpboffset = fs->bpboffset = 0x50 - 0x0b;

/* FAT volumes formatted with early MS-DOS lack BS_55AA and BS_FilSysType, so FAT VBR needs to be identified without them. */
w = ld_word(fs->win + BPB_BytsPerSec);
b = fs->win[BPB_SecPerClus];
w = ld_word(fs->win + bpboffset + BPB_BytsPerSec);
b = fs->win[bpboffset + BPB_SecPerClus];
if ((w & (w - 1)) == 0 && w >= FF_MIN_SS && w <= FF_MAX_SS /* Properness of sector size (512-4096 and 2^n) */
&& b != 0 && (b & (b - 1)) == 0 /* Properness of cluster size (2^n) */
&& ld_word(fs->win + BPB_RsvdSecCnt) != 0 /* Properness of reserved sectors (MNBZ) */
&& (UINT)fs->win[BPB_NumFATs] - 1 <= 1 /* Properness of FATs (1 or 2) */
&& ld_word(fs->win + BPB_RootEntCnt) != 0 /* Properness of root dir entries (MNBZ) */
&& (ld_word(fs->win + BPB_TotSec16) >= 128 || ld_dword(fs->win + BPB_TotSec32) >= 0x10000) /* Properness of volume sectors (>=128) */
&& ld_word(fs->win + BPB_FATSz16) != 0) { /* Properness of FAT size (MNBZ) */
&& ld_word(fs->win + bpboffset + BPB_RsvdSecCnt) != 0 /* Properness of reserved sectors (MNBZ) */
&& (UINT)fs->win[bpboffset + BPB_NumFATs] - 1 <= 1 /* Properness of FATs (1 or 2) */
&& ld_word(fs->win + bpboffset + BPB_RootEntCnt) != 0 /* Properness of root dir entries (MNBZ) */
&& (ld_word(fs->win + bpboffset + BPB_TotSec16) >= 128 || ld_dword(fs->win + bpboffset + BPB_TotSec32) >= 0x10000) /* Properness of volume sectors (>=128) */
&& ld_word(fs->win + bpboffset + BPB_FATSz16) != 0) { /* Properness of FAT size (MNBZ) */
return 0; /* It can be presumed an FAT VBR */
}
}
Expand Down Expand Up @@ -3407,6 +3411,8 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
#endif

fs->bpboffset = 0;

/* Find an FAT volume on the drive */
fmt = find_volume(fs, LD2PT(vol));
if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */
Expand All @@ -3420,36 +3426,36 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
QWORD maxlba;
DWORD so, cv, bcl, i;

for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
for (i = bpboffset + BPB_ZeroedEx; i < bpboffset + BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
if (i < bpboffset + BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;

if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */
if (ld_word(fs->win + bpboffset + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT version (must be version 1.0) */

if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
if (1 << fs->win[bpboffset + BPB_BytsPerSecEx] != SS(fs)) { /* (bpboffset + BPB_BytsPerSecEx must be equal to the physical sector size) */
return FR_NO_FILESYSTEM;
}

maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA of the volume + 1 */
maxlba = ld_qword(fs->win + bpboffset + BPB_TotSecEx) + bsect; /* Last LBA of the volume + 1 */
if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be accessed in 32-bit LBA) */

fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */
fs->fsize = ld_dword(fs->win + bpboffset + BPB_FatSzEx); /* Number of sectors per FAT */

fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */
fs->n_fats = fs->win[bpboffset + BPB_NumFATsEx]; /* Number of FATs */
if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */

fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */
fs->csize = 1 << fs->win[bpboffset + BPB_SecPerClusEx]; /* Cluster size */
if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768 sectors) */

nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */
nclst = ld_dword(fs->win + bpboffset + BPB_NumClusEx); /* Number of clusters */
if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */
fs->n_fatent = nclst + 2;

/* Boundaries and Limits */
fs->volbase = bsect;
fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
fs->database = bsect + ld_dword(fs->win + bpboffset + BPB_DataOfsEx);
fs->fatbase = bsect + ld_dword(fs->win + bpboffset + BPB_FatOfsEx);
if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */
fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
fs->dirbase = ld_dword(fs->win + bpboffset + BPB_RootClusEx);

/* Get bitmap location and check if it is contiguous (implementation assumption) */
so = i = 0;
Expand Down Expand Up @@ -3479,26 +3485,27 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
} else
#endif /* FF_FS_EXFAT */
{
if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */
BYTE bpboffset = fs->bpboffset;
if (ld_word(fs->win + bpboffset + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (bpboffset + BPB_BytsPerSec must be equal to the physical sector size) */

fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */
if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);
fasize = ld_word(fs->win + bpboffset + BPB_FATSz16); /* Number of sectors per FAT */
if (fasize == 0) fasize = ld_dword(fs->win + bpboffset + BPB_FATSz32);
fs->fsize = fasize;

fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */
fs->n_fats = fs->win[bpboffset + BPB_NumFATs]; /* Number of FATs */
if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */
fasize *= fs->n_fats; /* Number of sectors for FAT area */

fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */
fs->csize = fs->win[bpboffset + BPB_SecPerClus]; /* Cluster size */
if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */

fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */
fs->n_rootdir = ld_word(fs->win + bpboffset + BPB_RootEntCnt); /* Number of root directory entries */
if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */

tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */
if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
tsect = ld_word(fs->win + bpboffset + BPB_TotSec16); /* Number of sectors on the volume */
if (tsect == 0) tsect = ld_dword(fs->win + bpboffset + BPB_TotSec32);

nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */
nrsv = ld_word(fs->win + bpboffset + BPB_RsvdSecCnt); /* Number of reserved sectors */
if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */

/* Determine the FAT sub type */
Expand All @@ -3518,25 +3525,25 @@ static FRESULT mount_volume ( /* FR_OK(0): successful, !=0: an error occurred */
fs->fatbase = bsect + nrsv; /* FAT start sector */
fs->database = bsect + sysect; /* Data start sector */
if (fmt == FS_FAT32) {
if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */
if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */
fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */
if (ld_word(fs->win + bpboffset + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */
if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM; /* (bpboffset + BPB_RootEntCnt must be 0) */
fs->dirbase = ld_dword(fs->win + bpboffset + BPB_RootClus32); /* Root directory start cluster */
szbfat = fs->n_fatent * 4; /* (Needed FAT size) */
} else {
if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must not be 0) */
if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM; /* (bpboffset + BPB_RootEntCnt must not be 0) */
fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */
szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */
fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
}
if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */
if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (bpboffset + BPB_FATSz must not be less than the size needed) */

#if !FF_FS_READONLY
/* Get FSInfo if available */
fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */
fs->fsi_flag = 0x80;
#if (FF_FS_NOFSINFO & 3) != 3
if (fmt == FS_FAT32 /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
&& ld_word(fs->win + BPB_FSInfo32) == 1
if (fmt == FS_FAT32 /* Allow to update FSInfo only if bpboffset + BPB_FSInfo32 == 1 */
&& ld_word(fs->win + bpboffset + BPB_FSInfo32) == 1
&& move_window(fs, bsect + 1) == FR_OK)
{
fs->fsi_flag = 0;
Expand Down Expand Up @@ -5366,7 +5373,7 @@ FRESULT f_getlabel (
if (res == FR_OK) {
switch (fs->fs_type) {
case FS_EXFAT:
di = BPB_VolIDEx;
di = fs->bpboffset + BPB_VolIDEx;
break;

case FS_FAT32:
Expand Down Expand Up @@ -5841,6 +5848,7 @@ FRESULT f_mkfs (
int vol;
DSTATUS ds;
FRESULT fr;
BYTE bpboffset;


/* Check mounted drive and clear work area */
Expand All @@ -5864,6 +5872,9 @@ FRESULT f_mkfs (
#else
ss = FF_MAX_SS;
#endif
bpboffset = 0;


/* Options for FAT sub-type and FAT parameters */
fsopt = opt->fmt & (FM_ANY | FM_SFD);
n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1;
Expand Down Expand Up @@ -6080,23 +6091,23 @@ FRESULT f_mkfs (
/* Main record (+0) */
memset(buf, 0, ss);
memcpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */
st_qword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */
st_qword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */
st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* FAT offset [sector] */
st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */
st_dword(buf + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */
st_dword(buf + BPB_VolIDEx, vsn); /* VSN */
st_word(buf + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */
buf[BPB_NumFATsEx] = 1; /* Number of FATs */
buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */
st_qword(buf + bpboffset + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */
st_qword(buf + bpboffset + BPB_TotSecEx, sz_vol); /* Volume size [sector] */
st_dword(buf + bpboffset + BPB_FatOfsEx, (DWORD)(b_fat - b_vol)); /* FAT offset [sector] */
st_dword(buf + bpboffset + BPB_FatSzEx, sz_fat); /* FAT size [sector] */
st_dword(buf + bpboffset + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
st_dword(buf + bpboffset + BPB_NumClusEx, n_clst); /* Number of clusters */
st_dword(buf + bpboffset + BPB_RootClusEx, 2 + clen[0] + clen[1]); /* Root dir cluster # */
st_dword(buf + bpboffset + BPB_VolIDEx, vsn); /* VSN */
st_word(buf + bpboffset + BPB_FSVerEx, 0x100); /* Filesystem version (1.00) */
for (buf[bpboffset + BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[bpboffset + BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */
for (buf[bpboffset + BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; buf[bpboffset + BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */
buf[bpboffset + BPB_NumFATsEx] = 1; /* Number of FATs */
buf[bpboffset + BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */
st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */
st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */
for (i = sum = 0; i < ss; i++) { /* VBR checksum */
if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum);
if (i != bpboffset + BPB_VolFlagEx && i != bpboffset + BPB_VolFlagEx + 1 && i != bpboffset + BPB_PercInUseEx) sum = xsum32(buf[i], sum);
}
if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
/* Extended bootstrap record (+1..+8) */
Expand Down Expand Up @@ -6201,32 +6212,32 @@ FRESULT f_mkfs (
/* Create FAT VBR */
memset(buf, 0, ss);
memcpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11); /* Boot jump code (x86), OEM name */
st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */
buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */
st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */
buf[BPB_NumFATs] = (BYTE)n_fat; /* Number of FATs */
st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : n_root)); /* Number of root directory entries */
st_word(buf + bpboffset + BPB_BytsPerSec, ss); /* Sector size [byte] */
buf[bpboffset + BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */
st_word(buf + bpboffset + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */
buf[bpboffset + BPB_NumFATs] = (BYTE)n_fat; /* Number of FATs */
st_word(buf + bpboffset + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : n_root)); /* Number of root directory entries */
if (sz_vol < 0x10000) {
st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */
st_word(buf + bpboffset + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */
} else {
st_dword(buf + BPB_TotSec32, (DWORD)sz_vol); /* Volume size in 32-bit LBA */
st_dword(buf + bpboffset + BPB_TotSec32, (DWORD)sz_vol); /* Volume size in 32-bit LBA */
}
buf[BPB_Media] = 0xF8; /* Media descriptor byte */
st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */
st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */
st_dword(buf + BPB_HiddSec, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */
buf[bpboffset + BPB_Media] = 0xF8; /* Media descriptor byte */
st_word(buf + bpboffset + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */
st_word(buf + bpboffset + BPB_NumHeads, 255); /* Number of heads (for int13) */
st_dword(buf + bpboffset + BPB_HiddSec, (DWORD)b_vol); /* Volume offset in the physical drive [sector] */
if (fsty == FS_FAT32) {
st_dword(buf + BS_VolID32, vsn); /* VSN */
st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */
st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */
st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */
st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */
st_dword(buf + bpboffset + BPB_FATSz32, sz_fat); /* FAT size [sector] */
st_dword(buf + bpboffset + BPB_RootClus32, 2); /* Root directory cluster # (2) */
st_word(buf + bpboffset + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */
st_word(buf + bpboffset + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */
buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */
buf[BS_BootSig32] = 0x29; /* Extended boot signature */
memcpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */
} else {
st_dword(buf + BS_VolID, vsn); /* VSN */
st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */
st_word(buf + bpboffset + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */
buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */
buf[BS_BootSig] = 0x29; /* Extended boot signature */
memcpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */
Expand Down
1 change: 1 addition & 0 deletions dep/fatfs/source/ff.h
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ typedef struct {
WORD id; /* Volume mount ID */
WORD n_rootdir; /* Number of root directory entries (FAT12/16) */
WORD csize; /* Cluster size [sectors] */
BYTE bpboffset; /* Offset to BPB in boot sector */
#if FF_MAX_SS != FF_MIN_SS
WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */
#endif
Expand Down

0 comments on commit 6ae4d8c

Please sign in to comment.