Message ID | 20171225195211.9380-1-zajec5@gmail.com |
---|---|
State | Accepted |
Delegated to: | Rafał Miłecki |
Headers | show |
Series | [LEDE-DEV,fstools] libblkid-tiny: add support for NTFS superblock | expand |
On 25/12/17 20:52, Rafał Miłecki wrote: > From: Rafał Miłecki <rafal@milecki.pl> > > Its copied from the util-linux project and its libblkid. Call to the > blkid_probe_set_utf8label was commented out due to libblkid-tiny not > supporting it yet. > > Signed-off-by: Rafał Miłecki <rafal@milecki.pl> Acked-by: John Crispin <john@phrozen.org> > --- > CMakeLists.txt | 1 + > libblkid-tiny/libblkid-tiny.c | 1 + > libblkid-tiny/ntfs.c | 228 ++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 230 insertions(+) > create mode 100644 libblkid-tiny/ntfs.c > > diff --git a/CMakeLists.txt b/CMakeLists.txt > index 8752612..9e855bd 100644 > --- a/CMakeLists.txt > +++ b/CMakeLists.txt > @@ -24,6 +24,7 @@ ADD_LIBRARY(blkid-tiny SHARED > libblkid-tiny/ext.c > libblkid-tiny/jffs2.c > libblkid-tiny/vfat.c > + libblkid-tiny/ntfs.c > libblkid-tiny/hfs.c > libblkid-tiny/swap.c > libblkid-tiny/ubi.c > diff --git a/libblkid-tiny/libblkid-tiny.c b/libblkid-tiny/libblkid-tiny.c > index 352d1f6..f020e23 100644 > --- a/libblkid-tiny/libblkid-tiny.c > +++ b/libblkid-tiny/libblkid-tiny.c > @@ -162,6 +162,7 @@ static const struct blkid_idinfo *idinfos[] = > &ext3_idinfo, > &ext2_idinfo, > &jbd_idinfo, > + &ntfs_idinfo, > &squashfs_idinfo, > &ubi_idinfo, > &ubifs_idinfo, > diff --git a/libblkid-tiny/ntfs.c b/libblkid-tiny/ntfs.c > new file mode 100644 > index 0000000..93c1d88 > --- /dev/null > +++ b/libblkid-tiny/ntfs.c > @@ -0,0 +1,228 @@ > +/* > + * 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_bios_parameters { > + uint16_t sector_size; /* Size of a sector in bytes. */ > + uint8_t sectors_per_cluster; /* Size of a cluster in sectors. */ > + uint16_t reserved_sectors; /* zero */ > + uint8_t fats; /* zero */ > + uint16_t root_entries; /* zero */ > + uint16_t sectors; /* zero */ > + uint8_t media_type; /* 0xf8 = hard disk */ > + uint16_t sectors_per_fat; /* zero */ > + uint16_t sectors_per_track; /* irrelevant */ > + uint16_t heads; /* irrelevant */ > + uint32_t hidden_sectors; /* zero */ > + uint32_t large_sectors; /* zero */ > +} __attribute__ ((__packed__)); > + > +struct ntfs_super_block { > + uint8_t jump[3]; > + uint8_t oem_id[8]; /* magic string */ > + > + struct ntfs_bios_parameters bpb; > + > + uint16_t unused[2]; > + uint64_t number_of_sectors; > + uint64_t mft_cluster_location; > + uint64_t mft_mirror_cluster_location; > + int8_t clusters_per_mft_record; > + uint8_t reserved1[3]; > + int8_t cluster_per_index_record; > + uint8_t reserved2[3]; > + uint64_t volume_serial; > + uint32_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 NTFS_MAX_CLUSTER_SIZE (64 * 1024) > + > +enum { > + MFT_RECORD_ATTR_VOLUME_NAME = 0x60, > + MFT_RECORD_ATTR_END = 0xffffffff > +}; > + > +static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag) > +{ > + struct ntfs_super_block *ns; > +#if 0 > + struct master_file_table_record *mft; > +#endif > + > + uint32_t sectors_per_cluster, mft_record_size; > + uint16_t sector_size; > + uint64_t nr_clusters, off; //, attr_off; > + unsigned char *buf_mft; > + > + ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); > + if (!ns) > + return errno ? -errno : 1; > + > + /* > + * Check bios parameters block > + */ > + sector_size = le16_to_cpu(ns->bpb.sector_size); > + sectors_per_cluster = ns->bpb.sectors_per_cluster; > + > + if (sector_size < 256 || sector_size > 4096) > + return 1; > + > + switch (sectors_per_cluster) { > + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: > + break; > + default: > + return 1; > + } > + > + if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) * > + ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) > + return 1; > + > + /* Unused fields must be zero */ > + if (le16_to_cpu(ns->bpb.reserved_sectors) > + || le16_to_cpu(ns->bpb.root_entries) > + || le16_to_cpu(ns->bpb.sectors) > + || le16_to_cpu(ns->bpb.sectors_per_fat) > + || le32_to_cpu(ns->bpb.large_sectors) > + || ns->bpb.fats) > + return 1; > + > + if ((uint8_t) ns->clusters_per_mft_record < 0xe1 > + || (uint8_t) ns->clusters_per_mft_record > 0xf7) { > + > + switch (ns->clusters_per_mft_record) { > + case 1: case 2: case 4: case 8: case 16: case 32: case 64: > + break; > + default: > + return 1; > + } > + } > + > + if (ns->clusters_per_mft_record > 0) > + mft_record_size = ns->clusters_per_mft_record * > + sectors_per_cluster * sector_size; > + else > + mft_record_size = 1 << (0 - ns->clusters_per_mft_record); > + > + 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_cluster_location) * sector_size * > + sectors_per_cluster; > + > + DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", " > + "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" " > + "cluster_offset=%"PRIu64"", > + sector_size, mft_record_size, > + sectors_per_cluster, nr_clusters, > + off)); > + > + buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); > + if (!buf_mft) > + return errno ? -errno : 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 errno ? -errno : 1; > + > + if (memcmp(buf_mft, "FILE", 4)) > + return 1; > + > +#if 0 > + mft = (struct master_file_table_record *) buf_mft; > + attr_off = le16_to_cpu(mft->attrs_offset); > + > + while (attr_off + sizeof(struct file_attribute) <= mft_record_size && > + attr_off <= le32_to_cpu(mft->bytes_allocated)) { > + > + uint32_t attr_len; > + struct file_attribute *attr; > + > + attr = (struct file_attribute *) (buf_mft + attr_off); > + attr_len = le32_to_cpu(attr->len); > + if (!attr_len) > + break; > + > + if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_END) > + break; > + if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_VOLUME_NAME) { > + unsigned int val_off = le16_to_cpu(attr->value_offset); > + unsigned int val_len = le32_to_cpu(attr->value_len); > + unsigned char *val = ((uint8_t *) attr) + val_off; > + > + if (attr_off + val_off + val_len <= mft_record_size) > + blkid_probe_set_utf8label(pr, val, val_len, > + BLKID_ENC_UTF16LE); > + break; > + } > + > + attr_off += attr_len; > + } > +#endif > + > + 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 } > + } > +}; > +
diff --git a/CMakeLists.txt b/CMakeLists.txt index 8752612..9e855bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ ADD_LIBRARY(blkid-tiny SHARED libblkid-tiny/ext.c libblkid-tiny/jffs2.c libblkid-tiny/vfat.c + libblkid-tiny/ntfs.c libblkid-tiny/hfs.c libblkid-tiny/swap.c libblkid-tiny/ubi.c diff --git a/libblkid-tiny/libblkid-tiny.c b/libblkid-tiny/libblkid-tiny.c index 352d1f6..f020e23 100644 --- a/libblkid-tiny/libblkid-tiny.c +++ b/libblkid-tiny/libblkid-tiny.c @@ -162,6 +162,7 @@ static const struct blkid_idinfo *idinfos[] = &ext3_idinfo, &ext2_idinfo, &jbd_idinfo, + &ntfs_idinfo, &squashfs_idinfo, &ubi_idinfo, &ubifs_idinfo, diff --git a/libblkid-tiny/ntfs.c b/libblkid-tiny/ntfs.c new file mode 100644 index 0000000..93c1d88 --- /dev/null +++ b/libblkid-tiny/ntfs.c @@ -0,0 +1,228 @@ +/* + * 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_bios_parameters { + uint16_t sector_size; /* Size of a sector in bytes. */ + uint8_t sectors_per_cluster; /* Size of a cluster in sectors. */ + uint16_t reserved_sectors; /* zero */ + uint8_t fats; /* zero */ + uint16_t root_entries; /* zero */ + uint16_t sectors; /* zero */ + uint8_t media_type; /* 0xf8 = hard disk */ + uint16_t sectors_per_fat; /* zero */ + uint16_t sectors_per_track; /* irrelevant */ + uint16_t heads; /* irrelevant */ + uint32_t hidden_sectors; /* zero */ + uint32_t large_sectors; /* zero */ +} __attribute__ ((__packed__)); + +struct ntfs_super_block { + uint8_t jump[3]; + uint8_t oem_id[8]; /* magic string */ + + struct ntfs_bios_parameters bpb; + + uint16_t unused[2]; + uint64_t number_of_sectors; + uint64_t mft_cluster_location; + uint64_t mft_mirror_cluster_location; + int8_t clusters_per_mft_record; + uint8_t reserved1[3]; + int8_t cluster_per_index_record; + uint8_t reserved2[3]; + uint64_t volume_serial; + uint32_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 NTFS_MAX_CLUSTER_SIZE (64 * 1024) + +enum { + MFT_RECORD_ATTR_VOLUME_NAME = 0x60, + MFT_RECORD_ATTR_END = 0xffffffff +}; + +static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag) +{ + struct ntfs_super_block *ns; +#if 0 + struct master_file_table_record *mft; +#endif + + uint32_t sectors_per_cluster, mft_record_size; + uint16_t sector_size; + uint64_t nr_clusters, off; //, attr_off; + unsigned char *buf_mft; + + ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block); + if (!ns) + return errno ? -errno : 1; + + /* + * Check bios parameters block + */ + sector_size = le16_to_cpu(ns->bpb.sector_size); + sectors_per_cluster = ns->bpb.sectors_per_cluster; + + if (sector_size < 256 || sector_size > 4096) + return 1; + + switch (sectors_per_cluster) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128: + break; + default: + return 1; + } + + if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) * + ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE) + return 1; + + /* Unused fields must be zero */ + if (le16_to_cpu(ns->bpb.reserved_sectors) + || le16_to_cpu(ns->bpb.root_entries) + || le16_to_cpu(ns->bpb.sectors) + || le16_to_cpu(ns->bpb.sectors_per_fat) + || le32_to_cpu(ns->bpb.large_sectors) + || ns->bpb.fats) + return 1; + + if ((uint8_t) ns->clusters_per_mft_record < 0xe1 + || (uint8_t) ns->clusters_per_mft_record > 0xf7) { + + switch (ns->clusters_per_mft_record) { + case 1: case 2: case 4: case 8: case 16: case 32: case 64: + break; + default: + return 1; + } + } + + if (ns->clusters_per_mft_record > 0) + mft_record_size = ns->clusters_per_mft_record * + sectors_per_cluster * sector_size; + else + mft_record_size = 1 << (0 - ns->clusters_per_mft_record); + + 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_cluster_location) * sector_size * + sectors_per_cluster; + + DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", " + "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" " + "cluster_offset=%"PRIu64"", + sector_size, mft_record_size, + sectors_per_cluster, nr_clusters, + off)); + + buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size); + if (!buf_mft) + return errno ? -errno : 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 errno ? -errno : 1; + + if (memcmp(buf_mft, "FILE", 4)) + return 1; + +#if 0 + mft = (struct master_file_table_record *) buf_mft; + attr_off = le16_to_cpu(mft->attrs_offset); + + while (attr_off + sizeof(struct file_attribute) <= mft_record_size && + attr_off <= le32_to_cpu(mft->bytes_allocated)) { + + uint32_t attr_len; + struct file_attribute *attr; + + attr = (struct file_attribute *) (buf_mft + attr_off); + attr_len = le32_to_cpu(attr->len); + if (!attr_len) + break; + + if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_END) + break; + if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_VOLUME_NAME) { + unsigned int val_off = le16_to_cpu(attr->value_offset); + unsigned int val_len = le32_to_cpu(attr->value_len); + unsigned char *val = ((uint8_t *) attr) + val_off; + + if (attr_off + val_off + val_len <= mft_record_size) + blkid_probe_set_utf8label(pr, val, val_len, + BLKID_ENC_UTF16LE); + break; + } + + attr_off += attr_len; + } +#endif + + 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 } + } +}; +