]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libblkid/src/superblocks/exfat.c
hwclock: make glibc 2.31 compatible
[thirdparty/util-linux.git] / libblkid / src / superblocks / exfat.c
CommitLineData
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
9struct 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
33struct 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
49static 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 55static 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 63static 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
69static 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
84static 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
117static 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
147const 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};