HAMMER 28/many: Implement zoned blockmap
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
... / ...
CommitLineData
1/*
2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 *
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.18 2008/02/10 09:50:56 dillon Exp $
35 */
36
37#include "newfs_hammer.h"
38
39static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
40static const char *sizetostr(off_t size);
41static void check_volume(struct volume_info *vol);
42static void format_volume(struct volume_info *vol, int nvols,const char *label);
43static hammer_off_t format_root(void);
44static void usage(void);
45
46int
47main(int ac, char **av)
48{
49 int i;
50 int ch;
51 u_int32_t status;
52 off_t total;
53 const char *label = NULL;
54
55 /*
56 * Sanity check basic filesystem structures. No cookies for us
57 * if it gets broken!
58 */
59 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
60 assert(sizeof(union hammer_record_ondisk) == HAMMER_RECORD_SIZE);
61 assert(sizeof(struct hammer_blockmap_entry) == 32);
62
63 /*
64 * Generate a filesysem id and lookup the filesystem type
65 */
66 uuidgen(&Hammer_FSId, 1);
67 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
68 if (status != uuid_s_ok) {
69 errx(1, "uuids file does not have the DragonFly "
70 "HAMMER filesystem type");
71 }
72
73 /*
74 * Parse arguments
75 */
76 while ((ch = getopt(ac, av, "L:b:m:")) != -1) {
77 switch(ch) {
78 case 'L':
79 label = optarg;
80 break;
81 case 'b':
82 BootAreaSize = getsize(optarg,
83 HAMMER_BUFSIZE,
84 HAMMER_BOOT_MAXBYTES, 2);
85 break;
86 case 'm':
87 MemAreaSize = getsize(optarg,
88 HAMMER_BUFSIZE,
89 HAMMER_MEM_MAXBYTES, 2);
90 break;
91 default:
92 usage();
93 break;
94 }
95 }
96
97 if (label == NULL) {
98 fprintf(stderr,
99 "newfs_hammer: A filesystem label must be specified\n");
100 exit(1);
101 }
102
103 /*
104 * Collect volume information
105 */
106 ac -= optind;
107 av += optind;
108 NumVolumes = ac;
109 RootVolNo = 0;
110
111 total = 0;
112 for (i = 0; i < NumVolumes; ++i) {
113 struct volume_info *vol;
114
115 vol = setup_volume(i, av[i], 1, O_RDWR);
116
117 /*
118 * Load up information on the volume and initialize
119 * its remaining fields.
120 */
121 check_volume(vol);
122 total += vol->size;
123 }
124
125 /*
126 * Calculate defaults for the boot and memory area sizes.
127 */
128 if (BootAreaSize == 0) {
129 BootAreaSize = HAMMER_BOOT_NOMBYTES;
130 while (BootAreaSize > total / NumVolumes / 256)
131 BootAreaSize >>= 1;
132 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
133 BootAreaSize = 0;
134 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
135 BootAreaSize = HAMMER_BOOT_MINBYTES;
136 }
137 if (MemAreaSize == 0) {
138 MemAreaSize = HAMMER_MEM_NOMBYTES;
139 while (MemAreaSize > total / NumVolumes / 256)
140 MemAreaSize >>= 1;
141 if (MemAreaSize < HAMMER_MEM_MINBYTES)
142 MemAreaSize = 0;
143 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
144 MemAreaSize = HAMMER_MEM_MINBYTES;
145 }
146
147 printf("---------------------------------------------\n");
148 printf("%d volume%s total size %s\n",
149 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
150 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
151 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
152 printf("\n");
153
154 /*
155 * Format the volumes.
156 */
157 for (i = 0; i < NumVolumes; ++i) {
158 format_volume(get_volume(i), NumVolumes, label);
159 }
160 flush_all_volumes();
161 return(0);
162}
163
164static
165void
166usage(void)
167{
168 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
169 exit(1);
170}
171
172/*
173 * Convert the size in bytes to a human readable string.
174 */
175static const char *
176sizetostr(off_t size)
177{
178 static char buf[32];
179
180 if (size < 1024 / 2) {
181 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
182 } else if (size < 1024 * 1024 / 2) {
183 snprintf(buf, sizeof(buf), "%6.2fKB",
184 (double)size / 1024);
185 } else if (size < 1024 * 1024 * 1024LL / 2) {
186 snprintf(buf, sizeof(buf), "%6.2fMB",
187 (double)size / (1024 * 1024));
188 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
189 snprintf(buf, sizeof(buf), "%6.2fGB",
190 (double)size / (1024 * 1024 * 1024LL));
191 } else {
192 snprintf(buf, sizeof(buf), "%6.2fTB",
193 (double)size / (1024 * 1024 * 1024LL * 1024LL));
194 }
195 return(buf);
196}
197
198/*
199 * Convert a string to a 64 bit signed integer with various requirements.
200 */
201static int64_t
202getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
203{
204 int64_t val;
205 char *ptr;
206
207 val = strtoll(str, &ptr, 0);
208 switch(*ptr) {
209 case 't':
210 case 'T':
211 val *= 1024;
212 /* fall through */
213 case 'g':
214 case 'G':
215 val *= 1024;
216 /* fall through */
217 case 'm':
218 case 'M':
219 val *= 1024;
220 /* fall through */
221 case 'k':
222 case 'K':
223 val *= 1024;
224 break;
225 default:
226 errx(1, "Unknown suffix in number '%s'\n", str);
227 /* not reached */
228 }
229 if (ptr[1]) {
230 errx(1, "Unknown suffix in number '%s'\n", str);
231 /* not reached */
232 }
233 if (val < minval) {
234 errx(1, "Value too small: %s, min is %s\n",
235 str, sizetostr(minval));
236 /* not reached */
237 }
238 if (val > maxval) {
239 errx(1, "Value too large: %s, max is %s\n",
240 str, sizetostr(maxval));
241 /* not reached */
242 }
243 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
244 errx(1, "Value not power of 2: %s\n", str);
245 /* not reached */
246 }
247 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
248 errx(1, "Value not an integral multiple of %dK: %s",
249 HAMMER_BUFSIZE / 1024, str);
250 /* not reached */
251 }
252 return(val);
253}
254
255/*
256 * Generate a transaction id
257 */
258static hammer_tid_t
259createtid(void)
260{
261 static hammer_tid_t lasttid;
262 struct timeval tv;
263
264 if (lasttid == 0) {
265 gettimeofday(&tv, NULL);
266 lasttid = tv.tv_sec * 1000000000LL +
267 tv.tv_usec * 1000LL;
268 }
269 return(lasttid++);
270}
271
272/*
273 * Check basic volume characteristics. HAMMER filesystems use a minimum
274 * of a 16KB filesystem buffer size.
275 */
276static
277void
278check_volume(struct volume_info *vol)
279{
280 struct partinfo pinfo;
281 struct stat st;
282
283 /*
284 * Get basic information about the volume
285 */
286 vol->fd = open(vol->name, O_RDWR);
287 if (vol->fd < 0)
288 err(1, "Unable to open %s R+W", vol->name);
289 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
290 /*
291 * Allow the formatting of regular filews as HAMMER volumes
292 */
293 if (fstat(vol->fd, &st) < 0)
294 err(1, "Unable to stat %s", vol->name);
295 vol->size = st.st_size;
296 vol->type = "REGFILE";
297 } else {
298 /*
299 * When formatting a block device as a HAMMER volume the
300 * sector size must be compatible. HAMMER uses 16384 byte
301 * filesystem buffers.
302 */
303 if (pinfo.reserved_blocks) {
304 errx(1, "HAMMER cannot be placed in a partition "
305 "which overlaps the disklabel or MBR");
306 }
307 if (pinfo.media_blksize > 16384 ||
308 16384 % pinfo.media_blksize) {
309 errx(1, "A media sector size of %d is not supported",
310 pinfo.media_blksize);
311 }
312
313 vol->size = pinfo.media_size;
314 vol->type = "DEVICE";
315 }
316 printf("Volume %d %s %-15s size %s\n",
317 vol->vol_no, vol->type, vol->name,
318 sizetostr(vol->size));
319
320 /*
321 * Reserve space for (future) header junk
322 */
323 vol->vol_alloc = HAMMER_BUFSIZE * 16;
324}
325
326/*
327 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
328 */
329static
330void
331format_volume(struct volume_info *vol, int nvols, const char *label)
332{
333 struct hammer_volume_ondisk *ondisk;
334
335 /*
336 * Initialize basic information in the on-disk volume structure.
337 */
338 ondisk = vol->ondisk;
339
340 ondisk->vol_fsid = Hammer_FSId;
341 ondisk->vol_fstype = Hammer_FSType;
342 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
343 ondisk->vol_no = vol->vol_no;
344 ondisk->vol_count = nvols;
345 ondisk->vol_version = 1;
346
347 ondisk->vol_bot_beg = vol->vol_alloc;
348 vol->vol_alloc += BootAreaSize;
349 ondisk->vol_mem_beg = vol->vol_alloc;
350 vol->vol_alloc += MemAreaSize;
351
352 /*
353 * The remaining area is the zone 2 buffer allocation area. These
354 * buffers
355 */
356 ondisk->vol_buf_beg = vol->vol_alloc;
357 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
358
359 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
360 errx(1, "volume %d %s is too small to hold the volume header",
361 vol->vol_no, vol->name);
362 }
363
364 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
365 HAMMER_BUFSIZE;
366 ondisk->vol_blocksize = HAMMER_BUFSIZE;
367
368 /*
369 * Assign the root volume to volume 0.
370 */
371 ondisk->vol_rootvol = 0;
372 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
373 if (ondisk->vol_no == (int)ondisk->vol_rootvol) {
374 /*
375 * Create an empty FIFO starting at the first buffer
376 * in volume 0. hammer_off_t must be properly formatted
377 * (see vfs/hammer/hammer_disk.h)
378 */
379 ondisk->vol0_free_off = HAMMER_ENCODE_RAW_BUFFER(0, 0);
380 ondisk->vol0_next_tid = createtid();
381 ondisk->vol0_next_seq = 1;
382 format_blockmap(
383 &ondisk->vol0_blockmap[HAMMER_ZONE_BTREE_INDEX],
384 HAMMER_ZONE_BTREE);
385 format_blockmap(
386 &ondisk->vol0_blockmap[HAMMER_ZONE_RECORD_INDEX],
387 HAMMER_ZONE_RECORD);
388 format_blockmap(
389 &ondisk->vol0_blockmap[HAMMER_ZONE_LARGE_DATA_INDEX],
390 HAMMER_ZONE_LARGE_DATA);
391 format_blockmap(
392 &ondisk->vol0_blockmap[HAMMER_ZONE_SMALL_DATA_INDEX],
393 HAMMER_ZONE_SMALL_DATA);
394
395 ondisk->vol0_btree_root = format_root();
396 ++ondisk->vol0_stat_inodes; /* root inode */
397
398 }
399}
400
401/*
402 * Format the root directory.
403 */
404static
405hammer_off_t
406format_root(void)
407{
408 hammer_off_t btree_off;
409 hammer_off_t rec_off;
410 hammer_node_ondisk_t bnode;
411 hammer_record_ondisk_t rec;
412 struct hammer_inode_data *idata;
413 hammer_btree_elm_t elm;
414
415 bnode = alloc_btree_element(&btree_off);
416 rec = alloc_record_element(&rec_off, sizeof(*idata), (void **)&idata);
417
418 /*
419 * Populate the inode data and inode record for the root directory.
420 */
421 idata->version = HAMMER_INODE_DATA_VERSION;
422 idata->mode = 0755;
423
424 rec->base.base.btype = HAMMER_BTREE_TYPE_RECORD;
425 rec->base.base.obj_id = HAMMER_OBJID_ROOT;
426 rec->base.base.key = 0;
427 rec->base.base.create_tid = createtid();
428 rec->base.base.delete_tid = 0;
429 rec->base.base.rec_type = HAMMER_RECTYPE_INODE;
430 rec->base.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
431 /* rec->base.data_offset - initialized by alloc_record_element */
432 /* rec->base.data_len - initialized by alloc_record_element */
433 rec->base.data_crc = crc32(idata, sizeof(*idata));
434 rec->inode.ino_atime = rec->base.base.create_tid;
435 rec->inode.ino_mtime = rec->base.base.create_tid;
436 rec->inode.ino_size = 0;
437 rec->inode.ino_nlinks = 1;
438
439 /*
440 * Create the root of the B-Tree. The root is a leaf node so we
441 * do not have to worry about boundary elements.
442 */
443 bnode->count = 1;
444 bnode->type = HAMMER_BTREE_TYPE_LEAF;
445
446 elm = &bnode->elms[0];
447 elm->base = rec->base.base;
448 elm->leaf.rec_offset = rec_off;
449 elm->leaf.data_offset = rec->base.data_off;
450 elm->leaf.data_len = rec->base.data_len;
451 elm->leaf.data_crc = rec->base.data_crc;
452 return(btree_off);
453}
454