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