1/* 2 * volume_id - reads filesystem label and uuid 3 * 4 * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation version 2 of the License. 9 */ 10 11#ifndef _GNU_SOURCE 12#define _GNU_SOURCE 1 13#endif 14 15#ifdef HAVE_CONFIG_H 16# include <config.h> 17#endif 18 19#include <stdio.h> 20#include <stdlib.h> 21#include <unistd.h> 22#include <string.h> 23#include <errno.h> 24#include <ctype.h> 25 26#include "libvolume_id.h" 27#include "util.h" 28 29static struct ntfs_super_block { 30 uint8_t jump[3]; 31 uint8_t oem_id[8]; 32 uint16_t bytes_per_sector; 33 uint8_t sectors_per_cluster; 34 uint16_t reserved_sectors; 35 uint8_t fats; 36 uint16_t root_entries; 37 uint16_t sectors; 38 uint8_t media_type; 39 uint16_t sectors_per_fat; 40 uint16_t sectors_per_track; 41 uint16_t heads; 42 uint32_t hidden_sectors; 43 uint32_t large_sectors; 44 uint16_t unused[2]; 45 uint64_t number_of_sectors; 46 uint64_t mft_cluster_location; 47 uint64_t mft_mirror_cluster_location; 48 int8_t cluster_per_mft_record; 49 uint8_t reserved1[3]; 50 int8_t cluster_per_index_record; 51 uint8_t reserved2[3]; 52 uint8_t volume_serial[8]; 53 uint16_t checksum; 54} PACKED *ns; 55 56static struct master_file_table_record { 57 uint8_t magic[4]; 58 uint16_t usa_ofs; 59 uint16_t usa_count; 60 uint64_t lsn; 61 uint16_t sequence_number; 62 uint16_t link_count; 63 uint16_t attrs_offset; 64 uint16_t flags; 65 uint32_t bytes_in_use; 66 uint32_t bytes_allocated; 67} PACKED *mftr; 68 69static struct file_attribute { 70 uint32_t type; 71 uint32_t len; 72 uint8_t non_resident; 73 uint8_t name_len; 74 uint16_t name_offset; 75 uint16_t flags; 76 uint16_t instance; 77 uint32_t value_len; 78 uint16_t value_offset; 79} PACKED *attr; 80 81static struct volume_info { 82 uint64_t reserved; 83 uint8_t major_ver; 84 uint8_t minor_ver; 85} PACKED *info; 86 87#define MFT_RECORD_VOLUME 3 88#define MFT_RECORD_ATTR_VOLUME_NAME 0x60 89#define MFT_RECORD_ATTR_VOLUME_INFO 0x70 90#define MFT_RECORD_ATTR_OBJECT_ID 0x40 91#define MFT_RECORD_ATTR_END 0xffffffffu 92 93int volume_id_probe_ntfs(struct volume_id *id, uint64_t off, uint64_t size) 94{ 95 unsigned int sector_size; 96 unsigned int cluster_size; 97 uint64_t mft_cluster; 98 uint64_t mft_off; 99 unsigned int mft_record_size; 100 unsigned int attr_type; 101 unsigned int attr_off; 102 unsigned int attr_len; 103 unsigned int val_off; 104 unsigned int val_len; 105 const uint8_t *buf; 106 const uint8_t *val; 107 108 info("probing at offset 0x%llx", (unsigned long long) off); 109 110 ns = (struct ntfs_super_block *) volume_id_get_buffer(id, off, 0x200); 111 if (ns == NULL) 112 return -1; 113 114 if (memcmp(ns->oem_id, "NTFS", 4) != 0) 115 return -1; 116 117 volume_id_set_uuid(id, ns->volume_serial, 0, UUID_64BIT_LE); 118 119 sector_size = le16_to_cpu(ns->bytes_per_sector); 120 cluster_size = ns->sectors_per_cluster * sector_size; 121 mft_cluster = le64_to_cpu(ns->mft_cluster_location); 122 mft_off = mft_cluster * cluster_size; 123 124 if (ns->cluster_per_mft_record < 0) 125 /* size = -log2(mft_record_size); normally 1024 Bytes */ 126 mft_record_size = 1 << -ns->cluster_per_mft_record; 127 else 128 mft_record_size = ns->cluster_per_mft_record * cluster_size; 129 130 dbg("sectorsize 0x%x", sector_size); 131 dbg("clustersize 0x%x", cluster_size); 132 dbg("mftcluster %llu", (unsigned long long) mft_cluster); 133 dbg("mftoffset 0x%llx", (unsigned long long) mft_off); 134 dbg("cluster per mft_record %i", ns->cluster_per_mft_record); 135 dbg("mft record size %i", mft_record_size); 136 137 buf = volume_id_get_buffer(id, off + mft_off + (MFT_RECORD_VOLUME * mft_record_size), 138 mft_record_size); 139 if (buf == NULL) 140 goto found; 141 142 mftr = (struct master_file_table_record*) buf; 143 144 dbg("mftr->magic '%c%c%c%c'", mftr->magic[0], mftr->magic[1], mftr->magic[2], mftr->magic[3]); 145 if (memcmp(mftr->magic, "FILE", 4) != 0) 146 goto found; 147 148 attr_off = le16_to_cpu(mftr->attrs_offset); 149 dbg("file $Volume's attributes are at offset %i", attr_off); 150 151 while (1) { 152 attr = (struct file_attribute*) &buf[attr_off]; 153 attr_type = le32_to_cpu(attr->type); 154 attr_len = le16_to_cpu(attr->len); 155 val_off = le16_to_cpu(attr->value_offset); 156 val_len = le32_to_cpu(attr->value_len); 157 attr_off += attr_len; 158 159 if (attr_len == 0) 160 break; 161 162 if (attr_off >= mft_record_size) 163 break; 164 165 if (attr_type == MFT_RECORD_ATTR_END) 166 break; 167 168 dbg("found attribute type 0x%x, len %i, at offset %i", 169 attr_type, attr_len, attr_off); 170 171 if (attr_type == MFT_RECORD_ATTR_VOLUME_INFO) { 172 dbg("found info, len %i", val_len); 173 info = (struct volume_info*) (((uint8_t *) attr) + val_off); 174 snprintf(id->type_version, sizeof(id->type_version)-1, 175 "%u.%u", info->major_ver, info->minor_ver); 176 } 177 178 if (attr_type == MFT_RECORD_ATTR_VOLUME_NAME) { 179 dbg("found label, len %i", val_len); 180 if (val_len > VOLUME_ID_LABEL_SIZE) 181 val_len = VOLUME_ID_LABEL_SIZE; 182 183 val = ((uint8_t *) attr) + val_off; 184 volume_id_set_label_raw(id, val, val_len); 185 volume_id_set_label_unicode16(id, val, LE, val_len); 186 } 187 } 188 189found: 190 volume_id_set_usage(id, VOLUME_ID_FILESYSTEM); 191 id->type = "ntfs"; 192 193 return 0; 194} 195