]>
Commit | Line | Data |
---|---|---|
8604c255 AN |
1 | /* |
2 | * Copyright (C) 2010 Andrew Nayenko <resver@gmail.com> | |
3 | * | |
4 | * This file may be redistributed under the terms of the | |
5 | * GNU Lesser General Public License. | |
6 | */ | |
7 | #include "superblocks.h" | |
8 | ||
9 | struct exfat_super_block { | |
10 | uint8_t jump[3]; | |
11 | uint8_t oem_name[8]; | |
12 | uint8_t __unused1[53]; | |
13 | uint64_t block_start; | |
14 | uint64_t block_count; | |
15 | uint32_t fat_block_start; | |
16 | uint32_t fat_block_count; | |
17 | uint32_t cluster_block_start; | |
18 | uint32_t cluster_count; | |
19 | uint32_t rootdir_cluster; | |
20 | uint8_t volume_serial[4]; | |
21 | struct { | |
8f6a58ef KZ |
22 | uint8_t vermin; |
23 | uint8_t vermaj; | |
8604c255 AN |
24 | } version; |
25 | uint16_t volume_state; | |
26 | uint8_t block_bits; | |
27 | uint8_t bpc_bits; | |
28 | uint8_t fat_count; | |
29 | uint8_t drive_no; | |
30 | uint8_t allocated_percent; | |
31 | } __attribute__((__packed__)); | |
32 | ||
33 | struct exfat_entry_label { | |
34 | uint8_t type; | |
35 | uint8_t length; | |
fb4ed8a5 PR |
36 | uint8_t name[22]; |
37 | uint8_t reserved[8]; | |
8604c255 AN |
38 | } __attribute__((__packed__)); |
39 | ||
11e904f0 | 40 | #define BLOCK_SIZE(sb) (1u << (sb)->block_bits) |
8604c255 AN |
41 | #define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits) |
42 | #define EXFAT_FIRST_DATA_CLUSTER 2 | |
43 | #define EXFAT_LAST_DATA_CLUSTER 0xffffff6 | |
44 | #define EXFAT_ENTRY_SIZE 32 | |
45 | ||
46 | #define EXFAT_ENTRY_EOD 0x00 | |
47 | #define EXFAT_ENTRY_LABEL 0x83 | |
48 | ||
f12cd8d1 KZ |
49 | static uint64_t block_to_offset(const struct exfat_super_block *sb, |
50 | uint64_t block) | |
8604c255 | 51 | { |
f12cd8d1 | 52 | return block << sb->block_bits; |
8604c255 AN |
53 | } |
54 | ||
f12cd8d1 | 55 | static uint64_t cluster_to_block(const struct exfat_super_block *sb, |
8604c255 AN |
56 | uint32_t cluster) |
57 | { | |
58 | return le32_to_cpu(sb->cluster_block_start) + | |
f12cd8d1 | 59 | ((uint64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER) |
8604c255 AN |
60 | << sb->bpc_bits); |
61 | } | |
62 | ||
f12cd8d1 | 63 | static uint64_t cluster_to_offset(const struct exfat_super_block *sb, |
8604c255 AN |
64 | uint32_t cluster) |
65 | { | |
66 | return block_to_offset(sb, cluster_to_block(sb, cluster)); | |
67 | } | |
68 | ||
69 | static uint32_t next_cluster(blkid_probe pr, | |
70 | const struct exfat_super_block *sb, uint32_t cluster) | |
71 | { | |
72 | uint32_t *next; | |
f12cd8d1 | 73 | uint64_t fat_offset; |
8604c255 AN |
74 | |
75 | fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start)) | |
f12cd8d1 | 76 | + (uint64_t) cluster * sizeof(cluster); |
8604c255 AN |
77 | next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset, |
78 | sizeof(uint32_t)); | |
79 | if (!next) | |
80 | return 0; | |
81 | return le32_to_cpu(*next); | |
82 | } | |
83 | ||
84 | static struct exfat_entry_label *find_label(blkid_probe pr, | |
85 | const struct exfat_super_block *sb) | |
86 | { | |
87 | uint32_t cluster = le32_to_cpu(sb->rootdir_cluster); | |
f12cd8d1 | 88 | uint64_t offset = cluster_to_offset(sb, cluster); |
8604c255 | 89 | uint8_t *entry; |
f98b5632 RS |
90 | const size_t max_iter = 10000; |
91 | size_t i = 0; | |
8604c255 | 92 | |
f98b5632 | 93 | for (; i < max_iter; i++) { |
8604c255 AN |
94 | entry = (uint8_t *) blkid_probe_get_buffer(pr, offset, |
95 | EXFAT_ENTRY_SIZE); | |
96 | if (!entry) | |
97 | return NULL; | |
98 | if (entry[0] == EXFAT_ENTRY_EOD) | |
99 | return NULL; | |
100 | if (entry[0] == EXFAT_ENTRY_LABEL) | |
101 | return (struct exfat_entry_label *) entry; | |
f98b5632 | 102 | |
8604c255 AN |
103 | offset += EXFAT_ENTRY_SIZE; |
104 | if (offset % CLUSTER_SIZE(sb) == 0) { | |
105 | cluster = next_cluster(pr, sb, cluster); | |
106 | if (cluster < EXFAT_FIRST_DATA_CLUSTER) | |
107 | return NULL; | |
108 | if (cluster > EXFAT_LAST_DATA_CLUSTER) | |
109 | return NULL; | |
110 | offset = cluster_to_offset(sb, cluster); | |
111 | } | |
112 | } | |
f98b5632 RS |
113 | |
114 | return NULL; | |
8604c255 AN |
115 | } |
116 | ||
117 | static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag) | |
118 | { | |
119 | struct exfat_super_block *sb; | |
120 | struct exfat_entry_label *label; | |
121 | ||
122 | sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block); | |
9d89a95e | 123 | if (!sb || !CLUSTER_SIZE(sb)) |
37f40602 | 124 | return errno ? -errno : BLKID_PROBE_NONE; |
8604c255 AN |
125 | |
126 | label = find_label(pr, sb); | |
127 | if (label) | |
128 | blkid_probe_set_utf8label(pr, label->name, | |
fb4ed8a5 PR |
129 | min(label->length * 2, sizeof(label->name)), |
130 | BLKID_ENC_UTF16LE); | |
37f40602 HR |
131 | else if (errno) |
132 | return -errno; | |
8604c255 AN |
133 | |
134 | blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4, | |
135 | "%02hhX%02hhX-%02hhX%02hhX", | |
136 | sb->volume_serial[3], sb->volume_serial[2], | |
137 | sb->volume_serial[1], sb->volume_serial[0]); | |
138 | ||
07ce7003 | 139 | blkid_probe_sprintf_version(pr, "%u.%u", |
8f6a58ef | 140 | sb->version.vermaj, sb->version.vermin); |
8604c255 | 141 | |
cd129b7d MP |
142 | blkid_probe_set_block_size(pr, BLOCK_SIZE(sb)); |
143 | ||
37f40602 | 144 | return BLKID_PROBE_OK; |
8604c255 AN |
145 | } |
146 | ||
147 | const struct blkid_idinfo exfat_idinfo = | |
148 | { | |
149 | .name = "exfat", | |
150 | .usage = BLKID_USAGE_FILESYSTEM, | |
151 | .probefunc = probe_exfat, | |
152 | .magics = | |
153 | { | |
154 | { .magic = "EXFAT ", .len = 8, .sboff = 3 }, | |
155 | { NULL } | |
156 | } | |
157 | }; |