Upload Tizen:Base source
[framework/base/util-linux-ng.git] / shlibs / blkid / src / probers / vfat.c
1 /*
2  * Copyright (C) 1999 by Andries Brouwer
3  * Copyright (C) 1999, 2000, 2003 by Theodore Ts'o
4  * Copyright (C) 2001 by Andreas Dilger
5  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
6  * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
7  *
8  * This file may be redistributed under the terms of the
9  * GNU Lesser General Public License.
10  */
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <errno.h>
16 #include <ctype.h>
17 #include <stdint.h>
18
19 #include "blkidP.h"
20
21 /* Yucky misaligned values */
22 struct vfat_super_block {
23 /* 00*/ unsigned char   vs_ignored[3];
24 /* 03*/ unsigned char   vs_sysid[8];
25 /* 0b*/ unsigned char   vs_sector_size[2];
26 /* 0d*/ uint8_t         vs_cluster_size;
27 /* 0e*/ uint16_t        vs_reserved;
28 /* 10*/ uint8_t         vs_fats;
29 /* 11*/ unsigned char   vs_dir_entries[2];
30 /* 13*/ unsigned char   vs_sectors[2];
31 /* 15*/ unsigned char   vs_media;
32 /* 16*/ uint16_t        vs_fat_length;
33 /* 18*/ uint16_t        vs_secs_track;
34 /* 1a*/ uint16_t        vs_heads;
35 /* 1c*/ uint32_t        vs_hidden;
36 /* 20*/ uint32_t        vs_total_sect;
37 /* 24*/ uint32_t        vs_fat32_length;
38 /* 28*/ uint16_t        vs_flags;
39 /* 2a*/ uint8_t         vs_version[2];
40 /* 2c*/ uint32_t        vs_root_cluster;
41 /* 30*/ uint16_t        vs_fsinfo_sector;
42 /* 32*/ uint16_t        vs_backup_boot;
43 /* 34*/ uint16_t        vs_reserved2[6];
44 /* 40*/ unsigned char   vs_unknown[3];
45 /* 43*/ unsigned char   vs_serno[4];
46 /* 47*/ unsigned char   vs_label[11];
47 /* 52*/ unsigned char   vs_magic[8];
48 /* 5a*/ unsigned char   vs_dummy2[0x1fe - 0x5a];
49 /*1fe*/ unsigned char   vs_pmagic[2];
50 } __attribute__((packed));
51
52 /* Yucky misaligned values */
53 struct msdos_super_block {
54 /* 00*/ unsigned char   ms_ignored[3];
55 /* 03*/ unsigned char   ms_sysid[8];
56 /* 0b*/ unsigned char   ms_sector_size[2];
57 /* 0d*/ uint8_t         ms_cluster_size;
58 /* 0e*/ uint16_t        ms_reserved;
59 /* 10*/ uint8_t         ms_fats;
60 /* 11*/ unsigned char   ms_dir_entries[2];
61 /* 13*/ unsigned char   ms_sectors[2];
62 /* 15*/ unsigned char   ms_media;
63 /* 16*/ uint16_t        ms_fat_length;
64 /* 18*/ uint16_t        ms_secs_track;
65 /* 1a*/ uint16_t        ms_heads;
66 /* 1c*/ uint32_t        ms_hidden;
67 /* 20*/ uint32_t        ms_total_sect;
68 /* 24*/ unsigned char   ms_unknown[3];
69 /* 27*/ unsigned char   ms_serno[4];
70 /* 2b*/ unsigned char   ms_label[11];
71 /* 36*/ unsigned char   ms_magic[8];
72 /* 3e*/ unsigned char   ms_dummy2[0x1fe - 0x3e];
73 /*1fe*/ unsigned char   ms_pmagic[2];
74 } __attribute__((packed));
75
76 struct vfat_dir_entry {
77         uint8_t         name[11];
78         uint8_t         attr;
79         uint16_t        time_creat;
80         uint16_t        date_creat;
81         uint16_t        time_acc;
82         uint16_t        date_acc;
83         uint16_t        cluster_high;
84         uint16_t        time_write;
85         uint16_t        date_write;
86         uint16_t        cluster_low;
87         uint32_t        size;
88 } __attribute__((packed));
89
90 struct fat32_fsinfo {
91         uint8_t signature1[4];
92         uint32_t reserved1[120];
93         uint8_t signature2[4];
94         uint32_t free_clusters;
95         uint32_t next_cluster;
96         uint32_t reserved2[4];
97 } __attribute__((packed));
98
99 /* maximum number of clusters */
100 #define FAT12_MAX 0xFF4
101 #define FAT16_MAX 0xFFF4
102 #define FAT32_MAX 0x0FFFFFF6
103
104 #define FAT_ATTR_VOLUME_ID              0x08
105 #define FAT_ATTR_DIR                    0x10
106 #define FAT_ATTR_LONG_NAME              0x0f
107 #define FAT_ATTR_MASK                   0x3f
108 #define FAT_ENTRY_FREE                  0xe5
109
110 static const char *no_name = "NO NAME    ";
111
112 static unsigned char *search_fat_label(struct vfat_dir_entry *dir, int count)
113 {
114         int i;
115
116         for (i = 0; i < count; i++) {
117                 if (dir[i].name[0] == 0x00)
118                         break;
119
120                 if ((dir[i].name[0] == FAT_ENTRY_FREE) ||
121                     (dir[i].cluster_high != 0 || dir[i].cluster_low != 0) ||
122                     ((dir[i].attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME))
123                         continue;
124
125                 if ((dir[i].attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) ==
126                     FAT_ATTR_VOLUME_ID) {
127                         return dir[i].name;
128                 }
129         }
130         return 0;
131 }
132
133 /*
134  * The FAT filesystem could be without a magic string in superblock
135  * (e.g. old floppies).  This heuristic for FAT detection is inspired
136  * by libvolume_id and the Linux kernel.
137  */
138 static int probe_fat_nomagic(blkid_probe pr, const struct blkid_idmag *mag)
139 {
140         struct msdos_super_block *ms;
141
142         ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
143         if (!ms)
144                 return -1;
145
146         if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA)
147                 return 1;
148
149         /* heads check */
150         if (ms->ms_heads == 0)
151                 return 1;
152
153         /* cluster size check*/
154         if (ms->ms_cluster_size == 0 ||
155             (ms->ms_cluster_size & (ms->ms_cluster_size-1)))
156                 return 1;
157
158         /* media check */
159         if (ms->ms_media < 0xf8 && ms->ms_media != 0xf0)
160                 return 1;
161
162         /* fat counts(Linux kernel expects at least 1 FAT table) */
163         if (!ms->ms_fats)
164                 return 1;
165
166         /*
167          * OS/2 and apparently DFSee will place a FAT12/16-like
168          * pseudo-superblock in the first 512 bytes of non-FAT
169          * filesystems --- at least JFS and HPFS, and possibly others.
170          * So we explicitly check for those filesystems at the
171          * FAT12/16 filesystem magic field identifier, and if they are
172          * present, we rule this out as a FAT filesystem, despite the
173          * FAT-like pseudo-header.
174          */
175         if ((memcmp(ms->ms_magic, "JFS     ", 8) == 0) ||
176             (memcmp(ms->ms_magic, "HPFS    ", 8) == 0))
177                 return 1;
178
179         return 0;
180 }
181
182 /* FAT label extraction from the root directory taken from Kay
183  * Sievers's volume_id library */
184 static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
185 {
186         struct vfat_super_block *vs;
187         struct msdos_super_block *ms;
188         struct vfat_dir_entry *dir;
189         const unsigned char *vol_label = 0, *tmp;
190         unsigned char   *vol_serno;
191         int maxloop = 100;
192         uint16_t sector_size, dir_entries, reserved;
193         uint32_t sect_count, fat_size, dir_size, cluster_count, fat_length;
194         uint32_t buf_size, start_data_sect, next, root_start, root_dir_entries;
195         const char *version = NULL;
196
197
198         /* non-standard magic strings */
199         if (mag->len <= 2 && probe_fat_nomagic(pr, mag) != 0)
200                 return 1;
201
202         vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
203         if (!vs)
204                 return -1;
205
206         ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
207         if (!ms)
208                 return -1;
209
210         /* sector size check */
211         tmp = (unsigned char *) &ms->ms_sector_size;
212         sector_size = tmp[0] + (tmp[1] << 8);
213         if (sector_size != 0x200 && sector_size != 0x400 &&
214             sector_size != 0x800 && sector_size != 0x1000)
215                 return 1;
216
217         tmp = (unsigned char *) &ms->ms_dir_entries;
218         dir_entries = tmp[0] + (tmp[1] << 8);
219         reserved =  le16_to_cpu(ms->ms_reserved);
220         tmp = (unsigned char *) &ms->ms_sectors;
221         sect_count = tmp[0] + (tmp[1] << 8);
222         if (sect_count == 0)
223                 sect_count = le32_to_cpu(ms->ms_total_sect);
224
225         fat_length = le16_to_cpu(ms->ms_fat_length);
226         if (fat_length == 0)
227                 fat_length = le32_to_cpu(vs->vs_fat32_length);
228
229         fat_size = fat_length * ms->ms_fats;
230         dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
231                         (sector_size-1)) / sector_size;
232
233         cluster_count = sect_count - (reserved + fat_size + dir_size);
234         if (ms->ms_cluster_size == 0)
235                 return 1;
236         cluster_count /= ms->ms_cluster_size;
237
238         if (cluster_count > FAT32_MAX)
239                 return 1;
240
241         if (ms->ms_fat_length) {
242                 /* the label may be an attribute in the root directory */
243                 root_start = (reserved + fat_size) * sector_size;
244                 root_dir_entries = vs->vs_dir_entries[0] +
245                         (vs->vs_dir_entries[1] << 8);
246
247                 buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
248                 dir = (struct vfat_dir_entry *)
249                         blkid_probe_get_buffer(pr, root_start, buf_size);
250                 if (dir)
251                         vol_label = search_fat_label(dir, root_dir_entries);
252
253                 if (!vol_label || !memcmp(vol_label, no_name, 11))
254                         vol_label = ms->ms_label;
255                 vol_serno = ms->ms_serno;
256
257                 blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos",
258                               sizeof("msdos"));
259
260                 if (cluster_count < FAT12_MAX)
261                         version = "FAT12";
262                 else if (cluster_count < FAT16_MAX)
263                         version = "FAT16";
264         } else {
265                 unsigned char *buf;
266                 uint16_t fsinfo_sect;
267
268                 /* Search the FAT32 root dir for the label attribute */
269                 buf_size = vs->vs_cluster_size * sector_size;
270                 start_data_sect = reserved + fat_size;
271
272                 version = "FAT32";
273
274                 next = le32_to_cpu(vs->vs_root_cluster);
275                 while (next && --maxloop) {
276                         uint32_t next_sect_off;
277                         uint64_t next_off, fat_entry_off;
278                         int count;
279
280                         next_sect_off = (next - 2) * vs->vs_cluster_size;
281                         next_off = (start_data_sect + next_sect_off) *
282                                 sector_size;
283
284                         dir = (struct vfat_dir_entry *)
285                                 blkid_probe_get_buffer(pr, next_off, buf_size);
286                         if (dir == NULL)
287                                 break;
288
289                         count = buf_size / sizeof(struct vfat_dir_entry);
290
291                         vol_label = search_fat_label(dir, count);
292                         if (vol_label)
293                                 break;
294
295                         /* get FAT entry */
296                         fat_entry_off = (reserved * sector_size) +
297                                 (next * sizeof(uint32_t));
298                         buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size);
299                         if (buf == NULL)
300                                 break;
301
302                         /* set next cluster */
303                         next = le32_to_cpu(*((uint32_t *) buf) & 0x0fffffff);
304                 }
305
306                 if (!vol_label || !memcmp(vol_label, no_name, 11))
307                         vol_label = vs->vs_label;
308                 vol_serno = vs->vs_serno;
309
310                 /*
311                  * FAT32 should have a valid signature in the fsinfo block,
312                  * but also allow all bytes set to '\0', because some volumes
313                  * do not set the signature at all.
314                  */
315                 fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector);
316                 if (fsinfo_sect) {
317                         struct fat32_fsinfo *fsinfo;
318
319                         buf = blkid_probe_get_buffer(pr,
320                                         fsinfo_sect * sector_size,
321                                         sizeof(struct fat32_fsinfo));
322                         if (buf == NULL)
323                                 return -1;
324
325                         fsinfo = (struct fat32_fsinfo *) buf;
326                         if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 &&
327                             memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0)
328                                 return -1;
329                         if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 &&
330                             memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0)
331                                 return -1;
332                 }
333         }
334
335         if (vol_label && memcmp(vol_label, no_name, 11))
336                 blkid_probe_set_label(pr, (unsigned char *) vol_label, 11);
337
338         /* We can't just print them as %04X, because they are unaligned */
339         blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X",
340                 vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]);
341
342         if (version)
343                 blkid_probe_set_version(pr, version);
344
345         return 0;
346 }
347
348
349 const struct blkid_idinfo vfat_idinfo =
350 {
351         .name           = "vfat",
352         .usage          = BLKID_USAGE_FILESYSTEM,
353         .probefunc      = probe_vfat,
354         .magics         =
355         {
356                 { .magic = "MSWIN",    .len = 5, .sboff = 0x52 },
357                 { .magic = "FAT32   ", .len = 8, .sboff = 0x52 },
358                 { .magic = "MSDOS",    .len = 5, .sboff = 0x36 },
359                 { .magic = "FAT16   ", .len = 8, .sboff = 0x36 },
360                 { .magic = "FAT12   ", .len = 8, .sboff = 0x36 },
361                 { .magic = "\353",     .len = 1, },
362                 { .magic = "\351",     .len = 1, },
363                 { .magic = "\125\252", .len = 2, .sboff = 0x1fe },
364                 { NULL }
365         }
366 };
367