diff mbox series

[LEDE-DEV,fstools] libblkid-tiny: add support for NTFS superblock

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

Commit Message

Rafał Miłecki Dec. 25, 2017, 7:52 p.m. UTC
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>
---
 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

Comments

John Crispin Dec. 29, 2017, 9:30 p.m. UTC | #1
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 mbox series

Patch

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 }
+	}
+};
+