~ubuntu-branches/ubuntu/trusty/util-linux/trusty-proposed

« back to all changes in this revision

Viewing changes to libblkid/src/superblocks/vfat.c

  • Committer: Package Import Robot
  • Author(s): LaMont Jones
  • Date: 2011-11-03 15:38:23 UTC
  • mto: (4.5.5 sid) (1.6.4)
  • mto: This revision was merged to the branch mainline in revision 85.
  • Revision ID: package-import@ubuntu.com-20111103153823-10sx16jprzxlhkqf
ImportĀ upstreamĀ versionĀ 2.20.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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 "superblocks.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]; /* =0 iff V3 or later */
 
62
/* 15*/ unsigned char   ms_media;
 
63
/* 16*/ uint16_t        ms_fat_length; /* Sectors per FAT */
 
64
/* 18*/ uint16_t        ms_secs_track;
 
65
/* 1a*/ uint16_t        ms_heads;
 
66
/* 1c*/ uint32_t        ms_hidden;
 
67
/* V3 BPB */
 
68
/* 20*/ uint32_t        ms_total_sect; /* iff ms_sectors == 0 */
 
69
/* V4 BPB */
 
70
/* 24*/ unsigned char   ms_unknown[3]; /* Phys drive no., resvd, V4 sig (0x29) */
 
71
/* 27*/ unsigned char   ms_serno[4];
 
72
/* 2b*/ unsigned char   ms_label[11];
 
73
/* 36*/ unsigned char   ms_magic[8];
 
74
/* 3e*/ unsigned char   ms_dummy2[0x1fe - 0x3e];
 
75
/*1fe*/ unsigned char   ms_pmagic[2];
 
76
} __attribute__((packed));
 
77
 
 
78
struct vfat_dir_entry {
 
79
        uint8_t         name[11];
 
80
        uint8_t         attr;
 
81
        uint16_t        time_creat;
 
82
        uint16_t        date_creat;
 
83
        uint16_t        time_acc;
 
84
        uint16_t        date_acc;
 
85
        uint16_t        cluster_high;
 
86
        uint16_t        time_write;
 
87
        uint16_t        date_write;
 
88
        uint16_t        cluster_low;
 
89
        uint32_t        size;
 
90
} __attribute__((packed));
 
91
 
 
92
struct fat32_fsinfo {
 
93
        uint8_t signature1[4];
 
94
        uint32_t reserved1[120];
 
95
        uint8_t signature2[4];
 
96
        uint32_t free_clusters;
 
97
        uint32_t next_cluster;
 
98
        uint32_t reserved2[4];
 
99
} __attribute__((packed));
 
100
 
 
101
/* maximum number of clusters */
 
102
#define FAT12_MAX 0xFF4
 
103
#define FAT16_MAX 0xFFF4
 
104
#define FAT32_MAX 0x0FFFFFF6
 
105
 
 
106
#define FAT_ATTR_VOLUME_ID              0x08
 
107
#define FAT_ATTR_DIR                    0x10
 
108
#define FAT_ATTR_LONG_NAME              0x0f
 
109
#define FAT_ATTR_MASK                   0x3f
 
110
#define FAT_ENTRY_FREE                  0xe5
 
111
 
 
112
static const char *no_name = "NO NAME    ";
 
113
 
 
114
#define unaligned_le16(x) \
 
115
                (((unsigned char *) x)[0] + (((unsigned char *) x)[1] << 8))
 
116
 
 
117
/*
 
118
 * Look for LABEL (name) in the FAT root directory.
 
119
 */
 
120
static unsigned char *search_fat_label(blkid_probe pr,
 
121
                                uint64_t offset, uint32_t entries)
 
122
{
 
123
        struct vfat_dir_entry *ent, *dir = NULL;
 
124
        uint32_t i;
 
125
 
 
126
        DBG(DEBUG_LOWPROBE,
 
127
                printf("\tlook for label in root-dir "
 
128
                        "(entries: %d, offset: %jd)\n", entries, offset));
 
129
 
 
130
        if (!blkid_probe_is_tiny(pr)) {
 
131
                /* large disk, read whole root directory */
 
132
                dir = (struct vfat_dir_entry *)
 
133
                        blkid_probe_get_buffer(pr,
 
134
                                        offset,
 
135
                                        (blkid_loff_t) entries *
 
136
                                                sizeof(struct vfat_dir_entry));
 
137
                if (!dir)
 
138
                        return NULL;
 
139
        }
 
140
 
 
141
        for (i = 0; i < entries; i++) {
 
142
                /*
 
143
                 * The root directory could be relatively large (4-16kB).
 
144
                 * Fortunately, the LABEL is usually the first entry in the
 
145
                 * directory. On tiny disks we call read() per entry.
 
146
                 */
 
147
                if (!dir)
 
148
                        ent = (struct vfat_dir_entry *)
 
149
                                blkid_probe_get_buffer(pr,
 
150
                                        (blkid_loff_t) offset + (i *
 
151
                                                sizeof(struct vfat_dir_entry)),
 
152
                                        sizeof(struct vfat_dir_entry));
 
153
                else
 
154
                        ent = &dir[i];
 
155
 
 
156
                if (!ent || ent->name[0] == 0x00)
 
157
                        break;
 
158
 
 
159
                if ((ent->name[0] == FAT_ENTRY_FREE) ||
 
160
                    (ent->cluster_high != 0 || ent->cluster_low != 0) ||
 
161
                    ((ent->attr & FAT_ATTR_MASK) == FAT_ATTR_LONG_NAME))
 
162
                        continue;
 
163
 
 
164
                if ((ent->attr & (FAT_ATTR_VOLUME_ID | FAT_ATTR_DIR)) ==
 
165
                    FAT_ATTR_VOLUME_ID) {
 
166
                        DBG(DEBUG_LOWPROBE,
 
167
                                printf("\tfound fs LABEL at entry %d\n", i));
 
168
                        return ent->name;
 
169
                }
 
170
        }
 
171
        return NULL;
 
172
}
 
173
 
 
174
static int fat_valid_superblock(const struct blkid_idmag *mag,
 
175
                        struct msdos_super_block *ms,
 
176
                        struct vfat_super_block *vs,
 
177
                        uint32_t *cluster_count, uint32_t *fat_size)
 
178
{
 
179
        uint16_t sector_size, dir_entries, reserved;
 
180
        uint32_t sect_count, __fat_size, dir_size, __cluster_count, fat_length;
 
181
        uint32_t max_count;
 
182
 
 
183
        /* extra check for FATs without magic strings */
 
184
        if (mag->len <= 2) {
 
185
                /* Old floppies have a valid MBR signature */
 
186
                if (ms->ms_pmagic[0] != 0x55 || ms->ms_pmagic[1] != 0xAA)
 
187
                        return 0;
 
188
 
 
189
                /*
 
190
                 * OS/2 and apparently DFSee will place a FAT12/16-like
 
191
                 * pseudo-superblock in the first 512 bytes of non-FAT
 
192
                 * filesystems --- at least JFS and HPFS, and possibly others.
 
193
                 * So we explicitly check for those filesystems at the
 
194
                 * FAT12/16 filesystem magic field identifier, and if they are
 
195
                 * present, we rule this out as a FAT filesystem, despite the
 
196
                 * FAT-like pseudo-header.
 
197
                 */
 
198
                if ((memcmp(ms->ms_magic, "JFS     ", 8) == 0) ||
 
199
                    (memcmp(ms->ms_magic, "HPFS    ", 8) == 0))
 
200
                        return 0;
 
201
        }
 
202
 
 
203
        /* fat counts(Linux kernel expects at least 1 FAT table) */
 
204
        if (!ms->ms_fats)
 
205
                return 0;
 
206
        if (!ms->ms_reserved)
 
207
                return 0;
 
208
        if (!(0xf8 <= ms->ms_media || ms->ms_media == 0xf0))
 
209
                return 0;
 
210
        if (!is_power_of_2(ms->ms_cluster_size))
 
211
                return 0;
 
212
 
 
213
        sector_size = unaligned_le16(&ms->ms_sector_size);
 
214
        if (!is_power_of_2(sector_size) ||
 
215
            sector_size < 512 || sector_size > 4096)
 
216
                return 0;
 
217
 
 
218
        dir_entries = unaligned_le16(&ms->ms_dir_entries);
 
219
        reserved =  le16_to_cpu(ms->ms_reserved);
 
220
        sect_count = unaligned_le16(&ms->ms_sectors);
 
221
 
 
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
                                                        ms->ms_cluster_size;
 
235
        if (!ms->ms_fat_length && vs->vs_fat32_length)
 
236
                max_count = FAT32_MAX;
 
237
        else
 
238
                max_count = __cluster_count > FAT12_MAX ? FAT16_MAX : FAT12_MAX;
 
239
 
 
240
        if (__cluster_count > max_count)
 
241
                return 0;
 
242
 
 
243
        if (fat_size)
 
244
                *fat_size = __fat_size;
 
245
        if (cluster_count)
 
246
                *cluster_count = __cluster_count;
 
247
 
 
248
        return 1;       /* valid */
 
249
}
 
250
 
 
251
/*
 
252
 * This function is used by MBR partition table parser to avoid
 
253
 * misinterpretation of FAT filesystem.
 
254
 */
 
255
int blkid_probe_is_vfat(blkid_probe pr)
 
256
{
 
257
        struct vfat_super_block *vs;
 
258
        struct msdos_super_block *ms;
 
259
        const struct blkid_idmag *mag = NULL;
 
260
 
 
261
        if (blkid_probe_get_idmag(pr, &vfat_idinfo, NULL, &mag) || !mag)
 
262
                return 0;
 
263
 
 
264
        ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
 
265
        if (!ms)
 
266
                return 0;
 
267
        vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
 
268
        if (!vs)
 
269
                return 0;
 
270
 
 
271
        return fat_valid_superblock(mag, ms, vs, NULL, NULL);
 
272
}
 
273
 
 
274
/* FAT label extraction from the root directory taken from Kay
 
275
 * Sievers's volume_id library */
 
276
static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
 
277
{
 
278
        struct vfat_super_block *vs;
 
279
        struct msdos_super_block *ms;
 
280
        const unsigned char *vol_label = 0;
 
281
        unsigned char *vol_serno = NULL, vol_label_buf[11];
 
282
        uint16_t sector_size = 0, reserved;
 
283
        uint32_t cluster_count, fat_size;
 
284
        const char *version = NULL;
 
285
 
 
286
        ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
 
287
        if (!ms)
 
288
                return 0;
 
289
        vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
 
290
        if (!vs)
 
291
                return 0;
 
292
        if (!fat_valid_superblock(mag, ms, vs, &cluster_count, &fat_size))
 
293
                return 1;
 
294
 
 
295
        sector_size = unaligned_le16(&ms->ms_sector_size);
 
296
        reserved =  le16_to_cpu(ms->ms_reserved);
 
297
 
 
298
        if (ms->ms_fat_length) {
 
299
                /* the label may be an attribute in the root directory */
 
300
                uint32_t root_start = (reserved + fat_size) * sector_size;
 
301
                uint32_t root_dir_entries = unaligned_le16(&vs->vs_dir_entries);
 
302
 
 
303
                vol_label = search_fat_label(pr, root_start, root_dir_entries);
 
304
                if (vol_label) {
 
305
                        memcpy(vol_label_buf, vol_label, 11);
 
306
                        vol_label = vol_label_buf;
 
307
                }
 
308
 
 
309
                if (!vol_label || !memcmp(vol_label, no_name, 11))
 
310
                        vol_label = ms->ms_label;
 
311
                vol_serno = ms->ms_serno;
 
312
 
 
313
                blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos",
 
314
                              sizeof("msdos"));
 
315
 
 
316
                if (cluster_count < FAT12_MAX)
 
317
                        version = "FAT12";
 
318
                else if (cluster_count < FAT16_MAX)
 
319
                        version = "FAT16";
 
320
 
 
321
        } else if (vs->vs_fat32_length) {
 
322
                unsigned char *buf;
 
323
                uint16_t fsinfo_sect;
 
324
                int maxloop = 100;
 
325
 
 
326
                /* Search the FAT32 root dir for the label attribute */
 
327
                uint32_t buf_size = vs->vs_cluster_size * sector_size;
 
328
                uint32_t start_data_sect = reserved + fat_size;
 
329
                uint32_t entries = le32_to_cpu(vs->vs_fat32_length) *
 
330
                                        sector_size / sizeof(uint32_t);
 
331
                uint32_t next = le32_to_cpu(vs->vs_root_cluster);
 
332
 
 
333
                while (next && next < entries && --maxloop) {
 
334
                        uint32_t next_sect_off;
 
335
                        uint64_t next_off, fat_entry_off;
 
336
                        int count;
 
337
 
 
338
                        next_sect_off = (next - 2) * vs->vs_cluster_size;
 
339
                        next_off = (uint64_t)(start_data_sect + next_sect_off) *
 
340
                                sector_size;
 
341
 
 
342
                        count = buf_size / sizeof(struct vfat_dir_entry);
 
343
 
 
344
                        vol_label = search_fat_label(pr, next_off, count);
 
345
                        if (vol_label) {
 
346
                                memcpy(vol_label_buf, vol_label, 11);
 
347
                                vol_label = vol_label_buf;
 
348
                                break;
 
349
                        }
 
350
 
 
351
                        /* get FAT entry */
 
352
                        fat_entry_off = (reserved * sector_size) +
 
353
                                (next * sizeof(uint32_t));
 
354
                        buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size);
 
355
                        if (buf == NULL)
 
356
                                break;
 
357
 
 
358
                        /* set next cluster */
 
359
                        next = le32_to_cpu(*((uint32_t *) buf)) & 0x0fffffff;
 
360
                }
 
361
 
 
362
                version = "FAT32";
 
363
 
 
364
                if (!vol_label || !memcmp(vol_label, no_name, 11))
 
365
                        vol_label = vs->vs_label;
 
366
                vol_serno = vs->vs_serno;
 
367
 
 
368
                /*
 
369
                 * FAT32 should have a valid signature in the fsinfo block,
 
370
                 * but also allow all bytes set to '\0', because some volumes
 
371
                 * do not set the signature at all.
 
372
                 */
 
373
                fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector);
 
374
                if (fsinfo_sect) {
 
375
                        struct fat32_fsinfo *fsinfo;
 
376
 
 
377
                        buf = blkid_probe_get_buffer(pr,
 
378
                                        (blkid_loff_t) fsinfo_sect * sector_size,
 
379
                                        sizeof(struct fat32_fsinfo));
 
380
                        if (buf == NULL)
 
381
                                return -1;
 
382
 
 
383
                        fsinfo = (struct fat32_fsinfo *) buf;
 
384
                        if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 &&
 
385
                            memcmp(fsinfo->signature1, "\x52\x52\x64\x41", 4) != 0 &&
 
386
                            memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0)
 
387
                                return -1;
 
388
                        if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 &&
 
389
                            memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0)
 
390
                                return -1;
 
391
                }
 
392
        }
 
393
 
 
394
        if (vol_label && memcmp(vol_label, no_name, 11))
 
395
                blkid_probe_set_label(pr, (unsigned char *) vol_label, 11);
 
396
 
 
397
        /* We can't just print them as %04X, because they are unaligned */
 
398
        if (vol_serno)
 
399
                blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X",
 
400
                        vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]);
 
401
        if (version)
 
402
                blkid_probe_set_version(pr, version);
 
403
 
 
404
        return 0;
 
405
}
 
406
 
 
407
 
 
408
const struct blkid_idinfo vfat_idinfo =
 
409
{
 
410
        .name           = "vfat",
 
411
        .usage          = BLKID_USAGE_FILESYSTEM,
 
412
        .probefunc      = probe_vfat,
 
413
        .magics         =
 
414
        {
 
415
                { .magic = "MSWIN",    .len = 5, .sboff = 0x52 },
 
416
                { .magic = "FAT32   ", .len = 8, .sboff = 0x52 },
 
417
                { .magic = "MSDOS",    .len = 5, .sboff = 0x36 },
 
418
                { .magic = "FAT16   ", .len = 8, .sboff = 0x36 },
 
419
                { .magic = "FAT12   ", .len = 8, .sboff = 0x36 },
 
420
                { .magic = "FAT     ", .len = 8, .sboff = 0x36 },
 
421
                { .magic = "\353",     .len = 1, },
 
422
                { .magic = "\351",     .len = 1, },
 
423
                { .magic = "\125\252", .len = 2, .sboff = 0x1fe },
 
424
                { NULL }
 
425
        }
 
426
};
 
427