libblkid-tiny: add support for NTFS superblock
authorRafał Miłecki <rafal@milecki.pl>
Mon, 25 Dec 2017 19:52:11 +0000 (20:52 +0100)
committerRafał Miłecki <rafal@milecki.pl>
Fri, 29 Dec 2017 22:06:03 +0000 (23:06 +0100)
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
libblkid-tiny/libblkid-tiny.c
libblkid-tiny/ntfs.c [new file with mode: 0644]

index 8752612a934e01af958a19591864883806dae51a..9e855bda27699f26e0831565a3813daa55636132 100644 (file)
@@ -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
index 352d1f62ab6e8f07cb0762482b04cf30549dedb4..f020e231ed83eb1b3611d57f2d0ff9a62b4453bc 100644 (file)
@@ -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 (file)
index 0000000..93c1d88
--- /dev/null
@@ -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 }
+       }
+};
+