/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 | | }; |