Wednesday, April 27, 2011

(Commit 925) Macros

1. I have introduced two macros: FAT_IS_FAT12 and FAT_IS_FAT16 for determining type of FAT and macros for calculating cluster bad and last value based on FAT type.
uspace/srv/fs/fat/fat_fat.h
 
#define FAT_IS_FAT12(bs)        (CC(bs) < FAT12_CLST_MAX)
#define FAT_IS_FAT16(bs) \
    ((CC(bs) >= FAT12_CLST_MAX) && (CC(bs) < FAT16_CLST_MAX))
#define FAT_CLST_LAST1(bs) \
    (FAT_IS_FAT12(bs) ? FAT12_CLST_LAST1 : FAT16_CLST_LAST1)
#define FAT_CLST_LAST8(bs) \
    (FAT_IS_FAT12(bs) ? FAT12_CLST_LAST8 : FAT16_CLST_LAST8)
#define FAT_CLST_BAD(bs) \
    (FAT_IS_FAT12(bs) ? FAT12_CLST_BAD : FAT16_CLST_BAD)
Max clusters count for FAT12
 
#define FAT12_CLST_MAX    4085
Max clusters count for FAT16
 
#define FAT16_CLST_MAX    65525

2. I used new macros while mounting first to check type of FAT:
uspace/srv/fs/fat/fat_ops.c
 
        /* Determining type of FAT  */
        if (FAT_IS_FAT12(bs)) {
                printf("Found FAT12 filesystem\n");
        } else if (FAT_IS_FAT16(bs)) {
                printf("Found FAT16 filesystem\n");
        } else {
                printf("FAT32 filesystem is not supported by FAT server.\n");
                block_fini(devmap_handle);
                async_answer_0(rid, ENOTSUP);
                return;
        }

3. Of course I have changed in fat_fat.c all FATTYPE(bs) == 12 to FAT_IS_FAT12(bs)

4. I try going to use FAT_IS_FAT12 instead of FAT_IS_FAT16 because it require only one calculation for clusters count.

5. I insert additional variables like clst_last1 or clst_bad to store FAT depended values when its necessary.
 
fat_cluster_t clst_last1 = FAT_CLST_LAST1(bs);
fat_cluster_t clst_bad = FAT_CLST_BAD(bs);

Sunday, April 10, 2011

(Commit 924) Initial FAT12 support

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;
}