Coverage Report

Created: 2025-12-14 06:36

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/util-linux/libblkid/src/superblocks/apfs.c
Line
Count
Source
1
/*
2
 * Copyright (C) 2018 Harry Mallon <hjmallon@gmail.com>
3
 *
4
 * This file may be redistributed under the terms of the
5
 * GNU Lesser General Public License.
6
 */
7
8
#include "superblocks.h"
9
10
12
#define APFS_CONTAINER_SUPERBLOCK_TYPE 1
11
3
#define APFS_CONTAINER_SUPERBLOCK_SUBTYPE 0
12
0
#define APFS_STANDARD_BLOCK_SIZE 4096
13
14
/*
15
 * This struct is much longer than this, but this seems
16
 * to contain the useful bits (for now).
17
 *
18
 * All values are little-endian.
19
 */
20
struct apfs_super_block {
21
  // Generic part to all APFS objects
22
  uint64_t checksum;
23
  uint64_t oid;
24
  uint64_t xid;
25
  uint16_t type;
26
  uint16_t flags;
27
  uint16_t subtype;
28
  uint16_t pad;
29
30
  // Specific to container header
31
  uint32_t magic; // 'NXSB'
32
  uint32_t block_size;
33
  uint64_t block_count;
34
  uint64_t features;
35
  uint64_t read_only_features;
36
  uint64_t incompatible_features;
37
  uint8_t uuid[16];
38
39
  uint8_t padding[4008]; // Pad to 4096 bytes for checksum
40
} __attribute__((packed));
41
42
static uint64_t apfs_fletcher64(const uint8_t *buf, size_t size)
43
436
{
44
436
  uint64_t lo32 = 0, hi32 = 0, csum_hi;
45
436
  uint32_t csum_low;
46
436
  size_t i;
47
48
446k
  for (i = 0; i < size / 4; i++) {
49
445k
    lo32 += le32_to_cpu(((uint32_t *)buf)[i]);
50
445k
    hi32 += lo32;
51
445k
  }
52
53
436
  csum_low = ~((lo32 + hi32) % UINT32_MAX);
54
436
  csum_hi = ~((lo32 + csum_low) % UINT32_MAX);
55
56
436
  return csum_hi << 32 | csum_low;
57
436
}
58
59
static int apfs_verify_checksum(blkid_probe pr,
60
        const struct apfs_super_block *sb)
61
436
{
62
436
  const size_t csummed_start_offset = offsetof(__typeof__(*sb), oid);
63
436
  uint64_t csum;
64
65
436
  csum = apfs_fletcher64(((const uint8_t *)sb) + csummed_start_offset,
66
436
             sizeof(*sb) - csummed_start_offset);
67
68
436
  return blkid_probe_verify_csum(pr, csum, le64_to_cpu(sb->checksum));
69
436
}
70
71
static int probe_apfs(blkid_probe pr, const struct blkid_idmag *mag)
72
436
{
73
436
  const struct apfs_super_block *sb;
74
75
436
  sb = blkid_probe_get_sb(pr, mag, struct apfs_super_block);
76
436
  if (!sb)
77
0
    return errno ? -errno : BLKID_PROBE_NONE;
78
79
436
  if (!apfs_verify_checksum(pr, sb))
80
424
    return BLKID_PROBE_NONE;
81
82
12
  if (le16_to_cpu(sb->type) != APFS_CONTAINER_SUPERBLOCK_TYPE)
83
9
    return BLKID_PROBE_NONE;
84
85
3
  if (le16_to_cpu(sb->subtype) != APFS_CONTAINER_SUPERBLOCK_SUBTYPE)
86
0
    return BLKID_PROBE_NONE;
87
88
3
  if (le16_to_cpu(sb->pad) != 0)
89
3
    return BLKID_PROBE_NONE;
90
91
  /*
92
   * This check is pretty draconian, but should avoid false
93
   * positives. Can be improved as more APFS documentation
94
   * is published.
95
   */
96
0
  if (le32_to_cpu(sb->block_size) != APFS_STANDARD_BLOCK_SIZE)
97
0
    return BLKID_PROBE_NONE;
98
99
0
  if (blkid_probe_set_uuid(pr, sb->uuid) < 0)
100
0
    return BLKID_PROBE_NONE;
101
102
0
  blkid_probe_set_fsblocksize(pr, le32_to_cpu(sb->block_size));
103
0
  blkid_probe_set_block_size(pr, le32_to_cpu(sb->block_size));
104
105
0
  return BLKID_PROBE_OK;
106
0
}
107
108
const struct blkid_idinfo apfs_idinfo =
109
{
110
  .name   = "apfs",
111
  .usage    = BLKID_USAGE_FILESYSTEM,
112
  .probefunc  = probe_apfs,
113
  .magics   =
114
  {
115
    { .magic = "NXSB", .len = 4, .sboff = 32 },
116
    { NULL }
117
  }
118
};