]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - fs/fat/fat_write.c
mmc: omap_hsmmc: enable 8bit interface for eMMC for AM43xx
[karo-tx-uboot.git] / fs / fat / fat_write.c
1 /*
2  * fat_write.c
3  *
4  * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
5  *
6  * SPDX-License-Identifier:     GPL-2.0+
7  */
8
9 #include <common.h>
10 #include <command.h>
11 #include <config.h>
12 #include <fat.h>
13 #include <asm/byteorder.h>
14 #include <part.h>
15 #include <linux/ctype.h>
16 #include <div64.h>
17 #include <linux/math64.h>
18 #include "fat.c"
19
20 static void uppercase(char *str, int len)
21 {
22         int i;
23
24         for (i = 0; i < len; i++) {
25                 *str = toupper(*str);
26                 str++;
27         }
28 }
29
30 static int total_sector;
31 static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
32 {
33         if (!cur_dev || !cur_dev->block_write)
34                 return -1;
35
36         if (cur_part_info.start + block + nr_blocks >
37                 cur_part_info.start + total_sector) {
38                 printf("error: overflow occurs\n");
39                 return -1;
40         }
41
42         return cur_dev->block_write(cur_dev->dev,
43                         cur_part_info.start + block, nr_blocks, buf);
44 }
45
46 /*
47  * Set short name in directory entry
48  */
49 static void set_name(dir_entry *dirent, const char *filename)
50 {
51         char s_name[VFAT_MAXLEN_BYTES];
52         char *period;
53         int period_location, len, i, ext_num;
54
55         if (filename == NULL)
56                 return;
57
58         len = strlen(filename);
59         if (len == 0)
60                 return;
61
62         strcpy(s_name, filename);
63         uppercase(s_name, len);
64
65         period = strchr(s_name, '.');
66         if (period == NULL) {
67                 period_location = len;
68                 ext_num = 0;
69         } else {
70                 period_location = period - s_name;
71                 ext_num = len - period_location - 1;
72         }
73
74         /* Pad spaces when the length of file name is shorter than eight */
75         if (period_location < 8) {
76                 memcpy(dirent->name, s_name, period_location);
77                 for (i = period_location; i < 8; i++)
78                         dirent->name[i] = ' ';
79         } else if (period_location == 8) {
80                 memcpy(dirent->name, s_name, period_location);
81         } else {
82                 memcpy(dirent->name, s_name, 6);
83                 dirent->name[6] = '~';
84                 dirent->name[7] = '1';
85         }
86
87         if (ext_num < 3) {
88                 memcpy(dirent->ext, s_name + period_location + 1, ext_num);
89                 for (i = ext_num; i < 3; i++)
90                         dirent->ext[i] = ' ';
91         } else
92                 memcpy(dirent->ext, s_name + period_location + 1, 3);
93
94         debug("name : %s\n", dirent->name);
95         debug("ext : %s\n", dirent->ext);
96 }
97
98 static __u8 num_of_fats;
99 /*
100  * Write fat buffer into block device
101  */
102 static int flush_fat_buffer(fsdata *mydata)
103 {
104         int getsize = FATBUFBLOCKS;
105         __u32 fatlength = mydata->fatlength;
106         __u8 *bufptr = mydata->fatbuf;
107         __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
108
109         startblock += mydata->fat_sect;
110
111         if (getsize > fatlength)
112                 getsize = fatlength;
113
114         /* Write FAT buf */
115         if (disk_write(startblock, getsize, bufptr) < 0) {
116                 debug("error: writing FAT blocks\n");
117                 return -1;
118         }
119
120         if (num_of_fats == 2) {
121                 /* Update corresponding second FAT blocks */
122                 startblock += mydata->fatlength;
123                 if (disk_write(startblock, getsize, bufptr) < 0) {
124                         debug("error: writing second FAT blocks\n");
125                         return -1;
126                 }
127         }
128
129         return 0;
130 }
131
132 /*
133  * Get the entry at index 'entry' in a FAT (12/16/32) table.
134  * On failure 0x00 is returned.
135  * When bufnum is changed, write back the previous fatbuf to the disk.
136  */
137 static __u32 get_fatent_value(fsdata *mydata, __u32 entry)
138 {
139         __u32 bufnum;
140         __u32 off16, offset;
141         __u32 ret = 0x00;
142         __u16 val1, val2;
143
144         if (CHECK_CLUST(entry, mydata->fatsize)) {
145                 printf("Error: Invalid FAT entry: 0x%08x\n", entry);
146                 return ret;
147         }
148
149         switch (mydata->fatsize) {
150         case 32:
151                 bufnum = entry / FAT32BUFSIZE;
152                 offset = entry - bufnum * FAT32BUFSIZE;
153                 break;
154         case 16:
155                 bufnum = entry / FAT16BUFSIZE;
156                 offset = entry - bufnum * FAT16BUFSIZE;
157                 break;
158         case 12:
159                 bufnum = entry / FAT12BUFSIZE;
160                 offset = entry - bufnum * FAT12BUFSIZE;
161                 break;
162
163         default:
164                 /* Unsupported FAT size */
165                 return ret;
166         }
167
168         debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
169                mydata->fatsize, entry, entry, offset, offset);
170
171         /* Read a new block of FAT entries into the cache. */
172         if (bufnum != mydata->fatbufnum) {
173                 int getsize = FATBUFBLOCKS;
174                 __u8 *bufptr = mydata->fatbuf;
175                 __u32 fatlength = mydata->fatlength;
176                 __u32 startblock = bufnum * FATBUFBLOCKS;
177
178                 if (getsize > fatlength)
179                         getsize = fatlength;
180
181                 fatlength *= mydata->sect_size; /* We want it in bytes now */
182                 startblock += mydata->fat_sect; /* Offset from start of disk */
183
184                 /* Write back the fatbuf to the disk */
185                 if (mydata->fatbufnum != -1) {
186                         if (flush_fat_buffer(mydata) < 0)
187                                 return -1;
188                 }
189
190                 if (disk_read(startblock, getsize, bufptr) < 0) {
191                         debug("Error reading FAT blocks\n");
192                         return ret;
193                 }
194                 mydata->fatbufnum = bufnum;
195         }
196
197         /* Get the actual entry from the table */
198         switch (mydata->fatsize) {
199         case 32:
200                 ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
201                 break;
202         case 16:
203                 ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
204                 break;
205         case 12:
206                 off16 = (offset * 3) / 4;
207
208                 switch (offset & 0x3) {
209                 case 0:
210                         ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
211                         ret &= 0xfff;
212                         break;
213                 case 1:
214                         val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
215                         val1 &= 0xf000;
216                         val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
217                         val2 &= 0x00ff;
218                         ret = (val2 << 4) | (val1 >> 12);
219                         break;
220                 case 2:
221                         val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
222                         val1 &= 0xff00;
223                         val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
224                         val2 &= 0x000f;
225                         ret = (val2 << 8) | (val1 >> 8);
226                         break;
227                 case 3:
228                         ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
229                         ret = (ret & 0xfff0) >> 4;
230                         break;
231                 default:
232                         break;
233                 }
234                 break;
235         }
236         debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n",
237                mydata->fatsize, ret, entry, offset);
238
239         return ret;
240 }
241
242 /*
243  * Set the file name information from 'name' into 'slotptr',
244  */
245 static int str2slot(dir_slot *slotptr, const char *name, int *idx)
246 {
247         int j, end_idx = 0;
248
249         for (j = 0; j <= 8; j += 2) {
250                 if (name[*idx] == 0x00) {
251                         slotptr->name0_4[j] = 0;
252                         slotptr->name0_4[j + 1] = 0;
253                         end_idx++;
254                         goto name0_4;
255                 }
256                 slotptr->name0_4[j] = name[*idx];
257                 (*idx)++;
258                 end_idx++;
259         }
260         for (j = 0; j <= 10; j += 2) {
261                 if (name[*idx] == 0x00) {
262                         slotptr->name5_10[j] = 0;
263                         slotptr->name5_10[j + 1] = 0;
264                         end_idx++;
265                         goto name5_10;
266                 }
267                 slotptr->name5_10[j] = name[*idx];
268                 (*idx)++;
269                 end_idx++;
270         }
271         for (j = 0; j <= 2; j += 2) {
272                 if (name[*idx] == 0x00) {
273                         slotptr->name11_12[j] = 0;
274                         slotptr->name11_12[j + 1] = 0;
275                         end_idx++;
276                         goto name11_12;
277                 }
278                 slotptr->name11_12[j] = name[*idx];
279                 (*idx)++;
280                 end_idx++;
281         }
282
283         if (name[*idx] == 0x00)
284                 return 1;
285
286         return 0;
287 /* Not used characters are filled with 0xff 0xff */
288 name0_4:
289         for (; end_idx < 5; end_idx++) {
290                 slotptr->name0_4[end_idx * 2] = 0xff;
291                 slotptr->name0_4[end_idx * 2 + 1] = 0xff;
292         }
293         end_idx = 5;
294 name5_10:
295         end_idx -= 5;
296         for (; end_idx < 6; end_idx++) {
297                 slotptr->name5_10[end_idx * 2] = 0xff;
298                 slotptr->name5_10[end_idx * 2 + 1] = 0xff;
299         }
300         end_idx = 11;
301 name11_12:
302         end_idx -= 11;
303         for (; end_idx < 2; end_idx++) {
304                 slotptr->name11_12[end_idx * 2] = 0xff;
305                 slotptr->name11_12[end_idx * 2 + 1] = 0xff;
306         }
307
308         return 1;
309 }
310
311 static int is_next_clust(fsdata *mydata, dir_entry *dentptr);
312 static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
313
314 /*
315  * Fill dir_slot entries with appropriate name, id, and attr
316  * The real directory entry is returned by 'dentptr'
317  */
318 static void
319 fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
320 {
321         dir_slot *slotptr = (dir_slot *)get_contents_vfatname_block;
322         __u8 counter = 0, checksum;
323         int idx = 0, ret;
324         char s_name[16];
325
326         /* Get short file name and checksum value */
327         strncpy(s_name, (*dentptr)->name, 16);
328         checksum = mkcksum((*dentptr)->name, (*dentptr)->ext);
329
330         do {
331                 memset(slotptr, 0x00, sizeof(dir_slot));
332                 ret = str2slot(slotptr, l_name, &idx);
333                 slotptr->id = ++counter;
334                 slotptr->attr = ATTR_VFAT;
335                 slotptr->alias_checksum = checksum;
336                 slotptr++;
337         } while (ret == 0);
338
339         slotptr--;
340         slotptr->id |= LAST_LONG_ENTRY_MASK;
341
342         while (counter >= 1) {
343                 if (is_next_clust(mydata, *dentptr)) {
344                         /* A new cluster is allocated for directory table */
345                         flush_dir_table(mydata, dentptr);
346                 }
347                 memcpy(*dentptr, slotptr, sizeof(dir_slot));
348                 (*dentptr)++;
349                 slotptr--;
350                 counter--;
351         }
352
353         if (is_next_clust(mydata, *dentptr)) {
354                 /* A new cluster is allocated for directory table */
355                 flush_dir_table(mydata, dentptr);
356         }
357 }
358
359 static __u32 dir_curclust;
360
361 /*
362  * Extract the full long filename starting at 'retdent' (which is really
363  * a slot) into 'l_name'. If successful also copy the real directory entry
364  * into 'retdent'
365  * If additional adjacent cluster for directory entries is read into memory,
366  * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and
367  * the location of the real directory entry is returned by 'retdent'
368  * Return 0 on success, -1 otherwise.
369  */
370 static int
371 get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
372               dir_entry **retdent, char *l_name)
373 {
374         dir_entry *realdent;
375         dir_slot *slotptr = (dir_slot *)(*retdent);
376         dir_slot *slotptr2 = NULL;
377         __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
378                                                         PREFETCH_BLOCKS :
379                                                         mydata->clust_size);
380         __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
381         int idx = 0, cur_position = 0;
382
383         if (counter > VFAT_MAXSEQ) {
384                 debug("Error: VFAT name is too long\n");
385                 return -1;
386         }
387
388         while ((__u8 *)slotptr < buflimit) {
389                 if (counter == 0)
390                         break;
391                 if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
392                         return -1;
393                 slotptr++;
394                 counter--;
395         }
396
397         if ((__u8 *)slotptr >= buflimit) {
398                 if (curclust == 0)
399                         return -1;
400                 curclust = get_fatent_value(mydata, dir_curclust);
401                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
402                         debug("curclust: 0x%x\n", curclust);
403                         printf("Invalid FAT entry\n");
404                         return -1;
405                 }
406
407                 dir_curclust = curclust;
408
409                 if (get_cluster(mydata, curclust, get_contents_vfatname_block,
410                                 mydata->clust_size * mydata->sect_size) != 0) {
411                         debug("Error: reading directory block\n");
412                         return -1;
413                 }
414
415                 slotptr2 = (dir_slot *)get_contents_vfatname_block;
416                 while (counter > 0) {
417                         if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
418                             & 0xff) != counter)
419                                 return -1;
420                         slotptr2++;
421                         counter--;
422                 }
423
424                 /* Save the real directory entry */
425                 realdent = (dir_entry *)slotptr2;
426                 while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
427                         slotptr2--;
428                         slot2str(slotptr2, l_name, &idx);
429                 }
430         } else {
431                 /* Save the real directory entry */
432                 realdent = (dir_entry *)slotptr;
433         }
434
435         do {
436                 slotptr--;
437                 if (slot2str(slotptr, l_name, &idx))
438                         break;
439         } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
440
441         l_name[idx] = '\0';
442         if (*l_name == DELETED_FLAG)
443                 *l_name = '\0';
444         else if (*l_name == aRING)
445                 *l_name = DELETED_FLAG;
446         downcase(l_name);
447
448         /* Return the real directory entry */
449         *retdent = realdent;
450
451         if (slotptr2) {
452                 memcpy(get_dentfromdir_block, get_contents_vfatname_block,
453                         mydata->clust_size * mydata->sect_size);
454                 cur_position = (__u8 *)realdent - get_contents_vfatname_block;
455                 *retdent = (dir_entry *) &get_dentfromdir_block[cur_position];
456         }
457
458         return 0;
459 }
460
461 /*
462  * Set the entry at index 'entry' in a FAT (16/32) table.
463  */
464 static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
465 {
466         __u32 bufnum, offset;
467
468         switch (mydata->fatsize) {
469         case 32:
470                 bufnum = entry / FAT32BUFSIZE;
471                 offset = entry - bufnum * FAT32BUFSIZE;
472                 break;
473         case 16:
474                 bufnum = entry / FAT16BUFSIZE;
475                 offset = entry - bufnum * FAT16BUFSIZE;
476                 break;
477         default:
478                 /* Unsupported FAT size */
479                 return -1;
480         }
481
482         /* Read a new block of FAT entries into the cache. */
483         if (bufnum != mydata->fatbufnum) {
484                 int getsize = FATBUFBLOCKS;
485                 __u8 *bufptr = mydata->fatbuf;
486                 __u32 fatlength = mydata->fatlength;
487                 __u32 startblock = bufnum * FATBUFBLOCKS;
488
489                 fatlength *= mydata->sect_size;
490                 startblock += mydata->fat_sect;
491
492                 if (getsize > fatlength)
493                         getsize = fatlength;
494
495                 if (mydata->fatbufnum != -1) {
496                         if (flush_fat_buffer(mydata) < 0)
497                                 return -1;
498                 }
499
500                 if (disk_read(startblock, getsize, bufptr) < 0) {
501                         debug("Error reading FAT blocks\n");
502                         return -1;
503                 }
504                 mydata->fatbufnum = bufnum;
505         }
506
507         /* Set the actual entry */
508         switch (mydata->fatsize) {
509         case 32:
510                 ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
511                 break;
512         case 16:
513                 ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
514                 break;
515         default:
516                 return -1;
517         }
518
519         return 0;
520 }
521
522 /*
523  * Determine the entry value at index 'entry' in a FAT (16/32) table
524  */
525 static __u32 determine_fatent(fsdata *mydata, __u32 entry)
526 {
527         __u32 next_fat, next_entry = entry + 1;
528
529         while (1) {
530                 next_fat = get_fatent_value(mydata, next_entry);
531                 if (next_fat == 0) {
532                         set_fatent_value(mydata, entry, next_entry);
533                         break;
534                 }
535                 next_entry++;
536         }
537         debug("FAT%d: entry: %08x, entry_value: %04x\n",
538                mydata->fatsize, entry, next_entry);
539
540         return next_entry;
541 }
542
543 /*
544  * Write at most 'size' bytes from 'buffer' into the specified cluster.
545  * Return 0 on success, -1 otherwise.
546  */
547 static int
548 set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
549              unsigned long size)
550 {
551         int idx = 0;
552         __u32 startsect;
553
554         if (clustnum > 0)
555                 startsect = mydata->data_begin +
556                                 clustnum * mydata->clust_size;
557         else
558                 startsect = mydata->rootdir_sect;
559
560         debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
561
562         if ((size / mydata->sect_size) > 0) {
563                 if (disk_write(startsect, size / mydata->sect_size, buffer) < 0) {
564                         debug("Error writing data\n");
565                         return -1;
566                 }
567         }
568
569         if (size % mydata->sect_size) {
570                 __u8 tmpbuf[mydata->sect_size];
571
572                 idx = size / mydata->sect_size;
573                 buffer += idx * mydata->sect_size;
574                 memcpy(tmpbuf, buffer, size % mydata->sect_size);
575
576                 if (disk_write(startsect + idx, 1, tmpbuf) < 0) {
577                         debug("Error writing data\n");
578                         return -1;
579                 }
580
581                 return 0;
582         }
583
584         return 0;
585 }
586
587 /*
588  * Find the first empty cluster
589  */
590 static int find_empty_cluster(fsdata *mydata)
591 {
592         __u32 fat_val, entry = 3;
593
594         while (1) {
595                 fat_val = get_fatent_value(mydata, entry);
596                 if (fat_val == 0)
597                         break;
598                 entry++;
599         }
600
601         return entry;
602 }
603
604 /*
605  * Write directory entries in 'get_dentfromdir_block' to block device
606  */
607 static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
608 {
609         int dir_newclust = 0;
610
611         if (set_cluster(mydata, dir_curclust,
612                     get_dentfromdir_block,
613                     mydata->clust_size * mydata->sect_size) != 0) {
614                 printf("error: wrinting directory entry\n");
615                 return;
616         }
617         dir_newclust = find_empty_cluster(mydata);
618         set_fatent_value(mydata, dir_curclust, dir_newclust);
619         if (mydata->fatsize == 32)
620                 set_fatent_value(mydata, dir_newclust, 0xffffff8);
621         else if (mydata->fatsize == 16)
622                 set_fatent_value(mydata, dir_newclust, 0xfff8);
623
624         dir_curclust = dir_newclust;
625
626         if (flush_fat_buffer(mydata) < 0)
627                 return;
628
629         memset(get_dentfromdir_block, 0x00,
630                 mydata->clust_size * mydata->sect_size);
631
632         *dentptr = (dir_entry *) get_dentfromdir_block;
633 }
634
635 /*
636  * Set empty cluster from 'entry' to the end of a file
637  */
638 static int clear_fatent(fsdata *mydata, __u32 entry)
639 {
640         __u32 fat_val;
641
642         while (1) {
643                 fat_val = get_fatent_value(mydata, entry);
644                 if (fat_val != 0)
645                         set_fatent_value(mydata, entry, 0);
646                 else
647                         break;
648
649                 if (fat_val == 0xfffffff || fat_val == 0xffff)
650                         break;
651
652                 entry = fat_val;
653         }
654
655         /* Flush fat buffer */
656         if (flush_fat_buffer(mydata) < 0)
657                 return -1;
658
659         return 0;
660 }
661
662 /*
663  * Write at most 'maxsize' bytes from 'buffer' into
664  * the file associated with 'dentptr'
665  * Update the number of bytes written in *gotsize and return 0
666  * or return -1 on fatal errors.
667  */
668 static int
669 set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
670               loff_t maxsize, loff_t *gotsize)
671 {
672         loff_t filesize = FAT2CPU32(dentptr->size);
673         unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
674         __u32 curclust = START(dentptr);
675         __u32 endclust = 0, newclust = 0;
676         loff_t actsize;
677
678         *gotsize = 0;
679         debug("Filesize: %llu bytes\n", filesize);
680
681         if (maxsize > 0 && filesize > maxsize)
682                 filesize = maxsize;
683
684         debug("%llu bytes\n", filesize);
685
686         actsize = bytesperclust;
687         endclust = curclust;
688         do {
689                 /* search for consecutive clusters */
690                 while (actsize < filesize) {
691                         newclust = determine_fatent(mydata, endclust);
692
693                         if ((newclust - 1) != endclust)
694                                 goto getit;
695
696                         if (CHECK_CLUST(newclust, mydata->fatsize)) {
697                                 debug("curclust: 0x%x\n", newclust);
698                                 debug("Invalid FAT entry\n");
699                                 return 0;
700                         }
701                         endclust = newclust;
702                         actsize += bytesperclust;
703                 }
704                 /* actsize >= file size */
705                 actsize -= bytesperclust;
706                 /* set remaining clusters */
707                 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
708                         debug("error: writing cluster\n");
709                         return -1;
710                 }
711
712                 /* set remaining bytes */
713                 *gotsize += actsize;
714                 filesize -= actsize;
715                 buffer += actsize;
716                 actsize = filesize;
717
718                 if (set_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
719                         debug("error: writing cluster\n");
720                         return -1;
721                 }
722                 *gotsize += actsize;
723
724                 /* Mark end of file in FAT */
725                 if (mydata->fatsize == 16)
726                         newclust = 0xffff;
727                 else if (mydata->fatsize == 32)
728                         newclust = 0xfffffff;
729                 set_fatent_value(mydata, endclust, newclust);
730
731                 return 0;
732 getit:
733                 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
734                         debug("error: writing cluster\n");
735                         return -1;
736                 }
737                 *gotsize += actsize;
738                 filesize -= actsize;
739                 buffer += actsize;
740
741                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
742                         debug("curclust: 0x%x\n", curclust);
743                         debug("Invalid FAT entry\n");
744                         return 0;
745                 }
746                 actsize = bytesperclust;
747                 curclust = endclust = newclust;
748         } while (1);
749 }
750
751 /*
752  * Fill dir_entry
753  */
754 static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
755         const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
756 {
757         if (mydata->fatsize == 32)
758                 dentptr->starthi =
759                         cpu_to_le16((start_cluster & 0xffff0000) >> 16);
760         dentptr->start = cpu_to_le16(start_cluster & 0xffff);
761         dentptr->size = cpu_to_le32(size);
762
763         dentptr->attr = attr;
764
765         set_name(dentptr, filename);
766 }
767
768 /*
769  * Check whether adding a file makes the file system to
770  * exceed the size of the block device
771  * Return -1 when overflow occurs, otherwise return 0
772  */
773 static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
774 {
775         __u32 startsect, sect_num, offset;
776
777         if (clustnum > 0) {
778                 startsect = mydata->data_begin +
779                                 clustnum * mydata->clust_size;
780         } else {
781                 startsect = mydata->rootdir_sect;
782         }
783
784         sect_num = div_u64_rem(size, mydata->sect_size, &offset);
785
786         if (offset != 0)
787                 sect_num++;
788
789         if (startsect + sect_num > cur_part_info.start + total_sector)
790                 return -1;
791         return 0;
792 }
793
794 /*
795  * Check if adding several entries exceed one cluster boundary
796  */
797 static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
798 {
799         int cur_position;
800
801         cur_position = (__u8 *)dentptr - get_dentfromdir_block;
802
803         if (cur_position >= mydata->clust_size * mydata->sect_size)
804                 return 1;
805         else
806                 return 0;
807 }
808
809 static dir_entry *empty_dentptr;
810 /*
811  * Find a directory entry based on filename or start cluster number
812  * If the directory entry is not found,
813  * the new position for writing a directory entry will be returned
814  */
815 static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
816         char *filename, dir_entry *retdent, __u32 start)
817 {
818         __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size;
819
820         debug("get_dentfromdir: %s\n", filename);
821
822         while (1) {
823                 dir_entry *dentptr;
824
825                 int i;
826
827                 if (get_cluster(mydata, curclust, get_dentfromdir_block,
828                             mydata->clust_size * mydata->sect_size) != 0) {
829                         printf("Error: reading directory block\n");
830                         return NULL;
831                 }
832
833                 dentptr = (dir_entry *)get_dentfromdir_block;
834
835                 dir_curclust = curclust;
836
837                 for (i = 0; i < DIRENTSPERCLUST; i++) {
838                         char s_name[14], l_name[VFAT_MAXLEN_BYTES];
839
840                         l_name[0] = '\0';
841                         if (dentptr->name[0] == DELETED_FLAG) {
842                                 dentptr++;
843                                 if (is_next_clust(mydata, dentptr))
844                                         break;
845                                 continue;
846                         }
847                         if ((dentptr->attr & ATTR_VOLUME)) {
848                                 if (vfat_enabled &&
849                                     (dentptr->attr & ATTR_VFAT) &&
850                                     (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
851                                         get_long_file_name(mydata, curclust,
852                                                      get_dentfromdir_block,
853                                                      &dentptr, l_name);
854                                         debug("vfatname: |%s|\n", l_name);
855                                 } else {
856                                         /* Volume label or VFAT entry */
857                                         dentptr++;
858                                         if (is_next_clust(mydata, dentptr))
859                                                 break;
860                                         continue;
861                                 }
862                         }
863                         if (dentptr->name[0] == 0) {
864                                 debug("Dentname == NULL - %d\n", i);
865                                 empty_dentptr = dentptr;
866                                 return NULL;
867                         }
868
869                         get_name(dentptr, s_name);
870
871                         if (strcmp(filename, s_name)
872                             && strcmp(filename, l_name)) {
873                                 debug("Mismatch: |%s|%s|\n",
874                                         s_name, l_name);
875                                 dentptr++;
876                                 if (is_next_clust(mydata, dentptr))
877                                         break;
878                                 continue;
879                         }
880
881                         memcpy(retdent, dentptr, sizeof(dir_entry));
882
883                         debug("DentName: %s", s_name);
884                         debug(", start: 0x%x", START(dentptr));
885                         debug(", size:  0x%x %s\n",
886                               FAT2CPU32(dentptr->size),
887                               (dentptr->attr & ATTR_DIR) ?
888                               "(DIR)" : "");
889
890                         return dentptr;
891                 }
892
893                 /*
894                  * In FAT16/12, the root dir is locate before data area, shows
895                  * in following:
896                  * -------------------------------------------------------------
897                  * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) |
898                  * -------------------------------------------------------------
899                  *
900                  * As a result if curclust is in Root dir, it is a negative
901                  * number or 0, 1.
902                  *
903                  */
904                 if (mydata->fatsize != 32 && (int)curclust <= 1) {
905                         /* Current clust is in root dir, set to next clust */
906                         curclust++;
907                         if ((int)curclust <= 1)
908                                 continue;       /* continue to find */
909
910                         /* Reach the end of root dir */
911                         empty_dentptr = dentptr;
912                         return NULL;
913                 }
914
915                 curclust = get_fatent_value(mydata, dir_curclust);
916                 if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
917                         empty_dentptr = dentptr;
918                         return NULL;
919                 }
920                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
921                         debug("curclust: 0x%x\n", curclust);
922                         debug("Invalid FAT entry\n");
923                         return NULL;
924                 }
925         }
926
927         return NULL;
928 }
929
930 static int do_fat_write(const char *filename, void *buffer, loff_t size,
931                         loff_t *actwrite)
932 {
933         dir_entry *dentptr, *retdent;
934         __u32 startsect;
935         __u32 start_cluster;
936         boot_sector bs;
937         volume_info volinfo;
938         fsdata datablock;
939         fsdata *mydata = &datablock;
940         int cursect;
941         int ret = -1, name_len;
942         char l_filename[VFAT_MAXLEN_BYTES];
943
944         *actwrite = size;
945         dir_curclust = 0;
946
947         if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
948                 debug("error: reading boot sector\n");
949                 return -1;
950         }
951
952         total_sector = bs.total_sect;
953         if (total_sector == 0)
954                 total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
955
956         if (mydata->fatsize == 32)
957                 mydata->fatlength = bs.fat32_length;
958         else
959                 mydata->fatlength = bs.fat_length;
960
961         mydata->fat_sect = bs.reserved;
962
963         cursect = mydata->rootdir_sect
964                 = mydata->fat_sect + mydata->fatlength * bs.fats;
965         num_of_fats = bs.fats;
966
967         mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
968         mydata->clust_size = bs.cluster_size;
969
970         if (mydata->fatsize == 32) {
971                 mydata->data_begin = mydata->rootdir_sect -
972                                         (mydata->clust_size * 2);
973         } else {
974                 int rootdir_size;
975
976                 rootdir_size = ((bs.dir_entries[1]  * (int)256 +
977                                  bs.dir_entries[0]) *
978                                  sizeof(dir_entry)) /
979                                  mydata->sect_size;
980                 mydata->data_begin = mydata->rootdir_sect +
981                                         rootdir_size -
982                                         (mydata->clust_size * 2);
983         }
984
985         mydata->fatbufnum = -1;
986         mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
987         if (mydata->fatbuf == NULL) {
988                 debug("Error: allocating memory\n");
989                 return -1;
990         }
991
992         if (disk_read(cursect,
993                 (mydata->fatsize == 32) ?
994                 (mydata->clust_size) :
995                 PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
996                 debug("Error: reading rootdir block\n");
997                 goto exit;
998         }
999         dentptr = (dir_entry *) do_fat_read_at_block;
1000
1001         name_len = strlen(filename);
1002         if (name_len >= VFAT_MAXLEN_BYTES)
1003                 name_len = VFAT_MAXLEN_BYTES - 1;
1004
1005         memcpy(l_filename, filename, name_len);
1006         l_filename[name_len] = 0; /* terminate the string */
1007         downcase(l_filename);
1008
1009         startsect = mydata->rootdir_sect;
1010         retdent = find_directory_entry(mydata, startsect,
1011                                 l_filename, dentptr, 0);
1012         if (retdent) {
1013                 /* Update file size and start_cluster in a directory entry */
1014                 retdent->size = cpu_to_le32(size);
1015                 start_cluster = FAT2CPU16(retdent->start);
1016                 if (mydata->fatsize == 32)
1017                         start_cluster |=
1018                                 (FAT2CPU16(retdent->starthi) << 16);
1019
1020                 ret = check_overflow(mydata, start_cluster, size);
1021                 if (ret) {
1022                         printf("Error: %llu overflow\n", size);
1023                         goto exit;
1024                 }
1025
1026                 ret = clear_fatent(mydata, start_cluster);
1027                 if (ret) {
1028                         printf("Error: clearing FAT entries\n");
1029                         goto exit;
1030                 }
1031
1032                 ret = set_contents(mydata, retdent, buffer, size, actwrite);
1033                 if (ret < 0) {
1034                         printf("Error: writing contents\n");
1035                         goto exit;
1036                 }
1037                 debug("attempt to write 0x%llx bytes\n", *actwrite);
1038
1039                 /* Flush fat buffer */
1040                 ret = flush_fat_buffer(mydata);
1041                 if (ret) {
1042                         printf("Error: flush fat buffer\n");
1043                         goto exit;
1044                 }
1045
1046                 /* Write directory table to device */
1047                 ret = set_cluster(mydata, dir_curclust,
1048                             get_dentfromdir_block,
1049                             mydata->clust_size * mydata->sect_size);
1050                 if (ret) {
1051                         printf("Error: writing directory entry\n");
1052                         goto exit;
1053                 }
1054         } else {
1055                 /* Set short name to set alias checksum field in dir_slot */
1056                 set_name(empty_dentptr, filename);
1057                 fill_dir_slot(mydata, &empty_dentptr, filename);
1058
1059                 ret = start_cluster = find_empty_cluster(mydata);
1060                 if (ret < 0) {
1061                         printf("Error: finding empty cluster\n");
1062                         goto exit;
1063                 }
1064
1065                 ret = check_overflow(mydata, start_cluster, size);
1066                 if (ret) {
1067                         printf("Error: %llu overflow\n", size);
1068                         goto exit;
1069                 }
1070
1071                 /* Set attribute as archieve for regular file */
1072                 fill_dentry(mydata, empty_dentptr, filename,
1073                         start_cluster, size, 0x20);
1074
1075                 ret = set_contents(mydata, empty_dentptr, buffer, size,
1076                                    actwrite);
1077                 if (ret < 0) {
1078                         printf("Error: writing contents\n");
1079                         goto exit;
1080                 }
1081                 debug("attempt to write 0x%llx bytes\n", *actwrite);
1082
1083                 /* Flush fat buffer */
1084                 ret = flush_fat_buffer(mydata);
1085                 if (ret) {
1086                         printf("Error: flush fat buffer\n");
1087                         goto exit;
1088                 }
1089
1090                 /* Write directory table to device */
1091                 ret = set_cluster(mydata, dir_curclust,
1092                             get_dentfromdir_block,
1093                             mydata->clust_size * mydata->sect_size);
1094                 if (ret) {
1095                         printf("Error: writing directory entry\n");
1096                         goto exit;
1097                 }
1098         }
1099
1100 exit:
1101         free(mydata->fatbuf);
1102         return ret;
1103 }
1104
1105 int file_fat_write(const char *filename, void *buffer, loff_t offset,
1106                    loff_t maxsize, loff_t *actwrite)
1107 {
1108         if (offset != 0) {
1109                 printf("Error: non zero offset is currently not suported.\n");
1110                 return -1;
1111         }
1112
1113         printf("writing %s\n", filename);
1114         return do_fat_write(filename, buffer, maxsize, actwrite);
1115 }