summaryrefslogtreecommitdiffhomepage
path: root/util-linux/mkfs_reiser.c
blob: d01119f7fd433754cd5768fb8ca619693c34ae54 (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
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
/* vi: set sw=4 ts=4: */
/*
 * mkfs_reiser: utility to create ReiserFS filesystem
 *
 * Busybox'ed (2009) by Vladimir Dronnikov <dronnikov@gmail.com>
 *
 * Licensed under GPLv2, see file LICENSE in this source tree.
 */
//config:config MKFS_REISER
//config:	bool "mkfs_reiser"
//config:	default n
//config:	select PLATFORM_LINUX
//config:	help
//config:	  Utility to create ReiserFS filesystems.
//config:	  Note: this applet needs a lot of testing and polishing.

//applet:IF_MKFS_REISER(APPLET_ODDNAME(mkfs.reiser, mkfs_reiser, BB_DIR_SBIN, BB_SUID_DROP, mkfs_reiser))

//kbuild:lib-$(CONFIG_MKFS_REISER) += mkfs_reiser.o

//usage:#define mkfs_reiser_trivial_usage
//usage:       "[-f] [-l LABEL] BLOCKDEV [4K-BLOCKS]"
//usage:#define mkfs_reiser_full_usage "\n\n"
//usage:       "Make a ReiserFS V3 filesystem\n"
//usage:     "\n	-f	Force"
//usage:     "\n	-l LBL	Volume label"

#include "libbb.h"
#include <linux/fs.h>

char BUG_wrong_field_size(void);
#define STORE_LE(field, value) \
do { \
	if (sizeof(field) == 4) \
		field = SWAP_LE32(value); \
	else if (sizeof(field) == 2) \
		field = SWAP_LE16(value); \
	else if (sizeof(field) == 1) \
		field = (value); \
	else \
		BUG_wrong_field_size(); \
} while (0)

#define FETCH_LE32(field) \
	(sizeof(field) == 4 ? SWAP_LE32(field) : BUG_wrong_field_size())

struct journal_params {
	uint32_t jp_journal_1st_block;      /* where does journal start from on its device */
	uint32_t jp_journal_dev;            /* journal device st_rdev */
	uint32_t jp_journal_size;           /* size of the journal on FS creation. used to make sure they don't overflow it */
	uint32_t jp_journal_trans_max;      /* max number of blocks in a transaction.  */
	uint32_t jp_journal_magic;          /* random value made on fs creation (this was sb_journal_block_count) */
	uint32_t jp_journal_max_batch;      /* max number of blocks to batch into a trans */
	uint32_t jp_journal_max_commit_age; /* in seconds, how old can an async commit be */
	uint32_t jp_journal_max_trans_age;  /* in seconds, how old can a transaction be */
};

struct reiserfs_journal_header {
	uint32_t jh_last_flush_trans_id;    /* id of last fully flushed transaction */
	uint32_t jh_first_unflushed_offset; /* offset in the log of where to start replay after a crash */
	uint32_t jh_mount_id;
	struct journal_params jh_journal;
	uint32_t jh_last_check_mount_id;    /* the mount id of the fs during the last reiserfsck --check. */
};

struct reiserfs_super_block {
	uint32_t sb_block_count;            /* 0 number of block on data device */
	uint32_t sb_free_blocks;            /* 4 free blocks count */
	uint32_t sb_root_block;             /* 8 root of the tree */

	struct journal_params sb_journal;   /* 12 */

	uint16_t sb_blocksize;          /* 44 */
	uint16_t sb_oid_maxsize;        /* 46 max size of object id array, see get_objectid() commentary */
	uint16_t sb_oid_cursize;        /* 48 current size of object id array */
	uint16_t sb_umount_state;       /* 50 this is set to 1 when filesystem was umounted, to 2 - when not */

	char s_magic[10];               /* 52 "ReIsErFs" or "ReIsEr2Fs" or "ReIsEr3Fs" */
	uint16_t sb_fs_state;           /* 62 it is set to used by fsck to mark which phase of rebuilding is done (used for fsck debugging) */
	uint32_t sb_hash_function_code; /* 64 code of function which was/is/will be used to sort names in a directory. See codes in above */
	uint16_t sb_tree_height;        /* 68 height of filesytem tree. Tree consisting of only one root block has 2 here */
	uint16_t sb_bmap_nr;            /* 70 amount of bitmap blocks needed to address each block of file system */
	uint16_t sb_version;            /* 72 this field is only reliable on filesystem with non-standard journal */
	uint16_t sb_reserved_for_journal;  /* 74 size in blocks of journal area on main device, we need to keep after non-standard journal relocation */
	uint32_t sb_inode_generation;   /* 76 */
	uint32_t sb_flags;              /* 80 Right now used only by inode-attributes, if enabled */
	unsigned char s_uuid[16];       /* 84 filesystem unique identifier */
	unsigned char s_label[16];      /* 100 filesystem volume label */
	uint16_t sb_mnt_count;          /* 116 */
	uint16_t sb_max_mnt_count;      /* 118 */
	uint32_t sb_lastcheck;          /* 120 */
	uint32_t sb_check_interval;     /* 124 */
/* zero filled by mkreiserfs and reiserfs_convert_objectid_map_v1() so any additions must be updated there as well. */
	char s_unused[76];              /* 128 */
	/* 204 */
};

/* Header of a disk block.  More precisely, header of a formatted leaf
   or internal node, and not the header of an unformatted node. */
struct block_head {
	uint16_t blk2_level;        /* Level of a block in the tree. */
	uint16_t blk2_nr_item;      /* Number of keys/items in a block. */
	uint16_t blk2_free_space;   /* Block free space in bytes. */
	uint16_t blk_reserved;
	uint32_t reserved[4];
};

#define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)

#define REISERFS_3_6_SUPER_MAGIC_STRING "ReIsEr2Fs"
#define REISERFS_FORMAT_3_6     2
#define DEFAULT_MAX_MNT_COUNT   30                      /* 30 mounts */
#define DEFAULT_CHECK_INTERVAL  (180 * 60 * 60 * 24)    /* 180 days */

#define FS_CLEANLY_UMOUNTED     1 /* this was REISERFS_VALID_FS */

#define JOURNAL_MIN_SIZE        512
/* biggest possible single transaction, don't change for now (8/3/99) */
#define JOURNAL_TRANS_MAX       1024
#define JOURNAL_TRANS_MIN       256     /* need to check whether it works */
#define JOURNAL_DEFAULT_RATIO   8       /* default journal size / max trans length */
#define JOURNAL_MIN_RATIO       2
/* max blocks to batch into one transaction, don't make this any bigger than 900 */
#define JOURNAL_MAX_BATCH       900
#define JOURNAL_MAX_COMMIT_AGE  30


// Standard mkreiserfs 3.6.21:
//   -b | --block-size N              size of file-system block, in bytes
//   -j | --journal-device FILE       path to separate device to hold journal
//   -s | --journal-size N            size of the journal in blocks
//   -o | --journal-offset N          offset of the journal from the start of
//                                    the separate device, in blocks
//   -t | --transaction-max-size N    maximal size of transaction, in blocks
//   -B | --badblocks file            store all bad blocks given in file on the fs
//   -h | --hash rupasov|tea|r5       hash function to use by default
//   -u | --uuid UUID                 store UUID in the superblock
//   -l | --label LABEL               store LABEL in the superblock
//   --format 3.5|3.6                 old 3.5 format or newer 3.6
//   -f | --force                     specified once, make mkreiserfs the whole
//                                    disk, not block device or mounted partition;
//                                    specified twice, do not ask for confirmation
//   -q | --quiet                     quiet work without messages, progress and
//                                    questions. Useful if run in a script. For use
//                                    by end users only.
//   -d | --debug                     print debugging information during mkreiser
//   -V                               print version and exit

// Options not commented below are taken but silently ignored:
enum {
	OPT_b = 1 << 0,
	OPT_j = 1 << 1,
	OPT_s = 1 << 2,
	OPT_o = 1 << 3,
	OPT_t = 1 << 4,
	OPT_B = 1 << 5,
	OPT_h = 1 << 6,
	OPT_u = 1 << 7,
	OPT_l = 1 << 8,		// label
	OPT_f = 1 << 9,		// ask no questions
	OPT_q = 1 << 10,
	OPT_d = 1 << 11,
	//OPT_V = 1 << 12,	// -V version. bbox applets don't support that
};

int mkfs_reiser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int mkfs_reiser_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned blocksize = 4096;
	unsigned journal_blocks = 8192;
	unsigned blocks, bitmap_blocks, i, block;
	time_t timestamp;
	const char *label = "";
	struct stat st;
	int fd;
	uint8_t *buf;
	struct reiserfs_super_block *sb;
	struct journal_params *jp;
	struct block_head *root;

	// using global "option_mask32" instead of local "opts":
	// we are register starved here
	opt_complementary = "-1";
	/*opts =*/ getopt32(argv, "b:+j:s:o:t:B:h:u:l:fqd",
		NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &label);
	argv += optind; // argv[0] -- device

	// check the device is a block device
	fd = xopen(argv[0], O_WRONLY | O_EXCL);
	xfstat(fd, &st, argv[0]);
	if (!S_ISBLK(st.st_mode) && !(option_mask32 & OPT_f))
		bb_error_msg_and_die("%s: not a block device", argv[0]);

	// check if it is mounted
	// N.B. what if we format a file? find_mount_point will return false negative since
	// it is loop block device which is mounted!
	if (find_mount_point(argv[0], 0))
		bb_error_msg_and_die("can't format mounted filesystem");

	// open the device, get size in blocks
	blocks = get_volume_size_in_bytes(fd, argv[1], blocksize, /*extend:*/ 1) / blocksize;

	// block number sanity check
	// we have a limit: skipped area, super block, journal and root block
	// all have to be addressed by one first bitmap
	block = REISERFS_DISK_OFFSET_IN_BYTES / blocksize // boot area
		+ 1		// sb
		+ 1		// bitmap#0
		+ journal_blocks+1	// journal
	;

	// count overhead
	bitmap_blocks = (blocks - 1) / (blocksize * 8) + 1;
	i = block + bitmap_blocks;

	// check overhead
	if (MIN(blocksize * 8, blocks) < i)
		bb_error_msg_and_die("need >= %u blocks", i);

	// ask confirmation?
	// TODO: ???

	// wipe out first REISERFS_DISK_OFFSET_IN_BYTES of device
	// TODO: do we really need to wipe?!
	xlseek(fd, REISERFS_DISK_OFFSET_IN_BYTES, SEEK_SET);

	// fill superblock
	sb = (struct reiserfs_super_block *)xzalloc(blocksize);
	// block count
	STORE_LE(sb->sb_block_count, blocks);
	STORE_LE(sb->sb_free_blocks, blocks - i);
	// TODO: decypher!
	STORE_LE(sb->sb_root_block, block);
	// fill journal related fields
	jp = &sb->sb_journal;
	STORE_LE(jp->jp_journal_1st_block, REISERFS_DISK_OFFSET_IN_BYTES / blocksize + 1/*sb*/ + 1/*bmp#0*/);
	timestamp = time(NULL);
	srand(timestamp);
	STORE_LE(jp->jp_journal_magic, rand());
	STORE_LE(jp->jp_journal_size, journal_blocks);
	STORE_LE(jp->jp_journal_trans_max, JOURNAL_TRANS_MAX);
	STORE_LE(jp->jp_journal_max_batch, JOURNAL_MAX_BATCH);
	STORE_LE(jp->jp_journal_max_commit_age, JOURNAL_MAX_COMMIT_AGE);
	// sizes
	STORE_LE(sb->sb_blocksize, blocksize);
	STORE_LE(sb->sb_oid_maxsize, (blocksize - sizeof(*sb)) / sizeof(uint32_t) / 2 * 2);
	STORE_LE(sb->sb_oid_cursize, 2); // "." and ".."
	strcpy(sb->s_magic, REISERFS_3_6_SUPER_MAGIC_STRING);
	STORE_LE(sb->sb_bmap_nr, (bitmap_blocks > ((1LL << 16) - 1)) ? 0 : bitmap_blocks);
	// misc
	STORE_LE(sb->sb_version, REISERFS_FORMAT_3_6);
	STORE_LE(sb->sb_lastcheck, timestamp);
	STORE_LE(sb->sb_check_interval, DEFAULT_CHECK_INTERVAL);
	STORE_LE(sb->sb_mnt_count, 1);
	STORE_LE(sb->sb_max_mnt_count, DEFAULT_MAX_MNT_COUNT);
	STORE_LE(sb->sb_umount_state, FS_CLEANLY_UMOUNTED);
	STORE_LE(sb->sb_tree_height, 2);
	STORE_LE(sb->sb_hash_function_code, 3); // R5_HASH
	STORE_LE(sb->sb_flags, 1);
	//STORE_LE(sb->sb_reserved_for_journal, 0);
	// create UUID
	generate_uuid(sb->s_uuid);
	// write the label
	safe_strncpy((char *)sb->s_label, label, sizeof(sb->s_label));

	// TODO: EMPIRIC! ENDIANNESS!
	// superblock has only 204 bytes. What are these?
	buf = (uint8_t *)sb;
	buf[205] = 1;
	buf[209] = 3;

	// put superblock
	xwrite(fd, sb, blocksize);

	// create bitmaps
	buf = xzalloc(blocksize);

	// bitmap #0 uses initial "block"+1 blocks
	i = block + 1;
	memset(buf, 0xFF, i / 8);
	buf[i / 8] = (1 << (i & 7)) - 1; //0..7 => 00000000..01111111
	// mark trailing absent blocks, if any
	if (blocks < 8*blocksize) {
		unsigned n = 8*blocksize - blocks;
		i = n / 8;
		buf[blocksize - i - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110
		memset(buf + blocksize - i, 0xFF, i); // N.B. no overflow here!
	}
	// put bitmap #0
	xwrite(fd, buf, blocksize);

	// now go journal blocks
	memset(buf, 0, blocksize);
	for (i = 0; i < journal_blocks; i++)
		xwrite(fd, buf, blocksize);
	// dump journal control block
	memcpy(&((struct reiserfs_journal_header *)buf)->jh_journal, &sb->sb_journal, sizeof(sb->sb_journal));
	xwrite(fd, buf, blocksize);

	// other bitmaps are in every (8*blocksize)-th block
	// N.B. they use the only block -- namely bitmap itself!
	buf[0] = 0x01;
	// put bitmaps
	for (i = 1; i < bitmap_blocks; i++) {
		xlseek(fd, i*8*blocksize * blocksize, SEEK_SET);
		// mark trailing absent blocks, if any
		if (i == bitmap_blocks - 1 && (blocks % (8*blocksize))) {
			unsigned n = 8*blocksize - blocks % (8*blocksize);
			unsigned j = n / 8;
			buf[blocksize - j - 1] |= 0x7F00 >> (n & 7); //0..7 => 00000000..11111110
			memset(buf + blocksize - j, 0xFF, j); // N.B. no overflow here!
		}
		xwrite(fd, buf, blocksize);
	}

	// fill root block
	// block head
	memset(buf, 0, blocksize);
	root = (struct block_head *)buf;
	STORE_LE(root->blk2_level, 1); // leaf node
	STORE_LE(root->blk2_nr_item, 2); // "." and ".."
	STORE_LE(root->blk2_free_space, blocksize - sizeof(struct block_head));
	// item head
	// root directory
	// TODO: EMPIRIC! ENDIANNESS!
	// TODO: indented assignments seem to be timestamps
buf[4] = 0134;
buf[24] = 01;
buf[28] = 02;
buf[42] = 054;
buf[44] = 0324;
buf[45] = 017;
buf[46] = 01;
buf[48] = 01;
buf[52] = 02;
buf[56] = 01;
buf[60] = 0364;
buf[61] = 01;
buf[64] = 02;
buf[66] = 060;
buf[68] = 0244;
buf[69] = 017;
buf[4004] = 01;
buf[4008] = 01;
buf[4012] = 02;
buf[4016] = 050;
buf[4018] = 04;
buf[4020] = 02;
buf[4028] = 01;
buf[4032] = 040;
buf[4034] = 04;

buf[4036] = 056; buf[4037] = 056;	// ".."
buf[4044] = 056;			// "."

buf[4052] = 0355;
buf[4053] = 0101;
buf[4056] = 03;
buf[4060] = 060;
		buf[4076] = 0173;
		buf[4077] = 0240;
	buf[4078] = 0344;
	buf[4079] = 0112;
		buf[4080] = 0173;
		buf[4081] = 0240;
	buf[4082] = 0344;
	buf[4083] = 0112;
		buf[4084] = 0173;
		buf[4085] = 0240;
	buf[4086] = 0344;
	buf[4087] = 0112;
buf[4088] = 01;

	// put root block
	xlseek(fd, block * blocksize, SEEK_SET);
	xwrite(fd, buf, blocksize);

	// cleanup
	if (ENABLE_FEATURE_CLEAN_UP) {
		free(buf);
		free(sb);
	}

	xclose(fd);
	return EXIT_SUCCESS;
}