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