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