]> git.sur5r.net Git - u-boot/blob - fs/fat/fat_write.c
Merge branch 'master' of git://git.denx.de/u-boot-i2c
[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         memcpy(s_name, filename, len);
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 (disk_write(startsect, size / mydata->sect_size, buffer) < 0) {
556                 debug("Error writing data\n");
557                 return -1;
558         }
559
560         if (size % mydata->sect_size) {
561                 __u8 tmpbuf[mydata->sect_size];
562
563                 idx = size / mydata->sect_size;
564                 buffer += idx * mydata->sect_size;
565                 memcpy(tmpbuf, buffer, size % mydata->sect_size);
566
567                 if (disk_write(startsect + idx, 1, tmpbuf) < 0) {
568                         debug("Error writing data\n");
569                         return -1;
570                 }
571
572                 return 0;
573         }
574
575         return 0;
576 }
577
578 /*
579  * Find the first empty cluster
580  */
581 static int find_empty_cluster(fsdata *mydata)
582 {
583         __u32 fat_val, entry = 3;
584
585         while (1) {
586                 fat_val = get_fatent_value(mydata, entry);
587                 if (fat_val == 0)
588                         break;
589                 entry++;
590         }
591
592         return entry;
593 }
594
595 /*
596  * Write directory entries in 'get_dentfromdir_block' to block device
597  */
598 static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
599 {
600         int dir_newclust = 0;
601
602         if (set_cluster(mydata, dir_curclust,
603                     get_dentfromdir_block,
604                     mydata->clust_size * mydata->sect_size) != 0) {
605                 printf("error: wrinting directory entry\n");
606                 return;
607         }
608         dir_newclust = find_empty_cluster(mydata);
609         set_fatent_value(mydata, dir_curclust, dir_newclust);
610         if (mydata->fatsize == 32)
611                 set_fatent_value(mydata, dir_newclust, 0xffffff8);
612         else if (mydata->fatsize == 16)
613                 set_fatent_value(mydata, dir_newclust, 0xfff8);
614
615         dir_curclust = dir_newclust;
616
617         if (flush_fat_buffer(mydata) < 0)
618                 return;
619
620         memset(get_dentfromdir_block, 0x00,
621                 mydata->clust_size * mydata->sect_size);
622
623         *dentptr = (dir_entry *) get_dentfromdir_block;
624 }
625
626 /*
627  * Set empty cluster from 'entry' to the end of a file
628  */
629 static int clear_fatent(fsdata *mydata, __u32 entry)
630 {
631         __u32 fat_val;
632
633         while (1) {
634                 fat_val = get_fatent_value(mydata, entry);
635                 if (fat_val != 0)
636                         set_fatent_value(mydata, entry, 0);
637                 else
638                         break;
639
640                 if (fat_val == 0xfffffff || fat_val == 0xffff)
641                         break;
642
643                 entry = fat_val;
644         }
645
646         /* Flush fat buffer */
647         if (flush_fat_buffer(mydata) < 0)
648                 return -1;
649
650         return 0;
651 }
652
653 /*
654  * Write at most 'maxsize' bytes from 'buffer' into
655  * the file associated with 'dentptr'
656  * Return the number of bytes read or -1 on fatal errors.
657  */
658 static int
659 set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
660               unsigned long maxsize)
661 {
662         unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
663         unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
664         __u32 curclust = START(dentptr);
665         __u32 endclust = 0, newclust = 0;
666         unsigned long actsize;
667
668         debug("Filesize: %ld bytes\n", filesize);
669
670         if (maxsize > 0 && filesize > maxsize)
671                 filesize = maxsize;
672
673         debug("%ld bytes\n", filesize);
674
675         actsize = bytesperclust;
676         endclust = curclust;
677         do {
678                 /* search for consecutive clusters */
679                 while (actsize < filesize) {
680                         newclust = determine_fatent(mydata, endclust);
681
682                         if ((newclust - 1) != endclust)
683                                 goto getit;
684
685                         if (CHECK_CLUST(newclust, mydata->fatsize)) {
686                                 debug("curclust: 0x%x\n", newclust);
687                                 debug("Invalid FAT entry\n");
688                                 return gotsize;
689                         }
690                         endclust = newclust;
691                         actsize += bytesperclust;
692                 }
693                 /* actsize >= file size */
694                 actsize -= bytesperclust;
695                 /* set remaining clusters */
696                 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
697                         debug("error: writing cluster\n");
698                         return -1;
699                 }
700
701                 /* set remaining bytes */
702                 gotsize += (int)actsize;
703                 filesize -= actsize;
704                 buffer += actsize;
705                 actsize = filesize;
706
707                 if (set_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
708                         debug("error: writing cluster\n");
709                         return -1;
710                 }
711                 gotsize += actsize;
712
713                 /* Mark end of file in FAT */
714                 if (mydata->fatsize == 16)
715                         newclust = 0xffff;
716                 else if (mydata->fatsize == 32)
717                         newclust = 0xfffffff;
718                 set_fatent_value(mydata, endclust, newclust);
719
720                 return gotsize;
721 getit:
722                 if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
723                         debug("error: writing cluster\n");
724                         return -1;
725                 }
726                 gotsize += (int)actsize;
727                 filesize -= actsize;
728                 buffer += actsize;
729
730                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
731                         debug("curclust: 0x%x\n", curclust);
732                         debug("Invalid FAT entry\n");
733                         return gotsize;
734                 }
735                 actsize = bytesperclust;
736                 curclust = endclust = newclust;
737         } while (1);
738 }
739
740 /*
741  * Fill dir_entry
742  */
743 static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
744         const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
745 {
746         if (mydata->fatsize == 32)
747                 dentptr->starthi =
748                         cpu_to_le16((start_cluster & 0xffff0000) >> 16);
749         dentptr->start = cpu_to_le16(start_cluster & 0xffff);
750         dentptr->size = cpu_to_le32(size);
751
752         dentptr->attr = attr;
753
754         set_name(dentptr, filename);
755 }
756
757 /*
758  * Check whether adding a file makes the file system to
759  * exceed the size of the block device
760  * Return -1 when overflow occurs, otherwise return 0
761  */
762 static int check_overflow(fsdata *mydata, __u32 clustnum, unsigned long size)
763 {
764         __u32 startsect, sect_num;
765
766         if (clustnum > 0) {
767                 startsect = mydata->data_begin +
768                                 clustnum * mydata->clust_size;
769         } else {
770                 startsect = mydata->rootdir_sect;
771         }
772
773         sect_num = size / mydata->sect_size;
774         if (size % mydata->sect_size)
775                 sect_num++;
776
777         if (startsect + sect_num > cur_part_info.start + total_sector)
778                 return -1;
779
780         return 0;
781 }
782
783 /*
784  * Check if adding several entries exceed one cluster boundary
785  */
786 static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
787 {
788         int cur_position;
789
790         cur_position = (__u8 *)dentptr - get_dentfromdir_block;
791
792         if (cur_position >= mydata->clust_size * mydata->sect_size)
793                 return 1;
794         else
795                 return 0;
796 }
797
798 static dir_entry *empty_dentptr;
799 /*
800  * Find a directory entry based on filename or start cluster number
801  * If the directory entry is not found,
802  * the new position for writing a directory entry will be returned
803  */
804 static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
805         char *filename, dir_entry *retdent, __u32 start)
806 {
807         __u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size;
808
809         debug("get_dentfromdir: %s\n", filename);
810
811         while (1) {
812                 dir_entry *dentptr;
813
814                 int i;
815
816                 if (get_cluster(mydata, curclust, get_dentfromdir_block,
817                             mydata->clust_size * mydata->sect_size) != 0) {
818                         printf("Error: reading directory block\n");
819                         return NULL;
820                 }
821
822                 dentptr = (dir_entry *)get_dentfromdir_block;
823
824                 dir_curclust = curclust;
825
826                 for (i = 0; i < DIRENTSPERCLUST; i++) {
827                         char s_name[14], l_name[VFAT_MAXLEN_BYTES];
828
829                         l_name[0] = '\0';
830                         if (dentptr->name[0] == DELETED_FLAG) {
831                                 dentptr++;
832                                 if (is_next_clust(mydata, dentptr))
833                                         break;
834                                 continue;
835                         }
836                         if ((dentptr->attr & ATTR_VOLUME)) {
837                                 if (vfat_enabled &&
838                                     (dentptr->attr & ATTR_VFAT) &&
839                                     (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
840                                         get_long_file_name(mydata, curclust,
841                                                      get_dentfromdir_block,
842                                                      &dentptr, l_name);
843                                         debug("vfatname: |%s|\n", l_name);
844                                 } else {
845                                         /* Volume label or VFAT entry */
846                                         dentptr++;
847                                         if (is_next_clust(mydata, dentptr))
848                                                 break;
849                                         continue;
850                                 }
851                         }
852                         if (dentptr->name[0] == 0) {
853                                 debug("Dentname == NULL - %d\n", i);
854                                 empty_dentptr = dentptr;
855                                 return NULL;
856                         }
857
858                         get_name(dentptr, s_name);
859
860                         if (strcmp(filename, s_name)
861                             && strcmp(filename, l_name)) {
862                                 debug("Mismatch: |%s|%s|\n",
863                                         s_name, l_name);
864                                 dentptr++;
865                                 if (is_next_clust(mydata, dentptr))
866                                         break;
867                                 continue;
868                         }
869
870                         memcpy(retdent, dentptr, sizeof(dir_entry));
871
872                         debug("DentName: %s", s_name);
873                         debug(", start: 0x%x", START(dentptr));
874                         debug(", size:  0x%x %s\n",
875                               FAT2CPU32(dentptr->size),
876                               (dentptr->attr & ATTR_DIR) ?
877                               "(DIR)" : "");
878
879                         return dentptr;
880                 }
881
882                 curclust = get_fatent_value(mydata, dir_curclust);
883                 if ((curclust >= 0xffffff8) || (curclust >= 0xfff8)) {
884                         empty_dentptr = dentptr;
885                         return NULL;
886                 }
887                 if (CHECK_CLUST(curclust, mydata->fatsize)) {
888                         debug("curclust: 0x%x\n", curclust);
889                         debug("Invalid FAT entry\n");
890                         return NULL;
891                 }
892         }
893
894         return NULL;
895 }
896
897 static int do_fat_write(const char *filename, void *buffer,
898         unsigned long size)
899 {
900         dir_entry *dentptr, *retdent;
901         __u32 startsect;
902         __u32 start_cluster;
903         boot_sector bs;
904         volume_info volinfo;
905         fsdata datablock;
906         fsdata *mydata = &datablock;
907         int cursect;
908         int ret = -1, name_len;
909         char l_filename[VFAT_MAXLEN_BYTES];
910         int write_size = size;
911
912         dir_curclust = 0;
913
914         if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
915                 debug("error: reading boot sector\n");
916                 return -1;
917         }
918
919         total_sector = bs.total_sect;
920         if (total_sector == 0)
921                 total_sector = cur_part_info.size;
922
923         if (mydata->fatsize == 32)
924                 mydata->fatlength = bs.fat32_length;
925         else
926                 mydata->fatlength = bs.fat_length;
927
928         mydata->fat_sect = bs.reserved;
929
930         cursect = mydata->rootdir_sect
931                 = mydata->fat_sect + mydata->fatlength * bs.fats;
932         num_of_fats = bs.fats;
933
934         mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
935         mydata->clust_size = bs.cluster_size;
936
937         if (mydata->fatsize == 32) {
938                 mydata->data_begin = mydata->rootdir_sect -
939                                         (mydata->clust_size * 2);
940         } else {
941                 int rootdir_size;
942
943                 rootdir_size = ((bs.dir_entries[1]  * (int)256 +
944                                  bs.dir_entries[0]) *
945                                  sizeof(dir_entry)) /
946                                  mydata->sect_size;
947                 mydata->data_begin = mydata->rootdir_sect +
948                                         rootdir_size -
949                                         (mydata->clust_size * 2);
950         }
951
952         mydata->fatbufnum = -1;
953         mydata->fatbuf = malloc(FATBUFSIZE);
954         if (mydata->fatbuf == NULL) {
955                 debug("Error: allocating memory\n");
956                 return -1;
957         }
958
959         if (disk_read(cursect,
960                 (mydata->fatsize == 32) ?
961                 (mydata->clust_size) :
962                 PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
963                 debug("Error: reading rootdir block\n");
964                 goto exit;
965         }
966         dentptr = (dir_entry *) do_fat_read_at_block;
967
968         name_len = strlen(filename);
969         if (name_len >= VFAT_MAXLEN_BYTES)
970                 name_len = VFAT_MAXLEN_BYTES - 1;
971
972         memcpy(l_filename, filename, name_len);
973         l_filename[name_len] = 0; /* terminate the string */
974         downcase(l_filename);
975
976         startsect = mydata->rootdir_sect;
977         retdent = find_directory_entry(mydata, startsect,
978                                 l_filename, dentptr, 0);
979         if (retdent) {
980                 /* Update file size and start_cluster in a directory entry */
981                 retdent->size = cpu_to_le32(size);
982                 start_cluster = FAT2CPU16(retdent->start);
983                 if (mydata->fatsize == 32)
984                         start_cluster |=
985                                 (FAT2CPU16(retdent->starthi) << 16);
986
987                 ret = check_overflow(mydata, start_cluster, size);
988                 if (ret) {
989                         printf("Error: %ld overflow\n", size);
990                         goto exit;
991                 }
992
993                 ret = clear_fatent(mydata, start_cluster);
994                 if (ret) {
995                         printf("Error: clearing FAT entries\n");
996                         goto exit;
997                 }
998
999                 ret = set_contents(mydata, retdent, buffer, size);
1000                 if (ret < 0) {
1001                         printf("Error: writing contents\n");
1002                         goto exit;
1003                 }
1004                 write_size = ret;
1005                 debug("attempt to write 0x%x bytes\n", write_size);
1006
1007                 /* Flush fat buffer */
1008                 ret = flush_fat_buffer(mydata);
1009                 if (ret) {
1010                         printf("Error: flush fat buffer\n");
1011                         goto exit;
1012                 }
1013
1014                 /* Write directory table to device */
1015                 ret = set_cluster(mydata, dir_curclust,
1016                             get_dentfromdir_block,
1017                             mydata->clust_size * mydata->sect_size);
1018                 if (ret) {
1019                         printf("Error: writing directory entry\n");
1020                         goto exit;
1021                 }
1022         } else {
1023                 /* Set short name to set alias checksum field in dir_slot */
1024                 set_name(empty_dentptr, filename);
1025                 fill_dir_slot(mydata, &empty_dentptr, filename);
1026
1027                 ret = start_cluster = find_empty_cluster(mydata);
1028                 if (ret < 0) {
1029                         printf("Error: finding empty cluster\n");
1030                         goto exit;
1031                 }
1032
1033                 ret = check_overflow(mydata, start_cluster, size);
1034                 if (ret) {
1035                         printf("Error: %ld overflow\n", size);
1036                         goto exit;
1037                 }
1038
1039                 /* Set attribute as archieve for regular file */
1040                 fill_dentry(mydata, empty_dentptr, filename,
1041                         start_cluster, size, 0x20);
1042
1043                 ret = set_contents(mydata, empty_dentptr, buffer, size);
1044                 if (ret < 0) {
1045                         printf("Error: writing contents\n");
1046                         goto exit;
1047                 }
1048                 write_size = ret;
1049                 debug("attempt to write 0x%x bytes\n", write_size);
1050
1051                 /* Flush fat buffer */
1052                 ret = flush_fat_buffer(mydata);
1053                 if (ret) {
1054                         printf("Error: flush fat buffer\n");
1055                         goto exit;
1056                 }
1057
1058                 /* Write directory table to device */
1059                 ret = set_cluster(mydata, dir_curclust,
1060                             get_dentfromdir_block,
1061                             mydata->clust_size * mydata->sect_size);
1062                 if (ret) {
1063                         printf("Error: writing directory entry\n");
1064                         goto exit;
1065                 }
1066         }
1067
1068 exit:
1069         free(mydata->fatbuf);
1070         return ret < 0 ? ret : write_size;
1071 }
1072
1073 int file_fat_write(const char *filename, void *buffer, unsigned long maxsize)
1074 {
1075         printf("writing %s\n", filename);
1076         return do_fat_write(filename, buffer, maxsize);
1077 }