libfstools: fit: improve fit_volume_find string handling
[project/fstools.git] / libblkid-tiny / exfat.c
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 JumpBoot[3];
11 uint8_t FileSystemName[8];
12 uint8_t MustBeZero[53];
13 uint64_t PartitionOffset;
14 uint64_t VolumeLength;
15 uint32_t FatOffset;
16 uint32_t FatLength;
17 uint32_t ClusterHeapOffset;
18 uint32_t ClusterCount;
19 uint32_t FirstClusterOfRootDirectory;
20 uint8_t VolumeSerialNumber[4];
21 struct {
22 uint8_t vermin;
23 uint8_t vermaj;
24 } FileSystemRevision;
25 uint16_t VolumeFlags;
26 uint8_t BytesPerSectorShift;
27 uint8_t SectorsPerClusterShift;
28 uint8_t NumberOfFats;
29 uint8_t DriveSelect;
30 uint8_t PercentInUse;
31 uint8_t Reserved[7];
32 uint8_t BootCode[390];
33 uint16_t BootSignature;
34 } __attribute__((__packed__));
35
36 struct exfat_entry_label {
37 uint8_t type;
38 uint8_t length;
39 uint8_t name[22];
40 uint8_t reserved[8];
41 } __attribute__((__packed__));
42
43 #define BLOCK_SIZE(sb) ((sb)->BytesPerSectorShift < 32 ? (1u << (sb)->BytesPerSectorShift) : 0)
44 #define CLUSTER_SIZE(sb) ((sb)->SectorsPerClusterShift < 32 ? (BLOCK_SIZE(sb) << (sb)->SectorsPerClusterShift) : 0)
45 #define EXFAT_FIRST_DATA_CLUSTER 2
46 #define EXFAT_LAST_DATA_CLUSTER 0xffffff6
47 #define EXFAT_ENTRY_SIZE 32
48
49 #define EXFAT_ENTRY_EOD 0x00
50 #define EXFAT_ENTRY_LABEL 0x83
51
52 static uint64_t block_to_offset(const struct exfat_super_block *sb,
53 uint64_t block)
54 {
55 return block << sb->BytesPerSectorShift;
56 }
57
58 static uint64_t cluster_to_block(const struct exfat_super_block *sb,
59 uint32_t cluster)
60 {
61 return le32_to_cpu(sb->ClusterHeapOffset) +
62 ((uint64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
63 << sb->SectorsPerClusterShift);
64 }
65
66 static uint64_t cluster_to_offset(const struct exfat_super_block *sb,
67 uint32_t cluster)
68 {
69 return block_to_offset(sb, cluster_to_block(sb, cluster));
70 }
71
72 static uint32_t next_cluster(blkid_probe pr,
73 const struct exfat_super_block *sb, uint32_t cluster)
74 {
75 uint32_t *nextp, next;
76 uint64_t fat_offset;
77
78 fat_offset = block_to_offset(sb, le32_to_cpu(sb->FatOffset))
79 + (uint64_t) cluster * sizeof(cluster);
80 nextp = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
81 sizeof(uint32_t));
82 if (!nextp)
83 return 0;
84 memcpy(&next, nextp, sizeof(next));
85 return le32_to_cpu(next);
86 }
87
88 static struct exfat_entry_label *find_label(blkid_probe pr,
89 const struct exfat_super_block *sb)
90 {
91 uint32_t cluster = le32_to_cpu(sb->FirstClusterOfRootDirectory);
92 uint64_t offset = cluster_to_offset(sb, cluster);
93 uint8_t *entry;
94 const size_t max_iter = 10000;
95 size_t i = 0;
96
97 for (; i < max_iter; i++) {
98 entry = (uint8_t *) blkid_probe_get_buffer(pr, offset,
99 EXFAT_ENTRY_SIZE);
100 if (!entry)
101 return NULL;
102 if (entry[0] == EXFAT_ENTRY_EOD)
103 return NULL;
104 if (entry[0] == EXFAT_ENTRY_LABEL)
105 return (struct exfat_entry_label *) entry;
106
107 offset += EXFAT_ENTRY_SIZE;
108 if (CLUSTER_SIZE(sb) && (offset % CLUSTER_SIZE(sb)) == 0) {
109 cluster = next_cluster(pr, sb, cluster);
110 if (cluster < EXFAT_FIRST_DATA_CLUSTER)
111 return NULL;
112 if (cluster > EXFAT_LAST_DATA_CLUSTER)
113 return NULL;
114 offset = cluster_to_offset(sb, cluster);
115 }
116 }
117
118 return NULL;
119 }
120
121 static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
122 {
123 struct exfat_super_block *sb;
124 struct exfat_entry_label *label;
125
126 sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
127 if (!sb || !CLUSTER_SIZE(sb))
128 return errno ? -errno : BLKID_PROBE_NONE;
129
130 if (le16_to_cpu(sb->BootSignature) != 0xAA55)
131 return BLKID_PROBE_NONE;
132
133 if (memcmp(sb->JumpBoot, "\xEB\x76\x90", 3) != 0)
134 return BLKID_PROBE_NONE;
135
136 for (size_t i = 0; i < sizeof(sb->MustBeZero); i++)
137 if (sb->MustBeZero[i] != 0x00)
138 return BLKID_PROBE_NONE;
139
140 label = find_label(pr, sb);
141 if (label)
142 blkid_probe_set_utf8label(pr, label->name,
143 min((size_t) label->length * 2, sizeof(label->name)),
144 BLKID_ENC_UTF16LE);
145 else if (errno)
146 return -errno;
147
148 blkid_probe_sprintf_uuid(pr, sb->VolumeSerialNumber, 4,
149 "%02hhX%02hhX-%02hhX%02hhX",
150 sb->VolumeSerialNumber[3], sb->VolumeSerialNumber[2],
151 sb->VolumeSerialNumber[1], sb->VolumeSerialNumber[0]);
152
153 blkid_probe_sprintf_version(pr, "%u.%u",
154 sb->FileSystemRevision.vermaj, sb->FileSystemRevision.vermin);
155
156 #if 0
157 blkid_probe_set_fsblocksize(pr, BLOCK_SIZE(sb));
158 blkid_probe_set_block_size(pr, BLOCK_SIZE(sb));
159 blkid_probe_set_fssize(pr, BLOCK_SIZE(sb) * le64_to_cpu(sb->VolumeLength));
160 #endif
161
162 return BLKID_PROBE_OK;
163 }
164
165 const struct blkid_idinfo exfat_idinfo =
166 {
167 .name = "exfat",
168 .usage = BLKID_USAGE_FILESYSTEM,
169 .probefunc = probe_exfat,
170 .magics =
171 {
172 { .magic = "EXFAT ", .len = 8, .sboff = 3 },
173 { NULL }
174 }
175 };