Coverage Report

Created: 2026-09-05 06:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/superblocks/ntfs.c
Line
Count
Source
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
88
#define MFT_RECORD_VOLUME 3
75
/* Windows 10 Creators edition has extended the cluster size limit to 2MB */
76
444
#define NTFS_MAX_CLUSTER_SIZE (2 * 1024 * 1024)
77
78
98
#define MFT_RECORD_ATTR_VOLUME_NAME 0x60
79
103
#define MFT_RECORD_ATTR_END   0xffffffff
80
81
static int __probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag, int save_info)
82
559
{
83
559
  const struct ntfs_super_block *ns;
84
559
  const struct master_file_table_record *mft;
85
86
559
  uint32_t sectors_per_cluster, mft_record_size;
87
559
  uint16_t sector_size;
88
559
  uint64_t nr_clusters, off, attr_off;
89
559
  const unsigned char *buf_mft;
90
91
559
  ns = blkid_probe_get_sb(pr, mag, struct ntfs_super_block);
92
559
  if (!ns)
93
0
    return errno ? -errno : 1;
94
95
  /*
96
   * Check bios parameters block
97
   */
98
559
  sector_size = le16_to_cpu(ns->bpb.sector_size);
99
100
559
  if (sector_size < 256 || sector_size > 4096 || !is_power_of_2(sector_size))
101
95
    return 1;
102
103
464
  switch (ns->bpb.sectors_per_cluster) {
104
432
  case 1: case 2: case 4: case 8: case 16: case 32: case 64: case 128:
105
432
    sectors_per_cluster = ns->bpb.sectors_per_cluster;
106
432
    break;
107
32
  default:
108
32
    if ((ns->bpb.sectors_per_cluster < 240)
109
24
        || (ns->bpb.sectors_per_cluster > 249))
110
20
      return 1;
111
12
    sectors_per_cluster = 1 << (256 - ns->bpb.sectors_per_cluster);
112
464
  }
113
114
444
  if ((uint16_t) le16_to_cpu(ns->bpb.sector_size) *
115
444
      sectors_per_cluster > NTFS_MAX_CLUSTER_SIZE)
116
3
    return 1;
117
118
  /* Unused fields must be zero */
119
441
  if (le16_to_cpu(ns->bpb.reserved_sectors)
120
374
      || le16_to_cpu(ns->bpb.root_entries)
121
367
      || le16_to_cpu(ns->bpb.sectors)
122
335
      || le16_to_cpu(ns->bpb.sectors_per_fat)
123
320
      || le32_to_cpu(ns->bpb.large_sectors)
124
307
      || ns->bpb.fats)
125
137
    return 1;
126
127
304
  if ((uint8_t) ns->clusters_per_mft_record < 0xe1
128
277
      || (uint8_t) ns->clusters_per_mft_record > 0xf7) {
129
130
277
    switch (ns->clusters_per_mft_record) {
131
261
    case 1: case 2: case 4: case 8: case 16: case 32: case 64:
132
261
      break;
133
16
    default:
134
16
      return 1;
135
277
    }
136
277
  }
137
138
288
  if (ns->clusters_per_mft_record > 0) {
139
261
    mft_record_size = ns->clusters_per_mft_record *
140
261
          sectors_per_cluster * sector_size;
141
261
  } else {
142
27
    int8_t mft_record_size_shift = 0 - ns->clusters_per_mft_record;
143
27
    if (mft_record_size_shift < 0 || mft_record_size_shift >= 31)
144
3
      return 1;
145
24
    mft_record_size = 1 << mft_record_size_shift;
146
24
  }
147
148
285
  nr_clusters = le64_to_cpu(ns->number_of_sectors) / sectors_per_cluster;
149
150
285
  if ((le64_to_cpu(ns->mft_cluster_location) > nr_clusters) ||
151
222
      (le64_to_cpu(ns->mft_mirror_cluster_location) > nr_clusters))
152
94
    return 1;
153
154
155
191
  off = le64_to_cpu(ns->mft_cluster_location) * sector_size *
156
191
    sectors_per_cluster;
157
158
191
  DBG(LOWPROBE, ul_debug("NTFS: sector_size=%"PRIu16", mft_record_size=%"PRIu32", "
159
191
      "sectors_per_cluster=%"PRIu32", nr_clusters=%"PRIu64" "
160
191
      "cluster_offset=%"PRIu64"",
161
191
      sector_size, mft_record_size,
162
191
      sectors_per_cluster, nr_clusters,
163
191
      off));
164
165
191
  if (mft_record_size < 4)
166
0
    return 1;
167
168
191
  buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
169
191
  if (!buf_mft)
170
82
    return errno ? -errno : 1;
171
172
109
  if (memcmp(buf_mft, "FILE", 4) != 0)
173
21
    return 1;
174
175
88
  off += MFT_RECORD_VOLUME * mft_record_size;
176
177
88
  buf_mft = blkid_probe_get_buffer(pr, off, mft_record_size);
178
88
  if (!buf_mft)
179
3
    return errno ? -errno : 1;
180
181
85
  if (memcmp(buf_mft, "FILE", 4) != 0)
182
8
    return 1;
183
184
  /* return if caller does not care about UUID and LABEL */
185
77
  if (!save_info)
186
15
    return 0;
187
188
62
  mft = (struct master_file_table_record *) buf_mft;
189
62
  attr_off = le16_to_cpu(mft->attrs_offset);
190
191
154
  while (attr_off + sizeof(struct file_attribute) <= mft_record_size &&
192
105
         attr_off <= le32_to_cpu(mft->bytes_allocated)) {
193
194
104
    uint32_t attr_len;
195
104
    struct file_attribute *attr;
196
197
104
    attr = (struct file_attribute *) (buf_mft + attr_off);
198
104
    attr_len = le32_to_cpu(attr->len);
199
104
    if (!attr_len)
200
1
      break;
201
202
103
    if (le32_to_cpu(attr->type) == (uint32_t) MFT_RECORD_ATTR_END)
203
5
      break;
204
98
    if (le32_to_cpu(attr->type) == (uint32_t) MFT_RECORD_ATTR_VOLUME_NAME) {
205
6
      unsigned int val_off = le16_to_cpu(attr->value_offset);
206
6
      unsigned int val_len = le32_to_cpu(attr->value_len);
207
6
      unsigned char *val = ((uint8_t *) attr) + val_off;
208
209
6
      if (val_off <= mft_record_size - attr_off &&
210
3
          val_len <= mft_record_size - attr_off - val_off)
211
2
        blkid_probe_set_utf8label(pr, val, val_len,
212
2
                UL_ENCODE_UTF16LE);
213
6
      break;
214
6
    }
215
216
92
    attr_off += attr_len;
217
92
  }
218
219
220
62
  blkid_probe_set_fsblocksize(pr, sector_size * sectors_per_cluster);
221
62
  blkid_probe_set_block_size(pr, sector_size);
222
62
  blkid_probe_set_fssize(pr, le64_to_cpu(ns->number_of_sectors) * sector_size);
223
224
62
  blkid_probe_sprintf_uuid(pr,
225
62
      (unsigned char *) &ns->volume_serial,
226
62
      sizeof(ns->volume_serial),
227
62
      "%016" PRIX64, le64_to_cpu(ns->volume_serial));
228
62
  return 0;
229
77
}
230
231
static int probe_ntfs(blkid_probe pr, const struct blkid_idmag *mag)
232
512
{
233
512
  return __probe_ntfs(pr, mag, 1);
234
512
}
235
236
int blkid_probe_is_ntfs(blkid_probe pr)
237
123
{
238
123
  const struct blkid_idmag *mag = NULL;
239
123
  int rc;
240
241
123
  rc = blkid_probe_get_idmag(pr, &ntfs_idinfo, NULL, &mag);
242
123
  if (rc < 0)
243
0
    return rc; /* error */
244
123
  if (rc != BLKID_PROBE_OK || !mag)
245
76
    return 0;
246
247
47
  return __probe_ntfs(pr, mag, 0) == 0 ? 1 : 0;
248
123
}
249
250
const struct blkid_idinfo ntfs_idinfo =
251
{
252
  .name   = "ntfs",
253
  .usage    = BLKID_USAGE_FILESYSTEM,
254
  .probefunc  = probe_ntfs,
255
  .magics   =
256
  {
257
    { .magic = "NTFS    ", .len = 8, .sboff = 3 },
258
    { NULL }
259
  }
260
};
261