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