xref: /openbmc/u-boot/fs/fat/fat_write.c (revision 425faf74)
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 }
1078