~pr0gg3d/ubuntu/oneiric/util-linux/bug-805886

« back to all changes in this revision

Viewing changes to shlibs/blkid/src/probers/vfat.c

  • Committer: Bazaar Package Importer
  • Author(s): Scott James Remnant
  • Date: 2009-07-16 15:48:23 UTC
  • mfrom: (1.3.7 upstream)
  • Revision ID: james.westby@ubuntu.com-20090716154823-i26fshvs4v8h90qh
Tags: 2.16-1ubuntu1
* Merge from Debian, remaining changes:
  - Since udev is required in Ubuntu, the hwclock.sh init script is
    not called on startup and the hwclockfirst.sh init script is
    removed.
  - Remove /etc/adjtime on upgrade if it was not used.
  - Install custom blkid.conf to use /dev/.blkid.tab since we don't
    expect device names to survive a reboot
  - No lsb_release call in mount.preinst since we'd need Pre-Depends
    (LP: #383697).

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 "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[164];
 
49
/*1fe*/ unsigned char   vs_pmagic[2];
 
50
};
 
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
/* 3d*/ unsigned char   ms_dummy2[192];
 
73
/*1fe*/ unsigned char   ms_pmagic[2];
 
74
};
 
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
};
 
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
};
 
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
        /* heads check */
 
147
        if (ms->ms_heads == 0)
 
148
                return 1;
 
149
 
 
150
        /* cluster size check*/
 
151
        if (ms->ms_cluster_size == 0 ||
 
152
            (ms->ms_cluster_size & (ms->ms_cluster_size-1)))
 
153
                return 1;
 
154
 
 
155
        /* media check */
 
156
        if (ms->ms_media < 0xf8 && ms->ms_media != 0xf0)
 
157
                return 1;
 
158
 
 
159
        /* fat counts(Linux kernel expects at least 1 FAT table) */
 
160
        if (!ms->ms_fats)
 
161
                return 1;
 
162
 
 
163
        /*
 
164
         * OS/2 and apparently DFSee will place a FAT12/16-like
 
165
         * pseudo-superblock in the first 512 bytes of non-FAT
 
166
         * filesystems --- at least JFS and HPFS, and possibly others.
 
167
         * So we explicitly check for those filesystems at the
 
168
         * FAT12/16 filesystem magic field identifier, and if they are
 
169
         * present, we rule this out as a FAT filesystem, despite the
 
170
         * FAT-like pseudo-header.
 
171
         */
 
172
        if ((memcmp(ms->ms_magic, "JFS     ", 8) == 0) ||
 
173
            (memcmp(ms->ms_magic, "HPFS    ", 8) == 0))
 
174
                return 1;
 
175
 
 
176
        return 0;
 
177
}
 
178
 
 
179
/* FAT label extraction from the root directory taken from Kay
 
180
 * Sievers's volume_id library */
 
181
static int probe_vfat(blkid_probe pr, const struct blkid_idmag *mag)
 
182
{
 
183
        struct vfat_super_block *vs;
 
184
        struct msdos_super_block *ms;
 
185
        struct vfat_dir_entry *dir;
 
186
        const unsigned char *vol_label = 0, *tmp;
 
187
        unsigned char   *vol_serno;
 
188
        int maxloop = 100;
 
189
        uint16_t sector_size, dir_entries, reserved;
 
190
        uint32_t sect_count, fat_size, dir_size, cluster_count, fat_length;
 
191
        uint32_t buf_size, start_data_sect, next, root_start, root_dir_entries;
 
192
        const char *version = NULL;
 
193
 
 
194
 
 
195
        /* non-standard magic strings */
 
196
        if (mag->len <= 2 && probe_fat_nomagic(pr, mag) != 0)
 
197
                return 1;
 
198
 
 
199
        vs = blkid_probe_get_sb(pr, mag, struct vfat_super_block);
 
200
        if (!vs)
 
201
                return -1;
 
202
 
 
203
        ms = blkid_probe_get_sb(pr, mag, struct msdos_super_block);
 
204
        if (!ms)
 
205
                return -1;
 
206
 
 
207
        /* sector size check */
 
208
        tmp = (unsigned char *) &ms->ms_sector_size;
 
209
        sector_size = tmp[0] + (tmp[1] << 8);
 
210
        if (sector_size != 0x200 && sector_size != 0x400 &&
 
211
            sector_size != 0x800 && sector_size != 0x1000)
 
212
                return 1;
 
213
 
 
214
        tmp = (unsigned char *) &ms->ms_dir_entries;
 
215
        dir_entries = tmp[0] + (tmp[1] << 8);
 
216
        reserved =  le16_to_cpu(ms->ms_reserved);
 
217
        tmp = (unsigned char *) &ms->ms_sectors;
 
218
        sect_count = tmp[0] + (tmp[1] << 8);
 
219
        if (sect_count == 0)
 
220
                sect_count = le32_to_cpu(ms->ms_total_sect);
 
221
 
 
222
        fat_length = le16_to_cpu(ms->ms_fat_length);
 
223
        if (fat_length == 0)
 
224
                fat_length = le32_to_cpu(vs->vs_fat32_length);
 
225
 
 
226
        fat_size = fat_length * ms->ms_fats;
 
227
        dir_size = ((dir_entries * sizeof(struct vfat_dir_entry)) +
 
228
                        (sector_size-1)) / sector_size;
 
229
 
 
230
        cluster_count = sect_count - (reserved + fat_size + dir_size);
 
231
        if (ms->ms_cluster_size == 0)
 
232
                return 1;
 
233
        cluster_count /= ms->ms_cluster_size;
 
234
 
 
235
        if (cluster_count > FAT32_MAX)
 
236
                return 1;
 
237
 
 
238
        if (ms->ms_fat_length) {
 
239
                /* the label may be an attribute in the root directory */
 
240
                root_start = (reserved + fat_size) * sector_size;
 
241
                root_dir_entries = vs->vs_dir_entries[0] +
 
242
                        (vs->vs_dir_entries[1] << 8);
 
243
 
 
244
                buf_size = root_dir_entries * sizeof(struct vfat_dir_entry);
 
245
                dir = (struct vfat_dir_entry *)
 
246
                        blkid_probe_get_buffer(pr, root_start, buf_size);
 
247
                if (dir)
 
248
                        vol_label = search_fat_label(dir, root_dir_entries);
 
249
 
 
250
                if (!vol_label || !memcmp(vol_label, no_name, 11))
 
251
                        vol_label = ms->ms_label;
 
252
                vol_serno = ms->ms_serno;
 
253
 
 
254
                blkid_probe_set_value(pr, "SEC_TYPE", (unsigned char *) "msdos",
 
255
                              sizeof("msdos"));
 
256
 
 
257
                if (cluster_count < FAT12_MAX)
 
258
                        version = "FAT12";
 
259
                else if (cluster_count < FAT16_MAX)
 
260
                        version = "FAT16";
 
261
        } else {
 
262
                unsigned char *buf;
 
263
                uint16_t fsinfo_sect;
 
264
 
 
265
                /* Search the FAT32 root dir for the label attribute */
 
266
                buf_size = vs->vs_cluster_size * sector_size;
 
267
                start_data_sect = reserved + fat_size;
 
268
 
 
269
                version = "FAT32";
 
270
 
 
271
                next = le32_to_cpu(vs->vs_root_cluster);
 
272
                while (next && --maxloop) {
 
273
                        uint32_t next_sect_off;
 
274
                        uint64_t next_off, fat_entry_off;
 
275
                        int count;
 
276
 
 
277
                        next_sect_off = (next - 2) * vs->vs_cluster_size;
 
278
                        next_off = (start_data_sect + next_sect_off) *
 
279
                                sector_size;
 
280
 
 
281
                        dir = (struct vfat_dir_entry *)
 
282
                                blkid_probe_get_buffer(pr, next_off, buf_size);
 
283
                        if (dir == NULL)
 
284
                                break;
 
285
 
 
286
                        count = buf_size / sizeof(struct vfat_dir_entry);
 
287
 
 
288
                        vol_label = search_fat_label(dir, count);
 
289
                        if (vol_label)
 
290
                                break;
 
291
 
 
292
                        /* get FAT entry */
 
293
                        fat_entry_off = (reserved * sector_size) +
 
294
                                (next * sizeof(uint32_t));
 
295
                        buf = blkid_probe_get_buffer(pr, fat_entry_off, buf_size);
 
296
                        if (buf == NULL)
 
297
                                break;
 
298
 
 
299
                        /* set next cluster */
 
300
                        next = le32_to_cpu(*((uint32_t *) buf) & 0x0fffffff);
 
301
                }
 
302
 
 
303
                if (!vol_label || !memcmp(vol_label, no_name, 11))
 
304
                        vol_label = vs->vs_label;
 
305
                vol_serno = vs->vs_serno;
 
306
 
 
307
                /*
 
308
                 * FAT32 should have a valid signature in the fsinfo block,
 
309
                 * but also allow all bytes set to '\0', because some volumes
 
310
                 * do not set the signature at all.
 
311
                 */
 
312
                fsinfo_sect = le16_to_cpu(vs->vs_fsinfo_sector);
 
313
                if (fsinfo_sect) {
 
314
                        struct fat32_fsinfo *fsinfo;
 
315
 
 
316
                        buf = blkid_probe_get_buffer(pr,
 
317
                                        fsinfo_sect * sector_size,
 
318
                                        sizeof(struct fat32_fsinfo));
 
319
                        if (buf == NULL)
 
320
                                return -1;
 
321
 
 
322
                        fsinfo = (struct fat32_fsinfo *) buf;
 
323
                        if (memcmp(fsinfo->signature1, "\x52\x52\x61\x41", 4) != 0 &&
 
324
                            memcmp(fsinfo->signature1, "\x00\x00\x00\x00", 4) != 0)
 
325
                                return -1;
 
326
                        if (memcmp(fsinfo->signature2, "\x72\x72\x41\x61", 4) != 0 &&
 
327
                            memcmp(fsinfo->signature2, "\x00\x00\x00\x00", 4) != 0)
 
328
                                return -1;
 
329
                }
 
330
        }
 
331
 
 
332
        if (vol_label && memcmp(vol_label, no_name, 11))
 
333
                blkid_probe_set_label(pr, (unsigned char *) vol_label, 11);
 
334
 
 
335
        /* We can't just print them as %04X, because they are unaligned */
 
336
        blkid_probe_sprintf_uuid(pr, vol_serno, 4, "%02X%02X-%02X%02X",
 
337
                vol_serno[3], vol_serno[2], vol_serno[1], vol_serno[0]);
 
338
 
 
339
        if (version)
 
340
                blkid_probe_set_version(pr, version);
 
341
 
 
342
        return 0;
 
343
}
 
344
 
 
345
 
 
346
const struct blkid_idinfo vfat_idinfo =
 
347
{
 
348
        .name           = "vfat",
 
349
        .usage          = BLKID_USAGE_FILESYSTEM,
 
350
        .probefunc      = probe_vfat,
 
351
        .magics         =
 
352
        {
 
353
                { .magic = "MSWIN",    .len = 5, .sboff = 0x52 },
 
354
                { .magic = "FAT32   ", .len = 8, .sboff = 0x52 },
 
355
                { .magic = "MSDOS",    .len = 5, .sboff = 0x36 },
 
356
                { .magic = "FAT16   ", .len = 8, .sboff = 0x36 },
 
357
                { .magic = "FAT12   ", .len = 8, .sboff = 0x36 },
 
358
                { .magic = "\353",     .len = 1, },
 
359
                { .magic = "\351",     .len = 1, },
 
360
                { .magic = "\125\252", .len = 2, .sboff = 0x1fe },
 
361
                { NULL }
 
362
        }
 
363
};
 
364