/src/util-linux/libblkid/src/superblocks/f2fs.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2013 Alejandro Martinez Ruiz <alex@nowcomputing.com> |
3 | | * |
4 | | * This file may be redistributed under the terms of the |
5 | | * GNU Lesser General Public License |
6 | | */ |
7 | | |
8 | | #include <stddef.h> |
9 | | #include <string.h> |
10 | | |
11 | | #include "superblocks.h" |
12 | | #include "crc32.h" |
13 | | |
14 | | #define F2FS_MAGIC "\x10\x20\xF5\xF2" |
15 | | #define F2FS_MAGIC_OFF 0 |
16 | | #define F2FS_UUID_SIZE 16 |
17 | | #define F2FS_LABEL_SIZE 512 |
18 | | #define F2FS_SB1_OFF 0x400 |
19 | | #define F2FS_SB1_KBOFF (F2FS_SB1_OFF >> 10) |
20 | | #define F2FS_SB2_OFF 0x1400 |
21 | | #define F2FS_SB2_KBOFF (F2FS_SB2_OFF >> 10) |
22 | | |
23 | | struct f2fs_super_block { /* According to version 1.1 */ |
24 | | /* 0x00 */ uint32_t magic; /* Magic Number */ |
25 | | /* 0x04 */ uint16_t major_ver; /* Major Version */ |
26 | | /* 0x06 */ uint16_t minor_ver; /* Minor Version */ |
27 | | /* 0x08 */ uint32_t log_sectorsize; /* log2 sector size in bytes */ |
28 | | /* 0x0C */ uint32_t log_sectors_per_block; /* log2 # of sectors per block */ |
29 | | /* 0x10 */ uint32_t log_blocksize; /* log2 block size in bytes */ |
30 | | /* 0x14 */ uint32_t log_blocks_per_seg; /* log2 # of blocks per segment */ |
31 | | /* 0x18 */ uint32_t segs_per_sec; /* # of segments per section */ |
32 | | /* 0x1C */ uint32_t secs_per_zone; /* # of sections per zone */ |
33 | | /* 0x20 */ uint32_t checksum_offset; /* checksum offset inside super block */ |
34 | | /* 0x24 */ uint64_t block_count; /* total # of user blocks */ |
35 | | /* 0x2C */ uint32_t section_count; /* total # of sections */ |
36 | | /* 0x30 */ uint32_t segment_count; /* total # of segments */ |
37 | | /* 0x34 */ uint32_t segment_count_ckpt; /* # of segments for checkpoint */ |
38 | | /* 0x38 */ uint32_t segment_count_sit; /* # of segments for SIT */ |
39 | | /* 0x3C */ uint32_t segment_count_nat; /* # of segments for NAT */ |
40 | | /* 0x40 */ uint32_t segment_count_ssa; /* # of segments for SSA */ |
41 | | /* 0x44 */ uint32_t segment_count_main; /* # of segments for main area */ |
42 | | /* 0x48 */ uint32_t segment0_blkaddr; /* start block address of segment 0 */ |
43 | | /* 0x4C */ uint32_t cp_blkaddr; /* start block address of checkpoint */ |
44 | | /* 0x50 */ uint32_t sit_blkaddr; /* start block address of SIT */ |
45 | | /* 0x54 */ uint32_t nat_blkaddr; /* start block address of NAT */ |
46 | | /* 0x58 */ uint32_t ssa_blkaddr; /* start block address of SSA */ |
47 | | /* 0x5C */ uint32_t main_blkaddr; /* start block address of main area */ |
48 | | /* 0x60 */ uint32_t root_ino; /* root inode number */ |
49 | | /* 0x64 */ uint32_t node_ino; /* node inode number */ |
50 | | /* 0x68 */ uint32_t meta_ino; /* meta inode number */ |
51 | | /* 0x6C */ uint8_t uuid[F2FS_UUID_SIZE]; /* 128-bit uuid for volume */ |
52 | | /* 0x7C */ uint16_t volume_name[F2FS_LABEL_SIZE]; /* volume name */ |
53 | | #if 0 |
54 | | /* 0x47C */ uint32_t extension_count; /* # of extensions below */ |
55 | | /* 0x480 */ uint8_t extension_list[64][8]; /* extension array */ |
56 | | #endif |
57 | | } __attribute__((packed)); |
58 | | |
59 | | static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off, |
60 | | const struct f2fs_super_block *sb) |
61 | 274 | { |
62 | 274 | uint32_t csum_off = le32_to_cpu(sb->checksum_offset); |
63 | 274 | if (!csum_off) |
64 | 66 | return 1; |
65 | 208 | if (csum_off % sizeof(uint32_t) != 0) |
66 | 70 | return 0; |
67 | 138 | if (csum_off + sizeof(uint32_t) > 4096) |
68 | 112 | return 0; |
69 | | |
70 | 26 | const unsigned char *csum_data = blkid_probe_get_buffer(pr, |
71 | 26 | sb_off + csum_off, sizeof(uint32_t)); |
72 | 26 | if (!csum_data) |
73 | 0 | return 0; |
74 | | |
75 | 26 | uint32_t expected = le32_to_cpu(*(uint32_t *) csum_data); |
76 | | |
77 | 26 | const unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off); |
78 | 26 | if (!csummed) |
79 | 0 | return 0; |
80 | | |
81 | 26 | uint32_t csum = ul_crc32(0xF2F52010, csummed, csum_off); |
82 | | |
83 | 26 | return blkid_probe_verify_csum(pr, csum, expected); |
84 | 26 | } |
85 | | |
86 | | static int probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag) |
87 | 275 | { |
88 | 275 | const struct f2fs_super_block *sb; |
89 | 275 | uint16_t vermaj, vermin; |
90 | | |
91 | 275 | sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block); |
92 | 275 | if (!sb) |
93 | 0 | return errno ? -errno : 1; |
94 | | |
95 | 275 | vermaj = le16_to_cpu(sb->major_ver); |
96 | 275 | vermin = le16_to_cpu(sb->minor_ver); |
97 | | |
98 | | /* For version 1.0 we cannot know the correct sb structure */ |
99 | 275 | if (vermaj == 1 && vermin == 0) |
100 | 1 | return 0; |
101 | | |
102 | 274 | if (!f2fs_validate_checksum(pr, mag->kboff << 10, sb)) |
103 | 207 | return 1; |
104 | | |
105 | 67 | if (*((unsigned char *) sb->volume_name)) |
106 | 33 | blkid_probe_set_utf8label(pr, (unsigned char *) sb->volume_name, |
107 | 33 | sizeof(sb->volume_name), |
108 | 33 | UL_ENCODE_UTF16LE); |
109 | | |
110 | 67 | blkid_probe_set_uuid(pr, sb->uuid); |
111 | 67 | blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin); |
112 | 67 | if (le32_to_cpu(sb->log_blocksize) < 32){ |
113 | 11 | uint32_t blocksize = 1U << le32_to_cpu(sb->log_blocksize); |
114 | 11 | blkid_probe_set_fsblocksize(pr, blocksize); |
115 | 11 | blkid_probe_set_block_size(pr, blocksize); |
116 | | blkid_probe_set_fssize(pr, le64_to_cpu(sb->block_count) * blocksize); |
117 | 11 | } |
118 | 67 | return 0; |
119 | 274 | } |
120 | | |
121 | | const struct blkid_idinfo f2fs_idinfo = |
122 | | { |
123 | | .name = "f2fs", |
124 | | .usage = BLKID_USAGE_FILESYSTEM, |
125 | | .probefunc = probe_f2fs, |
126 | | .magics = |
127 | | { |
128 | | { |
129 | | .magic = F2FS_MAGIC, |
130 | | .len = 4, |
131 | | .kboff = F2FS_SB1_KBOFF, |
132 | | .sboff = F2FS_MAGIC_OFF |
133 | | }, |
134 | | { NULL } |
135 | | } |
136 | | }; |