blob: 7cf173caaaae7b1f0f6c96de6e3dc1105ce5accb [file] [log] [blame]
wdenk7a428cc2003-06-15 22:40:42 +00001/*
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>
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +000030#include <exports.h>
wdenk7a428cc2003-06-15 22:40:42 +000031#include <fat.h>
32#include <asm/byteorder.h>
wdenk2c9b05d2003-09-10 22:30:53 +000033#include <part.h>
wdenk7a428cc2003-06-15 22:40:42 +000034
wdenk7a428cc2003-06-15 22:40:42 +000035/*
36 * Convert a string to lowercase.
37 */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020038static void downcase (char *str)
wdenk7a428cc2003-06-15 22:40:42 +000039{
40 while (*str != '\0') {
41 TOLOWER(*str);
42 str++;
43 }
44}
45
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020046static block_dev_desc_t *cur_dev = NULL;
47
wdenk2c9b05d2003-09-10 22:30:53 +000048static unsigned long part_offset = 0;
Donggeun Kim8f814002011-10-24 21:15:28 +000049static unsigned long part_size;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020050
wdenk2c9b05d2003-09-10 22:30:53 +000051static int cur_part = 1;
52
53#define DOS_PART_TBL_OFFSET 0x1be
54#define DOS_PART_MAGIC_OFFSET 0x1fe
55#define DOS_FS_TYPE_OFFSET 0x36
Wolfgang Denk7b2290c2010-07-19 11:36:57 +020056#define DOS_FS32_TYPE_OFFSET 0x52
wdenk7a428cc2003-06-15 22:40:42 +000057
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020058static int disk_read (__u32 startblock, __u32 getsize, __u8 * bufptr)
wdenk7a428cc2003-06-15 22:40:42 +000059{
wdenk2c9b05d2003-09-10 22:30:53 +000060 if (cur_dev == NULL)
61 return -1;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020062
63 startblock += part_offset;
64
wdenk2c9b05d2003-09-10 22:30:53 +000065 if (cur_dev->block_read) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020066 return cur_dev->block_read(cur_dev->dev, startblock, getsize,
67 (unsigned long *) bufptr);
wdenk7a428cc2003-06-15 22:40:42 +000068 }
69 return -1;
70}
71
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020072int fat_register_device (block_dev_desc_t * dev_desc, int part_no)
wdenk7a428cc2003-06-15 22:40:42 +000073{
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +000074 unsigned char buffer[dev_desc->blksz];
Heiko Schocher633e03a2007-06-22 19:11:54 +020075 disk_partition_t info;
wdenk2c9b05d2003-09-10 22:30:53 +000076
77 if (!dev_desc->block_read)
78 return -1;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020079
Heiko Schocher633e03a2007-06-22 19:11:54 +020080 cur_dev = dev_desc;
wdenk2c9b05d2003-09-10 22:30:53 +000081 /* check if we have a MBR (on floppies we have only a PBR) */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020082 if (dev_desc->block_read(dev_desc->dev, 0, 1, (ulong *)buffer) != 1) {
83 printf("** Can't read from device %d **\n",
84 dev_desc->dev);
wdenk2c9b05d2003-09-10 22:30:53 +000085 return -1;
86 }
87 if (buffer[DOS_PART_MAGIC_OFFSET] != 0x55 ||
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020088 buffer[DOS_PART_MAGIC_OFFSET + 1] != 0xaa) {
wdenk2c9b05d2003-09-10 22:30:53 +000089 /* no signature found */
90 return -1;
91 }
Jon Loeliger80eb47c2007-07-09 17:56:50 -050092#if (defined(CONFIG_CMD_IDE) || \
unsik Kimf0b1bdd2009-02-25 11:31:24 +090093 defined(CONFIG_CMD_MG_DISK) || \
Sonic Zhang853208e2008-12-09 23:20:18 -050094 defined(CONFIG_CMD_SATA) || \
Jon Loeliger80eb47c2007-07-09 17:56:50 -050095 defined(CONFIG_CMD_SCSI) || \
96 defined(CONFIG_CMD_USB) || \
Andy Fleming55b86682008-01-09 13:51:32 -060097 defined(CONFIG_MMC) || \
98 defined(CONFIG_SYSTEMACE) )
Wolfgang Denk927b8ce2010-07-19 11:37:00 +020099 /* First we assume there is a MBR */
100 if (!get_partition_info(dev_desc, part_no, &info)) {
Andy Fleming55b86682008-01-09 13:51:32 -0600101 part_offset = info.start;
102 cur_part = part_no;
Donggeun Kim8f814002011-10-24 21:15:28 +0000103 part_size = info.size;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200104 } else if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) ||
105 (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) {
Andy Fleming55b86682008-01-09 13:51:32 -0600106 /* ok, we assume we are on a PBR only */
107 cur_part = 1;
108 part_offset = 0;
109 } else {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200110 printf("** Partition %d not valid on device %d **\n",
111 part_no, dev_desc->dev);
Andy Fleming55b86682008-01-09 13:51:32 -0600112 return -1;
113 }
114
wdenk2c9b05d2003-09-10 22:30:53 +0000115#else
Wolfgang Denk7b2290c2010-07-19 11:36:57 +0200116 if ((strncmp((char *)&buffer[DOS_FS_TYPE_OFFSET], "FAT", 3) == 0) ||
117 (strncmp((char *)&buffer[DOS_FS32_TYPE_OFFSET], "FAT32", 5) == 0)) {
Andy Fleming55b86682008-01-09 13:51:32 -0600118 /* ok, we assume we are on a PBR only */
119 cur_part = 1;
120 part_offset = 0;
121 info.start = part_offset;
122 } else {
123 /* FIXME we need to determine the start block of the
124 * partition where the DOS FS resides. This can be done
125 * by using the get_partition_info routine. For this
126 * purpose the libpart must be included.
127 */
128 part_offset = 32;
129 cur_part = 1;
Wolfgang Denke78f25892007-08-07 16:02:13 +0200130 }
Andy Fleming55b86682008-01-09 13:51:32 -0600131#endif
wdenk7a428cc2003-06-15 22:40:42 +0000132 return 0;
133}
134
wdenk7a428cc2003-06-15 22:40:42 +0000135/*
136 * Get the first occurence of a directory delimiter ('/' or '\') in a string.
137 * Return index into string if found, -1 otherwise.
138 */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200139static int dirdelim (char *str)
wdenk7a428cc2003-06-15 22:40:42 +0000140{
141 char *start = str;
142
143 while (*str != '\0') {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200144 if (ISDIRDELIM(*str))
145 return str - start;
wdenk7a428cc2003-06-15 22:40:42 +0000146 str++;
147 }
148 return -1;
149}
150
wdenk7a428cc2003-06-15 22:40:42 +0000151/*
152 * Extract zero terminated short name from a directory entry.
153 */
154static void get_name (dir_entry *dirent, char *s_name)
155{
156 char *ptr;
157
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200158 memcpy(s_name, dirent->name, 8);
wdenk7a428cc2003-06-15 22:40:42 +0000159 s_name[8] = '\0';
160 ptr = s_name;
161 while (*ptr && *ptr != ' ')
162 ptr++;
163 if (dirent->ext[0] && dirent->ext[0] != ' ') {
164 *ptr = '.';
165 ptr++;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200166 memcpy(ptr, dirent->ext, 3);
wdenk7a428cc2003-06-15 22:40:42 +0000167 ptr[3] = '\0';
168 while (*ptr && *ptr != ' ')
169 ptr++;
170 }
171 *ptr = '\0';
172 if (*s_name == DELETED_FLAG)
173 *s_name = '\0';
174 else if (*s_name == aRING)
Remy Bohmer7c8f2ce2008-11-27 22:30:27 +0100175 *s_name = DELETED_FLAG;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200176 downcase(s_name);
wdenk7a428cc2003-06-15 22:40:42 +0000177}
178
179/*
180 * Get the entry at index 'entry' in a FAT (12/16/32) table.
181 * On failure 0x00 is returned.
182 */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200183static __u32 get_fatent (fsdata *mydata, __u32 entry)
wdenk7a428cc2003-06-15 22:40:42 +0000184{
185 __u32 bufnum;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200186 __u32 off16, offset;
wdenk7a428cc2003-06-15 22:40:42 +0000187 __u32 ret = 0x00;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200188 __u16 val1, val2;
wdenk7a428cc2003-06-15 22:40:42 +0000189
190 switch (mydata->fatsize) {
191 case 32:
192 bufnum = entry / FAT32BUFSIZE;
193 offset = entry - bufnum * FAT32BUFSIZE;
194 break;
195 case 16:
196 bufnum = entry / FAT16BUFSIZE;
197 offset = entry - bufnum * FAT16BUFSIZE;
198 break;
199 case 12:
200 bufnum = entry / FAT12BUFSIZE;
201 offset = entry - bufnum * FAT12BUFSIZE;
202 break;
203
204 default:
205 /* Unsupported FAT size */
206 return ret;
207 }
208
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200209 debug("FAT%d: entry: 0x%04x = %d, offset: 0x%04x = %d\n",
210 mydata->fatsize, entry, entry, offset, offset);
Wolfgang Denka48d0a32010-07-19 11:36:58 +0200211
wdenk7a428cc2003-06-15 22:40:42 +0000212 /* Read a new block of FAT entries into the cache. */
213 if (bufnum != mydata->fatbufnum) {
Sergei Shtylyovd5916532011-08-08 09:39:29 +0000214 __u32 getsize = FATBUFBLOCKS;
wdenk7a428cc2003-06-15 22:40:42 +0000215 __u8 *bufptr = mydata->fatbuf;
216 __u32 fatlength = mydata->fatlength;
217 __u32 startblock = bufnum * FATBUFBLOCKS;
218
Sergei Shtylyovd5916532011-08-08 09:39:29 +0000219 if (getsize > fatlength)
220 getsize = fatlength;
221
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000222 fatlength *= mydata->sect_size; /* We want it in bytes now */
wdenk7a428cc2003-06-15 22:40:42 +0000223 startblock += mydata->fat_sect; /* Offset from start of disk */
224
wdenk7a428cc2003-06-15 22:40:42 +0000225 if (disk_read(startblock, getsize, bufptr) < 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200226 debug("Error reading FAT blocks\n");
wdenk7a428cc2003-06-15 22:40:42 +0000227 return ret;
228 }
229 mydata->fatbufnum = bufnum;
230 }
231
232 /* Get the actual entry from the table */
233 switch (mydata->fatsize) {
234 case 32:
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200235 ret = FAT2CPU32(((__u32 *) mydata->fatbuf)[offset]);
wdenk7a428cc2003-06-15 22:40:42 +0000236 break;
237 case 16:
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200238 ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[offset]);
wdenk7a428cc2003-06-15 22:40:42 +0000239 break;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200240 case 12:
241 off16 = (offset * 3) / 4;
wdenk7a428cc2003-06-15 22:40:42 +0000242
243 switch (offset & 0x3) {
244 case 0:
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200245 ret = FAT2CPU16(((__u16 *) mydata->fatbuf)[off16]);
wdenk7a428cc2003-06-15 22:40:42 +0000246 ret &= 0xfff;
247 break;
248 case 1:
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200249 val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
wdenk7a428cc2003-06-15 22:40:42 +0000250 val1 &= 0xf000;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200251 val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
wdenk7a428cc2003-06-15 22:40:42 +0000252 val2 &= 0x00ff;
253 ret = (val2 << 4) | (val1 >> 12);
254 break;
255 case 2:
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200256 val1 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
wdenk7a428cc2003-06-15 22:40:42 +0000257 val1 &= 0xff00;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200258 val2 = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16 + 1]);
wdenk7a428cc2003-06-15 22:40:42 +0000259 val2 &= 0x000f;
260 ret = (val2 << 8) | (val1 >> 8);
261 break;
262 case 3:
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200263 ret = FAT2CPU16(((__u16 *)mydata->fatbuf)[off16]);
wdenk7a428cc2003-06-15 22:40:42 +0000264 ret = (ret & 0xfff0) >> 4;
265 break;
266 default:
267 break;
268 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200269 break;
wdenk7a428cc2003-06-15 22:40:42 +0000270 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200271 debug("FAT%d: ret: %08x, offset: %04x\n",
272 mydata->fatsize, ret, offset);
wdenk7a428cc2003-06-15 22:40:42 +0000273
274 return ret;
275}
276
wdenk7a428cc2003-06-15 22:40:42 +0000277/*
278 * Read at most 'size' bytes from the specified cluster into 'buffer'.
279 * Return 0 on success, -1 otherwise.
280 */
281static int
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200282get_cluster (fsdata *mydata, __u32 clustnum, __u8 *buffer,
283 unsigned long size)
wdenk7a428cc2003-06-15 22:40:42 +0000284{
Erik Hansen4ea89662011-03-24 10:15:37 +0100285 __u32 idx = 0;
wdenk7a428cc2003-06-15 22:40:42 +0000286 __u32 startsect;
287
288 if (clustnum > 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200289 startsect = mydata->data_begin +
290 clustnum * mydata->clust_size;
wdenk7a428cc2003-06-15 22:40:42 +0000291 } else {
292 startsect = mydata->rootdir_sect;
293 }
294
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200295 debug("gc - clustnum: %d, startsect: %d\n", clustnum, startsect);
296
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000297 if (disk_read(startsect, size / mydata->sect_size, buffer) < 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200298 debug("Error reading data\n");
wdenk2c9b05d2003-09-10 22:30:53 +0000299 return -1;
300 }
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000301 if (size % mydata->sect_size) {
302 __u8 tmpbuf[mydata->sect_size];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200303
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000304 idx = size / mydata->sect_size;
wdenk2c9b05d2003-09-10 22:30:53 +0000305 if (disk_read(startsect + idx, 1, tmpbuf) < 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200306 debug("Error reading data\n");
wdenk2c9b05d2003-09-10 22:30:53 +0000307 return -1;
wdenk7a428cc2003-06-15 22:40:42 +0000308 }
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000309 buffer += idx * mydata->sect_size;
wdenk2c9b05d2003-09-10 22:30:53 +0000310
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000311 memcpy(buffer, tmpbuf, size % mydata->sect_size);
wdenk2c9b05d2003-09-10 22:30:53 +0000312 return 0;
wdenk7a428cc2003-06-15 22:40:42 +0000313 }
314
315 return 0;
316}
317
wdenk7a428cc2003-06-15 22:40:42 +0000318/*
319 * Read at most 'maxsize' bytes from the file associated with 'dentptr'
320 * into 'buffer'.
321 * Return the number of bytes read or -1 on fatal errors.
322 */
323static long
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200324get_contents (fsdata *mydata, dir_entry *dentptr, __u8 *buffer,
325 unsigned long maxsize)
wdenk7a428cc2003-06-15 22:40:42 +0000326{
327 unsigned long filesize = FAT2CPU32(dentptr->size), gotsize = 0;
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000328 unsigned int bytesperclust = mydata->clust_size * mydata->sect_size;
wdenk7a428cc2003-06-15 22:40:42 +0000329 __u32 curclust = START(dentptr);
wdenk2c9b05d2003-09-10 22:30:53 +0000330 __u32 endclust, newclust;
331 unsigned long actsize;
wdenk7a428cc2003-06-15 22:40:42 +0000332
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200333 debug("Filesize: %ld bytes\n", filesize);
wdenk7a428cc2003-06-15 22:40:42 +0000334
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200335 if (maxsize > 0 && filesize > maxsize)
336 filesize = maxsize;
wdenk7a428cc2003-06-15 22:40:42 +0000337
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200338 debug("%ld bytes\n", filesize);
wdenk7a428cc2003-06-15 22:40:42 +0000339
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200340 actsize = bytesperclust;
341 endclust = curclust;
342
wdenk7a428cc2003-06-15 22:40:42 +0000343 do {
wdenk2c9b05d2003-09-10 22:30:53 +0000344 /* search for consecutive clusters */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200345 while (actsize < filesize) {
wdenk2c9b05d2003-09-10 22:30:53 +0000346 newclust = get_fatent(mydata, endclust);
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200347 if ((newclust - 1) != endclust)
wdenk2c9b05d2003-09-10 22:30:53 +0000348 goto getit;
michael2c4d2982008-03-02 23:33:46 +0100349 if (CHECK_CLUST(newclust, mydata->fatsize)) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200350 debug("curclust: 0x%x\n", newclust);
351 debug("Invalid FAT entry\n");
wdenk2c9b05d2003-09-10 22:30:53 +0000352 return gotsize;
353 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200354 endclust = newclust;
355 actsize += bytesperclust;
wdenk2c9b05d2003-09-10 22:30:53 +0000356 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200357
wdenk2c9b05d2003-09-10 22:30:53 +0000358 /* actsize >= file size */
359 actsize -= bytesperclust;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200360
wdenk2c9b05d2003-09-10 22:30:53 +0000361 /* get remaining clusters */
362 if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200363 printf("Error reading cluster\n");
wdenk7a428cc2003-06-15 22:40:42 +0000364 return -1;
365 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200366
wdenk2c9b05d2003-09-10 22:30:53 +0000367 /* get remaining bytes */
368 gotsize += (int)actsize;
369 filesize -= actsize;
370 buffer += actsize;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200371 actsize = filesize;
wdenk2c9b05d2003-09-10 22:30:53 +0000372 if (get_cluster(mydata, endclust, buffer, (int)actsize) != 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200373 printf("Error reading cluster\n");
wdenk2c9b05d2003-09-10 22:30:53 +0000374 return -1;
375 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200376 gotsize += actsize;
wdenk2c9b05d2003-09-10 22:30:53 +0000377 return gotsize;
378getit:
379 if (get_cluster(mydata, curclust, buffer, (int)actsize) != 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200380 printf("Error reading cluster\n");
wdenk2c9b05d2003-09-10 22:30:53 +0000381 return -1;
382 }
383 gotsize += (int)actsize;
384 filesize -= actsize;
385 buffer += actsize;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200386
wdenk2c9b05d2003-09-10 22:30:53 +0000387 curclust = get_fatent(mydata, endclust);
michael2c4d2982008-03-02 23:33:46 +0100388 if (CHECK_CLUST(curclust, mydata->fatsize)) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200389 debug("curclust: 0x%x\n", curclust);
390 printf("Invalid FAT entry\n");
wdenk7a428cc2003-06-15 22:40:42 +0000391 return gotsize;
392 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200393 actsize = bytesperclust;
394 endclust = curclust;
wdenk7a428cc2003-06-15 22:40:42 +0000395 } while (1);
396}
397
wdenk7a428cc2003-06-15 22:40:42 +0000398#ifdef CONFIG_SUPPORT_VFAT
399/*
400 * Extract the file name information from 'slotptr' into 'l_name',
401 * starting at l_name[*idx].
402 * Return 1 if terminator (zero byte) is found, 0 otherwise.
403 */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200404static int slot2str (dir_slot *slotptr, char *l_name, int *idx)
wdenk7a428cc2003-06-15 22:40:42 +0000405{
406 int j;
407
408 for (j = 0; j <= 8; j += 2) {
409 l_name[*idx] = slotptr->name0_4[j];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200410 if (l_name[*idx] == 0x00)
411 return 1;
wdenk7a428cc2003-06-15 22:40:42 +0000412 (*idx)++;
413 }
414 for (j = 0; j <= 10; j += 2) {
415 l_name[*idx] = slotptr->name5_10[j];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200416 if (l_name[*idx] == 0x00)
417 return 1;
wdenk7a428cc2003-06-15 22:40:42 +0000418 (*idx)++;
419 }
420 for (j = 0; j <= 2; j += 2) {
421 l_name[*idx] = slotptr->name11_12[j];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200422 if (l_name[*idx] == 0x00)
423 return 1;
wdenk7a428cc2003-06-15 22:40:42 +0000424 (*idx)++;
425 }
426
427 return 0;
428}
429
wdenk7a428cc2003-06-15 22:40:42 +0000430/*
431 * Extract the full long filename starting at 'retdent' (which is really
432 * a slot) into 'l_name'. If successful also copy the real directory entry
433 * into 'retdent'
434 * Return 0 on success, -1 otherwise.
435 */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200436__attribute__ ((__aligned__ (__alignof__ (dir_entry))))
Bryan Wu95f99a42009-01-02 20:47:45 -0500437__u8 get_vfatname_block[MAX_CLUSTSIZE];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200438
wdenk7a428cc2003-06-15 22:40:42 +0000439static int
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200440get_vfatname (fsdata *mydata, int curclust, __u8 *cluster,
441 dir_entry *retdent, char *l_name)
wdenk7a428cc2003-06-15 22:40:42 +0000442{
443 dir_entry *realdent;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200444 dir_slot *slotptr = (dir_slot *)retdent;
Sergei Shtylyov51de6cf2011-08-19 09:37:46 +0000445 __u8 *buflimit = cluster + mydata->sect_size * ((curclust == 0) ?
446 PREFETCH_BLOCKS :
447 mydata->clust_size);
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200448 __u8 counter = (slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff;
wdenk7a428cc2003-06-15 22:40:42 +0000449 int idx = 0;
450
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300451 if (counter > VFAT_MAXSEQ) {
452 debug("Error: VFAT name is too long\n");
453 return -1;
454 }
455
456 while ((__u8 *)slotptr < buflimit) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200457 if (counter == 0)
458 break;
wdenk2ebee312004-02-23 19:30:57 +0000459 if (((slotptr->id & ~LAST_LONG_ENTRY_MASK) & 0xff) != counter)
460 return -1;
wdenk7a428cc2003-06-15 22:40:42 +0000461 slotptr++;
462 counter--;
463 }
464
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300465 if ((__u8 *)slotptr >= buflimit) {
wdenk7a428cc2003-06-15 22:40:42 +0000466 dir_slot *slotptr2;
467
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300468 if (curclust == 0)
469 return -1;
wdenk7a428cc2003-06-15 22:40:42 +0000470 curclust = get_fatent(mydata, curclust);
michael2c4d2982008-03-02 23:33:46 +0100471 if (CHECK_CLUST(curclust, mydata->fatsize)) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200472 debug("curclust: 0x%x\n", curclust);
473 printf("Invalid FAT entry\n");
wdenk7a428cc2003-06-15 22:40:42 +0000474 return -1;
475 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200476
wdenkb6c60cb32003-10-29 23:18:55 +0000477 if (get_cluster(mydata, curclust, get_vfatname_block,
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000478 mydata->clust_size * mydata->sect_size) != 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200479 debug("Error: reading directory block\n");
wdenk7a428cc2003-06-15 22:40:42 +0000480 return -1;
481 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200482
483 slotptr2 = (dir_slot *)get_vfatname_block;
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300484 while (counter > 0) {
485 if (((slotptr2->id & ~LAST_LONG_ENTRY_MASK)
486 & 0xff) != counter)
487 return -1;
wdenk7a428cc2003-06-15 22:40:42 +0000488 slotptr2++;
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300489 counter--;
490 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200491
wdenk7a428cc2003-06-15 22:40:42 +0000492 /* Save the real directory entry */
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300493 realdent = (dir_entry *)slotptr2;
494 while ((__u8 *)slotptr2 > get_vfatname_block) {
wdenk7a428cc2003-06-15 22:40:42 +0000495 slotptr2--;
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300496 slot2str(slotptr2, l_name, &idx);
wdenk7a428cc2003-06-15 22:40:42 +0000497 }
498 } else {
499 /* Save the real directory entry */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200500 realdent = (dir_entry *)slotptr;
wdenk7a428cc2003-06-15 22:40:42 +0000501 }
502
503 do {
504 slotptr--;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200505 if (slot2str(slotptr, l_name, &idx))
506 break;
wdenk2ebee312004-02-23 19:30:57 +0000507 } while (!(slotptr->id & LAST_LONG_ENTRY_MASK));
wdenk7a428cc2003-06-15 22:40:42 +0000508
509 l_name[idx] = '\0';
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200510 if (*l_name == DELETED_FLAG)
511 *l_name = '\0';
512 else if (*l_name == aRING)
513 *l_name = DELETED_FLAG;
wdenk7a428cc2003-06-15 22:40:42 +0000514 downcase(l_name);
515
516 /* Return the real directory entry */
517 memcpy(retdent, realdent, sizeof(dir_entry));
518
519 return 0;
520}
521
wdenk7a428cc2003-06-15 22:40:42 +0000522/* Calculate short name checksum */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200523static __u8 mkcksum (const char *str)
wdenk7a428cc2003-06-15 22:40:42 +0000524{
525 int i;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200526
wdenk7a428cc2003-06-15 22:40:42 +0000527 __u8 ret = 0;
528
529 for (i = 0; i < 11; i++) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200530 ret = (((ret & 1) << 7) | ((ret & 0xfe) >> 1)) + str[i];
wdenk7a428cc2003-06-15 22:40:42 +0000531 }
532
533 return ret;
534}
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200535#endif /* CONFIG_SUPPORT_VFAT */
wdenk7a428cc2003-06-15 22:40:42 +0000536
537/*
538 * Get the directory entry associated with 'filename' from the directory
539 * starting at 'startsect'
540 */
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200541__attribute__ ((__aligned__ (__alignof__ (dir_entry))))
wdenkb6c60cb32003-10-29 23:18:55 +0000542__u8 get_dentfromdir_block[MAX_CLUSTSIZE];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200543
544static dir_entry *get_dentfromdir (fsdata *mydata, int startsect,
545 char *filename, dir_entry *retdent,
wdenk7a428cc2003-06-15 22:40:42 +0000546 int dols)
547{
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200548 __u16 prevcksum = 0xffff;
549 __u32 curclust = START(retdent);
550 int files = 0, dirs = 0;
wdenk7a428cc2003-06-15 22:40:42 +0000551
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200552 debug("get_dentfromdir: %s\n", filename);
wdenk7a428cc2003-06-15 22:40:42 +0000553
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200554 while (1) {
555 dir_entry *dentptr;
556
557 int i;
558
559 if (get_cluster(mydata, curclust, get_dentfromdir_block,
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000560 mydata->clust_size * mydata->sect_size) != 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200561 debug("Error: reading directory block\n");
562 return NULL;
563 }
wdenk7a428cc2003-06-15 22:40:42 +0000564
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200565 dentptr = (dir_entry *)get_dentfromdir_block;
566
567 for (i = 0; i < DIRENTSPERCLUST; i++) {
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300568 char s_name[14], l_name[VFAT_MAXLEN_BYTES];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200569
570 l_name[0] = '\0';
571 if (dentptr->name[0] == DELETED_FLAG) {
572 dentptr++;
573 continue;
574 }
575 if ((dentptr->attr & ATTR_VOLUME)) {
wdenk7a428cc2003-06-15 22:40:42 +0000576#ifdef CONFIG_SUPPORT_VFAT
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200577 if ((dentptr->attr & ATTR_VFAT) &&
578 (dentptr-> name[0] & LAST_LONG_ENTRY_MASK)) {
579 prevcksum = ((dir_slot *)dentptr)->alias_checksum;
580 get_vfatname(mydata, curclust,
581 get_dentfromdir_block,
582 dentptr, l_name);
583 if (dols) {
584 int isdir;
585 char dirc;
586 int doit = 0;
wdenk7a428cc2003-06-15 22:40:42 +0000587
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200588 isdir = (dentptr->attr & ATTR_DIR);
589
590 if (isdir) {
591 dirs++;
592 dirc = '/';
593 doit = 1;
594 } else {
595 dirc = ' ';
596 if (l_name[0] != 0) {
597 files++;
598 doit = 1;
599 }
600 }
601 if (doit) {
602 if (dirc == ' ') {
603 printf(" %8ld %s%c\n",
604 (long)FAT2CPU32(dentptr->size),
605 l_name,
606 dirc);
607 } else {
608 printf(" %s%c\n",
609 l_name,
610 dirc);
611 }
612 }
613 dentptr++;
614 continue;
615 }
616 debug("vfatname: |%s|\n", l_name);
617 } else
618#endif
619 {
620 /* Volume label or VFAT entry */
621 dentptr++;
622 continue;
623 }
wdenk7a428cc2003-06-15 22:40:42 +0000624 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200625 if (dentptr->name[0] == 0) {
626 if (dols) {
627 printf("\n%d file(s), %d dir(s)\n\n",
628 files, dirs);
629 }
630 debug("Dentname == NULL - %d\n", i);
631 return NULL;
wdenk7a428cc2003-06-15 22:40:42 +0000632 }
wdenk7a428cc2003-06-15 22:40:42 +0000633#ifdef CONFIG_SUPPORT_VFAT
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200634 if (dols && mkcksum(dentptr->name) == prevcksum) {
635 dentptr++;
636 continue;
637 }
wdenk7a428cc2003-06-15 22:40:42 +0000638#endif
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200639 get_name(dentptr, s_name);
640 if (dols) {
641 int isdir = (dentptr->attr & ATTR_DIR);
642 char dirc;
643 int doit = 0;
wdenk7a428cc2003-06-15 22:40:42 +0000644
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200645 if (isdir) {
646 dirs++;
647 dirc = '/';
648 doit = 1;
649 } else {
650 dirc = ' ';
651 if (s_name[0] != 0) {
652 files++;
653 doit = 1;
654 }
655 }
wdenk7a428cc2003-06-15 22:40:42 +0000656
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200657 if (doit) {
658 if (dirc == ' ') {
659 printf(" %8ld %s%c\n",
660 (long)FAT2CPU32(dentptr->size),
661 s_name, dirc);
662 } else {
663 printf(" %s%c\n",
664 s_name, dirc);
665 }
666 }
wdenk7a428cc2003-06-15 22:40:42 +0000667
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200668 dentptr++;
669 continue;
670 }
671
672 if (strcmp(filename, s_name)
673 && strcmp(filename, l_name)) {
674 debug("Mismatch: |%s|%s|\n", s_name, l_name);
675 dentptr++;
676 continue;
677 }
678
679 memcpy(retdent, dentptr, sizeof(dir_entry));
680
681 debug("DentName: %s", s_name);
682 debug(", start: 0x%x", START(dentptr));
683 debug(", size: 0x%x %s\n",
684 FAT2CPU32(dentptr->size),
685 (dentptr->attr & ATTR_DIR) ? "(DIR)" : "");
686
687 return retdent;
688 }
689
690 curclust = get_fatent(mydata, curclust);
691 if (CHECK_CLUST(curclust, mydata->fatsize)) {
692 debug("curclust: 0x%x\n", curclust);
693 printf("Invalid FAT entry\n");
694 return NULL;
695 }
wdenk7a428cc2003-06-15 22:40:42 +0000696 }
wdenk7a428cc2003-06-15 22:40:42 +0000697
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200698 return NULL;
wdenk7a428cc2003-06-15 22:40:42 +0000699}
700
wdenk7a428cc2003-06-15 22:40:42 +0000701/*
702 * Read boot sector and volume info from a FAT filesystem
703 */
704static int
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200705read_bootsectandvi (boot_sector *bs, volume_info *volinfo, int *fatsize)
wdenk7a428cc2003-06-15 22:40:42 +0000706{
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000707 __u8 *block;
wdenk7a428cc2003-06-15 22:40:42 +0000708 volume_info *vistart;
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000709 int ret = 0;
710
711 if (cur_dev == NULL) {
712 debug("Error: no device selected\n");
713 return -1;
714 }
715
716 block = malloc(cur_dev->blksz);
717 if (block == NULL) {
718 debug("Error: allocating block\n");
719 return -1;
720 }
wdenk7a428cc2003-06-15 22:40:42 +0000721
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200722 if (disk_read (0, 1, block) < 0) {
723 debug("Error: reading block\n");
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000724 goto fail;
wdenk7a428cc2003-06-15 22:40:42 +0000725 }
726
727 memcpy(bs, block, sizeof(boot_sector));
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200728 bs->reserved = FAT2CPU16(bs->reserved);
729 bs->fat_length = FAT2CPU16(bs->fat_length);
730 bs->secs_track = FAT2CPU16(bs->secs_track);
731 bs->heads = FAT2CPU16(bs->heads);
732 bs->total_sect = FAT2CPU32(bs->total_sect);
wdenk7a428cc2003-06-15 22:40:42 +0000733
734 /* FAT32 entries */
735 if (bs->fat_length == 0) {
736 /* Assume FAT32 */
737 bs->fat32_length = FAT2CPU32(bs->fat32_length);
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200738 bs->flags = FAT2CPU16(bs->flags);
wdenk7a428cc2003-06-15 22:40:42 +0000739 bs->root_cluster = FAT2CPU32(bs->root_cluster);
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200740 bs->info_sector = FAT2CPU16(bs->info_sector);
741 bs->backup_boot = FAT2CPU16(bs->backup_boot);
742 vistart = (volume_info *)(block + sizeof(boot_sector));
wdenk7a428cc2003-06-15 22:40:42 +0000743 *fatsize = 32;
744 } else {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200745 vistart = (volume_info *)&(bs->fat32_length);
wdenk7a428cc2003-06-15 22:40:42 +0000746 *fatsize = 0;
747 }
748 memcpy(volinfo, vistart, sizeof(volume_info));
749
wdenk7a428cc2003-06-15 22:40:42 +0000750 if (*fatsize == 32) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200751 if (strncmp(FAT32_SIGN, vistart->fs_type, SIGNLEN) == 0)
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000752 goto exit;
wdenk7a428cc2003-06-15 22:40:42 +0000753 } else {
Tom Rix155abaa2009-05-20 07:55:41 -0500754 if (strncmp(FAT12_SIGN, vistart->fs_type, SIGNLEN) == 0) {
wdenk7a428cc2003-06-15 22:40:42 +0000755 *fatsize = 12;
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000756 goto exit;
wdenk7a428cc2003-06-15 22:40:42 +0000757 }
Tom Rix155abaa2009-05-20 07:55:41 -0500758 if (strncmp(FAT16_SIGN, vistart->fs_type, SIGNLEN) == 0) {
wdenk7a428cc2003-06-15 22:40:42 +0000759 *fatsize = 16;
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000760 goto exit;
wdenk7a428cc2003-06-15 22:40:42 +0000761 }
762 }
763
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200764 debug("Error: broken fs_type sign\n");
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000765fail:
766 ret = -1;
767exit:
768 free(block);
769 return ret;
wdenk7a428cc2003-06-15 22:40:42 +0000770}
771
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200772__attribute__ ((__aligned__ (__alignof__ (dir_entry))))
Bryan Wu95f99a42009-01-02 20:47:45 -0500773__u8 do_fat_read_block[MAX_CLUSTSIZE];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200774
stroese83082242004-12-16 17:57:26 +0000775long
wdenk7a428cc2003-06-15 22:40:42 +0000776do_fat_read (const char *filename, void *buffer, unsigned long maxsize,
777 int dols)
778{
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200779 char fnamecopy[2048];
780 boot_sector bs;
781 volume_info volinfo;
782 fsdata datablock;
783 fsdata *mydata = &datablock;
784 dir_entry *dentptr;
785 __u16 prevcksum = 0xffff;
786 char *subname = "";
Erik Hansen4ea89662011-03-24 10:15:37 +0100787 __u32 cursect;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200788 int idx, isdir = 0;
789 int files = 0, dirs = 0;
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000790 long ret = -1;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200791 int firsttime;
Sergei Shtylyov42cdc9f2011-08-19 09:32:34 +0000792 __u32 root_cluster = 0;
Erik Hansen4ea89662011-03-24 10:15:37 +0100793 int rootdir_size = 0;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200794 int j;
wdenk7a428cc2003-06-15 22:40:42 +0000795
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200796 if (read_bootsectandvi(&bs, &volinfo, &mydata->fatsize)) {
797 debug("Error: reading boot sector\n");
798 return -1;
799 }
800
Sergei Shtylyov42cdc9f2011-08-19 09:32:34 +0000801 if (mydata->fatsize == 32) {
802 root_cluster = bs.root_cluster;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200803 mydata->fatlength = bs.fat32_length;
Sergei Shtylyov42cdc9f2011-08-19 09:32:34 +0000804 } else {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200805 mydata->fatlength = bs.fat_length;
Sergei Shtylyov42cdc9f2011-08-19 09:32:34 +0000806 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200807
808 mydata->fat_sect = bs.reserved;
809
810 cursect = mydata->rootdir_sect
811 = mydata->fat_sect + mydata->fatlength * bs.fats;
812
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000813 mydata->sect_size = (bs.sector_size[1] << 8) + bs.sector_size[0];
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200814 mydata->clust_size = bs.cluster_size;
Wolfgang Denka48d0a32010-07-19 11:36:58 +0200815
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200816 if (mydata->fatsize == 32) {
817 mydata->data_begin = mydata->rootdir_sect -
818 (mydata->clust_size * 2);
819 } else {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200820 rootdir_size = ((bs.dir_entries[1] * (int)256 +
821 bs.dir_entries[0]) *
822 sizeof(dir_entry)) /
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000823 mydata->sect_size;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200824 mydata->data_begin = mydata->rootdir_sect +
825 rootdir_size -
826 (mydata->clust_size * 2);
827 }
828
829 mydata->fatbufnum = -1;
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000830 mydata->fatbuf = malloc(FATBUFSIZE);
831 if (mydata->fatbuf == NULL) {
832 debug("Error: allocating memory\n");
833 return -1;
834 }
wdenk7a428cc2003-06-15 22:40:42 +0000835
Wolfgang Denka48d0a32010-07-19 11:36:58 +0200836#ifdef CONFIG_SUPPORT_VFAT
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200837 debug("VFAT Support enabled\n");
Wolfgang Denka48d0a32010-07-19 11:36:58 +0200838#endif
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200839 debug("FAT%d, fat_sect: %d, fatlength: %d\n",
840 mydata->fatsize, mydata->fat_sect, mydata->fatlength);
841 debug("Rootdir begins at cluster: %d, sector: %d, offset: %x\n"
842 "Data begins at: %d\n",
843 root_cluster,
844 mydata->rootdir_sect,
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000845 mydata->rootdir_sect * mydata->sect_size, mydata->data_begin);
846 debug("Sector size: %d, cluster size: %d\n", mydata->sect_size,
847 mydata->clust_size);
wdenk7a428cc2003-06-15 22:40:42 +0000848
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200849 /* "cwd" is always the root... */
850 while (ISDIRDELIM(*filename))
851 filename++;
wdenk7a428cc2003-06-15 22:40:42 +0000852
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200853 /* Make a copy of the filename and convert it to lowercase */
854 strcpy(fnamecopy, filename);
855 downcase(fnamecopy);
wdenk7a428cc2003-06-15 22:40:42 +0000856
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200857 if (*fnamecopy == '\0') {
858 if (!dols)
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000859 goto exit;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200860
861 dols = LS_ROOT;
862 } else if ((idx = dirdelim(fnamecopy)) >= 0) {
863 isdir = 1;
864 fnamecopy[idx] = '\0';
865 subname = fnamecopy + idx + 1;
866
867 /* Handle multiple delimiters */
868 while (ISDIRDELIM(*subname))
869 subname++;
870 } else if (dols) {
871 isdir = 1;
wdenk7a428cc2003-06-15 22:40:42 +0000872 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200873
874 j = 0;
875 while (1) {
876 int i;
877
878 debug("FAT read sect=%d, clust_size=%d, DIRENTSPERBLOCK=%d\n",
879 cursect, mydata->clust_size, DIRENTSPERBLOCK);
880
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300881 if (disk_read(cursect,
882 (mydata->fatsize == 32) ?
883 (mydata->clust_size) :
Sergei Shtylyov51de6cf2011-08-19 09:37:46 +0000884 PREFETCH_BLOCKS,
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300885 do_fat_read_block) < 0) {
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200886 debug("Error: reading rootdir block\n");
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000887 goto exit;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200888 }
889
890 dentptr = (dir_entry *) do_fat_read_block;
891
892 for (i = 0; i < DIRENTSPERBLOCK; i++) {
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300893 char s_name[14], l_name[VFAT_MAXLEN_BYTES];
wdenk7a428cc2003-06-15 22:40:42 +0000894
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200895 l_name[0] = '\0';
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300896 if (dentptr->name[0] == DELETED_FLAG) {
897 dentptr++;
898 continue;
899 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200900 if ((dentptr->attr & ATTR_VOLUME)) {
wdenk7a428cc2003-06-15 22:40:42 +0000901#ifdef CONFIG_SUPPORT_VFAT
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200902 if ((dentptr->attr & ATTR_VFAT) &&
903 (dentptr->name[0] & LAST_LONG_ENTRY_MASK)) {
904 prevcksum =
905 ((dir_slot *)dentptr)->alias_checksum;
wdenk7a428cc2003-06-15 22:40:42 +0000906
Mikhail Zolotaryov45d513e2010-09-08 17:06:03 +0300907 get_vfatname(mydata,
Sergei Shtylyov42cdc9f2011-08-19 09:32:34 +0000908 root_cluster,
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200909 do_fat_read_block,
910 dentptr, l_name);
911
912 if (dols == LS_ROOT) {
913 char dirc;
914 int doit = 0;
915 int isdir =
916 (dentptr->attr & ATTR_DIR);
917
918 if (isdir) {
919 dirs++;
920 dirc = '/';
921 doit = 1;
922 } else {
923 dirc = ' ';
924 if (l_name[0] != 0) {
925 files++;
926 doit = 1;
927 }
928 }
929 if (doit) {
930 if (dirc == ' ') {
931 printf(" %8ld %s%c\n",
932 (long)FAT2CPU32(dentptr->size),
933 l_name,
934 dirc);
935 } else {
936 printf(" %s%c\n",
937 l_name,
938 dirc);
939 }
940 }
941 dentptr++;
942 continue;
943 }
944 debug("Rootvfatname: |%s|\n",
945 l_name);
946 } else
wdenk7a428cc2003-06-15 22:40:42 +0000947#endif
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200948 {
949 /* Volume label or VFAT entry */
950 dentptr++;
951 continue;
952 }
953 } else if (dentptr->name[0] == 0) {
954 debug("RootDentname == NULL - %d\n", i);
955 if (dols == LS_ROOT) {
956 printf("\n%d file(s), %d dir(s)\n\n",
957 files, dirs);
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000958 ret = 0;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200959 }
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +0000960 goto exit;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200961 }
wdenk7a428cc2003-06-15 22:40:42 +0000962#ifdef CONFIG_SUPPORT_VFAT
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200963 else if (dols == LS_ROOT &&
964 mkcksum(dentptr->name) == prevcksum) {
965 dentptr++;
966 continue;
967 }
wdenk7a428cc2003-06-15 22:40:42 +0000968#endif
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200969 get_name(dentptr, s_name);
wdenk7a428cc2003-06-15 22:40:42 +0000970
Wolfgang Denk927b8ce2010-07-19 11:37:00 +0200971 if (dols == LS_ROOT) {
972 int isdir = (dentptr->attr & ATTR_DIR);
973 char dirc;
974 int doit = 0;
975
976 if (isdir) {
977 dirc = '/';
978 if (s_name[0] != 0) {
979 dirs++;
980 doit = 1;
981 }
982 } else {
983 dirc = ' ';
984 if (s_name[0] != 0) {
985 files++;
986 doit = 1;
987 }
988 }
989 if (doit) {
990 if (dirc == ' ') {
991 printf(" %8ld %s%c\n",
992 (long)FAT2CPU32(dentptr->size),
993 s_name, dirc);
994 } else {
995 printf(" %s%c\n",
996 s_name, dirc);
997 }
998 }
999 dentptr++;
1000 continue;
1001 }
1002
1003 if (strcmp(fnamecopy, s_name)
1004 && strcmp(fnamecopy, l_name)) {
1005 debug("RootMismatch: |%s|%s|\n", s_name,
1006 l_name);
1007 dentptr++;
1008 continue;
1009 }
1010
1011 if (isdir && !(dentptr->attr & ATTR_DIR))
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +00001012 goto exit;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001013
1014 debug("RootName: %s", s_name);
1015 debug(", start: 0x%x", START(dentptr));
1016 debug(", size: 0x%x %s\n",
1017 FAT2CPU32(dentptr->size),
1018 isdir ? "(DIR)" : "");
1019
1020 goto rootdir_done; /* We got a match */
wdenk7a428cc2003-06-15 22:40:42 +00001021 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001022 debug("END LOOP: j=%d clust_size=%d\n", j,
1023 mydata->clust_size);
wdenk7a428cc2003-06-15 22:40:42 +00001024
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001025 /*
1026 * On FAT32 we must fetch the FAT entries for the next
1027 * root directory clusters when a cluster has been
1028 * completely processed.
1029 */
Erik Hansen4ea89662011-03-24 10:15:37 +01001030 ++j;
1031 int fat32_end = 0;
1032 if ((mydata->fatsize == 32) && (j == mydata->clust_size)) {
1033 int nxtsect = 0;
1034 int nxt_clust = 0;
wdenk7a428cc2003-06-15 22:40:42 +00001035
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001036 nxt_clust = get_fatent(mydata, root_cluster);
Erik Hansen4ea89662011-03-24 10:15:37 +01001037 fat32_end = CHECK_CLUST(nxt_clust, 32);
Wolfgang Denka48d0a32010-07-19 11:36:58 +02001038
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001039 nxtsect = mydata->data_begin +
1040 (nxt_clust * mydata->clust_size);
Wolfgang Denka48d0a32010-07-19 11:36:58 +02001041
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001042 root_cluster = nxt_clust;
1043
1044 cursect = nxtsect;
1045 j = 0;
1046 } else {
1047 cursect++;
1048 }
Erik Hansen4ea89662011-03-24 10:15:37 +01001049
1050 /* If end of rootdir reached */
1051 if ((mydata->fatsize == 32 && fat32_end) ||
1052 (mydata->fatsize != 32 && j == rootdir_size)) {
1053 if (dols == LS_ROOT) {
1054 printf("\n%d file(s), %d dir(s)\n\n",
1055 files, dirs);
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +00001056 ret = 0;
Erik Hansen4ea89662011-03-24 10:15:37 +01001057 }
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +00001058 goto exit;
Erik Hansen4ea89662011-03-24 10:15:37 +01001059 }
Wolfgang Denka48d0a32010-07-19 11:36:58 +02001060 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001061rootdir_done:
wdenk7a428cc2003-06-15 22:40:42 +00001062
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001063 firsttime = 1;
wdenk7a428cc2003-06-15 22:40:42 +00001064
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001065 while (isdir) {
1066 int startsect = mydata->data_begin
1067 + START(dentptr) * mydata->clust_size;
1068 dir_entry dent;
1069 char *nextname = NULL;
wdenk7a428cc2003-06-15 22:40:42 +00001070
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001071 dent = *dentptr;
1072 dentptr = &dent;
wdenk7a428cc2003-06-15 22:40:42 +00001073
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001074 idx = dirdelim(subname);
wdenk7a428cc2003-06-15 22:40:42 +00001075
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001076 if (idx >= 0) {
1077 subname[idx] = '\0';
1078 nextname = subname + idx + 1;
1079 /* Handle multiple delimiters */
1080 while (ISDIRDELIM(*nextname))
1081 nextname++;
1082 if (dols && *nextname == '\0')
1083 firsttime = 0;
1084 } else {
1085 if (dols && firsttime) {
1086 firsttime = 0;
1087 } else {
1088 isdir = 0;
1089 }
1090 }
1091
1092 if (get_dentfromdir(mydata, startsect, subname, dentptr,
1093 isdir ? 0 : dols) == NULL) {
1094 if (dols && !isdir)
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +00001095 ret = 0;
1096 goto exit;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001097 }
1098
1099 if (idx >= 0) {
1100 if (!(dentptr->attr & ATTR_DIR))
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +00001101 goto exit;
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001102 subname = nextname;
1103 }
wdenk7a428cc2003-06-15 22:40:42 +00001104 }
wdenk7a428cc2003-06-15 22:40:42 +00001105
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001106 ret = get_contents(mydata, dentptr, buffer, maxsize);
1107 debug("Size: %d, got: %ld\n", FAT2CPU32(dentptr->size), ret);
wdenk7a428cc2003-06-15 22:40:42 +00001108
Sergei Shtylyov5ab3ba72011-08-08 09:38:33 +00001109exit:
1110 free(mydata->fatbuf);
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001111 return ret;
1112}
wdenk7a428cc2003-06-15 22:40:42 +00001113
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001114int file_fat_detectfs (void)
wdenk7a428cc2003-06-15 22:40:42 +00001115{
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001116 boot_sector bs;
1117 volume_info volinfo;
1118 int fatsize;
1119 char vol_label[12];
wdenk7a428cc2003-06-15 22:40:42 +00001120
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001121 if (cur_dev == NULL) {
wdenk2c9b05d2003-09-10 22:30:53 +00001122 printf("No current device\n");
1123 return 1;
1124 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001125
Jon Loeliger80eb47c2007-07-09 17:56:50 -05001126#if defined(CONFIG_CMD_IDE) || \
unsik Kimf0b1bdd2009-02-25 11:31:24 +09001127 defined(CONFIG_CMD_MG_DISK) || \
Sonic Zhang853208e2008-12-09 23:20:18 -05001128 defined(CONFIG_CMD_SATA) || \
Jon Loeliger80eb47c2007-07-09 17:56:50 -05001129 defined(CONFIG_CMD_SCSI) || \
1130 defined(CONFIG_CMD_USB) || \
Andy Fleming1efe5782008-01-16 13:06:59 -06001131 defined(CONFIG_MMC)
wdenk2c9b05d2003-09-10 22:30:53 +00001132 printf("Interface: ");
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001133 switch (cur_dev->if_type) {
1134 case IF_TYPE_IDE:
1135 printf("IDE");
1136 break;
1137 case IF_TYPE_SATA:
1138 printf("SATA");
1139 break;
1140 case IF_TYPE_SCSI:
1141 printf("SCSI");
1142 break;
1143 case IF_TYPE_ATAPI:
1144 printf("ATAPI");
1145 break;
1146 case IF_TYPE_USB:
1147 printf("USB");
1148 break;
1149 case IF_TYPE_DOC:
1150 printf("DOC");
1151 break;
1152 case IF_TYPE_MMC:
1153 printf("MMC");
1154 break;
1155 default:
1156 printf("Unknown");
wdenk2c9b05d2003-09-10 22:30:53 +00001157 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001158
1159 printf("\n Device %d: ", cur_dev->dev);
wdenk2c9b05d2003-09-10 22:30:53 +00001160 dev_print(cur_dev);
1161#endif
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001162
1163 if (read_bootsectandvi(&bs, &volinfo, &fatsize)) {
wdenk2c9b05d2003-09-10 22:30:53 +00001164 printf("\nNo valid FAT fs found\n");
1165 return 1;
1166 }
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001167
1168 memcpy(vol_label, volinfo.volume_label, 11);
wdenk2c9b05d2003-09-10 22:30:53 +00001169 vol_label[11] = '\0';
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001170 volinfo.fs_type[5] = '\0';
1171
1172 printf("Partition %d: Filesystem: %s \"%s\"\n", cur_part,
1173 volinfo.fs_type, vol_label);
1174
wdenk2c9b05d2003-09-10 22:30:53 +00001175 return 0;
wdenk7a428cc2003-06-15 22:40:42 +00001176}
1177
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001178int file_fat_ls (const char *dir)
wdenk7a428cc2003-06-15 22:40:42 +00001179{
1180 return do_fat_read(dir, NULL, 0, LS_YES);
1181}
1182
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001183long file_fat_read (const char *filename, void *buffer, unsigned long maxsize)
wdenk7a428cc2003-06-15 22:40:42 +00001184{
Wolfgang Denk927b8ce2010-07-19 11:37:00 +02001185 printf("reading %s\n", filename);
wdenk7a428cc2003-06-15 22:40:42 +00001186 return do_fat_read(filename, buffer, maxsize, LS_NO);
1187}