Initial public busybox upstream commit
[busybox4maemo] / util-linux / volume_id / fat.c
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
31 struct 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
73 struct 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
87 static 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
121 int 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         if (cluster_count >= FAT16_MAX)
239                 goto fat32;
240
241         /* the label may be an attribute in the root directory */
242         root_start = (reserved + fat_size) * sector_size;
243         dbg("root dir start 0x%llx", (unsigned long long) root_start);
244         root_dir_entries = le16_to_cpu(vs->dir_entries);
245         dbg("expected entries 0x%x", root_dir_entries);
246
247         buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
248         buf = volume_id_get_buffer(id, off + root_start, buf_size);
249         if (buf == NULL)
250                 goto found;
251
252         dir = (struct vfat_dir_entry*) buf;
253
254         label = get_attr_volume_id(dir, root_dir_entries);
255
256         vs = volume_id_get_buffer(id, off, 0x200);
257         if (vs == NULL)
258                 return -1;
259
260         if (label != NULL && memcmp(label, "NO NAME    ", 11) != 0) {
261 //              volume_id_set_label_raw(id, label, 11);
262                 volume_id_set_label_string(id, label, 11);
263         } else if (memcmp(vs->type.fat.label, "NO NAME    ", 11) != 0) {
264 //              volume_id_set_label_raw(id, vs->type.fat.label, 11);
265                 volume_id_set_label_string(id, vs->type.fat.label, 11);
266         }
267         volume_id_set_uuid(id, vs->type.fat.serno, UUID_DOS);
268         goto found;
269
270  fat32:
271         /* FAT32 root dir is a cluster chain like any other directory */
272         buf_size = vs->sectors_per_cluster * sector_size;
273         root_cluster = le32_to_cpu(vs->type.fat32.root_cluster);
274         dbg("root dir cluster %u", root_cluster);
275         start_data_sect = reserved + fat_size;
276
277         next = root_cluster;
278         maxloop = 100;
279         while (--maxloop) {
280                 uint32_t next_sect_off;
281                 uint64_t next_off;
282                 uint64_t fat_entry_off;
283                 int count;
284
285                 dbg("next cluster %u", next);
286                 next_sect_off = (next - 2) * vs->sectors_per_cluster;
287                 next_off = (start_data_sect + next_sect_off) * sector_size;
288                 dbg("cluster offset 0x%llx", (unsigned long long) next_off);
289
290                 /* get cluster */
291                 buf = volume_id_get_buffer(id, off + next_off, buf_size);
292                 if (buf == NULL)
293                         goto found;
294
295                 dir = (struct vfat_dir_entry*) buf;
296                 count = buf_size / sizeof(struct vfat_dir_entry);
297                 dbg("expected entries 0x%x", count);
298
299                 label = get_attr_volume_id(dir, count);
300                 if (label)
301                         break;
302
303                 /* get FAT entry */
304                 fat_entry_off = (reserved * sector_size) + (next * sizeof(uint32_t));
305                 buf = volume_id_get_buffer(id, off + fat_entry_off, buf_size);
306                 if (buf == NULL)
307                         goto found;
308
309                 /* set next cluster */
310                 next = le32_to_cpu(*((uint32_t *) buf) & 0x0fffffff);
311                 if (next == 0)
312                         break;
313         }
314         if (maxloop == 0)
315                 dbg("reached maximum follow count of root cluster chain, give up");
316
317         vs = volume_id_get_buffer(id, off, 0x200);
318         if (vs == NULL)
319                 return -1;
320
321         if (label != NULL && memcmp(label, "NO NAME    ", 11) != 0) {
322 //              volume_id_set_label_raw(id, label, 11);
323                 volume_id_set_label_string(id, label, 11);
324         } else if (memcmp(vs->type.fat32.label, "NO NAME    ", 11) != 0) {
325 //              volume_id_set_label_raw(id, vs->type.fat32.label, 11);
326                 volume_id_set_label_string(id, vs->type.fat32.label, 11);
327         }
328         volume_id_set_uuid(id, vs->type.fat32.serno, UUID_DOS);
329
330  found:
331 //      volume_id_set_usage(id, VOLUME_ID_FILESYSTEM);
332 //      id->type = "vfat";
333
334         return 0;
335 }