1c30a15e5SDonggeun Kim /*
2c30a15e5SDonggeun Kim * fat_write.c
3c30a15e5SDonggeun Kim *
4c30a15e5SDonggeun Kim * R/W (V)FAT 12/16/32 filesystem implementation by Donggeun Kim
5c30a15e5SDonggeun Kim *
61a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
7c30a15e5SDonggeun Kim */
8c30a15e5SDonggeun Kim
9c30a15e5SDonggeun Kim #include <common.h>
10c30a15e5SDonggeun Kim #include <command.h>
11c30a15e5SDonggeun Kim #include <config.h>
12c30a15e5SDonggeun Kim #include <fat.h>
13c30a15e5SDonggeun Kim #include <asm/byteorder.h>
14c30a15e5SDonggeun Kim #include <part.h>
15fb7e16ccSRichard Genoud #include <linux/ctype.h>
169e374e7bSTom Rini #include <div64.h>
179e374e7bSTom Rini #include <linux/math64.h>
18c30a15e5SDonggeun Kim #include "fat.c"
19c30a15e5SDonggeun Kim
uppercase(char * str,int len)20c30a15e5SDonggeun Kim static void uppercase(char *str, int len)
21c30a15e5SDonggeun Kim {
22c30a15e5SDonggeun Kim int i;
23c30a15e5SDonggeun Kim
24c30a15e5SDonggeun Kim for (i = 0; i < len; i++) {
25fb7e16ccSRichard Genoud *str = toupper(*str);
26c30a15e5SDonggeun Kim str++;
27c30a15e5SDonggeun Kim }
28c30a15e5SDonggeun Kim }
29c30a15e5SDonggeun Kim
30c30a15e5SDonggeun Kim static int total_sector;
disk_write(__u32 block,__u32 nr_blocks,void * buf)31079df722SDonggeun Kim static int disk_write(__u32 block, __u32 nr_blocks, void *buf)
32c30a15e5SDonggeun Kim {
330a04ed86SŁukasz Majewski ulong ret;
340a04ed86SŁukasz Majewski
352a981dc2SSimon Glass if (!cur_dev)
36c30a15e5SDonggeun Kim return -1;
37c30a15e5SDonggeun Kim
38079df722SDonggeun Kim if (cur_part_info.start + block + nr_blocks >
39079df722SDonggeun Kim cur_part_info.start + total_sector) {
40c30a15e5SDonggeun Kim printf("error: overflow occurs\n");
41c30a15e5SDonggeun Kim return -1;
42c30a15e5SDonggeun Kim }
43c30a15e5SDonggeun Kim
442a981dc2SSimon Glass ret = blk_dwrite(cur_dev, cur_part_info.start + block, nr_blocks, buf);
450a04ed86SŁukasz Majewski if (nr_blocks && ret == 0)
460a04ed86SŁukasz Majewski return -1;
470a04ed86SŁukasz Majewski
480a04ed86SŁukasz Majewski return ret;
49c30a15e5SDonggeun Kim }
50c30a15e5SDonggeun Kim
51c30a15e5SDonggeun Kim /*
52c30a15e5SDonggeun Kim * Set short name in directory entry
53c30a15e5SDonggeun Kim */
set_name(dir_entry * dirent,const char * filename)54c30a15e5SDonggeun Kim static void set_name(dir_entry *dirent, const char *filename)
55c30a15e5SDonggeun Kim {
56c30a15e5SDonggeun Kim char s_name[VFAT_MAXLEN_BYTES];
57c30a15e5SDonggeun Kim char *period;
58c30a15e5SDonggeun Kim int period_location, len, i, ext_num;
59c30a15e5SDonggeun Kim
60c30a15e5SDonggeun Kim if (filename == NULL)
61c30a15e5SDonggeun Kim return;
62c30a15e5SDonggeun Kim
63c30a15e5SDonggeun Kim len = strlen(filename);
64c30a15e5SDonggeun Kim if (len == 0)
65c30a15e5SDonggeun Kim return;
66c30a15e5SDonggeun Kim
6773dc8328SPiotr Wilczek strcpy(s_name, filename);
68c30a15e5SDonggeun Kim uppercase(s_name, len);
69c30a15e5SDonggeun Kim
70c30a15e5SDonggeun Kim period = strchr(s_name, '.');
71c30a15e5SDonggeun Kim if (period == NULL) {
72c30a15e5SDonggeun Kim period_location = len;
73c30a15e5SDonggeun Kim ext_num = 0;
74c30a15e5SDonggeun Kim } else {
75c30a15e5SDonggeun Kim period_location = period - s_name;
76c30a15e5SDonggeun Kim ext_num = len - period_location - 1;
77c30a15e5SDonggeun Kim }
78c30a15e5SDonggeun Kim
79c30a15e5SDonggeun Kim /* Pad spaces when the length of file name is shorter than eight */
80c30a15e5SDonggeun Kim if (period_location < 8) {
81c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, period_location);
82c30a15e5SDonggeun Kim for (i = period_location; i < 8; i++)
83c30a15e5SDonggeun Kim dirent->name[i] = ' ';
84c30a15e5SDonggeun Kim } else if (period_location == 8) {
85c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, period_location);
86c30a15e5SDonggeun Kim } else {
87c30a15e5SDonggeun Kim memcpy(dirent->name, s_name, 6);
88c30a15e5SDonggeun Kim dirent->name[6] = '~';
89c30a15e5SDonggeun Kim dirent->name[7] = '1';
90c30a15e5SDonggeun Kim }
91c30a15e5SDonggeun Kim
92c30a15e5SDonggeun Kim if (ext_num < 3) {
93c30a15e5SDonggeun Kim memcpy(dirent->ext, s_name + period_location + 1, ext_num);
94c30a15e5SDonggeun Kim for (i = ext_num; i < 3; i++)
95c30a15e5SDonggeun Kim dirent->ext[i] = ' ';
96c30a15e5SDonggeun Kim } else
97c30a15e5SDonggeun Kim memcpy(dirent->ext, s_name + period_location + 1, 3);
98c30a15e5SDonggeun Kim
99c30a15e5SDonggeun Kim debug("name : %s\n", dirent->name);
100c30a15e5SDonggeun Kim debug("ext : %s\n", dirent->ext);
101c30a15e5SDonggeun Kim }
102c30a15e5SDonggeun Kim
103627182eaSDonggeun Kim static __u8 num_of_fats;
104c30a15e5SDonggeun Kim /*
105c30a15e5SDonggeun Kim * Write fat buffer into block device
106c30a15e5SDonggeun Kim */
flush_dirty_fat_buffer(fsdata * mydata)1073c0ed9c3SStefan Brüns static int flush_dirty_fat_buffer(fsdata *mydata)
108c30a15e5SDonggeun Kim {
109c30a15e5SDonggeun Kim int getsize = FATBUFBLOCKS;
110c30a15e5SDonggeun Kim __u32 fatlength = mydata->fatlength;
111c30a15e5SDonggeun Kim __u8 *bufptr = mydata->fatbuf;
112c30a15e5SDonggeun Kim __u32 startblock = mydata->fatbufnum * FATBUFBLOCKS;
113c30a15e5SDonggeun Kim
1143c0ed9c3SStefan Brüns debug("debug: evicting %d, dirty: %d\n", mydata->fatbufnum,
1153c0ed9c3SStefan Brüns (int)mydata->fat_dirty);
1163c0ed9c3SStefan Brüns
1173c0ed9c3SStefan Brüns if ((!mydata->fat_dirty) || (mydata->fatbufnum == -1))
1183c0ed9c3SStefan Brüns return 0;
1193c0ed9c3SStefan Brüns
1206c1a8080SStefan Brüns /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
1216c1a8080SStefan Brüns if (startblock + getsize > fatlength)
1226c1a8080SStefan Brüns getsize = fatlength - startblock;
123c30a15e5SDonggeun Kim
1246c1a8080SStefan Brüns startblock += mydata->fat_sect;
125c30a15e5SDonggeun Kim
126c30a15e5SDonggeun Kim /* Write FAT buf */
127c30a15e5SDonggeun Kim if (disk_write(startblock, getsize, bufptr) < 0) {
128c30a15e5SDonggeun Kim debug("error: writing FAT blocks\n");
129c30a15e5SDonggeun Kim return -1;
130c30a15e5SDonggeun Kim }
131c30a15e5SDonggeun Kim
132627182eaSDonggeun Kim if (num_of_fats == 2) {
133627182eaSDonggeun Kim /* Update corresponding second FAT blocks */
134627182eaSDonggeun Kim startblock += mydata->fatlength;
135627182eaSDonggeun Kim if (disk_write(startblock, getsize, bufptr) < 0) {
136627182eaSDonggeun Kim debug("error: writing second FAT blocks\n");
137627182eaSDonggeun Kim return -1;
138627182eaSDonggeun Kim }
139627182eaSDonggeun Kim }
1403c0ed9c3SStefan Brüns mydata->fat_dirty = 0;
141627182eaSDonggeun Kim
142c30a15e5SDonggeun Kim return 0;
143c30a15e5SDonggeun Kim }
144c30a15e5SDonggeun Kim
145c30a15e5SDonggeun Kim /*
146c30a15e5SDonggeun Kim * Set the file name information from 'name' into 'slotptr',
147c30a15e5SDonggeun Kim */
str2slot(dir_slot * slotptr,const char * name,int * idx)148c30a15e5SDonggeun Kim static int str2slot(dir_slot *slotptr, const char *name, int *idx)
149c30a15e5SDonggeun Kim {
150c30a15e5SDonggeun Kim int j, end_idx = 0;
151c30a15e5SDonggeun Kim
152c30a15e5SDonggeun Kim for (j = 0; j <= 8; j += 2) {
153c30a15e5SDonggeun Kim if (name[*idx] == 0x00) {
154c30a15e5SDonggeun Kim slotptr->name0_4[j] = 0;
155c30a15e5SDonggeun Kim slotptr->name0_4[j + 1] = 0;
156c30a15e5SDonggeun Kim end_idx++;
157c30a15e5SDonggeun Kim goto name0_4;
158c30a15e5SDonggeun Kim }
159c30a15e5SDonggeun Kim slotptr->name0_4[j] = name[*idx];
160c30a15e5SDonggeun Kim (*idx)++;
161c30a15e5SDonggeun Kim end_idx++;
162c30a15e5SDonggeun Kim }
163c30a15e5SDonggeun Kim for (j = 0; j <= 10; j += 2) {
164c30a15e5SDonggeun Kim if (name[*idx] == 0x00) {
165c30a15e5SDonggeun Kim slotptr->name5_10[j] = 0;
166c30a15e5SDonggeun Kim slotptr->name5_10[j + 1] = 0;
167c30a15e5SDonggeun Kim end_idx++;
168c30a15e5SDonggeun Kim goto name5_10;
169c30a15e5SDonggeun Kim }
170c30a15e5SDonggeun Kim slotptr->name5_10[j] = name[*idx];
171c30a15e5SDonggeun Kim (*idx)++;
172c30a15e5SDonggeun Kim end_idx++;
173c30a15e5SDonggeun Kim }
174c30a15e5SDonggeun Kim for (j = 0; j <= 2; j += 2) {
175c30a15e5SDonggeun Kim if (name[*idx] == 0x00) {
176c30a15e5SDonggeun Kim slotptr->name11_12[j] = 0;
177c30a15e5SDonggeun Kim slotptr->name11_12[j + 1] = 0;
178c30a15e5SDonggeun Kim end_idx++;
179c30a15e5SDonggeun Kim goto name11_12;
180c30a15e5SDonggeun Kim }
181c30a15e5SDonggeun Kim slotptr->name11_12[j] = name[*idx];
182c30a15e5SDonggeun Kim (*idx)++;
183c30a15e5SDonggeun Kim end_idx++;
184c30a15e5SDonggeun Kim }
185c30a15e5SDonggeun Kim
186c30a15e5SDonggeun Kim if (name[*idx] == 0x00)
187c30a15e5SDonggeun Kim return 1;
188c30a15e5SDonggeun Kim
189c30a15e5SDonggeun Kim return 0;
190c30a15e5SDonggeun Kim /* Not used characters are filled with 0xff 0xff */
191c30a15e5SDonggeun Kim name0_4:
192c30a15e5SDonggeun Kim for (; end_idx < 5; end_idx++) {
193c30a15e5SDonggeun Kim slotptr->name0_4[end_idx * 2] = 0xff;
194c30a15e5SDonggeun Kim slotptr->name0_4[end_idx * 2 + 1] = 0xff;
195c30a15e5SDonggeun Kim }
196c30a15e5SDonggeun Kim end_idx = 5;
197c30a15e5SDonggeun Kim name5_10:
198c30a15e5SDonggeun Kim end_idx -= 5;
199c30a15e5SDonggeun Kim for (; end_idx < 6; end_idx++) {
200c30a15e5SDonggeun Kim slotptr->name5_10[end_idx * 2] = 0xff;
201c30a15e5SDonggeun Kim slotptr->name5_10[end_idx * 2 + 1] = 0xff;
202c30a15e5SDonggeun Kim }
203c30a15e5SDonggeun Kim end_idx = 11;
204c30a15e5SDonggeun Kim name11_12:
205c30a15e5SDonggeun Kim end_idx -= 11;
206c30a15e5SDonggeun Kim for (; end_idx < 2; end_idx++) {
207c30a15e5SDonggeun Kim slotptr->name11_12[end_idx * 2] = 0xff;
208c30a15e5SDonggeun Kim slotptr->name11_12[end_idx * 2 + 1] = 0xff;
209c30a15e5SDonggeun Kim }
210c30a15e5SDonggeun Kim
211c30a15e5SDonggeun Kim return 1;
212c30a15e5SDonggeun Kim }
213c30a15e5SDonggeun Kim
214c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr);
215c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr);
216c30a15e5SDonggeun Kim
217c30a15e5SDonggeun Kim /*
218c30a15e5SDonggeun Kim * Fill dir_slot entries with appropriate name, id, and attr
219c30a15e5SDonggeun Kim * The real directory entry is returned by 'dentptr'
220c30a15e5SDonggeun Kim */
221c30a15e5SDonggeun Kim static void
fill_dir_slot(fsdata * mydata,dir_entry ** dentptr,const char * l_name)222c30a15e5SDonggeun Kim fill_dir_slot(fsdata *mydata, dir_entry **dentptr, const char *l_name)
223c30a15e5SDonggeun Kim {
2247aa1a6b7STien Fong Chee __u8 temp_dir_slot_buffer[MAX_LFN_SLOT * sizeof(dir_slot)];
2257aa1a6b7STien Fong Chee dir_slot *slotptr = (dir_slot *)temp_dir_slot_buffer;
2268506eb8dSAnatolij Gustschin __u8 counter = 0, checksum;
227c30a15e5SDonggeun Kim int idx = 0, ret;
228c30a15e5SDonggeun Kim
229ed76f912SStefan Brüns /* Get short file name checksum value */
230ff04f6d1SMarek Vasut checksum = mkcksum((*dentptr)->name, (*dentptr)->ext);
231c30a15e5SDonggeun Kim
232c30a15e5SDonggeun Kim do {
233c30a15e5SDonggeun Kim memset(slotptr, 0x00, sizeof(dir_slot));
234c30a15e5SDonggeun Kim ret = str2slot(slotptr, l_name, &idx);
235c30a15e5SDonggeun Kim slotptr->id = ++counter;
236c30a15e5SDonggeun Kim slotptr->attr = ATTR_VFAT;
237c30a15e5SDonggeun Kim slotptr->alias_checksum = checksum;
238c30a15e5SDonggeun Kim slotptr++;
239c30a15e5SDonggeun Kim } while (ret == 0);
240c30a15e5SDonggeun Kim
241c30a15e5SDonggeun Kim slotptr--;
242c30a15e5SDonggeun Kim slotptr->id |= LAST_LONG_ENTRY_MASK;
243c30a15e5SDonggeun Kim
244c30a15e5SDonggeun Kim while (counter >= 1) {
245c30a15e5SDonggeun Kim if (is_next_clust(mydata, *dentptr)) {
246c30a15e5SDonggeun Kim /* A new cluster is allocated for directory table */
247c30a15e5SDonggeun Kim flush_dir_table(mydata, dentptr);
248c30a15e5SDonggeun Kim }
249c30a15e5SDonggeun Kim memcpy(*dentptr, slotptr, sizeof(dir_slot));
250c30a15e5SDonggeun Kim (*dentptr)++;
251c30a15e5SDonggeun Kim slotptr--;
252c30a15e5SDonggeun Kim counter--;
253c30a15e5SDonggeun Kim }
254c30a15e5SDonggeun Kim
255c30a15e5SDonggeun Kim if (is_next_clust(mydata, *dentptr)) {
256c30a15e5SDonggeun Kim /* A new cluster is allocated for directory table */
257c30a15e5SDonggeun Kim flush_dir_table(mydata, dentptr);
258c30a15e5SDonggeun Kim }
259c30a15e5SDonggeun Kim }
260c30a15e5SDonggeun Kim
261c30a15e5SDonggeun Kim static __u32 dir_curclust;
262c30a15e5SDonggeun Kim
263c30a15e5SDonggeun Kim /*
264c30a15e5SDonggeun Kim * Extract the full long filename starting at 'retdent' (which is really
265c30a15e5SDonggeun Kim * a slot) into 'l_name'. If successful also copy the real directory entry
266c30a15e5SDonggeun Kim * into 'retdent'
267c30a15e5SDonggeun Kim * If additional adjacent cluster for directory entries is read into memory,
2681170e634SBenoît Thébaudeau * then 'get_contents_vfatname_block' is copied into 'get_dentfromdir_block' and
269c30a15e5SDonggeun Kim * the location of the real directory entry is returned by 'retdent'
270c30a15e5SDonggeun Kim * Return 0 on success, -1 otherwise.
271c30a15e5SDonggeun Kim */
272c30a15e5SDonggeun Kim static int
get_long_file_name(fsdata * mydata,int curclust,__u8 * cluster,dir_entry ** retdent,char * l_name)273c30a15e5SDonggeun Kim get_long_file_name(fsdata *mydata, int curclust, __u8 *cluster,
274c30a15e5SDonggeun Kim dir_entry **retdent, char *l_name)
275c30a15e5SDonggeun Kim {
276c30a15e5SDonggeun Kim dir_entry *realdent;
277c30a15e5SDonggeun Kim dir_slot *slotptr = (dir_slot *)(*retdent);
278c30a15e5SDonggeun Kim dir_slot *slotptr2 = NULL;
279c30a15e5SDonggeun Kim __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
280c30a15e5SDonggeun Kim PREFETCH_BLOCKS :
281c30a15e5SDonggeun Kim mydata->clust_size);
282c30a15e5SDonggeun Kim __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
283c30a15e5SDonggeun Kim int idx = 0, cur_position = 0;
284c30a15e5SDonggeun Kim
285c30a15e5SDonggeun Kim if (counter > VFAT_MAXSEQ) {
286c30a15e5SDonggeun Kim debug("Error: VFAT name is too long\n");
287c30a15e5SDonggeun Kim return -1;
288c30a15e5SDonggeun Kim }
289c30a15e5SDonggeun Kim
290c30a15e5SDonggeun Kim while ((__u8 *)slotptr < buflimit) {
291c30a15e5SDonggeun Kim if (counter == 0)
292c30a15e5SDonggeun Kim break;
293c30a15e5SDonggeun Kim if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
294c30a15e5SDonggeun Kim return -1;
295c30a15e5SDonggeun Kim slotptr++;
296c30a15e5SDonggeun Kim counter--;
297c30a15e5SDonggeun Kim }
298c30a15e5SDonggeun Kim
299c30a15e5SDonggeun Kim if ((__u8 *)slotptr >= buflimit) {
300c30a15e5SDonggeun Kim if (curclust == 0)
301c30a15e5SDonggeun Kim return -1;
302b8948d2aSStefan Brüns curclust = get_fatent(mydata, dir_curclust);
303c30a15e5SDonggeun Kim if (CHECK_CLUST(curclust, mydata->fatsize)) {
304c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", curclust);
305c30a15e5SDonggeun Kim printf("Invalid FAT entry\n");
306c30a15e5SDonggeun Kim return -1;
307c30a15e5SDonggeun Kim }
308c30a15e5SDonggeun Kim
309c30a15e5SDonggeun Kim dir_curclust = curclust;
310c30a15e5SDonggeun Kim
3111170e634SBenoît Thébaudeau if (get_cluster(mydata, curclust, get_contents_vfatname_block,
312c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) {
313c30a15e5SDonggeun Kim debug("Error: reading directory block\n");
314c30a15e5SDonggeun Kim return -1;
315c30a15e5SDonggeun Kim }
316c30a15e5SDonggeun Kim
3171170e634SBenoît Thébaudeau slotptr2 = (dir_slot *)get_contents_vfatname_block;
318c30a15e5SDonggeun Kim while (counter > 0) {
319c30a15e5SDonggeun Kim if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
320c30a15e5SDonggeun Kim & 0xff) != counter)
321c30a15e5SDonggeun Kim return -1;
322c30a15e5SDonggeun Kim slotptr2++;
323c30a15e5SDonggeun Kim counter--;
324c30a15e5SDonggeun Kim }
325c30a15e5SDonggeun Kim
326c30a15e5SDonggeun Kim /* Save the real directory entry */
327c30a15e5SDonggeun Kim realdent = (dir_entry *)slotptr2;
3281170e634SBenoît Thébaudeau while ((__u8 *)slotptr2 > get_contents_vfatname_block) {
329c30a15e5SDonggeun Kim slotptr2--;
330c30a15e5SDonggeun Kim slot2str(slotptr2, l_name, &idx);
331c30a15e5SDonggeun Kim }
332c30a15e5SDonggeun Kim } else {
333c30a15e5SDonggeun Kim /* Save the real directory entry */
334c30a15e5SDonggeun Kim realdent = (dir_entry *)slotptr;
335c30a15e5SDonggeun Kim }
336c30a15e5SDonggeun Kim
337c30a15e5SDonggeun Kim do {
338c30a15e5SDonggeun Kim slotptr--;
339c30a15e5SDonggeun Kim if (slot2str(slotptr, l_name, &idx))
340c30a15e5SDonggeun Kim break;
341c30a15e5SDonggeun Kim } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
342c30a15e5SDonggeun Kim
343c30a15e5SDonggeun Kim l_name[idx] = '\0';
344c30a15e5SDonggeun Kim if (*l_name == DELETED_FLAG)
345c30a15e5SDonggeun Kim *l_name = '\0';
346c30a15e5SDonggeun Kim else if (*l_name == aRING)
347c30a15e5SDonggeun Kim *l_name = DELETED_FLAG;
348271eb208SRob Clark downcase(l_name, INT_MAX);
349c30a15e5SDonggeun Kim
350c30a15e5SDonggeun Kim /* Return the real directory entry */
351c30a15e5SDonggeun Kim *retdent = realdent;
352c30a15e5SDonggeun Kim
353c30a15e5SDonggeun Kim if (slotptr2) {
3541170e634SBenoît Thébaudeau memcpy(get_dentfromdir_block, get_contents_vfatname_block,
355c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size);
3561170e634SBenoît Thébaudeau cur_position = (__u8 *)realdent - get_contents_vfatname_block;
357c30a15e5SDonggeun Kim *retdent = (dir_entry *) &get_dentfromdir_block[cur_position];
358c30a15e5SDonggeun Kim }
359c30a15e5SDonggeun Kim
360c30a15e5SDonggeun Kim return 0;
361c30a15e5SDonggeun Kim }
362c30a15e5SDonggeun Kim
363c30a15e5SDonggeun Kim /*
36449abbd9cSPhilipp Skadorov * Set the entry at index 'entry' in a FAT (12/16/32) table.
365c30a15e5SDonggeun Kim */
set_fatent_value(fsdata * mydata,__u32 entry,__u32 entry_value)366c30a15e5SDonggeun Kim static int set_fatent_value(fsdata *mydata, __u32 entry, __u32 entry_value)
367c30a15e5SDonggeun Kim {
36849abbd9cSPhilipp Skadorov __u32 bufnum, offset, off16;
36949abbd9cSPhilipp Skadorov __u16 val1, val2;
370c30a15e5SDonggeun Kim
371c30a15e5SDonggeun Kim switch (mydata->fatsize) {
372c30a15e5SDonggeun Kim case 32:
373c30a15e5SDonggeun Kim bufnum = entry / FAT32BUFSIZE;
374c30a15e5SDonggeun Kim offset = entry - bufnum * FAT32BUFSIZE;
375c30a15e5SDonggeun Kim break;
376c30a15e5SDonggeun Kim case 16:
377c30a15e5SDonggeun Kim bufnum = entry / FAT16BUFSIZE;
378c30a15e5SDonggeun Kim offset = entry - bufnum * FAT16BUFSIZE;
379c30a15e5SDonggeun Kim break;
38049abbd9cSPhilipp Skadorov case 12:
38149abbd9cSPhilipp Skadorov bufnum = entry / FAT12BUFSIZE;
38249abbd9cSPhilipp Skadorov offset = entry - bufnum * FAT12BUFSIZE;
38349abbd9cSPhilipp Skadorov break;
384c30a15e5SDonggeun Kim default:
385c30a15e5SDonggeun Kim /* Unsupported FAT size */
386c30a15e5SDonggeun Kim return -1;
387c30a15e5SDonggeun Kim }
388c30a15e5SDonggeun Kim
389c30a15e5SDonggeun Kim /* Read a new block of FAT entries into the cache. */
390c30a15e5SDonggeun Kim if (bufnum != mydata->fatbufnum) {
391c30a15e5SDonggeun Kim int getsize = FATBUFBLOCKS;
392c30a15e5SDonggeun Kim __u8 *bufptr = mydata->fatbuf;
393c30a15e5SDonggeun Kim __u32 fatlength = mydata->fatlength;
394c30a15e5SDonggeun Kim __u32 startblock = bufnum * FATBUFBLOCKS;
395c30a15e5SDonggeun Kim
3966c1a8080SStefan Brüns /* Cap length if fatlength is not a multiple of FATBUFBLOCKS */
3976c1a8080SStefan Brüns if (startblock + getsize > fatlength)
3986c1a8080SStefan Brüns getsize = fatlength - startblock;
399c30a15e5SDonggeun Kim
4003c0ed9c3SStefan Brüns if (flush_dirty_fat_buffer(mydata) < 0)
401c30a15e5SDonggeun Kim return -1;
402c30a15e5SDonggeun Kim
4036c1a8080SStefan Brüns startblock += mydata->fat_sect;
4046c1a8080SStefan Brüns
405c30a15e5SDonggeun Kim if (disk_read(startblock, getsize, bufptr) < 0) {
406c30a15e5SDonggeun Kim debug("Error reading FAT blocks\n");
407c30a15e5SDonggeun Kim return -1;
408c30a15e5SDonggeun Kim }
409c30a15e5SDonggeun Kim mydata->fatbufnum = bufnum;
410c30a15e5SDonggeun Kim }
411c30a15e5SDonggeun Kim
4123c0ed9c3SStefan Brüns /* Mark as dirty */
4133c0ed9c3SStefan Brüns mydata->fat_dirty = 1;
4143c0ed9c3SStefan Brüns
415c30a15e5SDonggeun Kim /* Set the actual entry */
416c30a15e5SDonggeun Kim switch (mydata->fatsize) {
417c30a15e5SDonggeun Kim case 32:
418c30a15e5SDonggeun Kim ((__u32 *) mydata->fatbuf)[offset] = cpu_to_le32(entry_value);
419c30a15e5SDonggeun Kim break;
420c30a15e5SDonggeun Kim case 16:
421c30a15e5SDonggeun Kim ((__u16 *) mydata->fatbuf)[offset] = cpu_to_le16(entry_value);
422c30a15e5SDonggeun Kim break;
42349abbd9cSPhilipp Skadorov case 12:
42449abbd9cSPhilipp Skadorov off16 = (offset * 3) / 4;
42549abbd9cSPhilipp Skadorov
42649abbd9cSPhilipp Skadorov switch (offset & 0x3) {
42749abbd9cSPhilipp Skadorov case 0:
42849abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xfff;
42949abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff;
43049abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= val1;
43149abbd9cSPhilipp Skadorov break;
43249abbd9cSPhilipp Skadorov case 1:
43349abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xf;
43449abbd9cSPhilipp Skadorov val2 = (cpu_to_le16(entry_value) >> 4) & 0xff;
43549abbd9cSPhilipp Skadorov
43649abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xf000;
43749abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 12);
43849abbd9cSPhilipp Skadorov
43949abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xff;
44049abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
44149abbd9cSPhilipp Skadorov break;
44249abbd9cSPhilipp Skadorov case 2:
44349abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xff;
44449abbd9cSPhilipp Skadorov val2 = (cpu_to_le16(entry_value) >> 8) & 0xf;
44549abbd9cSPhilipp Skadorov
44649abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xff00;
44749abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 8);
44849abbd9cSPhilipp Skadorov
44949abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] &= ~0xf;
45049abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16 + 1] |= val2;
45149abbd9cSPhilipp Skadorov break;
45249abbd9cSPhilipp Skadorov case 3:
45349abbd9cSPhilipp Skadorov val1 = cpu_to_le16(entry_value) & 0xfff;
45449abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] &= ~0xfff0;
45549abbd9cSPhilipp Skadorov ((__u16 *)mydata->fatbuf)[off16] |= (val1 << 4);
45649abbd9cSPhilipp Skadorov break;
45749abbd9cSPhilipp Skadorov default:
45849abbd9cSPhilipp Skadorov break;
45949abbd9cSPhilipp Skadorov }
46049abbd9cSPhilipp Skadorov
46149abbd9cSPhilipp Skadorov break;
462c30a15e5SDonggeun Kim default:
463c30a15e5SDonggeun Kim return -1;
464c30a15e5SDonggeun Kim }
465c30a15e5SDonggeun Kim
466c30a15e5SDonggeun Kim return 0;
467c30a15e5SDonggeun Kim }
468c30a15e5SDonggeun Kim
469c30a15e5SDonggeun Kim /*
47049abbd9cSPhilipp Skadorov * Determine the next free cluster after 'entry' in a FAT (12/16/32) table
471ae1755beSStefan Brüns * and link it to 'entry'. EOC marker is not set on returned entry.
472c30a15e5SDonggeun Kim */
determine_fatent(fsdata * mydata,__u32 entry)473c30a15e5SDonggeun Kim static __u32 determine_fatent(fsdata *mydata, __u32 entry)
474c30a15e5SDonggeun Kim {
475c30a15e5SDonggeun Kim __u32 next_fat, next_entry = entry + 1;
476c30a15e5SDonggeun Kim
477c30a15e5SDonggeun Kim while (1) {
478b8948d2aSStefan Brüns next_fat = get_fatent(mydata, next_entry);
479c30a15e5SDonggeun Kim if (next_fat == 0) {
480ae1755beSStefan Brüns /* found free entry, link to entry */
481c30a15e5SDonggeun Kim set_fatent_value(mydata, entry, next_entry);
482c30a15e5SDonggeun Kim break;
483c30a15e5SDonggeun Kim }
484c30a15e5SDonggeun Kim next_entry++;
485c30a15e5SDonggeun Kim }
486c30a15e5SDonggeun Kim debug("FAT%d: entry: %08x, entry_value: %04x\n",
487c30a15e5SDonggeun Kim mydata->fatsize, entry, next_entry);
488c30a15e5SDonggeun Kim
489c30a15e5SDonggeun Kim return next_entry;
490c30a15e5SDonggeun Kim }
491c30a15e5SDonggeun Kim
492c30a15e5SDonggeun Kim /*
493c30a15e5SDonggeun Kim * Write at most 'size' bytes from 'buffer' into the specified cluster.
494c30a15e5SDonggeun Kim * Return 0 on success, -1 otherwise.
495c30a15e5SDonggeun Kim */
496c30a15e5SDonggeun Kim static int
set_cluster(fsdata * mydata,__u32 clustnum,__u8 * buffer,unsigned long size)497c30a15e5SDonggeun Kim set_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer,
498c30a15e5SDonggeun Kim unsigned long size)
499c30a15e5SDonggeun Kim {
5008133f43dSBenoît Thébaudeau __u32 idx = 0;
501c30a15e5SDonggeun Kim __u32 startsect;
5028133f43dSBenoît Thébaudeau int ret;
503c30a15e5SDonggeun Kim
504c30a15e5SDonggeun Kim if (clustnum > 0)
505*1d8c5dbbSRob Clark startsect = clust_to_sect(mydata, clustnum);
506c30a15e5SDonggeun Kim else
507c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect;
508c30a15e5SDonggeun Kim
509c30a15e5SDonggeun Kim debug("clustnum: %d, startsect: %d\n", clustnum, startsect);
510c30a15e5SDonggeun Kim
5118133f43dSBenoît Thébaudeau if ((unsigned long)buffer & (ARCH_DMA_MINALIGN - 1)) {
5128133f43dSBenoît Thébaudeau ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
5138133f43dSBenoît Thébaudeau
5148133f43dSBenoît Thébaudeau printf("FAT: Misaligned buffer address (%p)\n", buffer);
5158133f43dSBenoît Thébaudeau
5168133f43dSBenoît Thébaudeau while (size >= mydata->sect_size) {
5178133f43dSBenoît Thébaudeau memcpy(tmpbuf, buffer, mydata->sect_size);
5188133f43dSBenoît Thébaudeau ret = disk_write(startsect++, 1, tmpbuf);
5198133f43dSBenoît Thébaudeau if (ret != 1) {
5208133f43dSBenoît Thébaudeau debug("Error writing data (got %d)\n", ret);
521c30a15e5SDonggeun Kim return -1;
522c30a15e5SDonggeun Kim }
5238133f43dSBenoît Thébaudeau
5248133f43dSBenoît Thébaudeau buffer += mydata->sect_size;
5258133f43dSBenoît Thébaudeau size -= mydata->sect_size;
5266b8f185fSWu, Josh }
5278133f43dSBenoît Thébaudeau } else if (size >= mydata->sect_size) {
528c30a15e5SDonggeun Kim idx = size / mydata->sect_size;
5298133f43dSBenoît Thébaudeau ret = disk_write(startsect, idx, buffer);
5308133f43dSBenoît Thébaudeau if (ret != idx) {
5318133f43dSBenoît Thébaudeau debug("Error writing data (got %d)\n", ret);
532c30a15e5SDonggeun Kim return -1;
533c30a15e5SDonggeun Kim }
534c30a15e5SDonggeun Kim
5358133f43dSBenoît Thébaudeau startsect += idx;
5368133f43dSBenoît Thébaudeau idx *= mydata->sect_size;
5378133f43dSBenoît Thébaudeau buffer += idx;
5388133f43dSBenoît Thébaudeau size -= idx;
5398133f43dSBenoît Thébaudeau }
5408133f43dSBenoît Thébaudeau
5418133f43dSBenoît Thébaudeau if (size) {
5428133f43dSBenoît Thébaudeau ALLOC_CACHE_ALIGN_BUFFER(__u8, tmpbuf, mydata->sect_size);
5438133f43dSBenoît Thébaudeau
5448133f43dSBenoît Thébaudeau memcpy(tmpbuf, buffer, size);
5458133f43dSBenoît Thébaudeau ret = disk_write(startsect, 1, tmpbuf);
5468133f43dSBenoît Thébaudeau if (ret != 1) {
5478133f43dSBenoît Thébaudeau debug("Error writing data (got %d)\n", ret);
5488133f43dSBenoît Thébaudeau return -1;
5498133f43dSBenoît Thébaudeau }
550c30a15e5SDonggeun Kim }
551c30a15e5SDonggeun Kim
552c30a15e5SDonggeun Kim return 0;
553c30a15e5SDonggeun Kim }
554c30a15e5SDonggeun Kim
555c30a15e5SDonggeun Kim /*
556c30a15e5SDonggeun Kim * Find the first empty cluster
557c30a15e5SDonggeun Kim */
find_empty_cluster(fsdata * mydata)558c30a15e5SDonggeun Kim static int find_empty_cluster(fsdata *mydata)
559c30a15e5SDonggeun Kim {
560c30a15e5SDonggeun Kim __u32 fat_val, entry = 3;
561c30a15e5SDonggeun Kim
562c30a15e5SDonggeun Kim while (1) {
563b8948d2aSStefan Brüns fat_val = get_fatent(mydata, entry);
564c30a15e5SDonggeun Kim if (fat_val == 0)
565c30a15e5SDonggeun Kim break;
566c30a15e5SDonggeun Kim entry++;
567c30a15e5SDonggeun Kim }
568c30a15e5SDonggeun Kim
569c30a15e5SDonggeun Kim return entry;
570c30a15e5SDonggeun Kim }
571c30a15e5SDonggeun Kim
572c30a15e5SDonggeun Kim /*
573c30a15e5SDonggeun Kim * Write directory entries in 'get_dentfromdir_block' to block device
574c30a15e5SDonggeun Kim */
flush_dir_table(fsdata * mydata,dir_entry ** dentptr)575c30a15e5SDonggeun Kim static void flush_dir_table(fsdata *mydata, dir_entry **dentptr)
576c30a15e5SDonggeun Kim {
577c30a15e5SDonggeun Kim int dir_newclust = 0;
578c30a15e5SDonggeun Kim
579c30a15e5SDonggeun Kim if (set_cluster(mydata, dir_curclust,
580c30a15e5SDonggeun Kim get_dentfromdir_block,
581c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) {
582c30a15e5SDonggeun Kim printf("error: wrinting directory entry\n");
583c30a15e5SDonggeun Kim return;
584c30a15e5SDonggeun Kim }
585c30a15e5SDonggeun Kim dir_newclust = find_empty_cluster(mydata);
586c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_curclust, dir_newclust);
587c30a15e5SDonggeun Kim if (mydata->fatsize == 32)
588c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_newclust, 0xffffff8);
589c30a15e5SDonggeun Kim else if (mydata->fatsize == 16)
590c30a15e5SDonggeun Kim set_fatent_value(mydata, dir_newclust, 0xfff8);
59149abbd9cSPhilipp Skadorov else if (mydata->fatsize == 12)
59249abbd9cSPhilipp Skadorov set_fatent_value(mydata, dir_newclust, 0xff8);
593c30a15e5SDonggeun Kim
594c30a15e5SDonggeun Kim dir_curclust = dir_newclust;
595c30a15e5SDonggeun Kim
5963c0ed9c3SStefan Brüns if (flush_dirty_fat_buffer(mydata) < 0)
597c30a15e5SDonggeun Kim return;
598c30a15e5SDonggeun Kim
599c30a15e5SDonggeun Kim memset(get_dentfromdir_block, 0x00,
600c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size);
601c30a15e5SDonggeun Kim
602c30a15e5SDonggeun Kim *dentptr = (dir_entry *) get_dentfromdir_block;
603c30a15e5SDonggeun Kim }
604c30a15e5SDonggeun Kim
605c30a15e5SDonggeun Kim /*
606c30a15e5SDonggeun Kim * Set empty cluster from 'entry' to the end of a file
607c30a15e5SDonggeun Kim */
clear_fatent(fsdata * mydata,__u32 entry)608c30a15e5SDonggeun Kim static int clear_fatent(fsdata *mydata, __u32 entry)
609c30a15e5SDonggeun Kim {
610c30a15e5SDonggeun Kim __u32 fat_val;
611c30a15e5SDonggeun Kim
61249abbd9cSPhilipp Skadorov while (!CHECK_CLUST(entry, mydata->fatsize)) {
613b8948d2aSStefan Brüns fat_val = get_fatent(mydata, entry);
614c30a15e5SDonggeun Kim if (fat_val != 0)
615c30a15e5SDonggeun Kim set_fatent_value(mydata, entry, 0);
616c30a15e5SDonggeun Kim else
617c30a15e5SDonggeun Kim break;
618c30a15e5SDonggeun Kim
619c30a15e5SDonggeun Kim entry = fat_val;
620c30a15e5SDonggeun Kim }
621c30a15e5SDonggeun Kim
622c30a15e5SDonggeun Kim /* Flush fat buffer */
6233c0ed9c3SStefan Brüns if (flush_dirty_fat_buffer(mydata) < 0)
624c30a15e5SDonggeun Kim return -1;
625c30a15e5SDonggeun Kim
626c30a15e5SDonggeun Kim return 0;
627c30a15e5SDonggeun Kim }
628c30a15e5SDonggeun Kim
629c30a15e5SDonggeun Kim /*
630c30a15e5SDonggeun Kim * Write at most 'maxsize' bytes from 'buffer' into
631c30a15e5SDonggeun Kim * the file associated with 'dentptr'
6321ad0b98aSSuriyan Ramasami * Update the number of bytes written in *gotsize and return 0
6331ad0b98aSSuriyan Ramasami * or return -1 on fatal errors.
634c30a15e5SDonggeun Kim */
635c30a15e5SDonggeun Kim static int
set_contents(fsdata * mydata,dir_entry * dentptr,__u8 * buffer,loff_t maxsize,loff_t * gotsize)636c30a15e5SDonggeun Kim set_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
6371ad0b98aSSuriyan Ramasami loff_t maxsize, loff_t *gotsize)
638c30a15e5SDonggeun Kim {
6391ad0b98aSSuriyan Ramasami loff_t filesize = FAT2CPU32(dentptr->size);
640c30a15e5SDonggeun Kim unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
641c30a15e5SDonggeun Kim __u32 curclust = START(dentptr);
642c30a15e5SDonggeun Kim __u32 endclust = 0, newclust = 0;
6431ad0b98aSSuriyan Ramasami loff_t actsize;
644c30a15e5SDonggeun Kim
6451ad0b98aSSuriyan Ramasami *gotsize = 0;
6461ad0b98aSSuriyan Ramasami debug("Filesize: %llu bytes\n", filesize);
647c30a15e5SDonggeun Kim
648c30a15e5SDonggeun Kim if (maxsize > 0 && filesize > maxsize)
649c30a15e5SDonggeun Kim filesize = maxsize;
650c30a15e5SDonggeun Kim
6511ad0b98aSSuriyan Ramasami debug("%llu bytes\n", filesize);
652c30a15e5SDonggeun Kim
6531254b44aSBenoît Thébaudeau if (!curclust) {
6541254b44aSBenoît Thébaudeau if (filesize) {
6551254b44aSBenoît Thébaudeau debug("error: nonempty clusterless file!\n");
6561254b44aSBenoît Thébaudeau return -1;
6571254b44aSBenoît Thébaudeau }
6581254b44aSBenoît Thébaudeau return 0;
6591254b44aSBenoît Thébaudeau }
6601254b44aSBenoît Thébaudeau
661c30a15e5SDonggeun Kim actsize = bytesperclust;
662c30a15e5SDonggeun Kim endclust = curclust;
663c30a15e5SDonggeun Kim do {
664c30a15e5SDonggeun Kim /* search for consecutive clusters */
665c30a15e5SDonggeun Kim while (actsize < filesize) {
666c30a15e5SDonggeun Kim newclust = determine_fatent(mydata, endclust);
667c30a15e5SDonggeun Kim
668c30a15e5SDonggeun Kim if ((newclust - 1) != endclust)
669c30a15e5SDonggeun Kim goto getit;
670c30a15e5SDonggeun Kim
671c30a15e5SDonggeun Kim if (CHECK_CLUST(newclust, mydata->fatsize)) {
6725e1a860eSBenoît Thébaudeau debug("newclust: 0x%x\n", newclust);
673c30a15e5SDonggeun Kim debug("Invalid FAT entry\n");
6741ad0b98aSSuriyan Ramasami return 0;
675c30a15e5SDonggeun Kim }
676c30a15e5SDonggeun Kim endclust = newclust;
677c30a15e5SDonggeun Kim actsize += bytesperclust;
678c30a15e5SDonggeun Kim }
679c30a15e5SDonggeun Kim
680c30a15e5SDonggeun Kim /* set remaining bytes */
681c30a15e5SDonggeun Kim actsize = filesize;
6821d7f2eceSBenoît Thébaudeau if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
683c30a15e5SDonggeun Kim debug("error: writing cluster\n");
684c30a15e5SDonggeun Kim return -1;
685c30a15e5SDonggeun Kim }
6861ad0b98aSSuriyan Ramasami *gotsize += actsize;
687c30a15e5SDonggeun Kim
688c30a15e5SDonggeun Kim /* Mark end of file in FAT */
68949abbd9cSPhilipp Skadorov if (mydata->fatsize == 12)
69049abbd9cSPhilipp Skadorov newclust = 0xfff;
69149abbd9cSPhilipp Skadorov else if (mydata->fatsize == 16)
692c30a15e5SDonggeun Kim newclust = 0xffff;
693c30a15e5SDonggeun Kim else if (mydata->fatsize == 32)
694c30a15e5SDonggeun Kim newclust = 0xfffffff;
695c30a15e5SDonggeun Kim set_fatent_value(mydata, endclust, newclust);
696c30a15e5SDonggeun Kim
6971ad0b98aSSuriyan Ramasami return 0;
698c30a15e5SDonggeun Kim getit:
699c30a15e5SDonggeun Kim if (set_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
700c30a15e5SDonggeun Kim debug("error: writing cluster\n");
701c30a15e5SDonggeun Kim return -1;
702c30a15e5SDonggeun Kim }
7031ad0b98aSSuriyan Ramasami *gotsize += actsize;
704c30a15e5SDonggeun Kim filesize -= actsize;
705c30a15e5SDonggeun Kim buffer += actsize;
706c30a15e5SDonggeun Kim
7075e1a860eSBenoît Thébaudeau if (CHECK_CLUST(newclust, mydata->fatsize)) {
7085e1a860eSBenoît Thébaudeau debug("newclust: 0x%x\n", newclust);
709c30a15e5SDonggeun Kim debug("Invalid FAT entry\n");
7101ad0b98aSSuriyan Ramasami return 0;
711c30a15e5SDonggeun Kim }
712c30a15e5SDonggeun Kim actsize = bytesperclust;
713c30a15e5SDonggeun Kim curclust = endclust = newclust;
714c30a15e5SDonggeun Kim } while (1);
715c30a15e5SDonggeun Kim }
716c30a15e5SDonggeun Kim
717c30a15e5SDonggeun Kim /*
7181254b44aSBenoît Thébaudeau * Set start cluster in directory entry
719c30a15e5SDonggeun Kim */
set_start_cluster(const fsdata * mydata,dir_entry * dentptr,__u32 start_cluster)7201254b44aSBenoît Thébaudeau static void set_start_cluster(const fsdata *mydata, dir_entry *dentptr,
7211254b44aSBenoît Thébaudeau __u32 start_cluster)
722c30a15e5SDonggeun Kim {
723c30a15e5SDonggeun Kim if (mydata->fatsize == 32)
724c30a15e5SDonggeun Kim dentptr->starthi =
725c30a15e5SDonggeun Kim cpu_to_le16((start_cluster & 0xffff0000) >> 16);
726c30a15e5SDonggeun Kim dentptr->start = cpu_to_le16(start_cluster & 0xffff);
7271254b44aSBenoît Thébaudeau }
7281254b44aSBenoît Thébaudeau
7291254b44aSBenoît Thébaudeau /*
7301254b44aSBenoît Thébaudeau * Fill dir_entry
7311254b44aSBenoît Thébaudeau */
fill_dentry(fsdata * mydata,dir_entry * dentptr,const char * filename,__u32 start_cluster,__u32 size,__u8 attr)7321254b44aSBenoît Thébaudeau static void fill_dentry(fsdata *mydata, dir_entry *dentptr,
7331254b44aSBenoît Thébaudeau const char *filename, __u32 start_cluster, __u32 size, __u8 attr)
7341254b44aSBenoît Thébaudeau {
7351254b44aSBenoît Thébaudeau set_start_cluster(mydata, dentptr, start_cluster);
736c30a15e5SDonggeun Kim dentptr->size = cpu_to_le32(size);
737c30a15e5SDonggeun Kim
738c30a15e5SDonggeun Kim dentptr->attr = attr;
739c30a15e5SDonggeun Kim
740c30a15e5SDonggeun Kim set_name(dentptr, filename);
741c30a15e5SDonggeun Kim }
742c30a15e5SDonggeun Kim
743c30a15e5SDonggeun Kim /*
744c30a15e5SDonggeun Kim * Check whether adding a file makes the file system to
745c30a15e5SDonggeun Kim * exceed the size of the block device
746c30a15e5SDonggeun Kim * Return -1 when overflow occurs, otherwise return 0
747c30a15e5SDonggeun Kim */
check_overflow(fsdata * mydata,__u32 clustnum,loff_t size)7481ad0b98aSSuriyan Ramasami static int check_overflow(fsdata *mydata, __u32 clustnum, loff_t size)
749c30a15e5SDonggeun Kim {
7509e374e7bSTom Rini __u32 startsect, sect_num, offset;
751c30a15e5SDonggeun Kim
752c30a15e5SDonggeun Kim if (clustnum > 0) {
753*1d8c5dbbSRob Clark startsect = clust_to_sect(mydata, clustnum);
754c30a15e5SDonggeun Kim } else {
755c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect;
756c30a15e5SDonggeun Kim }
757c30a15e5SDonggeun Kim
7589e374e7bSTom Rini sect_num = div_u64_rem(size, mydata->sect_size, &offset);
7599e374e7bSTom Rini
7609e374e7bSTom Rini if (offset != 0)
761c30a15e5SDonggeun Kim sect_num++;
762c30a15e5SDonggeun Kim
76376216211SReno Farnesi if (startsect + sect_num > total_sector)
764c30a15e5SDonggeun Kim return -1;
765c30a15e5SDonggeun Kim return 0;
766c30a15e5SDonggeun Kim }
767c30a15e5SDonggeun Kim
768c30a15e5SDonggeun Kim /*
769c30a15e5SDonggeun Kim * Check if adding several entries exceed one cluster boundary
770c30a15e5SDonggeun Kim */
is_next_clust(fsdata * mydata,dir_entry * dentptr)771c30a15e5SDonggeun Kim static int is_next_clust(fsdata *mydata, dir_entry *dentptr)
772c30a15e5SDonggeun Kim {
773c30a15e5SDonggeun Kim int cur_position;
774c30a15e5SDonggeun Kim
775c30a15e5SDonggeun Kim cur_position = (__u8 *)dentptr - get_dentfromdir_block;
776c30a15e5SDonggeun Kim
777c30a15e5SDonggeun Kim if (cur_position >= mydata->clust_size * mydata->sect_size)
778c30a15e5SDonggeun Kim return 1;
779c30a15e5SDonggeun Kim else
780c30a15e5SDonggeun Kim return 0;
781c30a15e5SDonggeun Kim }
782c30a15e5SDonggeun Kim
783c30a15e5SDonggeun Kim static dir_entry *empty_dentptr;
784c30a15e5SDonggeun Kim /*
785c30a15e5SDonggeun Kim * Find a directory entry based on filename or start cluster number
786c30a15e5SDonggeun Kim * If the directory entry is not found,
787c30a15e5SDonggeun Kim * the new position for writing a directory entry will be returned
788c30a15e5SDonggeun Kim */
find_directory_entry(fsdata * mydata,int startsect,char * filename,dir_entry * retdent,__u32 start)789c30a15e5SDonggeun Kim static dir_entry *find_directory_entry(fsdata *mydata, int startsect,
790c30a15e5SDonggeun Kim char *filename, dir_entry *retdent, __u32 start)
791c30a15e5SDonggeun Kim {
792*1d8c5dbbSRob Clark __u32 curclust = sect_to_clust(mydata, startsect);
793c30a15e5SDonggeun Kim
794c30a15e5SDonggeun Kim debug("get_dentfromdir: %s\n", filename);
795c30a15e5SDonggeun Kim
796c30a15e5SDonggeun Kim while (1) {
797c30a15e5SDonggeun Kim dir_entry *dentptr;
798c30a15e5SDonggeun Kim
799c30a15e5SDonggeun Kim int i;
800c30a15e5SDonggeun Kim
801c30a15e5SDonggeun Kim if (get_cluster(mydata, curclust, get_dentfromdir_block,
802c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size) != 0) {
803c30a15e5SDonggeun Kim printf("Error: reading directory block\n");
804c30a15e5SDonggeun Kim return NULL;
805c30a15e5SDonggeun Kim }
806c30a15e5SDonggeun Kim
807c30a15e5SDonggeun Kim dentptr = (dir_entry *)get_dentfromdir_block;
808c30a15e5SDonggeun Kim
809c30a15e5SDonggeun Kim dir_curclust = curclust;
810c30a15e5SDonggeun Kim
811c30a15e5SDonggeun Kim for (i = 0; i < DIRENTSPERCLUST; i++) {
812c30a15e5SDonggeun Kim char s_name[14], l_name[VFAT_MAXLEN_BYTES];
813c30a15e5SDonggeun Kim
814c30a15e5SDonggeun Kim l_name[0] = '\0';
815c30a15e5SDonggeun Kim if (dentptr->name[0] == DELETED_FLAG) {
816c30a15e5SDonggeun Kim dentptr++;
817c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr))
818c30a15e5SDonggeun Kim break;
819c30a15e5SDonggeun Kim continue;
820c30a15e5SDonggeun Kim }
821c30a15e5SDonggeun Kim if ((dentptr->attr & ATTR_VOLUME)) {
822cb940c7eSRichard Genoud if (vfat_enabled &&
823cb940c7eSRichard Genoud (dentptr->attr & ATTR_VFAT) &&
824c30a15e5SDonggeun Kim (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
825c30a15e5SDonggeun Kim get_long_file_name(mydata, curclust,
826c30a15e5SDonggeun Kim get_dentfromdir_block,
827c30a15e5SDonggeun Kim &dentptr, l_name);
828c30a15e5SDonggeun Kim debug("vfatname: |%s|\n", l_name);
829cb940c7eSRichard Genoud } else {
830c30a15e5SDonggeun Kim /* Volume label or VFAT entry */
831c30a15e5SDonggeun Kim dentptr++;
832c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr))
833c30a15e5SDonggeun Kim break;
834c30a15e5SDonggeun Kim continue;
835c30a15e5SDonggeun Kim }
836c30a15e5SDonggeun Kim }
837c30a15e5SDonggeun Kim if (dentptr->name[0] == 0) {
838c30a15e5SDonggeun Kim debug("Dentname == NULL - %d\n", i);
839c30a15e5SDonggeun Kim empty_dentptr = dentptr;
840c30a15e5SDonggeun Kim return NULL;
841c30a15e5SDonggeun Kim }
842c30a15e5SDonggeun Kim
843c30a15e5SDonggeun Kim get_name(dentptr, s_name);
844c30a15e5SDonggeun Kim
845c30a15e5SDonggeun Kim if (strcmp(filename, s_name)
846c30a15e5SDonggeun Kim && strcmp(filename, l_name)) {
847c30a15e5SDonggeun Kim debug("Mismatch: |%s|%s|\n",
848c30a15e5SDonggeun Kim s_name, l_name);
849c30a15e5SDonggeun Kim dentptr++;
850c30a15e5SDonggeun Kim if (is_next_clust(mydata, dentptr))
851c30a15e5SDonggeun Kim break;
852c30a15e5SDonggeun Kim continue;
853c30a15e5SDonggeun Kim }
854c30a15e5SDonggeun Kim
855c30a15e5SDonggeun Kim memcpy(retdent, dentptr, sizeof(dir_entry));
856c30a15e5SDonggeun Kim
857c30a15e5SDonggeun Kim debug("DentName: %s", s_name);
858c30a15e5SDonggeun Kim debug(", start: 0x%x", START(dentptr));
859c30a15e5SDonggeun Kim debug(", size: 0x%x %s\n",
860c30a15e5SDonggeun Kim FAT2CPU32(dentptr->size),
861c30a15e5SDonggeun Kim (dentptr->attr & ATTR_DIR) ?
862c30a15e5SDonggeun Kim "(DIR)" : "");
863c30a15e5SDonggeun Kim
864c30a15e5SDonggeun Kim return dentptr;
865c30a15e5SDonggeun Kim }
866c30a15e5SDonggeun Kim
867dd6d7967SWu, Josh /*
868dd6d7967SWu, Josh * In FAT16/12, the root dir is locate before data area, shows
869dd6d7967SWu, Josh * in following:
870dd6d7967SWu, Josh * -------------------------------------------------------------
871dd6d7967SWu, Josh * | Boot | FAT1 & 2 | Root dir | Data (start from cluster #2) |
872dd6d7967SWu, Josh * -------------------------------------------------------------
873dd6d7967SWu, Josh *
874dd6d7967SWu, Josh * As a result if curclust is in Root dir, it is a negative
875dd6d7967SWu, Josh * number or 0, 1.
876dd6d7967SWu, Josh *
877dd6d7967SWu, Josh */
878dd6d7967SWu, Josh if (mydata->fatsize != 32 && (int)curclust <= 1) {
879dd6d7967SWu, Josh /* Current clust is in root dir, set to next clust */
880dd6d7967SWu, Josh curclust++;
881dd6d7967SWu, Josh if ((int)curclust <= 1)
882dd6d7967SWu, Josh continue; /* continue to find */
883dd6d7967SWu, Josh
884dd6d7967SWu, Josh /* Reach the end of root dir */
885dd6d7967SWu, Josh empty_dentptr = dentptr;
886dd6d7967SWu, Josh return NULL;
887dd6d7967SWu, Josh }
888dd6d7967SWu, Josh
889b8948d2aSStefan Brüns curclust = get_fatent(mydata, dir_curclust);
8902e98f708SWu, Josh if (IS_LAST_CLUST(curclust, mydata->fatsize)) {
891c30a15e5SDonggeun Kim empty_dentptr = dentptr;
892c30a15e5SDonggeun Kim return NULL;
893c30a15e5SDonggeun Kim }
894c30a15e5SDonggeun Kim if (CHECK_CLUST(curclust, mydata->fatsize)) {
895c30a15e5SDonggeun Kim debug("curclust: 0x%x\n", curclust);
896c30a15e5SDonggeun Kim debug("Invalid FAT entry\n");
897c30a15e5SDonggeun Kim return NULL;
898c30a15e5SDonggeun Kim }
899c30a15e5SDonggeun Kim }
900c30a15e5SDonggeun Kim
901c30a15e5SDonggeun Kim return NULL;
902c30a15e5SDonggeun Kim }
903c30a15e5SDonggeun Kim
do_fat_write(const char * filename,void * buffer,loff_t size,loff_t * actwrite)9041ad0b98aSSuriyan Ramasami static int do_fat_write(const char *filename, void *buffer, loff_t size,
9051ad0b98aSSuriyan Ramasami loff_t *actwrite)
906c30a15e5SDonggeun Kim {
907c30a15e5SDonggeun Kim dir_entry *dentptr, *retdent;
908c30a15e5SDonggeun Kim __u32 startsect;
909c30a15e5SDonggeun Kim __u32 start_cluster;
910c30a15e5SDonggeun Kim boot_sector bs;
911c30a15e5SDonggeun Kim volume_info volinfo;
912c30a15e5SDonggeun Kim fsdata datablock;
913c30a15e5SDonggeun Kim fsdata *mydata = &datablock;
914c30a15e5SDonggeun Kim int cursect;
915bf6b6af7SAnatolij Gustschin int ret = -1, name_len;
916c30a15e5SDonggeun Kim char l_filename[VFAT_MAXLEN_BYTES];
917c30a15e5SDonggeun Kim
9181ad0b98aSSuriyan Ramasami *actwrite = size;
919c30a15e5SDonggeun Kim dir_curclust = 0;
920c30a15e5SDonggeun Kim
921c30a15e5SDonggeun Kim if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
922c30a15e5SDonggeun Kim debug("error: reading boot sector\n");
923c30a15e5SDonggeun Kim return -1;
924c30a15e5SDonggeun Kim }
925c30a15e5SDonggeun Kim
926c30a15e5SDonggeun Kim total_sector = bs.total_sect;
927c30a15e5SDonggeun Kim if (total_sector == 0)
928e04350d2SSteve Rae total_sector = (int)cur_part_info.size; /* cast of lbaint_t */
929c30a15e5SDonggeun Kim
930c30a15e5SDonggeun Kim if (mydata->fatsize == 32)
931c30a15e5SDonggeun Kim mydata->fatlength = bs.fat32_length;
932c30a15e5SDonggeun Kim else
933c30a15e5SDonggeun Kim mydata->fatlength = bs.fat_length;
934c30a15e5SDonggeun Kim
935c30a15e5SDonggeun Kim mydata->fat_sect = bs.reserved;
936c30a15e5SDonggeun Kim
937c30a15e5SDonggeun Kim cursect = mydata->rootdir_sect
938c30a15e5SDonggeun Kim = mydata->fat_sect + mydata->fatlength * bs.fats;
939627182eaSDonggeun Kim num_of_fats = bs.fats;
940c30a15e5SDonggeun Kim
941c30a15e5SDonggeun Kim mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
942c30a15e5SDonggeun Kim mydata->clust_size = bs.cluster_size;
943c30a15e5SDonggeun Kim
944c30a15e5SDonggeun Kim if (mydata->fatsize == 32) {
945c30a15e5SDonggeun Kim mydata->data_begin = mydata->rootdir_sect -
946c30a15e5SDonggeun Kim (mydata->clust_size * 2);
947c30a15e5SDonggeun Kim } else {
948c30a15e5SDonggeun Kim int rootdir_size;
949c30a15e5SDonggeun Kim
950c30a15e5SDonggeun Kim rootdir_size = ((bs.dir_entries[1] * (int)256 +
951c30a15e5SDonggeun Kim bs.dir_entries[0]) *
952c30a15e5SDonggeun Kim sizeof(dir_entry)) /
953c30a15e5SDonggeun Kim mydata->sect_size;
954c30a15e5SDonggeun Kim mydata->data_begin = mydata->rootdir_sect +
955c30a15e5SDonggeun Kim rootdir_size -
956c30a15e5SDonggeun Kim (mydata->clust_size * 2);
957c30a15e5SDonggeun Kim }
958c30a15e5SDonggeun Kim
959c30a15e5SDonggeun Kim mydata->fatbufnum = -1;
9603c0ed9c3SStefan Brüns mydata->fat_dirty = 0;
9618abd053cSNobuhiro Iwamatsu mydata->fatbuf = memalign(ARCH_DMA_MINALIGN, FATBUFSIZE);
962c30a15e5SDonggeun Kim if (mydata->fatbuf == NULL) {
963c30a15e5SDonggeun Kim debug("Error: allocating memory\n");
964c30a15e5SDonggeun Kim return -1;
965c30a15e5SDonggeun Kim }
966c30a15e5SDonggeun Kim
967c30a15e5SDonggeun Kim if (disk_read(cursect,
968c30a15e5SDonggeun Kim (mydata->fatsize == 32) ?
969c30a15e5SDonggeun Kim (mydata->clust_size) :
9701170e634SBenoît Thébaudeau PREFETCH_BLOCKS, do_fat_read_at_block) < 0) {
971c30a15e5SDonggeun Kim debug("Error: reading rootdir block\n");
972c30a15e5SDonggeun Kim goto exit;
973c30a15e5SDonggeun Kim }
9741170e634SBenoît Thébaudeau dentptr = (dir_entry *) do_fat_read_at_block;
975c30a15e5SDonggeun Kim
976c30a15e5SDonggeun Kim name_len = strlen(filename);
9778506eb8dSAnatolij Gustschin if (name_len >= VFAT_MAXLEN_BYTES)
9788506eb8dSAnatolij Gustschin name_len = VFAT_MAXLEN_BYTES - 1;
9798506eb8dSAnatolij Gustschin
980c30a15e5SDonggeun Kim memcpy(l_filename, filename, name_len);
9818506eb8dSAnatolij Gustschin l_filename[name_len] = 0; /* terminate the string */
982271eb208SRob Clark downcase(l_filename, INT_MAX);
983c30a15e5SDonggeun Kim
984c30a15e5SDonggeun Kim startsect = mydata->rootdir_sect;
985c30a15e5SDonggeun Kim retdent = find_directory_entry(mydata, startsect,
986c30a15e5SDonggeun Kim l_filename, dentptr, 0);
987c30a15e5SDonggeun Kim if (retdent) {
988c30a15e5SDonggeun Kim /* Update file size and start_cluster in a directory entry */
989c30a15e5SDonggeun Kim retdent->size = cpu_to_le32(size);
990e876be4bSBenoît Thébaudeau start_cluster = START(retdent);
991c30a15e5SDonggeun Kim
9921254b44aSBenoît Thébaudeau if (start_cluster) {
9931254b44aSBenoît Thébaudeau if (size) {
9941254b44aSBenoît Thébaudeau ret = check_overflow(mydata, start_cluster,
9951254b44aSBenoît Thébaudeau size);
996c30a15e5SDonggeun Kim if (ret) {
9971ad0b98aSSuriyan Ramasami printf("Error: %llu overflow\n", size);
998c30a15e5SDonggeun Kim goto exit;
999c30a15e5SDonggeun Kim }
10001254b44aSBenoît Thébaudeau }
1001c30a15e5SDonggeun Kim
1002c30a15e5SDonggeun Kim ret = clear_fatent(mydata, start_cluster);
1003c30a15e5SDonggeun Kim if (ret) {
1004c30a15e5SDonggeun Kim printf("Error: clearing FAT entries\n");
1005c30a15e5SDonggeun Kim goto exit;
1006c30a15e5SDonggeun Kim }
1007c30a15e5SDonggeun Kim
10081254b44aSBenoît Thébaudeau if (!size)
10091254b44aSBenoît Thébaudeau set_start_cluster(mydata, retdent, 0);
10101254b44aSBenoît Thébaudeau } else if (size) {
1011c30a15e5SDonggeun Kim ret = start_cluster = find_empty_cluster(mydata);
1012c30a15e5SDonggeun Kim if (ret < 0) {
1013c30a15e5SDonggeun Kim printf("Error: finding empty cluster\n");
1014c30a15e5SDonggeun Kim goto exit;
1015c30a15e5SDonggeun Kim }
1016c30a15e5SDonggeun Kim
1017c30a15e5SDonggeun Kim ret = check_overflow(mydata, start_cluster, size);
1018c30a15e5SDonggeun Kim if (ret) {
10191ad0b98aSSuriyan Ramasami printf("Error: %llu overflow\n", size);
1020c30a15e5SDonggeun Kim goto exit;
1021c30a15e5SDonggeun Kim }
1022c30a15e5SDonggeun Kim
10231254b44aSBenoît Thébaudeau set_start_cluster(mydata, retdent, start_cluster);
10241254b44aSBenoît Thébaudeau }
10251254b44aSBenoît Thébaudeau } else {
10261254b44aSBenoît Thébaudeau /* Set short name to set alias checksum field in dir_slot */
10271254b44aSBenoît Thébaudeau set_name(empty_dentptr, filename);
10281254b44aSBenoît Thébaudeau fill_dir_slot(mydata, &empty_dentptr, filename);
10291254b44aSBenoît Thébaudeau
10301254b44aSBenoît Thébaudeau if (size) {
10311254b44aSBenoît Thébaudeau ret = start_cluster = find_empty_cluster(mydata);
10321254b44aSBenoît Thébaudeau if (ret < 0) {
10331254b44aSBenoît Thébaudeau printf("Error: finding empty cluster\n");
10341254b44aSBenoît Thébaudeau goto exit;
10351254b44aSBenoît Thébaudeau }
10361254b44aSBenoît Thébaudeau
10371254b44aSBenoît Thébaudeau ret = check_overflow(mydata, start_cluster, size);
10381254b44aSBenoît Thébaudeau if (ret) {
10391254b44aSBenoît Thébaudeau printf("Error: %llu overflow\n", size);
10401254b44aSBenoît Thébaudeau goto exit;
10411254b44aSBenoît Thébaudeau }
10421254b44aSBenoît Thébaudeau } else {
10431254b44aSBenoît Thébaudeau start_cluster = 0;
10441254b44aSBenoît Thébaudeau }
10451254b44aSBenoît Thébaudeau
1046c30a15e5SDonggeun Kim /* Set attribute as archieve for regular file */
1047c30a15e5SDonggeun Kim fill_dentry(mydata, empty_dentptr, filename,
1048c30a15e5SDonggeun Kim start_cluster, size, 0x20);
1049c30a15e5SDonggeun Kim
1050e876be4bSBenoît Thébaudeau retdent = empty_dentptr;
1051e876be4bSBenoît Thébaudeau }
1052e876be4bSBenoît Thébaudeau
1053e876be4bSBenoît Thébaudeau ret = set_contents(mydata, retdent, buffer, size, actwrite);
10548506eb8dSAnatolij Gustschin if (ret < 0) {
1055c30a15e5SDonggeun Kim printf("Error: writing contents\n");
1056c30a15e5SDonggeun Kim goto exit;
1057c30a15e5SDonggeun Kim }
10581ad0b98aSSuriyan Ramasami debug("attempt to write 0x%llx bytes\n", *actwrite);
1059c30a15e5SDonggeun Kim
1060c30a15e5SDonggeun Kim /* Flush fat buffer */
10613c0ed9c3SStefan Brüns ret = flush_dirty_fat_buffer(mydata);
1062c30a15e5SDonggeun Kim if (ret) {
1063c30a15e5SDonggeun Kim printf("Error: flush fat buffer\n");
1064c30a15e5SDonggeun Kim goto exit;
1065c30a15e5SDonggeun Kim }
1066c30a15e5SDonggeun Kim
1067c30a15e5SDonggeun Kim /* Write directory table to device */
1068e876be4bSBenoît Thébaudeau ret = set_cluster(mydata, dir_curclust, get_dentfromdir_block,
1069c30a15e5SDonggeun Kim mydata->clust_size * mydata->sect_size);
1070e876be4bSBenoît Thébaudeau if (ret)
1071c30a15e5SDonggeun Kim printf("Error: writing directory entry\n");
1072c30a15e5SDonggeun Kim
1073c30a15e5SDonggeun Kim exit:
1074c30a15e5SDonggeun Kim free(mydata->fatbuf);
10751ad0b98aSSuriyan Ramasami return ret;
1076c30a15e5SDonggeun Kim }
1077c30a15e5SDonggeun Kim
file_fat_write(const char * filename,void * buffer,loff_t offset,loff_t maxsize,loff_t * actwrite)10781ad0b98aSSuriyan Ramasami int file_fat_write(const char *filename, void *buffer, loff_t offset,
10791ad0b98aSSuriyan Ramasami loff_t maxsize, loff_t *actwrite)
1080c30a15e5SDonggeun Kim {
10811ad0b98aSSuriyan Ramasami if (offset != 0) {
10820af49b95SVagrant Cascadian printf("Error: non zero offset is currently not supported.\n");
10831ad0b98aSSuriyan Ramasami return -1;
10841ad0b98aSSuriyan Ramasami }
10851ad0b98aSSuriyan Ramasami
1086c30a15e5SDonggeun Kim printf("writing %s\n", filename);
10871ad0b98aSSuriyan Ramasami return do_fat_write(filename, buffer, maxsize, actwrite);
1088c30a15e5SDonggeun Kim }
1089