aboutsummaryrefslogtreecommitdiff
path: root/util-linux/volume_id/fat.c
diff options
context:
space:
mode:
Diffstat (limited to 'util-linux/volume_id/fat.c')
-rw-r--r--util-linux/volume_id/fat.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/util-linux/volume_id/fat.c b/util-linux/volume_id/fat.c
new file mode 100644
index 000000000..4c2a91749
--- /dev/null
+++ b/util-linux/volume_id/fat.c
@@ -0,0 +1,333 @@
1/*
2 * volume_id - reads filesystem label and uuid
3 *
4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21#include "volume_id_internal.h"
22
23#define FAT12_MAX 0xff5
24#define FAT16_MAX 0xfff5
25#define FAT_ATTR_VOLUME_ID 0x08
26#define FAT_ATTR_DIR 0x10
27#define FAT_ATTR_LONG_NAME 0x0f
28#define FAT_ATTR_MASK 0x3f
29#define FAT_ENTRY_FREE 0xe5
30
31struct vfat_super_block {
32 uint8_t boot_jump[3];
33 uint8_t sysid[8];
34 uint16_t sector_size;
35 uint8_t sectors_per_cluster;
36 uint16_t reserved;
37 uint8_t fats;
38 uint16_t dir_entries;
39 uint16_t sectors;
40 uint8_t media;
41 uint16_t fat_length;
42 uint16_t secs_track;
43 uint16_t heads;
44 uint32_t hidden;
45 uint32_t total_sect;
46 union {
47 struct fat_super_block {
48 uint8_t unknown[3];
49 uint8_t serno[4];
50 uint8_t label[11];
51 uint8_t magic[8];
52 uint8_t dummy2[192];
53 uint8_t pmagic[2];
54 } __attribute__((__packed__)) fat;
55 struct fat32_super_block {
56 uint32_t fat32_length;
57 uint16_t flags;
58 uint8_t version[2];
59 uint32_t root_cluster;
60 uint16_t insfo_sector;
61 uint16_t backup_boot;
62 uint16_t reserved2[6];
63 uint8_t unknown[3];
64 uint8_t serno[4];
65 uint8_t label[11];
66 uint8_t magic[8];
67 uint8_t dummy2[164];
68 uint8_t pmagic[2];
69 } __attribute__((__packed__)) fat32;
70 } __attribute__((__packed__)) type;
71} __attribute__((__packed__));
72
73struct vfat_dir_entry {
74 uint8_t name[11];
75 uint8_t attr;
76 uint16_t time_creat;
77 uint16_t date_creat;
78 uint16_t time_acc;
79 uint16_t date_acc;
80 uint16_t cluster_high;
81 uint16_t time_write;
82 uint16_t date_write;
83 uint16_t cluster_low;
84 uint32_t size;
85} __attribute__((__packed__));
86
87static uint8_t *get_attr_volume_id(struct vfat_dir_entry *dir, unsigned count)
88{
89 unsigned i;
90
91 for (i = 0; i < count; i++) {
92 /* end marker */
93 if (dir[i].name[0] == 0x00) {
94 dbg("end of dir");
95 break;
96 }
97
98 /* empty entry */
99 if (dir[i].name[0] == FAT_ENTRY_FREE)
100 continue;
101
102 /* long name */
103 if ((dir[i].attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)
104 continue;
105
106 if ((dir[i].attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) {
107 /* labels do not have file data */
108 if (dir[i].cluster_high != 0 || dir[i].cluster_low != 0)
109 continue;
110
111 dbg("found ATTR_VOLUME_ID id in root dir");
112 return dir[i].name;
113 }
114
115 dbg("skip dir entry");
116 }
117
118 return NULL;
119}
120
121int volume_id_probe_vfat(struct volume_id *id, uint64_t off)
122{
123 struct vfat_super_block *vs;
124 struct vfat_dir_entry *dir;
125 uint16_t sector_size;
126 uint16_t dir_entries;
127 uint32_t sect_count;
128 uint16_t reserved;
129 uint32_t fat_size;
130 uint32_t root_cluster;
131 uint32_t dir_size;
132 uint32_t cluster_count;
133 uint32_t fat_length;
134 uint64_t root_start;
135 uint32_t start_data_sect;
136 uint16_t root_dir_entries;
137 uint8_t *buf;
138 uint32_t buf_size;
139 uint8_t *label = NULL;
140 uint32_t next;
141 int maxloop;
142
143 dbg("probing at offset 0x%llx", (unsigned long long) off);
144
145 vs = volume_id_get_buffer(id, off, 0x200);
146 if (vs == NULL)
147 return -1;
148
149 /* believe only that's fat, don't trust the version
150 * the cluster_count will tell us
151 */
152 if (memcmp(vs->sysid, "NTFS", 4) == 0)
153 return -1;
154
155 if (memcmp(vs->type.fat32.magic, "MSWIN", 5) == 0)
156 goto valid;
157
158 if (memcmp(vs->type.fat32.magic, "FAT32 ", 8) == 0)
159 goto valid;
160
161 if (memcmp(vs->type.fat.magic, "FAT16 ", 8) == 0)
162 goto valid;
163
164 if (memcmp(vs->type.fat.magic, "MSDOS", 5) == 0)
165 goto valid;
166
167 if (memcmp(vs->type.fat.magic, "FAT12 ", 8) == 0)
168 goto valid;
169
170 /*
171 * There are old floppies out there without a magic, so we check
172 * for well known values and guess if it's a fat volume
173 */
174
175 /* boot jump address check */
176 if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90) &&
177 vs->boot_jump[0] != 0xe9)
178 return -1;
179
180 /* heads check */
181 if (vs->heads == 0)
182 return -1;
183
184 /* cluster size check*/
185 if (vs->sectors_per_cluster == 0 ||
186 (vs->sectors_per_cluster & (vs->sectors_per_cluster-1)))
187 return -1;
188
189 /* media check */
190 if (vs->media < 0xf8 && vs->media != 0xf0)
191 return -1;
192
193 /* fat count*/
194 if (vs->fats != 2)
195 return -1;
196
197 valid:
198 /* sector size check */
199 sector_size = le16_to_cpu(vs->sector_size);
200 if (sector_size != 0x200 && sector_size != 0x400 &&
201 sector_size != 0x800 && sector_size != 0x1000)
202 return -1;
203
204 dbg("sector_size 0x%x", sector_size);
205 dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster);
206
207 dir_entries = le16_to_cpu(vs->dir_entries);
208 reserved = le16_to_cpu(vs->reserved);
209 dbg("reserved 0x%x", reserved);
210
211 sect_count = le16_to_cpu(vs->sectors);
212 if (sect_count == 0)
213 sect_count = le32_to_cpu(vs->total_sect);
214 dbg("sect_count 0x%x", sect_count);
215
216 fat_length = le16_to_cpu(vs->fat_length);
217 if (fat_length == 0)
218 fat_length = le32_to_cpu(vs->type.fat32.fat32_length);
219 dbg("fat_length 0x%x", fat_length);
220
221 fat_size = fat_length * vs->fats;
222 dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
223 (sector_size-1)) / sector_size;
224 dbg("dir_size 0x%x", dir_size);
225
226 cluster_count = sect_count - (reserved + fat_size + dir_size);
227 cluster_count /= vs->sectors_per_cluster;
228 dbg("cluster_count 0x%x", cluster_count);
229
230 if (cluster_count < FAT12_MAX) {
231 strcpy(id->type_version, "FAT12");
232 } else if (cluster_count < FAT16_MAX) {
233 strcpy(id->type_version, "FAT16");
234 } else {
235 strcpy(id->type_version, "FAT32");
236 goto fat32;
237 }
238
239 /* the label may be an attribute in the root directory */
240 root_start = (reserved + fat_size) * sector_size;
241 dbg("root dir start 0x%llx", (unsigned long long) root_start);
242 root_dir_entries = le16_to_cpu(vs->dir_entries);
243 dbg("expected entries 0x%x", root_dir_entries);
244
245 buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
246 buf = volume_id_get_buffer(id, off + root_start, buf_size);
247 if (buf == NULL)
248 goto found;
249
250 dir = (struct vfat_dir_entry*) buf;
251
252 label = get_attr_volume_id(dir, root_dir_entries);
253
254 vs = volume_id_get_buffer(id, off, 0x200);
255 if (vs == NULL)
256 return -1;
257
258 if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) {
259 volume_id_set_label_raw(id, label, 11);
260 volume_id_set_label_string(id, label, 11);
261 } else if (memcmp(vs->type.fat.label, "NO NAME ", 11) != 0) {
262 volume_id_set_label_raw(id, vs->type.fat.label, 11);
263 volume_id_set_label_string(id, vs->type.fat.label, 11);
264 }
265 volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS);
266 goto found;
267
268 fat32:
269 /* FAT32 root dir is a cluster chain like any other directory */
270 buf_size = vs->sectors_per_cluster * sector_size;
271 root_cluster = le32_to_cpu(vs->type.fat32.root_cluster);
272 dbg("root dir cluster %u", root_cluster);
273 start_data_sect = reserved + fat_size;
274
275 next = root_cluster;
276 maxloop = 100;
277 while (--maxloop) {
278 uint32_t next_sect_off;
279 uint64_t next_off;
280 uint64_t fat_entry_off;
281 int count;
282
283 dbg("next cluster %u", next);
284 next_sect_off = (next - 2) * vs->sectors_per_cluster;
285 next_off = (start_data_sect + next_sect_off) * sector_size;
286 dbg("cluster offset 0x%llx", (unsigned long long) next_off);
287
288 /* get cluster */
289 buf = volume_id_get_buffer(id, off + next_off, buf_size);
290 if (buf == NULL)
291 goto found;
292
293 dir = (struct vfat_dir_entry*) buf;
294 count = buf_size / sizeof(struct vfat_dir_entry);
295 dbg("expected entries 0x%x", count);
296
297 label = get_attr_volume_id(dir, count);
298 if (label)
299 break;
300
301 /* get FAT entry */
302 fat_entry_off = (reserved * sector_size) + (next * sizeof(uint32_t));
303 buf = volume_id_get_buffer(id, off + fat_entry_off, buf_size);
304 if (buf == NULL)
305 goto found;
306
307 /* set next cluster */
308 next = le32_to_cpu(*((uint32_t *) buf) & 0x0fffffff);
309 if (next == 0)
310 break;
311 }
312 if (maxloop == 0)
313 dbg("reached maximum follow count of root cluster chain, give up");
314
315 vs = volume_id_get_buffer(id, off, 0x200);
316 if (vs == NULL)
317 return -1;
318
319 if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) {
320 volume_id_set_label_raw(id, label, 11);
321 volume_id_set_label_string(id, label, 11);
322 } else if (memcmp(vs->type.fat32.label, "NO NAME ", 11) != 0) {
323 volume_id_set_label_raw(id, vs->type.fat32.label, 11);
324 volume_id_set_label_string(id, vs->type.fat32.label, 11);
325 }
326 volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS);
327
328 found:
329 volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
330 id->type = "vfat";
331
332 return 0;
333}