xref: /rk3399_rockchip-uboot/fs/fat/fat_write.c (revision ae1755be37e3d51af631ff2df12189c9fdc3e1d7)
1c30a15e5SDonggeun Kim /*
2c30a15e5SDonggeun Kim  * fat_write.c
3c30a15e5SDonggeun Kim  *
4c30a15e5SDonggeun Kim  * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
5c30a15e5SDonggeun Kim  *
61a459660SWolfgang Denk  * SPDX-License-Identifier:	GPL-2.0+
7c30a15e5SDonggeun Kim  */
8c30a15e5SDonggeun Kim 
9c30a15e5SDonggeun Kim #include <common.h>
10c30a15e5SDonggeun Kim #include <command.h>
11c30a15e5SDonggeun Kim #include <config.h>
12c30a15e5SDonggeun Kim #include <fat.h>
13c30a15e5SDonggeun Kim #include <asm/byteorder.h>
14c30a15e5SDonggeun Kim #include <part.h>
15fb7e16ccSRichard Genoud #include <linux/ctype.h>
169e374e7bSTom Rini #include <div64.h>
179e374e7bSTom Rini #include <linux/math64.h>
18c30a15e5SDonggeun Kim #include "fat.c"
19c30a15e5SDonggeun Kim 
20c30a15e5SDonggeun Kim static void uppercase(char *str, int len)
21c30a15e5SDonggeun Kim {
22c30a15e5SDonggeun Kim 	int i;
23c30a15e5SDonggeun Kim 
24c30a15e5SDonggeun Kim 	for (i = 0; i < len; i++) {
25fb7e16ccSRichard Genoud 		*str = toupper(*str);
26c30a15e5SDonggeun Kim 		str++;
27c30a15e5SDonggeun Kim 	}
28c30a15e5SDonggeun Kim }
29c30a15e5SDonggeun Kim 
30c30a15e5SDonggeun Kim static int total_sector;
31079df722SDonggeun Kim static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
32c30a15e5SDonggeun Kim {
330a04ed86SŁukasz Majewski 	ulong ret;
340a04ed86SŁukasz Majewski 
352a981dc2SSimon Glass 	if (!cur_dev)
36c30a15e5SDonggeun Kim 		return -1;
37c30a15e5SDonggeun Kim 
38079df722SDonggeun Kim 	if (cur_part_info.start + block + nr_blocks >
39079df722SDonggeun Kim 		cur_part_info.start + total_sector) {
40c30a15e5SDonggeun Kim 		printf("error: overflow occurs\n");
41c30a15e5SDonggeun Kim 		return -1;
42c30a15e5SDonggeun Kim 	}
43c30a15e5SDonggeun Kim 
442a981dc2SSimon Glass 	ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
450a04ed86SŁukasz Majewski 	if (nr_blocks && ret == 0)
460a04ed86SŁukasz Majewski 		return -1;
470a04ed86SŁukasz Majewski 
480a04ed86SŁukasz Majewski 	return ret;
49c30a15e5SDonggeun Kim }
50c30a15e5SDonggeun Kim 
51c30a15e5SDonggeun Kim /*
52c30a15e5SDonggeun Kim  * Set short name in directory entry
53c30a15e5SDonggeun Kim  */
54c30a15e5SDonggeun Kim static void set_name(dir_entry *dirent, const char *filename)
55c30a15e5SDonggeun Kim {
56c30a15e5SDonggeun Kim 	char s_name[VFAT_MAXLEN_BYTES];
57c30a15e5SDonggeun Kim 	char *period;
58c30a15e5SDonggeun Kim 	int period_location, len, i, ext_num;
59c30a15e5SDonggeun Kim 
60c30a15e5SDonggeun Kim 	if (filename == NULL)
61c30a15e5SDonggeun Kim 		return;
62c30a15e5SDonggeun Kim 
63c30a15e5SDonggeun Kim 	len = strlen(filename);
64c30a15e5SDonggeun Kim 	if (len == 0)
65c30a15e5SDonggeun Kim 		return;
66c30a15e5SDonggeun Kim 
6773dc8328SPiotr Wilczek 	strcpy(s_name, filename);
68c30a15e5SDonggeun Kim 	uppercase(s_name, len);
69c30a15e5SDonggeun Kim 
70c30a15e5SDonggeun Kim 	period = strchr(s_name, '.');
71c30a15e5SDonggeun Kim 	if (period == NULL) {
72c30a15e5SDonggeun Kim 		period_location = len;
73c30a15e5SDonggeun Kim 		ext_num = 0;
74c30a15e5SDonggeun Kim 	} else {
75c30a15e5SDonggeun Kim 		period_location = period - s_name;
76c30a15e5SDonggeun Kim 		ext_num = len - period_location - 1;
77c30a15e5SDonggeun Kim 	}
78c30a15e5SDonggeun Kim 
79c30a15e5SDonggeun Kim 	/* Pad spaces when the length of file name is shorter than eight */
80c30a15e5SDonggeun Kim 	if (period_location < 8) {
81c30a15e5SDonggeun Kim 		memcpy(dirent->name, s_name, period_location);
82c30a15e5SDonggeun Kim 		for (i = period_location; i < 8; i++)
83c30a15e5SDonggeun Kim 			dirent->name[i] = ' ';
84c30a15e5SDonggeun Kim 	} else if (period_location == 8) {
85c30a15e5SDonggeun Kim 		memcpy(dirent->name, s_name, period_location);
86c30a15e5SDonggeun Kim 	} else {
87c30a15e5SDonggeun Kim 		memcpy(dirent->name, s_name, 6);
88c30a15e5SDonggeun Kim 		dirent->name[6] = '~';
89c30a15e5SDonggeun Kim 		dirent->name[7] = '1';
90c30a15e5SDonggeun Kim 	}
91c30a15e5SDonggeun Kim 
92c30a15e5SDonggeun Kim 	if (ext_num < 3) {
93c30a15e5SDonggeun Kim 		memcpy(dirent->ext, s_name + period_location + 1, ext_num);
94c30a15e5SDonggeun Kim 		for (i = ext_num; i < 3; i++)
95c30a15e5SDonggeun Kim 			dirent->ext[i] = ' ';
96c30a15e5SDonggeun Kim 	} else
97c30a15e5SDonggeun Kim 		memcpy(dirent->ext, s_name + period_location + 1, 3);
98c30a15e5SDonggeun Kim 
99c30a15e5SDonggeun Kim 	debug("name : %s\n", dirent->name);
100c30a15e5SDonggeun Kim 	debug("ext : %s\n", dirent->ext);
101c30a15e5SDonggeun Kim }
102c30a15e5SDonggeun Kim 
103627182eaSDonggeun Kim static __u8 num_of_fats;
104c30a15e5SDonggeun Kim /*
105c30a15e5SDonggeun Kim  * Write fat buffer into block device
106c30a15e5SDonggeun Kim  */
1073c0ed9c3SStefan Brüns static int flush_dirty_fat_buffer(fsdata *mydata)
108c30a15e5SDonggeun Kim {
109c30a15e5SDonggeun Kim 	int getsize = FATBUFBLOCKS;
110c30a15e5SDonggeun Kim 	__u32 fatlength = mydata->fatlength;
111c30a15e5SDonggeun Kim 	__u8 *bufptr = mydata->fatbuf;
112c30a15e5SDonggeun Kim 	__u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
113c30a15e5SDonggeun Kim 
1143c0ed9c3SStefan Brüns 	debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
1153c0ed9c3SStefan Brüns 	      (int)mydata->fat_dirty);
1163c0ed9c3SStefan Brüns 
1173c0ed9c3SStefan Brüns 	if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
1183c0ed9c3SStefan Brüns 		return 0;
1193c0ed9c3SStefan Brüns 
120c30a15e5SDonggeun Kim 	startblock += mydata->fat_sect;
121c30a15e5SDonggeun Kim 
122c30a15e5SDonggeun Kim 	if (getsize > fatlength)
123c30a15e5SDonggeun Kim 		getsize = fatlength;
124c30a15e5SDonggeun Kim 
125c30a15e5SDonggeun Kim 	/* Write FAT buf */
126c30a15e5SDonggeun Kim 	if (disk_write(startblock, getsize, bufptr) < 0) {
127c30a15e5SDonggeun Kim 		debug("error: writing FAT blocks\n");
128c30a15e5SDonggeun Kim 		return -1;
129c30a15e5SDonggeun Kim 	}
130c30a15e5SDonggeun Kim 
131627182eaSDonggeun Kim 	if (num_of_fats == 2) {
132627182eaSDonggeun Kim 		/* Update corresponding second FAT blocks */
133627182eaSDonggeun Kim 		startblock += mydata->fatlength;
134627182eaSDonggeun Kim 		if (disk_write(startblock, getsize, bufptr) < 0) {
135627182eaSDonggeun Kim 			debug("error: writing second FAT blocks\n");
136627182eaSDonggeun Kim 			return -1;
137627182eaSDonggeun Kim 		}
138627182eaSDonggeun Kim 	}
1393c0ed9c3SStefan Brüns 	mydata->fat_dirty = 0;
140627182eaSDonggeun Kim 
141c30a15e5SDonggeun Kim 	return 0;
142c30a15e5SDonggeun Kim }
143c30a15e5SDonggeun Kim 
144c30a15e5SDonggeun Kim /*
145c30a15e5SDonggeun Kim  * Get the entry at index 'entry' in a FAT (12/16/32) table.
146c30a15e5SDonggeun Kim  * On failure 0x00 is returned.
147c30a15e5SDonggeun Kim  * When bufnum is changed, write back the previous fatbuf to the disk.
148c30a15e5SDonggeun Kim  */
149c30a15e5SDonggeun Kim static __u32 get_fatent_value(fsdata *mydata, __u32 entry)
150c30a15e5SDonggeun Kim {
151c30a15e5SDonggeun Kim 	__u32 bufnum;
152c30a15e5SDonggeun Kim 	__u32 off16, offset;
153c30a15e5SDonggeun Kim 	__u32 ret = 0x00;
154c30a15e5SDonggeun Kim 	__u16 val1, val2;
155c30a15e5SDonggeun Kim 
156dd6d7967SWu, Josh 	if (CHECK_CLUST(entry, mydata->fatsize)) {
157dd6d7967SWu, Josh 		printf("Error: Invalid FAT entry: 0x%08x\n", entry);
158dd6d7967SWu, Josh 		return ret;
159dd6d7967SWu, Josh 	}
160dd6d7967SWu, Josh 
161c30a15e5SDonggeun Kim 	switch (mydata->fatsize) {
162c30a15e5SDonggeun Kim 	case 32:
163c30a15e5SDonggeun Kim 		bufnum = entry / FAT32BUFSIZE;
164c30a15e5SDonggeun Kim 		offset = entry - bufnum * FAT32BUFSIZE;
165c30a15e5SDonggeun Kim 		break;
166c30a15e5SDonggeun Kim 	case 16:
167c30a15e5SDonggeun Kim 		bufnum = entry / FAT16BUFSIZE;
168c30a15e5SDonggeun Kim 		offset = entry - bufnum * FAT16BUFSIZE;
169c30a15e5SDonggeun Kim 		break;
170c30a15e5SDonggeun Kim 	case 12:
171c30a15e5SDonggeun Kim 		bufnum = entry / FAT12BUFSIZE;
172c30a15e5SDonggeun Kim 		offset = entry - bufnum * FAT12BUFSIZE;
173c30a15e5SDonggeun Kim 		break;
174c30a15e5SDonggeun Kim 
175c30a15e5SDonggeun Kim 	default:
176c30a15e5SDonggeun Kim 		/* Unsupported FAT size */
177c30a15e5SDonggeun Kim 		return ret;
178c30a15e5SDonggeun Kim 	}
179c30a15e5SDonggeun Kim 
180c30a15e5SDonggeun Kim 	debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
181c30a15e5SDonggeun Kim 	       mydata->fatsize, entry, entry, offset, offset);
182c30a15e5SDonggeun Kim 
183c30a15e5SDonggeun Kim 	/* Read a new block of FAT entries into the cache. */
184c30a15e5SDonggeun Kim 	if (bufnum != mydata->fatbufnum) {
185c30a15e5SDonggeun Kim 		int getsize = FATBUFBLOCKS;
186c30a15e5SDonggeun Kim 		__u8 *bufptr = mydata->fatbuf;
187c30a15e5SDonggeun Kim 		__u32 fatlength = mydata->fatlength;
188c30a15e5SDonggeun Kim 		__u32 startblock = bufnum * FATBUFBLOCKS;
189c30a15e5SDonggeun Kim 
190c30a15e5SDonggeun Kim 		if (getsize > fatlength)
191c30a15e5SDonggeun Kim 			getsize = fatlength;
192c30a15e5SDonggeun Kim 
193c30a15e5SDonggeun Kim 		startblock += mydata->fat_sect;	/* Offset from start of disk */
194c30a15e5SDonggeun Kim 
195c30a15e5SDonggeun Kim 		/* Write back the fatbuf to the disk */
1963c0ed9c3SStefan Brüns 		if (flush_dirty_fat_buffer(mydata) < 0)
197c30a15e5SDonggeun Kim 			return -1;
198c30a15e5SDonggeun Kim 
199c30a15e5SDonggeun Kim 		if (disk_read(startblock, getsize, bufptr) < 0) {
200c30a15e5SDonggeun Kim 			debug("Error reading FAT blocks\n");
201c30a15e5SDonggeun Kim 			return ret;
202c30a15e5SDonggeun Kim 		}
203c30a15e5SDonggeun Kim 		mydata->fatbufnum = bufnum;
204c30a15e5SDonggeun Kim 	}
205c30a15e5SDonggeun Kim 
206c30a15e5SDonggeun Kim 	/* Get the actual entry from the table */
207c30a15e5SDonggeun Kim 	switch (mydata->fatsize) {
208c30a15e5SDonggeun Kim 	case 32:
209c30a15e5SDonggeun Kim 		ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
210c30a15e5SDonggeun Kim 		break;
211c30a15e5SDonggeun Kim 	case 16:
212c30a15e5SDonggeun Kim 		ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
213c30a15e5SDonggeun Kim 		break;
214c30a15e5SDonggeun Kim 	case 12:
215c30a15e5SDonggeun Kim 		off16 = (offset * 3) / 4;
216c30a15e5SDonggeun Kim 
217c30a15e5SDonggeun Kim 		switch (offset & 0x3) {
218c30a15e5SDonggeun Kim 		case 0:
219c30a15e5SDonggeun Kim 			ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
220c30a15e5SDonggeun Kim 			ret &= 0xfff;
221c30a15e5SDonggeun Kim 			break;
222c30a15e5SDonggeun Kim 		case 1:
223c30a15e5SDonggeun Kim 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
224c30a15e5SDonggeun Kim 			val1 &= 0xf000;
225c30a15e5SDonggeun Kim 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
226c30a15e5SDonggeun Kim 			val2 &= 0x00ff;
227c30a15e5SDonggeun Kim 			ret = (val2 << 4) | (val1 >> 12);
228c30a15e5SDonggeun Kim 			break;
229c30a15e5SDonggeun Kim 		case 2:
230c30a15e5SDonggeun Kim 			val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
231c30a15e5SDonggeun Kim 			val1 &= 0xff00;
232c30a15e5SDonggeun Kim 			val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
233c30a15e5SDonggeun Kim 			val2 &= 0x000f;
234c30a15e5SDonggeun Kim 			ret = (val2 << 8) | (val1 >> 8);
235c30a15e5SDonggeun Kim 			break;
236c30a15e5SDonggeun Kim 		case 3:
237c30a15e5SDonggeun Kim 			ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
238c30a15e5SDonggeun Kim 			ret = (ret & 0xfff0) >> 4;
239c30a15e5SDonggeun Kim 			break;
240c30a15e5SDonggeun Kim 		default:
241c30a15e5SDonggeun Kim 			break;
242c30a15e5SDonggeun Kim 		}
243c30a15e5SDonggeun Kim 		break;
244c30a15e5SDonggeun Kim 	}
245c30a15e5SDonggeun Kim 	debug("FAT%d: ret: %08x, entry: %08x, offset: %04x\n",
246c30a15e5SDonggeun Kim 	       mydata->fatsize, ret, entry, offset);
247c30a15e5SDonggeun Kim 
248c30a15e5SDonggeun Kim 	return ret;
249c30a15e5SDonggeun Kim }
250c30a15e5SDonggeun Kim 
251c30a15e5SDonggeun Kim /*
252c30a15e5SDonggeun Kim  * Set the file name information from 'name' into 'slotptr',
253c30a15e5SDonggeun Kim  */
254c30a15e5SDonggeun Kim static int str2slot(dir_slot *slotptr, const char *name, int *idx)
255c30a15e5SDonggeun Kim {
256c30a15e5SDonggeun Kim 	int j, end_idx = 0;
257c30a15e5SDonggeun Kim 
258c30a15e5SDonggeun Kim 	for (j = 0; j <= 8; j += 2) {
259c30a15e5SDonggeun Kim 		if (name[*idx] == 0x00) {
260c30a15e5SDonggeun Kim 			slotptr->name0_4[j] = 0;
261c30a15e5SDonggeun Kim 			slotptr->name0_4[j + 1] = 0;
262c30a15e5SDonggeun Kim 			end_idx++;
263c30a15e5SDonggeun Kim 			goto name0_4;
264c30a15e5SDonggeun Kim 		}
265c30a15e5SDonggeun Kim 		slotptr->name0_4[j] = name[*idx];
266c30a15e5SDonggeun Kim 		(*idx)++;
267c30a15e5SDonggeun Kim 		end_idx++;
268c30a15e5SDonggeun Kim 	}
269c30a15e5SDonggeun Kim 	for (j = 0; j <= 10; j += 2) {
270c30a15e5SDonggeun Kim 		if (name[*idx] == 0x00) {
271c30a15e5SDonggeun Kim 			slotptr->name5_10[j] = 0;
272c30a15e5SDonggeun Kim 			slotptr->name5_10[j + 1] = 0;
273c30a15e5SDonggeun Kim 			end_idx++;
274c30a15e5SDonggeun Kim 			goto name5_10;
275c30a15e5SDonggeun Kim 		}
276c30a15e5SDonggeun Kim 		slotptr->name5_10[j] = name[*idx];
277c30a15e5SDonggeun Kim 		(*idx)++;
278c30a15e5SDonggeun Kim 		end_idx++;
279c30a15e5SDonggeun Kim 	}
280c30a15e5SDonggeun Kim 	for (j = 0; j <= 2; j += 2) {
281c30a15e5SDonggeun Kim 		if (name[*idx] == 0x00) {
282c30a15e5SDonggeun Kim 			slotptr->name11_12[j] = 0;
283c30a15e5SDonggeun Kim 			slotptr->name11_12[j + 1] = 0;
284c30a15e5SDonggeun Kim 			end_idx++;
285c30a15e5SDonggeun Kim 			goto name11_12;
286c30a15e5SDonggeun Kim 		}
287c30a15e5SDonggeun Kim 		slotptr->name11_12[j] = name[*idx];
288c30a15e5SDonggeun Kim 		(*idx)++;
289c30a15e5SDonggeun Kim 		end_idx++;
290c30a15e5SDonggeun Kim 	}
291c30a15e5SDonggeun Kim 
292c30a15e5SDonggeun Kim 	if (name[*idx] == 0x00)
293c30a15e5SDonggeun Kim 		return 1;
294c30a15e5SDonggeun Kim 
295c30a15e5SDonggeun Kim 	return 0;
296c30a15e5SDonggeun Kim /* Not used characters are filled with 0xff 0xff */
297c30a15e5SDonggeun Kim name0_4:
298c30a15e5SDonggeun Kim 	for (; end_idx < 5; end_idx++) {
299c30a15e5SDonggeun Kim 		slotptr->name0_4[end_idx * 2] = 0xff;
300c30a15e5SDonggeun Kim 		slotptr->name0_4[end_idx * 2 + 1] = 0xff;
301c30a15e5SDonggeun Kim 	}
302c30a15e5SDonggeun Kim 	end_idx = 5;
303c30a15e5SDonggeun Kim name5_10:
304c30a15e5SDonggeun Kim 	end_idx -= 5;
305c30a15e5SDonggeun Kim 	for (; end_idx < 6; end_idx++) {
306c30a15e5SDonggeun Kim 		slotptr->name5_10[end_idx * 2] = 0xff;
307c30a15e5SDonggeun Kim 		slotptr->name5_10[end_idx * 2 + 1] = 0xff;
308c30a15e5SDonggeun Kim 	}
309c30a15e5SDonggeun Kim 	end_idx = 11;
310c30a15e5SDonggeun Kim name11_12:
311c30a15e5SDonggeun Kim 	end_idx -= 11;
312c30a15e5SDonggeun Kim 	for (; end_idx < 2; end_idx++) {
313c30a15e5SDonggeun Kim 		slotptr->name11_12[end_idx * 2] = 0xff;
314c30a15e5SDonggeun Kim 		slotptr->name11_12[end_idx * 2 + 1] = 0xff;
315c30a15e5SDonggeun Kim 	}
316c30a15e5SDonggeun Kim 
317c30a15e5SDonggeun Kim 	return 1;
318c30a15e5SDonggeun Kim }
319c30a15e5SDonggeun Kim 
320c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr);
321c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
322c30a15e5SDonggeun Kim 
323c30a15e5SDonggeun Kim /*
324c30a15e5SDonggeun Kim  * Fill dir_slot entries with appropriate name, id, and attr
325c30a15e5SDonggeun Kim  * The real directory entry is returned by 'dentptr'
326c30a15e5SDonggeun Kim  */
327c30a15e5SDonggeun Kim static void
328c30a15e5SDonggeun Kim fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
329c30a15e5SDonggeun Kim {
3301170e634SBenoît Thébaudeau 	dir_slot *slotptr = (dir_slot *)get_contents_vfatname_block;
3318506eb8dSAnatolij Gustschin 	__u8 counter = 0, checksum;
332c30a15e5SDonggeun Kim 	int idx = 0, ret;
333c30a15e5SDonggeun Kim 
334ed76f912SStefan Brüns 	/* Get short file name checksum value */
335ff04f6d1SMarek Vasut 	checksum = mkcksum((*dentptr)->name, (*dentptr)->ext);
336c30a15e5SDonggeun Kim 
337c30a15e5SDonggeun Kim 	do {
338c30a15e5SDonggeun Kim 		memset(slotptr, 0x00, sizeof(dir_slot));
339c30a15e5SDonggeun Kim 		ret = str2slot(slotptr, l_name, &idx);
340c30a15e5SDonggeun Kim 		slotptr->id = ++counter;
341c30a15e5SDonggeun Kim 		slotptr->attr = ATTR_VFAT;
342c30a15e5SDonggeun Kim 		slotptr->alias_checksum = checksum;
343c30a15e5SDonggeun Kim 		slotptr++;
344c30a15e5SDonggeun Kim 	} while (ret == 0);
345c30a15e5SDonggeun Kim 
346c30a15e5SDonggeun Kim 	slotptr--;
347c30a15e5SDonggeun Kim 	slotptr->id |= LAST_LONG_ENTRY_MASK;
348c30a15e5SDonggeun Kim 
349c30a15e5SDonggeun Kim 	while (counter >= 1) {
350c30a15e5SDonggeun Kim 		if (is_next_clust(mydata, *dentptr)) {
351c30a15e5SDonggeun Kim 			/* A new cluster is allocated for directory table */
352c30a15e5SDonggeun Kim 			flush_dir_table(mydata, dentptr);
353c30a15e5SDonggeun Kim 		}
354c30a15e5SDonggeun Kim 		memcpy(*dentptr, slotptr, sizeof(dir_slot));
355c30a15e5SDonggeun Kim 		(*dentptr)++;
356c30a15e5SDonggeun Kim 		slotptr--;
357c30a15e5SDonggeun Kim 		counter--;
358c30a15e5SDonggeun Kim 	}
359c30a15e5SDonggeun Kim 
360c30a15e5SDonggeun Kim 	if (is_next_clust(mydata, *dentptr)) {
361c30a15e5SDonggeun Kim 		/* A new cluster is allocated for directory table */
362c30a15e5SDonggeun Kim 		flush_dir_table(mydata, dentptr);
363c30a15e5SDonggeun Kim 	}
364c30a15e5SDonggeun Kim }
365c30a15e5SDonggeun Kim 
366c30a15e5SDonggeun Kim static __u32 dir_curclust;
367c30a15e5SDonggeun Kim 
368c30a15e5SDonggeun Kim /*
369c30a15e5SDonggeun Kim  * Extract the full long filename starting at 'retdent' (which is really
370c30a15e5SDonggeun Kim  * a slot) into 'l_name'. If successful also copy the real directory entry
371c30a15e5SDonggeun Kim  * into 'retdent'
372c30a15e5SDonggeun Kim  * If additional adjacent cluster for directory entries is read into memory,
3731170e634SBenoît Thébaudeau  * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and
374c30a15e5SDonggeun Kim  * the location of the real directory entry is returned by 'retdent'
375c30a15e5SDonggeun Kim  * Return 0 on success, -1 otherwise.
376c30a15e5SDonggeun Kim  */
377c30a15e5SDonggeun Kim static int
378c30a15e5SDonggeun Kim get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
379c30a15e5SDonggeun Kim 	      dir_entry **retdent, char *l_name)
380c30a15e5SDonggeun Kim {
381c30a15e5SDonggeun Kim 	dir_entry *realdent;
382c30a15e5SDonggeun Kim 	dir_slot *slotptr = (dir_slot *)(*retdent);
383c30a15e5SDonggeun Kim 	dir_slot *slotptr2 = NULL;
384c30a15e5SDonggeun Kim 	__u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
385c30a15e5SDonggeun Kim 							PREFETCH_BLOCKS :
386c30a15e5SDonggeun Kim 							mydata->clust_size);
387c30a15e5SDonggeun Kim 	__u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
388c30a15e5SDonggeun Kim 	int idx = 0, cur_position = 0;
389c30a15e5SDonggeun Kim 
390c30a15e5SDonggeun Kim 	if (counter > VFAT_MAXSEQ) {
391c30a15e5SDonggeun Kim 		debug("Error: VFAT name is too long\n");
392c30a15e5SDonggeun Kim 		return -1;
393c30a15e5SDonggeun Kim 	}
394c30a15e5SDonggeun Kim 
395c30a15e5SDonggeun Kim 	while ((__u8 *)slotptr < buflimit) {
396c30a15e5SDonggeun Kim 		if (counter == 0)
397c30a15e5SDonggeun Kim 			break;
398c30a15e5SDonggeun Kim 		if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
399c30a15e5SDonggeun Kim 			return -1;
400c30a15e5SDonggeun Kim 		slotptr++;
401c30a15e5SDonggeun Kim 		counter--;
402c30a15e5SDonggeun Kim 	}
403c30a15e5SDonggeun Kim 
404c30a15e5SDonggeun Kim 	if ((__u8 *)slotptr >= buflimit) {
405c30a15e5SDonggeun Kim 		if (curclust == 0)
406c30a15e5SDonggeun Kim 			return -1;
407c30a15e5SDonggeun Kim 		curclust = get_fatent_value(mydata, dir_curclust);
408c30a15e5SDonggeun Kim 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
409c30a15e5SDonggeun Kim 			debug("curclust: 0x%x\n", curclust);
410c30a15e5SDonggeun Kim 			printf("Invalid FAT entry\n");
411c30a15e5SDonggeun Kim 			return -1;
412c30a15e5SDonggeun Kim 		}
413c30a15e5SDonggeun Kim 
414c30a15e5SDonggeun Kim 		dir_curclust = curclust;
415c30a15e5SDonggeun Kim 
4161170e634SBenoît Thébaudeau 		if (get_cluster(mydata, curclust, get_contents_vfatname_block,
417c30a15e5SDonggeun Kim 				mydata->clust_size * mydata->sect_size) != 0) {
418c30a15e5SDonggeun Kim 			debug("Error: reading directory block\n");
419c30a15e5SDonggeun Kim 			return -1;
420c30a15e5SDonggeun Kim 		}
421c30a15e5SDonggeun Kim 
4221170e634SBenoît Thébaudeau 		slotptr2 = (dir_slot *)get_contents_vfatname_block;
423c30a15e5SDonggeun Kim 		while (counter > 0) {
424c30a15e5SDonggeun Kim 			if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
425c30a15e5SDonggeun Kim 			    & 0xff) != counter)
426c30a15e5SDonggeun Kim 				return -1;
427c30a15e5SDonggeun Kim 			slotptr2++;
428c30a15e5SDonggeun Kim 			counter--;
429c30a15e5SDonggeun Kim 		}
430c30a15e5SDonggeun Kim 
431c30a15e5SDonggeun Kim 		/* Save the real directory entry */
432c30a15e5SDonggeun Kim 		realdent = (dir_entry *)slotptr2;
4331170e634SBenoît Thébaudeau 		while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
434c30a15e5SDonggeun Kim 			slotptr2--;
435c30a15e5SDonggeun Kim 			slot2str(slotptr2, l_name, &idx);
436c30a15e5SDonggeun Kim 		}
437c30a15e5SDonggeun Kim 	} else {
438c30a15e5SDonggeun Kim 		/* Save the real directory entry */
439c30a15e5SDonggeun Kim 		realdent = (dir_entry *)slotptr;
440c30a15e5SDonggeun Kim 	}
441c30a15e5SDonggeun Kim 
442c30a15e5SDonggeun Kim 	do {
443c30a15e5SDonggeun Kim 		slotptr--;
444c30a15e5SDonggeun Kim 		if (slot2str(slotptr, l_name, &idx))
445c30a15e5SDonggeun Kim 			break;
446c30a15e5SDonggeun Kim 	} while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
447c30a15e5SDonggeun Kim 
448c30a15e5SDonggeun Kim 	l_name[idx] = '\0';
449c30a15e5SDonggeun Kim 	if (*l_name == DELETED_FLAG)
450c30a15e5SDonggeun Kim 		*l_name = '\0';
451c30a15e5SDonggeun Kim 	else if (*l_name == aRING)
452c30a15e5SDonggeun Kim 		*l_name = DELETED_FLAG;
453c30a15e5SDonggeun Kim 	downcase(l_name);
454c30a15e5SDonggeun Kim 
455c30a15e5SDonggeun Kim 	/* Return the real directory entry */
456c30a15e5SDonggeun Kim 	*retdent = realdent;
457c30a15e5SDonggeun Kim 
458c30a15e5SDonggeun Kim 	if (slotptr2) {
4591170e634SBenoît Thébaudeau 		memcpy(get_dentfromdir_block, get_contents_vfatname_block,
460c30a15e5SDonggeun Kim 			mydata->clust_size * mydata->sect_size);
4611170e634SBenoît Thébaudeau 		cur_position = (__u8 *)realdent - get_contents_vfatname_block;
462c30a15e5SDonggeun Kim 		*retdent = (dir_entry *) &get_dentfromdir_block[cur_position];
463c30a15e5SDonggeun Kim 	}
464c30a15e5SDonggeun Kim 
465c30a15e5SDonggeun Kim 	return 0;
466c30a15e5SDonggeun Kim }
467c30a15e5SDonggeun Kim 
468c30a15e5SDonggeun Kim /*
469c30a15e5SDonggeun Kim  * Set the entry at index 'entry' in a FAT (16/32) table.
470c30a15e5SDonggeun Kim  */
471c30a15e5SDonggeun Kim static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
472c30a15e5SDonggeun Kim {
473c30a15e5SDonggeun Kim 	__u32 bufnum, offset;
474c30a15e5SDonggeun Kim 
475c30a15e5SDonggeun Kim 	switch (mydata->fatsize) {
476c30a15e5SDonggeun Kim 	case 32:
477c30a15e5SDonggeun Kim 		bufnum = entry / FAT32BUFSIZE;
478c30a15e5SDonggeun Kim 		offset = entry - bufnum * FAT32BUFSIZE;
479c30a15e5SDonggeun Kim 		break;
480c30a15e5SDonggeun Kim 	case 16:
481c30a15e5SDonggeun Kim 		bufnum = entry / FAT16BUFSIZE;
482c30a15e5SDonggeun Kim 		offset = entry - bufnum * FAT16BUFSIZE;
483c30a15e5SDonggeun Kim 		break;
484c30a15e5SDonggeun Kim 	default:
485c30a15e5SDonggeun Kim 		/* Unsupported FAT size */
486c30a15e5SDonggeun Kim 		return -1;
487c30a15e5SDonggeun Kim 	}
488c30a15e5SDonggeun Kim 
489c30a15e5SDonggeun Kim 	/* Read a new block of FAT entries into the cache. */
490c30a15e5SDonggeun Kim 	if (bufnum != mydata->fatbufnum) {
491c30a15e5SDonggeun Kim 		int getsize = FATBUFBLOCKS;
492c30a15e5SDonggeun Kim 		__u8 *bufptr = mydata->fatbuf;
493c30a15e5SDonggeun Kim 		__u32 fatlength = mydata->fatlength;
494c30a15e5SDonggeun Kim 		__u32 startblock = bufnum * FATBUFBLOCKS;
495c30a15e5SDonggeun Kim 
496c30a15e5SDonggeun Kim 		fatlength *= mydata->sect_size;
497c30a15e5SDonggeun Kim 		startblock += mydata->fat_sect;
498c30a15e5SDonggeun Kim 
499c30a15e5SDonggeun Kim 		if (getsize > fatlength)
500c30a15e5SDonggeun Kim 			getsize = fatlength;
501c30a15e5SDonggeun Kim 
5023c0ed9c3SStefan Brüns 		if (flush_dirty_fat_buffer(mydata) < 0)
503c30a15e5SDonggeun Kim 			return -1;
504c30a15e5SDonggeun Kim 
505c30a15e5SDonggeun Kim 		if (disk_read(startblock, getsize, bufptr) < 0) {
506c30a15e5SDonggeun Kim 			debug("Error reading FAT blocks\n");
507c30a15e5SDonggeun Kim 			return -1;
508c30a15e5SDonggeun Kim 		}
509c30a15e5SDonggeun Kim 		mydata->fatbufnum = bufnum;
510c30a15e5SDonggeun Kim 	}
511c30a15e5SDonggeun Kim 
5123c0ed9c3SStefan Brüns 	/* Mark as dirty */
5133c0ed9c3SStefan Brüns 	mydata->fat_dirty = 1;
5143c0ed9c3SStefan Brüns 
515c30a15e5SDonggeun Kim 	/* Set the actual entry */
516c30a15e5SDonggeun Kim 	switch (mydata->fatsize) {
517c30a15e5SDonggeun Kim 	case 32:
518c30a15e5SDonggeun Kim 		((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
519c30a15e5SDonggeun Kim 		break;
520c30a15e5SDonggeun Kim 	case 16:
521c30a15e5SDonggeun Kim 		((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
522c30a15e5SDonggeun Kim 		break;
523c30a15e5SDonggeun Kim 	default:
524c30a15e5SDonggeun Kim 		return -1;
525c30a15e5SDonggeun Kim 	}
526c30a15e5SDonggeun Kim 
527c30a15e5SDonggeun Kim 	return 0;
528c30a15e5SDonggeun Kim }
529c30a15e5SDonggeun Kim 
530c30a15e5SDonggeun Kim /*
531*ae1755beSStefan Brüns  * Determine the next free cluster after 'entry' in a FAT (16/32) table
532*ae1755beSStefan Brüns  * and link it to 'entry'. EOC marker is not set on returned entry.
533c30a15e5SDonggeun Kim  */
534c30a15e5SDonggeun Kim static __u32 determine_fatent(fsdata *mydata, __u32 entry)
535c30a15e5SDonggeun Kim {
536c30a15e5SDonggeun Kim 	__u32 next_fat, next_entry = entry + 1;
537c30a15e5SDonggeun Kim 
538c30a15e5SDonggeun Kim 	while (1) {
539c30a15e5SDonggeun Kim 		next_fat = get_fatent_value(mydata, next_entry);
540c30a15e5SDonggeun Kim 		if (next_fat == 0) {
541*ae1755beSStefan Brüns 			/* found free entry, link to entry */
542c30a15e5SDonggeun Kim 			set_fatent_value(mydata, entry, next_entry);
543c30a15e5SDonggeun Kim 			break;
544c30a15e5SDonggeun Kim 		}
545c30a15e5SDonggeun Kim 		next_entry++;
546c30a15e5SDonggeun Kim 	}
547c30a15e5SDonggeun Kim 	debug("FAT%d: entry: %08x, entry_value: %04x\n",
548c30a15e5SDonggeun Kim 	       mydata->fatsize, entry, next_entry);
549c30a15e5SDonggeun Kim 
550c30a15e5SDonggeun Kim 	return next_entry;
551c30a15e5SDonggeun Kim }
552c30a15e5SDonggeun Kim 
553c30a15e5SDonggeun Kim /*
554c30a15e5SDonggeun Kim  * Write at most 'size' bytes from 'buffer' into the specified cluster.
555c30a15e5SDonggeun Kim  * Return 0 on success, -1 otherwise.
556c30a15e5SDonggeun Kim  */
557c30a15e5SDonggeun Kim static int
558c30a15e5SDonggeun Kim set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
559c30a15e5SDonggeun Kim 	     unsigned long size)
560c30a15e5SDonggeun Kim {
5618133f43dSBenoît Thébaudeau 	__u32 idx = 0;
562c30a15e5SDonggeun Kim 	__u32 startsect;
5638133f43dSBenoît Thébaudeau 	int ret;
564c30a15e5SDonggeun Kim 
565c30a15e5SDonggeun Kim 	if (clustnum > 0)
566c30a15e5SDonggeun Kim 		startsect = mydata->data_begin +
567c30a15e5SDonggeun Kim 				clustnum * mydata->clust_size;
568c30a15e5SDonggeun Kim 	else
569c30a15e5SDonggeun Kim 		startsect = mydata->rootdir_sect;
570c30a15e5SDonggeun Kim 
571c30a15e5SDonggeun Kim 	debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
572c30a15e5SDonggeun Kim 
5738133f43dSBenoît Thébaudeau 	if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
5748133f43dSBenoît Thébaudeau 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
5758133f43dSBenoît Thébaudeau 
5768133f43dSBenoît Thébaudeau 		printf("FAT: Misaligned buffer address (%p)\n", buffer);
5778133f43dSBenoît Thébaudeau 
5788133f43dSBenoît Thébaudeau 		while (size >= mydata->sect_size) {
5798133f43dSBenoît Thébaudeau 			memcpy(tmpbuf, buffer, mydata->sect_size);
5808133f43dSBenoît Thébaudeau 			ret = disk_write(startsect++, 1, tmpbuf);
5818133f43dSBenoît Thébaudeau 			if (ret != 1) {
5828133f43dSBenoît Thébaudeau 				debug("Error writing data (got %d)\n", ret);
583c30a15e5SDonggeun Kim 				return -1;
584c30a15e5SDonggeun Kim 			}
5858133f43dSBenoît Thébaudeau 
5868133f43dSBenoît Thébaudeau 			buffer += mydata->sect_size;
5878133f43dSBenoît Thébaudeau 			size -= mydata->sect_size;
5886b8f185fSWu, Josh 		}
5898133f43dSBenoît Thébaudeau 	} else if (size >= mydata->sect_size) {
590c30a15e5SDonggeun Kim 		idx = size / mydata->sect_size;
5918133f43dSBenoît Thébaudeau 		ret = disk_write(startsect, idx, buffer);
5928133f43dSBenoît Thébaudeau 		if (ret != idx) {
5938133f43dSBenoît Thébaudeau 			debug("Error writing data (got %d)\n", ret);
594c30a15e5SDonggeun Kim 			return -1;
595c30a15e5SDonggeun Kim 		}
596c30a15e5SDonggeun Kim 
5978133f43dSBenoît Thébaudeau 		startsect += idx;
5988133f43dSBenoît Thébaudeau 		idx *= mydata->sect_size;
5998133f43dSBenoît Thébaudeau 		buffer += idx;
6008133f43dSBenoît Thébaudeau 		size -= idx;
6018133f43dSBenoît Thébaudeau 	}
6028133f43dSBenoît Thébaudeau 
6038133f43dSBenoît Thébaudeau 	if (size) {
6048133f43dSBenoît Thébaudeau 		ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
6058133f43dSBenoît Thébaudeau 
6068133f43dSBenoît Thébaudeau 		memcpy(tmpbuf, buffer, size);
6078133f43dSBenoît Thébaudeau 		ret = disk_write(startsect, 1, tmpbuf);
6088133f43dSBenoît Thébaudeau 		if (ret != 1) {
6098133f43dSBenoît Thébaudeau 			debug("Error writing data (got %d)\n", ret);
6108133f43dSBenoît Thébaudeau 			return -1;
6118133f43dSBenoît Thébaudeau 		}
612c30a15e5SDonggeun Kim 	}
613c30a15e5SDonggeun Kim 
614c30a15e5SDonggeun Kim 	return 0;
615c30a15e5SDonggeun Kim }
616c30a15e5SDonggeun Kim 
617c30a15e5SDonggeun Kim /*
618c30a15e5SDonggeun Kim  * Find the first empty cluster
619c30a15e5SDonggeun Kim  */
620c30a15e5SDonggeun Kim static int find_empty_cluster(fsdata *mydata)
621c30a15e5SDonggeun Kim {
622c30a15e5SDonggeun Kim 	__u32 fat_val, entry = 3;
623c30a15e5SDonggeun Kim 
624c30a15e5SDonggeun Kim 	while (1) {
625c30a15e5SDonggeun Kim 		fat_val = get_fatent_value(mydata, entry);
626c30a15e5SDonggeun Kim 		if (fat_val == 0)
627c30a15e5SDonggeun Kim 			break;
628c30a15e5SDonggeun Kim 		entry++;
629c30a15e5SDonggeun Kim 	}
630c30a15e5SDonggeun Kim 
631c30a15e5SDonggeun Kim 	return entry;
632c30a15e5SDonggeun Kim }
633c30a15e5SDonggeun Kim 
634c30a15e5SDonggeun Kim /*
635c30a15e5SDonggeun Kim  * Write directory entries in 'get_dentfromdir_block' to block device
636c30a15e5SDonggeun Kim  */
637c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
638c30a15e5SDonggeun Kim {
639c30a15e5SDonggeun Kim 	int dir_newclust = 0;
640c30a15e5SDonggeun Kim 
641c30a15e5SDonggeun Kim 	if (set_cluster(mydata, dir_curclust,
642c30a15e5SDonggeun Kim 		    get_dentfromdir_block,
643c30a15e5SDonggeun Kim 		    mydata->clust_size * mydata->sect_size) != 0) {
644c30a15e5SDonggeun Kim 		printf("error: wrinting directory entry\n");
645c30a15e5SDonggeun Kim 		return;
646c30a15e5SDonggeun Kim 	}
647c30a15e5SDonggeun Kim 	dir_newclust = find_empty_cluster(mydata);
648c30a15e5SDonggeun Kim 	set_fatent_value(mydata, dir_curclust, dir_newclust);
649c30a15e5SDonggeun Kim 	if (mydata->fatsize == 32)
650c30a15e5SDonggeun Kim 		set_fatent_value(mydata, dir_newclust, 0xffffff8);
651c30a15e5SDonggeun Kim 	else if (mydata->fatsize == 16)
652c30a15e5SDonggeun Kim 		set_fatent_value(mydata, dir_newclust, 0xfff8);
653c30a15e5SDonggeun Kim 
654c30a15e5SDonggeun Kim 	dir_curclust = dir_newclust;
655c30a15e5SDonggeun Kim 
6563c0ed9c3SStefan Brüns 	if (flush_dirty_fat_buffer(mydata) < 0)
657c30a15e5SDonggeun Kim 		return;
658c30a15e5SDonggeun Kim 
659c30a15e5SDonggeun Kim 	memset(get_dentfromdir_block, 0x00,
660c30a15e5SDonggeun Kim 		mydata->clust_size * mydata->sect_size);
661c30a15e5SDonggeun Kim 
662c30a15e5SDonggeun Kim 	*dentptr = (dir_entry *) get_dentfromdir_block;
663c30a15e5SDonggeun Kim }
664c30a15e5SDonggeun Kim 
665c30a15e5SDonggeun Kim /*
666c30a15e5SDonggeun Kim  * Set empty cluster from 'entry' to the end of a file
667c30a15e5SDonggeun Kim  */
668c30a15e5SDonggeun Kim static int clear_fatent(fsdata *mydata, __u32 entry)
669c30a15e5SDonggeun Kim {
670c30a15e5SDonggeun Kim 	__u32 fat_val;
671c30a15e5SDonggeun Kim 
672c30a15e5SDonggeun Kim 	while (1) {
673c30a15e5SDonggeun Kim 		fat_val = get_fatent_value(mydata, entry);
674c30a15e5SDonggeun Kim 		if (fat_val != 0)
675c30a15e5SDonggeun Kim 			set_fatent_value(mydata, entry, 0);
676c30a15e5SDonggeun Kim 		else
677c30a15e5SDonggeun Kim 			break;
678c30a15e5SDonggeun Kim 
679c30a15e5SDonggeun Kim 		if (fat_val == 0xfffffff || fat_val == 0xffff)
680c30a15e5SDonggeun Kim 			break;
681c30a15e5SDonggeun Kim 
682c30a15e5SDonggeun Kim 		entry = fat_val;
683c30a15e5SDonggeun Kim 	}
684c30a15e5SDonggeun Kim 
685c30a15e5SDonggeun Kim 	/* Flush fat buffer */
6863c0ed9c3SStefan Brüns 	if (flush_dirty_fat_buffer(mydata) < 0)
687c30a15e5SDonggeun Kim 		return -1;
688c30a15e5SDonggeun Kim 
689c30a15e5SDonggeun Kim 	return 0;
690c30a15e5SDonggeun Kim }
691c30a15e5SDonggeun Kim 
692c30a15e5SDonggeun Kim /*
693c30a15e5SDonggeun Kim  * Write at most 'maxsize' bytes from 'buffer' into
694c30a15e5SDonggeun Kim  * the file associated with 'dentptr'
6951ad0b98aSSuriyan Ramasami  * Update the number of bytes written in *gotsize and return 0
6961ad0b98aSSuriyan Ramasami  * or return -1 on fatal errors.
697c30a15e5SDonggeun Kim  */
698c30a15e5SDonggeun Kim static int
699c30a15e5SDonggeun Kim set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
7001ad0b98aSSuriyan Ramasami 	      loff_t maxsize, loff_t *gotsize)
701c30a15e5SDonggeun Kim {
7021ad0b98aSSuriyan Ramasami 	loff_t filesize = FAT2CPU32(dentptr->size);
703c30a15e5SDonggeun Kim 	unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
704c30a15e5SDonggeun Kim 	__u32 curclust = START(dentptr);
705c30a15e5SDonggeun Kim 	__u32 endclust = 0, newclust = 0;
7061ad0b98aSSuriyan Ramasami 	loff_t actsize;
707c30a15e5SDonggeun Kim 
7081ad0b98aSSuriyan Ramasami 	*gotsize = 0;
7091ad0b98aSSuriyan Ramasami 	debug("Filesize: %llu bytes\n", filesize);
710c30a15e5SDonggeun Kim 
711c30a15e5SDonggeun Kim 	if (maxsize > 0 && filesize > maxsize)
712c30a15e5SDonggeun Kim 		filesize = maxsize;
713c30a15e5SDonggeun Kim 
7141ad0b98aSSuriyan Ramasami 	debug("%llu bytes\n", filesize);
715c30a15e5SDonggeun Kim 
7161254b44aSBenoît Thébaudeau 	if (!curclust) {
7171254b44aSBenoît Thébaudeau 		if (filesize) {
7181254b44aSBenoît Thébaudeau 			debug("error: nonempty clusterless file!\n");
7191254b44aSBenoît Thébaudeau 			return -1;
7201254b44aSBenoît Thébaudeau 		}
7211254b44aSBenoît Thébaudeau 		return 0;
7221254b44aSBenoît Thébaudeau 	}
7231254b44aSBenoît Thébaudeau 
724c30a15e5SDonggeun Kim 	actsize = bytesperclust;
725c30a15e5SDonggeun Kim 	endclust = curclust;
726c30a15e5SDonggeun Kim 	do {
727c30a15e5SDonggeun Kim 		/* search for consecutive clusters */
728c30a15e5SDonggeun Kim 		while (actsize < filesize) {
729c30a15e5SDonggeun Kim 			newclust = determine_fatent(mydata, endclust);
730c30a15e5SDonggeun Kim 
731c30a15e5SDonggeun Kim 			if ((newclust - 1) != endclust)
732c30a15e5SDonggeun Kim 				goto getit;
733c30a15e5SDonggeun Kim 
734c30a15e5SDonggeun Kim 			if (CHECK_CLUST(newclust, mydata->fatsize)) {
7355e1a860eSBenoît Thébaudeau 				debug("newclust: 0x%x\n", newclust);
736c30a15e5SDonggeun Kim 				debug("Invalid FAT entry\n");
7371ad0b98aSSuriyan Ramasami 				return 0;
738c30a15e5SDonggeun Kim 			}
739c30a15e5SDonggeun Kim 			endclust = newclust;
740c30a15e5SDonggeun Kim 			actsize += bytesperclust;
741c30a15e5SDonggeun Kim 		}
742c30a15e5SDonggeun Kim 
743c30a15e5SDonggeun Kim 		/* set remaining bytes */
744c30a15e5SDonggeun Kim 		actsize = filesize;
7451d7f2eceSBenoît Thébaudeau 		if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
746c30a15e5SDonggeun Kim 			debug("error: writing cluster\n");
747c30a15e5SDonggeun Kim 			return -1;
748c30a15e5SDonggeun Kim 		}
7491ad0b98aSSuriyan Ramasami 		*gotsize += actsize;
750c30a15e5SDonggeun Kim 
751c30a15e5SDonggeun Kim 		/* Mark end of file in FAT */
752c30a15e5SDonggeun Kim 		if (mydata->fatsize == 16)
753c30a15e5SDonggeun Kim 			newclust = 0xffff;
754c30a15e5SDonggeun Kim 		else if (mydata->fatsize == 32)
755c30a15e5SDonggeun Kim 			newclust = 0xfffffff;
756c30a15e5SDonggeun Kim 		set_fatent_value(mydata, endclust, newclust);
757c30a15e5SDonggeun Kim 
7581ad0b98aSSuriyan Ramasami 		return 0;
759c30a15e5SDonggeun Kim getit:
760c30a15e5SDonggeun Kim 		if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
761c30a15e5SDonggeun Kim 			debug("error: writing cluster\n");
762c30a15e5SDonggeun Kim 			return -1;
763c30a15e5SDonggeun Kim 		}
7641ad0b98aSSuriyan Ramasami 		*gotsize += actsize;
765c30a15e5SDonggeun Kim 		filesize -= actsize;
766c30a15e5SDonggeun Kim 		buffer += actsize;
767c30a15e5SDonggeun Kim 
7685e1a860eSBenoît Thébaudeau 		if (CHECK_CLUST(newclust, mydata->fatsize)) {
7695e1a860eSBenoît Thébaudeau 			debug("newclust: 0x%x\n", newclust);
770c30a15e5SDonggeun Kim 			debug("Invalid FAT entry\n");
7711ad0b98aSSuriyan Ramasami 			return 0;
772c30a15e5SDonggeun Kim 		}
773c30a15e5SDonggeun Kim 		actsize = bytesperclust;
774c30a15e5SDonggeun Kim 		curclust = endclust = newclust;
775c30a15e5SDonggeun Kim 	} while (1);
776c30a15e5SDonggeun Kim }
777c30a15e5SDonggeun Kim 
778c30a15e5SDonggeun Kim /*
7791254b44aSBenoît Thébaudeau  * Set start cluster in directory entry
780c30a15e5SDonggeun Kim  */
7811254b44aSBenoît Thébaudeau static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
7821254b44aSBenoît Thébaudeau 				__u32 start_cluster)
783c30a15e5SDonggeun Kim {
784c30a15e5SDonggeun Kim 	if (mydata->fatsize == 32)
785c30a15e5SDonggeun Kim 		dentptr->starthi =
786c30a15e5SDonggeun Kim 			cpu_to_le16((start_cluster & 0xffff0000) >> 16);
787c30a15e5SDonggeun Kim 	dentptr->start = cpu_to_le16(start_cluster & 0xffff);
7881254b44aSBenoît Thébaudeau }
7891254b44aSBenoît Thébaudeau 
7901254b44aSBenoît Thébaudeau /*
7911254b44aSBenoît Thébaudeau  * Fill dir_entry
7921254b44aSBenoît Thébaudeau  */
7931254b44aSBenoît Thébaudeau static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
7941254b44aSBenoît Thébaudeau 	const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
7951254b44aSBenoît Thébaudeau {
7961254b44aSBenoît Thébaudeau 	set_start_cluster(mydata, dentptr, start_cluster);
797c30a15e5SDonggeun Kim 	dentptr->size = cpu_to_le32(size);
798c30a15e5SDonggeun Kim 
799c30a15e5SDonggeun Kim 	dentptr->attr = attr;
800c30a15e5SDonggeun Kim 
801c30a15e5SDonggeun Kim 	set_name(dentptr, filename);
802c30a15e5SDonggeun Kim }
803c30a15e5SDonggeun Kim 
804c30a15e5SDonggeun Kim /*
805c30a15e5SDonggeun Kim  * Check whether adding a file makes the file system to
806c30a15e5SDonggeun Kim  * exceed the size of the block device
807c30a15e5SDonggeun Kim  * Return -1 when overflow occurs, otherwise return 0
808c30a15e5SDonggeun Kim  */
8091ad0b98aSSuriyan Ramasami static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
810c30a15e5SDonggeun Kim {
8119e374e7bSTom Rini 	__u32 startsect, sect_num, offset;
812c30a15e5SDonggeun Kim 
813c30a15e5SDonggeun Kim 	if (clustnum > 0) {
814c30a15e5SDonggeun Kim 		startsect = mydata->data_begin +
815c30a15e5SDonggeun Kim 				clustnum * mydata->clust_size;
816c30a15e5SDonggeun Kim 	} else {
817c30a15e5SDonggeun Kim 		startsect = mydata->rootdir_sect;
818c30a15e5SDonggeun Kim 	}
819c30a15e5SDonggeun Kim 
8209e374e7bSTom Rini 	sect_num = div_u64_rem(size, mydata->sect_size, &offset);
8219e374e7bSTom Rini 
8229e374e7bSTom Rini 	if (offset != 0)
823c30a15e5SDonggeun Kim 		sect_num++;
824c30a15e5SDonggeun Kim 
825079df722SDonggeun Kim 	if (startsect + sect_num > cur_part_info.start + total_sector)
826c30a15e5SDonggeun Kim 		return -1;
827c30a15e5SDonggeun Kim 	return 0;
828c30a15e5SDonggeun Kim }
829c30a15e5SDonggeun Kim 
830c30a15e5SDonggeun Kim /*
831c30a15e5SDonggeun Kim  * Check if adding several entries exceed one cluster boundary
832c30a15e5SDonggeun Kim  */
833c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
834c30a15e5SDonggeun Kim {
835c30a15e5SDonggeun Kim 	int cur_position;
836c30a15e5SDonggeun Kim 
837c30a15e5SDonggeun Kim 	cur_position = (__u8 *)dentptr - get_dentfromdir_block;
838c30a15e5SDonggeun Kim 
839c30a15e5SDonggeun Kim 	if (cur_position >= mydata->clust_size * mydata->sect_size)
840c30a15e5SDonggeun Kim 		return 1;
841c30a15e5SDonggeun Kim 	else
842c30a15e5SDonggeun Kim 		return 0;
843c30a15e5SDonggeun Kim }
844c30a15e5SDonggeun Kim 
845c30a15e5SDonggeun Kim static dir_entry *empty_dentptr;
846c30a15e5SDonggeun Kim /*
847c30a15e5SDonggeun Kim  * Find a directory entry based on filename or start cluster number
848c30a15e5SDonggeun Kim  * If the directory entry is not found,
849c30a15e5SDonggeun Kim  * the new position for writing a directory entry will be returned
850c30a15e5SDonggeun Kim  */
851c30a15e5SDonggeun Kim static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
852c30a15e5SDonggeun Kim 	char *filename, dir_entry *retdent, __u32 start)
853c30a15e5SDonggeun Kim {
854c30a15e5SDonggeun Kim 	__u32 curclust = (startsect - mydata->data_begin) / mydata->clust_size;
855c30a15e5SDonggeun Kim 
856c30a15e5SDonggeun Kim 	debug("get_dentfromdir: %s\n", filename);
857c30a15e5SDonggeun Kim 
858c30a15e5SDonggeun Kim 	while (1) {
859c30a15e5SDonggeun Kim 		dir_entry *dentptr;
860c30a15e5SDonggeun Kim 
861c30a15e5SDonggeun Kim 		int i;
862c30a15e5SDonggeun Kim 
863c30a15e5SDonggeun Kim 		if (get_cluster(mydata, curclust, get_dentfromdir_block,
864c30a15e5SDonggeun Kim 			    mydata->clust_size * mydata->sect_size) != 0) {
865c30a15e5SDonggeun Kim 			printf("Error: reading directory block\n");
866c30a15e5SDonggeun Kim 			return NULL;
867c30a15e5SDonggeun Kim 		}
868c30a15e5SDonggeun Kim 
869c30a15e5SDonggeun Kim 		dentptr = (dir_entry *)get_dentfromdir_block;
870c30a15e5SDonggeun Kim 
871c30a15e5SDonggeun Kim 		dir_curclust = curclust;
872c30a15e5SDonggeun Kim 
873c30a15e5SDonggeun Kim 		for (i = 0; i < DIRENTSPERCLUST; i++) {
874c30a15e5SDonggeun Kim 			char s_name[14], l_name[VFAT_MAXLEN_BYTES];
875c30a15e5SDonggeun Kim 
876c30a15e5SDonggeun Kim 			l_name[0] = '\0';
877c30a15e5SDonggeun Kim 			if (dentptr->name[0] == DELETED_FLAG) {
878c30a15e5SDonggeun Kim 				dentptr++;
879c30a15e5SDonggeun Kim 				if (is_next_clust(mydata, dentptr))
880c30a15e5SDonggeun Kim 					break;
881c30a15e5SDonggeun Kim 				continue;
882c30a15e5SDonggeun Kim 			}
883c30a15e5SDonggeun Kim 			if ((dentptr->attr & ATTR_VOLUME)) {
884cb940c7eSRichard Genoud 				if (vfat_enabled &&
885cb940c7eSRichard Genoud 				    (dentptr->attr & ATTR_VFAT) &&
886c30a15e5SDonggeun Kim 				    (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
887c30a15e5SDonggeun Kim 					get_long_file_name(mydata, curclust,
888c30a15e5SDonggeun Kim 						     get_dentfromdir_block,
889c30a15e5SDonggeun Kim 						     &dentptr, l_name);
890c30a15e5SDonggeun Kim 					debug("vfatname: |%s|\n", l_name);
891cb940c7eSRichard Genoud 				} else {
892c30a15e5SDonggeun Kim 					/* Volume label or VFAT entry */
893c30a15e5SDonggeun Kim 					dentptr++;
894c30a15e5SDonggeun Kim 					if (is_next_clust(mydata, dentptr))
895c30a15e5SDonggeun Kim 						break;
896c30a15e5SDonggeun Kim 					continue;
897c30a15e5SDonggeun Kim 				}
898c30a15e5SDonggeun Kim 			}
899c30a15e5SDonggeun Kim 			if (dentptr->name[0] == 0) {
900c30a15e5SDonggeun Kim 				debug("Dentname == NULL - %d\n", i);
901c30a15e5SDonggeun Kim 				empty_dentptr = dentptr;
902c30a15e5SDonggeun Kim 				return NULL;
903c30a15e5SDonggeun Kim 			}
904c30a15e5SDonggeun Kim 
905c30a15e5SDonggeun Kim 			get_name(dentptr, s_name);
906c30a15e5SDonggeun Kim 
907c30a15e5SDonggeun Kim 			if (strcmp(filename, s_name)
908c30a15e5SDonggeun Kim 			    && strcmp(filename, l_name)) {
909c30a15e5SDonggeun Kim 				debug("Mismatch: |%s|%s|\n",
910c30a15e5SDonggeun Kim 					s_name, l_name);
911c30a15e5SDonggeun Kim 				dentptr++;
912c30a15e5SDonggeun Kim 				if (is_next_clust(mydata, dentptr))
913c30a15e5SDonggeun Kim 					break;
914c30a15e5SDonggeun Kim 				continue;
915c30a15e5SDonggeun Kim 			}
916c30a15e5SDonggeun Kim 
917c30a15e5SDonggeun Kim 			memcpy(retdent, dentptr, sizeof(dir_entry));
918c30a15e5SDonggeun Kim 
919c30a15e5SDonggeun Kim 			debug("DentName: %s", s_name);
920c30a15e5SDonggeun Kim 			debug(", start: 0x%x", START(dentptr));
921c30a15e5SDonggeun Kim 			debug(", size:  0x%x %s\n",
922c30a15e5SDonggeun Kim 			      FAT2CPU32(dentptr->size),
923c30a15e5SDonggeun Kim 			      (dentptr->attr & ATTR_DIR) ?
924c30a15e5SDonggeun Kim 			      "(DIR)" : "");
925c30a15e5SDonggeun Kim 
926c30a15e5SDonggeun Kim 			return dentptr;
927c30a15e5SDonggeun Kim 		}
928c30a15e5SDonggeun Kim 
929dd6d7967SWu, Josh 		/*
930dd6d7967SWu, Josh 		 * In FAT16/12, the root dir is locate before data area, shows
931dd6d7967SWu, Josh 		 * in following:
932dd6d7967SWu, Josh 		 * -------------------------------------------------------------
933dd6d7967SWu, Josh 		 * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) |
934dd6d7967SWu, Josh 		 * -------------------------------------------------------------
935dd6d7967SWu, Josh 		 *
936dd6d7967SWu, Josh 		 * As a result if curclust is in Root dir, it is a negative
937dd6d7967SWu, Josh 		 * number or 0, 1.
938dd6d7967SWu, Josh 		 *
939dd6d7967SWu, Josh 		 */
940dd6d7967SWu, Josh 		if (mydata->fatsize != 32 && (int)curclust <= 1) {
941dd6d7967SWu, Josh 			/* Current clust is in root dir, set to next clust */
942dd6d7967SWu, Josh 			curclust++;
943dd6d7967SWu, Josh 			if ((int)curclust <= 1)
944dd6d7967SWu, Josh 				continue;	/* continue to find */
945dd6d7967SWu, Josh 
946dd6d7967SWu, Josh 			/* Reach the end of root dir */
947dd6d7967SWu, Josh 			empty_dentptr = dentptr;
948dd6d7967SWu, Josh 			return NULL;
949dd6d7967SWu, Josh 		}
950dd6d7967SWu, Josh 
951c30a15e5SDonggeun Kim 		curclust = get_fatent_value(mydata, dir_curclust);
9522e98f708SWu, Josh 		if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
953c30a15e5SDonggeun Kim 			empty_dentptr = dentptr;
954c30a15e5SDonggeun Kim 			return NULL;
955c30a15e5SDonggeun Kim 		}
956c30a15e5SDonggeun Kim 		if (CHECK_CLUST(curclust, mydata->fatsize)) {
957c30a15e5SDonggeun Kim 			debug("curclust: 0x%x\n", curclust);
958c30a15e5SDonggeun Kim 			debug("Invalid FAT entry\n");
959c30a15e5SDonggeun Kim 			return NULL;
960c30a15e5SDonggeun Kim 		}
961c30a15e5SDonggeun Kim 	}
962c30a15e5SDonggeun Kim 
963c30a15e5SDonggeun Kim 	return NULL;
964c30a15e5SDonggeun Kim }
965c30a15e5SDonggeun Kim 
9661ad0b98aSSuriyan Ramasami static int do_fat_write(const char *filename, void *buffer, loff_t size,
9671ad0b98aSSuriyan Ramasami 			loff_t *actwrite)
968c30a15e5SDonggeun Kim {
969c30a15e5SDonggeun Kim 	dir_entry *dentptr, *retdent;
970c30a15e5SDonggeun Kim 	__u32 startsect;
971c30a15e5SDonggeun Kim 	__u32 start_cluster;
972c30a15e5SDonggeun Kim 	boot_sector bs;
973c30a15e5SDonggeun Kim 	volume_info volinfo;
974c30a15e5SDonggeun Kim 	fsdata datablock;
975c30a15e5SDonggeun Kim 	fsdata *mydata = &datablock;
976c30a15e5SDonggeun Kim 	int cursect;
977bf6b6af7SAnatolij Gustschin 	int ret = -1, name_len;
978c30a15e5SDonggeun Kim 	char l_filename[VFAT_MAXLEN_BYTES];
979c30a15e5SDonggeun Kim 
9801ad0b98aSSuriyan Ramasami 	*actwrite = size;
981c30a15e5SDonggeun Kim 	dir_curclust = 0;
982c30a15e5SDonggeun Kim 
983c30a15e5SDonggeun Kim 	if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
984c30a15e5SDonggeun Kim 		debug("error: reading boot sector\n");
985c30a15e5SDonggeun Kim 		return -1;
986c30a15e5SDonggeun Kim 	}
987c30a15e5SDonggeun Kim 
988c30a15e5SDonggeun Kim 	total_sector = bs.total_sect;
989c30a15e5SDonggeun Kim 	if (total_sector == 0)
990e04350d2SSteve Rae 		total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
991c30a15e5SDonggeun Kim 
992c30a15e5SDonggeun Kim 	if (mydata->fatsize == 32)
993c30a15e5SDonggeun Kim 		mydata->fatlength = bs.fat32_length;
994c30a15e5SDonggeun Kim 	else
995c30a15e5SDonggeun Kim 		mydata->fatlength = bs.fat_length;
996c30a15e5SDonggeun Kim 
997c30a15e5SDonggeun Kim 	mydata->fat_sect = bs.reserved;
998c30a15e5SDonggeun Kim 
999c30a15e5SDonggeun Kim 	cursect = mydata->rootdir_sect
1000c30a15e5SDonggeun Kim 		= mydata->fat_sect + mydata->fatlength * bs.fats;
1001627182eaSDonggeun Kim 	num_of_fats = bs.fats;
1002c30a15e5SDonggeun Kim 
1003c30a15e5SDonggeun Kim 	mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
1004c30a15e5SDonggeun Kim 	mydata->clust_size = bs.cluster_size;
1005c30a15e5SDonggeun Kim 
1006c30a15e5SDonggeun Kim 	if (mydata->fatsize == 32) {
1007c30a15e5SDonggeun Kim 		mydata->data_begin = mydata->rootdir_sect -
1008c30a15e5SDonggeun Kim 					(mydata->clust_size * 2);
1009c30a15e5SDonggeun Kim 	} else {
1010c30a15e5SDonggeun Kim 		int rootdir_size;
1011c30a15e5SDonggeun Kim 
1012c30a15e5SDonggeun Kim 		rootdir_size = ((bs.dir_entries[1]  * (int)256 +
1013c30a15e5SDonggeun Kim 				 bs.dir_entries[0]) *
1014c30a15e5SDonggeun Kim 				 sizeof(dir_entry)) /
1015c30a15e5SDonggeun Kim 				 mydata->sect_size;
1016c30a15e5SDonggeun Kim 		mydata->data_begin = mydata->rootdir_sect +
1017c30a15e5SDonggeun Kim 					rootdir_size -
1018c30a15e5SDonggeun Kim 					(mydata->clust_size * 2);
1019c30a15e5SDonggeun Kim 	}
1020c30a15e5SDonggeun Kim 
1021c30a15e5SDonggeun Kim 	mydata->fatbufnum = -1;
10223c0ed9c3SStefan Brüns 	mydata->fat_dirty = 0;
10238abd053cSNobuhiro Iwamatsu 	mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
1024c30a15e5SDonggeun Kim 	if (mydata->fatbuf == NULL) {
1025c30a15e5SDonggeun Kim 		debug("Error: allocating memory\n");
1026c30a15e5SDonggeun Kim 		return -1;
1027c30a15e5SDonggeun Kim 	}
1028c30a15e5SDonggeun Kim 
1029c30a15e5SDonggeun Kim 	if (disk_read(cursect,
1030c30a15e5SDonggeun Kim 		(mydata->fatsize == 32) ?
1031c30a15e5SDonggeun Kim 		(mydata->clust_size) :
10321170e634SBenoît Thébaudeau 		PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
1033c30a15e5SDonggeun Kim 		debug("Error: reading rootdir block\n");
1034c30a15e5SDonggeun Kim 		goto exit;
1035c30a15e5SDonggeun Kim 	}
10361170e634SBenoît Thébaudeau 	dentptr = (dir_entry *) do_fat_read_at_block;
1037c30a15e5SDonggeun Kim 
1038c30a15e5SDonggeun Kim 	name_len = strlen(filename);
10398506eb8dSAnatolij Gustschin 	if (name_len >= VFAT_MAXLEN_BYTES)
10408506eb8dSAnatolij Gustschin 		name_len = VFAT_MAXLEN_BYTES - 1;
10418506eb8dSAnatolij Gustschin 
1042c30a15e5SDonggeun Kim 	memcpy(l_filename, filename, name_len);
10438506eb8dSAnatolij Gustschin 	l_filename[name_len] = 0; /* terminate the string */
1044c30a15e5SDonggeun Kim 	downcase(l_filename);
1045c30a15e5SDonggeun Kim 
1046c30a15e5SDonggeun Kim 	startsect = mydata->rootdir_sect;
1047c30a15e5SDonggeun Kim 	retdent = find_directory_entry(mydata, startsect,
1048c30a15e5SDonggeun Kim 				l_filename, dentptr, 0);
1049c30a15e5SDonggeun Kim 	if (retdent) {
1050c30a15e5SDonggeun Kim 		/* Update file size and start_cluster in a directory entry */
1051c30a15e5SDonggeun Kim 		retdent->size = cpu_to_le32(size);
1052e876be4bSBenoît Thébaudeau 		start_cluster = START(retdent);
1053c30a15e5SDonggeun Kim 
10541254b44aSBenoît Thébaudeau 		if (start_cluster) {
10551254b44aSBenoît Thébaudeau 			if (size) {
10561254b44aSBenoît Thébaudeau 				ret = check_overflow(mydata, start_cluster,
10571254b44aSBenoît Thébaudeau 							size);
1058c30a15e5SDonggeun Kim 				if (ret) {
10591ad0b98aSSuriyan Ramasami 					printf("Error: %llu overflow\n", size);
1060c30a15e5SDonggeun Kim 					goto exit;
1061c30a15e5SDonggeun Kim 				}
10621254b44aSBenoît Thébaudeau 			}
1063c30a15e5SDonggeun Kim 
1064c30a15e5SDonggeun Kim 			ret = clear_fatent(mydata, start_cluster);
1065c30a15e5SDonggeun Kim 			if (ret) {
1066c30a15e5SDonggeun Kim 				printf("Error: clearing FAT entries\n");
1067c30a15e5SDonggeun Kim 				goto exit;
1068c30a15e5SDonggeun Kim 			}
1069c30a15e5SDonggeun Kim 
10701254b44aSBenoît Thébaudeau 			if (!size)
10711254b44aSBenoît Thébaudeau 				set_start_cluster(mydata, retdent, 0);
10721254b44aSBenoît Thébaudeau 		} else if (size) {
1073c30a15e5SDonggeun Kim 			ret = start_cluster = find_empty_cluster(mydata);
1074c30a15e5SDonggeun Kim 			if (ret < 0) {
1075c30a15e5SDonggeun Kim 				printf("Error: finding empty cluster\n");
1076c30a15e5SDonggeun Kim 				goto exit;
1077c30a15e5SDonggeun Kim 			}
1078c30a15e5SDonggeun Kim 
1079c30a15e5SDonggeun Kim 			ret = check_overflow(mydata, start_cluster, size);
1080c30a15e5SDonggeun Kim 			if (ret) {
10811ad0b98aSSuriyan Ramasami 				printf("Error: %llu overflow\n", size);
1082c30a15e5SDonggeun Kim 				goto exit;
1083c30a15e5SDonggeun Kim 			}
1084c30a15e5SDonggeun Kim 
10851254b44aSBenoît Thébaudeau 			set_start_cluster(mydata, retdent, start_cluster);
10861254b44aSBenoît Thébaudeau 		}
10871254b44aSBenoît Thébaudeau 	} else {
10881254b44aSBenoît Thébaudeau 		/* Set short name to set alias checksum field in dir_slot */
10891254b44aSBenoît Thébaudeau 		set_name(empty_dentptr, filename);
10901254b44aSBenoît Thébaudeau 		fill_dir_slot(mydata, &empty_dentptr, filename);
10911254b44aSBenoît Thébaudeau 
10921254b44aSBenoît Thébaudeau 		if (size) {
10931254b44aSBenoît Thébaudeau 			ret = start_cluster = find_empty_cluster(mydata);
10941254b44aSBenoît Thébaudeau 			if (ret < 0) {
10951254b44aSBenoît Thébaudeau 				printf("Error: finding empty cluster\n");
10961254b44aSBenoît Thébaudeau 				goto exit;
10971254b44aSBenoît Thébaudeau 			}
10981254b44aSBenoît Thébaudeau 
10991254b44aSBenoît Thébaudeau 			ret = check_overflow(mydata, start_cluster, size);
11001254b44aSBenoît Thébaudeau 			if (ret) {
11011254b44aSBenoît Thébaudeau 				printf("Error: %llu overflow\n", size);
11021254b44aSBenoît Thébaudeau 				goto exit;
11031254b44aSBenoît Thébaudeau 			}
11041254b44aSBenoît Thébaudeau 		} else {
11051254b44aSBenoît Thébaudeau 			start_cluster = 0;
11061254b44aSBenoît Thébaudeau 		}
11071254b44aSBenoît Thébaudeau 
1108c30a15e5SDonggeun Kim 		/* Set attribute as archieve for regular file */
1109c30a15e5SDonggeun Kim 		fill_dentry(mydata, empty_dentptr, filename,
1110c30a15e5SDonggeun Kim 			start_cluster, size, 0x20);
1111c30a15e5SDonggeun Kim 
1112e876be4bSBenoît Thébaudeau 		retdent = empty_dentptr;
1113e876be4bSBenoît Thébaudeau 	}
1114e876be4bSBenoît Thébaudeau 
1115e876be4bSBenoît Thébaudeau 	ret = set_contents(mydata, retdent, buffer, size, actwrite);
11168506eb8dSAnatolij Gustschin 	if (ret < 0) {
1117c30a15e5SDonggeun Kim 		printf("Error: writing contents\n");
1118c30a15e5SDonggeun Kim 		goto exit;
1119c30a15e5SDonggeun Kim 	}
11201ad0b98aSSuriyan Ramasami 	debug("attempt to write 0x%llx bytes\n", *actwrite);
1121c30a15e5SDonggeun Kim 
1122c30a15e5SDonggeun Kim 	/* Flush fat buffer */
11233c0ed9c3SStefan Brüns 	ret = flush_dirty_fat_buffer(mydata);
1124c30a15e5SDonggeun Kim 	if (ret) {
1125c30a15e5SDonggeun Kim 		printf("Error: flush fat buffer\n");
1126c30a15e5SDonggeun Kim 		goto exit;
1127c30a15e5SDonggeun Kim 	}
1128c30a15e5SDonggeun Kim 
1129c30a15e5SDonggeun Kim 	/* Write directory table to device */
1130e876be4bSBenoît Thébaudeau 	ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block,
1131c30a15e5SDonggeun Kim 			mydata->clust_size * mydata->sect_size);
1132e876be4bSBenoît Thébaudeau 	if (ret)
1133c30a15e5SDonggeun Kim 		printf("Error: writing directory entry\n");
1134c30a15e5SDonggeun Kim 
1135c30a15e5SDonggeun Kim exit:
1136c30a15e5SDonggeun Kim 	free(mydata->fatbuf);
11371ad0b98aSSuriyan Ramasami 	return ret;
1138c30a15e5SDonggeun Kim }
1139c30a15e5SDonggeun Kim 
11401ad0b98aSSuriyan Ramasami int file_fat_write(const char *filename, void *buffer, loff_t offset,
11411ad0b98aSSuriyan Ramasami 		   loff_t maxsize, loff_t *actwrite)
1142c30a15e5SDonggeun Kim {
11431ad0b98aSSuriyan Ramasami 	if (offset != 0) {
11440af49b95SVagrant Cascadian 		printf("Error: non zero offset is currently not supported.\n");
11451ad0b98aSSuriyan Ramasami 		return -1;
11461ad0b98aSSuriyan Ramasami 	}
11471ad0b98aSSuriyan Ramasami 
1148c30a15e5SDonggeun Kim 	printf("writing %s\n", filename);
11491ad0b98aSSuriyan Ramasami 	return do_fat_write(filename, buffer, maxsize, actwrite);
1150c30a15e5SDonggeun Kim }
1151