2 * Copyright (c) 2007 The DragonFly Project. All rights reserved.
4 * This code is derived from software contributed to The DragonFly Project
5 * by Matthew Dillon <dillon@backplane.com>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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
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.
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
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.29 2008/06/08 17:19:09 dillon Exp $
37 #include "newfs_hammer.h"
39 static int64_t getsize(const char *str
, int64_t minval
, int64_t maxval
, int pw
);
40 static const char *sizetostr(off_t size
);
41 static void check_volume(struct volume_info
*vol
);
42 static void format_volume(struct volume_info
*vol
, int nvols
,const char *label
,
44 static hammer_off_t
format_root(void);
45 static void usage(void);
48 main(int ac
, char **av
)
54 const char *label
= NULL
;
55 struct volume_info
*vol
;
56 hammer_off_t vol0_zone_limit
;
59 * Sanity check basic filesystem structures. No cookies for us
62 assert(sizeof(struct hammer_volume_ondisk
) <= HAMMER_BUFSIZE
);
63 assert(sizeof(struct hammer_blockmap_layer1
) == 32);
64 assert(sizeof(struct hammer_blockmap_layer2
) == 16);
67 * Generate a filesysem id and lookup the filesystem type
69 uuidgen(&Hammer_FSId
, 1);
70 uuid_name_lookup(&Hammer_FSType
, "DragonFly HAMMER", &status
);
71 if (status
!= uuid_s_ok
) {
72 errx(1, "uuids file does not have the DragonFly "
73 "HAMMER filesystem type");
79 while ((ch
= getopt(ac
, av
, "L:b:m:u:")) != -1) {
85 BootAreaSize
= getsize(optarg
,
87 HAMMER_BOOT_MAXBYTES
, 2);
90 MemAreaSize
= getsize(optarg
,
92 HAMMER_MEM_MAXBYTES
, 2);
95 UndoBufferSize
= getsize(optarg
,
96 HAMMER_LARGEBLOCK_SIZE
,
97 HAMMER_LARGEBLOCK_SIZE
*
98 HAMMER_UNDO_LAYER2
, 2);
108 "newfs_hammer: A filesystem label must be specified\n");
113 * Collect volume information
121 for (i
= 0; i
< NumVolumes
; ++i
) {
122 vol
= setup_volume(i
, av
[i
], 1, O_RDWR
);
125 * Load up information on the volume and initialize
126 * its remaining fields.
133 * Calculate defaults for the boot and memory area sizes.
135 if (BootAreaSize
== 0) {
136 BootAreaSize
= HAMMER_BOOT_NOMBYTES
;
137 while (BootAreaSize
> total
/ NumVolumes
/ 256)
139 if (BootAreaSize
< HAMMER_BOOT_MINBYTES
)
141 } else if (BootAreaSize
< HAMMER_BOOT_MINBYTES
) {
142 BootAreaSize
= HAMMER_BOOT_MINBYTES
;
144 if (MemAreaSize
== 0) {
145 MemAreaSize
= HAMMER_MEM_NOMBYTES
;
146 while (MemAreaSize
> total
/ NumVolumes
/ 256)
148 if (MemAreaSize
< HAMMER_MEM_MINBYTES
)
150 } else if (MemAreaSize
< HAMMER_MEM_MINBYTES
) {
151 MemAreaSize
= HAMMER_MEM_MINBYTES
;
155 * Format the volumes. Format the root volume first so we can
156 * bootstrap the freemap.
158 format_volume(get_volume(RootVolNo
), NumVolumes
, label
, total
);
159 for (i
= 0; i
< NumVolumes
; ++i
) {
161 format_volume(get_volume(i
), NumVolumes
, label
, total
);
165 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
166 * limit. If we do this the filesystem does not have to allocate
167 * new layer2 blocks which reduces the chances of the reblocker
168 * having to fallback to an extremely inefficient algorithm.
170 vol
= get_volume(RootVolNo
);
171 vol0_zone_limit
= vol
->ondisk
->vol0_zone_limit
;
172 printf("Initializing B-Tree blockmap\n");
173 presize_blockmap(&vol
->ondisk
->vol0_blockmap
[HAMMER_ZONE_BTREE_INDEX
],
174 HAMMER_ZONE_BTREE
, vol0_zone_limit
);
175 printf("Initializing Large-Data blockmap\n");
176 presize_blockmap(&vol
->ondisk
->vol0_blockmap
[HAMMER_ZONE_LARGE_DATA_INDEX
],
177 HAMMER_ZONE_LARGE_DATA
, vol0_zone_limit
);
178 printf("Initializing Small-Data blockmap\n");
179 presize_blockmap(&vol
->ondisk
->vol0_blockmap
[HAMMER_ZONE_SMALL_DATA_INDEX
],
180 HAMMER_ZONE_SMALL_DATA
, vol0_zone_limit
);
181 vol
->ondisk
->vol0_stat_bigblocks
= vol
->ondisk
->vol0_stat_freebigblocks
;
182 vol
->cache
.modified
= 1;
184 printf("---------------------------------------------\n");
185 printf("%d volume%s total size %s\n",
186 NumVolumes
, (NumVolumes
== 1 ? "" : "s"), sizetostr(total
));
187 printf("boot-area-size: %s\n", sizetostr(BootAreaSize
));
188 printf("memory-log-size: %s\n", sizetostr(MemAreaSize
));
189 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize
));
191 printf("zone-limit: %s\n", sizetostr(vol0_zone_limit
));
192 printf("total-pre-allocated: %s\n",
193 sizetostr(vol
->vol_free_off
& HAMMER_OFF_SHORT_MASK
));
204 fprintf(stderr
, "newfs_hammer vol0 [vol1 ...]\n");
209 * Convert the size in bytes to a human readable string.
213 sizetostr(off_t size
)
217 if (size
< 1024 / 2) {
218 snprintf(buf
, sizeof(buf
), "%6.2f", (double)size
);
219 } else if (size
< 1024 * 1024 / 2) {
220 snprintf(buf
, sizeof(buf
), "%6.2fKB",
221 (double)size
/ 1024);
222 } else if (size
< 1024 * 1024 * 1024LL / 2) {
223 snprintf(buf
, sizeof(buf
), "%6.2fMB",
224 (double)size
/ (1024 * 1024));
225 } else if (size
< 1024 * 1024 * 1024LL * 1024LL / 2) {
226 snprintf(buf
, sizeof(buf
), "%6.2fGB",
227 (double)size
/ (1024 * 1024 * 1024LL));
229 snprintf(buf
, sizeof(buf
), "%6.2fTB",
230 (double)size
/ (1024 * 1024 * 1024LL * 1024LL));
236 * Convert a string to a 64 bit signed integer with various requirements.
239 getsize(const char *str
, int64_t minval
, int64_t maxval
, int powerof2
)
244 val
= strtoll(str
, &ptr
, 0);
263 errx(1, "Unknown suffix in number '%s'\n", str
);
267 errx(1, "Unknown suffix in number '%s'\n", str
);
271 errx(1, "Value too small: %s, min is %s\n",
272 str
, sizetostr(minval
));
276 errx(1, "Value too large: %s, max is %s\n",
277 str
, sizetostr(maxval
));
280 if ((powerof2
& 1) && (val
^ (val
- 1)) != ((val
<< 1) - 1)) {
281 errx(1, "Value not power of 2: %s\n", str
);
284 if ((powerof2
& 2) && (val
& HAMMER_BUFMASK
)) {
285 errx(1, "Value not an integral multiple of %dK: %s",
286 HAMMER_BUFSIZE
/ 1024, str
);
293 * Generate a transaction id
298 static hammer_tid_t lasttid
;
302 gettimeofday(&tv
, NULL
);
303 lasttid
= tv
.tv_sec
* 1000000000LL +
310 * Check basic volume characteristics. HAMMER filesystems use a minimum
311 * of a 16KB filesystem buffer size.
315 check_volume(struct volume_info
*vol
)
317 struct partinfo pinfo
;
321 * Get basic information about the volume
323 vol
->fd
= open(vol
->name
, O_RDWR
);
325 err(1, "Unable to open %s R+W", vol
->name
);
326 if (ioctl(vol
->fd
, DIOCGPART
, &pinfo
) < 0) {
328 * Allow the formatting of regular filews as HAMMER volumes
330 if (fstat(vol
->fd
, &st
) < 0)
331 err(1, "Unable to stat %s", vol
->name
);
332 vol
->size
= st
.st_size
;
333 vol
->type
= "REGFILE";
336 * When formatting a block device as a HAMMER volume the
337 * sector size must be compatible. HAMMER uses 16384 byte
338 * filesystem buffers.
340 if (pinfo
.reserved_blocks
) {
341 errx(1, "HAMMER cannot be placed in a partition "
342 "which overlaps the disklabel or MBR");
344 if (pinfo
.media_blksize
> 16384 ||
345 16384 % pinfo
.media_blksize
) {
346 errx(1, "A media sector size of %d is not supported",
347 pinfo
.media_blksize
);
350 vol
->size
= pinfo
.media_size
;
351 vol
->type
= "DEVICE";
353 printf("Volume %d %s %-15s size %s\n",
354 vol
->vol_no
, vol
->type
, vol
->name
,
355 sizetostr(vol
->size
));
358 * Reserve space for (future) header junk, setup our poor-man's
359 * bigblock allocator.
361 vol
->vol_alloc
= HAMMER_BUFSIZE
* 16;
365 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
369 format_volume(struct volume_info
*vol
, int nvols
, const char *label
,
372 struct volume_info
*root_vol
;
373 struct hammer_volume_ondisk
*ondisk
;
377 * Initialize basic information in the on-disk volume structure.
379 ondisk
= vol
->ondisk
;
381 ondisk
->vol_fsid
= Hammer_FSId
;
382 ondisk
->vol_fstype
= Hammer_FSType
;
383 snprintf(ondisk
->vol_name
, sizeof(ondisk
->vol_name
), "%s", label
);
384 ondisk
->vol_no
= vol
->vol_no
;
385 ondisk
->vol_count
= nvols
;
386 ondisk
->vol_version
= 1;
388 ondisk
->vol_bot_beg
= vol
->vol_alloc
;
389 vol
->vol_alloc
+= BootAreaSize
;
390 ondisk
->vol_mem_beg
= vol
->vol_alloc
;
391 vol
->vol_alloc
+= MemAreaSize
;
394 * The remaining area is the zone 2 buffer allocation area. These
397 ondisk
->vol_buf_beg
= vol
->vol_alloc
;
398 ondisk
->vol_buf_end
= vol
->size
& ~(int64_t)HAMMER_BUFMASK
;
400 if (ondisk
->vol_buf_end
< ondisk
->vol_buf_beg
) {
401 errx(1, "volume %d %s is too small to hold the volume header",
402 vol
->vol_no
, vol
->name
);
405 ondisk
->vol_nblocks
= (ondisk
->vol_buf_end
- ondisk
->vol_buf_beg
) /
407 ondisk
->vol_blocksize
= HAMMER_BUFSIZE
;
409 ondisk
->vol_rootvol
= RootVolNo
;
410 ondisk
->vol_signature
= HAMMER_FSBUF_VOLUME
;
412 vol
->vol_free_off
= HAMMER_ENCODE_RAW_BUFFER(vol
->vol_no
, 0);
413 vol
->vol_free_end
= HAMMER_ENCODE_RAW_BUFFER(vol
->vol_no
, (ondisk
->vol_buf_end
- ondisk
->vol_buf_beg
) & ~HAMMER_LARGEBLOCK_MASK64
);
416 * Format the root volume.
418 if (vol
->vol_no
== RootVolNo
) {
419 hammer_off_t zone_limit
;
424 ondisk
->vol0_next_tid
= createtid();
427 * Set the default zone limit to 4x the size of the
428 * filesystem, allowing the filesystem to survive a
429 * worse-case of 75% fragmentation per zone. The limit
430 * can be expanded at any time if the filesystem is
433 * We do not want to create a huge zone limit for tiny
434 * filesystems because the blockmap could wind up getting
435 * fragmented and eating a large chunk of the disk space.
437 zone_limit
= (hammer_off_t
)total_size
* 4;
438 zone_limit
= (zone_limit
+ HAMMER_BLOCKMAP_LAYER2_MASK
) &
439 ~HAMMER_BLOCKMAP_LAYER2_MASK
;
440 if (zone_limit
< (hammer_off_t
)total_size
||
441 zone_limit
> HAMMER_ZONE_LIMIT
) {
442 zone_limit
= HAMMER_ZONE_LIMIT
;
444 ondisk
->vol0_zone_limit
= zone_limit
;
447 &ondisk
->vol0_blockmap
[HAMMER_ZONE_FREEMAP_INDEX
]);
449 freeblks
= initialize_freemap(vol
);
450 ondisk
->vol0_stat_freebigblocks
= freeblks
;
453 &ondisk
->vol0_blockmap
[HAMMER_ZONE_BTREE_INDEX
],
457 &ondisk
->vol0_blockmap
[HAMMER_ZONE_RECORD_INDEX
],
461 &ondisk
->vol0_blockmap
[HAMMER_ZONE_LARGE_DATA_INDEX
],
462 HAMMER_ZONE_LARGE_DATA
);
464 &ondisk
->vol0_blockmap
[HAMMER_ZONE_SMALL_DATA_INDEX
],
465 HAMMER_ZONE_SMALL_DATA
);
467 format_undomap(ondisk
);
469 ondisk
->vol0_btree_root
= format_root();
470 ++ondisk
->vol0_stat_inodes
; /* root inode */
472 freeblks
= initialize_freemap(vol
);
473 root_vol
= get_volume(RootVolNo
);
474 root_vol
->cache
.modified
= 1;
475 root_vol
->ondisk
->vol0_stat_freebigblocks
+= freeblks
;
476 root_vol
->ondisk
->vol0_stat_bigblocks
+= freeblks
;
477 rel_volume(root_vol
);
482 * Format the root directory.
488 hammer_off_t btree_off
;
489 hammer_off_t data_off
;
490 hammer_tid_t create_tid
;
491 hammer_node_ondisk_t bnode
;
492 struct hammer_inode_data
*idata
;
493 struct buffer_info
*data_buffer
= NULL
;
494 hammer_btree_elm_t elm
;
496 bnode
= alloc_btree_element(&btree_off
);
497 idata
= alloc_data_element(&data_off
, sizeof(*idata
), &data_buffer
);
498 create_tid
= createtid();
501 * Populate the inode data and inode record for the root directory.
503 idata
->version
= HAMMER_INODE_DATA_VERSION
;
505 idata
->mtime
= create_tid
;
506 idata
->obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
511 * Create the root of the B-Tree. The root is a leaf node so we
512 * do not have to worry about boundary elements.
514 bnode
->signature
= HAMMER_BTREE_SIGNATURE_GOOD
;
516 bnode
->type
= HAMMER_BTREE_TYPE_LEAF
;
518 elm
= &bnode
->elms
[0];
519 elm
->leaf
.base
.btype
= HAMMER_BTREE_TYPE_RECORD
;
520 elm
->leaf
.base
.localization
= HAMMER_LOCALIZE_INODE
;
521 elm
->leaf
.base
.obj_id
= HAMMER_OBJID_ROOT
;
522 elm
->leaf
.base
.key
= 0;
523 elm
->leaf
.base
.create_tid
= create_tid
;
524 elm
->leaf
.base
.delete_tid
= 0;
525 elm
->leaf
.base
.rec_type
= HAMMER_RECTYPE_INODE
;
526 elm
->leaf
.base
.obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
528 elm
->leaf
.atime
= create_tid
;
529 elm
->leaf
.data_offset
= data_off
;
530 elm
->leaf
.data_len
= sizeof(*idata
);
531 elm
->leaf
.data_crc
= crc32(idata
, sizeof(*idata
));
533 bnode
->crc
= crc32(&bnode
->crc
+ 1, HAMMER_BTREE_CRCSIZE
);