blob: 41c6b9cd5fe5e2aea417572b42a0fd66e6b51e48 [file] [log] [blame]
bigbiff bigbiffe60683a2013-02-22 20:55:50 -05001/*
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
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
31struct 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
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
74#define MFT_RECORD_VOLUME 3
75#define NTFS_MAX_CLUSTER_SIZE (64 * 1024)
76
77enum {
78 MFT_RECORD_ATTR_VOLUME_NAME = cpu_to_le32(0x60),
79 MFT_RECORD_ATTR_END = cpu_to_le32(0xffffffff)
80};
81
82static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
83{
84 struct ntfs_super_block *ns;
85 struct master_file_table_record *mft;
86
87 uint32_t sectors_per_cluster, mft_record_size, attr_off;
88 uint16_t sector_size;
89 uint64_t nr_clusters, off;
90 unsigned char *buf_mft;
91
92 ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
93 if (!ns)
94 return -1;
95
96 /*
97 * Check bios parameters block
98 */
99 sector_size = le16_to_cpu(ns->bpb.sector_size);
100 sectors_per_cluster = ns->bpb.sectors_per_cluster;
101
102 if (sector_size < 256 || sector_size > 4096)
103 return 1;
104
105 switch (sectors_per_cluster) {
106 case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
107 break;
108 default:
109 return 1;
110 }
111
112 if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
113 ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
114 return 1;
115
116 /* Unused fields must be zero */
117 if (le16_to_cpu(ns->bpb.reserved_sectors)
118 || le16_to_cpu(ns->bpb.root_entries)
119 || le16_to_cpu(ns->bpb.sectors)
120 || le16_to_cpu(ns->bpb.sectors_per_fat)
121 || le32_to_cpu(ns->bpb.large_sectors)
122 || ns->bpb.fats)
123 return 1;
124
125 if ((uint8_t) ns->clusters_per_mft_record < 0xe1
126 || (uint8_t) ns->clusters_per_mft_record > 0xf7) {
127
128 switch (ns->clusters_per_mft_record) {
129 case 1: case 2: case 4: case 8: case 16: case 32: case 64:
130 break;
131 default:
132 return 1;
133 }
134 }
135
136 if (ns->clusters_per_mft_record > 0)
137 mft_record_size = ns->clusters_per_mft_record *
138 sectors_per_cluster * sector_size;
139 else
140 mft_record_size = 1 << (0 - ns->clusters_per_mft_record);
141
142 nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster;
143
144 if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) ||
145 (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters))
146 return 1;
147
148
149 off = le64_to_cpu(ns->mft_cluster_location) * sector_size *
150 sectors_per_cluster;
151
152 DBG(DEBUG_LOWPROBE, printf("NTFS: sector_size=%d, mft_record_size=%d, "
153 "sectors_per_cluster=%d, nr_clusters=%ju "
154 "cluster_offset=%jd\n",
155 (int) sector_size, mft_record_size,
156 sectors_per_cluster, nr_clusters,
157 off));
158
159 buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
160 if (!buf_mft)
161 return 1;
162
163 if (memcmp(buf_mft, "FILE", 4))
164 return 1;
165
166 off += MFT_RECORD_VOLUME * mft_record_size;
167
168 buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
169 if (!buf_mft)
170 return 1;
171
172 if (memcmp(buf_mft, "FILE", 4))
173 return 1;
174
175 mft = (struct master_file_table_record *) buf_mft;
176 attr_off = le16_to_cpu(mft->attrs_offset);
177
178 while (attr_off < mft_record_size &&
179 attr_off <= le32_to_cpu(mft->bytes_allocated)) {
180
181 uint32_t attr_len;
182 struct file_attribute *attr;
183
184 attr = (struct file_attribute *) (buf_mft + attr_off);
185 attr_len = le32_to_cpu(attr->len);
186 if (!attr_len)
187 break;
188
189 if (attr->type == MFT_RECORD_ATTR_END)
190 break;
191 if (attr->type == MFT_RECORD_ATTR_VOLUME_NAME) {
192 unsigned int val_off = le16_to_cpu(attr->value_offset);
193 unsigned int val_len = le32_to_cpu(attr->value_len);
194 unsigned char *val = ((uint8_t *) attr) + val_off;
195
196 blkid_probe_set_utf8label(pr, val, val_len, BLKID_ENC_UTF16LE);
197 break;
198 }
199
200 if (UINT_MAX - attr_len < attr_off)
201 break;
202 attr_off += attr_len;
203 }
204
205 blkid_probe_sprintf_uuid(pr,
206 (unsigned char *) &ns->volume_serial,
207 sizeof(ns->volume_serial),
208 "%016" PRIX64, le64_to_cpu(ns->volume_serial));
209 return 0;
210}
211
212
213const struct blkid_idinfo ntfs_idinfo =
214{
215 .name = "ntfs",
216 .usage = BLKID_USAGE_FILESYSTEM,
217 .probefunc = probe_ntfs,
218 .magics =
219 {
220 { .magic = "NTFS ", .len = 8, .sboff = 3 },
221 { NULL }
222 }
223};
224