/src/util-linux/libblkid/src/superblocks/ntfs.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> |
3 | | * Copyright (C) 2008 Karel Zak <kzak@redhat.com> |
4 | | * |
5 | | * This file may be redistributed under the terms of the |
6 | | * GNU Lesser General Public License. |
7 | | */ |
8 | | #include <stdio.h> |
9 | | #include <stdlib.h> |
10 | | #include <unistd.h> |
11 | | #include <string.h> |
12 | | #include <inttypes.h> |
13 | | |
14 | | #include "superblocks.h" |
15 | | |
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 | | |
31 | | struct ntfs_super_block { |
32 | | uint8_t jump[3]; |
33 | | uint8_t oem_id[8]; /* magic string */ |
34 | | |
35 | | struct ntfs_bios_parameters bpb; |
36 | | |
37 | | uint16_t unused[2]; |
38 | | uint64_t number_of_sectors; |
39 | | uint64_t mft_cluster_location; |
40 | | uint64_t mft_mirror_cluster_location; |
41 | | int8_t clusters_per_mft_record; |
42 | | uint8_t reserved1[3]; |
43 | | int8_t cluster_per_index_record; |
44 | | uint8_t reserved2[3]; |
45 | | uint64_t volume_serial; |
46 | | uint32_t checksum; |
47 | | } __attribute__((packed)); |
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 | | |
74 | 148 | #define MFT_RECORD_VOLUME 3 |
75 | | /* Windows 10 Creators edition has extended the cluster size limit to 2MB */ |
76 | 604 | #define NTFS_MAX_CLUSTER_SIZE (2 * 1024 * 1024) |
77 | | |
78 | 252 | #define MFT_RECORD_ATTR_VOLUME_NAME 0x60 |
79 | 255 | #define MFT_RECORD_ATTR_END 0xffffffff |
80 | | |
81 | | static int __probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag, int save_info) |
82 | 754 | { |
83 | 754 | const struct ntfs_super_block *ns; |
84 | 754 | const struct master_file_table_record *mft; |
85 | | |
86 | 754 | uint32_t sectors_per_cluster, mft_record_size; |
87 | 754 | uint16_t sector_size; |
88 | 754 | uint64_t nr_clusters, off, attr_off; |
89 | 754 | const unsigned char *buf_mft; |
90 | | |
91 | 754 | ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); |
92 | 754 | if (!ns) |
93 | 0 | return errno ? -errno : 1; |
94 | | |
95 | | /* |
96 | | * Check bios parameters block |
97 | | */ |
98 | 754 | sector_size = le16_to_cpu(ns->bpb.sector_size); |
99 | | |
100 | 754 | if (sector_size < 256 || sector_size > 4096 || !is_power_of_2(sector_size)) |
101 | 115 | return 1; |
102 | | |
103 | 639 | switch (ns->bpb.sectors_per_cluster) { |
104 | 586 | case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: |
105 | 586 | sectors_per_cluster = ns->bpb.sectors_per_cluster; |
106 | 586 | break; |
107 | 53 | default: |
108 | 53 | if ((ns->bpb.sectors_per_cluster < 240) |
109 | 44 | || (ns->bpb.sectors_per_cluster > 249)) |
110 | 35 | return 1; |
111 | 18 | sectors_per_cluster = 1 << (256 - ns->bpb.sectors_per_cluster); |
112 | 639 | } |
113 | | |
114 | 604 | if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) * |
115 | 604 | sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) |
116 | 7 | return 1; |
117 | | |
118 | | /* Unused fields must be zero */ |
119 | 597 | if (le16_to_cpu(ns->bpb.reserved_sectors) |
120 | 546 | || le16_to_cpu(ns->bpb.root_entries) |
121 | 513 | || le16_to_cpu(ns->bpb.sectors) |
122 | 487 | || le16_to_cpu(ns->bpb.sectors_per_fat) |
123 | 464 | || le32_to_cpu(ns->bpb.large_sectors) |
124 | 415 | || ns->bpb.fats) |
125 | 193 | return 1; |
126 | | |
127 | 404 | if ((uint8_t) ns->clusters_per_mft_record < 0xe1 |
128 | 316 | || (uint8_t) ns->clusters_per_mft_record > 0xf7) { |
129 | | |
130 | 316 | switch (ns->clusters_per_mft_record) { |
131 | 302 | case 1: case 2: case 4: case 8: case 16: case 32: case 64: |
132 | 302 | break; |
133 | 14 | default: |
134 | 14 | return 1; |
135 | 316 | } |
136 | 316 | } |
137 | | |
138 | 390 | if (ns->clusters_per_mft_record > 0) { |
139 | 302 | mft_record_size = ns->clusters_per_mft_record * |
140 | 302 | sectors_per_cluster * sector_size; |
141 | 302 | } else { |
142 | 88 | int8_t mft_record_size_shift = 0 - ns->clusters_per_mft_record; |
143 | 88 | if (mft_record_size_shift < 0 || mft_record_size_shift >= 31) |
144 | 3 | return 1; |
145 | 85 | mft_record_size = 1 << mft_record_size_shift; |
146 | 85 | } |
147 | | |
148 | 387 | nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster; |
149 | | |
150 | 387 | if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) || |
151 | 348 | (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters)) |
152 | 62 | return 1; |
153 | | |
154 | | |
155 | 325 | off = le64_to_cpu(ns->mft_cluster_location) * sector_size * |
156 | 325 | sectors_per_cluster; |
157 | | |
158 | 325 | DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", " |
159 | 325 | "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" " |
160 | 325 | "cluster_offset=%"PRIu64"", |
161 | 325 | sector_size, mft_record_size, |
162 | 325 | sectors_per_cluster, nr_clusters, |
163 | 325 | off)); |
164 | | |
165 | 325 | if (mft_record_size < 4) |
166 | 0 | return 1; |
167 | | |
168 | 325 | buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); |
169 | 325 | if (!buf_mft) |
170 | 162 | return errno ? -errno : 1; |
171 | | |
172 | 163 | if (memcmp(buf_mft, "FILE", 4) != 0) |
173 | 15 | return 1; |
174 | | |
175 | 148 | off += MFT_RECORD_VOLUME * mft_record_size; |
176 | | |
177 | 148 | buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); |
178 | 148 | if (!buf_mft) |
179 | 6 | return errno ? -errno : 1; |
180 | | |
181 | 142 | if (memcmp(buf_mft, "FILE", 4) != 0) |
182 | 7 | return 1; |
183 | | |
184 | | /* return if caller does not care about UUID and LABEL */ |
185 | 135 | if (!save_info) |
186 | 5 | return 0; |
187 | | |
188 | 130 | mft = (struct master_file_table_record *) buf_mft; |
189 | 130 | attr_off = le16_to_cpu(mft->attrs_offset); |
190 | | |
191 | 348 | while (attr_off + sizeof(struct file_attribute) <= mft_record_size && |
192 | 259 | attr_off <= le32_to_cpu(mft->bytes_allocated)) { |
193 | | |
194 | 257 | uint32_t attr_len; |
195 | 257 | struct file_attribute *attr; |
196 | | |
197 | 257 | attr = (struct file_attribute *) (buf_mft + attr_off); |
198 | 257 | attr_len = le32_to_cpu(attr->len); |
199 | 257 | if (!attr_len) |
200 | 2 | break; |
201 | | |
202 | 255 | if (le32_to_cpu(attr->type) == (uint32_t) MFT_RECORD_ATTR_END) |
203 | 3 | break; |
204 | 252 | if (le32_to_cpu(attr->type) == (uint32_t) MFT_RECORD_ATTR_VOLUME_NAME) { |
205 | 34 | unsigned int val_off = le16_to_cpu(attr->value_offset); |
206 | 34 | unsigned int val_len = le32_to_cpu(attr->value_len); |
207 | 34 | unsigned char *val = ((uint8_t *) attr) + val_off; |
208 | | |
209 | 34 | if (val_off <= mft_record_size - attr_off && |
210 | 32 | val_len <= mft_record_size - attr_off - val_off) |
211 | 12 | blkid_probe_set_utf8label(pr, val, val_len, |
212 | 12 | UL_ENCODE_UTF16LE); |
213 | 34 | break; |
214 | 34 | } |
215 | | |
216 | 218 | attr_off += attr_len; |
217 | 218 | } |
218 | | |
219 | | |
220 | 130 | blkid_probe_set_fsblocksize(pr, sector_size * sectors_per_cluster); |
221 | 130 | blkid_probe_set_block_size(pr, sector_size); |
222 | 130 | blkid_probe_set_fssize(pr, le64_to_cpu(ns->number_of_sectors) * sector_size); |
223 | | |
224 | 130 | blkid_probe_sprintf_uuid(pr, |
225 | 130 | (unsigned char *) &ns->volume_serial, |
226 | 130 | sizeof(ns->volume_serial), |
227 | 130 | "%016" PRIX64, le64_to_cpu(ns->volume_serial)); |
228 | 130 | return 0; |
229 | 135 | } |
230 | | |
231 | | static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag) |
232 | 332 | { |
233 | 332 | return __probe_ntfs(pr, mag, 1); |
234 | 332 | } |
235 | | |
236 | | int blkid_probe_is_ntfs(blkid_probe pr) |
237 | 909 | { |
238 | 909 | const struct blkid_idmag *mag = NULL; |
239 | 909 | int rc; |
240 | | |
241 | 909 | rc = blkid_probe_get_idmag(pr, &ntfs_idinfo, NULL, &mag); |
242 | 909 | if (rc < 0) |
243 | 0 | return rc; /* error */ |
244 | 909 | if (rc != BLKID_PROBE_OK || !mag) |
245 | 487 | return 0; |
246 | | |
247 | 422 | return __probe_ntfs(pr, mag, 0) == 0 ? 1 : 0; |
248 | 909 | } |
249 | | |
250 | | const struct blkid_idinfo ntfs_idinfo = |
251 | | { |
252 | | .name = "ntfs", |
253 | | .usage = BLKID_USAGE_FILESYSTEM, |
254 | | .probefunc = probe_ntfs, |
255 | | .magics = |
256 | | { |
257 | | { .magic = "NTFS ", .len = 8, .sboff = 3 }, |
258 | | { NULL } |
259 | | } |
260 | | }; |
261 | | |