Merge tag 'ti-v2020.07-rc3' of https://gitlab.denx.de/u-boot/custodians/u-boot-ti
[platform/kernel/u-boot.git] / fs / fat / fat_write.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * fat_write.c
4  *
5  * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
6  */
7
8 #include <common.h>
9 #include <command.h>
10 #include <config.h>
11 #include <fat.h>
12 #include <log.h>
13 #include <malloc.h>
14 #include <asm/byteorder.h>
15 #include <part.h>
16 #include <asm/cache.h>
17 #include <linux/ctype.h>
18 #include <div64.h>
19 #include <linux/math64.h>
20 #include "fat.c"
21
22 static void uppercase(char *str, int len)
23 {
24         int i;
25
26         for (i = 0; i < len; i++) {
27                 *str = toupper(*str);
28                 str++;
29         }
30 }
31
32 static int total_sector;
33 static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
34 {
35         ulong ret;
36
37         if (!cur_dev)
38                 return -1;
39
40         if (cur_part_info.start + block + nr_blocks >
41                 cur_part_info.start + total_sector) {
42                 printf("error: overflow occurs\n");
43                 return -1;
44         }
45
46         ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
47         if (nr_blocks && ret == 0)
48                 return -1;
49
50         return ret;
51 }
52
53 /*
54  * Set short name in directory entry
55  */
56 static void set_name(dir_entry *dirent, const char *filename)
57 {
58         char s_name[VFAT_MAXLEN_BYTES];
59         char *period;
60         int period_location, len, i, ext_num;
61
62         if (filename == NULL)
63                 return;
64
65         len = strlen(filename);
66         if (len == 0)
67                 return;
68
69         strcpy(s_name, filename);
70         uppercase(s_name, len);
71
72         period = strchr(s_name, '.');
73         if (period == NULL) {
74                 period_location = len;
75                 ext_num = 0;
76         } else {
77                 period_location = period - s_name;
78                 ext_num = len - period_location - 1;
79         }
80
81         /* Pad spaces when the length of file name is shorter than eight */
82         if (period_location < 8) {
83                 memcpy(dirent->name, s_name, period_location);
84                 for (i = period_location; i < 8; i++)
85                         dirent->name[i] = ' ';
86         } else if (period_location == 8) {
87                 memcpy(dirent->name, s_name, period_location);
88         } else {
89                 memcpy(dirent->name, s_name, 6);
90                 dirent->name[6] = '~';
91                 dirent->name[7] = '1';
92         }
93
94         if (ext_num < 3) {
95                 memcpy(dirent->ext, s_name + period_location + 1, ext_num);
96                 for (i = ext_num; i < 3; i++)
97                         dirent->ext[i] = ' ';
98         } else
99                 memcpy(dirent->ext, s_name + period_location + 1, 3);
100
101         debug("name : %s\n", dirent->name);
102         debug("ext : %s\n", dirent->ext);
103 }
104
105 /*
106  * Write fat buffer into block device
107  */
108 static int flush_dirty_fat_buffer(fsdata *mydata)
109 {
110         int getsize = FATBUFBLOCKS;
111         __u32 fatlength = mydata->fatlength;
112         __u8 *bufptr = mydata->fatbuf;
113         __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
114
115         debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
116               (int)mydata->fat_dirty);
117
118         if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
119                 return 0;
120
121         /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
122         if (startblock + getsize > fatlength)
123                 getsize = fatlength - startblock;
124
125         startblock += mydata->fat_sect;
126
127         /* Write FAT buf */
128         if (disk_write(startblock, getsize, bufptr) < 0) {
129                 debug("error: writing FAT blocks\n");
130                 return -1;
131         }
132
133         if (mydata->fats == 2) {
134                 /* Update corresponding second FAT blocks */
135                 startblock += mydata->fatlength;
136                 if (disk_write(startblock, getsize, bufptr) < 0) {
137                         debug("error: writing second FAT blocks\n");
138                         return -1;
139                 }
140         }
141         mydata->fat_dirty = 0;
142
143         return 0;
144 }
145
146 /*
147  * Set the file name information from 'name' into 'slotptr',
148  */
149 static int str2slot(dir_slot *slotptr, const char *name, int *idx)
150 {
151         int j, end_idx = 0;
152
153         for (j = 0; j <= 8; j += 2) {
154                 if (name[*idx] == 0x00) {
155                         slotptr->name0_4[j] = 0;
156                         slotptr->name0_4[j + 1] = 0;
157                         end_idx++;
158                         goto name0_4;
159                 }
160                 slotptr->name0_4[j] = name[*idx];
161                 (*idx)++;
162                 end_idx++;
163         }
164         for (j = 0; j <= 10; j += 2) {
165                 if (name[*idx] == 0x00) {
166                         slotptr->name5_10[j] = 0;
167                         slotptr->name5_10[j + 1] = 0;
168                         end_idx++;
169                         goto name5_10;
170                 }
171                 slotptr->name5_10[j] = name[*idx];
172                 (*idx)++;
173                 end_idx++;
174         }
175         for (j = 0; j <= 2; j += 2) {
176                 if (name[*idx] == 0x00) {
177                         slotptr->name11_12[j] = 0;
178                         slotptr->name11_12[j + 1] = 0;
179                         end_idx++;
180                         goto name11_12;
181                 }
182                 slotptr->name11_12[j] = name[*idx];
183                 (*idx)++;
184                 end_idx++;
185         }
186
187         if (name[*idx] == 0x00)
188                 return 1;
189
190         return 0;
191 /* Not used characters are filled with 0xff 0xff */
192 name0_4:
193         for (; end_idx < 5; end_idx++) {
194                 slotptr->name0_4[end_idx * 2] = 0xff;
195                 slotptr->name0_4[end_idx * 2 + 1] = 0xff;
196         }
197         end_idx = 5;
198 name5_10:
199         end_idx -= 5;
200         for (; end_idx < 6; end_idx++) {
201                 slotptr->name5_10[end_idx * 2] = 0xff;
202                 slotptr->name5_10[end_idx * 2 + 1] = 0xff;
203         }
204         end_idx = 11;
205 name11_12:
206         end_idx -= 11;
207         for (; end_idx < 2; end_idx++) {
208                 slotptr->name11_12[end_idx * 2] = 0xff;
209                 slotptr->name11_12[end_idx * 2 + 1] = 0xff;
210         }
211
212         return 1;
213 }
214
215 static int new_dir_table(fat_itr *itr);
216 static int flush_dir(fat_itr *itr);
217
218 /*
219  * Fill dir_slot entries with appropriate name, id, and attr
220  * 'itr' will point to a next entry
221  */
222 static int
223 fill_dir_slot(fat_itr *itr, const char *l_name)
224 {
225         __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
226         dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
227         __u8 counter = 0, checksum;
228         int idx = 0, ret;
229
230         /* Get short file name checksum value */
231         checksum = mkcksum(itr->dent->name, itr->dent->ext);
232
233         do {
234                 memset(slotptr, 0x00, sizeof(dir_slot));
235                 ret = str2slot(slotptr, l_name, &idx);
236                 slotptr->id = ++counter;
237                 slotptr->attr = ATTR_VFAT;
238                 slotptr->alias_checksum = checksum;
239                 slotptr++;
240         } while (ret == 0);
241
242         slotptr--;
243         slotptr->id |= LAST_LONG_ENTRY_MASK;
244
245         while (counter >= 1) {
246                 memcpy(itr->dent, slotptr, sizeof(dir_slot));
247                 slotptr--;
248                 counter--;
249
250                 if (itr->remaining == 0)
251                         flush_dir(itr);
252
253                 /* allocate a cluster for more entries */
254                 if (!fat_itr_next(itr))
255                         if (!itr->dent &&
256                             (!itr->is_root || itr->fsdata->fatsize == 32) &&
257                             new_dir_table(itr))
258                                 return -1;
259         }
260
261         return 0;
262 }
263
264 /*
265  * Set the entry at index 'entry' in a FAT (12/16/32) table.
266  */
267 static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
268 {
269         __u32 bufnum, offset, off16;
270         __u16 val1, val2;
271
272         switch (mydata->fatsize) {
273         case 32:
274                 bufnum = entry / FAT32BUFSIZE;
275                 offset = entry - bufnum * FAT32BUFSIZE;
276                 break;
277         case 16:
278                 bufnum = entry / FAT16BUFSIZE;
279                 offset = entry - bufnum * FAT16BUFSIZE;
280                 break;
281         case 12:
282                 bufnum = entry / FAT12BUFSIZE;
283                 offset = entry - bufnum * FAT12BUFSIZE;
284                 break;
285         default:
286                 /* Unsupported FAT size */
287                 return -1;
288         }
289
290         /* Read a new block of FAT entries into the cache. */
291         if (bufnum != mydata->fatbufnum) {
292                 int getsize = FATBUFBLOCKS;
293                 __u8 *bufptr = mydata->fatbuf;
294                 __u32 fatlength = mydata->fatlength;
295                 __u32 startblock = bufnum * FATBUFBLOCKS;
296
297                 /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
298                 if (startblock + getsize > fatlength)
299                         getsize = fatlength - startblock;
300
301                 if (flush_dirty_fat_buffer(mydata) < 0)
302                         return -1;
303
304                 startblock += mydata->fat_sect;
305
306                 if (disk_read(startblock, getsize, bufptr) < 0) {
307                         debug("Error reading FAT blocks\n");
308                         return -1;
309                 }
310                 mydata->fatbufnum = bufnum;
311         }
312
313         /* Mark as dirty */
314         mydata->fat_dirty = 1;
315
316         /* Set the actual entry */
317         switch (mydata->fatsize) {
318         case 32:
319                 ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
320                 break;
321         case 16:
322                 ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
323                 break;
324         case 12:
325                 off16 = (offset * 3) / 4;
326
327                 switch (offset & 0x3) {
328                 case 0:
329                         val1 = cpu_to_le16(entry_value) & 0xfff;
330                         ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
331                         ((__u16 *)mydata->fatbuf)[off16] |= val1;
332                         break;
333                 case 1:
334                         val1 = cpu_to_le16(entry_value) & 0xf;
335                         val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
336
337                         ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
338                         ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
339
340                         ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
341                         ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
342                         break;
343                 case 2:
344                         val1 = cpu_to_le16(entry_value) & 0xff;
345                         val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
346
347                         ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
348                         ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
349
350                         ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
351                         ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
352                         break;
353                 case 3:
354                         val1 = cpu_to_le16(entry_value) & 0xfff;
355                         ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
356                         ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
357                         break;
358                 default:
359                         break;
360                 }
361
362                 break;
363         default:
364                 return -1;
365         }
366
367         return 0;
368 }
369
370 /*
371  * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
372  * and link it to 'entry'. EOC marker is not set on returned entry.
373  */
374 static __u32 determine_fatent(fsdata *mydata, __u32 entry)
375 {
376         __u32 next_fat, next_entry = entry + 1;
377
378         while (1) {
379                 next_fat = get_fatent(mydata, next_entry);
380                 if (next_fat == 0) {
381                         /* found free entry, link to entry */
382                         set_fatent_value(mydata, entry, next_entry);
383                         break;
384                 }
385                 next_entry++;
386         }
387         debug("FAT%d: entry: %08x, entry_value: %04x\n",
388                mydata->fatsize, entry, next_entry);
389
390         return next_entry;
391 }
392
393 /**
394  * set_sectors() - write data to sectors
395  *
396  * Write 'size' bytes from 'buffer' into the specified sector.
397  *
398  * @mydata:     data to be written
399  * @startsect:  sector to be written to
400  * @buffer:     data to be written
401  * @size:       bytes to be written (but not more than the size of a cluster)
402  * Return:      0 on success, -1 otherwise
403  */
404 static int
405 set_sectors(fsdata *mydata, u32 startsect, u8 *buffer, u32 size)
406 {
407         u32 nsects = 0;
408         int ret;
409
410         debug("startsect: %d\n", startsect);
411
412         if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
413                 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
414
415                 debug("FAT: Misaligned buffer address (%p)\n", buffer);
416
417                 while (size >= mydata->sect_size) {
418                         memcpy(tmpbuf, buffer, mydata->sect_size);
419                         ret = disk_write(startsect++, 1, tmpbuf);
420                         if (ret != 1) {
421                                 debug("Error writing data (got %d)\n", ret);
422                                 return -1;
423                         }
424
425                         buffer += mydata->sect_size;
426                         size -= mydata->sect_size;
427                 }
428         } else if (size >= mydata->sect_size) {
429                 nsects = size / mydata->sect_size;
430                 ret = disk_write(startsect, nsects, buffer);
431                 if (ret != nsects) {
432                         debug("Error writing data (got %d)\n", ret);
433                         return -1;
434                 }
435
436                 startsect += nsects;
437                 buffer += nsects * mydata->sect_size;
438                 size -= nsects * mydata->sect_size;
439         }
440
441         if (size) {
442                 ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
443                 /* Do not leak content of stack */
444                 memset(tmpbuf, 0, mydata->sect_size);
445                 memcpy(tmpbuf, buffer, size);
446                 ret = disk_write(startsect, 1, tmpbuf);
447                 if (ret != 1) {
448                         debug("Error writing data (got %d)\n", ret);
449                         return -1;
450                 }
451         }
452
453         return 0;
454 }
455
456 /**
457  * set_cluster() - write data to cluster
458  *
459  * Write 'size' bytes from 'buffer' into the specified cluster.
460  *
461  * @mydata:     data to be written
462  * @clustnum:   cluster to be written to
463  * @buffer:     data to be written
464  * @size:       bytes to be written (but not more than the size of a cluster)
465  * Return:      0 on success, -1 otherwise
466  */
467 static int
468 set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
469 {
470         return set_sectors(mydata, clust_to_sect(mydata, clustnum),
471                            buffer, size);
472 }
473
474 static int
475 flush_dir(fat_itr *itr)
476 {
477         fsdata *mydata = itr->fsdata;
478         u32 startsect, sect_offset, nsects;
479
480         if (!itr->is_root || mydata->fatsize == 32)
481                 return set_cluster(mydata, itr->clust, itr->block,
482                                    mydata->clust_size * mydata->sect_size);
483
484         sect_offset = itr->clust * mydata->clust_size;
485         startsect = mydata->rootdir_sect + sect_offset;
486         /* do not write past the end of rootdir */
487         nsects = min_t(u32, mydata->clust_size,
488                        mydata->rootdir_size - sect_offset);
489
490         return set_sectors(mydata, startsect, itr->block,
491                            nsects * mydata->sect_size);
492 }
493
494 static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
495
496 /*
497  * Read and modify data on existing and consecutive cluster blocks
498  */
499 static int
500 get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
501                 loff_t size, loff_t *gotsize)
502 {
503         unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
504         __u32 startsect;
505         loff_t wsize;
506         int clustcount, i, ret;
507
508         *gotsize = 0;
509         if (!size)
510                 return 0;
511
512         assert(pos < bytesperclust);
513         startsect = clust_to_sect(mydata, clustnum);
514
515         debug("clustnum: %d, startsect: %d, pos: %lld\n",
516               clustnum, startsect, pos);
517
518         /* partial write at beginning */
519         if (pos) {
520                 wsize = min(bytesperclust - pos, size);
521                 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
522                 if (ret != mydata->clust_size) {
523                         debug("Error reading data (got %d)\n", ret);
524                         return -1;
525                 }
526
527                 memcpy(tmpbuf_cluster + pos, buffer, wsize);
528                 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
529                 if (ret != mydata->clust_size) {
530                         debug("Error writing data (got %d)\n", ret);
531                         return -1;
532                 }
533
534                 size -= wsize;
535                 buffer += wsize;
536                 *gotsize += wsize;
537
538                 startsect += mydata->clust_size;
539
540                 if (!size)
541                         return 0;
542         }
543
544         /* full-cluster write */
545         if (size >= bytesperclust) {
546                 clustcount = lldiv(size, bytesperclust);
547
548                 if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
549                         wsize = clustcount * bytesperclust;
550                         ret = disk_write(startsect,
551                                          clustcount * mydata->clust_size,
552                                          buffer);
553                         if (ret != clustcount * mydata->clust_size) {
554                                 debug("Error writing data (got %d)\n", ret);
555                                 return -1;
556                         }
557
558                         size -= wsize;
559                         buffer += wsize;
560                         *gotsize += wsize;
561
562                         startsect += clustcount * mydata->clust_size;
563                 } else {
564                         for (i = 0; i < clustcount; i++) {
565                                 memcpy(tmpbuf_cluster, buffer, bytesperclust);
566                                 ret = disk_write(startsect,
567                                                  mydata->clust_size,
568                                                  tmpbuf_cluster);
569                                 if (ret != mydata->clust_size) {
570                                         debug("Error writing data (got %d)\n",
571                                               ret);
572                                         return -1;
573                                 }
574
575                                 size -= bytesperclust;
576                                 buffer += bytesperclust;
577                                 *gotsize += bytesperclust;
578
579                                 startsect += mydata->clust_size;
580                         }
581                 }
582         }
583
584         /* partial write at end */
585         if (size) {
586                 wsize = size;
587                 ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
588                 if (ret != mydata->clust_size) {
589                         debug("Error reading data (got %d)\n", ret);
590                         return -1;
591                 }
592                 memcpy(tmpbuf_cluster, buffer, wsize);
593                 ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
594                 if (ret != mydata->clust_size) {
595                         debug("Error writing data (got %d)\n", ret);
596                         return -1;
597                 }
598
599                 size -= wsize;
600                 buffer += wsize;
601                 *gotsize += wsize;
602         }
603
604         assert(!size);
605
606         return 0;
607 }
608
609 /*
610  * Find the first empty cluster
611  */
612 static int find_empty_cluster(fsdata *mydata)
613 {
614         __u32 fat_val, entry = 3;
615
616         while (1) {
617                 fat_val = get_fatent(mydata, entry);
618                 if (fat_val == 0)
619                         break;
620                 entry++;
621         }
622
623         return entry;
624 }
625
626 /*
627  * Allocate a cluster for additional directory entries
628  */
629 static int new_dir_table(fat_itr *itr)
630 {
631         fsdata *mydata = itr->fsdata;
632         int dir_newclust = 0;
633         unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
634
635         dir_newclust = find_empty_cluster(mydata);
636         set_fatent_value(mydata, itr->clust, dir_newclust);
637         if (mydata->fatsize == 32)
638                 set_fatent_value(mydata, dir_newclust, 0xffffff8);
639         else if (mydata->fatsize == 16)
640                 set_fatent_value(mydata, dir_newclust, 0xfff8);
641         else if (mydata->fatsize == 12)
642                 set_fatent_value(mydata, dir_newclust, 0xff8);
643
644         itr->clust = dir_newclust;
645         itr->next_clust = dir_newclust;
646
647         if (flush_dirty_fat_buffer(mydata) < 0)
648                 return -1;
649
650         memset(itr->block, 0x00, bytesperclust);
651
652         itr->dent = (dir_entry *)itr->block;
653         itr->last_cluster = 1;
654         itr->remaining = bytesperclust / sizeof(dir_entry) - 1;
655
656         return 0;
657 }
658
659 /*
660  * Set empty cluster from 'entry' to the end of a file
661  */
662 static int clear_fatent(fsdata *mydata, __u32 entry)
663 {
664         __u32 fat_val;
665
666         while (!CHECK_CLUST(entry, mydata->fatsize)) {
667                 fat_val = get_fatent(mydata, entry);
668                 if (fat_val != 0)
669                         set_fatent_value(mydata, entry, 0);
670                 else
671                         break;
672
673                 entry = fat_val;
674         }
675
676         /* Flush fat buffer */
677         if (flush_dirty_fat_buffer(mydata) < 0)
678                 return -1;
679
680         return 0;
681 }
682
683 /*
684  * Set start cluster in directory entry
685  */
686 static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
687                               __u32 start_cluster)
688 {
689         if (mydata->fatsize == 32)
690                 dentptr->starthi =
691                         cpu_to_le16((start_cluster & 0xffff0000) >> 16);
692         dentptr->start = cpu_to_le16(start_cluster & 0xffff);
693 }
694
695 /*
696  * Check whether adding a file makes the file system to
697  * exceed the size of the block device
698  * Return -1 when overflow occurs, otherwise return 0
699  */
700 static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
701 {
702         __u32 startsect, sect_num, offset;
703
704         if (clustnum > 0)
705                 startsect = clust_to_sect(mydata, clustnum);
706         else
707                 startsect = mydata->rootdir_sect;
708
709         sect_num = div_u64_rem(size, mydata->sect_size, &offset);
710
711         if (offset != 0)
712                 sect_num++;
713
714         if (startsect + sect_num > total_sector)
715                 return -1;
716         return 0;
717 }
718
719 /*
720  * Write at most 'maxsize' bytes from 'buffer' into
721  * the file associated with 'dentptr'
722  * Update the number of bytes written in *gotsize and return 0
723  * or return -1 on fatal errors.
724  */
725 static int
726 set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
727              loff_t maxsize, loff_t *gotsize)
728 {
729         unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
730         __u32 curclust = START(dentptr);
731         __u32 endclust = 0, newclust = 0;
732         u64 cur_pos, filesize;
733         loff_t offset, actsize, wsize;
734
735         *gotsize = 0;
736         filesize = pos + maxsize;
737
738         debug("%llu bytes\n", filesize);
739
740         if (!filesize) {
741                 if (!curclust)
742                         return 0;
743                 if (!CHECK_CLUST(curclust, mydata->fatsize) ||
744                     IS_LAST_CLUST(curclust, mydata->fatsize)) {
745                         clear_fatent(mydata, curclust);
746                         set_start_cluster(mydata, dentptr, 0);
747                         return 0;
748                 }
749                 debug("curclust: 0x%x\n", curclust);
750                 debug("Invalid FAT entry\n");
751                 return -1;
752         }
753
754         if (!curclust) {
755                 assert(pos == 0);
756                 goto set_clusters;
757         }
758
759         /* go to cluster at pos */
760         cur_pos = bytesperclust;
761         while (1) {
762                 if (pos <= cur_pos)
763                         break;
764                 if (IS_LAST_CLUST(curclust, mydata->fatsize))
765                         break;
766
767                 newclust = get_fatent(mydata, curclust);
768                 if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
769                     CHECK_CLUST(newclust, mydata->fatsize)) {
770                         debug("curclust: 0x%x\n", curclust);
771                         debug("Invalid FAT entry\n");
772                         return -1;
773                 }
774
775                 cur_pos += bytesperclust;
776                 curclust = newclust;
777         }
778         if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
779                 assert(pos == cur_pos);
780                 goto set_clusters;
781         }
782
783         assert(pos < cur_pos);
784         cur_pos -= bytesperclust;
785
786         /* overwrite */
787         assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
788                !CHECK_CLUST(curclust, mydata->fatsize));
789
790         while (1) {
791                 /* search for allocated consecutive clusters */
792                 actsize = bytesperclust;
793                 endclust = curclust;
794                 while (1) {
795                         if (filesize <= (cur_pos + actsize))
796                                 break;
797
798                         newclust = get_fatent(mydata, endclust);
799
800                         if (newclust != endclust + 1)
801                                 break;
802                         if (IS_LAST_CLUST(newclust, mydata->fatsize))
803                                 break;
804                         if (CHECK_CLUST(newclust, mydata->fatsize)) {
805                                 debug("curclust: 0x%x\n", curclust);
806                                 debug("Invalid FAT entry\n");
807                                 return -1;
808                         }
809
810                         actsize += bytesperclust;
811                         endclust = newclust;
812                 }
813
814                 /* overwrite to <curclust..endclust> */
815                 if (pos < cur_pos)
816                         offset = 0;
817                 else
818                         offset = pos - cur_pos;
819                 wsize = min_t(unsigned long long, actsize, filesize - cur_pos);
820                 wsize -= offset;
821
822                 if (get_set_cluster(mydata, curclust, offset,
823                                     buffer, wsize, &actsize)) {
824                         printf("Error get-and-setting cluster\n");
825                         return -1;
826                 }
827                 buffer += wsize;
828                 *gotsize += wsize;
829                 cur_pos += offset + wsize;
830
831                 if (filesize <= cur_pos)
832                         break;
833
834                 if (IS_LAST_CLUST(newclust, mydata->fatsize))
835                         /* no more clusters */
836                         break;
837
838                 curclust = newclust;
839         }
840
841         if (filesize <= cur_pos) {
842                 /* no more write */
843                 newclust = get_fatent(mydata, endclust);
844                 if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
845                         /* truncate the rest */
846                         clear_fatent(mydata, newclust);
847
848                         /* Mark end of file in FAT */
849                         if (mydata->fatsize == 12)
850                                 newclust = 0xfff;
851                         else if (mydata->fatsize == 16)
852                                 newclust = 0xffff;
853                         else if (mydata->fatsize == 32)
854                                 newclust = 0xfffffff;
855                         set_fatent_value(mydata, endclust, newclust);
856                 }
857
858                 return 0;
859         }
860
861         curclust = endclust;
862         filesize -= cur_pos;
863         assert(!do_div(cur_pos, bytesperclust));
864
865 set_clusters:
866         /* allocate and write */
867         assert(!pos);
868
869         /* Assure that curclust is valid */
870         if (!curclust) {
871                 curclust = find_empty_cluster(mydata);
872                 set_start_cluster(mydata, dentptr, curclust);
873         } else {
874                 newclust = get_fatent(mydata, curclust);
875
876                 if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
877                         newclust = determine_fatent(mydata, curclust);
878                         set_fatent_value(mydata, curclust, newclust);
879                         curclust = newclust;
880                 } else {
881                         debug("error: something wrong\n");
882                         return -1;
883                 }
884         }
885
886         /* TODO: already partially written */
887         if (check_overflow(mydata, curclust, filesize)) {
888                 printf("Error: no space left: %llu\n", filesize);
889                 return -1;
890         }
891
892         actsize = bytesperclust;
893         endclust = curclust;
894         do {
895                 /* search for consecutive clusters */
896                 while (actsize < filesize) {
897                         newclust = determine_fatent(mydata, endclust);
898
899                         if ((newclust - 1) != endclust)
900                                 /* write to <curclust..endclust> */
901                                 goto getit;
902
903                         if (CHECK_CLUST(newclust, mydata->fatsize)) {
904                                 debug("newclust: 0x%x\n", newclust);
905                                 debug("Invalid FAT entry\n");
906                                 return 0;
907                         }
908                         endclust = newclust;
909                         actsize += bytesperclust;
910                 }
911
912                 /* set remaining bytes */
913                 actsize = filesize;
914                 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
915                         debug("error: writing cluster\n");
916                         return -1;
917                 }
918                 *gotsize += actsize;
919
920                 /* Mark end of file in FAT */
921                 if (mydata->fatsize == 12)
922                         newclust = 0xfff;
923                 else if (mydata->fatsize == 16)
924                         newclust = 0xffff;
925                 else if (mydata->fatsize == 32)
926                         newclust = 0xfffffff;
927                 set_fatent_value(mydata, endclust, newclust);
928
929                 return 0;
930 getit:
931                 if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
932                         debug("error: writing cluster\n");
933                         return -1;
934                 }
935                 *gotsize += actsize;
936                 filesize -= actsize;
937                 buffer += actsize;
938
939                 if (CHECK_CLUST(newclust, mydata->fatsize)) {
940                         debug("newclust: 0x%x\n", newclust);
941                         debug("Invalid FAT entry\n");
942                         return 0;
943                 }
944                 actsize = bytesperclust;
945                 curclust = endclust = newclust;
946         } while (1);
947
948         return 0;
949 }
950
951 /*
952  * Fill dir_entry
953  */
954 static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
955         const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
956 {
957         set_start_cluster(mydata, dentptr, start_cluster);
958         dentptr->size = cpu_to_le32(size);
959
960         dentptr->attr = attr;
961
962         set_name(dentptr, filename);
963 }
964
965 /*
966  * Find a directory entry based on filename or start cluster number
967  * If the directory entry is not found,
968  * the new position for writing a directory entry will be returned
969  */
970 static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
971 {
972         int match = 0;
973
974         while (fat_itr_next(itr)) {
975                 /* check both long and short name: */
976                 if (!strcasecmp(filename, itr->name))
977                         match = 1;
978                 else if (itr->name != itr->s_name &&
979                          !strcasecmp(filename, itr->s_name))
980                         match = 1;
981
982                 if (!match)
983                         continue;
984
985                 if (itr->dent->name[0] == '\0')
986                         return NULL;
987                 else
988                         return itr->dent;
989         }
990
991         /* allocate a cluster for more entries */
992         if (!itr->dent &&
993             (!itr->is_root || itr->fsdata->fatsize == 32) &&
994             new_dir_table(itr))
995                 /* indicate that allocating dent failed */
996                 itr->dent = NULL;
997
998         return NULL;
999 }
1000
1001 static int split_filename(char *filename, char **dirname, char **basename)
1002 {
1003         char *p, *last_slash, *last_slash_cont;
1004
1005 again:
1006         p = filename;
1007         last_slash = NULL;
1008         last_slash_cont = NULL;
1009         while (*p) {
1010                 if (ISDIRDELIM(*p)) {
1011                         last_slash = p;
1012                         last_slash_cont = p;
1013                         /* continuous slashes */
1014                         while (ISDIRDELIM(*p))
1015                                 last_slash_cont = p++;
1016                         if (!*p)
1017                                 break;
1018                 }
1019                 p++;
1020         }
1021
1022         if (last_slash) {
1023                 if (last_slash_cont == (filename + strlen(filename) - 1)) {
1024                         /* remove trailing slashes */
1025                         *last_slash = '\0';
1026                         goto again;
1027                 }
1028
1029                 if (last_slash == filename) {
1030                         /* avoid ""(null) directory */
1031                         *dirname = "/";
1032                 } else {
1033                         *last_slash = '\0';
1034                         *dirname = filename;
1035                 }
1036
1037                 *last_slash_cont = '\0';
1038                 *basename = last_slash_cont + 1;
1039         } else {
1040                 *dirname = "/"; /* root by default */
1041                 *basename = filename;
1042         }
1043
1044         return 0;
1045 }
1046
1047 /**
1048  * normalize_longname() - check long file name and convert to lower case
1049  *
1050  * We assume here that the FAT file system is using an 8bit code page.
1051  * Linux typically uses CP437, EDK2 assumes CP1250.
1052  *
1053  * @l_filename: preallocated buffer receiving the normalized name
1054  * @filename:   filename to normalize
1055  * Return:      0 on success, -1 on failure
1056  */
1057 static int normalize_longname(char *l_filename, const char *filename)
1058 {
1059         const char *p, illegal[] = "<>:\"/\\|?*";
1060
1061         if (strlen(filename) >= VFAT_MAXLEN_BYTES)
1062                 return -1;
1063
1064         for (p = filename; *p; ++p) {
1065                 if ((unsigned char)*p < 0x20)
1066                         return -1;
1067                 if (strchr(illegal, *p))
1068                         return -1;
1069         }
1070
1071         strcpy(l_filename, filename);
1072         downcase(l_filename, VFAT_MAXLEN_BYTES);
1073
1074         return 0;
1075 }
1076
1077 int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
1078                       loff_t size, loff_t *actwrite)
1079 {
1080         dir_entry *retdent;
1081         fsdata datablock = { .fatbuf = NULL, };
1082         fsdata *mydata = &datablock;
1083         fat_itr *itr = NULL;
1084         int ret = -1;
1085         char *filename_copy, *parent, *basename;
1086         char l_filename[VFAT_MAXLEN_BYTES];
1087
1088         debug("writing %s\n", filename);
1089
1090         filename_copy = strdup(filename);
1091         if (!filename_copy)
1092                 return -ENOMEM;
1093
1094         split_filename(filename_copy, &parent, &basename);
1095         if (!strlen(basename)) {
1096                 ret = -EINVAL;
1097                 goto exit;
1098         }
1099
1100         filename = basename;
1101         if (normalize_longname(l_filename, filename)) {
1102                 printf("FAT: illegal filename (%s)\n", filename);
1103                 ret = -EINVAL;
1104                 goto exit;
1105         }
1106
1107         itr = malloc_cache_aligned(sizeof(fat_itr));
1108         if (!itr) {
1109                 ret = -ENOMEM;
1110                 goto exit;
1111         }
1112
1113         ret = fat_itr_root(itr, &datablock);
1114         if (ret)
1115                 goto exit;
1116
1117         total_sector = datablock.total_sect;
1118
1119         ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1120         if (ret) {
1121                 printf("%s: doesn't exist (%d)\n", parent, ret);
1122                 goto exit;
1123         }
1124
1125         retdent = find_directory_entry(itr, l_filename);
1126
1127         if (retdent) {
1128                 if (fat_itr_isdir(itr)) {
1129                         ret = -EISDIR;
1130                         goto exit;
1131                 }
1132
1133                 /* A file exists */
1134                 if (pos == -1)
1135                         /* Append to the end */
1136                         pos = FAT2CPU32(retdent->size);
1137                 if (pos > retdent->size) {
1138                         /* No hole allowed */
1139                         ret = -EINVAL;
1140                         goto exit;
1141                 }
1142
1143                 /* Update file size in a directory entry */
1144                 retdent->size = cpu_to_le32(pos + size);
1145         } else {
1146                 /* Create a new file */
1147
1148                 if (itr->is_root) {
1149                         /* root dir cannot have "." or ".." */
1150                         if (!strcmp(l_filename, ".") ||
1151                             !strcmp(l_filename, "..")) {
1152                                 ret = -EINVAL;
1153                                 goto exit;
1154                         }
1155                 }
1156
1157                 if (!itr->dent) {
1158                         printf("Error: allocating new dir entry\n");
1159                         ret = -EIO;
1160                         goto exit;
1161                 }
1162
1163                 if (pos) {
1164                         /* No hole allowed */
1165                         ret = -EINVAL;
1166                         goto exit;
1167                 }
1168
1169                 memset(itr->dent, 0, sizeof(*itr->dent));
1170
1171                 /* Calculate checksum for short name */
1172                 set_name(itr->dent, filename);
1173
1174                 /* Set long name entries */
1175                 if (fill_dir_slot(itr, filename)) {
1176                         ret = -EIO;
1177                         goto exit;
1178                 }
1179
1180                 /* Set short name entry */
1181                 fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
1182
1183                 retdent = itr->dent;
1184         }
1185
1186         ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
1187         if (ret < 0) {
1188                 printf("Error: writing contents\n");
1189                 ret = -EIO;
1190                 goto exit;
1191         }
1192         debug("attempt to write 0x%llx bytes\n", *actwrite);
1193
1194         /* Flush fat buffer */
1195         ret = flush_dirty_fat_buffer(mydata);
1196         if (ret) {
1197                 printf("Error: flush fat buffer\n");
1198                 ret = -EIO;
1199                 goto exit;
1200         }
1201
1202         /* Write directory table to device */
1203         ret = flush_dir(itr);
1204         if (ret) {
1205                 printf("Error: writing directory entry\n");
1206                 ret = -EIO;
1207         }
1208
1209 exit:
1210         free(filename_copy);
1211         free(mydata->fatbuf);
1212         free(itr);
1213         return ret;
1214 }
1215
1216 int file_fat_write(const char *filename, void *buffer, loff_t offset,
1217                    loff_t maxsize, loff_t *actwrite)
1218 {
1219         return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
1220 }
1221
1222 static int fat_dir_entries(fat_itr *itr)
1223 {
1224         fat_itr *dirs;
1225         fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
1226                                                 /* for FATBUFSIZE */
1227         int count;
1228
1229         dirs = malloc_cache_aligned(sizeof(fat_itr));
1230         if (!dirs) {
1231                 debug("Error: allocating memory\n");
1232                 count = -ENOMEM;
1233                 goto exit;
1234         }
1235
1236         /* duplicate fsdata */
1237         fat_itr_child(dirs, itr);
1238         fsdata = *dirs->fsdata;
1239
1240         /* allocate local fat buffer */
1241         fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
1242         if (!fsdata.fatbuf) {
1243                 debug("Error: allocating memory\n");
1244                 count = -ENOMEM;
1245                 goto exit;
1246         }
1247         fsdata.fatbufnum = -1;
1248         dirs->fsdata = &fsdata;
1249
1250         for (count = 0; fat_itr_next(dirs); count++)
1251                 ;
1252
1253 exit:
1254         free(fsdata.fatbuf);
1255         free(dirs);
1256         return count;
1257 }
1258
1259 static int delete_dentry(fat_itr *itr)
1260 {
1261         fsdata *mydata = itr->fsdata;
1262         dir_entry *dentptr = itr->dent;
1263
1264         /* free cluster blocks */
1265         clear_fatent(mydata, START(dentptr));
1266         if (flush_dirty_fat_buffer(mydata) < 0) {
1267                 printf("Error: flush fat buffer\n");
1268                 return -EIO;
1269         }
1270
1271         /*
1272          * update a directory entry
1273          * TODO:
1274          *  - long file name support
1275          *  - find and mark the "new" first invalid entry as name[0]=0x00
1276          */
1277         memset(dentptr, 0, sizeof(*dentptr));
1278         dentptr->name[0] = 0xe5;
1279
1280         if (flush_dir(itr)) {
1281                 printf("error: writing directory entry\n");
1282                 return -EIO;
1283         }
1284
1285         return 0;
1286 }
1287
1288 int fat_unlink(const char *filename)
1289 {
1290         fsdata fsdata = { .fatbuf = NULL, };
1291         fat_itr *itr = NULL;
1292         int n_entries, ret;
1293         char *filename_copy, *dirname, *basename;
1294
1295         filename_copy = strdup(filename);
1296         if (!filename_copy) {
1297                 printf("Error: allocating memory\n");
1298                 ret = -ENOMEM;
1299                 goto exit;
1300         }
1301         split_filename(filename_copy, &dirname, &basename);
1302
1303         if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
1304                 printf("Error: cannot remove root\n");
1305                 ret = -EINVAL;
1306                 goto exit;
1307         }
1308
1309         itr = malloc_cache_aligned(sizeof(fat_itr));
1310         if (!itr) {
1311                 printf("Error: allocating memory\n");
1312                 ret = -ENOMEM;
1313                 goto exit;
1314         }
1315
1316         ret = fat_itr_root(itr, &fsdata);
1317         if (ret)
1318                 goto exit;
1319
1320         total_sector = fsdata.total_sect;
1321
1322         ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
1323         if (ret) {
1324                 printf("%s: doesn't exist (%d)\n", dirname, ret);
1325                 ret = -ENOENT;
1326                 goto exit;
1327         }
1328
1329         if (!find_directory_entry(itr, basename)) {
1330                 printf("%s: doesn't exist\n", basename);
1331                 ret = -ENOENT;
1332                 goto exit;
1333         }
1334
1335         if (fat_itr_isdir(itr)) {
1336                 n_entries = fat_dir_entries(itr);
1337                 if (n_entries < 0) {
1338                         ret = n_entries;
1339                         goto exit;
1340                 }
1341                 if (n_entries > 2) {
1342                         printf("Error: directory is not empty: %d\n",
1343                                n_entries);
1344                         ret = -EINVAL;
1345                         goto exit;
1346                 }
1347         }
1348
1349         ret = delete_dentry(itr);
1350
1351 exit:
1352         free(fsdata.fatbuf);
1353         free(itr);
1354         free(filename_copy);
1355
1356         return ret;
1357 }
1358
1359 int fat_mkdir(const char *new_dirname)
1360 {
1361         dir_entry *retdent;
1362         fsdata datablock = { .fatbuf = NULL, };
1363         fsdata *mydata = &datablock;
1364         fat_itr *itr = NULL;
1365         char *dirname_copy, *parent, *dirname;
1366         char l_dirname[VFAT_MAXLEN_BYTES];
1367         int ret = -1;
1368         loff_t actwrite;
1369         unsigned int bytesperclust;
1370         dir_entry *dotdent = NULL;
1371
1372         dirname_copy = strdup(new_dirname);
1373         if (!dirname_copy)
1374                 goto exit;
1375
1376         split_filename(dirname_copy, &parent, &dirname);
1377         if (!strlen(dirname)) {
1378                 ret = -EINVAL;
1379                 goto exit;
1380         }
1381
1382         if (normalize_longname(l_dirname, dirname)) {
1383                 printf("FAT: illegal filename (%s)\n", dirname);
1384                 ret = -EINVAL;
1385                 goto exit;
1386         }
1387
1388         itr = malloc_cache_aligned(sizeof(fat_itr));
1389         if (!itr) {
1390                 ret = -ENOMEM;
1391                 goto exit;
1392         }
1393
1394         ret = fat_itr_root(itr, &datablock);
1395         if (ret)
1396                 goto exit;
1397
1398         total_sector = datablock.total_sect;
1399
1400         ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1401         if (ret) {
1402                 printf("%s: doesn't exist (%d)\n", parent, ret);
1403                 goto exit;
1404         }
1405
1406         retdent = find_directory_entry(itr, l_dirname);
1407
1408         if (retdent) {
1409                 printf("%s: already exists\n", l_dirname);
1410                 ret = -EEXIST;
1411                 goto exit;
1412         } else {
1413                 if (itr->is_root) {
1414                         /* root dir cannot have "." or ".." */
1415                         if (!strcmp(l_dirname, ".") ||
1416                             !strcmp(l_dirname, "..")) {
1417                                 ret = -EINVAL;
1418                                 goto exit;
1419                         }
1420                 }
1421
1422                 if (!itr->dent) {
1423                         printf("Error: allocating new dir entry\n");
1424                         ret = -EIO;
1425                         goto exit;
1426                 }
1427
1428                 memset(itr->dent, 0, sizeof(*itr->dent));
1429
1430                 /* Set short name to set alias checksum field in dir_slot */
1431                 set_name(itr->dent, dirname);
1432                 fill_dir_slot(itr, dirname);
1433
1434                 /* Set attribute as archive for regular file */
1435                 fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
1436                             ATTR_DIR | ATTR_ARCH);
1437
1438                 retdent = itr->dent;
1439         }
1440
1441         /* Default entries */
1442         bytesperclust = mydata->clust_size * mydata->sect_size;
1443         dotdent = malloc_cache_aligned(bytesperclust);
1444         if (!dotdent) {
1445                 ret = -ENOMEM;
1446                 goto exit;
1447         }
1448         memset(dotdent, 0, bytesperclust);
1449
1450         memcpy(dotdent[0].name, ".       ", 8);
1451         memcpy(dotdent[0].ext, "   ", 3);
1452         dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
1453
1454         memcpy(dotdent[1].name, "..      ", 8);
1455         memcpy(dotdent[1].ext, "   ", 3);
1456         dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
1457         set_start_cluster(mydata, &dotdent[1], itr->start_clust);
1458
1459         ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1460                            bytesperclust, &actwrite);
1461         if (ret < 0) {
1462                 printf("Error: writing contents\n");
1463                 goto exit;
1464         }
1465         /* Write twice for "." */
1466         set_start_cluster(mydata, &dotdent[0], START(retdent));
1467         ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1468                            bytesperclust, &actwrite);
1469         if (ret < 0) {
1470                 printf("Error: writing contents\n");
1471                 goto exit;
1472         }
1473
1474         /* Flush fat buffer */
1475         ret = flush_dirty_fat_buffer(mydata);
1476         if (ret) {
1477                 printf("Error: flush fat buffer\n");
1478                 goto exit;
1479         }
1480
1481         /* Write directory table to device */
1482         ret = flush_dir(itr);
1483         if (ret)
1484                 printf("Error: writing directory entry\n");
1485
1486 exit:
1487         free(dirname_copy);
1488         free(mydata->fatbuf);
1489         free(itr);
1490         free(dotdent);
1491         return ret;
1492 }