blob: 787deb2c97705177564657c0153b9ca8cbcf02a7 [file] [log] [blame]
/*
* Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
* Copyright (C) 2008 Karel Zak <kzak@redhat.com>
*
* This file may be redistributed under the terms of the
* GNU Lesser General Public License.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <inttypes.h>
#include "superblocks.h"
struct ntfs_super_block {
uint8_t jump[3];
uint8_t oem_id[8];
uint8_t bios_parameter_block[25];
uint16_t unused[2];
uint64_t number_of_sectors;
uint64_t mft_cluster_location;
uint64_t mft_mirror_cluster_location;
int8_t cluster_per_mft_record;
uint8_t reserved1[3];
int8_t cluster_per_index_record;
uint8_t reserved2[3];
uint64_t volume_serial;
uint16_t checksum;
} __attribute__((packed));
struct master_file_table_record {
uint32_t magic;
uint16_t usa_ofs;
uint16_t usa_count;
uint64_t lsn;
uint16_t sequence_number;
uint16_t link_count;
uint16_t attrs_offset;
uint16_t flags;
uint32_t bytes_in_use;
uint32_t bytes_allocated;
} __attribute__((__packed__));
struct file_attribute {
uint32_t type;
uint32_t len;
uint8_t non_resident;
uint8_t name_len;
uint16_t name_offset;
uint16_t flags;
uint16_t instance;
uint32_t value_len;
uint16_t value_offset;
} __attribute__((__packed__));
#define MFT_RECORD_VOLUME 3
#define MFT_RECORD_ATTR_VOLUME_NAME 0x60
#define MFT_RECORD_ATTR_VOLUME_INFO 0x70
#define MFT_RECORD_ATTR_OBJECT_ID 0x40
#define MFT_RECORD_ATTR_END 0xffffffffu
static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
{
struct ntfs_super_block *ns;
struct master_file_table_record *mft;
struct file_attribute *attr;
int bytes_per_sector, sectors_per_cluster;
int mft_record_size, attr_off, attr_len;
unsigned int attr_type, val_len;
int val_off;
uint64_t nr_clusters;
blkid_loff_t off;
unsigned char *buf_mft, *val;
ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
if (!ns)
return -1;
bytes_per_sector = ns->bios_parameter_block[0] +
(ns->bios_parameter_block[1] << 8);
sectors_per_cluster = ns->bios_parameter_block[2];
if ((bytes_per_sector < 512) || (sectors_per_cluster == 0))
return 1;
if (ns->cluster_per_mft_record < 0)
mft_record_size = 1 << (0 - ns->cluster_per_mft_record);
else
mft_record_size = ns->cluster_per_mft_record *
sectors_per_cluster * bytes_per_sector;
nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster;
if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) ||
(le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters))
return 1;
off = le64_to_cpu(ns->mft_mirror_cluster_location) *
bytes_per_sector * sectors_per_cluster;
buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
if (!buf_mft)
return 1;
if (memcmp(buf_mft, "FILE", 4))
return 1;
off = le64_to_cpu(ns->mft_cluster_location) * bytes_per_sector *
sectors_per_cluster;
buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
if (!buf_mft)
return 1;
if (memcmp(buf_mft, "FILE", 4))
return 1;
off += MFT_RECORD_VOLUME * mft_record_size;
buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
if (!buf_mft)
return 1;
if (memcmp(buf_mft, "FILE", 4))
return 1;
mft = (struct master_file_table_record *) buf_mft;
attr_off = le16_to_cpu(mft->attrs_offset);
while (1) {
attr = (struct file_attribute *) (buf_mft + attr_off);
attr_len = le16_to_cpu(attr->len);
attr_type = le32_to_cpu(attr->type);
val_off = le16_to_cpu(attr->value_offset);
val_len = le32_to_cpu(attr->value_len);
attr_off += attr_len;
if ((attr_off > mft_record_size) ||
(attr_len == 0))
break;
if (attr_type == MFT_RECORD_ATTR_END)
break;
if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) {
val = ((uint8_t *) attr) + val_off;
blkid_probe_set_utf8label(pr, val, val_len, BLKID_ENC_UTF16LE);
}
}
blkid_probe_sprintf_uuid(pr,
(unsigned char *) &ns->volume_serial,
sizeof(ns->volume_serial),
"%016" PRIX64, le64_to_cpu(ns->volume_serial));
return 0;
}
const struct blkid_idinfo ntfs_idinfo =
{
.name = "ntfs",
.usage = BLKID_USAGE_FILESYSTEM,
.probefunc = probe_ntfs,
.magics =
{
{ .magic = "NTFS ", .len = 8, .sboff = 3 },
{ NULL }
}
};