Coverage Report

Created: 2026-06-30 06:41

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