Skip to content

Commit f0cba8b

Browse files
committed
Allow building with clang.
Improved checks. Added more documentation/definitions to fat.h.
1 parent 68c5965 commit f0cba8b

File tree

4 files changed

+177
-94
lines changed

4 files changed

+177
-94
lines changed

Makefile

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,17 @@ ARFLAGS := -rcs
2121
LDFLAGS := $(ARCH) -O2 -s -pie -fPIE -Wl,--gc-sections,-z,relro,-z,now,-z,noexecstack
2222

2323
PREFIX :=
24+
ifneq ($(strip $(USE_CLANG)),)
25+
CC := $(PREFIX)clang
26+
CXX := $(PREFIX)clang++
27+
AS := $(PREFIX)clang
28+
AR := $(PREFIX)gcc-ar
29+
else
2430
CC := $(PREFIX)gcc
2531
CXX := $(PREFIX)g++
2632
AS := $(PREFIX)gcc
2733
AR := $(PREFIX)gcc-ar
34+
endif
2835

2936

3037
# Do not change anything after this

include/fat.h

Lines changed: 91 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
// FAT12/16/32: http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/fatgen103.doc
1212

1313

14-
// Volume Boot Record.
15-
typedef struct
14+
// Boot sector.
15+
typedef struct __attribute__((packed))
1616
{
1717
u8 jmpBoot[3]; // {0xEB, 0xXX, 0x90} or {0xE9, 0xXX, 0xXX}.
1818
char oemName[8]; // Usually system name that formatted the volume.
@@ -37,13 +37,13 @@ typedef struct
3737
struct __attribute__((packed)) // FAT12/FAT16.
3838
{
3939
u8 drvNum; // 0x80 or 0x00.
40-
u8 reserved1; // Must be 0.
40+
u8 reserved1; // Must be 0. Used by Windows for dirty flag (bit 0 set = dirty).
4141
u8 bootSig; // 0x29 if one or both of the following 2 fields are non-zero.
4242
u32 volId; // Volume serial number generated from date and time.
4343
char volLab[11]; // "NO NAME " or space padded label.
4444
char filSysType[8]; // "FAT12 ", "FAT16 " or "FAT ".
4545
u8 bootCode[448];
46-
} fat16;
46+
} ebpb;
4747
struct __attribute__((packed)) // FAT32.
4848
{
4949
u32 fatSz32; // Must be non-zero.
@@ -54,20 +54,19 @@ typedef struct
5454
u16 bkBootSec; // 0 or 6. Backup boot sector must be present if the later.
5555
u8 reserved[12]; // Must be 0.
5656
u8 drvNum; // 0x80 or 0x00.
57-
u8 reserved1; // Must be 0.
57+
u8 reserved1; // Must be 0. Used by Windows for dirty flag (bit 0 set = dirty).
5858
u8 bootSig; // 0x29 if one or both of the following 2 fields are non-zero.
5959
u32 volId; // Volume serial number generated from date and time.
6060
char volLab[11]; // "NO NAME " or space padded label.
6161
char filSysType[8]; // "FAT32 ".
6262
u8 bootCode[420];
63-
} fat32;
63+
} ebpb32;
6464
};
65-
6665
u16 sigWord; // 0xAA55.
67-
} __attribute__((packed)) Vbr;
68-
static_assert(offsetof(Vbr, fat16.bootCode) == 62, "Member fat16.bootCode of Vbr not at offsetof 62.");
69-
static_assert(offsetof(Vbr, fat32.bootCode) == 90, "Member fat32.bootCode of Vbr not at offsetof 90.");
70-
static_assert(offsetof(Vbr, sigWord) == 510, "Member sigWord of Vbr not at offsetof 510.");
66+
} BootSec;
67+
static_assert(offsetof(BootSec, ebpb.bootCode) == 62, "Member ebpb.bootCode of BootSec not at offset 62.");
68+
static_assert(offsetof(BootSec, ebpb32.bootCode) == 90, "Member ebpb32.bootCode of BootSec not at offset 90.");
69+
static_assert(offsetof(BootSec, sigWord) == 510, "Member sigWord of BootSec not at offset 510.");
7170

7271
typedef struct
7372
{
@@ -79,7 +78,7 @@ typedef struct
7978
u8 reserved2[12]; // Must be 0.
8079
u32 trailSig; // Must be 0xAA550000.
8180
} FsInfo;
82-
static_assert(offsetof(FsInfo, trailSig) == 508, "Member trailSig of FsInfo not at offsetof 508.");
81+
static_assert(offsetof(FsInfo, trailSig) == 508, "Member trailSig of FsInfo not at offset 508.");
8382

8483
typedef struct
8584
{
@@ -96,9 +95,88 @@ typedef struct
9695
u16 fstClusLo; // Low u16 of first data cluster.
9796
u32 fileSize; // File/directory size in bytes.
9897
} FatDir;
99-
static_assert(offsetof(FatDir, fileSize) == 28, "Member fileSize of FatDir not at offsetof 28.");
98+
static_assert(offsetof(FatDir, fileSize) == 28, "Member fileSize of FatDir not at offset 28.");
99+
100+
typedef struct __attribute__((packed))
101+
{
102+
u8 ord; // Order of LDIR entries. Last entry (which comes first) must have LAST_LONG_ENTRY (0x40) set.
103+
u16 name1[5]; // UTF-16 character 1-5 of long name.
104+
u8 attr; // Must be ATTR_LONG_NAME (DIR_ATTR_VOLUME_ID | DIR_ATTR_SYSTEM | DIR_ATTR_HIDDEN | DIR_ATTR_READ_ONLY).
105+
u8 type; // Must be 0 (sub-component of long name). Other values unknown (extensions).
106+
u8 chksum; // Checksum of 11 characters short name.
107+
u16 name2[6]; // UTF-16 character 6-11 of long name.
108+
u16 fstClusLo; // Must be 0.
109+
u16 name3[2]; // UTF-16 character 12-13 of long name.
110+
} FatLdir;
111+
static_assert(offsetof(FatLdir, name3) == 28, "Member name3 of FatLdir not at offset 28.");
112+
113+
114+
// Boot sector.
115+
#define BS_JMP_BOOT_FAT "\xEB\x3C\x90"
116+
#define BS_JMP_BOOT_FAT32 "\xEB\x58\x90"
117+
#define BS_DEFAULT_OEM_NAME "MSWIN4.1" // Recommended default OEM name.
118+
119+
// BIOS Parameter Block.
120+
#define BPB_DEFAULT_MEDIA (0xF8u)
121+
122+
// Extended BIOS Parameter Block.
123+
#define EBPB_DEFAULT_DRV_NUM (0x80u)
124+
#define EBPB_BOOT_SIG (0x29u)
125+
#define EBPB_VOL_LAB_NO_NAME "NO NAME "
126+
#define EBPB_FIL_SYS_TYPE_FAT12 "FAT12 "
127+
#define EBPB_FIL_SYS_TYPE_FAT16 "FAT16 "
128+
#define EBPB_FIL_SYS_TYPE_FAT32 "FAT32 "
129+
#define EBPB_SIG_WORD (0xAA55u)
130+
131+
// FSInfo.
132+
#define FS_INFO_LEAD_SIG (0x41615252u)
133+
#define FS_INFO_STRUC_SIG (0x61417272u)
134+
#define FS_INFO_UNK_FREE_COUNT (0xFFFFFFFFu)
135+
#define FS_INFO_UNK_NXT_FREE (0xFFFFFFFFu)
136+
#define FS_INFO_TRAIL_SIG (0xAA550000u)
100137

138+
// FAT directory entry.
139+
#define DIR_ATTR_READ_ONLY (1u)
140+
#define DIR_ATTR_HIDDEN (1u<<1)
141+
#define DIR_ATTR_SYSTEM (1u<<2)
142+
#define DIR_ATTR_VOLUME_ID (1u<<3)
143+
#define DIR_ATTR_DIRECTORY (1u<<4)
144+
#define DIR_ATTR_ARCHIVE (1u<<5)
145+
146+
// File allocation table.
147+
// Note: MAX_CLUS actually means number of clusters, not index!
148+
#define FAT_FIRST_ENT (2u) // Index 0 and 1 are reserved.
149+
#define FAT12_MAX_CLUS (0xFF4u) // Specification limit.
150+
#define FAT16_MAX_CLUS (0xFFF4u) // Specification limit.
151+
#define FAT32_MAX_CLUS (0x0FFFFFF6u) // Theoretical limit. 2 clusters will not be allocatable. Spec limit is 0x0FFFFFF4?
152+
153+
#define FAT_FREE (0u) // FAT entry is unallocated/free. Common for all 3 variants.
154+
// 0xXXXXXFF6 is reserved.
155+
#define FAT12_BAD (0xFF7u)
156+
#define FAT16_BAD (0xFFF7u)
157+
#define FAT32_BAD (0x0FFFFFF7u)
158+
// 0xXXXXXFF8 to 0xXXXXXFFE is reserved.
159+
#define FAT12_EOF (0xFFFu)
160+
#define FAT16_EOF (0xFFFFu)
161+
#define FAT32_EOF (0x0FFFFFFFu)
162+
163+
// FAT long directory entry.
164+
#define LDIR_LAST_LONG_ENTRY (1u<<6)
165+
#define LDIR_ATTR_LONG_NAME (DIR_ATTR_VOLUME_ID | DIR_ATTR_SYSTEM | DIR_ATTR_HIDDEN | DIR_ATTR_READ_ONLY)
166+
#define LDIR_ATTR_LONG_NAME_MASK (DIR_ATTR_ARCHIVE | DIR_ATTR_DIRECTORY | LDIR_ATTR_LONG_NAME)
167+
168+
169+
170+
static inline u8 calcLdirChksum(const char *shortName)
171+
{
172+
u8 chksum = 0;
173+
for(unsigned i = 0; i < 11; i++)
174+
{
175+
chksum = ((chksum & 1u ? 0x80u : 0u) | chksum>>1) + *shortName++;
176+
}
101177

178+
return chksum;
179+
}
102180

103181
void calcFormatFat(FormatParams &params);
104182
void calcFormatFat32(FormatParams &params);

source/fat.cpp

Lines changed: 60 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ int makeFsFat(const FormatParams &params, BufferedFsWriter &dev, const std::stri
128128
if(res != 0) return res;
129129

130130
// Prepare label.
131-
char labelBuf[12] = "NO NAME ";
131+
char labelBuf[12] = EBPB_VOL_LAB_NO_NAME;
132132
if(!label.empty())
133133
{
134134
memset(labelBuf, ' ', 11); // Padding must be spaces.
@@ -137,65 +137,63 @@ int makeFsFat(const FormatParams &params, BufferedFsWriter &dev, const std::stri
137137
memcpy(labelBuf, label.c_str(), labelLen);
138138
}
139139

140-
// Volume Boot Record (VBR).
141-
Vbr vbr{};
142-
vbr.jmpBoot[0] = 0xEB;
143-
vbr.jmpBoot[1] = 0x00;
144-
vbr.jmpBoot[2] = 0x90;
145-
memcpy(vbr.oemName, "MSWIN4.1", 8); // SDFormatter hardcodes this.
140+
// Boot sector.
141+
BootSec bs{};
142+
memcpy(bs.jmpBoot, "\xEB\x00\x90", 3); // Doesn't jump to bootCode but it's what SDFormatter uses.
143+
memcpy(bs.oemName, BS_DEFAULT_OEM_NAME, 8);
146144

147145
// BIOS Parameter Block (BPB).
148146
const u32 secPerClus = params.secPerClus;
149147
const u32 rsvdSecCnt = params.rsvdSecCnt;
150148
const u8 fatBits = params.fatBits;
151149
const u32 partSectors = static_cast<u32>(params.totSec - partStart);
152150
const u32 secPerFat = params.secPerFat;
153-
vbr.bytesPerSec = bytesPerSec;
154-
vbr.secPerClus = secPerClus;
155-
vbr.rsvdSecCnt = rsvdSecCnt;
156-
vbr.numFats = 2;
157-
vbr.rootEntCnt = (fatBits == 32 ? 0 : 512);
158-
vbr.totSec16 = (partSectors > 0xFFFF || fatBits == 32 ? 0 : partSectors); // Not used for FAT32.
159-
vbr.media = 0xF8;
160-
vbr.fatSz16 = (secPerFat > 0xFFFF || fatBits == 32 ? 0 : secPerFat); // Not used for FAT32.
161-
vbr.secPerTrk = params.secPerTrk;
162-
vbr.numHeads = params.heads;
163-
vbr.hiddSec = partStart;
164-
vbr.totSec32 = (partSectors > 0xFFFF || fatBits == 32 ? partSectors : 0);
165-
vbr.sigWord = 0xAA55;
151+
bs.bytesPerSec = bytesPerSec;
152+
bs.secPerClus = secPerClus;
153+
bs.rsvdSecCnt = rsvdSecCnt;
154+
bs.numFats = 2;
155+
bs.rootEntCnt = (fatBits == 32 ? 0 : 512);
156+
bs.totSec16 = (partSectors > 0xFFFF || fatBits == 32 ? 0 : partSectors); // Not used for FAT32.
157+
bs.media = BPB_DEFAULT_MEDIA;
158+
bs.fatSz16 = (secPerFat > 0xFFFF || fatBits == 32 ? 0 : secPerFat); // Not used for FAT32.
159+
bs.secPerTrk = params.secPerTrk;
160+
bs.numHeads = params.heads;
161+
bs.hiddSec = partStart;
162+
bs.totSec32 = (partSectors > 0xFFFF || fatBits == 32 ? partSectors : 0);
163+
bs.sigWord = EBPB_SIG_WORD;
166164

167165
if(fatBits < 32)
168166
{
169167
// Extended BIOS Parameter Block FAT12/FAT16.
170-
vbr.fat16.drvNum = 0x80;
171-
vbr.fat16.bootSig = 0x29;
172-
vbr.fat16.volId = makeVolId();
173-
memcpy(vbr.fat16.volLab, labelBuf, 11);
174-
memcpy(vbr.fat16.filSysType, (fatBits == 12 ? "FAT12 " : "FAT16 "), 8);
175-
memset(vbr.fat16.bootCode, 0xF4, sizeof(vbr.fat16.bootCode));
176-
177-
// Write Vbr.
178-
res = dev.write(reinterpret_cast<u8*>(&vbr), sizeof(Vbr));
168+
bs.ebpb.drvNum = EBPB_DEFAULT_DRV_NUM;
169+
bs.ebpb.bootSig = EBPB_BOOT_SIG;
170+
bs.ebpb.volId = makeVolId();
171+
memcpy(bs.ebpb.volLab, labelBuf, 11);
172+
memcpy(bs.ebpb.filSysType, (fatBits == 12 ? EBPB_FIL_SYS_TYPE_FAT12 : EBPB_FIL_SYS_TYPE_FAT16), 8);
173+
memset(bs.ebpb.bootCode, 0xF4, sizeof(bs.ebpb.bootCode)); // Fill with x86 hlt instructions.
174+
175+
// Write boot sector.
176+
res = dev.write(reinterpret_cast<u8*>(&bs), sizeof(BootSec));
179177
if(res != 0) return res;
180178
}
181179
else
182180
{
183181
// Extended BIOS Parameter Block FAT32.
184-
vbr.fat32.fatSz32 = secPerFat;
185-
vbr.fat32.extFlags = 0;
186-
vbr.fat32.fsVer = 0; // 0.0.
187-
vbr.fat32.rootClus = 2; // 2 or the first cluster not marked as defective.
188-
vbr.fat32.fsInfoSector = 1;
189-
vbr.fat32.bkBootSec = 6;
190-
vbr.fat32.drvNum = 0x80;
191-
vbr.fat32.bootSig = 0x29;
192-
vbr.fat32.volId = makeVolId();
193-
memcpy(vbr.fat32.volLab, labelBuf, 11);
194-
memcpy(vbr.fat32.filSysType, "FAT32 ", 8);
195-
memset(vbr.fat32.bootCode, 0xF4, sizeof(vbr.fat32.bootCode));
196-
197-
// Write Vbr.
198-
res = dev.write(reinterpret_cast<u8*>(&vbr), sizeof(Vbr));
182+
bs.ebpb32.fatSz32 = secPerFat;
183+
bs.ebpb32.extFlags = 0;
184+
bs.ebpb32.fsVer = 0; // 0.0.
185+
bs.ebpb32.rootClus = 2; // 2 or the first cluster not marked as defective.
186+
bs.ebpb32.fsInfoSector = 1;
187+
bs.ebpb32.bkBootSec = 6;
188+
bs.ebpb32.drvNum = EBPB_DEFAULT_DRV_NUM;
189+
bs.ebpb32.bootSig = EBPB_BOOT_SIG;
190+
bs.ebpb32.volId = makeVolId();
191+
memcpy(bs.ebpb32.volLab, labelBuf, 11);
192+
memcpy(bs.ebpb32.filSysType, EBPB_FIL_SYS_TYPE_FAT32, 8);
193+
memset(bs.ebpb32.bootCode, 0xF4, sizeof(bs.ebpb32.bootCode)); // Fill with x86 hlt instructions.
194+
195+
// Write boot sector.
196+
res = dev.write(reinterpret_cast<u8*>(&bs), sizeof(BootSec));
199197
if(res != 0) return res;
200198

201199
// There are apparently drivers based on wrong documentation stating the
@@ -205,50 +203,46 @@ int makeFsFat(const FormatParams &params, BufferedFsWriter &dev, const std::stri
205203
if(bytesPerSec > 512)
206204
{
207205
tmpOffset = curOffset + bytesPerSec - 2;
208-
res = dev.fillAndWrite(reinterpret_cast<u8*>(&vbr.sigWord), tmpOffset, 2);
206+
res = dev.fillAndWrite(reinterpret_cast<u8*>(&bs.sigWord), tmpOffset, 2);
209207
if(res != 0) return res;
210208
}
211209

212210
// Write FSInfo.
213211
FsInfo fsInfo{};
214-
fsInfo.leadSig = 0x41615252;
215-
fsInfo.strucSig = 0x61417272;
212+
fsInfo.leadSig = FS_INFO_LEAD_SIG;
213+
fsInfo.strucSig = FS_INFO_STRUC_SIG;
216214
fsInfo.freeCount = params.maxClus - 1;
217215
fsInfo.nxtFree = 3;
218-
fsInfo.trailSig = 0xAA550000;
216+
fsInfo.trailSig = FS_INFO_TRAIL_SIG;
219217
res = dev.write(reinterpret_cast<u8*>(&fsInfo), sizeof(FsInfo));
220218
if(res != 0) return res;
221219

222-
// TODO: FSInfo sector signature word needed?
223-
224220
// The FAT spec says there is actually a third boot sector with just a signature word.
225221
tmpOffset = curOffset + (2 * bytesPerSec) + bytesPerSec - 2;
226-
res = dev.fillAndWrite(reinterpret_cast<u8*>(&vbr.sigWord), tmpOffset, 2);
222+
res = dev.fillAndWrite(reinterpret_cast<u8*>(&bs.sigWord), tmpOffset, 2);
227223
if(res != 0) return res;
228224

229-
// Write copy of Vbr.
225+
// Write copy of boot sector.
230226
tmpOffset += 2 + (3 * bytesPerSec);
231-
res = dev.fillAndWrite(reinterpret_cast<u8*>(&vbr), tmpOffset, sizeof(Vbr));
227+
res = dev.fillAndWrite(reinterpret_cast<u8*>(&bs), tmpOffset, sizeof(BootSec));
232228
if(res != 0) return res;
233229

234-
// Write sector signature word of VBR copy.
230+
// Write sector signature word of boot sector copy.
235231
if(bytesPerSec > 512)
236232
{
237233
tmpOffset += bytesPerSec - 2;
238-
res = dev.fillAndWrite(reinterpret_cast<u8*>(&vbr.sigWord), tmpOffset, 2);
234+
res = dev.fillAndWrite(reinterpret_cast<u8*>(&bs.sigWord), tmpOffset, 2);
239235
if(res != 0) return res;
240236
}
241237

242-
// Free cluster count is 0xFFFFFFFF (unknown) for FSInfo copy.
243-
fsInfo.freeCount = 0xFFFFFFFF;
238+
// Free cluster count is unknown for FSInfo copy.
239+
fsInfo.freeCount = FS_INFO_UNK_FREE_COUNT;
244240
res = dev.write(reinterpret_cast<u8*>(&fsInfo), sizeof(FsInfo));
245241
if(res != 0) return res;
246242

247-
// TODO: FSInfo sector signature word needed?
248-
249243
// Write copy of third sector signature word.
250244
tmpOffset = curOffset + (8 * bytesPerSec) + bytesPerSec - 2;
251-
res = dev.fillAndWrite(reinterpret_cast<u8*>(&vbr.sigWord), tmpOffset, 2);
245+
res = dev.fillAndWrite(reinterpret_cast<u8*>(&bs.sigWord), tmpOffset, 2);
252246
if(res != 0) return res;
253247
}
254248

@@ -258,14 +252,15 @@ int makeFsFat(const FormatParams &params, BufferedFsWriter &dev, const std::stri
258252
if(fatBits < 32)
259253
{
260254
// Reserve first 2 FAT entries.
261-
*fat = (fatBits == 16 ? 0xFFFFFFF8 : 0x00FFFFF8);
255+
u32 rsvdEnt = (fatBits == 16 ? FAT16_EOF<<16 | FAT16_EOF : FAT12_EOF<<12 | FAT12_EOF);
256+
*fat = (rsvdEnt & ~0xFFu) | BPB_DEFAULT_MEDIA;
262257
}
263258
else
264259
{
265260
// Reserve first 2 FAT entries. A third entry for the root directory cluster.
266-
fat[0] = 0x0FFFFFF8;
267-
fat[1] = 0x0FFFFFFF;
268-
fat[2] = 0x0FFFFFFF;
261+
fat[0] = (FAT32_EOF & ~0xFFu) | BPB_DEFAULT_MEDIA;
262+
fat[1] = FAT32_EOF;
263+
fat[2] = FAT32_EOF;
269264
rsvdEntrySize = 3 * 4;
270265
}
271266

@@ -284,7 +279,7 @@ int makeFsFat(const FormatParams &params, BufferedFsWriter &dev, const std::stri
284279
{
285280
FatDir dir{}; // Make sure all other fields are zero.
286281
memcpy(dir.name, labelBuf, 11);
287-
dir.attr = 0x08; // ATTR_VOLUME_ID.
282+
dir.attr = DIR_ATTR_VOLUME_ID;
288283

289284
curOffset += secPerFat * bytesPerSec;
290285
res = dev.fillAndWrite(reinterpret_cast<u8*>(&dir), curOffset, sizeof(FatDir));

0 commit comments

Comments
 (0)