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