File: mkapfs.h

package info (click to toggle)
apfsprogs 0.2.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 1,108 kB
  • sloc: ansic: 16,034; makefile: 175; sh: 57
file content (231 lines) | stat: -rw-r--r-- 6,860 bytes parent folder | download
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
/*
 * Copyright (C) 2019 Ernesto A. Fernández <ernesto.mnd.fernandez@gmail.com>
 */

#ifndef _MKAPFS_H
#define _MKAPFS_H

#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <apfs/raw.h>

/* Filesystem parameters */
struct parameters {
	unsigned long	blocksize;	/* Block size */
	u64		block_count;	/* Number of blocks in the container */
	u64		main_blkcnt;	/* Number of blocks in the main device */
	u64		tier2_blkcnt;	/* Number of blocks in the tier 2 device */
	char		*label;		/* Volume label */
	char		*main_uuid;	/* Container UUID in standard format */
	char		*vol_uuid;	/* Volume UUID in standard format */
	char		*fusion_uuid;	/* Fusion drive UUID in standard format */
	bool		case_sensitive;	/* Is the filesystem case-sensitive? */
	bool		norm_sensitive;	/* Is it normalization-sensitive? */
};

/* Declarations for global variables */
extern struct parameters *param;	/* Filesystem parameters */
extern int fd_main;			/* File descriptor for the main device */
extern int fd_tier2;			/* File descriptor for the tier 2 device, if any */

/* Hardcoded transaction ids */
#define MKFS_XID	1

/* Hardcoded object ids */
#define	SPACEMAN_OID		APFS_OID_RESERVED_COUNT
#define REAPER_OID		(SPACEMAN_OID + 1)
#define FIRST_VOL_OID		(REAPER_OID + 1)
#define FIRST_VOL_CAT_ROOT_OID	(FIRST_VOL_OID + 1)
#define	IP_FREE_QUEUE_OID	(FIRST_VOL_CAT_ROOT_OID + 1)
#define MAIN_FREE_QUEUE_OID	(IP_FREE_QUEUE_OID + 1)
#define TIER2_FREE_QUEUE_OID	(MAIN_FREE_QUEUE_OID + 1)
#define FUSION_WBC_OID		(TIER2_FREE_QUEUE_OID + 1)

/**
 * cpoint_desc_blocks - Calculate the number of checkpoint descriptor blocks
 *
 * TODO: what if the tier 2 device is much bigger than the main one?
 */
static inline u32 cpoint_desc_blocks(void)
{
	/*
	 * These numbers are a rough approximation of what I learned from
	 * testing newfs_apfs. I don't think the exact block counts matter.
	 */
	if (param->block_count < 512 * 1024 / 4) /* Up to 512M */
		return 8;
	if (param->block_count < 1024 * 1024 / 4) /* Up to 1G */
		return 12;
	if (param->block_count < 50 * 1024 * 1024 / 4) { /* Up to 50G */
		u32 off_512M = (param->block_count - 1024 * 1024 / 4) / (512 * 1024 / 4);
		return 20 + 60 * off_512M / 23;
	}

	/*
	 * From here on, newfs_apfs increases the number very slowly. I don't
	 * think it matters...
	 */
	return 280;
}

/**
 * cpoint_data_blocks - Calculate the number of checkpoint data blocks
 *
 * TODO: what if the tier 2 device is much bigger than the main one?
 */
static inline u32 cpoint_data_blocks(void)
{
	/* I got these numbers from testing newfs_apfs */
	if (param->block_count < 4545)
		return 52;
	if (param->block_count < 13633)
		return 124;
	if (param->block_count < 36353)
		return 160 + 36 * ((param->block_count - 13633) / 4544);
	if (param->block_count < 131777)
		return 308 + 4 * ((param->block_count - 36353) / 4544);
	if (param->block_count < 262144)
		return 648 + 4 * ((param->block_count - 131777) / 4544);
	if (param->block_count == 262144) /* 1G is a special case, odd */
		return 992;

	/*
	 * At this point I got bored and the sizes stop matching exactly. The
	 * official fsck doesn't care. TODO?
	 */
	if (param->block_count < 1048576) { /* Up to 4G */
		u32 off_512M = (param->block_count - 262144) / 131072;
		return 1248 + 488 * off_512M + 4 * (((long long)param->block_count - (261280 + off_512M * 131776)) / 2272);
	}
	if (param->block_count < 4063232) { /* Up to 10G */
		u32 off_512M = (param->block_count - 1048576) / 131072;
		return 4112 + 256 * off_512M;
	}
	if (param->block_count < 13107200) { /* Up to 50G */
		u32 off_512M = (param->block_count - 4063232) / 131072;
		return 10000 + 256 * off_512M;
	}

	/*
	 * From here on, newfs_apfs increases the number very slowly. I don't
	 * think it matters...
	 */
	return 27672;
}

/* Description of the checkpoint areas */
#define CPOINT_DESC_BASE	(APFS_NX_BLOCK_NUM + 1)
#define CPOINT_DESC_BLOCKS	cpoint_desc_blocks()
#define CPOINT_DATA_BASE	(CPOINT_DESC_BASE + CPOINT_DESC_BLOCKS)
#define CPOINT_DATA_BLOCKS	cpoint_data_blocks()
#define CPOINT_END		(CPOINT_DATA_BASE + CPOINT_DATA_BLOCKS)

/*
 * Some block numbers for ephemeral objects will need to be calculated on
 * runtime, so that they can all be kept consecutive and in the right order.
 */
extern struct ephemeral_info {
	u64 reaper_bno;
	u64 spaceman_bno;
	u32 spaceman_sz;
	u32 spaceman_blkcnt;
	u64 ip_free_queue_bno;
	u64 main_free_queue_bno;
	u64 tier2_free_queue_bno;
	u64 fusion_wbc_bno;
	u32 total_blkcnt;
} eph_info;

/* Hardcoded block numbers calculated from the checkpoint areas */
#define CPOINT_MAP_BNO			CPOINT_DESC_BASE
#define CPOINT_SB_BNO			(CPOINT_DESC_BASE + 1)
#define MAIN_OMAP_BNO			CPOINT_END
#define MAIN_OMAP_ROOT_BNO		(CPOINT_END + 1)
#define FIRST_VOL_BNO			(CPOINT_END + 2)
#define FIRST_VOL_OMAP_BNO		(CPOINT_END + 3)
#define FIRST_VOL_OMAP_ROOT_BNO		(CPOINT_END + 4)
#define FIRST_VOL_CAT_ROOT_BNO		(CPOINT_END + 5)
#define FIRST_VOL_EXTREF_ROOT_BNO	(CPOINT_END + 6)
#define FIRST_VOL_SNAP_ROOT_BNO		(CPOINT_END + 7)
#define FUSION_MT_BNO			(CPOINT_END + 8)
/* Just a single block for now (TODO) */
#define FUSION_WBC_FIRST_BNO		(CPOINT_END + 9)
/* First ip bitmap comes last because the size is not hardcoded */
#define IP_BMAP_BASE			(CPOINT_END + 10)

extern __attribute__((noreturn)) void system_error(void);
extern __attribute__((noreturn)) void fatal(const char *message);

/* Forwards the pwrite() call to the proper device of a fusion drive */
static inline void apfs_writeall(void *buf, u64 blkcnt, u64 bno)
{
	int proper_fd;
	off_t offset;
	size_t count, copied;
	ssize_t ret;

	offset = bno * param->blocksize;
	count = blkcnt * param->blocksize;

	proper_fd = fd_main;
	if (offset >= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR) {
		if (fd_tier2 == -1)
			fatal("allocation attempted in missing tier 2 device.");
		offset -= APFS_FUSION_TIER2_DEVICE_BYTE_ADDR;
		proper_fd = fd_tier2;
	}

	copied = 0;
	while (count > 0) {
		ret = pwrite(proper_fd, buf + copied, count, offset + copied);
		if (ret < 0)
			system_error();
		count -= ret;
		copied += ret;
	}

	free(buf);
}

/**
 * get_zeroed_blocks - Return a number of zeroed blocks
 * @bno:	first block number
 * @count:	number of blocks
 */
static inline void *get_zeroed_blocks(u64 count)
{
	void *blocks;

	blocks = calloc(count, param->blocksize);
	if (!blocks)
		system_error();
	return blocks;
}

/**
 * get_zeroed_block - Return a zeroed block
 * @bno: block number
 */
static inline void *get_zeroed_block(void)
{
	return get_zeroed_blocks(1);
}

/**
 * get_timestamp - Get the current time in nanoseconds
 *
 * Calls clock_gettime(), so may not work with old versions of glibc.
 */
static inline u64 get_timestamp(void)
{
	struct timespec time;

	if (clock_gettime(CLOCK_REALTIME, &time))
		system_error();

	return (u64)time.tv_sec * NSEC_PER_SEC + time.tv_nsec;
}

#endif	/* _MKAPFS_H */