libfstools: support file paths longer than 255 chars
[project/fstools.git] / libblkid-tiny / ntfs.c
1 /*
2  * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org>
3  * Copyright (C) 2008 Karel Zak <kzak@redhat.com>
4  *
5  * This file may be redistributed under the terms of the
6  * GNU Lesser General Public License.
7  */
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <unistd.h>
11 #include <string.h>
12 #include <inttypes.h>
13
14 #include "superblocks.h"
15
16 struct ntfs_bios_parameters {
17         uint16_t        sector_size;    /* Size of a sector in bytes. */
18         uint8_t         sectors_per_cluster;    /* Size of a cluster in sectors. */
19         uint16_t        reserved_sectors;       /* zero */
20         uint8_t         fats;                   /* zero */
21         uint16_t        root_entries;           /* zero */
22         uint16_t        sectors;                /* zero */
23         uint8_t         media_type;             /* 0xf8 = hard disk */
24         uint16_t        sectors_per_fat;        /* zero */
25         uint16_t        sectors_per_track;      /* irrelevant */
26         uint16_t        heads;                  /* irrelevant */
27         uint32_t        hidden_sectors;         /* zero */
28         uint32_t        large_sectors;          /* zero */
29 } __attribute__ ((__packed__));
30
31 struct ntfs_super_block {
32         uint8_t         jump[3];
33         uint8_t         oem_id[8];      /* magic string */
34
35         struct ntfs_bios_parameters     bpb;
36
37         uint16_t        unused[2];
38         uint64_t        number_of_sectors;
39         uint64_t        mft_cluster_location;
40         uint64_t        mft_mirror_cluster_location;
41         int8_t          clusters_per_mft_record;
42         uint8_t         reserved1[3];
43         int8_t          cluster_per_index_record;
44         uint8_t         reserved2[3];
45         uint64_t        volume_serial;
46         uint32_t        checksum;
47 } __attribute__((packed));
48
49 struct master_file_table_record {
50         uint32_t        magic;
51         uint16_t        usa_ofs;
52         uint16_t        usa_count;
53         uint64_t        lsn;
54         uint16_t        sequence_number;
55         uint16_t        link_count;
56         uint16_t        attrs_offset;
57         uint16_t        flags;
58         uint32_t        bytes_in_use;
59         uint32_t        bytes_allocated;
60 } __attribute__((__packed__));
61
62 struct file_attribute {
63         uint32_t        type;
64         uint32_t        len;
65         uint8_t         non_resident;
66         uint8_t         name_len;
67         uint16_t        name_offset;
68         uint16_t        flags;
69         uint16_t        instance;
70         uint32_t        value_len;
71         uint16_t        value_offset;
72 } __attribute__((__packed__));
73
74 #define MFT_RECORD_VOLUME       3
75 #define NTFS_MAX_CLUSTER_SIZE   (64 * 1024)
76
77 enum {
78         MFT_RECORD_ATTR_VOLUME_NAME             = 0x60,
79         MFT_RECORD_ATTR_END                     = 0xffffffff
80 };
81
82 static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
83 {
84         struct ntfs_super_block *ns;
85 #if 0
86         struct master_file_table_record *mft;
87 #endif
88
89         uint32_t sectors_per_cluster, mft_record_size;
90         uint16_t sector_size;
91         uint64_t nr_clusters, off; //, attr_off;
92         unsigned char *buf_mft;
93
94         ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
95         if (!ns)
96                 return errno ? -errno : 1;
97
98         /*
99          * Check bios parameters block
100          */
101         sector_size = le16_to_cpu(ns->bpb.sector_size);
102         sectors_per_cluster = ns->bpb.sectors_per_cluster;
103
104         if (sector_size < 256 || sector_size > 4096)
105                 return 1;
106
107         switch (sectors_per_cluster) {
108         case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
109                 break;
110         default:
111                 return 1;
112         }
113
114         if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
115                         ns->bpb.sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
116                 return 1;
117
118         /* Unused fields must be zero */
119         if (le16_to_cpu(ns->bpb.reserved_sectors)
120             || le16_to_cpu(ns->bpb.root_entries)
121             || le16_to_cpu(ns->bpb.sectors)
122             || le16_to_cpu(ns->bpb.sectors_per_fat)
123             || le32_to_cpu(ns->bpb.large_sectors)
124             || ns->bpb.fats)
125                 return 1;
126
127         if ((uint8_t) ns->clusters_per_mft_record < 0xe1
128             || (uint8_t) ns->clusters_per_mft_record > 0xf7) {
129
130                 switch (ns->clusters_per_mft_record) {
131                 case 1: case 2: case 4: case 8: case 16: case 32: case 64:
132                         break;
133                 default:
134                         return 1;
135                 }
136         }
137
138         if (ns->clusters_per_mft_record > 0)
139                 mft_record_size = ns->clusters_per_mft_record *
140                                   sectors_per_cluster * sector_size;
141         else
142                 mft_record_size = 1 << (0 - ns->clusters_per_mft_record);
143
144         nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster;
145
146         if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) ||
147             (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters))
148                 return 1;
149
150
151         off = le64_to_cpu(ns->mft_cluster_location) * sector_size *
152                 sectors_per_cluster;
153
154         DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", "
155                         "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" "
156                         "cluster_offset=%"PRIu64"",
157                         sector_size, mft_record_size,
158                         sectors_per_cluster, nr_clusters,
159                         off));
160
161         buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
162         if (!buf_mft)
163                 return errno ? -errno : 1;
164
165         if (memcmp(buf_mft, "FILE", 4))
166                 return 1;
167
168         off += MFT_RECORD_VOLUME * mft_record_size;
169
170         buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
171         if (!buf_mft)
172                 return errno ? -errno : 1;
173
174         if (memcmp(buf_mft, "FILE", 4))
175                 return 1;
176
177 #if 0
178         mft = (struct master_file_table_record *) buf_mft;
179         attr_off = le16_to_cpu(mft->attrs_offset);
180
181         while (attr_off + sizeof(struct file_attribute) <= mft_record_size &&
182                attr_off <= le32_to_cpu(mft->bytes_allocated)) {
183
184                 uint32_t attr_len;
185                 struct file_attribute *attr;
186
187                 attr = (struct file_attribute *) (buf_mft + attr_off);
188                 attr_len = le32_to_cpu(attr->len);
189                 if (!attr_len)
190                         break;
191
192                 if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_END)
193                         break;
194                 if (le32_to_cpu(attr->type) == MFT_RECORD_ATTR_VOLUME_NAME) {
195                         unsigned int val_off = le16_to_cpu(attr->value_offset);
196                         unsigned int val_len = le32_to_cpu(attr->value_len);
197                         unsigned char *val = ((uint8_t *) attr) + val_off;
198
199                         if (attr_off + val_off + val_len <= mft_record_size)
200                                 blkid_probe_set_utf8label(pr, val, val_len,
201                                                           BLKID_ENC_UTF16LE);
202                         break;
203                 }
204
205                 attr_off += attr_len;
206         }
207 #endif
208
209         blkid_probe_sprintf_uuid(pr,
210                         (unsigned char *) &ns->volume_serial,
211                         sizeof(ns->volume_serial),
212                         "%016" PRIX64, le64_to_cpu(ns->volume_serial));
213         return 0;
214 }
215
216
217 const struct blkid_idinfo ntfs_idinfo =
218 {
219         .name           = "ntfs",
220         .usage          = BLKID_USAGE_FILESYSTEM,
221         .probefunc      = probe_ntfs,
222         .magics         =
223         {
224                 { .magic = "NTFS    ", .len = 8, .sboff = 3 },
225                 { NULL }
226         }
227 };
228