source: MondoRescue/branches/3.3/mindi-busybox/util-linux/volume_id/fat.c@ 3625

Last change on this file since 3625 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

  • Property svn:eol-style set to native
File size: 9.3 KB
Line 
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//kbuild:lib-$(CONFIG_FEATURE_VOLUMEID_FAT) += fat.o
22
23//config:
24//config:config FEATURE_VOLUMEID_FAT
25//config: bool "fat filesystem"
26//config: default y
27//config: depends on VOLUMEID
28//config: help
29//config: TODO
30//config:
31
32#include "volume_id_internal.h"
33
34/* linux/msdos_fs.h says: */
35#define FAT12_MAX 0xff4
36#define FAT16_MAX 0xfff4
37#define FAT32_MAX 0x0ffffff6
38
39#define FAT_ATTR_VOLUME_ID 0x08
40#define FAT_ATTR_DIR 0x10
41#define FAT_ATTR_LONG_NAME 0x0f
42#define FAT_ATTR_MASK 0x3f
43#define FAT_ENTRY_FREE 0xe5
44
45struct vfat_super_block {
46 uint8_t boot_jump[3];
47 uint8_t sysid[8];
48 uint16_t sector_size_bytes;
49 uint8_t sectors_per_cluster;
50 uint16_t reserved_sct;
51 uint8_t fats;
52 uint16_t dir_entries;
53 uint16_t sectors;
54 uint8_t media;
55 uint16_t fat_length;
56 uint16_t secs_track;
57 uint16_t heads;
58 uint32_t hidden;
59 uint32_t total_sect;
60 union {
61 struct fat_super_block {
62 uint8_t unknown[3];
63 uint8_t serno[4];
64 uint8_t label[11];
65 uint8_t magic[8];
66 uint8_t dummy2[192];
67 uint8_t pmagic[2];
68 } PACKED fat;
69 struct fat32_super_block {
70 uint32_t fat32_length;
71 uint16_t flags;
72 uint8_t version[2];
73 uint32_t root_cluster;
74 uint16_t insfo_sector;
75 uint16_t backup_boot;
76 uint16_t reserved2[6];
77 uint8_t unknown[3];
78 uint8_t serno[4];
79 uint8_t label[11];
80 uint8_t magic[8];
81 uint8_t dummy2[164];
82 uint8_t pmagic[2];
83 } PACKED fat32;
84 } PACKED type;
85} PACKED;
86
87struct vfat_dir_entry {
88 uint8_t name[11];
89 uint8_t attr;
90 uint16_t time_creat;
91 uint16_t date_creat;
92 uint16_t time_acc;
93 uint16_t date_acc;
94 uint16_t cluster_high;
95 uint16_t time_write;
96 uint16_t date_write;
97 uint16_t cluster_low;
98 uint32_t size;
99} PACKED;
100
101static uint8_t *get_attr_volume_id(struct vfat_dir_entry *dir, int count)
102{
103 for (;--count >= 0; dir++) {
104 /* end marker */
105 if (dir->name[0] == 0x00) {
106 dbg("end of dir");
107 break;
108 }
109
110 /* empty entry */
111 if (dir->name[0] == FAT_ENTRY_FREE)
112 continue;
113
114 /* long name */
115 if ((dir->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME)
116 continue;
117
118 if ((dir->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) == FAT_ATTR_VOLUME_ID) {
119 /* labels do not have file data */
120 if (dir->cluster_high != 0 || dir->cluster_low != 0)
121 continue;
122
123 dbg("found ATTR_VOLUME_ID id in root dir");
124 return dir->name;
125 }
126
127 dbg("skip dir entry");
128 }
129
130 return NULL;
131}
132
133int FAST_FUNC volume_id_probe_vfat(struct volume_id *id /*,uint64_t fat_partition_off*/)
134{
135#define fat_partition_off ((uint64_t)0)
136 struct vfat_super_block *vs;
137 struct vfat_dir_entry *dir;
138 uint16_t sector_size_bytes;
139 uint16_t dir_entries;
140 uint32_t sect_count;
141 uint16_t reserved_sct;
142 uint32_t fat_size_sct;
143 uint32_t root_cluster;
144 uint32_t dir_size_sct;
145 uint32_t cluster_count;
146 uint64_t root_start_off;
147 uint32_t start_data_sct;
148 uint8_t *buf;
149 uint32_t buf_size;
150 uint8_t *label = NULL;
151 uint32_t next_cluster;
152 int maxloop;
153
154 dbg("probing at offset 0x%llx", (unsigned long long) fat_partition_off);
155
156 vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
157 if (vs == NULL)
158 return -1;
159
160 /* believe only that's fat, don't trust the version
161 * the cluster_count will tell us
162 */
163 if (memcmp(vs->sysid, "NTFS", 4) == 0)
164 return -1;
165
166 if (memcmp(vs->type.fat32.magic, "MSWIN", 5) == 0)
167 goto valid;
168
169 if (memcmp(vs->type.fat32.magic, "FAT32 ", 8) == 0)
170 goto valid;
171
172 if (memcmp(vs->type.fat.magic, "FAT16 ", 8) == 0)
173 goto valid;
174
175 if (memcmp(vs->type.fat.magic, "MSDOS", 5) == 0)
176 goto valid;
177
178 if (memcmp(vs->type.fat.magic, "FAT12 ", 8) == 0)
179 goto valid;
180
181 /*
182 * There are old floppies out there without a magic, so we check
183 * for well known values and guess if it's a fat volume
184 */
185
186 /* boot jump address check */
187 if ((vs->boot_jump[0] != 0xeb || vs->boot_jump[2] != 0x90)
188 && vs->boot_jump[0] != 0xe9
189 ) {
190 return -1;
191 }
192
193 /* heads check */
194 if (vs->heads == 0)
195 return -1;
196
197 /* cluster size check */
198 if (vs->sectors_per_cluster == 0
199 || (vs->sectors_per_cluster & (vs->sectors_per_cluster-1))
200 ) {
201 return -1;
202 }
203
204 /* media check */
205 if (vs->media < 0xf8 && vs->media != 0xf0)
206 return -1;
207
208 /* fat count*/
209 if (vs->fats != 2)
210 return -1;
211
212 valid:
213 /* sector size check */
214 sector_size_bytes = le16_to_cpu(vs->sector_size_bytes);
215 if (sector_size_bytes != 0x200 && sector_size_bytes != 0x400
216 && sector_size_bytes != 0x800 && sector_size_bytes != 0x1000
217 ) {
218 return -1;
219 }
220
221 dbg("sector_size_bytes 0x%x", sector_size_bytes);
222 dbg("sectors_per_cluster 0x%x", vs->sectors_per_cluster);
223
224 reserved_sct = le16_to_cpu(vs->reserved_sct);
225 dbg("reserved_sct 0x%x", reserved_sct);
226
227 sect_count = le16_to_cpu(vs->sectors);
228 if (sect_count == 0)
229 sect_count = le32_to_cpu(vs->total_sect);
230 dbg("sect_count 0x%x", sect_count);
231
232 fat_size_sct = le16_to_cpu(vs->fat_length);
233 if (fat_size_sct == 0)
234 fat_size_sct = le32_to_cpu(vs->type.fat32.fat32_length);
235 fat_size_sct *= vs->fats;
236 dbg("fat_size_sct 0x%x", fat_size_sct);
237
238 dir_entries = le16_to_cpu(vs->dir_entries);
239 dir_size_sct = ((dir_entries * sizeof(struct vfat_dir_entry)) +
240 (sector_size_bytes-1)) / sector_size_bytes;
241 dbg("dir_size_sct 0x%x", dir_size_sct);
242
243 cluster_count = sect_count - (reserved_sct + fat_size_sct + dir_size_sct);
244 cluster_count /= vs->sectors_per_cluster;
245 dbg("cluster_count 0x%x", cluster_count);
246
247// if (cluster_count < FAT12_MAX) {
248// strcpy(id->type_version, "FAT12");
249// } else if (cluster_count < FAT16_MAX) {
250// strcpy(id->type_version, "FAT16");
251// } else {
252// strcpy(id->type_version, "FAT32");
253// goto fat32;
254// }
255 if (cluster_count >= FAT16_MAX)
256 goto fat32;
257
258 /* the label may be an attribute in the root directory */
259 root_start_off = (reserved_sct + fat_size_sct) * sector_size_bytes;
260 dbg("root dir start 0x%llx", (unsigned long long) root_start_off);
261 dbg("expected entries 0x%x", dir_entries);
262
263 buf_size = dir_entries * sizeof(struct vfat_dir_entry);
264 buf = volume_id_get_buffer(id, fat_partition_off + root_start_off, buf_size);
265 if (buf == NULL)
266 goto ret;
267
268 label = get_attr_volume_id((struct vfat_dir_entry*) buf, dir_entries);
269
270 vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
271 if (vs == NULL)
272 return -1;
273
274 if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) {
275// volume_id_set_label_raw(id, label, 11);
276 volume_id_set_label_string(id, label, 11);
277 } else if (memcmp(vs->type.fat.label, "NO NAME ", 11) != 0) {
278// volume_id_set_label_raw(id, vs->type.fat.label, 11);
279 volume_id_set_label_string(id, vs->type.fat.label, 11);
280 }
281 volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS);
282 goto ret;
283
284 fat32:
285 /* FAT32 root dir is a cluster chain like any other directory */
286 buf_size = vs->sectors_per_cluster * sector_size_bytes;
287 root_cluster = le32_to_cpu(vs->type.fat32.root_cluster);
288 start_data_sct = reserved_sct + fat_size_sct;
289
290 next_cluster = root_cluster;
291 maxloop = 100;
292 while (--maxloop) {
293 uint64_t next_off_sct;
294 uint64_t next_off;
295 uint64_t fat_entry_off;
296 int count;
297
298 dbg("next_cluster 0x%x", (unsigned)next_cluster);
299 next_off_sct = (uint64_t)(next_cluster - 2) * vs->sectors_per_cluster;
300 next_off = (start_data_sct + next_off_sct) * sector_size_bytes;
301 dbg("cluster offset 0x%llx", (unsigned long long) next_off);
302
303 /* get cluster */
304 buf = volume_id_get_buffer(id, fat_partition_off + next_off, buf_size);
305 if (buf == NULL)
306 goto ret;
307
308 dir = (struct vfat_dir_entry*) buf;
309 count = buf_size / sizeof(struct vfat_dir_entry);
310 dbg("expected entries 0x%x", count);
311
312 label = get_attr_volume_id(dir, count);
313 if (label)
314 break;
315
316 /* get FAT entry */
317 fat_entry_off = (reserved_sct * sector_size_bytes) + (next_cluster * sizeof(uint32_t));
318 dbg("fat_entry_off 0x%llx", (unsigned long long)fat_entry_off);
319 buf = volume_id_get_buffer(id, fat_partition_off + fat_entry_off, buf_size);
320 if (buf == NULL)
321 goto ret;
322
323 /* set next cluster */
324 next_cluster = le32_to_cpu(*(uint32_t*)buf) & 0x0fffffff;
325 if (next_cluster < 2 || next_cluster > FAT32_MAX)
326 break;
327 }
328 if (maxloop == 0)
329 dbg("reached maximum follow count of root cluster chain, give up");
330
331 vs = volume_id_get_buffer(id, fat_partition_off, 0x200);
332 if (vs == NULL)
333 return -1;
334
335 if (label != NULL && memcmp(label, "NO NAME ", 11) != 0) {
336// volume_id_set_label_raw(id, label, 11);
337 volume_id_set_label_string(id, label, 11);
338 } else if (memcmp(vs->type.fat32.label, "NO NAME ", 11) != 0) {
339// volume_id_set_label_raw(id, vs->type.fat32.label, 11);
340 volume_id_set_label_string(id, vs->type.fat32.label, 11);
341 }
342 volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS);
343
344 ret:
345// volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
346 IF_FEATURE_BLKID_TYPE(id->type = "vfat";)
347
348 return 0;
349}
Note: See TracBrowser for help on using the repository browser.