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
19 * From Busybox 1.21.1 to Tomato RAF
22 #include "volume_id_internal.h"
24 #define EXFAT_SB_OFFSET 0
25 #define EXFAT_DIR_ENTRY_SZ 32
26 #define EXFAT_MAX_DIR_ENTRIES 100
28 struct exfat_super_block
{
29 /* 0x00 */ uint8_t boot_jump
[3];
30 /* 0x03 */ uint8_t fs_name
[8];
31 /* 0x0B */ uint8_t must_be_zero
[53];
32 /* 0x40 */ uint64_t partition_offset
;
33 /* 0x48 */ uint64_t volume_length
;
34 /* 0x50 */ uint32_t fat_offset
; // Sector address of 1st FAT
35 /* 0x54 */ uint32_t fat_size
; // In sectors
36 /* 0x58 */ uint32_t cluster_heap_offset
; // Sector address of Data Region
37 /* 0x5C */ uint32_t cluster_count
;
38 /* 0x60 */ uint32_t root_dir
; // Cluster address of Root Directory
39 /* 0x64 */ uint8_t vol_serial_nr
[4]; // Volume ID
40 /* 0x68 */ uint16_t fs_revision
; // VV.MM
41 /* 0x6A */ uint16_t vol_flags
;
42 /* 0x6C */ uint8_t bytes_per_sector
; // Power of 2: 9 => 512, 12 => 4096
43 /* 0x6D */ uint8_t sectors_per_cluster
; // Power of 2
44 /* 0x6E */ uint8_t nr_of_fats
; // 2 for TexFAT
48 struct exfat_dir_entry
{
49 /* 0x00 */ uint8_t entry_type
;
52 /* 0x01 */ uint8_t char_count
; // Length of label
53 /* 0x02 */ uint16_t vol_label
[11]; // UTF16 string without null termination
54 /* 0x18 */ uint8_t reserved
[8];
55 /* 0x20 */ } PACKED label
;
57 /* 0x01 */ uint8_t sec_count
;
58 /* 0x02 */ uint16_t set_checksum
;
59 /* 0x04 */ uint16_t flags
;
60 /* 0x06 */ uint8_t vol_guid
[16];
61 /* 0x16 */ uint8_t reserved
[10];
62 /* 0x20 */ } PACKED guid
;
66 int FAST_FUNC
volume_id_probe_exfat(struct volume_id
*id
/*,uint64_t off*/)
68 struct exfat_super_block
*sb
;
69 struct exfat_dir_entry
*de
;
72 uint64_t root_dir_off
;
74 unsigned need_lbl_guid
;
76 // Primary super block
77 dbg("exFAT: probing at offset 0x%x", EXFAT_SB_OFFSET
);
78 sb
= volume_id_get_buffer(id
, EXFAT_SB_OFFSET
, sizeof(*sb
));
83 if (memcmp(sb
->fs_name
, "EXFAT ", 8) != 0)
86 sector_sz
= 1 << sb
->bytes_per_sector
;
87 cluster_sz
= sector_sz
<< sb
->sectors_per_cluster
;
88 // There are no clusters 0 and 1, so the first cluster is 2.
89 root_dir_off
= (uint64_t)EXFAT_SB_OFFSET
+
90 // Hmm... should we cast sector_sz/cluster_sz to uint64_t?
91 (le32_to_cpu(sb
->cluster_heap_offset
)) * sector_sz
+
92 (le32_to_cpu(sb
->root_dir
) - 2) * cluster_sz
;
93 dbg("exFAT: sector size 0x%x bytes", sector_sz
);
94 dbg("exFAT: cluster size 0x%x bytes", cluster_sz
);
95 dbg("exFAT: root dir is at 0x%llx", (long long)root_dir_off
);
97 // Use DOS uuid as fallback, if no GUID set
98 volume_id_set_uuid(id
, sb
->vol_serial_nr
, UUID_DOS
);
100 // EXFAT_MAX_DIR_ENTRIES is used as a safety belt.
101 // The Root Directory may hold an unlimited number of entries,
102 // so we do not want to check all. Usually label and GUID
103 // are in the beginning, but there are no guarantees.
104 need_lbl_guid
= (1 << 0) | (1 << 1);
105 for (count
= 0; count
< EXFAT_MAX_DIR_ENTRIES
; count
++) {
106 de
= volume_id_get_buffer(id
, root_dir_off
+ (count
* EXFAT_DIR_ENTRY_SZ
), EXFAT_DIR_ENTRY_SZ
);
109 if (de
->entry_type
== 0x00) {
110 // End of Directory Marker
111 dbg("exFAT: End of root directory reached after %u entries", count
);
114 if (de
->entry_type
== 0x83) {
115 // Volume Label Directory Entry
116 volume_id_set_label_unicode16(id
, (uint8_t *)de
->type
.label
.vol_label
,
117 LE
, 2 * de
->type
.label
.char_count
);
118 need_lbl_guid
&= ~(1 << 0);
120 if (de
->entry_type
== 0xA0) {
121 // Volume GUID Directory Entry
122 volume_id_set_uuid(id
, de
->type
.guid
.vol_guid
, UUID_DCE
);
123 need_lbl_guid
&= ~(1 << 1);
129 IF_FEATURE_BLKID_TYPE(id
->type
= "exfat";)