I have implemented full support for FAT12 file system. Directories and files are creating correctly. This post describe what have done.
Revision 924
Changes affect only at the three files:
- fat_fat.h
- fat_fat.c
- fat_ops.c
1. I defined two macros to calculate count of clusters value:
#define DS(bs) (TS(bs) - SSA(bs))
#define CC(bs) (DS(bs) / SPC(bs))
2. Type of FAT (12,16,32) is calculated only on the basis of the count of clusters(according to Microsoft FAT specification). I.e. FAT with 4085 clusters or less will be determined as FAT12. FAT16 must contain more than 4085 clusters but less then 65525. I suggest to store this calculation in the reserved field of boot sector (bs->reserved).
#define FATTYPE(bs) (bs)->reserved
3. I redefined macros for FAT, because its value depends on FAT type:
#define FAT12_CLST_BAD 0x0ff7
#define FAT12_CLST_LAST1 0x0ff8
#define FAT12_CLST_LAST8 0x0fff
#define FAT16_CLST_BAD 0xfff7
#define FAT16_CLST_LAST1 0xfff8
#define FAT16_CLST_LAST8 0xffff
4. Insert this lines in every function witch early used FAT_CLST_LAST1 or FAT_CLST_BAD.
uint16_t clst_last1 = FATTYPE(bs) == 16 ? FAT16_CLST_LAST1 :
FAT12_CLST_LAST1;
uint16_t clst_bad = FATTYPE(bs) == 16 ? FAT16_CLST_BAD :
FAT12_CLST_BAD;
5. Calculation for type of FAT is processing while file system is mounted.
void fat_mounted(ipc_callid_t rid, ipc_call_t *request)
{
/* skip */
/* Storing FAT type (12, 16, 32) in reserved field (bs->reserved) */
if (CC(bs) < 4085) {
/* Volume is FAT12 */
printf("Found FAT12 filesystem\n");
(bs)->reserved = 12;
} else if (CC(bs) < 65525) {
/* Volume is FAT16 */
printf("Found FAT16 filesystem\n");
(bs)->reserved = 16;
} else {
/* Volume is FAT32 */
printf("FAT32 filesystem is not supported by FAT server. Sorry.\n");
block_fini(devmap_handle);
async_answer_0(rid, ENOTSUP);
return;
}
/* skip */
}
6. Instead of explicit adressing of FAT in several functions in fat_fat.c I have incapsulated all addressing logic into two functions: fat_get_cluster and fat_set_cluster. It requires to fully rewrite this functions, because in FAT12 value for some cluster may lay between two sectors, started from one and finishing on two. Therefore we need additional block_t *b1 - to store next sector data.
int
fat_get_cluster(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
fat_cluster_t clst, fat_cluster_t *value)
{
block_t *b, *b1;
aoff64_t offset;
int rc;
assert(fatno < FATCNT(bs));
if (FATTYPE(bs) == 16)
offset = (clst * 2);
else
offset = (clst + clst/2);
rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
offset / BPS(bs), BLOCK_FLAGS_NONE);
if (rc != EOK)
return rc;
/* This cluster access spans a sector boundary. Check only for FAT12 */
if (FATTYPE(bs) == 12 && (offset % BPS(bs)+1 == BPS(bs))) {
/* Is it last sector of FAT? */
if (offset / BPS(bs) < SF(bs)) { /* NO */
/* Reading next sector */
rc = block_get(&b1, devmap_handle, 1+RSCNT(bs)+SF(bs)*fatno +
offset / BPS(bs), BLOCK_FLAGS_NONE);
if (rc != EOK) {
block_put(b);
return rc;
}
/*
* Combining value with last byte of current sector and
* first byte of next sector
*/
*value = *(uint8_t *)(b->data + BPS(bs) - 1);
*value |= *(uint8_t *)(b1->data);
rc = block_put(b1);
if (rc != EOK) {
block_put(b);
return rc;
}
}
else { /* YES */
block_put(b);
return ERANGE;
}
}
else
*value = *(fat_cluster_t *)(b->data + offset % BPS(bs));
if (FATTYPE(bs) == 12) {
if (clst & 0x0001)
*value = (*value) >> 4;
else
*value = (*value) & 0x0fff;
}
*value = uint16_t_le2host(*value);
rc = block_put(b);
return rc;
}
7. To set cluster value we should load previous value, mask bits thats should not be changed (for FAT12 only) and store new value;
int
fat_set_cluster(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned fatno,
fat_cluster_t clst, fat_cluster_t value)
{
block_t *b, *b1;
aoff64_t offset;
fat_cluster_t *cp, temp;
int rc;
int spans = 0;
assert(fatno < FATCNT(bs));
if (FATTYPE(bs) == 16)
offset = (clst * 2);
else
offset = (clst + clst/2);
rc = block_get(&b, devmap_handle, RSCNT(bs) + SF(bs) * fatno +
offset / BPS(bs), BLOCK_FLAGS_NONE);
if (rc != EOK)
return rc;
/* This cluster access spans a sector boundary. Check only for FAT12 */
if (FATTYPE(bs) == 12 && (offset % BPS(bs)+1 == BPS(bs))) {
/* Is it last sector of FAT? */
if (offset / BPS(bs) < SF(bs)) { /* NO */
/* Reading next sector */
rc = block_get(&b1, devmap_handle, 1+RSCNT(bs)+SF(bs)*fatno +
offset / BPS(bs), BLOCK_FLAGS_NONE);
if (rc != EOK) {
block_put(b);
return rc;
}
/*
* Combining value with last byte of current sector and
* first byte of next sector
*/
spans=1;
cp = &temp;
*cp = *(uint8_t *)(b->data + BPS(bs) - 1);
*cp |= *(uint8_t *)(b1->data);
}
else { /* YES */
block_put(b);
return ERANGE;
}
}
else
cp = (fat_cluster_t *)(b->data + offset % BPS(bs));
value = host2uint16_t_le(value);
if (FATTYPE(bs) == 12) {
if (clst & 0x0001) {
*cp &= 0x000f;
*cp |= value << 4;
}
else {
*cp &= 0xf000;
*cp |= value & 0x0fff;
}
if (spans)
{
*(uint8_t *)(b->data + BPS(bs) - 1) = cp[0];
*(uint8_t *)(b1->data) = cp[1];
b1->dirty = true;
rc = block_put(b1);
if (rc != EOK) {
block_put(b);
return rc;
}
}
}
else
*cp = value;
b->dirty = true; /* need to sync block */
rc = block_put(b);
return rc;
}
8. In functions fat_sanity_check I added condition to check file system type because last check is not applicable to FAT12.
int fat_sanity_check(fat_bs_t *bs, devmap_handle_t devmap_handle)
{
/* skip */
/*
* Check that remaining bits of the first two entries are
* set to one. Not applicable to FAT12
*/
if (FATTYPE(bs)!=12 && ((e0 >> 8) != 0xff || e1 != 0xffff))
return ENOTSUP;
/* skip */
}
9. I rewrote fat_cluster_walk to use fat_get_cluster instead of explicit adressing FAT.
int
fat_cluster_walk(fat_bs_t *bs, devmap_handle_t devmap_handle, fat_cluster_t firstc,
fat_cluster_t *lastc, uint16_t *numc, uint16_t max_clusters)
{
uint16_t clusters = 0;
fat_cluster_t clst = firstc;
int rc;
uint16_t clst_last1 = FATTYPE(bs) == 16 ? FAT16_CLST_LAST1 : FAT12_CLST_LAST1;
uint16_t clst_bad = FATTYPE(bs) == 16 ? FAT16_CLST_BAD : FAT12_CLST_BAD;
if (firstc == FAT_CLST_RES0) {
/* No space allocated to the file. */
if (lastc)
*lastc = firstc;
if (numc)
*numc = 0;
return EOK;
}
while (clst < clst_last1 && clusters < max_clusters) {
assert(clst >= FAT_CLST_FIRST);
if (lastc)
*lastc = clst; /* remember the last cluster number */
/* read FAT1 */
rc = fat_get_cluster(bs, devmap_handle, FAT1, clst, &clst);
if (rc != EOK)
return rc;
assert(clst != clst_bad);
clusters++;
}
if (lastc && clst < clst_last1)
*lastc = clst;
if (numc)
*numc = clusters;
return EOK;
}
10. I rewrote fat_alloc_clusters to use fat_get_cluster instead of explicit adressing FAT. It also uses new macros to calculate count of clusters, so it does not need to check range for cluster as it was early.
int
fat_alloc_clusters(fat_bs_t *bs, devmap_handle_t devmap_handle, unsigned nclsts,
fat_cluster_t *mcl, fat_cluster_t *lcl)
{
fat_cluster_t *lifo; /* stack for storing free cluster numbers */
unsigned found = 0; /* top of the free cluster number stack */
fat_cluster_t clst, value;
int rc = EOK;
uint16_t clst_last1 = FATTYPE(bs) == 16 ? FAT16_CLST_LAST1 :
FAT12_CLST_LAST1;
lifo = (fat_cluster_t *) malloc(nclsts * sizeof(fat_cluster_t));
if (!lifo)
return ENOMEM;
/*
* Search FAT1 for unused clusters.
*/
fibril_mutex_lock(&fat_alloc_lock);
for (clst=FAT_CLST_FIRST; clst < CC(bs)+2 && found < nclsts; clst++) {
rc = fat_get_cluster(bs, devmap_handle, FAT1, clst, &value);
if (rc != EOK)
break;
if (value == FAT_CLST_RES0) {
/*
* The cluster is free. Put it into our stack
* of found clusters and mark it as non-free.
*/
lifo[found] = clst;
rc = fat_set_cluster(bs, devmap_handle, FAT1, clst,
(found == 0) ? clst_last1 :
lifo[found - 1]);
if (rc != EOK)
break;
found++;
}
}
if (rc == EOK && found == nclsts) {
rc = fat_alloc_shadow_clusters(bs, devmap_handle, lifo, nclsts);
if (rc == EOK) {
*mcl = lifo[found - 1];
*lcl = lifo[0];
free(lifo);
fibril_mutex_unlock(&fat_alloc_lock);
return EOK;
}
}
/* If something wrong - free the clusters */
if (found > 0) {
while (found--) {
rc = fat_set_cluster(bs, devmap_handle, FAT1, lifo[found],
FAT_CLST_RES0);
}
}
free(lifo);
fibril_mutex_unlock(&fat_alloc_lock);
return ENOSPC;
}