summaryrefslogtreecommitdiffstats
path: root/libblkid/src/superblocks/f2fs.c
blob: 4e0fdbfe30549d63b63b30570198f91ea53f0ec4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (C) 2013 Alejandro Martinez Ruiz <alex@nowcomputing.com>
 *
 * This file may be redistributed under the terms of the
 * GNU Lesser General Public License
 */

#include <stddef.h>
#include <string.h>

#include "superblocks.h"
#include "crc32.h"

#define F2FS_MAGIC		"\x10\x20\xF5\xF2"
#define F2FS_MAGIC_OFF		0
#define F2FS_UUID_SIZE		16
#define F2FS_LABEL_SIZE		512
#define F2FS_SB1_OFF		0x400
#define F2FS_SB1_KBOFF		(F2FS_SB1_OFF >> 10)
#define F2FS_SB2_OFF		0x1400
#define F2FS_SB2_KBOFF		(F2FS_SB2_OFF >> 10)

struct f2fs_super_block {					/* According to version 1.1 */
/* 0x00 */	uint32_t	magic;				/* Magic Number */
/* 0x04 */	uint16_t	major_ver;			/* Major Version */
/* 0x06 */	uint16_t	minor_ver;			/* Minor Version */
/* 0x08 */	uint32_t	log_sectorsize;			/* log2 sector size in bytes */
/* 0x0C */	uint32_t	log_sectors_per_block;		/* log2 # of sectors per block */
/* 0x10 */	uint32_t	log_blocksize;			/* log2 block size in bytes */
/* 0x14 */	uint32_t	log_blocks_per_seg;		/* log2 # of blocks per segment */
/* 0x18 */	uint32_t	segs_per_sec;			/* # of segments per section */
/* 0x1C */	uint32_t	secs_per_zone;			/* # of sections per zone */
/* 0x20 */	uint32_t	checksum_offset;		/* checksum offset inside super block */
/* 0x24 */	uint64_t	block_count;			/* total # of user blocks */
/* 0x2C */	uint32_t	section_count;			/* total # of sections */
/* 0x30 */	uint32_t	segment_count;			/* total # of segments */
/* 0x34 */	uint32_t	segment_count_ckpt;		/* # of segments for checkpoint */
/* 0x38 */	uint32_t	segment_count_sit;		/* # of segments for SIT */
/* 0x3C */	uint32_t	segment_count_nat;		/* # of segments for NAT */
/* 0x40 */	uint32_t	segment_count_ssa;		/* # of segments for SSA */
/* 0x44 */	uint32_t	segment_count_main;		/* # of segments for main area */
/* 0x48 */	uint32_t	segment0_blkaddr;		/* start block address of segment 0 */
/* 0x4C */	uint32_t	cp_blkaddr;			/* start block address of checkpoint */
/* 0x50 */	uint32_t	sit_blkaddr;			/* start block address of SIT */
/* 0x54 */	uint32_t	nat_blkaddr;			/* start block address of NAT */
/* 0x58 */	uint32_t	ssa_blkaddr;			/* start block address of SSA */
/* 0x5C */	uint32_t	main_blkaddr;			/* start block address of main area */
/* 0x60 */	uint32_t	root_ino;			/* root inode number */
/* 0x64 */	uint32_t	node_ino;			/* node inode number */
/* 0x68 */	uint32_t	meta_ino;			/* meta inode number */
/* 0x6C */	uint8_t		uuid[F2FS_UUID_SIZE];		/* 128-bit uuid for volume */
/* 0x7C */	uint16_t	volume_name[F2FS_LABEL_SIZE];	/* volume name */
#if 0
/* 0x47C */	uint32_t	extension_count;		/* # of extensions below */
/* 0x480 */	uint8_t		extension_list[64][8];		/* extension array */
#endif
} __attribute__((packed));

static int f2fs_validate_checksum(blkid_probe pr, size_t sb_off,
		const struct f2fs_super_block *sb)
{
	uint32_t csum_off = le32_to_cpu(sb->checksum_offset);
	if (!csum_off)
		return 1;
	if (csum_off % sizeof(uint32_t) != 0)
		return 0;
	if (csum_off + sizeof(uint32_t) > 4096)
		return 0;

	const unsigned char *csum_data = blkid_probe_get_buffer(pr,
			sb_off + csum_off, sizeof(uint32_t));
	if (!csum_data)
		return 0;

	uint32_t expected = le32_to_cpu(*(uint32_t *) csum_data);

	const unsigned char *csummed = blkid_probe_get_buffer(pr, sb_off, csum_off);
	if (!csummed)
		return 0;

	uint32_t csum = ul_crc32(0xF2F52010, csummed, csum_off);

	return blkid_probe_verify_csum(pr, csum, expected);
}

static int probe_f2fs(blkid_probe pr, const struct blkid_idmag *mag)
{
	const struct f2fs_super_block *sb;
	uint16_t vermaj, vermin;

	sb = blkid_probe_get_sb(pr, mag, struct f2fs_super_block);
	if (!sb)
		return errno ? -errno : 1;

	vermaj = le16_to_cpu(sb->major_ver);
	vermin = le16_to_cpu(sb->minor_ver);

	/* For version 1.0 we cannot know the correct sb structure */
	if (vermaj == 1 && vermin == 0)
		return 0;

	if (!f2fs_validate_checksum(pr, mag->kboff << 10, sb))
		return 1;

	if (*((unsigned char *) sb->volume_name))
		blkid_probe_set_utf8label(pr, (unsigned char *) sb->volume_name,
						sizeof(sb->volume_name),
						UL_ENCODE_UTF16LE);

	blkid_probe_set_uuid(pr, sb->uuid);
	blkid_probe_sprintf_version(pr, "%u.%u", vermaj, vermin);
	if (le32_to_cpu(sb->log_blocksize) < 32){
		uint32_t blocksize = 1U << le32_to_cpu(sb->log_blocksize);
		blkid_probe_set_fsblocksize(pr, blocksize);
		blkid_probe_set_block_size(pr, blocksize);
		blkid_probe_set_fssize(pr, le64_to_cpu(sb->block_count) * blocksize);
	}
	return 0;
}

const struct blkid_idinfo f2fs_idinfo =
{
	.name           = "f2fs",
	.usage          = BLKID_USAGE_FILESYSTEM,
	.probefunc      = probe_f2fs,
	.magics         =
        {
		{
			.magic = F2FS_MAGIC,
			.len = 4,
			.kboff = F2FS_SB1_KBOFF,
			.sboff = F2FS_MAGIC_OFF
		},
		{ NULL }
	}
};