xref: /openbmc/u-boot/fs/fat/fat_write.c (revision e11ef3d2)
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * fat_write.c
4  *
5  * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
6  */
7 
8 #include <common.h>
9 #include <command.h>
10 #include <config.h>
11 #include <fat.h>
12 #include <asm/byteorder.h>
13 #include <part.h>
14 #include <linux/ctype.h>
15 #include <div64.h>
16 #include <linux/math64.h>
17 #include "fat.c"
18 
19 static void uppercase(char *str, int len)
20 {
21 	int i;
22 
23 	for (i = 0; i < len; i++) {
24 		*str = toupper(*str);
25 		str++;
26 	}
27 }
28 
29 static int total_sector;
30 static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
31 {
32 	ulong ret;
33 
34 	if (!cur_dev)
35 		return -1;
36 
37 	if (cur_part_info.start + block + nr_blocks >
38 		cur_part_info.start + total_sector) {
39 		printf("error: overflow occurs\n");
40 		return -1;
41 	}
42 
43 	ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
44 	if (nr_blocks && ret == 0)
45 		return -1;
46 
47 	return ret;
48 }
49 
50 /*
51  * Set short name in directory entry
52  */
53 static void set_name(dir_entry *dirent, const char *filename)
54 {
55 	char s_name[VFAT_MAXLEN_BYTES];
56 	char *period;
57 	int period_location, len, i, ext_num;
58 
59 	if (filename == NULL)
60 		return;
61 
62 	len = strlen(filename);
63 	if (len == 0)
64 		return;
65 
66 	strcpy(s_name, filename);
67 	uppercase(s_name, len);
68 
69 	period = strchr(s_name, '.');
70 	if (period == NULL) {
71 		period_location = len;
72 		ext_num = 0;
73 	} else {
74 		period_location = period - s_name;
75 		ext_num = len - period_location - 1;
76 	}
77 
78 	/* Pad spaces when the length of file name is shorter than eight */
79 	if (period_location < 8) {
80 		memcpy(dirent->name, s_name, period_location);
81 		for (i = period_location; i < 8; i++)
82 			dirent->name[i] = ' ';
83 	} else if (period_location == 8) {
84 		memcpy(dirent->name, s_name, period_location);
85 	} else {
86 		memcpy(dirent->name, s_name, 6);
87 		dirent->name[6] = '~';
88 		dirent->name[7] = '1';
89 	}
90 
91 	if (ext_num < 3) {
92 		memcpy(dirent->ext, s_name + period_location + 1, ext_num);
93 		for (i = ext_num; i < 3; i++)
94 			dirent->ext[i] = ' ';
95 	} else
96 		memcpy(dirent->ext, s_name + period_location + 1, 3);
97 
98 	debug("name : %s\n", dirent->name);
99 	debug("ext : %s\n", dirent->ext);
100 }
101 
102 /*
103  * Write fat buffer into block device
104  */
105 static int flush_dirty_fat_buffer(fsdata *mydata)
106 {
107 	int getsize = FATBUFBLOCKS;
108 	__u32 fatlength = mydata->fatlength;
109 	__u8 *bufptr = mydata->fatbuf;
110 	__u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
111 
112 	debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
113 	      (int)mydata->fat_dirty);
114 
115 	if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
116 		return 0;
117 
118 	/* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
119 	if (startblock + getsize > fatlength)
120 		getsize = fatlength - startblock;
121 
122 	startblock += mydata->fat_sect;
123 
124 	/* Write FAT buf */
125 	if (disk_write(startblock, getsize, bufptr) < 0) {
126 		debug("error: writing FAT blocks\n");
127 		return -1;
128 	}
129 
130 	if (mydata->fats == 2) {
131 		/* Update corresponding second FAT blocks */
132 		startblock += mydata->fatlength;
133 		if (disk_write(startblock, getsize, bufptr) < 0) {
134 			debug("error: writing second FAT blocks\n");
135 			return -1;
136 		}
137 	}
138 	mydata->fat_dirty = 0;
139 
140 	return 0;
141 }
142 
143 /*
144  * Set the file name information from 'name' into 'slotptr',
145  */
146 static int str2slot(dir_slot *slotptr, const char *name, int *idx)
147 {
148 	int j, end_idx = 0;
149 
150 	for (j = 0; j <= 8; j += 2) {
151 		if (name[*idx] == 0x00) {
152 			slotptr->name0_4[j] = 0;
153 			slotptr->name0_4[j + 1] = 0;
154 			end_idx++;
155 			goto name0_4;
156 		}
157 		slotptr->name0_4[j] = name[*idx];
158 		(*idx)++;
159 		end_idx++;
160 	}
161 	for (j = 0; j <= 10; j += 2) {
162 		if (name[*idx] == 0x00) {
163 			slotptr->name5_10[j] = 0;
164 			slotptr->name5_10[j + 1] = 0;
165 			end_idx++;
166 			goto name5_10;
167 		}
168 		slotptr->name5_10[j] = name[*idx];
169 		(*idx)++;
170 		end_idx++;
171 	}
172 	for (j = 0; j <= 2; j += 2) {
173 		if (name[*idx] == 0x00) {
174 			slotptr->name11_12[j] = 0;
175 			slotptr->name11_12[j + 1] = 0;
176 			end_idx++;
177 			goto name11_12;
178 		}
179 		slotptr->name11_12[j] = name[*idx];
180 		(*idx)++;
181 		end_idx++;
182 	}
183 
184 	if (name[*idx] == 0x00)
185 		return 1;
186 
187 	return 0;
188 /* Not used characters are filled with 0xff 0xff */
189 name0_4:
190 	for (; end_idx < 5; end_idx++) {
191 		slotptr->name0_4[end_idx * 2] = 0xff;
192 		slotptr->name0_4[end_idx * 2 + 1] = 0xff;
193 	}
194 	end_idx = 5;
195 name5_10:
196 	end_idx -= 5;
197 	for (; end_idx < 6; end_idx++) {
198 		slotptr->name5_10[end_idx * 2] = 0xff;
199 		slotptr->name5_10[end_idx * 2 + 1] = 0xff;
200 	}
201 	end_idx = 11;
202 name11_12:
203 	end_idx -= 11;
204 	for (; end_idx < 2; end_idx++) {
205 		slotptr->name11_12[end_idx * 2] = 0xff;
206 		slotptr->name11_12[end_idx * 2 + 1] = 0xff;
207 	}
208 
209 	return 1;
210 }
211 
212 static int flush_dir_table(fat_itr *itr);
213 
214 /*
215  * Fill dir_slot entries with appropriate name, id, and attr
216  * 'itr' will point to a next entry
217  */
218 static int
219 fill_dir_slot(fat_itr *itr, const char *l_name)
220 {
221 	__u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
222 	dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
223 	__u8 counter = 0, checksum;
224 	int idx = 0, ret;
225 
226 	/* Get short file name checksum value */
227 	checksum = mkcksum(itr->dent->name, itr->dent->ext);
228 
229 	do {
230 		memset(slotptr, 0x00, sizeof(dir_slot));
231 		ret = str2slot(slotptr, l_name, &idx);
232 		slotptr->id = ++counter;
233 		slotptr->attr = ATTR_VFAT;
234 		slotptr->alias_checksum = checksum;
235 		slotptr++;
236 	} while (ret == 0);
237 
238 	slotptr--;
239 	slotptr->id |= LAST_LONG_ENTRY_MASK;
240 
241 	while (counter >= 1) {
242 		memcpy(itr->dent, slotptr, sizeof(dir_slot));
243 		slotptr--;
244 		counter--;
245 		if (!fat_itr_next(itr))
246 			if (!itr->dent && !itr->is_root && flush_dir_table(itr))
247 				return -1;
248 	}
249 
250 	if (!itr->dent && !itr->is_root)
251 		/*
252 		 * don't care return value here because we have already
253 		 * finished completing an entry with name, only ending up
254 		 * no more entry left
255 		 */
256 		flush_dir_table(itr);
257 
258 	return 0;
259 }
260 
261 /*
262  * Set the entry at index 'entry' in a FAT (12/16/32) table.
263  */
264 static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
265 {
266 	__u32 bufnum, offset, off16;
267 	__u16 val1, val2;
268 
269 	switch (mydata->fatsize) {
270 	case 32:
271 		bufnum = entry / FAT32BUFSIZE;
272 		offset = entry - bufnum * FAT32BUFSIZE;
273 		break;
274 	case 16:
275 		bufnum = entry / FAT16BUFSIZE;
276 		offset = entry - bufnum * FAT16BUFSIZE;
277 		break;
278 	case 12:
279 		bufnum = entry / FAT12BUFSIZE;
280 		offset = entry - bufnum * FAT12BUFSIZE;
281 		break;
282 	default:
283 		/* Unsupported FAT size */
284 		return -1;
285 	}
286 
287 	/* Read a new block of FAT entries into the cache. */
288 	if (bufnum != mydata->fatbufnum) {
289 		int getsize = FATBUFBLOCKS;
290 		__u8 *bufptr = mydata->fatbuf;
291 		__u32 fatlength = mydata->fatlength;
292 		__u32 startblock = bufnum * FATBUFBLOCKS;
293 
294 		/* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
295 		if (startblock + getsize > fatlength)
296 			getsize = fatlength - startblock;
297 
298 		if (flush_dirty_fat_buffer(mydata) < 0)
299 			return -1;
300 
301 		startblock += mydata->fat_sect;
302 
303 		if (disk_read(startblock, getsize, bufptr) < 0) {
304 			debug("Error reading FAT blocks\n");
305 			return -1;
306 		}
307 		mydata->fatbufnum = bufnum;
308 	}
309 
310 	/* Mark as dirty */
311 	mydata->fat_dirty = 1;
312 
313 	/* Set the actual entry */
314 	switch (mydata->fatsize) {
315 	case 32:
316 		((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
317 		break;
318 	case 16:
319 		((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
320 		break;
321 	case 12:
322 		off16 = (offset * 3) / 4;
323 
324 		switch (offset & 0x3) {
325 		case 0:
326 			val1 = cpu_to_le16(entry_value) & 0xfff;
327 			((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
328 			((__u16 *)mydata->fatbuf)[off16] |= val1;
329 			break;
330 		case 1:
331 			val1 = cpu_to_le16(entry_value) & 0xf;
332 			val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
333 
334 			((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
335 			((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
336 
337 			((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
338 			((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
339 			break;
340 		case 2:
341 			val1 = cpu_to_le16(entry_value) & 0xff;
342 			val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
343 
344 			((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
345 			((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
346 
347 			((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
348 			((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
349 			break;
350 		case 3:
351 			val1 = cpu_to_le16(entry_value) & 0xfff;
352 			((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
353 			((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
354 			break;
355 		default:
356 			break;
357 		}
358 
359 		break;
360 	default:
361 		return -1;
362 	}
363 
364 	return 0;
365 }
366 
367 /*
368  * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
369  * and link it to 'entry'. EOC marker is not set on returned entry.
370  */
371 static __u32 determine_fatent(fsdata *mydata, __u32 entry)
372 {
373 	__u32 next_fat, next_entry = entry + 1;
374 
375 	while (1) {
376 		next_fat = get_fatent(mydata, next_entry);
377 		if (next_fat == 0) {
378 			/* found free entry, link to entry */
379 			set_fatent_value(mydata, entry, next_entry);
380 			break;
381 		}
382 		next_entry++;
383 	}
384 	debug("FAT%d: entry: %08x, entry_value: %04x\n",
385 	       mydata->fatsize, entry, next_entry);
386 
387 	return next_entry;
388 }
389 
390 /**
391  * set_cluster() - write data to cluster
392  *
393  * Write 'size' bytes from 'buffer' into the specified cluster.
394  *
395  * @mydata:	data to be written
396  * @clustnum:	cluster to be written to
397  * @buffer:	data to be written
398  * @size:	bytes to be written (but not more than the size of a cluster)
399  * Return:	0 on success, -1 otherwise
400  */
401 static int
402 set_cluster(fsdata *mydata, u32 clustnum, u8 *buffer, u32 size)
403 {
404 	u32 idx = 0;
405 	u32 startsect;
406 	int ret;
407 
408 	if (clustnum > 0)
409 		startsect = clust_to_sect(mydata, clustnum);
410 	else
411 		startsect = mydata->rootdir_sect;
412 
413 	debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
414 
415 	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
416 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
417 
418 		debug("FAT: Misaligned buffer address (%p)\n", buffer);
419 
420 		while (size >= mydata->sect_size) {
421 			memcpy(tmpbuf, buffer, mydata->sect_size);
422 			ret = disk_write(startsect++, 1, tmpbuf);
423 			if (ret != 1) {
424 				debug("Error writing data (got %d)\n", ret);
425 				return -1;
426 			}
427 
428 			buffer += mydata->sect_size;
429 			size -= mydata->sect_size;
430 		}
431 	} else if (size >= mydata->sect_size) {
432 		idx = size / mydata->sect_size;
433 		ret = disk_write(startsect, idx, buffer);
434 		if (ret != idx) {
435 			debug("Error writing data (got %d)\n", ret);
436 			return -1;
437 		}
438 
439 		startsect += idx;
440 		idx *= mydata->sect_size;
441 		buffer += idx;
442 		size -= idx;
443 	}
444 
445 	if (size) {
446 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
447 		/* Do not leak content of stack */
448 		memset(tmpbuf, 0, mydata->sect_size);
449 		memcpy(tmpbuf, buffer, size);
450 		ret = disk_write(startsect, 1, tmpbuf);
451 		if (ret != 1) {
452 			debug("Error writing data (got %d)\n", ret);
453 			return -1;
454 		}
455 	}
456 
457 	return 0;
458 }
459 
460 static __u8 tmpbuf_cluster[MAX_CLUSTSIZE] __aligned(ARCH_DMA_MINALIGN);
461 
462 /*
463  * Read and modify data on existing and consecutive cluster blocks
464  */
465 static int
466 get_set_cluster(fsdata *mydata, __u32 clustnum, loff_t pos, __u8 *buffer,
467 		loff_t size, loff_t *gotsize)
468 {
469 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
470 	__u32 startsect;
471 	loff_t wsize;
472 	int clustcount, i, ret;
473 
474 	*gotsize = 0;
475 	if (!size)
476 		return 0;
477 
478 	assert(pos < bytesperclust);
479 	startsect = clust_to_sect(mydata, clustnum);
480 
481 	debug("clustnum: %d, startsect: %d, pos: %lld\n",
482 	      clustnum, startsect, pos);
483 
484 	/* partial write at beginning */
485 	if (pos) {
486 		wsize = min(bytesperclust - pos, size);
487 		ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
488 		if (ret != mydata->clust_size) {
489 			debug("Error reading data (got %d)\n", ret);
490 			return -1;
491 		}
492 
493 		memcpy(tmpbuf_cluster + pos, buffer, wsize);
494 		ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
495 		if (ret != mydata->clust_size) {
496 			debug("Error writing data (got %d)\n", ret);
497 			return -1;
498 		}
499 
500 		size -= wsize;
501 		buffer += wsize;
502 		*gotsize += wsize;
503 
504 		startsect += mydata->clust_size;
505 
506 		if (!size)
507 			return 0;
508 	}
509 
510 	/* full-cluster write */
511 	if (size >= bytesperclust) {
512 		clustcount = lldiv(size, bytesperclust);
513 
514 		if (!((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1))) {
515 			wsize = clustcount * bytesperclust;
516 			ret = disk_write(startsect,
517 					 clustcount * mydata->clust_size,
518 					 buffer);
519 			if (ret != clustcount * mydata->clust_size) {
520 				debug("Error writing data (got %d)\n", ret);
521 				return -1;
522 			}
523 
524 			size -= wsize;
525 			buffer += wsize;
526 			*gotsize += wsize;
527 
528 			startsect += clustcount * mydata->clust_size;
529 		} else {
530 			for (i = 0; i < clustcount; i++) {
531 				memcpy(tmpbuf_cluster, buffer, bytesperclust);
532 				ret = disk_write(startsect,
533 						 mydata->clust_size,
534 						 tmpbuf_cluster);
535 				if (ret != mydata->clust_size) {
536 					debug("Error writing data (got %d)\n",
537 					      ret);
538 					return -1;
539 				}
540 
541 				size -= bytesperclust;
542 				buffer += bytesperclust;
543 				*gotsize += bytesperclust;
544 
545 				startsect += mydata->clust_size;
546 			}
547 		}
548 	}
549 
550 	/* partial write at end */
551 	if (size) {
552 		wsize = size;
553 		ret = disk_read(startsect, mydata->clust_size, tmpbuf_cluster);
554 		if (ret != mydata->clust_size) {
555 			debug("Error reading data (got %d)\n", ret);
556 			return -1;
557 		}
558 		memcpy(tmpbuf_cluster, buffer, wsize);
559 		ret = disk_write(startsect, mydata->clust_size, tmpbuf_cluster);
560 		if (ret != mydata->clust_size) {
561 			debug("Error writing data (got %d)\n", ret);
562 			return -1;
563 		}
564 
565 		size -= wsize;
566 		buffer += wsize;
567 		*gotsize += wsize;
568 	}
569 
570 	assert(!size);
571 
572 	return 0;
573 }
574 
575 /*
576  * Find the first empty cluster
577  */
578 static int find_empty_cluster(fsdata *mydata)
579 {
580 	__u32 fat_val, entry = 3;
581 
582 	while (1) {
583 		fat_val = get_fatent(mydata, entry);
584 		if (fat_val == 0)
585 			break;
586 		entry++;
587 	}
588 
589 	return entry;
590 }
591 
592 /*
593  * Write directory entries in itr's buffer to block device
594  */
595 static int flush_dir_table(fat_itr *itr)
596 {
597 	fsdata *mydata = itr->fsdata;
598 	int dir_newclust = 0;
599 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
600 
601 	if (set_cluster(mydata, itr->clust, itr->block, bytesperclust) != 0) {
602 		printf("error: writing directory entry\n");
603 		return -1;
604 	}
605 	dir_newclust = find_empty_cluster(mydata);
606 	set_fatent_value(mydata, itr->clust, dir_newclust);
607 	if (mydata->fatsize == 32)
608 		set_fatent_value(mydata, dir_newclust, 0xffffff8);
609 	else if (mydata->fatsize == 16)
610 		set_fatent_value(mydata, dir_newclust, 0xfff8);
611 	else if (mydata->fatsize == 12)
612 		set_fatent_value(mydata, dir_newclust, 0xff8);
613 
614 	itr->clust = dir_newclust;
615 	itr->next_clust = dir_newclust;
616 
617 	if (flush_dirty_fat_buffer(mydata) < 0)
618 		return -1;
619 
620 	memset(itr->block, 0x00, bytesperclust);
621 
622 	itr->dent = (dir_entry *)itr->block;
623 	itr->last_cluster = 1;
624 	itr->remaining = bytesperclust / sizeof(dir_entry) - 1;
625 
626 	return 0;
627 }
628 
629 /*
630  * Set empty cluster from 'entry' to the end of a file
631  */
632 static int clear_fatent(fsdata *mydata, __u32 entry)
633 {
634 	__u32 fat_val;
635 
636 	while (!CHECK_CLUST(entry, mydata->fatsize)) {
637 		fat_val = get_fatent(mydata, entry);
638 		if (fat_val != 0)
639 			set_fatent_value(mydata, entry, 0);
640 		else
641 			break;
642 
643 		entry = fat_val;
644 	}
645 
646 	/* Flush fat buffer */
647 	if (flush_dirty_fat_buffer(mydata) < 0)
648 		return -1;
649 
650 	return 0;
651 }
652 
653 /*
654  * Set start cluster in directory entry
655  */
656 static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
657 			      __u32 start_cluster)
658 {
659 	if (mydata->fatsize == 32)
660 		dentptr->starthi =
661 			cpu_to_le16((start_cluster & 0xffff0000) >> 16);
662 	dentptr->start = cpu_to_le16(start_cluster & 0xffff);
663 }
664 
665 /*
666  * Check whether adding a file makes the file system to
667  * exceed the size of the block device
668  * Return -1 when overflow occurs, otherwise return 0
669  */
670 static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
671 {
672 	__u32 startsect, sect_num, offset;
673 
674 	if (clustnum > 0)
675 		startsect = clust_to_sect(mydata, clustnum);
676 	else
677 		startsect = mydata->rootdir_sect;
678 
679 	sect_num = div_u64_rem(size, mydata->sect_size, &offset);
680 
681 	if (offset != 0)
682 		sect_num++;
683 
684 	if (startsect + sect_num > total_sector)
685 		return -1;
686 	return 0;
687 }
688 
689 /*
690  * Write at most 'maxsize' bytes from 'buffer' into
691  * the file associated with 'dentptr'
692  * Update the number of bytes written in *gotsize and return 0
693  * or return -1 on fatal errors.
694  */
695 static int
696 set_contents(fsdata *mydata, dir_entry *dentptr, loff_t pos, __u8 *buffer,
697 	     loff_t maxsize, loff_t *gotsize)
698 {
699 	loff_t filesize;
700 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
701 	__u32 curclust = START(dentptr);
702 	__u32 endclust = 0, newclust = 0;
703 	loff_t cur_pos, offset, actsize, wsize;
704 
705 	*gotsize = 0;
706 	filesize = pos + maxsize;
707 
708 	debug("%llu bytes\n", filesize);
709 
710 	if (!filesize) {
711 		if (!curclust)
712 			return 0;
713 		if (!CHECK_CLUST(curclust, mydata->fatsize) ||
714 		    IS_LAST_CLUST(curclust, mydata->fatsize)) {
715 			clear_fatent(mydata, curclust);
716 			set_start_cluster(mydata, dentptr, 0);
717 			return 0;
718 		}
719 		debug("curclust: 0x%x\n", curclust);
720 		debug("Invalid FAT entry\n");
721 		return -1;
722 	}
723 
724 	if (!curclust) {
725 		assert(pos == 0);
726 		goto set_clusters;
727 	}
728 
729 	/* go to cluster at pos */
730 	cur_pos = bytesperclust;
731 	while (1) {
732 		if (pos <= cur_pos)
733 			break;
734 		if (IS_LAST_CLUST(curclust, mydata->fatsize))
735 			break;
736 
737 		newclust = get_fatent(mydata, curclust);
738 		if (!IS_LAST_CLUST(newclust, mydata->fatsize) &&
739 		    CHECK_CLUST(newclust, mydata->fatsize)) {
740 			debug("curclust: 0x%x\n", curclust);
741 			debug("Invalid FAT entry\n");
742 			return -1;
743 		}
744 
745 		cur_pos += bytesperclust;
746 		curclust = newclust;
747 	}
748 	if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
749 		assert(pos == cur_pos);
750 		goto set_clusters;
751 	}
752 
753 	assert(pos < cur_pos);
754 	cur_pos -= bytesperclust;
755 
756 	/* overwrite */
757 	assert(IS_LAST_CLUST(curclust, mydata->fatsize) ||
758 	       !CHECK_CLUST(curclust, mydata->fatsize));
759 
760 	while (1) {
761 		/* search for allocated consecutive clusters */
762 		actsize = bytesperclust;
763 		endclust = curclust;
764 		while (1) {
765 			if (filesize <= (cur_pos + actsize))
766 				break;
767 
768 			newclust = get_fatent(mydata, endclust);
769 
770 			if (IS_LAST_CLUST(newclust, mydata->fatsize))
771 				break;
772 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
773 				debug("curclust: 0x%x\n", curclust);
774 				debug("Invalid FAT entry\n");
775 				return -1;
776 			}
777 
778 			actsize += bytesperclust;
779 			endclust = newclust;
780 		}
781 
782 		/* overwrite to <curclust..endclust> */
783 		if (pos < cur_pos)
784 			offset = 0;
785 		else
786 			offset = pos - cur_pos;
787 		wsize = min(cur_pos + actsize, filesize) - pos;
788 		if (get_set_cluster(mydata, curclust, offset,
789 				    buffer, wsize, &actsize)) {
790 			printf("Error get-and-setting cluster\n");
791 			return -1;
792 		}
793 		buffer += wsize;
794 		*gotsize += wsize;
795 		cur_pos += offset + wsize;
796 
797 		if (filesize <= cur_pos)
798 			break;
799 
800 		/* CHECK: newclust = get_fatent(mydata, endclust); */
801 
802 		if (IS_LAST_CLUST(newclust, mydata->fatsize))
803 			/* no more clusters */
804 			break;
805 
806 		curclust = newclust;
807 	}
808 
809 	if (filesize <= cur_pos) {
810 		/* no more write */
811 		newclust = get_fatent(mydata, endclust);
812 		if (!IS_LAST_CLUST(newclust, mydata->fatsize)) {
813 			/* truncate the rest */
814 			clear_fatent(mydata, newclust);
815 
816 			/* Mark end of file in FAT */
817 			if (mydata->fatsize == 12)
818 				newclust = 0xfff;
819 			else if (mydata->fatsize == 16)
820 				newclust = 0xffff;
821 			else if (mydata->fatsize == 32)
822 				newclust = 0xfffffff;
823 			set_fatent_value(mydata, endclust, newclust);
824 		}
825 
826 		return 0;
827 	}
828 
829 	curclust = endclust;
830 	filesize -= cur_pos;
831 	assert(!(cur_pos % bytesperclust));
832 
833 set_clusters:
834 	/* allocate and write */
835 	assert(!pos);
836 
837 	/* Assure that curclust is valid */
838 	if (!curclust) {
839 		curclust = find_empty_cluster(mydata);
840 		set_start_cluster(mydata, dentptr, curclust);
841 	} else {
842 		newclust = get_fatent(mydata, curclust);
843 
844 		if (IS_LAST_CLUST(newclust, mydata->fatsize)) {
845 			newclust = determine_fatent(mydata, curclust);
846 			set_fatent_value(mydata, curclust, newclust);
847 			curclust = newclust;
848 		} else {
849 			debug("error: something wrong\n");
850 			return -1;
851 		}
852 	}
853 
854 	/* TODO: already partially written */
855 	if (check_overflow(mydata, curclust, filesize)) {
856 		printf("Error: no space left: %llu\n", filesize);
857 		return -1;
858 	}
859 
860 	actsize = bytesperclust;
861 	endclust = curclust;
862 	do {
863 		/* search for consecutive clusters */
864 		while (actsize < filesize) {
865 			newclust = determine_fatent(mydata, endclust);
866 
867 			if ((newclust - 1) != endclust)
868 				/* write to <curclust..endclust> */
869 				goto getit;
870 
871 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
872 				debug("newclust: 0x%x\n", newclust);
873 				debug("Invalid FAT entry\n");
874 				return 0;
875 			}
876 			endclust = newclust;
877 			actsize += bytesperclust;
878 		}
879 
880 		/* set remaining bytes */
881 		actsize = filesize;
882 		if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
883 			debug("error: writing cluster\n");
884 			return -1;
885 		}
886 		*gotsize += actsize;
887 
888 		/* Mark end of file in FAT */
889 		if (mydata->fatsize == 12)
890 			newclust = 0xfff;
891 		else if (mydata->fatsize == 16)
892 			newclust = 0xffff;
893 		else if (mydata->fatsize == 32)
894 			newclust = 0xfffffff;
895 		set_fatent_value(mydata, endclust, newclust);
896 
897 		return 0;
898 getit:
899 		if (set_cluster(mydata, curclust, buffer, (u32)actsize) != 0) {
900 			debug("error: writing cluster\n");
901 			return -1;
902 		}
903 		*gotsize += actsize;
904 		filesize -= actsize;
905 		buffer += actsize;
906 
907 		if (CHECK_CLUST(newclust, mydata->fatsize)) {
908 			debug("newclust: 0x%x\n", newclust);
909 			debug("Invalid FAT entry\n");
910 			return 0;
911 		}
912 		actsize = bytesperclust;
913 		curclust = endclust = newclust;
914 	} while (1);
915 
916 	return 0;
917 }
918 
919 /*
920  * Fill dir_entry
921  */
922 static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
923 	const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
924 {
925 	set_start_cluster(mydata, dentptr, start_cluster);
926 	dentptr->size = cpu_to_le32(size);
927 
928 	dentptr->attr = attr;
929 
930 	set_name(dentptr, filename);
931 }
932 
933 /*
934  * Find a directory entry based on filename or start cluster number
935  * If the directory entry is not found,
936  * the new position for writing a directory entry will be returned
937  */
938 static dir_entry *find_directory_entry(fat_itr *itr, char *filename)
939 {
940 	int match = 0;
941 
942 	while (fat_itr_next(itr)) {
943 		/* check both long and short name: */
944 		if (!strcasecmp(filename, itr->name))
945 			match = 1;
946 		else if (itr->name != itr->s_name &&
947 			 !strcasecmp(filename, itr->s_name))
948 			match = 1;
949 
950 		if (!match)
951 			continue;
952 
953 		if (itr->dent->name[0] == '\0')
954 			return NULL;
955 		else
956 			return itr->dent;
957 	}
958 
959 	if (!itr->dent && !itr->is_root && flush_dir_table(itr))
960 		/* indicate that allocating dent failed */
961 		itr->dent = NULL;
962 
963 	return NULL;
964 }
965 
966 static int split_filename(char *filename, char **dirname, char **basename)
967 {
968 	char *p, *last_slash, *last_slash_cont;
969 
970 again:
971 	p = filename;
972 	last_slash = NULL;
973 	last_slash_cont = NULL;
974 	while (*p) {
975 		if (ISDIRDELIM(*p)) {
976 			last_slash = p;
977 			last_slash_cont = p;
978 			/* continuous slashes */
979 			while (ISDIRDELIM(*p))
980 				last_slash_cont = p++;
981 			if (!*p)
982 				break;
983 		}
984 		p++;
985 	}
986 
987 	if (last_slash) {
988 		if (last_slash_cont == (filename + strlen(filename) - 1)) {
989 			/* remove trailing slashes */
990 			*last_slash = '\0';
991 			goto again;
992 		}
993 
994 		if (last_slash == filename) {
995 			/* avoid ""(null) directory */
996 			*dirname = "/";
997 		} else {
998 			*last_slash = '\0';
999 			*dirname = filename;
1000 		}
1001 
1002 		*last_slash_cont = '\0';
1003 		*basename = last_slash_cont + 1;
1004 	} else {
1005 		*dirname = "/"; /* root by default */
1006 		*basename = filename;
1007 	}
1008 
1009 	return 0;
1010 }
1011 
1012 static int normalize_longname(char *l_filename, const char *filename)
1013 {
1014 	const char *p, legal[] = "!#$%&\'()-.@^`_{}~";
1015 	unsigned char c;
1016 	int name_len;
1017 
1018 	/* Check that the filename is valid */
1019 	for (p = filename; p < filename + strlen(filename); p++) {
1020 		c = *p;
1021 
1022 		if (('0' <= c) && (c <= '9'))
1023 			continue;
1024 		if (('A' <= c) && (c <= 'Z'))
1025 			continue;
1026 		if (('a' <= c) && (c <= 'z'))
1027 			continue;
1028 		if (strchr(legal, c))
1029 			continue;
1030 		/* extended code */
1031 		if ((0x80 <= c) && (c <= 0xff))
1032 			continue;
1033 
1034 		return -1;
1035 	}
1036 
1037 	/* Normalize it */
1038 	name_len = strlen(filename);
1039 	if (name_len >= VFAT_MAXLEN_BYTES)
1040 		/* should return an error? */
1041 		name_len = VFAT_MAXLEN_BYTES - 1;
1042 
1043 	memcpy(l_filename, filename, name_len);
1044 	l_filename[name_len] = 0; /* terminate the string */
1045 	downcase(l_filename, INT_MAX);
1046 
1047 	return 0;
1048 }
1049 
1050 int file_fat_write_at(const char *filename, loff_t pos, void *buffer,
1051 		      loff_t size, loff_t *actwrite)
1052 {
1053 	dir_entry *retdent;
1054 	fsdata datablock = { .fatbuf = NULL, };
1055 	fsdata *mydata = &datablock;
1056 	fat_itr *itr = NULL;
1057 	int ret = -1;
1058 	char *filename_copy, *parent, *basename;
1059 	char l_filename[VFAT_MAXLEN_BYTES];
1060 
1061 	debug("writing %s\n", filename);
1062 
1063 	filename_copy = strdup(filename);
1064 	if (!filename_copy)
1065 		return -ENOMEM;
1066 
1067 	split_filename(filename_copy, &parent, &basename);
1068 	if (!strlen(basename)) {
1069 		ret = -EINVAL;
1070 		goto exit;
1071 	}
1072 
1073 	filename = basename;
1074 	if (normalize_longname(l_filename, filename)) {
1075 		printf("FAT: illegal filename (%s)\n", filename);
1076 		ret = -EINVAL;
1077 		goto exit;
1078 	}
1079 
1080 	itr = malloc_cache_aligned(sizeof(fat_itr));
1081 	if (!itr) {
1082 		ret = -ENOMEM;
1083 		goto exit;
1084 	}
1085 
1086 	ret = fat_itr_root(itr, &datablock);
1087 	if (ret)
1088 		goto exit;
1089 
1090 	total_sector = datablock.total_sect;
1091 
1092 	ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1093 	if (ret) {
1094 		printf("%s: doesn't exist (%d)\n", parent, ret);
1095 		goto exit;
1096 	}
1097 
1098 	retdent = find_directory_entry(itr, l_filename);
1099 
1100 	if (retdent) {
1101 		if (fat_itr_isdir(itr)) {
1102 			ret = -EISDIR;
1103 			goto exit;
1104 		}
1105 
1106 		/* A file exists */
1107 		if (pos == -1)
1108 			/* Append to the end */
1109 			pos = FAT2CPU32(retdent->size);
1110 		if (pos > retdent->size) {
1111 			/* No hole allowed */
1112 			ret = -EINVAL;
1113 			goto exit;
1114 		}
1115 
1116 		/* Update file size in a directory entry */
1117 		retdent->size = cpu_to_le32(pos + size);
1118 	} else {
1119 		/* Create a new file */
1120 
1121 		if (itr->is_root) {
1122 			/* root dir cannot have "." or ".." */
1123 			if (!strcmp(l_filename, ".") ||
1124 			    !strcmp(l_filename, "..")) {
1125 				ret = -EINVAL;
1126 				goto exit;
1127 			}
1128 		}
1129 
1130 		if (!itr->dent) {
1131 			printf("Error: allocating new dir entry\n");
1132 			ret = -EIO;
1133 			goto exit;
1134 		}
1135 
1136 		if (pos) {
1137 			/* No hole allowed */
1138 			ret = -EINVAL;
1139 			goto exit;
1140 		}
1141 
1142 		memset(itr->dent, 0, sizeof(*itr->dent));
1143 
1144 		/* Set short name to set alias checksum field in dir_slot */
1145 		set_name(itr->dent, filename);
1146 		if (fill_dir_slot(itr, filename)) {
1147 			ret = -EIO;
1148 			goto exit;
1149 		}
1150 
1151 		/* Set attribute as archive for regular file */
1152 		fill_dentry(itr->fsdata, itr->dent, filename, 0, size, 0x20);
1153 
1154 		retdent = itr->dent;
1155 	}
1156 
1157 	ret = set_contents(mydata, retdent, pos, buffer, size, actwrite);
1158 	if (ret < 0) {
1159 		printf("Error: writing contents\n");
1160 		ret = -EIO;
1161 		goto exit;
1162 	}
1163 	debug("attempt to write 0x%llx bytes\n", *actwrite);
1164 
1165 	/* Flush fat buffer */
1166 	ret = flush_dirty_fat_buffer(mydata);
1167 	if (ret) {
1168 		printf("Error: flush fat buffer\n");
1169 		ret = -EIO;
1170 		goto exit;
1171 	}
1172 
1173 	/* Write directory table to device */
1174 	ret = set_cluster(mydata, itr->clust, itr->block,
1175 			  mydata->clust_size * mydata->sect_size);
1176 	if (ret) {
1177 		printf("Error: writing directory entry\n");
1178 		ret = -EIO;
1179 	}
1180 
1181 exit:
1182 	free(filename_copy);
1183 	free(mydata->fatbuf);
1184 	free(itr);
1185 	return ret;
1186 }
1187 
1188 int file_fat_write(const char *filename, void *buffer, loff_t offset,
1189 		   loff_t maxsize, loff_t *actwrite)
1190 {
1191 	return file_fat_write_at(filename, offset, buffer, maxsize, actwrite);
1192 }
1193 
1194 static int fat_dir_entries(fat_itr *itr)
1195 {
1196 	fat_itr *dirs;
1197 	fsdata fsdata = { .fatbuf = NULL, }, *mydata = &fsdata;
1198 						/* for FATBUFSIZE */
1199 	int count;
1200 
1201 	dirs = malloc_cache_aligned(sizeof(fat_itr));
1202 	if (!dirs) {
1203 		debug("Error: allocating memory\n");
1204 		count = -ENOMEM;
1205 		goto exit;
1206 	}
1207 
1208 	/* duplicate fsdata */
1209 	fat_itr_child(dirs, itr);
1210 	fsdata = *dirs->fsdata;
1211 
1212 	/* allocate local fat buffer */
1213 	fsdata.fatbuf = malloc_cache_aligned(FATBUFSIZE);
1214 	if (!fsdata.fatbuf) {
1215 		debug("Error: allocating memory\n");
1216 		count = -ENOMEM;
1217 		goto exit;
1218 	}
1219 	fsdata.fatbufnum = -1;
1220 	dirs->fsdata = &fsdata;
1221 
1222 	for (count = 0; fat_itr_next(dirs); count++)
1223 		;
1224 
1225 exit:
1226 	free(fsdata.fatbuf);
1227 	free(dirs);
1228 	return count;
1229 }
1230 
1231 static int delete_dentry(fat_itr *itr)
1232 {
1233 	fsdata *mydata = itr->fsdata;
1234 	dir_entry *dentptr = itr->dent;
1235 
1236 	/* free cluster blocks */
1237 	clear_fatent(mydata, START(dentptr));
1238 	if (flush_dirty_fat_buffer(mydata) < 0) {
1239 		printf("Error: flush fat buffer\n");
1240 		return -EIO;
1241 	}
1242 
1243 	/*
1244 	 * update a directory entry
1245 	 * TODO:
1246 	 *  - long file name support
1247 	 *  - find and mark the "new" first invalid entry as name[0]=0x00
1248 	 */
1249 	memset(dentptr, 0, sizeof(*dentptr));
1250 	dentptr->name[0] = 0xe5;
1251 
1252 	if (set_cluster(mydata, itr->clust, itr->block,
1253 			mydata->clust_size * mydata->sect_size) != 0) {
1254 		printf("error: writing directory entry\n");
1255 		return -EIO;
1256 	}
1257 
1258 	return 0;
1259 }
1260 
1261 int fat_unlink(const char *filename)
1262 {
1263 	fsdata fsdata = { .fatbuf = NULL, };
1264 	fat_itr *itr = NULL;
1265 	int n_entries, ret;
1266 	char *filename_copy, *dirname, *basename;
1267 
1268 	filename_copy = strdup(filename);
1269 	if (!filename_copy) {
1270 		printf("Error: allocating memory\n");
1271 		ret = -ENOMEM;
1272 		goto exit;
1273 	}
1274 	split_filename(filename_copy, &dirname, &basename);
1275 
1276 	if (!strcmp(dirname, "/") && !strcmp(basename, "")) {
1277 		printf("Error: cannot remove root\n");
1278 		ret = -EINVAL;
1279 		goto exit;
1280 	}
1281 
1282 	itr = malloc_cache_aligned(sizeof(fat_itr));
1283 	if (!itr) {
1284 		printf("Error: allocating memory\n");
1285 		ret = -ENOMEM;
1286 		goto exit;
1287 	}
1288 
1289 	ret = fat_itr_root(itr, &fsdata);
1290 	if (ret)
1291 		goto exit;
1292 
1293 	total_sector = fsdata.total_sect;
1294 
1295 	ret = fat_itr_resolve(itr, dirname, TYPE_DIR);
1296 	if (ret) {
1297 		printf("%s: doesn't exist (%d)\n", dirname, ret);
1298 		ret = -ENOENT;
1299 		goto exit;
1300 	}
1301 
1302 	if (!find_directory_entry(itr, basename)) {
1303 		printf("%s: doesn't exist\n", basename);
1304 		ret = -ENOENT;
1305 		goto exit;
1306 	}
1307 
1308 	if (fat_itr_isdir(itr)) {
1309 		n_entries = fat_dir_entries(itr);
1310 		if (n_entries < 0) {
1311 			ret = n_entries;
1312 			goto exit;
1313 		}
1314 		if (n_entries > 2) {
1315 			printf("Error: directory is not empty: %d\n",
1316 			       n_entries);
1317 			ret = -EINVAL;
1318 			goto exit;
1319 		}
1320 	}
1321 
1322 	ret = delete_dentry(itr);
1323 
1324 exit:
1325 	free(fsdata.fatbuf);
1326 	free(itr);
1327 	free(filename_copy);
1328 
1329 	return ret;
1330 }
1331 
1332 int fat_mkdir(const char *new_dirname)
1333 {
1334 	dir_entry *retdent;
1335 	fsdata datablock = { .fatbuf = NULL, };
1336 	fsdata *mydata = &datablock;
1337 	fat_itr *itr = NULL;
1338 	char *dirname_copy, *parent, *dirname;
1339 	char l_dirname[VFAT_MAXLEN_BYTES];
1340 	int ret = -1;
1341 	loff_t actwrite;
1342 	unsigned int bytesperclust;
1343 	dir_entry *dotdent = NULL;
1344 
1345 	dirname_copy = strdup(new_dirname);
1346 	if (!dirname_copy)
1347 		goto exit;
1348 
1349 	split_filename(dirname_copy, &parent, &dirname);
1350 	if (!strlen(dirname)) {
1351 		ret = -EINVAL;
1352 		goto exit;
1353 	}
1354 
1355 	if (normalize_longname(l_dirname, dirname)) {
1356 		printf("FAT: illegal filename (%s)\n", dirname);
1357 		ret = -EINVAL;
1358 		goto exit;
1359 	}
1360 
1361 	itr = malloc_cache_aligned(sizeof(fat_itr));
1362 	if (!itr) {
1363 		ret = -ENOMEM;
1364 		goto exit;
1365 	}
1366 
1367 	ret = fat_itr_root(itr, &datablock);
1368 	if (ret)
1369 		goto exit;
1370 
1371 	total_sector = datablock.total_sect;
1372 
1373 	ret = fat_itr_resolve(itr, parent, TYPE_DIR);
1374 	if (ret) {
1375 		printf("%s: doesn't exist (%d)\n", parent, ret);
1376 		goto exit;
1377 	}
1378 
1379 	retdent = find_directory_entry(itr, l_dirname);
1380 
1381 	if (retdent) {
1382 		printf("%s: already exists\n", l_dirname);
1383 		ret = -EEXIST;
1384 		goto exit;
1385 	} else {
1386 		if (itr->is_root) {
1387 			/* root dir cannot have "." or ".." */
1388 			if (!strcmp(l_dirname, ".") ||
1389 			    !strcmp(l_dirname, "..")) {
1390 				ret = -EINVAL;
1391 				goto exit;
1392 			}
1393 		}
1394 
1395 		if (!itr->dent) {
1396 			printf("Error: allocating new dir entry\n");
1397 			ret = -EIO;
1398 			goto exit;
1399 		}
1400 
1401 		memset(itr->dent, 0, sizeof(*itr->dent));
1402 
1403 		/* Set short name to set alias checksum field in dir_slot */
1404 		set_name(itr->dent, dirname);
1405 		fill_dir_slot(itr, dirname);
1406 
1407 		/* Set attribute as archive for regular file */
1408 		fill_dentry(itr->fsdata, itr->dent, dirname, 0, 0,
1409 			    ATTR_DIR | ATTR_ARCH);
1410 
1411 		retdent = itr->dent;
1412 	}
1413 
1414 	/* Default entries */
1415 	bytesperclust = mydata->clust_size * mydata->sect_size;
1416 	dotdent = malloc_cache_aligned(bytesperclust);
1417 	if (!dotdent) {
1418 		ret = -ENOMEM;
1419 		goto exit;
1420 	}
1421 	memset(dotdent, 0, bytesperclust);
1422 
1423 	memcpy(dotdent[0].name, ".       ", 8);
1424 	memcpy(dotdent[0].ext, "   ", 3);
1425 	dotdent[0].attr = ATTR_DIR | ATTR_ARCH;
1426 
1427 	memcpy(dotdent[1].name, "..      ", 8);
1428 	memcpy(dotdent[1].ext, "   ", 3);
1429 	dotdent[1].attr = ATTR_DIR | ATTR_ARCH;
1430 	set_start_cluster(mydata, &dotdent[1], itr->start_clust);
1431 
1432 	ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1433 			   bytesperclust, &actwrite);
1434 	if (ret < 0) {
1435 		printf("Error: writing contents\n");
1436 		goto exit;
1437 	}
1438 	/* Write twice for "." */
1439 	set_start_cluster(mydata, &dotdent[0], START(retdent));
1440 	ret = set_contents(mydata, retdent, 0, (__u8 *)dotdent,
1441 			   bytesperclust, &actwrite);
1442 	if (ret < 0) {
1443 		printf("Error: writing contents\n");
1444 		goto exit;
1445 	}
1446 
1447 	/* Flush fat buffer */
1448 	ret = flush_dirty_fat_buffer(mydata);
1449 	if (ret) {
1450 		printf("Error: flush fat buffer\n");
1451 		goto exit;
1452 	}
1453 
1454 	/* Write directory table to device */
1455 	ret = set_cluster(mydata, itr->clust, itr->block,
1456 			  mydata->clust_size * mydata->sect_size);
1457 	if (ret)
1458 		printf("Error: writing directory entry\n");
1459 
1460 exit:
1461 	free(dirname_copy);
1462 	free(mydata->fatbuf);
1463 	free(itr);
1464 	free(dotdent);
1465 	return ret;
1466 }
1467