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