diff --git a/fs/fat/fat_write.c b/fs/fat/fat_write.c index 40a3860e47c..aab0b0e71d8 100644 --- a/fs/fat/fat_write.c +++ b/fs/fat/fat_write.c @@ -467,11 +467,12 @@ get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster, } /* - * Set the entry at index 'entry' in a FAT (16/32) table. + * Set the entry at index 'entry' in a FAT (12/16/32) table. */ static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) { - __u32 bufnum, offset; + __u32 bufnum, offset, off16; + __u16 val1, val2; switch (mydata->fatsize) { case 32: @@ -482,6 +483,10 @@ static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) bufnum = entry / FAT16BUFSIZE; offset = entry - bufnum * FAT16BUFSIZE; break; + case 12: + bufnum = entry / FAT12BUFSIZE; + offset = entry - bufnum * FAT12BUFSIZE; + break; default: /* Unsupported FAT size */ return -1; @@ -520,6 +525,45 @@ static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) break; case 16: ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value); + break; + case 12: + off16 = (offset * 3) / 4; + + switch (offset & 0x3) { + case 0: + val1 = cpu_to_le16(entry_value) & 0xfff; + ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff; + ((__u16 *)mydata->fatbuf)[off16] |= val1; + break; + case 1: + val1 = cpu_to_le16(entry_value) & 0xf; + val2 = (cpu_to_le16(entry_value) >> 4) & 0xff; + + ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000; + ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12); + + ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff; + ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2; + break; + case 2: + val1 = cpu_to_le16(entry_value) & 0xff; + val2 = (cpu_to_le16(entry_value) >> 8) & 0xf; + + ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00; + ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8); + + ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf; + ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2; + break; + case 3: + val1 = cpu_to_le16(entry_value) & 0xfff; + ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0; + ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4); + break; + default: + break; + } + break; default: return -1; @@ -529,7 +573,7 @@ static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value) } /* - * Determine the next free cluster after 'entry' in a FAT (16/32) table + * Determine the next free cluster after 'entry' in a FAT (12/16/32) table * and link it to 'entry'. EOC marker is not set on returned entry. */ static __u32 determine_fatent(fsdata *mydata, __u32 entry) @@ -651,6 +695,8 @@ static void flush_dir_table(fsdata *mydata, dir_entry **dentptr) set_fatent_value(mydata, dir_newclust, 0xffffff8); else if (mydata->fatsize == 16) set_fatent_value(mydata, dir_newclust, 0xfff8); + else if (mydata->fatsize == 12) + set_fatent_value(mydata, dir_newclust, 0xff8); dir_curclust = dir_newclust; @@ -670,16 +716,13 @@ static int clear_fatent(fsdata *mydata, __u32 entry) { __u32 fat_val; - while (1) { + while (!CHECK_CLUST(entry, mydata->fatsize)) { fat_val = get_fatent_value(mydata, entry); if (fat_val != 0) set_fatent_value(mydata, entry, 0); else break; - if (fat_val == 0xfffffff || fat_val == 0xffff) - break; - entry = fat_val; } @@ -750,7 +793,9 @@ set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, *gotsize += actsize; /* Mark end of file in FAT */ - if (mydata->fatsize == 16) + if (mydata->fatsize == 12) + newclust = 0xfff; + else if (mydata->fatsize == 16) newclust = 0xffff; else if (mydata->fatsize == 32) newclust = 0xfffffff;