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
;
85 struct master_file_table_record
*mft
;
87 uint32_t sectors_per_cluster
, mft_record_size
;
89 uint64_t volume_serial
;
90 uint64_t nr_clusters
, off
, attr_off
;
91 unsigned char *buf_mft
;
93 ns
= blkid_probe_get_sb(pr
, mag
, struct ntfs_super_block
);
95 return errno
? -errno
: 1;
98 * Check bios parameters block
100 sector_size
= le16_to_cpu(ns
->bpb
.sector_size
);
101 sectors_per_cluster
= ns
->bpb
.sectors_per_cluster
;
103 if (sector_size
< 256 || sector_size
> 4096)
106 switch (sectors_per_cluster
) {
107 case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
113 if ((uint16_t) le16_to_cpu(ns
->bpb
.sector_size
) *
114 ns
->bpb
.sectors_per_cluster
> NTFS_MAX_CLUSTER_SIZE
)
117 /* Unused fields must be zero */
118 if (le16_to_cpu(ns
->bpb
.reserved_sectors
)
119 || le16_to_cpu(ns
->bpb
.root_entries
)
120 || le16_to_cpu(ns
->bpb
.sectors
)
121 || le16_to_cpu(ns
->bpb
.sectors_per_fat
)
122 || le32_to_cpu(ns
->bpb
.large_sectors
)
126 if ((uint8_t) ns
->clusters_per_mft_record
< 0xe1
127 || (uint8_t) ns
->clusters_per_mft_record
> 0xf7) {
129 switch (ns
->clusters_per_mft_record
) {
130 case 1: case 2: case 4: case 8: case 16: case 32: case 64:
137 if (ns
->clusters_per_mft_record
> 0)
138 mft_record_size
= ns
->clusters_per_mft_record
*
139 sectors_per_cluster
* sector_size
;
141 mft_record_size
= 1 << (0 - ns
->clusters_per_mft_record
);
143 nr_clusters
= le64_to_cpu(ns
->number_of_sectors
) / sectors_per_cluster
;
145 if ((le64_to_cpu(ns
->mft_cluster_location
) > nr_clusters
) ||
146 (le64_to_cpu(ns
->mft_mirror_cluster_location
) > nr_clusters
))
150 volume_serial
= ns
->volume_serial
;
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
", volume_serial=%"PRIu64
"",
157 sector_size
, mft_record_size
,
158 sectors_per_cluster
, nr_clusters
,
159 off
, volume_serial
));
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))
177 mft
= (struct master_file_table_record
*) buf_mft
;
178 attr_off
= le16_to_cpu(mft
->attrs_offset
);
180 while (attr_off
+ sizeof(struct file_attribute
) <= mft_record_size
&&
181 attr_off
<= le32_to_cpu(mft
->bytes_allocated
)) {
184 struct file_attribute
*attr
;
186 attr
= (struct file_attribute
*) (buf_mft
+ attr_off
);
187 attr_len
= le32_to_cpu(attr
->len
);
191 if (le32_to_cpu(attr
->type
) == MFT_RECORD_ATTR_END
)
193 if (le32_to_cpu(attr
->type
) == MFT_RECORD_ATTR_VOLUME_NAME
) {
194 unsigned int val_off
= le16_to_cpu(attr
->value_offset
);
195 unsigned int val_len
= le32_to_cpu(attr
->value_len
);
196 unsigned char *val
= ((uint8_t *) attr
) + val_off
;
198 if (attr_off
+ val_off
+ val_len
<= mft_record_size
)
199 blkid_probe_set_utf8label(pr
, val
, val_len
,
204 attr_off
+= attr_len
;
207 blkid_probe_sprintf_uuid(pr
,
208 (unsigned char *) &volume_serial
,
209 sizeof(volume_serial
),
210 "%016" PRIX64
, le64_to_cpu(volume_serial
));
215 const struct blkid_idinfo ntfs_idinfo
=
218 .usage
= BLKID_USAGE_FILESYSTEM
,
219 .probefunc
= probe_ntfs
,
222 { .magic
= "NTFS ", .len
= 8, .sboff
= 3 },