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