1 // Copyright 2009 Segher Boessenkool <segher@kernel.crashing.org>
2 // This code is licensed to you under the terms of the GNU GPL, version 2;
3 // see file COPYING or http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt
14 static u8 raw_buf[RAW_BUF] __attribute__((aligned(32)));
16 static int raw_read(u32 sector)
18 static u32 current = -1;
20 if (current == sector)
24 return sd_read_sector(raw_buf, sector);
27 static u64 partition_start_offset;
29 static int read(u8 *data, u64 offset, u32 len)
31 offset += partition_start_offset;
34 u32 buf_off = offset % RAW_BUF;
37 n = RAW_BUF - buf_off;
41 int err = raw_read(offset / RAW_BUF);
45 memcpy(data, raw_buf + buf_off, n);
56 static u32 bytes_per_cluster;
57 static u32 root_entries;
59 static u32 fat_type; // 12, 16, or 32
61 static u64 fat_offset;
62 static u64 root_offset;
63 static u64 data_offset;
66 static u32 get_fat(u32 cluster)
70 u32 offset_bits = cluster*fat_type;
71 int err = read(fat, fat_offset + offset_bits/8, 4);
75 u32 res = le32(fat) >> (offset_bits % 8);
76 res &= (1 << fat_type) - 1;
77 res &= 0x0fffffff; // for FAT32
83 static u64 extent_offset;
84 static u32 extent_len;
85 static u32 extent_next_cluster;
87 static void get_extent(u32 cluster)
90 extent_next_cluster = 0;
92 if (cluster == 0) { // Root directory.
94 extent_offset = root_offset;
95 extent_len = 0x20*root_entries;
99 cluster = root_offset;
102 if (cluster - 2 >= clusters)
105 extent_offset = data_offset + (u64)bytes_per_cluster*(cluster - 2);
108 extent_len += bytes_per_cluster;
110 u32 next_cluster = get_fat(cluster);
112 if (next_cluster - 2 >= clusters)
115 if (next_cluster != cluster + 1) {
116 extent_next_cluster = next_cluster;
120 cluster = next_cluster;
125 static int read_extent(u8 *data, u32 len)
132 if (this > extent_len)
135 int err = read(data, extent_offset, this);
139 extent_offset += this;
145 if (extent_len == 0 && extent_next_cluster)
146 get_extent(extent_next_cluster);
153 int fat_read(void *data, u32 len)
155 return read_extent(data, len);
159 static u8 fat_name[11];
161 static u8 ucase(char c)
163 if (c >= 'a' && c <= 'z')
164 return c - 'a' + 'A';
169 static const char *parse_component(const char *path)
176 while (*path && *path != '/' && *path != '.') {
178 fat_name[i++] = ucase(*path);
188 while (*path && *path != '/') {
190 fat_name[i++] = ucase(*path);
197 if (fat_name[0] == 0xe5)
206 int fat_open(const char *name)
213 name = parse_component(name);
218 int err = read_extent(dir, 0x20);
225 if (dir[0x0b] & 0x08) // volume label or LFN
227 if (dir[0x00] == 0xe5) // deleted file
230 if (!!*name != !!(dir[0x0b] & 0x10)) // dir vs. file
233 if (memcmp(fat_name, dir, 11) == 0) {
234 cluster = le16(dir + 0x1a);
236 cluster |= le16(dir + 0x14) << 16;
239 fat_file_size = le32(dir + 0x1c);
255 static void print_dir_entry(u8 *dir)
259 if (dir[0x0b] & 0x08) // volume label or LFN
261 if (dir[0x00] == 0xe5) // deleted file
264 if (fat_type == 32) {
265 fprintf(stderr, "#%04x", le16(dir + 0x14));
266 fprintf(stderr, "%04x ", le16(dir + 0x1a));
268 fprintf(stderr, "#%04x ", le16(dir + 0x1a)); // start cluster
269 u16 date = le16(dir + 0x18);
270 fprintf(stderr, "%04d-%02d-%02d ", 1980 + (date >> 9), (date >> 5) & 0x0f, date & 0x1f);
271 u16 time = le16(dir + 0x16);
272 fprintf(stderr, "%02d:%02d:%02d ", time >> 11, (time >> 5) & 0x3f, 2*(time & 0x1f));
273 fprintf(stderr, "%10d ", le32(dir + 0x1c)); // file size
275 for (i = 0; i < 6; i++)
276 fprintf(stderr, "%c", (attr & (1 << i)) ? "RHSLDA"[i] : ' ');
277 fprintf(stderr, " ");
278 for (n = 8; n && dir[n - 1] == ' '; n--)
280 for (i = 0; i < n; i++)
281 fprintf(stderr, "%c", dir[i]);
282 for (n = 3; n && dir[8 + n - 1] == ' '; n--)
285 fprintf(stderr, ".");
286 for (i = 0; i < n; i++)
287 fprintf(stderr, "%c", dir[8 + i]);
290 fprintf(stderr, "\n");
294 int print_dir(u32 cluster)
301 int err = read_extent(dir, 0x20);
308 print_dir_entry(dir);
316 static int fat_init_fs(const u8 *sb)
318 u32 bytes_per_sector = le16(sb + 0x0b);
319 u32 sectors_per_cluster = sb[0x0d];
320 bytes_per_cluster = bytes_per_sector * sectors_per_cluster;
322 u32 reserved_sectors = le16(sb + 0x0e);
324 root_entries = le16(sb + 0x11);
325 u32 total_sectors = le16(sb + 0x13);
326 u32 sectors_per_fat = le16(sb + 0x16);
328 // For FAT16 and FAT32:
329 if (total_sectors == 0)
330 total_sectors = le32(sb + 0x20);
333 if (sectors_per_fat == 0)
334 sectors_per_fat = le32(sb + 0x24);
336 // XXX: For FAT32, we might want to look at offsets 28, 2a
337 // XXX: We _do_ need to look at 2c
339 u32 fat_sectors = sectors_per_fat * fats;
340 u32 root_sectors = (0x20*root_entries + bytes_per_sector - 1)
343 u32 fat_start_sector = reserved_sectors;
344 u32 root_start_sector = fat_start_sector + fat_sectors;
345 u32 data_start_sector = root_start_sector + root_sectors;
347 clusters = (total_sectors - data_start_sector) / sectors_per_cluster;
349 if (clusters < 0x0ff5)
351 else if (clusters < 0xfff5)
356 fat_offset = (u64)bytes_per_sector*fat_start_sector;
357 root_offset = (u64)bytes_per_sector*root_start_sector;
358 data_offset = (u64)bytes_per_sector*data_start_sector;
361 root_offset = le32(sb + 0x2c);
364 fprintf(stderr, "bytes_per_sector = %08x\n", bytes_per_sector);
365 fprintf(stderr, "sectors_per_cluster = %08x\n", sectors_per_cluster);
366 fprintf(stderr, "bytes_per_cluster = %08x\n", bytes_per_cluster);
367 fprintf(stderr, "root_entries = %08x\n", root_entries);
368 fprintf(stderr, "clusters = %08x\n", clusters);
369 fprintf(stderr, "fat_type = %08x\n", fat_type);
370 fprintf(stderr, "fat_offset = %012llx\n", fat_offset);
371 fprintf(stderr, "root_offset = %012llx\n", root_offset);
372 fprintf(stderr, "data_offset = %012llx\n", data_offset);
379 static int is_fat_fs(const u8 *sb)
381 // Bytes per sector should be 512, 1024, 2048, or 4096
382 u32 bps = le16(sb + 0x0b);
383 if (bps < 0x0200 || bps > 0x1000 || bps & (bps - 1))
386 // Media type should be f0 or f8,...,ff
387 if (sb[0x15] < 0xf8 && sb[0x15] != 0xf0)
390 // If those checks didn't fail, it's FAT. We hope.
400 partition_start_offset = 0;
401 err = read(buf, 0, 0x200);
405 if (le16(buf + 0x01fe) != 0xaa55) // Not a DOS disk.
409 return fat_init_fs(buf);
411 // Maybe there's a partition table? Let's try the first partition.
412 if (buf[0x01c2] == 0)
415 partition_start_offset = 0x200ULL*le32(buf + 0x01c6);
417 err = read(buf, 0, 0x200);
422 return fat_init_fs(buf);