]> git.ipfire.org Git - thirdparty/util-linux.git/blame - libblkid/src/superblocks/ntfs.c
libblkid: (ntfs) enlarge cluster limit to 2MB
[thirdparty/util-linux.git] / libblkid / src / superblocks / ntfs.c
CommitLineData
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
16struct 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
31struct 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
49struct 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
62struct 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
78enum {
45b048b3
KZ
79 MFT_RECORD_ATTR_VOLUME_NAME = 0x60,
80 MFT_RECORD_ATTR_END = 0xffffffff
1abde0cd 81};
214cb872
KZ
82
83static 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
217const 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