2 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
3 * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
5 * This file may be redistributed under the terms of the
6 * GNU Lesser General Public License.
14 #include "superblocks.h"
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__));
31 struct ntfs_super_block {
33 uint8_t oem_id[8]; /* magic string */
35 struct ntfs_bios_parameters bpb;
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;
43 int8_t cluster_per_index_record;
45 uint64_t volume_serial;
47 } __attribute__((packed));
49 struct master_file_table_record {
54 uint16_t sequence_number;
56 uint16_t attrs_offset;
58 uint32_t bytes_in_use;
59 uint32_t bytes_allocated;
60 } __attribute__((__packed__));
62 struct file_attribute {
71 uint16_t value_offset;
72 } __attribute__((__packed__));
74 #define MFT_RECORD_VOLUME 3
75 #define NTFS_MAX_CLUSTER_SIZE (64 * 1024)
78 MFT_RECORD_ATTR_VOLUME_NAME = 0x60,
79 MFT_RECORD_ATTR_END = 0xffffffff
82 static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
84 struct ntfs_super_block *ns;
86 struct master_file_table_record *mft;
89 uint32_t sectors_per_cluster, mft_record_size;
91 uint64_t nr_clusters, off; //, attr_off;
92 unsigned char *buf_mft;
94 ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
96 return errno ? -errno : 1;
99 * Check bios parameters block
101 sector_size = le16_to_cpu(ns->bpb.sector_size);
102 sectors_per_cluster = ns->bpb.sectors_per_cluster;
104 if (sector_size < 256 || sector_size > 4096)
107 switch (sectors_per_cluster) {
108 case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
114 if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
115 ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
118 /* Unused fields must be zero */
119 if (le16_to_cpu(ns->bpb.reserved_sectors)
120 || le16_to_cpu(ns->bpb.root_entries)
121 || le16_to_cpu(ns->bpb.sectors)
122 || le16_to_cpu(ns->bpb.sectors_per_fat)
123 || le32_to_cpu(ns->bpb.large_sectors)
127 if ((uint8_t) ns->clusters_per_mft_record < 0xe1
128 || (uint8_t) ns->clusters_per_mft_record > 0xf7) {
130 switch (ns->clusters_per_mft_record) {
131 case 1: case 2: case 4: case 8: case 16: case 32: case 64:
138 if (ns->clusters_per_mft_record > 0)
139 mft_record_size = ns->clusters_per_mft_record *
140 sectors_per_cluster * sector_size;
142 mft_record_size = 1 << (0 - ns->clusters_per_mft_record);
144 nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster;
146 if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) ||
147 (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters))
151 off = le64_to_cpu(ns->mft_cluster_location) * sector_size *
154 DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", "
155 "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" "
156 "cluster_offset=%"PRIu64"",
157 sector_size, mft_record_size,
158 sectors_per_cluster, nr_clusters,
161 buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
163 return errno ? -errno : 1;
165 if (memcmp(buf_mft, "FILE", 4))
168 off += MFT_RECORD_VOLUME * mft_record_size;
170 buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
172 return errno ? -errno : 1;
174 if (memcmp(buf_mft, "FILE", 4))
178 mft = (struct master_file_table_record *) buf_mft;
179 attr_off = le16_to_cpu(mft->attrs_offset);
181 while (attr_off + sizeof(struct file_attribute) <= mft_record_size &&
182 attr_off <= le32_to_cpu(mft->bytes_allocated)) {
185 struct file_attribute *attr;
187 attr = (struct file_attribute *) (buf_mft + attr_off);
188 attr_len = le32_to_cpu(attr->len);
192 if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_END)
194 if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_VOLUME_NAME) {
195 unsigned int val_off = le16_to_cpu(attr->value_offset);
196 unsigned int val_len = le32_to_cpu(attr->value_len);
197 unsigned char *val = ((uint8_t *) attr) + val_off;
199 if (attr_off + val_off + val_len <= mft_record_size)
200 blkid_probe_set_utf8label(pr, val, val_len,
205 attr_off += attr_len;
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));
217 const struct blkid_idinfo ntfs_idinfo =
220 .usage = BLKID_USAGE_FILESYSTEM,
221 .probefunc = probe_ntfs,
224 { .magic = "NTFS ", .len = 8, .sboff = 3 },