1 | /*
|
---|
2 | * volume_id - reads filesystem label and uuid
|
---|
3 | *
|
---|
4 | * Copyright (C) 2012 S-G Bergh <sgb@systemasis.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_EXFAT) += exfat.o
|
---|
22 |
|
---|
23 | //config:
|
---|
24 | //config:config FEATURE_VOLUMEID_EXFAT
|
---|
25 | //config: bool "exFAT filesystem"
|
---|
26 | //config: default y
|
---|
27 | //config: depends on VOLUMEID
|
---|
28 | //config: help
|
---|
29 | //config: exFAT (extended FAT) is a proprietary file system designed especially
|
---|
30 | //config: for flash drives. It has many features from NTFS, but with less
|
---|
31 | //config: overhead. exFAT is used on most SDXC cards for consumer electronics.
|
---|
32 | //config:
|
---|
33 |
|
---|
34 | #include "volume_id_internal.h"
|
---|
35 |
|
---|
36 | #define EXFAT_SB_OFFSET 0
|
---|
37 | #define EXFAT_DIR_ENTRY_SZ 32
|
---|
38 | #define EXFAT_MAX_DIR_ENTRIES 100
|
---|
39 |
|
---|
40 | struct exfat_super_block {
|
---|
41 | /* 0x00 */ uint8_t boot_jump[3];
|
---|
42 | /* 0x03 */ uint8_t fs_name[8];
|
---|
43 | /* 0x0B */ uint8_t must_be_zero[53];
|
---|
44 | /* 0x40 */ uint64_t partition_offset;
|
---|
45 | /* 0x48 */ uint64_t volume_length;
|
---|
46 | /* 0x50 */ uint32_t fat_offset; // Sector address of 1st FAT
|
---|
47 | /* 0x54 */ uint32_t fat_size; // In sectors
|
---|
48 | /* 0x58 */ uint32_t cluster_heap_offset; // Sector address of Data Region
|
---|
49 | /* 0x5C */ uint32_t cluster_count;
|
---|
50 | /* 0x60 */ uint32_t root_dir; // Cluster address of Root Directory
|
---|
51 | /* 0x64 */ uint8_t vol_serial_nr[4]; // Volume ID
|
---|
52 | /* 0x68 */ uint16_t fs_revision; // VV.MM
|
---|
53 | /* 0x6A */ uint16_t vol_flags;
|
---|
54 | /* 0x6C */ uint8_t bytes_per_sector; // Power of 2: 9 => 512, 12 => 4096
|
---|
55 | /* 0x6D */ uint8_t sectors_per_cluster; // Power of 2
|
---|
56 | /* 0x6E */ uint8_t nr_of_fats; // 2 for TexFAT
|
---|
57 | /* 0x6F */ // ...
|
---|
58 | } PACKED;
|
---|
59 |
|
---|
60 | struct exfat_dir_entry {
|
---|
61 | /* 0x00 */ uint8_t entry_type;
|
---|
62 | union {
|
---|
63 | struct volume_label {
|
---|
64 | /* 0x01 */ uint8_t char_count; // Length of label
|
---|
65 | /* 0x02 */ uint16_t vol_label[11]; // UTF16 string without null termination
|
---|
66 | /* 0x18 */ uint8_t reserved[8];
|
---|
67 | /* 0x20 */ } PACKED label;
|
---|
68 | struct volume_guid {
|
---|
69 | /* 0x01 */ uint8_t sec_count;
|
---|
70 | /* 0x02 */ uint16_t set_checksum;
|
---|
71 | /* 0x04 */ uint16_t flags;
|
---|
72 | /* 0x06 */ uint8_t vol_guid[16];
|
---|
73 | /* 0x16 */ uint8_t reserved[10];
|
---|
74 | /* 0x20 */ } PACKED guid;
|
---|
75 | } PACKED type;
|
---|
76 | } PACKED;
|
---|
77 |
|
---|
78 | int FAST_FUNC volume_id_probe_exfat(struct volume_id *id /*,uint64_t off*/)
|
---|
79 | {
|
---|
80 | struct exfat_super_block *sb;
|
---|
81 | struct exfat_dir_entry *de;
|
---|
82 | unsigned sector_sz;
|
---|
83 | unsigned cluster_sz;
|
---|
84 | uint64_t root_dir_off;
|
---|
85 | unsigned count;
|
---|
86 | unsigned need_lbl_guid;
|
---|
87 |
|
---|
88 | // Primary super block
|
---|
89 | dbg("exFAT: probing at offset 0x%x", EXFAT_SB_OFFSET);
|
---|
90 | sb = volume_id_get_buffer(id, EXFAT_SB_OFFSET, sizeof(*sb));
|
---|
91 |
|
---|
92 | if (!sb)
|
---|
93 | return -1;
|
---|
94 |
|
---|
95 | if (memcmp(sb->fs_name, "EXFAT ", 8) != 0)
|
---|
96 | return -1;
|
---|
97 |
|
---|
98 | sector_sz = 1 << sb->bytes_per_sector;
|
---|
99 | cluster_sz = sector_sz << sb->sectors_per_cluster;
|
---|
100 | // There are no clusters 0 and 1, so the first cluster is 2.
|
---|
101 | root_dir_off = (uint64_t)EXFAT_SB_OFFSET +
|
---|
102 | // Hmm... should we cast sector_sz/cluster_sz to uint64_t?
|
---|
103 | (le32_to_cpu(sb->cluster_heap_offset)) * sector_sz +
|
---|
104 | (le32_to_cpu(sb->root_dir) - 2) * cluster_sz;
|
---|
105 | dbg("exFAT: sector size 0x%x bytes", sector_sz);
|
---|
106 | dbg("exFAT: cluster size 0x%x bytes", cluster_sz);
|
---|
107 | dbg("exFAT: root dir is at 0x%llx", (long long)root_dir_off);
|
---|
108 |
|
---|
109 | // Use DOS uuid as fallback, if no GUID set
|
---|
110 | volume_id_set_uuid(id, sb->vol_serial_nr, UUID_DOS);
|
---|
111 |
|
---|
112 | // EXFAT_MAX_DIR_ENTRIES is used as a safety belt.
|
---|
113 | // The Root Directory may hold an unlimited number of entries,
|
---|
114 | // so we do not want to check all. Usually label and GUID
|
---|
115 | // are in the beginning, but there are no guarantees.
|
---|
116 | need_lbl_guid = (1 << 0) | (1 << 1);
|
---|
117 | for (count = 0; count < EXFAT_MAX_DIR_ENTRIES; count++) {
|
---|
118 | de = volume_id_get_buffer(id, root_dir_off + (count * EXFAT_DIR_ENTRY_SZ), EXFAT_DIR_ENTRY_SZ);
|
---|
119 | if (de == NULL)
|
---|
120 | break;
|
---|
121 | if (de->entry_type == 0x00) {
|
---|
122 | // End of Directory Marker
|
---|
123 | dbg("exFAT: End of root directory reached after %u entries", count);
|
---|
124 | break;
|
---|
125 | }
|
---|
126 | if (de->entry_type == 0x83) {
|
---|
127 | // Volume Label Directory Entry
|
---|
128 | volume_id_set_label_unicode16(id, (uint8_t *)de->type.label.vol_label,
|
---|
129 | LE, 2 * de->type.label.char_count);
|
---|
130 | need_lbl_guid &= ~(1 << 0);
|
---|
131 | }
|
---|
132 | if (de->entry_type == 0xA0) {
|
---|
133 | // Volume GUID Directory Entry
|
---|
134 | volume_id_set_uuid(id, de->type.guid.vol_guid, UUID_DCE);
|
---|
135 | need_lbl_guid &= ~(1 << 1);
|
---|
136 | }
|
---|
137 | if (!need_lbl_guid)
|
---|
138 | break;
|
---|
139 | }
|
---|
140 |
|
---|
141 | IF_FEATURE_BLKID_TYPE(id->type = "exfat";)
|
---|
142 | return 0;
|
---|
143 | }
|
---|