Update newfs_hammer doc:
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
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.40 2008/07/16 00:58:22 thomas Exp $
35  */
36
37 #include "newfs_hammer.h"
38
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(const char *label);
45 static u_int64_t nowtime(void);
46 static void usage(void);
47
48 int
49 main(int ac, char **av)
50 {
51         u_int32_t status;
52         off_t total;
53         int ch;
54         int i;
55         const char *label = NULL;
56         struct volume_info *vol;
57         char *fsidstr;
58
59         /*
60          * Sanity check basic filesystem structures.  No cookies for us
61          * if it gets broken!
62          */
63         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
64         assert(sizeof(struct hammer_blockmap_layer1) == 32);
65         assert(sizeof(struct hammer_blockmap_layer2) == 16);
66
67         /*
68          * Generate a filesysem id and lookup the filesystem type
69          */
70         uuidgen(&Hammer_FSId, 1);
71         uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
72         if (status != uuid_s_ok) {
73                 errx(1, "uuids file does not have the DragonFly "
74                         "HAMMER filesystem type");
75         }
76
77         /*
78          * Parse arguments
79          */
80         while ((ch = getopt(ac, av, "L:b:m:u:")) != -1) {
81                 switch(ch) {
82                 case 'L':
83                         label = optarg;
84                         break;
85                 case 'b':
86                         BootAreaSize = getsize(optarg,
87                                          HAMMER_BUFSIZE,
88                                          HAMMER_BOOT_MAXBYTES, 2);
89                         break;
90                 case 'm':
91                         MemAreaSize = getsize(optarg,
92                                          HAMMER_BUFSIZE,
93                                          HAMMER_MEM_MAXBYTES, 2);
94                         break;
95                 case 'u':
96                         UndoBufferSize = getsize(optarg,
97                                          HAMMER_LARGEBLOCK_SIZE,
98                                          HAMMER_LARGEBLOCK_SIZE *
99                                          HAMMER_UNDO_LAYER2, 2);
100                         break;
101                 default:
102                         usage();
103                         break;
104                 }
105         }
106
107         if (label == NULL) {
108                 fprintf(stderr,
109                         "newfs_hammer: A filesystem label must be specified\n");
110                 exit(1);
111         }
112
113         /*
114          * Collect volume information
115          */
116         ac -= optind;
117         av += optind;
118         NumVolumes = ac;
119         RootVolNo = 0;
120
121         if (NumVolumes == 0) {
122                 fprintf(stderr,
123                         "newfs_hammer: You must specify at least one special file (volume)\n");
124                 exit(1);
125         }
126
127         total = 0;
128         for (i = 0; i < NumVolumes; ++i) {
129                 vol = setup_volume(i, av[i], 1, O_RDWR);
130
131                 /*
132                  * Load up information on the volume and initialize
133                  * its remaining fields.
134                  */
135                 check_volume(vol);
136                 total += vol->size;
137         }
138
139         /*
140          * Calculate defaults for the boot and memory area sizes.
141          */
142         if (BootAreaSize == 0) {
143                 BootAreaSize = HAMMER_BOOT_NOMBYTES;
144                 while (BootAreaSize > total / NumVolumes / 256)
145                         BootAreaSize >>= 1;
146                 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
147                         BootAreaSize = 0;
148         } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
149                 BootAreaSize = HAMMER_BOOT_MINBYTES;
150         }
151         if (MemAreaSize == 0) {
152                 MemAreaSize = HAMMER_MEM_NOMBYTES;
153                 while (MemAreaSize > total / NumVolumes / 256)
154                         MemAreaSize >>= 1;
155                 if (MemAreaSize < HAMMER_MEM_MINBYTES)
156                         MemAreaSize = 0;
157         } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
158                 MemAreaSize = HAMMER_MEM_MINBYTES;
159         }
160
161         /*
162          * Format the volumes.  Format the root volume first so we can
163          * bootstrap the freemap.
164          */
165         format_volume(get_volume(RootVolNo), NumVolumes, label, total);
166         for (i = 0; i < NumVolumes; ++i) {
167                 if (i != RootVolNo)
168                         format_volume(get_volume(i), NumVolumes, label, total);
169         }
170
171         /*
172          * Pre-size the blockmap layer1/layer2 infrastructure to the zone
173          * limit.  If we do this the filesystem does not have to allocate
174          * new layer2 blocks which reduces the chances of the reblocker
175          * having to fallback to an extremely inefficient algorithm.
176          */
177         vol = get_volume(RootVolNo);
178         vol->ondisk->vol0_stat_bigblocks = vol->ondisk->vol0_stat_freebigblocks;
179         vol->cache.modified = 1;
180         uuid_to_string(&Hammer_FSId, &fsidstr, &status);
181
182         printf("---------------------------------------------\n");
183         printf("%d volume%s total size %s\n",
184                 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
185         printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
186         printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
187         printf("undo-buffer-size:    %s\n", sizetostr(UndoBufferSize));
188         printf("total-pre-allocated: %s\n",
189                 sizetostr(vol->vol_free_off & HAMMER_OFF_SHORT_MASK));
190         printf("fsid:                %s\n", fsidstr);
191         printf("\n");
192
193         flush_all_volumes();
194         return(0);
195 }
196
197 static
198 void
199 usage(void)
200 {
201         fprintf(stderr,
202                 "newfs_hammer -L label [-b bootsize] [-m savesize] [ -u undosize] "
203                         "special ...\n"
204         );
205         exit(1);
206 }
207
208 /*
209  * Convert the size in bytes to a human readable string.
210  */
211 static
212 const char *
213 sizetostr(off_t size)
214 {
215         static char buf[32];
216
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));
228         } else {
229                 snprintf(buf, sizeof(buf), "%6.2fTB",
230                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
231         }
232         return(buf);
233 }
234
235 /*
236  * Convert a string to a 64 bit signed integer with various requirements.
237  */
238 static int64_t
239 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
240 {
241         int64_t val;
242         char *ptr;
243
244         val = strtoll(str, &ptr, 0);
245         switch(*ptr) {
246         case 't':
247         case 'T':
248                 val *= 1024;
249                 /* fall through */
250         case 'g':
251         case 'G':
252                 val *= 1024;
253                 /* fall through */
254         case 'm':
255         case 'M':
256                 val *= 1024;
257                 /* fall through */
258         case 'k':
259         case 'K':
260                 val *= 1024;
261                 break;
262         default:
263                 errx(1, "Unknown suffix in number '%s'\n", str);
264                 /* not reached */
265         }
266         if (ptr[1]) {
267                 errx(1, "Unknown suffix in number '%s'\n", str);
268                 /* not reached */
269         }
270         if (val < minval) {
271                 errx(1, "Value too small: %s, min is %s\n",
272                      str, sizetostr(minval));
273                 /* not reached */
274         }
275         if (val > maxval) {
276                 errx(1, "Value too large: %s, max is %s\n",
277                      str, sizetostr(maxval));
278                 /* not reached */
279         }
280         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
281                 errx(1, "Value not power of 2: %s\n", str);
282                 /* not reached */
283         }
284         if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
285                 errx(1, "Value not an integral multiple of %dK: %s", 
286                      HAMMER_BUFSIZE / 1024, str);
287                 /* not reached */
288         }
289         return(val);
290 }
291
292 /*
293  * Generate a transaction id.  Transaction ids are no longer time-based.
294  * Put the nail in the coffin by not making the first one time-based.
295  *
296  * We could start at 1 here but start at 2^32 to reserve a small domain for
297  * possible future use.
298  */
299 static hammer_tid_t
300 createtid(void)
301 {
302         static hammer_tid_t lasttid;
303
304         if (lasttid == 0)
305                 lasttid = 0x0000000100000000ULL;
306         return(lasttid++);
307 }
308
309 static u_int64_t
310 nowtime(void)
311 {
312         struct timeval tv;
313         u_int64_t xtime;
314
315         gettimeofday(&tv, NULL);
316         xtime = tv.tv_sec * 1000000LL + tv.tv_usec;
317         return(xtime);
318 }
319
320 /*
321  * Check basic volume characteristics.  HAMMER filesystems use a minimum
322  * of a 16KB filesystem buffer size.
323  */
324 static
325 void
326 check_volume(struct volume_info *vol)
327 {
328         struct partinfo pinfo;
329         struct stat st;
330
331         /*
332          * Get basic information about the volume
333          */
334         vol->fd = open(vol->name, O_RDWR);
335         if (vol->fd < 0)
336                 err(1, "Unable to open %s R+W", vol->name);
337         if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
338                 /*
339                  * Allow the formatting of regular files as HAMMER volumes
340                  */
341                 if (fstat(vol->fd, &st) < 0)
342                         err(1, "Unable to stat %s", vol->name);
343                 vol->size = st.st_size;
344                 vol->type = "REGFILE";
345         } else {
346                 /*
347                  * When formatting a block device as a HAMMER volume the
348                  * sector size must be compatible.  HAMMER uses 16384 byte
349                  * filesystem buffers.
350                  */
351                 if (pinfo.reserved_blocks) {
352                         errx(1, "HAMMER cannot be placed in a partition "
353                                 "which overlaps the disklabel or MBR");
354                 }
355                 if (pinfo.media_blksize > 16384 ||
356                     16384 % pinfo.media_blksize) {
357                         errx(1, "A media sector size of %d is not supported",
358                              pinfo.media_blksize);
359                 }
360
361                 vol->size = pinfo.media_size;
362                 vol->type = "DEVICE";
363         }
364         printf("Volume %d %s %-15s size %s\n",
365                vol->vol_no, vol->type, vol->name,
366                sizetostr(vol->size));
367
368         /*
369          * Reserve space for (future) header junk, setup our poor-man's
370          * bigblock allocator.
371          */
372         vol->vol_alloc = HAMMER_BUFSIZE * 16;
373 }
374
375 /*
376  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
377  */
378 static
379 void
380 format_volume(struct volume_info *vol, int nvols, const char *label,
381               off_t total_size __unused)
382 {
383         struct volume_info *root_vol;
384         struct hammer_volume_ondisk *ondisk;
385         int64_t freeblks;
386         int i;
387
388         /*
389          * Initialize basic information in the on-disk volume structure.
390          */
391         ondisk = vol->ondisk;
392
393         ondisk->vol_fsid = Hammer_FSId;
394         ondisk->vol_fstype = Hammer_FSType;
395         snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
396         ondisk->vol_no = vol->vol_no;
397         ondisk->vol_count = nvols;
398         ondisk->vol_version = 1;
399
400         ondisk->vol_bot_beg = vol->vol_alloc;
401         vol->vol_alloc += BootAreaSize;
402         ondisk->vol_mem_beg = vol->vol_alloc;
403         vol->vol_alloc += MemAreaSize;
404
405         /*
406          * The remaining area is the zone 2 buffer allocation area.  These
407          * buffers
408          */
409         ondisk->vol_buf_beg = vol->vol_alloc;
410         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
411
412         if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
413                 errx(1, "volume %d %s is too small to hold the volume header",
414                      vol->vol_no, vol->name);
415         }
416
417         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
418                               HAMMER_BUFSIZE;
419         ondisk->vol_blocksize = HAMMER_BUFSIZE;
420
421         ondisk->vol_rootvol = RootVolNo;
422         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
423
424         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
425         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
426
427         /*
428          * Format the root volume.
429          */
430         if (vol->vol_no == RootVolNo) {
431                 /*
432                  * Starting TID
433                  */
434                 ondisk->vol0_next_tid = createtid();
435
436                 format_freemap(vol,
437                         &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
438
439                 freeblks = initialize_freemap(vol);
440                 ondisk->vol0_stat_freebigblocks = freeblks;
441
442                 for (i = 8; i < HAMMER_MAX_ZONES; ++i) {
443                         format_blockmap(&ondisk->vol0_blockmap[i],
444                                         HAMMER_ZONE_ENCODE(i, 0));
445                 }
446                 format_undomap(ondisk);
447
448                 ondisk->vol0_btree_root = format_root(label);
449                 ++ondisk->vol0_stat_inodes;     /* root inode */
450         } else {
451                 freeblks = initialize_freemap(vol);
452                 root_vol = get_volume(RootVolNo);
453                 root_vol->cache.modified = 1;
454                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
455                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
456                 rel_volume(root_vol);
457         }
458 }
459
460 /*
461  * Format the root directory.
462  */
463 static
464 hammer_off_t
465 format_root(const char *label)
466 {
467         hammer_off_t btree_off;
468         hammer_off_t pfsd_off;
469         hammer_off_t data_off;
470         hammer_tid_t create_tid;
471         hammer_node_ondisk_t bnode;
472         struct hammer_inode_data *idata;
473         hammer_pseudofs_data_t pfsd;
474         struct buffer_info *data_buffer1 = NULL;
475         struct buffer_info *data_buffer2 = NULL;
476         hammer_btree_elm_t elm;
477         u_int64_t xtime;
478
479         bnode = alloc_btree_element(&btree_off);
480         idata = alloc_data_element(&data_off, sizeof(*idata), &data_buffer1);
481         pfsd = alloc_data_element(&pfsd_off, sizeof(*pfsd), &data_buffer2);
482         create_tid = createtid();
483         xtime = nowtime();
484
485         /*
486          * Populate the inode data and inode record for the root directory.
487          */
488         idata->version = HAMMER_INODE_DATA_VERSION;
489         idata->mode = 0755;
490         idata->ctime = xtime;
491         idata->mtime = xtime;
492         idata->atime = xtime;
493         idata->obj_type = HAMMER_OBJTYPE_DIRECTORY;
494         idata->size = 0;
495         idata->nlinks = 1;
496
497         pfsd->sync_low_tid = 1;
498         pfsd->sync_beg_tid = 0;
499         pfsd->sync_end_tid = 0; /* overriden by vol0_next_tid on pfs0 */
500         pfsd->shared_uuid = Hammer_FSId;
501         pfsd->unique_uuid = Hammer_FSId;
502         pfsd->master_id = 0;
503         pfsd->mirror_flags = 0;
504         snprintf(pfsd->label, sizeof(pfsd->label), "%s", label);
505
506         /*
507          * Create the root of the B-Tree.  The root is a leaf node so we
508          * do not have to worry about boundary elements.
509          */
510         bnode->signature = HAMMER_BTREE_SIGNATURE_GOOD;
511         bnode->count = 2;
512         bnode->type = HAMMER_BTREE_TYPE_LEAF;
513
514         elm = &bnode->elms[0];
515         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
516         elm->leaf.base.localization = HAMMER_LOCALIZE_INODE;
517         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
518         elm->leaf.base.key = 0;
519         elm->leaf.base.create_tid = create_tid;
520         elm->leaf.base.delete_tid = 0;
521         elm->leaf.base.rec_type = HAMMER_RECTYPE_INODE;
522         elm->leaf.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
523         elm->leaf.create_ts = (u_int32_t)time(NULL);
524
525         elm->leaf.data_offset = data_off;
526         elm->leaf.data_len = sizeof(*idata);
527         elm->leaf.data_crc = crc32(idata, HAMMER_INODE_CRCSIZE);
528
529         elm = &bnode->elms[1];
530         elm->leaf.base.btype = HAMMER_BTREE_TYPE_RECORD;
531         elm->leaf.base.localization = HAMMER_LOCALIZE_MISC;
532         elm->leaf.base.obj_id = HAMMER_OBJID_ROOT;
533         elm->leaf.base.key = 0;
534         elm->leaf.base.create_tid = create_tid;
535         elm->leaf.base.delete_tid = 0;
536         elm->leaf.base.rec_type = HAMMER_RECTYPE_PFS;
537         elm->leaf.base.obj_type = 0;
538         elm->leaf.create_ts = (u_int32_t)time(NULL);
539
540         elm->leaf.data_offset = pfsd_off;
541         elm->leaf.data_len = sizeof(*pfsd);
542         elm->leaf.data_crc = crc32(pfsd, sizeof(*pfsd));
543
544         bnode->crc = crc32(&bnode->crc + 1, HAMMER_BTREE_CRCSIZE);
545
546         return(btree_off);
547 }
548