]>
Commit | Line | Data |
---|---|---|
214cb872 | 1 | /* |
214cb872 KZ |
2 | * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> |
3 | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> | |
4 | * | |
9c863b75 KZ |
5 | * This file may be redistributed under the terms of the |
6 | * GNU Lesser General Public License. | |
214cb872 | 7 | */ |
214cb872 KZ |
8 | #include <stdio.h> |
9 | #include <stdlib.h> | |
10 | #include <unistd.h> | |
11 | #include <string.h> | |
214cb872 KZ |
12 | #include <inttypes.h> |
13 | ||
1e3cf6a2 | 14 | #include "superblocks.h" |
214cb872 | 15 | |
1abde0cd KZ |
16 | struct ntfs_bios_parameters { |
17 | uint16_t sector_size; /* Size of a sector in bytes. */ | |
18 | uint8_t sectors_per_cluster; /* Size of a cluster in sectors. */ | |
19 | uint16_t reserved_sectors; /* zero */ | |
20 | uint8_t fats; /* zero */ | |
21 | uint16_t root_entries; /* zero */ | |
22 | uint16_t sectors; /* zero */ | |
23 | uint8_t media_type; /* 0xf8 = hard disk */ | |
24 | uint16_t sectors_per_fat; /* zero */ | |
25 | uint16_t sectors_per_track; /* irrelevant */ | |
26 | uint16_t heads; /* irrelevant */ | |
27 | uint32_t hidden_sectors; /* zero */ | |
28 | uint32_t large_sectors; /* zero */ | |
29 | } __attribute__ ((__packed__)); | |
30 | ||
214cb872 KZ |
31 | struct ntfs_super_block { |
32 | uint8_t jump[3]; | |
1abde0cd KZ |
33 | uint8_t oem_id[8]; /* magic string */ |
34 | ||
35 | struct ntfs_bios_parameters bpb; | |
36 | ||
214cb872 KZ |
37 | uint16_t unused[2]; |
38 | uint64_t number_of_sectors; | |
39 | uint64_t mft_cluster_location; | |
40 | uint64_t mft_mirror_cluster_location; | |
1abde0cd | 41 | int8_t clusters_per_mft_record; |
214cb872 KZ |
42 | uint8_t reserved1[3]; |
43 | int8_t cluster_per_index_record; | |
44 | uint8_t reserved2[3]; | |
45 | uint64_t volume_serial; | |
1abde0cd | 46 | uint32_t checksum; |
11854e2e | 47 | } __attribute__((packed)); |
214cb872 KZ |
48 | |
49 | struct master_file_table_record { | |
50 | uint32_t magic; | |
51 | uint16_t usa_ofs; | |
52 | uint16_t usa_count; | |
53 | uint64_t lsn; | |
54 | uint16_t sequence_number; | |
55 | uint16_t link_count; | |
56 | uint16_t attrs_offset; | |
57 | uint16_t flags; | |
58 | uint32_t bytes_in_use; | |
59 | uint32_t bytes_allocated; | |
60 | } __attribute__((__packed__)); | |
61 | ||
62 | struct file_attribute { | |
63 | uint32_t type; | |
64 | uint32_t len; | |
65 | uint8_t non_resident; | |
66 | uint8_t name_len; | |
67 | uint16_t name_offset; | |
68 | uint16_t flags; | |
69 | uint16_t instance; | |
70 | uint32_t value_len; | |
71 | uint16_t value_offset; | |
72 | } __attribute__((__packed__)); | |
73 | ||
1abde0cd | 74 | #define MFT_RECORD_VOLUME 3 |
b5a7500b KZ |
75 | /* Windows 10 Creators edition has extended the cluster size limit to 2MB */ |
76 | #define NTFS_MAX_CLUSTER_SIZE (2 * 1024 * 1024) | |
1abde0cd KZ |
77 | |
78 | enum { | |
45b048b3 KZ |
79 | MFT_RECORD_ATTR_VOLUME_NAME = 0x60, |
80 | MFT_RECORD_ATTR_END = 0xffffffff | |
1abde0cd | 81 | }; |
214cb872 KZ |
82 | |
83 | static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag) | |
84 | { | |
85 | struct ntfs_super_block *ns; | |
86 | struct master_file_table_record *mft; | |
7d447a81 | 87 | |
999a2ffe | 88 | uint32_t sectors_per_cluster, mft_record_size; |
1abde0cd | 89 | uint16_t sector_size; |
999a2ffe | 90 | uint64_t nr_clusters, off, attr_off; |
7d447a81 | 91 | unsigned char *buf_mft; |
214cb872 KZ |
92 | |
93 | ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); | |
94 | if (!ns) | |
37f40602 | 95 | return errno ? -errno : 1; |
214cb872 | 96 | |
1abde0cd KZ |
97 | /* |
98 | * Check bios parameters block | |
99 | */ | |
100 | sector_size = le16_to_cpu(ns->bpb.sector_size); | |
214cb872 | 101 | |
1abde0cd | 102 | if (sector_size < 256 || sector_size > 4096) |
214cb872 KZ |
103 | return 1; |
104 | ||
b5a7500b | 105 | switch (ns->bpb.sectors_per_cluster) { |
1abde0cd | 106 | case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: |
b5a7500b | 107 | sectors_per_cluster = ns->bpb.sectors_per_cluster; |
1abde0cd KZ |
108 | break; |
109 | default: | |
b5a7500b KZ |
110 | if ((ns->bpb.sectors_per_cluster < 240) |
111 | || (ns->bpb.sectors_per_cluster > 249)) | |
112 | return 1; | |
113 | sectors_per_cluster = 1 << (256 - ns->bpb.sectors_per_cluster); | |
1abde0cd | 114 | } |
214cb872 | 115 | |
1abde0cd KZ |
116 | if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) * |
117 | ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) | |
118 | return 1; | |
214cb872 | 119 | |
1abde0cd KZ |
120 | /* Unused fields must be zero */ |
121 | if (le16_to_cpu(ns->bpb.reserved_sectors) | |
122 | || le16_to_cpu(ns->bpb.root_entries) | |
123 | || le16_to_cpu(ns->bpb.sectors) | |
124 | || le16_to_cpu(ns->bpb.sectors_per_fat) | |
125 | || le32_to_cpu(ns->bpb.large_sectors) | |
126 | || ns->bpb.fats) | |
214cb872 KZ |
127 | return 1; |
128 | ||
1abde0cd KZ |
129 | if ((uint8_t) ns->clusters_per_mft_record < 0xe1 |
130 | || (uint8_t) ns->clusters_per_mft_record > 0xf7) { | |
131 | ||
132 | switch (ns->clusters_per_mft_record) { | |
133 | case 1: case 2: case 4: case 8: case 16: case 32: case 64: | |
134 | break; | |
135 | default: | |
136 | return 1; | |
137 | } | |
138 | } | |
139 | ||
140 | if (ns->clusters_per_mft_record > 0) | |
141 | mft_record_size = ns->clusters_per_mft_record * | |
142 | sectors_per_cluster * sector_size; | |
143 | else | |
144 | mft_record_size = 1 << (0 - ns->clusters_per_mft_record); | |
145 | ||
146 | nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster; | |
147 | ||
148 | if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) || | |
149 | (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters)) | |
214cb872 KZ |
150 | return 1; |
151 | ||
7d447a81 | 152 | |
1abde0cd | 153 | off = le64_to_cpu(ns->mft_cluster_location) * sector_size * |
214cb872 KZ |
154 | sectors_per_cluster; |
155 | ||
fdbd7bb9 RM |
156 | DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", " |
157 | "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" " | |
158 | "cluster_offset=%"PRIu64"", | |
159 | sector_size, mft_record_size, | |
1abde0cd KZ |
160 | sectors_per_cluster, nr_clusters, |
161 | off)); | |
162 | ||
214cb872 KZ |
163 | buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); |
164 | if (!buf_mft) | |
37f40602 | 165 | return errno ? -errno : 1; |
214cb872 KZ |
166 | |
167 | if (memcmp(buf_mft, "FILE", 4)) | |
168 | return 1; | |
169 | ||
170 | off += MFT_RECORD_VOLUME * mft_record_size; | |
171 | ||
172 | buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); | |
173 | if (!buf_mft) | |
37f40602 | 174 | return errno ? -errno : 1; |
214cb872 KZ |
175 | |
176 | if (memcmp(buf_mft, "FILE", 4)) | |
177 | return 1; | |
178 | ||
179 | mft = (struct master_file_table_record *) buf_mft; | |
214cb872 | 180 | attr_off = le16_to_cpu(mft->attrs_offset); |
214cb872 | 181 | |
999a2ffe | 182 | while (attr_off + sizeof(struct file_attribute) <= mft_record_size && |
1abde0cd | 183 | attr_off <= le32_to_cpu(mft->bytes_allocated)) { |
214cb872 | 184 | |
1abde0cd | 185 | uint32_t attr_len; |
7d447a81 | 186 | struct file_attribute *attr; |
214cb872 | 187 | |
7d447a81 KZ |
188 | attr = (struct file_attribute *) (buf_mft + attr_off); |
189 | attr_len = le32_to_cpu(attr->len); | |
190 | if (!attr_len) | |
214cb872 KZ |
191 | break; |
192 | ||
45b048b3 | 193 | if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_END) |
214cb872 | 194 | break; |
45b048b3 | 195 | if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_VOLUME_NAME) { |
7d447a81 KZ |
196 | unsigned int val_off = le16_to_cpu(attr->value_offset); |
197 | unsigned int val_len = le32_to_cpu(attr->value_len); | |
198 | unsigned char *val = ((uint8_t *) attr) + val_off; | |
199 | ||
999a2ffe AT |
200 | if (attr_off + val_off + val_len <= mft_record_size) |
201 | blkid_probe_set_utf8label(pr, val, val_len, | |
202 | BLKID_ENC_UTF16LE); | |
7d447a81 | 203 | break; |
214cb872 | 204 | } |
7d447a81 | 205 | |
7d447a81 | 206 | attr_off += attr_len; |
214cb872 KZ |
207 | } |
208 | ||
209 | blkid_probe_sprintf_uuid(pr, | |
210 | (unsigned char *) &ns->volume_serial, | |
211 | sizeof(ns->volume_serial), | |
212 | "%016" PRIX64, le64_to_cpu(ns->volume_serial)); | |
214cb872 KZ |
213 | return 0; |
214 | } | |
215 | ||
216 | ||
217 | const struct blkid_idinfo ntfs_idinfo = | |
218 | { | |
219 | .name = "ntfs", | |
220 | .usage = BLKID_USAGE_FILESYSTEM, | |
221 | .probefunc = probe_ntfs, | |
222 | .magics = | |
223 | { | |
224 | { .magic = "NTFS ", .len = 8, .sboff = 3 }, | |
225 | { NULL } | |
226 | } | |
227 | }; | |
228 |