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