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