2 * volume_id - reads filesystem label and uuid
4 * Copyright (C) 2012 S-G Bergh <sgb@systemasis.org>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #include "volume_id_internal.h"
23 #define EXFAT_SB_OFFSET 0
24 #define EXFAT_DIR_ENTRY_SZ 32
25 #define EXFAT_MAX_DIR_ENTRIES 100
27 struct exfat_super_block
{
28 /* 0x00 */ uint8_t boot_jump
[3];
29 /* 0x03 */ uint8_t fs_name
[8];
30 /* 0x0B */ uint8_t must_be_zero
[53];
31 /* 0x40 */ uint64_t partition_offset
;
32 /* 0x48 */ uint64_t volume_length
;
33 /* 0x50 */ uint32_t fat_offset
; // Sector address of 1st FAT
34 /* 0x54 */ uint32_t fat_size
; // In sectors
35 /* 0x58 */ uint32_t cluster_heap_offset
; // Sector address of Data Region
36 /* 0x5C */ uint32_t cluster_count
;
37 /* 0x60 */ uint32_t root_dir
; // Cluster address of Root Directory
38 /* 0x64 */ uint8_t vol_serial_nr
[4]; // Volume ID
39 /* 0x68 */ uint16_t fs_revision
; // VV.MM
40 /* 0x6A */ uint16_t vol_flags
;
41 /* 0x6C */ uint8_t bytes_per_sector
; // Power of 2: 9 => 512, 12 => 4096
42 /* 0x6D */ uint8_t sectors_per_cluster
; // Power of 2
43 /* 0x6E */ uint8_t nr_of_fats
; // 2 for TexFAT
47 struct exfat_dir_entry
{
48 /* 0x00 */ uint8_t entry_type
;
51 /* 0x01 */ uint8_t char_count
; // Length of label
52 /* 0x02 */ uint16_t vol_label
[11]; // UTF16 string without null termination
53 /* 0x18 */ uint8_t reserved
[8];
54 /* 0x20 */ } PACKED label
;
56 /* 0x01 */ uint8_t sec_count
;
57 /* 0x02 */ uint16_t set_checksum
;
58 /* 0x04 */ uint16_t flags
;
59 /* 0x06 */ uint8_t vol_guid
[16];
60 /* 0x16 */ uint8_t reserved
[10];
61 /* 0x20 */ } PACKED guid
;
65 int FAST_FUNC
volume_id_probe_exfat(struct volume_id
*id
/*,uint64_t off*/)
67 struct exfat_super_block
*sb
;
68 struct exfat_dir_entry
*de
;
71 uint64_t root_dir_off
;
73 unsigned need_lbl_guid
;
75 // Primary super block
76 dbg("exFAT: probing at offset 0x%x", EXFAT_SB_OFFSET
);
77 sb
= volume_id_get_buffer(id
, EXFAT_SB_OFFSET
, sizeof(*sb
));
82 if (memcmp(sb
->fs_name
, "EXFAT ", 8) != 0)
85 sector_sz
= 1 << sb
->bytes_per_sector
;
86 cluster_sz
= sector_sz
<< sb
->sectors_per_cluster
;
87 // There are no clusters 0 and 1, so the first cluster is 2.
88 root_dir_off
= (uint64_t)EXFAT_SB_OFFSET
+
89 // Hmm... should we cast sector_sz/cluster_sz to uint64_t?
90 (le32_to_cpu(sb
->cluster_heap_offset
)) * sector_sz
+
91 (le32_to_cpu(sb
->root_dir
) - 2) * cluster_sz
;
92 dbg("exFAT: sector size 0x%x bytes", sector_sz
);
93 dbg("exFAT: cluster size 0x%x bytes", cluster_sz
);
94 dbg("exFAT: root dir is at 0x%llx", (long long)root_dir_off
);
96 // Use DOS uuid as fallback, if no GUID set
97 volume_id_set_uuid(id
, sb
->vol_serial_nr
, UUID_DOS
);
99 // EXFAT_MAX_DIR_ENTRIES is used as a safety belt.
100 // The Root Directory may hold an unlimited number of entries,
101 // so we do not want to check all. Usually label and GUID
102 // are in the beginning, but there are no guarantees.
103 need_lbl_guid
= (1 << 0) | (1 << 1);
104 for (count
= 0; count
< EXFAT_MAX_DIR_ENTRIES
; count
++) {
105 de
= volume_id_get_buffer(id
, root_dir_off
+ (count
* EXFAT_DIR_ENTRY_SZ
), EXFAT_DIR_ENTRY_SZ
);
108 if (de
->entry_type
== 0x00) {
109 // End of Directory Marker
110 dbg("exFAT: End of root directory reached after %u entries", count
);
113 if (de
->entry_type
== 0x83) {
114 // Volume Label Directory Entry
115 volume_id_set_label_unicode16(id
, (uint8_t *)de
->type
.label
.vol_label
,
116 LE
, 2 * de
->type
.label
.char_count
);
117 need_lbl_guid
&= ~(1 << 0);
119 if (de
->entry_type
== 0xA0) {
120 // Volume GUID Directory Entry
121 volume_id_set_uuid(id
, de
->type
.guid
.vol_guid
, UUID_DCE
);
122 need_lbl_guid
&= ~(1 << 1);
128 IF_FEATURE_BLKID_TYPE(id
->type
= "exfat";)