HAMMER 27/many: Major surgery - change allocation model
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
blobd85d5271ec6bc98cc912333533bc6e996024a976
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.17 2008/02/08 08:30:58 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 static hammer_off_t format_root(void);
44 static void usage(void);
46 int
47 main(int ac, char **av)
49 int i;
50 int ch;
51 u_int32_t status;
52 off_t total;
53 const char *label = NULL;
56 * Sanity check basic filesystem structures. No cookies for us
57 * if it gets broken!
59 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
62 * Generate a filesysem id and lookup the filesystem type
64 uuidgen(&Hammer_FSId, 1);
65 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
66 if (status != uuid_s_ok) {
67 errx(1, "uuids file does not have the DragonFly "
68 "HAMMER filesystem type");
72 * Parse arguments
74 while ((ch = getopt(ac, av, "L:b:m:")) != -1) {
75 switch(ch) {
76 case 'L':
77 label = optarg;
78 break;
79 case 'b':
80 BootAreaSize = getsize(optarg,
81 HAMMER_BUFSIZE,
82 HAMMER_BOOT_MAXBYTES, 2);
83 break;
84 case 'm':
85 MemAreaSize = getsize(optarg,
86 HAMMER_BUFSIZE,
87 HAMMER_MEM_MAXBYTES, 2);
88 break;
89 default:
90 usage();
91 break;
95 if (label == NULL) {
96 fprintf(stderr,
97 "newfs_hammer: A filesystem label must be specified\n");
98 exit(1);
102 * Collect volume information
104 ac -= optind;
105 av += optind;
106 NumVolumes = ac;
107 RootVolNo = 0;
109 total = 0;
110 for (i = 0; i < NumVolumes; ++i) {
111 struct volume_info *vol;
113 vol = setup_volume(i, av[i], 1, O_RDWR);
116 * Load up information on the volume and initialize
117 * its remaining fields.
119 check_volume(vol);
120 total += vol->size;
124 * Calculate defaults for the boot and memory area sizes.
126 if (BootAreaSize == 0) {
127 BootAreaSize = HAMMER_BOOT_NOMBYTES;
128 while (BootAreaSize > total / NumVolumes / 256)
129 BootAreaSize >>= 1;
130 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
131 BootAreaSize = 0;
132 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
133 BootAreaSize = HAMMER_BOOT_MINBYTES;
135 if (MemAreaSize == 0) {
136 MemAreaSize = HAMMER_MEM_NOMBYTES;
137 while (MemAreaSize > total / NumVolumes / 256)
138 MemAreaSize >>= 1;
139 if (MemAreaSize < HAMMER_MEM_MINBYTES)
140 MemAreaSize = 0;
141 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
142 MemAreaSize = HAMMER_MEM_MINBYTES;
145 printf("---------------------------------------------\n");
146 printf("%d volume%s total size %s\n",
147 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
148 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
149 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
150 printf("\n");
153 * Format the volumes.
155 for (i = 0; i < NumVolumes; ++i) {
156 format_volume(get_volume(i), NumVolumes, label);
158 flush_all_volumes();
159 return(0);
162 static
163 void
164 usage(void)
166 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
167 exit(1);
171 * Convert the size in bytes to a human readable string.
173 static const char *
174 sizetostr(off_t size)
176 static char buf[32];
178 if (size < 1024 / 2) {
179 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
180 } else if (size < 1024 * 1024 / 2) {
181 snprintf(buf, sizeof(buf), "%6.2fKB",
182 (double)size / 1024);
183 } else if (size < 1024 * 1024 * 1024LL / 2) {
184 snprintf(buf, sizeof(buf), "%6.2fMB",
185 (double)size / (1024 * 1024));
186 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
187 snprintf(buf, sizeof(buf), "%6.2fGB",
188 (double)size / (1024 * 1024 * 1024LL));
189 } else {
190 snprintf(buf, sizeof(buf), "%6.2fTB",
191 (double)size / (1024 * 1024 * 1024LL * 1024LL));
193 return(buf);
197 * Convert a string to a 64 bit signed integer with various requirements.
199 static int64_t
200 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
202 int64_t val;
203 char *ptr;
205 val = strtoll(str, &ptr, 0);
206 switch(*ptr) {
207 case 't':
208 case 'T':
209 val *= 1024;
210 /* fall through */
211 case 'g':
212 case 'G':
213 val *= 1024;
214 /* fall through */
215 case 'm':
216 case 'M':
217 val *= 1024;
218 /* fall through */
219 case 'k':
220 case 'K':
221 val *= 1024;
222 break;
223 default:
224 errx(1, "Unknown suffix in number '%s'\n", str);
225 /* not reached */
227 if (ptr[1]) {
228 errx(1, "Unknown suffix in number '%s'\n", str);
229 /* not reached */
231 if (val < minval) {
232 errx(1, "Value too small: %s, min is %s\n",
233 str, sizetostr(minval));
234 /* not reached */
236 if (val > maxval) {
237 errx(1, "Value too large: %s, max is %s\n",
238 str, sizetostr(maxval));
239 /* not reached */
241 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
242 errx(1, "Value not power of 2: %s\n", str);
243 /* not reached */
245 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
246 errx(1, "Value not an integral multiple of %dK: %s",
247 HAMMER_BUFSIZE / 1024, str);
248 /* not reached */
250 return(val);
254 * Generate a transaction id
256 static hammer_tid_t
257 createtid(void)
259 static hammer_tid_t lasttid;
260 struct timeval tv;
262 if (lasttid == 0) {
263 gettimeofday(&tv, NULL);
264 lasttid = tv.tv_sec * 1000000000LL +
265 tv.tv_usec * 1000LL;
267 return(lasttid++);
271 * Check basic volume characteristics. HAMMER filesystems use a minimum
272 * of a 16KB filesystem buffer size.
274 static
275 void
276 check_volume(struct volume_info *vol)
278 struct partinfo pinfo;
279 struct stat st;
282 * Get basic information about the volume
284 vol->fd = open(vol->name, O_RDWR);
285 if (vol->fd < 0)
286 err(1, "Unable to open %s R+W", vol->name);
287 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
289 * Allow the formatting of regular filews as HAMMER volumes
291 if (fstat(vol->fd, &st) < 0)
292 err(1, "Unable to stat %s", vol->name);
293 vol->size = st.st_size;
294 vol->type = "REGFILE";
295 } else {
297 * When formatting a block device as a HAMMER volume the
298 * sector size must be compatible. HAMMER uses 16384 byte
299 * filesystem buffers.
301 if (pinfo.reserved_blocks) {
302 errx(1, "HAMMER cannot be placed in a partition "
303 "which overlaps the disklabel or MBR");
305 if (pinfo.media_blksize > 16384 ||
306 16384 % pinfo.media_blksize) {
307 errx(1, "A media sector size of %d is not supported",
308 pinfo.media_blksize);
311 vol->size = pinfo.media_size;
312 vol->type = "DEVICE";
314 printf("Volume %d %s %-15s size %s\n",
315 vol->vol_no, vol->type, vol->name,
316 sizetostr(vol->size));
319 * Reserve space for (future) header junk
321 vol->vol_alloc = HAMMER_BUFSIZE * 16;
325 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
327 static
328 void
329 format_volume(struct volume_info *vol, int nvols, const char *label)
331 struct hammer_volume_ondisk *ondisk;
334 * Initialize basic information in the on-disk volume structure.
336 ondisk = vol->ondisk;
338 ondisk->vol_fsid = Hammer_FSId;
339 ondisk->vol_fstype = Hammer_FSType;
340 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
341 ondisk->vol_no = vol->vol_no;
342 ondisk->vol_count = nvols;
343 ondisk->vol_version = 1;
345 ondisk->vol_bot_beg = vol->vol_alloc;
346 vol->vol_alloc += BootAreaSize;
347 ondisk->vol_mem_beg = vol->vol_alloc;
348 vol->vol_alloc += MemAreaSize;
349 ondisk->vol_buf_beg = vol->vol_alloc;
350 ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
352 if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
353 errx(1, "volume %d %s is too small to hold the volume header",
354 vol->vol_no, vol->name);
357 ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
358 HAMMER_BUFSIZE;
359 ondisk->vol_blocksize = HAMMER_BUFSIZE;
362 * Assign the root volume to volume 0.
364 ondisk->vol_rootvol = 0;
365 ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
366 if (ondisk->vol_no == (int)ondisk->vol_rootvol) {
368 * Create an empty FIFO starting at the first buffer
369 * in volume 0. hammer_off_t must be properly formatted
370 * (see vfs/hammer/hammer_disk.h)
372 ondisk->vol0_fifo_beg = HAMMER_ENCODE_RAW_BUFFER(0, 0);
373 ondisk->vol0_fifo_end = ondisk->vol0_fifo_beg;
374 ondisk->vol0_next_tid = createtid();
375 ondisk->vol0_next_seq = 1;
377 ondisk->vol0_btree_root = format_root();
378 ++ondisk->vol0_stat_inodes; /* root inode */
384 * Format the root directory.
386 static
387 hammer_off_t
388 format_root(void)
390 hammer_off_t btree_off;
391 hammer_off_t rec_off;
392 hammer_node_ondisk_t bnode;
393 hammer_record_ondisk_t rec;
394 struct hammer_inode_data *idata;
395 hammer_btree_elm_t elm;
397 bnode = alloc_btree_element(&btree_off);
398 rec = alloc_record_element(&rec_off, HAMMER_RECTYPE_INODE,
399 sizeof(rec->inode), sizeof(*idata),
400 (void **)&idata);
403 * Populate the inode data and inode record for the root directory.
405 idata->version = HAMMER_INODE_DATA_VERSION;
406 idata->mode = 0755;
408 rec->base.base.btype = HAMMER_BTREE_TYPE_RECORD;
409 rec->base.base.obj_id = HAMMER_OBJID_ROOT;
410 rec->base.base.key = 0;
411 rec->base.base.create_tid = createtid();
412 rec->base.base.delete_tid = 0;
413 rec->base.base.rec_type = HAMMER_RECTYPE_INODE;
414 rec->base.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
415 /* rec->base.data_offset - initialized by alloc_record_element */
416 /* rec->base.data_len - initialized by alloc_record_element */
417 rec->base.head.hdr_crc = crc32(idata, sizeof(*idata));
418 rec->inode.ino_atime = rec->base.base.create_tid;
419 rec->inode.ino_mtime = rec->base.base.create_tid;
420 rec->inode.ino_size = 0;
421 rec->inode.ino_nlinks = 1;
424 * Create the root of the B-Tree. The root is a leaf node so we
425 * do not have to worry about boundary elements.
427 bnode->count = 1;
428 bnode->type = HAMMER_BTREE_TYPE_LEAF;
430 elm = &bnode->elms[0];
431 elm->base = rec->base.base;
432 elm->leaf.rec_offset = rec_off;
433 elm->leaf.data_offset = rec->base.data_off;
434 elm->leaf.data_len = rec->base.data_len;
435 elm->leaf.data_crc = rec->base.head.hdr_crc;
436 return(btree_off);