]>
Commit | Line | Data |
---|---|---|
71f95118 WD |
1 | /* |
2 | * fat.c | |
3 | * | |
4 | * R/O (V)FAT 12/16/32 filesystem implementation by Marcus Sundberg | |
5 | * | |
6 | * 2002-07-28 - rjones@nexus-tech.net - ported to ppcboot v1.1.6 | |
7 | * 2003-03-10 - kharris@nexus-tech.net - ported to uboot | |
8 | * | |
9 | * See file CREDITS for list of people who contributed to this | |
10 | * project. | |
11 | * | |
12 | * This program is free software; you can redistribute it and/or | |
13 | * modify it under the terms of the GNU General Public License as | |
14 | * published by the Free Software Foundation; either version 2 of | |
15 | * the License, or (at your option) any later version. | |
16 | * | |
17 | * This program is distributed in the hope that it will be useful, | |
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
20 | * GNU General Public License for more details. | |
21 | * | |
22 | * You should have received a copy of the GNU General Public License | |
23 | * along with this program; if not, write to the Free Software | |
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, | |
25 | * MA 02111-1307 USA | |
26 | */ | |
27 | ||
28 | #include <common.h> | |
29 | #include <config.h> | |
30 | #include <fat.h> | |
31 | #include <asm/byteorder.h> | |
7205e407 | 32 | #include <part.h> |
71f95118 | 33 | |
71f95118 WD |
34 | /* |
35 | * Convert a string to lowercase. | |
36 | */ | |
37 | static void | |
38 | downcase(char *str) | |
39 | { | |
40 | while (*str != '\0') { | |
41 | TOLOWER(*str); | |
42 | str++; | |
43 | } | |
44 | } | |
45 | ||
7205e407 WD |
46 | static block_dev_desc_t *cur_dev = NULL; |
47 | static unsigned long part_offset = 0; | |
48 | static int cur_part = 1; | |
49 | ||
50 | #define DOS_PART_TBL_OFFSET 0x1be | |
51 | #define DOS_PART_MAGIC_OFFSET 0x1fe | |
52 | #define DOS_FS_TYPE_OFFSET 0x36 | |
71f95118 WD |
53 | |
54 | int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr) | |
55 | { | |
7205e407 WD |
56 | startblock += part_offset; |
57 | if (cur_dev == NULL) | |
58 | return -1; | |
59 | if (cur_dev->block_read) { | |
3e3b9569 PP |
60 | return cur_dev->block_read (cur_dev->dev |
61 | , startblock, getsize, (unsigned long *)bufptr); | |
71f95118 WD |
62 | } |
63 | return -1; | |
64 | } | |
65 | ||
66 | ||
67 | int | |
7205e407 | 68 | fat_register_device(block_dev_desc_t *dev_desc, int part_no) |
71f95118 | 69 | { |
7205e407 | 70 | unsigned char buffer[SECTOR_SIZE]; |
566a494f | 71 | disk_partition_t info; |
7205e407 WD |
72 | |
73 | if (!dev_desc->block_read) | |
74 | return -1; | |
566a494f | 75 | cur_dev = dev_desc; |
7205e407 WD |
76 | /* check if we have a MBR (on floppies we have only a PBR) */ |
77 | if (dev_desc->block_read (dev_desc->dev, 0, 1, (ulong *) buffer) != 1) { | |
78 | printf ("** Can't read from device %d **\n", dev_desc->dev); | |
79 | return -1; | |
80 | } | |
81 | if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 || | |
82 | buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) { | |
83 | /* no signature found */ | |
84 | return -1; | |
85 | } | |
dd60d122 | 86 | #if (defined(CONFIG_CMD_IDE) || \ |
75eb82ec | 87 | defined(CONFIG_CMD_MG_DISK) || \ |
8c5170a7 | 88 | defined(CONFIG_CMD_SATA) || \ |
dd60d122 JL |
89 | defined(CONFIG_CMD_SCSI) || \ |
90 | defined(CONFIG_CMD_USB) || \ | |
02df4a27 AF |
91 | defined(CONFIG_MMC) || \ |
92 | defined(CONFIG_SYSTEMACE) ) | |
93 | /* First we assume, there is a MBR */ | |
94 | if (!get_partition_info (dev_desc, part_no, &info)) { | |
95 | part_offset = info.start; | |
96 | cur_part = part_no; | |
97 | } else if (!strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3)) { | |
98 | /* ok, we assume we are on a PBR only */ | |
99 | cur_part = 1; | |
100 | part_offset = 0; | |
101 | } else { | |
102 | printf ("** Partition %d not valid on device %d **\n", | |
bf1060ea | 103 | part_no, dev_desc->dev); |
02df4a27 AF |
104 | return -1; |
105 | } | |
106 | ||
7205e407 | 107 | #else |
02df4a27 AF |
108 | if (!strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET],"FAT",3)) { |
109 | /* ok, we assume we are on a PBR only */ | |
110 | cur_part = 1; | |
111 | part_offset = 0; | |
112 | info.start = part_offset; | |
113 | } else { | |
114 | /* FIXME we need to determine the start block of the | |
115 | * partition where the DOS FS resides. This can be done | |
116 | * by using the get_partition_info routine. For this | |
117 | * purpose the libpart must be included. | |
118 | */ | |
119 | part_offset = 32; | |
120 | cur_part = 1; | |
bf1060ea | 121 | } |
02df4a27 | 122 | #endif |
71f95118 WD |
123 | return 0; |
124 | } | |
125 | ||
126 | ||
127 | /* | |
128 | * Get the first occurence of a directory delimiter ('/' or '\') in a string. | |
129 | * Return index into string if found, -1 otherwise. | |
130 | */ | |
131 | static int | |
132 | dirdelim(char *str) | |
133 | { | |
134 | char *start = str; | |
135 | ||
136 | while (*str != '\0') { | |
137 | if (ISDIRDELIM(*str)) return str - start; | |
138 | str++; | |
139 | } | |
140 | return -1; | |
141 | } | |
142 | ||
71f95118 WD |
143 | /* |
144 | * Extract zero terminated short name from a directory entry. | |
145 | */ | |
146 | static void get_name (dir_entry *dirent, char *s_name) | |
147 | { | |
148 | char *ptr; | |
149 | ||
150 | memcpy (s_name, dirent->name, 8); | |
151 | s_name[8] = '\0'; | |
152 | ptr = s_name; | |
153 | while (*ptr && *ptr != ' ') | |
154 | ptr++; | |
155 | if (dirent->ext[0] && dirent->ext[0] != ' ') { | |
156 | *ptr = '.'; | |
157 | ptr++; | |
158 | memcpy (ptr, dirent->ext, 3); | |
159 | ptr[3] = '\0'; | |
160 | while (*ptr && *ptr != ' ') | |
161 | ptr++; | |
162 | } | |
163 | *ptr = '\0'; | |
164 | if (*s_name == DELETED_FLAG) | |
165 | *s_name = '\0'; | |
166 | else if (*s_name == aRING) | |
3c2c2f42 | 167 | *s_name = DELETED_FLAG; |
71f95118 WD |
168 | downcase (s_name); |
169 | } | |
170 | ||
171 | /* | |
172 | * Get the entry at index 'entry' in a FAT (12/16/32) table. | |
173 | * On failure 0x00 is returned. | |
174 | */ | |
175 | static __u32 | |
176 | get_fatent(fsdata *mydata, __u32 entry) | |
177 | { | |
178 | __u32 bufnum; | |
179 | __u32 offset; | |
180 | __u32 ret = 0x00; | |
181 | ||
182 | switch (mydata->fatsize) { | |
183 | case 32: | |
184 | bufnum = entry / FAT32BUFSIZE; | |
185 | offset = entry - bufnum * FAT32BUFSIZE; | |
186 | break; | |
187 | case 16: | |
188 | bufnum = entry / FAT16BUFSIZE; | |
189 | offset = entry - bufnum * FAT16BUFSIZE; | |
190 | break; | |
191 | case 12: | |
192 | bufnum = entry / FAT12BUFSIZE; | |
193 | offset = entry - bufnum * FAT12BUFSIZE; | |
194 | break; | |
195 | ||
196 | default: | |
197 | /* Unsupported FAT size */ | |
198 | return ret; | |
199 | } | |
200 | ||
201 | /* Read a new block of FAT entries into the cache. */ | |
202 | if (bufnum != mydata->fatbufnum) { | |
203 | int getsize = FATBUFSIZE/FS_BLOCK_SIZE; | |
204 | __u8 *bufptr = mydata->fatbuf; | |
205 | __u32 fatlength = mydata->fatlength; | |
206 | __u32 startblock = bufnum * FATBUFBLOCKS; | |
207 | ||
208 | fatlength *= SECTOR_SIZE; /* We want it in bytes now */ | |
209 | startblock += mydata->fat_sect; /* Offset from start of disk */ | |
210 | ||
211 | if (getsize > fatlength) getsize = fatlength; | |
212 | if (disk_read(startblock, getsize, bufptr) < 0) { | |
213 | FAT_DPRINT("Error reading FAT blocks\n"); | |
214 | return ret; | |
215 | } | |
216 | mydata->fatbufnum = bufnum; | |
217 | } | |
218 | ||
219 | /* Get the actual entry from the table */ | |
220 | switch (mydata->fatsize) { | |
221 | case 32: | |
222 | ret = FAT2CPU32(((__u32*)mydata->fatbuf)[offset]); | |
223 | break; | |
224 | case 16: | |
225 | ret = FAT2CPU16(((__u16*)mydata->fatbuf)[offset]); | |
226 | break; | |
227 | case 12: { | |
228 | __u32 off16 = (offset*3)/4; | |
229 | __u16 val1, val2; | |
230 | ||
231 | switch (offset & 0x3) { | |
232 | case 0: | |
233 | ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]); | |
234 | ret &= 0xfff; | |
235 | break; | |
236 | case 1: | |
237 | val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]); | |
238 | val1 &= 0xf000; | |
239 | val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]); | |
240 | val2 &= 0x00ff; | |
241 | ret = (val2 << 4) | (val1 >> 12); | |
242 | break; | |
243 | case 2: | |
244 | val1 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]); | |
245 | val1 &= 0xff00; | |
246 | val2 = FAT2CPU16(((__u16*)mydata->fatbuf)[off16+1]); | |
247 | val2 &= 0x000f; | |
248 | ret = (val2 << 8) | (val1 >> 8); | |
249 | break; | |
250 | case 3: | |
251 | ret = FAT2CPU16(((__u16*)mydata->fatbuf)[off16]);; | |
252 | ret = (ret & 0xfff0) >> 4; | |
253 | break; | |
254 | default: | |
255 | break; | |
256 | } | |
257 | } | |
258 | break; | |
259 | } | |
260 | FAT_DPRINT("ret: %d, offset: %d\n", ret, offset); | |
261 | ||
262 | return ret; | |
263 | } | |
264 | ||
265 | ||
266 | /* | |
267 | * Read at most 'size' bytes from the specified cluster into 'buffer'. | |
268 | * Return 0 on success, -1 otherwise. | |
269 | */ | |
270 | static int | |
271 | get_cluster(fsdata *mydata, __u32 clustnum, __u8 *buffer, unsigned long size) | |
272 | { | |
273 | int idx = 0; | |
274 | __u32 startsect; | |
275 | ||
276 | if (clustnum > 0) { | |
277 | startsect = mydata->data_begin + clustnum*mydata->clust_size; | |
278 | } else { | |
279 | startsect = mydata->rootdir_sect; | |
280 | } | |
281 | ||
282 | FAT_DPRINT("gc - clustnum: %d, startsect: %d\n", clustnum, startsect); | |
7205e407 WD |
283 | if (disk_read(startsect, size/FS_BLOCK_SIZE , buffer) < 0) { |
284 | FAT_DPRINT("Error reading data\n"); | |
285 | return -1; | |
286 | } | |
287 | if(size % FS_BLOCK_SIZE) { | |
288 | __u8 tmpbuf[FS_BLOCK_SIZE]; | |
289 | idx= size/FS_BLOCK_SIZE; | |
290 | if (disk_read(startsect + idx, 1, tmpbuf) < 0) { | |
291 | FAT_DPRINT("Error reading data\n"); | |
292 | return -1; | |
71f95118 | 293 | } |
7205e407 WD |
294 | buffer += idx*FS_BLOCK_SIZE; |
295 | ||
296 | memcpy(buffer, tmpbuf, size % FS_BLOCK_SIZE); | |
297 | return 0; | |
71f95118 WD |
298 | } |
299 | ||
300 | return 0; | |
301 | } | |
302 | ||
303 | ||
304 | /* | |
305 | * Read at most 'maxsize' bytes from the file associated with 'dentptr' | |
306 | * into 'buffer'. | |
307 | * Return the number of bytes read or -1 on fatal errors. | |
308 | */ | |
309 | static long | |
310 | get_contents(fsdata *mydata, dir_entry *dentptr, __u8 *buffer, | |
311 | unsigned long maxsize) | |
312 | { | |
313 | unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0; | |
314 | unsigned int bytesperclust = mydata->clust_size * SECTOR_SIZE; | |
315 | __u32 curclust = START(dentptr); | |
7205e407 WD |
316 | __u32 endclust, newclust; |
317 | unsigned long actsize; | |
71f95118 WD |
318 | |
319 | FAT_DPRINT("Filesize: %ld bytes\n", filesize); | |
320 | ||
321 | if (maxsize > 0 && filesize > maxsize) filesize = maxsize; | |
322 | ||
323 | FAT_DPRINT("Reading: %ld bytes\n", filesize); | |
324 | ||
7205e407 WD |
325 | actsize=bytesperclust; |
326 | endclust=curclust; | |
71f95118 | 327 | do { |
7205e407 WD |
328 | /* search for consecutive clusters */ |
329 | while(actsize < filesize) { | |
330 | newclust = get_fatent(mydata, endclust); | |
331 | if((newclust -1)!=endclust) | |
332 | goto getit; | |
8ce4e5c2 | 333 | if (CHECK_CLUST(newclust, mydata->fatsize)) { |
7205e407 WD |
334 | FAT_DPRINT("curclust: 0x%x\n", newclust); |
335 | FAT_DPRINT("Invalid FAT entry\n"); | |
336 | return gotsize; | |
337 | } | |
338 | endclust=newclust; | |
339 | actsize+= bytesperclust; | |
340 | } | |
341 | /* actsize >= file size */ | |
342 | actsize -= bytesperclust; | |
343 | /* get remaining clusters */ | |
344 | if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { | |
71f95118 WD |
345 | FAT_ERROR("Error reading cluster\n"); |
346 | return -1; | |
347 | } | |
7205e407 WD |
348 | /* get remaining bytes */ |
349 | gotsize += (int)actsize; | |
350 | filesize -= actsize; | |
351 | buffer += actsize; | |
352 | actsize= filesize; | |
353 | if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) { | |
354 | FAT_ERROR("Error reading cluster\n"); | |
355 | return -1; | |
356 | } | |
357 | gotsize+=actsize; | |
358 | return gotsize; | |
359 | getit: | |
360 | if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) { | |
361 | FAT_ERROR("Error reading cluster\n"); | |
362 | return -1; | |
363 | } | |
364 | gotsize += (int)actsize; | |
365 | filesize -= actsize; | |
366 | buffer += actsize; | |
367 | curclust = get_fatent(mydata, endclust); | |
8ce4e5c2 | 368 | if (CHECK_CLUST(curclust, mydata->fatsize)) { |
71f95118 WD |
369 | FAT_DPRINT("curclust: 0x%x\n", curclust); |
370 | FAT_ERROR("Invalid FAT entry\n"); | |
371 | return gotsize; | |
372 | } | |
7205e407 WD |
373 | actsize=bytesperclust; |
374 | endclust=curclust; | |
71f95118 WD |
375 | } while (1); |
376 | } | |
377 | ||
378 | ||
379 | #ifdef CONFIG_SUPPORT_VFAT | |
380 | /* | |
381 | * Extract the file name information from 'slotptr' into 'l_name', | |
382 | * starting at l_name[*idx]. | |
383 | * Return 1 if terminator (zero byte) is found, 0 otherwise. | |
384 | */ | |
385 | static int | |
386 | slot2str(dir_slot *slotptr, char *l_name, int *idx) | |
387 | { | |
388 | int j; | |
389 | ||
390 | for (j = 0; j <= 8; j += 2) { | |
391 | l_name[*idx] = slotptr->name0_4[j]; | |
392 | if (l_name[*idx] == 0x00) return 1; | |
393 | (*idx)++; | |
394 | } | |
395 | for (j = 0; j <= 10; j += 2) { | |
396 | l_name[*idx] = slotptr->name5_10[j]; | |
397 | if (l_name[*idx] == 0x00) return 1; | |
398 | (*idx)++; | |
399 | } | |
400 | for (j = 0; j <= 2; j += 2) { | |
401 | l_name[*idx] = slotptr->name11_12[j]; | |
402 | if (l_name[*idx] == 0x00) return 1; | |
403 | (*idx)++; | |
404 | } | |
405 | ||
406 | return 0; | |
407 | } | |
408 | ||
409 | ||
410 | /* | |
411 | * Extract the full long filename starting at 'retdent' (which is really | |
412 | * a slot) into 'l_name'. If successful also copy the real directory entry | |
413 | * into 'retdent' | |
414 | * Return 0 on success, -1 otherwise. | |
415 | */ | |
7e4b9b4f BW |
416 | __attribute__ ((__aligned__(__alignof__(dir_entry)))) |
417 | __u8 get_vfatname_block[MAX_CLUSTSIZE]; | |
71f95118 WD |
418 | static int |
419 | get_vfatname(fsdata *mydata, int curclust, __u8 *cluster, | |
420 | dir_entry *retdent, char *l_name) | |
421 | { | |
422 | dir_entry *realdent; | |
423 | dir_slot *slotptr = (dir_slot*) retdent; | |
424 | __u8 *nextclust = cluster + mydata->clust_size * SECTOR_SIZE; | |
2d1a537d | 425 | __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff; |
71f95118 WD |
426 | int idx = 0; |
427 | ||
428 | while ((__u8*)slotptr < nextclust) { | |
429 | if (counter == 0) break; | |
2d1a537d WD |
430 | if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter) |
431 | return -1; | |
71f95118 WD |
432 | slotptr++; |
433 | counter--; | |
434 | } | |
435 | ||
436 | if ((__u8*)slotptr >= nextclust) { | |
71f95118 WD |
437 | dir_slot *slotptr2; |
438 | ||
439 | slotptr--; | |
440 | curclust = get_fatent(mydata, curclust); | |
8ce4e5c2 | 441 | if (CHECK_CLUST(curclust, mydata->fatsize)) { |
71f95118 WD |
442 | FAT_DPRINT("curclust: 0x%x\n", curclust); |
443 | FAT_ERROR("Invalid FAT entry\n"); | |
444 | return -1; | |
445 | } | |
5fa66df6 | 446 | if (get_cluster(mydata, curclust, get_vfatname_block, |
71f95118 WD |
447 | mydata->clust_size * SECTOR_SIZE) != 0) { |
448 | FAT_DPRINT("Error: reading directory block\n"); | |
449 | return -1; | |
450 | } | |
5fa66df6 | 451 | slotptr2 = (dir_slot*) get_vfatname_block; |
71f95118 WD |
452 | while (slotptr2->id > 0x01) { |
453 | slotptr2++; | |
454 | } | |
455 | /* Save the real directory entry */ | |
456 | realdent = (dir_entry*)slotptr2 + 1; | |
5fa66df6 | 457 | while ((__u8*)slotptr2 >= get_vfatname_block) { |
71f95118 WD |
458 | slot2str(slotptr2, l_name, &idx); |
459 | slotptr2--; | |
460 | } | |
461 | } else { | |
462 | /* Save the real directory entry */ | |
463 | realdent = (dir_entry*)slotptr; | |
464 | } | |
465 | ||
466 | do { | |
467 | slotptr--; | |
468 | if (slot2str(slotptr, l_name, &idx)) break; | |
2d1a537d | 469 | } while (!(slotptr->id & LAST_LONG_ENTRY_MASK)); |
71f95118 WD |
470 | |
471 | l_name[idx] = '\0'; | |
472 | if (*l_name == DELETED_FLAG) *l_name = '\0'; | |
3c2c2f42 | 473 | else if (*l_name == aRING) *l_name = DELETED_FLAG; |
71f95118 WD |
474 | downcase(l_name); |
475 | ||
476 | /* Return the real directory entry */ | |
477 | memcpy(retdent, realdent, sizeof(dir_entry)); | |
478 | ||
479 | return 0; | |
480 | } | |
481 | ||
482 | ||
483 | /* Calculate short name checksum */ | |
484 | static __u8 | |
485 | mkcksum(const char *str) | |
486 | { | |
487 | int i; | |
488 | __u8 ret = 0; | |
489 | ||
490 | for (i = 0; i < 11; i++) { | |
491 | ret = (((ret&1)<<7)|((ret&0xfe)>>1)) + str[i]; | |
492 | } | |
493 | ||
494 | return ret; | |
495 | } | |
496 | #endif | |
497 | ||
498 | ||
499 | /* | |
500 | * Get the directory entry associated with 'filename' from the directory | |
501 | * starting at 'startsect' | |
502 | */ | |
7e4b9b4f | 503 | __attribute__ ((__aligned__(__alignof__(dir_entry)))) |
5fa66df6 | 504 | __u8 get_dentfromdir_block[MAX_CLUSTSIZE]; |
71f95118 WD |
505 | static dir_entry *get_dentfromdir (fsdata * mydata, int startsect, |
506 | char *filename, dir_entry * retdent, | |
507 | int dols) | |
508 | { | |
509 | __u16 prevcksum = 0xffff; | |
71f95118 WD |
510 | __u32 curclust = START (retdent); |
511 | int files = 0, dirs = 0; | |
512 | ||
513 | FAT_DPRINT ("get_dentfromdir: %s\n", filename); | |
514 | while (1) { | |
515 | dir_entry *dentptr; | |
516 | int i; | |
517 | ||
5fa66df6 | 518 | if (get_cluster (mydata, curclust, get_dentfromdir_block, |
71f95118 WD |
519 | mydata->clust_size * SECTOR_SIZE) != 0) { |
520 | FAT_DPRINT ("Error: reading directory block\n"); | |
521 | return NULL; | |
522 | } | |
5fa66df6 | 523 | dentptr = (dir_entry *) get_dentfromdir_block; |
71f95118 WD |
524 | for (i = 0; i < DIRENTSPERCLUST; i++) { |
525 | char s_name[14], l_name[256]; | |
526 | ||
527 | l_name[0] = '\0'; | |
855a496f WD |
528 | if (dentptr->name[0] == DELETED_FLAG) { |
529 | dentptr++; | |
530 | continue; | |
531 | } | |
71f95118 WD |
532 | if ((dentptr->attr & ATTR_VOLUME)) { |
533 | #ifdef CONFIG_SUPPORT_VFAT | |
534 | if ((dentptr->attr & ATTR_VFAT) && | |
2d1a537d | 535 | (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { |
71f95118 WD |
536 | prevcksum = ((dir_slot *) dentptr) |
537 | ->alias_checksum; | |
5fa66df6 | 538 | get_vfatname (mydata, curclust, get_dentfromdir_block, |
71f95118 WD |
539 | dentptr, l_name); |
540 | if (dols) { | |
541 | int isdir = (dentptr->attr & ATTR_DIR); | |
542 | char dirc; | |
543 | int doit = 0; | |
544 | ||
545 | if (isdir) { | |
546 | dirs++; | |
547 | dirc = '/'; | |
548 | doit = 1; | |
549 | } else { | |
550 | dirc = ' '; | |
551 | if (l_name[0] != 0) { | |
552 | files++; | |
553 | doit = 1; | |
554 | } | |
555 | } | |
556 | if (doit) { | |
557 | if (dirc == ' ') { | |
558 | printf (" %8ld %s%c\n", | |
559 | (long) FAT2CPU32 (dentptr->size), | |
560 | l_name, dirc); | |
561 | } else { | |
562 | printf (" %s%c\n", l_name, dirc); | |
563 | } | |
564 | } | |
565 | dentptr++; | |
566 | continue; | |
567 | } | |
568 | FAT_DPRINT ("vfatname: |%s|\n", l_name); | |
569 | } else | |
570 | #endif | |
571 | { | |
572 | /* Volume label or VFAT entry */ | |
573 | dentptr++; | |
574 | continue; | |
575 | } | |
576 | } | |
577 | if (dentptr->name[0] == 0) { | |
578 | if (dols) { | |
579 | printf ("\n%d file(s), %d dir(s)\n\n", files, dirs); | |
580 | } | |
581 | FAT_DPRINT ("Dentname == NULL - %d\n", i); | |
582 | return NULL; | |
583 | } | |
584 | #ifdef CONFIG_SUPPORT_VFAT | |
585 | if (dols && mkcksum (dentptr->name) == prevcksum) { | |
586 | dentptr++; | |
587 | continue; | |
588 | } | |
589 | #endif | |
590 | get_name (dentptr, s_name); | |
591 | if (dols) { | |
592 | int isdir = (dentptr->attr & ATTR_DIR); | |
593 | char dirc; | |
594 | int doit = 0; | |
595 | ||
596 | if (isdir) { | |
597 | dirs++; | |
598 | dirc = '/'; | |
599 | doit = 1; | |
600 | } else { | |
601 | dirc = ' '; | |
602 | if (s_name[0] != 0) { | |
603 | files++; | |
604 | doit = 1; | |
605 | } | |
606 | } | |
607 | if (doit) { | |
608 | if (dirc == ' ') { | |
609 | printf (" %8ld %s%c\n", | |
610 | (long) FAT2CPU32 (dentptr->size), s_name, | |
611 | dirc); | |
612 | } else { | |
613 | printf (" %s%c\n", s_name, dirc); | |
614 | } | |
615 | } | |
616 | dentptr++; | |
617 | continue; | |
618 | } | |
619 | if (strcmp (filename, s_name) && strcmp (filename, l_name)) { | |
620 | FAT_DPRINT ("Mismatch: |%s|%s|\n", s_name, l_name); | |
621 | dentptr++; | |
622 | continue; | |
623 | } | |
624 | memcpy (retdent, dentptr, sizeof (dir_entry)); | |
625 | ||
626 | FAT_DPRINT ("DentName: %s", s_name); | |
627 | FAT_DPRINT (", start: 0x%x", START (dentptr)); | |
628 | FAT_DPRINT (", size: 0x%x %s\n", | |
629 | FAT2CPU32 (dentptr->size), | |
630 | (dentptr->attr & ATTR_DIR) ? "(DIR)" : ""); | |
631 | ||
632 | return retdent; | |
633 | } | |
634 | curclust = get_fatent (mydata, curclust); | |
8ce4e5c2 | 635 | if (CHECK_CLUST(curclust, mydata->fatsize)) { |
71f95118 WD |
636 | FAT_DPRINT ("curclust: 0x%x\n", curclust); |
637 | FAT_ERROR ("Invalid FAT entry\n"); | |
638 | return NULL; | |
639 | } | |
640 | } | |
641 | ||
642 | return NULL; | |
643 | } | |
644 | ||
645 | ||
646 | /* | |
647 | * Read boot sector and volume info from a FAT filesystem | |
648 | */ | |
649 | static int | |
650 | read_bootsectandvi(boot_sector *bs, volume_info *volinfo, int *fatsize) | |
651 | { | |
652 | __u8 block[FS_BLOCK_SIZE]; | |
653 | volume_info *vistart; | |
654 | ||
655 | if (disk_read(0, 1, block) < 0) { | |
656 | FAT_DPRINT("Error: reading block\n"); | |
657 | return -1; | |
658 | } | |
659 | ||
660 | memcpy(bs, block, sizeof(boot_sector)); | |
661 | bs->reserved = FAT2CPU16(bs->reserved); | |
662 | bs->fat_length = FAT2CPU16(bs->fat_length); | |
663 | bs->secs_track = FAT2CPU16(bs->secs_track); | |
664 | bs->heads = FAT2CPU16(bs->heads); | |
665 | #if 0 /* UNUSED */ | |
666 | bs->hidden = FAT2CPU32(bs->hidden); | |
667 | #endif | |
668 | bs->total_sect = FAT2CPU32(bs->total_sect); | |
669 | ||
670 | /* FAT32 entries */ | |
671 | if (bs->fat_length == 0) { | |
672 | /* Assume FAT32 */ | |
673 | bs->fat32_length = FAT2CPU32(bs->fat32_length); | |
674 | bs->flags = FAT2CPU16(bs->flags); | |
675 | bs->root_cluster = FAT2CPU32(bs->root_cluster); | |
676 | bs->info_sector = FAT2CPU16(bs->info_sector); | |
677 | bs->backup_boot = FAT2CPU16(bs->backup_boot); | |
678 | vistart = (volume_info*) (block + sizeof(boot_sector)); | |
679 | *fatsize = 32; | |
680 | } else { | |
681 | vistart = (volume_info*) &(bs->fat32_length); | |
682 | *fatsize = 0; | |
683 | } | |
684 | memcpy(volinfo, vistart, sizeof(volume_info)); | |
685 | ||
71f95118 | 686 | if (*fatsize == 32) { |
651351fe | 687 | if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0) { |
71f95118 WD |
688 | return 0; |
689 | } | |
690 | } else { | |
651351fe | 691 | if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) { |
71f95118 WD |
692 | *fatsize = 12; |
693 | return 0; | |
694 | } | |
651351fe | 695 | if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) { |
71f95118 WD |
696 | *fatsize = 16; |
697 | return 0; | |
698 | } | |
699 | } | |
700 | ||
701 | FAT_DPRINT("Error: broken fs_type sign\n"); | |
702 | return -1; | |
703 | } | |
704 | ||
7e4b9b4f BW |
705 | __attribute__ ((__aligned__(__alignof__(dir_entry)))) |
706 | __u8 do_fat_read_block[MAX_CLUSTSIZE]; | |
20cc00dd | 707 | long |
71f95118 WD |
708 | do_fat_read (const char *filename, void *buffer, unsigned long maxsize, |
709 | int dols) | |
710 | { | |
71f95118 WD |
711 | char fnamecopy[2048]; |
712 | boot_sector bs; | |
713 | volume_info volinfo; | |
714 | fsdata datablock; | |
715 | fsdata *mydata = &datablock; | |
716 | dir_entry *dentptr; | |
717 | __u16 prevcksum = 0xffff; | |
718 | char *subname = ""; | |
719 | int rootdir_size, cursect; | |
720 | int idx, isdir = 0; | |
721 | int files = 0, dirs = 0; | |
722 | long ret = 0; | |
723 | int firsttime; | |
724 | ||
725 | if (read_bootsectandvi (&bs, &volinfo, &mydata->fatsize)) { | |
726 | FAT_DPRINT ("Error: reading boot sector\n"); | |
727 | return -1; | |
728 | } | |
729 | if (mydata->fatsize == 32) { | |
730 | mydata->fatlength = bs.fat32_length; | |
731 | } else { | |
732 | mydata->fatlength = bs.fat_length; | |
733 | } | |
734 | mydata->fat_sect = bs.reserved; | |
735 | cursect = mydata->rootdir_sect | |
736 | = mydata->fat_sect + mydata->fatlength * bs.fats; | |
737 | mydata->clust_size = bs.cluster_size; | |
738 | if (mydata->fatsize == 32) { | |
739 | rootdir_size = mydata->clust_size; | |
740 | mydata->data_begin = mydata->rootdir_sect /* + rootdir_size */ | |
741 | - (mydata->clust_size * 2); | |
742 | } else { | |
743 | rootdir_size = ((bs.dir_entries[1] * (int) 256 + bs.dir_entries[0]) | |
744 | * sizeof (dir_entry)) / SECTOR_SIZE; | |
745 | mydata->data_begin = mydata->rootdir_sect + rootdir_size | |
746 | - (mydata->clust_size * 2); | |
747 | } | |
748 | mydata->fatbufnum = -1; | |
749 | ||
750 | FAT_DPRINT ("FAT%d, fatlength: %d\n", mydata->fatsize, | |
751 | mydata->fatlength); | |
752 | FAT_DPRINT ("Rootdir begins at sector: %d, offset: %x, size: %d\n" | |
753 | "Data begins at: %d\n", | |
754 | mydata->rootdir_sect, mydata->rootdir_sect * SECTOR_SIZE, | |
755 | rootdir_size, mydata->data_begin); | |
756 | FAT_DPRINT ("Cluster size: %d\n", mydata->clust_size); | |
757 | ||
758 | /* "cwd" is always the root... */ | |
759 | while (ISDIRDELIM (*filename)) | |
760 | filename++; | |
761 | /* Make a copy of the filename and convert it to lowercase */ | |
762 | strcpy (fnamecopy, filename); | |
763 | downcase (fnamecopy); | |
764 | if (*fnamecopy == '\0') { | |
765 | if (!dols) | |
766 | return -1; | |
767 | dols = LS_ROOT; | |
768 | } else if ((idx = dirdelim (fnamecopy)) >= 0) { | |
769 | isdir = 1; | |
770 | fnamecopy[idx] = '\0'; | |
771 | subname = fnamecopy + idx + 1; | |
772 | /* Handle multiple delimiters */ | |
773 | while (ISDIRDELIM (*subname)) | |
774 | subname++; | |
775 | } else if (dols) { | |
776 | isdir = 1; | |
777 | } | |
778 | ||
779 | while (1) { | |
780 | int i; | |
781 | ||
5fa66df6 | 782 | if (disk_read (cursect, mydata->clust_size, do_fat_read_block) < 0) { |
71f95118 WD |
783 | FAT_DPRINT ("Error: reading rootdir block\n"); |
784 | return -1; | |
785 | } | |
5fa66df6 | 786 | dentptr = (dir_entry *) do_fat_read_block; |
71f95118 WD |
787 | for (i = 0; i < DIRENTSPERBLOCK; i++) { |
788 | char s_name[14], l_name[256]; | |
789 | ||
790 | l_name[0] = '\0'; | |
791 | if ((dentptr->attr & ATTR_VOLUME)) { | |
792 | #ifdef CONFIG_SUPPORT_VFAT | |
793 | if ((dentptr->attr & ATTR_VFAT) && | |
2d1a537d | 794 | (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) { |
71f95118 | 795 | prevcksum = ((dir_slot *) dentptr)->alias_checksum; |
5fa66df6 | 796 | get_vfatname (mydata, 0, do_fat_read_block, dentptr, l_name); |
71f95118 WD |
797 | if (dols == LS_ROOT) { |
798 | int isdir = (dentptr->attr & ATTR_DIR); | |
799 | char dirc; | |
800 | int doit = 0; | |
801 | ||
802 | if (isdir) { | |
803 | dirs++; | |
804 | dirc = '/'; | |
805 | doit = 1; | |
806 | } else { | |
807 | dirc = ' '; | |
808 | if (l_name[0] != 0) { | |
809 | files++; | |
810 | doit = 1; | |
811 | } | |
812 | } | |
813 | if (doit) { | |
814 | if (dirc == ' ') { | |
815 | printf (" %8ld %s%c\n", | |
816 | (long) FAT2CPU32 (dentptr->size), | |
817 | l_name, dirc); | |
818 | } else { | |
819 | printf (" %s%c\n", l_name, dirc); | |
820 | } | |
821 | } | |
822 | dentptr++; | |
823 | continue; | |
824 | } | |
825 | FAT_DPRINT ("Rootvfatname: |%s|\n", l_name); | |
826 | } else | |
827 | #endif | |
828 | { | |
829 | /* Volume label or VFAT entry */ | |
830 | dentptr++; | |
831 | continue; | |
832 | } | |
833 | } else if (dentptr->name[0] == 0) { | |
834 | FAT_DPRINT ("RootDentname == NULL - %d\n", i); | |
835 | if (dols == LS_ROOT) { | |
836 | printf ("\n%d file(s), %d dir(s)\n\n", files, dirs); | |
837 | return 0; | |
838 | } | |
839 | return -1; | |
840 | } | |
841 | #ifdef CONFIG_SUPPORT_VFAT | |
842 | else if (dols == LS_ROOT | |
843 | && mkcksum (dentptr->name) == prevcksum) { | |
844 | dentptr++; | |
845 | continue; | |
846 | } | |
847 | #endif | |
848 | get_name (dentptr, s_name); | |
849 | if (dols == LS_ROOT) { | |
850 | int isdir = (dentptr->attr & ATTR_DIR); | |
851 | char dirc; | |
852 | int doit = 0; | |
853 | ||
854 | if (isdir) { | |
71f95118 | 855 | dirc = '/'; |
a43278a4 WD |
856 | if (s_name[0] != 0) { |
857 | dirs++; | |
858 | doit = 1; | |
859 | } | |
71f95118 WD |
860 | } else { |
861 | dirc = ' '; | |
862 | if (s_name[0] != 0) { | |
863 | files++; | |
864 | doit = 1; | |
865 | } | |
866 | } | |
867 | if (doit) { | |
868 | if (dirc == ' ') { | |
869 | printf (" %8ld %s%c\n", | |
870 | (long) FAT2CPU32 (dentptr->size), s_name, | |
871 | dirc); | |
872 | } else { | |
873 | printf (" %s%c\n", s_name, dirc); | |
874 | } | |
875 | } | |
876 | dentptr++; | |
877 | continue; | |
878 | } | |
879 | if (strcmp (fnamecopy, s_name) && strcmp (fnamecopy, l_name)) { | |
880 | FAT_DPRINT ("RootMismatch: |%s|%s|\n", s_name, l_name); | |
881 | dentptr++; | |
882 | continue; | |
883 | } | |
884 | if (isdir && !(dentptr->attr & ATTR_DIR)) | |
885 | return -1; | |
886 | ||
887 | FAT_DPRINT ("RootName: %s", s_name); | |
888 | FAT_DPRINT (", start: 0x%x", START (dentptr)); | |
889 | FAT_DPRINT (", size: 0x%x %s\n", | |
890 | FAT2CPU32 (dentptr->size), isdir ? "(DIR)" : ""); | |
891 | ||
892 | goto rootdir_done; /* We got a match */ | |
893 | } | |
894 | cursect++; | |
895 | } | |
896 | rootdir_done: | |
897 | ||
898 | firsttime = 1; | |
899 | while (isdir) { | |
900 | int startsect = mydata->data_begin | |
901 | + START (dentptr) * mydata->clust_size; | |
902 | dir_entry dent; | |
903 | char *nextname = NULL; | |
904 | ||
905 | dent = *dentptr; | |
906 | dentptr = &dent; | |
907 | ||
908 | idx = dirdelim (subname); | |
909 | if (idx >= 0) { | |
910 | subname[idx] = '\0'; | |
911 | nextname = subname + idx + 1; | |
912 | /* Handle multiple delimiters */ | |
913 | while (ISDIRDELIM (*nextname)) | |
914 | nextname++; | |
915 | if (dols && *nextname == '\0') | |
916 | firsttime = 0; | |
917 | } else { | |
918 | if (dols && firsttime) { | |
919 | firsttime = 0; | |
920 | } else { | |
921 | isdir = 0; | |
922 | } | |
923 | } | |
924 | ||
925 | if (get_dentfromdir (mydata, startsect, subname, dentptr, | |
926 | isdir ? 0 : dols) == NULL) { | |
927 | if (dols && !isdir) | |
928 | return 0; | |
929 | return -1; | |
930 | } | |
931 | ||
932 | if (idx >= 0) { | |
933 | if (!(dentptr->attr & ATTR_DIR)) | |
934 | return -1; | |
935 | subname = nextname; | |
936 | } | |
937 | } | |
938 | ret = get_contents (mydata, dentptr, buffer, maxsize); | |
939 | FAT_DPRINT ("Size: %d, got: %ld\n", FAT2CPU32 (dentptr->size), ret); | |
940 | ||
941 | return ret; | |
942 | } | |
943 | ||
944 | ||
945 | int | |
946 | file_fat_detectfs(void) | |
947 | { | |
948 | boot_sector bs; | |
949 | volume_info volinfo; | |
950 | int fatsize; | |
7205e407 | 951 | char vol_label[12]; |
71f95118 | 952 | |
7205e407 WD |
953 | if(cur_dev==NULL) { |
954 | printf("No current device\n"); | |
955 | return 1; | |
956 | } | |
dd60d122 | 957 | #if defined(CONFIG_CMD_IDE) || \ |
75eb82ec | 958 | defined(CONFIG_CMD_MG_DISK) || \ |
8c5170a7 | 959 | defined(CONFIG_CMD_SATA) || \ |
dd60d122 JL |
960 | defined(CONFIG_CMD_SCSI) || \ |
961 | defined(CONFIG_CMD_USB) || \ | |
21f6f963 | 962 | defined(CONFIG_MMC) |
7205e407 WD |
963 | printf("Interface: "); |
964 | switch(cur_dev->if_type) { | |
965 | case IF_TYPE_IDE : printf("IDE"); break; | |
8c5170a7 | 966 | case IF_TYPE_SATA : printf("SATA"); break; |
7205e407 WD |
967 | case IF_TYPE_SCSI : printf("SCSI"); break; |
968 | case IF_TYPE_ATAPI : printf("ATAPI"); break; | |
969 | case IF_TYPE_USB : printf("USB"); break; | |
970 | case IF_TYPE_DOC : printf("DOC"); break; | |
971 | case IF_TYPE_MMC : printf("MMC"); break; | |
972 | default : printf("Unknown"); | |
973 | } | |
974 | printf("\n Device %d: ",cur_dev->dev); | |
975 | dev_print(cur_dev); | |
976 | #endif | |
977 | if(read_bootsectandvi(&bs, &volinfo, &fatsize)) { | |
978 | printf("\nNo valid FAT fs found\n"); | |
979 | return 1; | |
980 | } | |
981 | memcpy (vol_label, volinfo.volume_label, 11); | |
982 | vol_label[11] = '\0'; | |
983 | volinfo.fs_type[5]='\0'; | |
3e3b9569 PP |
984 | printf("Partition %d: Filesystem: %s \"%s\"\n" |
985 | ,cur_part,volinfo.fs_type,vol_label); | |
7205e407 | 986 | return 0; |
71f95118 WD |
987 | } |
988 | ||
989 | ||
990 | int | |
991 | file_fat_ls(const char *dir) | |
992 | { | |
993 | return do_fat_read(dir, NULL, 0, LS_YES); | |
994 | } | |
995 | ||
996 | ||
997 | long | |
998 | file_fat_read(const char *filename, void *buffer, unsigned long maxsize) | |
999 | { | |
7205e407 | 1000 | printf("reading %s\n",filename); |
71f95118 WD |
1001 | return do_fat_read(filename, buffer, maxsize, LS_NO); |
1002 | } |