blob: 215c67114a859888295f14d48c9cbc1488574e77 [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
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 {
22 uint8_t minor;
23 uint8_t major;
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;
36 uint8_t name[30];
37} __attribute__((__packed__));
38
39#define BLOCK_SIZE(sb) (1 << (sb)->block_bits)
40#define CLUSTER_SIZE(sb) (BLOCK_SIZE(sb) << (sb)->bpc_bits)
41#define EXFAT_FIRST_DATA_CLUSTER 2
42#define EXFAT_LAST_DATA_CLUSTER 0xffffff6
43#define EXFAT_ENTRY_SIZE 32
44
45#define EXFAT_ENTRY_EOD 0x00
46#define EXFAT_ENTRY_LABEL 0x83
47
48static blkid_loff_t block_to_offset(const struct exfat_super_block *sb,
49 blkid_loff_t block)
50{
51 return (blkid_loff_t) block << sb->block_bits;
52}
53
54static blkid_loff_t cluster_to_block(const struct exfat_super_block *sb,
55 uint32_t cluster)
56{
57 return le32_to_cpu(sb->cluster_block_start) +
58 ((blkid_loff_t) (cluster - EXFAT_FIRST_DATA_CLUSTER)
59 << sb->bpc_bits);
60}
61
62static blkid_loff_t cluster_to_offset(const struct exfat_super_block *sb,
63 uint32_t cluster)
64{
65 return block_to_offset(sb, cluster_to_block(sb, cluster));
66}
67
68static uint32_t next_cluster(blkid_probe pr,
69 const struct exfat_super_block *sb, uint32_t cluster)
70{
71 uint32_t *next;
72 blkid_loff_t fat_offset;
73
74 fat_offset = block_to_offset(sb, le32_to_cpu(sb->fat_block_start))
75 + (blkid_loff_t) cluster * sizeof(cluster);
76 next = (uint32_t *) blkid_probe_get_buffer(pr, fat_offset,
77 sizeof(uint32_t));
78 if (!next)
79 return 0;
80 return le32_to_cpu(*next);
81}
82
83static struct exfat_entry_label *find_label(blkid_probe pr,
84 const struct exfat_super_block *sb)
85{
86 uint32_t cluster = le32_to_cpu(sb->rootdir_cluster);
87 blkid_loff_t offset = cluster_to_offset(sb, cluster);
88 uint8_t *entry;
89
90 for (;;) {
91 entry = (uint8_t *) blkid_probe_get_buffer(pr, offset,
92 EXFAT_ENTRY_SIZE);
93 if (!entry)
94 return NULL;
95 if (entry[0] == EXFAT_ENTRY_EOD)
96 return NULL;
97 if (entry[0] == EXFAT_ENTRY_LABEL)
98 return (struct exfat_entry_label *) entry;
99 offset += EXFAT_ENTRY_SIZE;
100 if (offset % CLUSTER_SIZE(sb) == 0) {
101 cluster = next_cluster(pr, sb, cluster);
102 if (cluster < EXFAT_FIRST_DATA_CLUSTER)
103 return NULL;
104 if (cluster > EXFAT_LAST_DATA_CLUSTER)
105 return NULL;
106 offset = cluster_to_offset(sb, cluster);
107 }
108 }
109}
110
111static int probe_exfat(blkid_probe pr, const struct blkid_idmag *mag)
112{
113 struct exfat_super_block *sb;
114 struct exfat_entry_label *label;
115
116 sb = blkid_probe_get_sb(pr, mag, struct exfat_super_block);
117 if (!sb)
118 return -1;
119
120 label = find_label(pr, sb);
121 if (label)
122 blkid_probe_set_utf8label(pr, label->name,
123 min(label->length * 2, 30), BLKID_ENC_UTF16LE);
124
125 blkid_probe_sprintf_uuid(pr, sb->volume_serial, 4,
126 "%02hhX%02hhX-%02hhX%02hhX",
127 sb->volume_serial[3], sb->volume_serial[2],
128 sb->volume_serial[1], sb->volume_serial[0]);
129
130 blkid_probe_sprintf_version(pr, "%u.%u",
131 sb->version.major, sb->version.minor);
132
133 return 0;
134}
135
136const struct blkid_idinfo exfat_idinfo =
137{
138 .name = "exfat",
139 .usage = BLKID_USAGE_FILESYSTEM,
140 .probefunc = probe_exfat,
141 .magics =
142 {
143 { .magic = "EXFAT ", .len = 8, .sboff = 3 },
144 { NULL }
145 }
146};