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