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.39.2.4 2008/07/25 03:18:44 swildner 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(const char *label
);
45 static u_int64_t
nowtime(void);
46 static void usage(void);
48 static int ForceOpt
= 0;
50 #define GIG (1024LL*1024*1024)
53 main(int ac
, char **av
)
59 const char *label
= NULL
;
60 struct volume_info
*vol
;
64 * Sanity check basic filesystem structures. No cookies for us
67 assert(sizeof(struct hammer_volume_ondisk
) <= HAMMER_BUFSIZE
);
68 assert(sizeof(struct hammer_blockmap_layer1
) == 32);
69 assert(sizeof(struct hammer_blockmap_layer2
) == 16);
72 * Generate a filesysem id and lookup the filesystem type
74 uuidgen(&Hammer_FSId
, 1);
75 uuid_name_lookup(&Hammer_FSType
, "DragonFly HAMMER", &status
);
76 if (status
!= uuid_s_ok
) {
77 errx(1, "uuids file does not have the DragonFly "
78 "HAMMER filesystem type");
84 while ((ch
= getopt(ac
, av
, "fL:b:m:u:")) != -1) {
93 BootAreaSize
= getsize(optarg
,
95 HAMMER_BOOT_MAXBYTES
, 2);
98 MemAreaSize
= getsize(optarg
,
100 HAMMER_MEM_MAXBYTES
, 2);
103 UndoBufferSize
= getsize(optarg
,
104 HAMMER_LARGEBLOCK_SIZE
,
105 HAMMER_LARGEBLOCK_SIZE
*
106 HAMMER_UNDO_LAYER2
, 2);
107 if (UndoBufferSize
< 100*1024*1024 && ForceOpt
== 0)
108 errx(1, "The minimum UNDO fifo size is 100M\n");
109 if (UndoBufferSize
< 100*1024*1024) {
111 "WARNING: you have specified an UNDO "
112 "FIFO size less than 100M, which may\n"
113 "lead to VFS panics.\n");
124 "newfs_hammer: A filesystem label must be specified\n");
129 * Collect volume information
136 if (NumVolumes
== 0) {
138 "newfs_hammer: You must specify at least one special file (volume)\n");
143 for (i
= 0; i
< NumVolumes
; ++i
) {
144 vol
= setup_volume(i
, av
[i
], 1, O_RDWR
);
147 * Load up information on the volume and initialize
148 * its remaining fields.
155 * Calculate defaults for the boot and memory area sizes.
157 if (BootAreaSize
== 0) {
158 BootAreaSize
= HAMMER_BOOT_NOMBYTES
;
159 while (BootAreaSize
> total
/ NumVolumes
/ 256)
161 if (BootAreaSize
< HAMMER_BOOT_MINBYTES
)
163 } else if (BootAreaSize
< HAMMER_BOOT_MINBYTES
) {
164 BootAreaSize
= HAMMER_BOOT_MINBYTES
;
166 if (MemAreaSize
== 0) {
167 MemAreaSize
= HAMMER_MEM_NOMBYTES
;
168 while (MemAreaSize
> total
/ NumVolumes
/ 256)
170 if (MemAreaSize
< HAMMER_MEM_MINBYTES
)
172 } else if (MemAreaSize
< HAMMER_MEM_MINBYTES
) {
173 MemAreaSize
= HAMMER_MEM_MINBYTES
;
177 * Format the volumes. Format the root volume first so we can
178 * bootstrap the freemap.
180 format_volume(get_volume(RootVolNo
), NumVolumes
, label
, total
);
181 for (i
= 0; i
< NumVolumes
; ++i
) {
183 format_volume(get_volume(i
), NumVolumes
, label
, total
);
187 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
188 * limit. If we do this the filesystem does not have to allocate
189 * new layer2 blocks which reduces the chances of the reblocker
190 * having to fallback to an extremely inefficient algorithm.
192 vol
= get_volume(RootVolNo
);
193 vol
->ondisk
->vol0_stat_bigblocks
= vol
->ondisk
->vol0_stat_freebigblocks
;
194 vol
->cache
.modified
= 1;
195 uuid_to_string(&Hammer_FSId
, &fsidstr
, &status
);
197 printf("---------------------------------------------\n");
198 printf("%d volume%s total size %s\n",
199 NumVolumes
, (NumVolumes
== 1 ? "" : "s"), sizetostr(total
));
200 printf("boot-area-size: %s\n", sizetostr(BootAreaSize
));
201 printf("memory-log-size: %s\n", sizetostr(MemAreaSize
));
202 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize
));
203 printf("total-pre-allocated: %s\n",
204 sizetostr(vol
->vol_free_off
& HAMMER_OFF_SHORT_MASK
));
205 printf("fsid: %s\n", fsidstr
);
207 printf("NOTE: Please remember to set up a cron job to prune and\n"
208 "reblock the filesystem regularly, see 'man hammer' for\n"
209 "more information.\n");
210 if (total
< 50*GIG
) {
211 printf("\nWARNING: HAMMER filesystems less than 50G are "
213 "You may have to run 'hammer prune-everything' and "
215 "quite often, even if using a nohistory mount.\n");
226 "newfs_hammer -L label [-b bootsize] [-m savesize] [ -u undosize] "
233 * Convert the size in bytes to a human readable string.
237 sizetostr(off_t size
)
241 if (size
< 1024 / 2) {
242 snprintf(buf
, sizeof(buf
), "%6.2f", (double)size
);
243 } else if (size
< 1024 * 1024 / 2) {
244 snprintf(buf
, sizeof(buf
), "%6.2fKB",
245 (double)size
/ 1024);
246 } else if (size
< 1024 * 1024 * 1024LL / 2) {
247 snprintf(buf
, sizeof(buf
), "%6.2fMB",
248 (double)size
/ (1024 * 1024));
249 } else if (size
< 1024 * 1024 * 1024LL * 1024LL / 2) {
250 snprintf(buf
, sizeof(buf
), "%6.2fGB",
251 (double)size
/ (1024 * 1024 * 1024LL));
253 snprintf(buf
, sizeof(buf
), "%6.2fTB",
254 (double)size
/ (1024 * 1024 * 1024LL * 1024LL));
260 * Convert a string to a 64 bit signed integer with various requirements.
263 getsize(const char *str
, int64_t minval
, int64_t maxval
, int powerof2
)
268 val
= strtoll(str
, &ptr
, 0);
287 errx(1, "Unknown suffix in number '%s'\n", str
);
291 errx(1, "Unknown suffix in number '%s'\n", str
);
295 errx(1, "Value too small: %s, min is %s\n",
296 str
, sizetostr(minval
));
300 errx(1, "Value too large: %s, max is %s\n",
301 str
, sizetostr(maxval
));
304 if ((powerof2
& 1) && (val
^ (val
- 1)) != ((val
<< 1) - 1)) {
305 errx(1, "Value not power of 2: %s\n", str
);
308 if ((powerof2
& 2) && (val
& HAMMER_BUFMASK
)) {
309 errx(1, "Value not an integral multiple of %dK: %s",
310 HAMMER_BUFSIZE
/ 1024, str
);
317 * Generate a transaction id. Transaction ids are no longer time-based.
318 * Put the nail in the coffin by not making the first one time-based.
320 * We could start at 1 here but start at 2^32 to reserve a small domain for
321 * possible future use.
326 static hammer_tid_t lasttid
;
329 lasttid
= 0x0000000100000000ULL
;
339 gettimeofday(&tv
, NULL
);
340 xtime
= tv
.tv_sec
* 1000000LL + tv
.tv_usec
;
345 * Check basic volume characteristics. HAMMER filesystems use a minimum
346 * of a 16KB filesystem buffer size.
350 check_volume(struct volume_info
*vol
)
352 struct partinfo pinfo
;
356 * Get basic information about the volume
358 vol
->fd
= open(vol
->name
, O_RDWR
);
360 err(1, "Unable to open %s R+W", vol
->name
);
361 if (ioctl(vol
->fd
, DIOCGPART
, &pinfo
) < 0) {
363 * Allow the formatting of regular files as HAMMER volumes
365 if (fstat(vol
->fd
, &st
) < 0)
366 err(1, "Unable to stat %s", vol
->name
);
367 vol
->size
= st
.st_size
;
368 vol
->type
= "REGFILE";
371 * When formatting a block device as a HAMMER volume the
372 * sector size must be compatible. HAMMER uses 16384 byte
373 * filesystem buffers.
375 if (pinfo
.reserved_blocks
) {
376 errx(1, "HAMMER cannot be placed in a partition "
377 "which overlaps the disklabel or MBR");
379 if (pinfo
.media_blksize
> 16384 ||
380 16384 % pinfo
.media_blksize
) {
381 errx(1, "A media sector size of %d is not supported",
382 pinfo
.media_blksize
);
385 vol
->size
= pinfo
.media_size
;
386 vol
->type
= "DEVICE";
388 printf("Volume %d %s %-15s size %s\n",
389 vol
->vol_no
, vol
->type
, vol
->name
,
390 sizetostr(vol
->size
));
393 * Reserve space for (future) header junk, setup our poor-man's
394 * bigblock allocator.
396 vol
->vol_alloc
= HAMMER_BUFSIZE
* 16;
400 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
404 format_volume(struct volume_info
*vol
, int nvols
, const char *label
,
405 off_t total_size __unused
)
407 struct volume_info
*root_vol
;
408 struct hammer_volume_ondisk
*ondisk
;
414 * Initialize basic information in the on-disk volume structure.
416 ondisk
= vol
->ondisk
;
418 ondisk
->vol_fsid
= Hammer_FSId
;
419 ondisk
->vol_fstype
= Hammer_FSType
;
420 snprintf(ondisk
->vol_name
, sizeof(ondisk
->vol_name
), "%s", label
);
421 ondisk
->vol_no
= vol
->vol_no
;
422 ondisk
->vol_count
= nvols
;
423 ondisk
->vol_version
= 1;
425 ondisk
->vol_bot_beg
= vol
->vol_alloc
;
426 vol
->vol_alloc
+= BootAreaSize
;
427 ondisk
->vol_mem_beg
= vol
->vol_alloc
;
428 vol
->vol_alloc
+= MemAreaSize
;
431 * The remaining area is the zone 2 buffer allocation area. These
434 ondisk
->vol_buf_beg
= vol
->vol_alloc
;
435 ondisk
->vol_buf_end
= vol
->size
& ~(int64_t)HAMMER_BUFMASK
;
437 if (ondisk
->vol_buf_end
< ondisk
->vol_buf_beg
) {
438 errx(1, "volume %d %s is too small to hold the volume header",
439 vol
->vol_no
, vol
->name
);
442 ondisk
->vol_nblocks
= (ondisk
->vol_buf_end
- ondisk
->vol_buf_beg
) /
444 ondisk
->vol_blocksize
= HAMMER_BUFSIZE
;
446 ondisk
->vol_rootvol
= RootVolNo
;
447 ondisk
->vol_signature
= HAMMER_FSBUF_VOLUME
;
449 vol
->vol_free_off
= HAMMER_ENCODE_RAW_BUFFER(vol
->vol_no
, 0);
450 vol
->vol_free_end
= HAMMER_ENCODE_RAW_BUFFER(vol
->vol_no
, (ondisk
->vol_buf_end
- ondisk
->vol_buf_beg
) & ~HAMMER_LARGEBLOCK_MASK64
);
453 * Format the root volume.
455 if (vol
->vol_no
== RootVolNo
) {
459 ondisk
->vol0_next_tid
= createtid();
462 &ondisk
->vol0_blockmap
[HAMMER_ZONE_FREEMAP_INDEX
]);
464 freeblks
= initialize_freemap(vol
);
465 ondisk
->vol0_stat_freebigblocks
= freeblks
;
467 freebytes
= freeblks
* HAMMER_LARGEBLOCK_SIZE64
;
468 if (freebytes
< 1*GIG
&& ForceOpt
== 0) {
469 errx(1, "Cannot create a HAMMER filesystem less than "
470 "1GB unless you use -f. HAMMER filesystems\n"
471 "less than 50G are not recommended\n");
474 for (i
= 8; i
< HAMMER_MAX_ZONES
; ++i
) {
475 format_blockmap(&ondisk
->vol0_blockmap
[i
],
476 HAMMER_ZONE_ENCODE(i
, 0));
478 format_undomap(ondisk
);
480 ondisk
->vol0_btree_root
= format_root(label
);
481 ++ondisk
->vol0_stat_inodes
; /* root inode */
483 freeblks
= initialize_freemap(vol
);
484 root_vol
= get_volume(RootVolNo
);
485 root_vol
->cache
.modified
= 1;
486 root_vol
->ondisk
->vol0_stat_freebigblocks
+= freeblks
;
487 root_vol
->ondisk
->vol0_stat_bigblocks
+= freeblks
;
488 rel_volume(root_vol
);
493 * Format the root directory.
497 format_root(const char *label
)
499 hammer_off_t btree_off
;
500 hammer_off_t pfsd_off
;
501 hammer_off_t data_off
;
502 hammer_tid_t create_tid
;
503 hammer_node_ondisk_t bnode
;
504 struct hammer_inode_data
*idata
;
505 hammer_pseudofs_data_t pfsd
;
506 struct buffer_info
*data_buffer1
= NULL
;
507 struct buffer_info
*data_buffer2
= NULL
;
508 hammer_btree_elm_t elm
;
511 bnode
= alloc_btree_element(&btree_off
);
512 idata
= alloc_data_element(&data_off
, sizeof(*idata
), &data_buffer1
);
513 pfsd
= alloc_data_element(&pfsd_off
, sizeof(*pfsd
), &data_buffer2
);
514 create_tid
= createtid();
518 * Populate the inode data and inode record for the root directory.
520 idata
->version
= HAMMER_INODE_DATA_VERSION
;
522 idata
->ctime
= xtime
;
523 idata
->mtime
= xtime
;
524 idata
->atime
= xtime
;
525 idata
->obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
529 pfsd
->sync_low_tid
= 1;
530 pfsd
->sync_beg_tid
= 0;
531 pfsd
->sync_end_tid
= 0; /* overriden by vol0_next_tid on pfs0 */
532 pfsd
->shared_uuid
= Hammer_FSId
;
533 pfsd
->unique_uuid
= Hammer_FSId
;
534 pfsd
->reserved01
= 0;
535 pfsd
->mirror_flags
= 0;
536 snprintf(pfsd
->label
, sizeof(pfsd
->label
), "%s", label
);
539 * Create the root of the B-Tree. The root is a leaf node so we
540 * do not have to worry about boundary elements.
542 bnode
->signature
= HAMMER_BTREE_SIGNATURE_GOOD
;
544 bnode
->type
= HAMMER_BTREE_TYPE_LEAF
;
546 elm
= &bnode
->elms
[0];
547 elm
->leaf
.base
.btype
= HAMMER_BTREE_TYPE_RECORD
;
548 elm
->leaf
.base
.localization
= HAMMER_LOCALIZE_INODE
;
549 elm
->leaf
.base
.obj_id
= HAMMER_OBJID_ROOT
;
550 elm
->leaf
.base
.key
= 0;
551 elm
->leaf
.base
.create_tid
= create_tid
;
552 elm
->leaf
.base
.delete_tid
= 0;
553 elm
->leaf
.base
.rec_type
= HAMMER_RECTYPE_INODE
;
554 elm
->leaf
.base
.obj_type
= HAMMER_OBJTYPE_DIRECTORY
;
555 elm
->leaf
.create_ts
= (u_int32_t
)time(NULL
);
557 elm
->leaf
.data_offset
= data_off
;
558 elm
->leaf
.data_len
= sizeof(*idata
);
559 elm
->leaf
.data_crc
= crc32(idata
, HAMMER_INODE_CRCSIZE
);
561 elm
= &bnode
->elms
[1];
562 elm
->leaf
.base
.btype
= HAMMER_BTREE_TYPE_RECORD
;
563 elm
->leaf
.base
.localization
= HAMMER_LOCALIZE_MISC
;
564 elm
->leaf
.base
.obj_id
= HAMMER_OBJID_ROOT
;
565 elm
->leaf
.base
.key
= 0;
566 elm
->leaf
.base
.create_tid
= create_tid
;
567 elm
->leaf
.base
.delete_tid
= 0;
568 elm
->leaf
.base
.rec_type
= HAMMER_RECTYPE_PFS
;
569 elm
->leaf
.base
.obj_type
= 0;
570 elm
->leaf
.create_ts
= (u_int32_t
)time(NULL
);
572 elm
->leaf
.data_offset
= pfsd_off
;
573 elm
->leaf
.data_len
= sizeof(*pfsd
);
574 elm
->leaf
.data_crc
= crc32(pfsd
, sizeof(*pfsd
));
576 bnode
->crc
= crc32(&bnode
->crc
+ 1, HAMMER_BTREE_CRCSIZE
);