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