]> git.ipfire.org Git - thirdparty/util-linux.git/blob - libblkid/src/superblocks/exfat.c
Merge branch 'fix/xfs-block-size' of https://github.com/alberand/util-linux
[thirdparty/util-linux.git] / libblkid / src / superblocks / 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 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 {
22 uint8_t vermin;
23 uint8_t vermaj;
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;
36 uint8_t name[22];
37 uint8_t reserved[8];
38 } __attribute__((__packed__));
39
40 #define BLOCK_SIZE(sb) (1u << (sb)->block_bits)
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
49 static uint64_t block_to_offset(const struct exfat_super_block *sb,
50 uint64_t block)
51 {
52 return block << sb->block_bits;
53 }
54
55 static uint64_t cluster_to_block(const struct exfat_super_block *sb,
56 uint32_t cluster)
57 {
58 return le32_to_cpu(sb->cluster_block_start) +
59 ((uint64_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
60 << sb->bpc_bits);
61 }
62
63 static uint64_t cluster_to_offset(const struct exfat_super_block *sb,
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;
73 uint64_t fat_offset;
74
75 fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start))
76 + (uint64_t) cluster * sizeof(cluster);
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);
88 uint64_t offset = cluster_to_offset(sb, cluster);
89 uint8_t *entry;
90 const size_t max_iter = 10000;
91 size_t i = 0;
92
93 for (; i < max_iter; i++) {
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;
102
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 }
113
114 return NULL;
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);
123 if (!sb || !CLUSTER_SIZE(sb))
124 return errno ? -errno : BLKID_PROBE_NONE;
125
126 label = find_label(pr, sb);
127 if (label)
128 blkid_probe_set_utf8label(pr, label->name,
129 min((size_t) label->length * 2, sizeof(label->name)),
130 UL_ENCODE_UTF16LE);
131 else if (errno)
132 return -errno;
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
139 blkid_probe_sprintf_version(pr, "%u.%u",
140 sb->version.vermaj, sb->version.vermin);
141
142 blkid_probe_set_fsblocksize(pr, BLOCK_SIZE(sb));
143 blkid_probe_set_block_size(pr, BLOCK_SIZE(sb));
144
145 return BLKID_PROBE_OK;
146 }
147
148 const struct blkid_idinfo exfat_idinfo =
149 {
150 .name = "exfat",
151 .usage = BLKID_USAGE_FILESYSTEM,
152 .probefunc = probe_exfat,
153 .magics =
154 {
155 { .magic = "EXFAT ", .len = 8, .sboff = 3 },
156 { NULL }
157 }
158 };