HAMMER Utilities: sync with 56A
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
blob7bdc7c6aad6cb632ad6c17db386ba75ee3db32f1
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:
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.
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.
34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.31 2008/06/17 04:03:37 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,
43 off_t total_size);
44 static hammer_off_t format_root(void);
45 static void usage(void);
47 int
48 main(int ac, char **av)
50 u_int32_t status;
51 off_t total;
52 int ch;
53 int i;
54 const char *label = NULL;
55 struct volume_info *vol;
58 * Sanity check basic filesystem structures. No cookies for us
59 * if it gets broken!
61 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
62 assert(sizeof(struct hammer_blockmap_layer1) == 32);
63 assert(sizeof(struct hammer_blockmap_layer2) == 16);
66 * Generate a filesysem id and lookup the filesystem type
68 uuidgen(&Hammer_FSId, 1);
69 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
70 if (status != uuid_s_ok) {
71 errx(1, "uuids file does not have the DragonFly "
72 "HAMMER filesystem type");
76 * Parse arguments
78 while ((ch = getopt(ac, av, "L:b:m:u:")) != -1) {
79 switch(ch) {
80 case 'L':
81 label = optarg;
82 break;
83 case 'b':
84 BootAreaSize = getsize(optarg,
85 HAMMER_BUFSIZE,
86 HAMMER_BOOT_MAXBYTES, 2);
87 break;
88 case 'm':
89 MemAreaSize = getsize(optarg,
90 HAMMER_BUFSIZE,
91 HAMMER_MEM_MAXBYTES, 2);
92 break;
93 case 'u':
94 UndoBufferSize = getsize(optarg,
95 HAMMER_LARGEBLOCK_SIZE,
96 HAMMER_LARGEBLOCK_SIZE *
97 HAMMER_UNDO_LAYER2, 2);
98 break;
99 default:
100 usage();
101 break;
105 if (label == NULL) {
106 fprintf(stderr,
107 "newfs_hammer: A filesystem label must be specified\n");
108 exit(1);
112 * Collect volume information
114 ac -= optind;
115 av += optind;
116 NumVolumes = ac;
117 RootVolNo = 0;
119 total = 0;
120 for (i = 0; i < NumVolumes; ++i) {
121 vol = setup_volume(i, av[i], 1, O_RDWR);
124 * Load up information on the volume and initialize
125 * its remaining fields.
127 check_volume(vol);
128 total += vol->size;
132 * Calculate defaults for the boot and memory area sizes.
134 if (BootAreaSize == 0) {
135 BootAreaSize = HAMMER_BOOT_NOMBYTES;
136 while (BootAreaSize > total / NumVolumes / 256)
137 BootAreaSize >>= 1;
138 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
139 BootAreaSize = 0;
140 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
141 BootAreaSize = HAMMER_BOOT_MINBYTES;
143 if (MemAreaSize == 0) {
144 MemAreaSize = HAMMER_MEM_NOMBYTES;
145 while (MemAreaSize > total / NumVolumes / 256)
146 MemAreaSize >>= 1;
147 if (MemAreaSize < HAMMER_MEM_MINBYTES)
148 MemAreaSize = 0;
149 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
150 MemAreaSize = HAMMER_MEM_MINBYTES;
154 * Format the volumes. Format the root volume first so we can
155 * bootstrap the freemap.
157 format_volume(get_volume(RootVolNo), NumVolumes, label, total);
158 for (i = 0; i < NumVolumes; ++i) {
159 if (i != RootVolNo)
160 format_volume(get_volume(i), NumVolumes, label, total);
164 * Pre-size the blockmap layer1/layer2 infrastructure to the zone
165 * limit. If we do this the filesystem does not have to allocate
166 * new layer2 blocks which reduces the chances of the reblocker
167 * having to fallback to an extremely inefficient algorithm.
169 vol = get_volume(RootVolNo);
170 vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
171 vol->cache.modified = 1;
173 printf("---------------------------------------------\n");
174 printf("%d volume%s total size %s\n",
175 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
176 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
177 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
178 printf("undo-buffer-size: %s\n", sizetostr(UndoBufferSize));
179 printf("total-pre-allocated: %s\n",
180 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
181 printf("\n");
183 flush_all_volumes();
184 return(0);
187 static
188 void
189 usage(void)
191 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
192 exit(1);
196 * Convert the size in bytes to a human readable string.
198 static
199 const char *
200 sizetostr(off_t size)
202 static char buf[32];
204 if (size < 1024 / 2) {
205 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
206 } else if (size < 1024 * 1024 / 2) {
207 snprintf(buf, sizeof(buf), "%6.2fKB",
208 (double)size / 1024);
209 } else if (size < 1024 * 1024 * 1024LL / 2) {
210 snprintf(buf, sizeof(buf), "%6.2fMB",
211 (double)size / (1024 * 1024));
212 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
213 snprintf(buf, sizeof(buf), "%6.2fGB",
214 (double)size / (1024 * 1024 * 1024LL));
215 } else {
216 snprintf(buf, sizeof(buf), "%6.2fTB",
217 (double)size / (1024 * 1024 * 1024LL * 1024LL));
219 return(buf);
223 * Convert a string to a 64 bit signed integer with various requirements.
225 static int64_t
226 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
228 int64_t val;
229 char *ptr;
231 val = strtoll(str, &ptr, 0);
232 switch(*ptr) {
233 case 't':
234 case 'T':
235 val *= 1024;
236 /* fall through */
237 case 'g':
238 case 'G':
239 val *= 1024;
240 /* fall through */
241 case 'm':
242 case 'M':
243 val *= 1024;
244 /* fall through */
245 case 'k':
246 case 'K':
247 val *= 1024;
248 break;
249 default:
250 errx(1, "Unknown suffix in number '%s'\n", str);
251 /* not reached */
253 if (ptr[1]) {
254 errx(1, "Unknown suffix in number '%s'\n", str);
255 /* not reached */
257 if (val < minval) {
258 errx(1, "Value too small: %s, min is %s\n",
259 str, sizetostr(minval));
260 /* not reached */
262 if (val > maxval) {
263 errx(1, "Value too large: %s, max is %s\n",
264 str, sizetostr(maxval));
265 /* not reached */
267 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
268 errx(1, "Value not power of 2: %s\n", str);
269 /* not reached */
271 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
272 errx(1, "Value not an integral multiple of %dK: %s",
273 HAMMER_BUFSIZE / 1024, str);
274 /* not reached */
276 return(val);
280 * Generate a transaction id
282 static hammer_tid_t
283 createtid(void)
285 static hammer_tid_t lasttid;
286 struct timeval tv;
288 if (lasttid == 0) {
289 gettimeofday(&tv, NULL);
290 lasttid = tv.tv_sec * 1000000000LL +
291 tv.tv_usec * 1000LL;
293 return(lasttid++);
297 * Check basic volume characteristics. HAMMER filesystems use a minimum
298 * of a 16KB filesystem buffer size.
300 static
301 void
302 check_volume(struct volume_info *vol)
304 struct partinfo pinfo;
305 struct stat st;
308 * Get basic information about the volume
310 vol->fd = open(vol->name, O_RDWR);
311 if (vol->fd < 0)
312 err(1, "Unable to open %s R+W", vol->name);
313 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
315 * Allow the formatting of regular filews as HAMMER volumes
317 if (fstat(vol->fd, &st) < 0)
318 err(1, "Unable to stat %s", vol->name);
319 vol->size = st.st_size;
320 vol->type = "REGFILE";
321 } else {
323 * When formatting a block device as a HAMMER volume the
324 * sector size must be compatible. HAMMER uses 16384 byte
325 * filesystem buffers.
327 if (pinfo.reserved_blocks) {
328 errx(1, "HAMMER cannot be placed in a partition "
329 "which overlaps the disklabel or MBR");
331 if (pinfo.media_blksize > 16384 ||
332 16384 % pinfo.media_blksize) {
333 errx(1, "A media sector size of %d is not supported",
334 pinfo.media_blksize);
337 vol->size = pinfo.media_size;
338 vol->type = "DEVICE";
340 printf("Volume %d %s %-15s size %s\n",
341 vol->vol_no, vol->type, vol->name,
342 sizetostr(vol->size));
345 * Reserve space for (future) header junk, setup our poor-man's
346 * bigblock allocator.
348 vol->vol_alloc = HAMMER_BUFSIZE * 16;
352 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
354 static
355 void
356 format_volume(struct volume_info *vol, int nvols, const char *label,
357 off_t total_size __unused)
359 struct volume_info *root_vol;
360 struct hammer_volume_ondisk *ondisk;
361 int64_t freeblks;
362 int i;
365 * Initialize basic information in the on-disk volume structure.
367 ondisk = vol->ondisk;
369 ondisk->vol_fsid = Hammer_FSId;
370 ondisk->vol_fstype = Hammer_FSType;
371 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
372 ondisk->vol_no = vol->vol_no;
373 ondisk->vol_count = nvols;
374 ondisk->vol_version = 1;
376 ondisk->vol_bot_beg = vol->vol_alloc;
377 vol->vol_alloc += BootAreaSize;
378 ondisk->vol_mem_beg = vol->vol_alloc;
379 vol->vol_alloc += MemAreaSize;
382 * The remaining area is the zone 2 buffer allocation area. These
383 * buffers
385 ondisk->vol_buf_beg = vol->vol_alloc;
386 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
388 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
389 errx(1, "volume %d %s is too small to hold the volume header",
390 vol->vol_no, vol->name);
393 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
394 HAMMER_BUFSIZE;
395 ondisk->vol_blocksize = HAMMER_BUFSIZE;
397 ondisk->vol_rootvol = RootVolNo;
398 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
400 vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
401 vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
404 * Format the root volume.
406 if (vol->vol_no == RootVolNo) {
408 * Starting TID
410 ondisk->vol0_next_tid = createtid();
412 format_freemap(vol,
413 &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
415 freeblks = initialize_freemap(vol);
416 ondisk->vol0_stat_freebigblocks = freeblks;
418 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
419 format_blockmap(&ondisk->vol0_blockmap[i],
420 HAMMER_ZONE_ENCODE(i, 0));
422 format_undomap(ondisk);
424 ondisk->vol0_btree_root = format_root();
425 ++ondisk->vol0_stat_inodes; /* root inode */
426 } else {
427 freeblks = initialize_freemap(vol);
428 root_vol = get_volume(RootVolNo);
429 root_vol->cache.modified = 1;
430 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
431 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
432 rel_volume(root_vol);
437 * Format the root directory.
439 static
440 hammer_off_t
441 format_root(void)
443 hammer_off_t btree_off;
444 hammer_off_t data_off;
445 hammer_tid_t create_tid;
446 hammer_node_ondisk_t bnode;
447 struct hammer_inode_data *idata;
448 struct buffer_info *data_buffer = NULL;
449 hammer_btree_elm_t elm;
451 bnode = alloc_btree_element(&btree_off);
452 idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer);
453 create_tid = createtid();
456 * Populate the inode data and inode record for the root directory.
458 idata->version = HAMMER_INODE_DATA_VERSION;
459 idata->mode = 0755;
460 idata->mtime = create_tid;
461 idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
462 idata->size = 0;
463 idata->nlinks = 1;
466 * Create the root of the B-Tree. The root is a leaf node so we
467 * do not have to worry about boundary elements.
469 bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
470 bnode->count = 1;
471 bnode->type = HAMMER_BTREE_TYPE_LEAF;
473 elm = &bnode->elms[0];
474 elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
475 elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
476 elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
477 elm->leaf.base.key = 0;
478 elm->leaf.base.create_tid = create_tid;
479 elm->leaf.base.delete_tid = 0;
480 elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
481 elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
483 elm->leaf.atime = create_tid;
484 elm->leaf.data_offset = data_off;
485 elm->leaf.data_len = sizeof(*idata);
486 elm->leaf.data_crc = crc32(idata, sizeof(*idata));
488 bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
490 return(btree_off);