Merge from vendor branch BZIP:
[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.21 2008/03/19 20:18:16 dillon 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 static hammer_off_t format_root(void);
44 static void usage(void);
45
46 int
47 main(int ac, char **av)
48 {
49         int i;
50         int ch;
51         u_int32_t status;
52         off_t total;
53         const char *label = NULL;
54
55         /*
56          * Sanity check basic filesystem structures.  No cookies for us
57          * if it gets broken!
58          */
59         assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
60         assert(sizeof(union hammer_record_ondisk) == HAMMER_RECORD_SIZE);
61         assert(sizeof(struct hammer_blockmap_layer1) == 32);
62         assert(sizeof(struct hammer_blockmap_layer2) == 16);
63
64         /*
65          * Generate a filesysem id and lookup the filesystem type
66          */
67         uuidgen(&Hammer_FSId, 1);
68         uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
69         if (status != uuid_s_ok) {
70                 errx(1, "uuids file does not have the DragonFly "
71                         "HAMMER filesystem type");
72         }
73
74         /*
75          * Parse arguments
76          */
77         while ((ch = getopt(ac, av, "L:b:m:")) != -1) {
78                 switch(ch) {
79                 case 'L':
80                         label = optarg;
81                         break;
82                 case 'b':
83                         BootAreaSize = getsize(optarg,
84                                          HAMMER_BUFSIZE,
85                                          HAMMER_BOOT_MAXBYTES, 2);
86                         break;
87                 case 'm':
88                         MemAreaSize = getsize(optarg,
89                                          HAMMER_BUFSIZE,
90                                          HAMMER_MEM_MAXBYTES, 2);
91                         break;
92                 default:
93                         usage();
94                         break;
95                 }
96         }
97
98         if (label == NULL) {
99                 fprintf(stderr,
100                         "newfs_hammer: A filesystem label must be specified\n");
101                 exit(1);
102         }
103
104         /*
105          * Collect volume information
106          */
107         ac -= optind;
108         av += optind;
109         NumVolumes = ac;
110         RootVolNo = 0;
111
112         total = 0;
113         for (i = 0; i < NumVolumes; ++i) {
114                 struct volume_info *vol;
115
116                 vol = setup_volume(i, av[i], 1, O_RDWR);
117
118                 /*
119                  * Load up information on the volume and initialize
120                  * its remaining fields.
121                  */
122                 check_volume(vol);
123                 total += vol->size;
124         }
125
126         /*
127          * Calculate defaults for the boot and memory area sizes.
128          */
129         if (BootAreaSize == 0) {
130                 BootAreaSize = HAMMER_BOOT_NOMBYTES;
131                 while (BootAreaSize > total / NumVolumes / 256)
132                         BootAreaSize >>= 1;
133                 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
134                         BootAreaSize = 0;
135         } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
136                 BootAreaSize = HAMMER_BOOT_MINBYTES;
137         }
138         if (MemAreaSize == 0) {
139                 MemAreaSize = HAMMER_MEM_NOMBYTES;
140                 while (MemAreaSize > total / NumVolumes / 256)
141                         MemAreaSize >>= 1;
142                 if (MemAreaSize < HAMMER_MEM_MINBYTES)
143                         MemAreaSize = 0;
144         } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
145                 MemAreaSize = HAMMER_MEM_MINBYTES;
146         }
147
148         printf("---------------------------------------------\n");
149         printf("%d volume%s total size %s\n",
150                 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
151         printf("boot-area-size:      %s\n", sizetostr(BootAreaSize));
152         printf("memory-log-size:     %s\n", sizetostr(MemAreaSize));
153         printf("\n");
154
155         /*
156          * Format the volumes.  Format the root volume first so we can
157          * bootstrap the freemap.
158          */
159         format_volume(get_volume(RootVolNo), NumVolumes, label);
160         for (i = 0; i < NumVolumes; ++i) {
161                 if (i != RootVolNo)
162                         format_volume(get_volume(i), NumVolumes, label);
163         }
164         flush_all_volumes();
165         return(0);
166 }
167
168 static
169 void
170 usage(void)
171 {
172         fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
173         exit(1);
174 }
175
176 /*
177  * Convert the size in bytes to a human readable string.
178  */
179 static const char *
180 sizetostr(off_t size)
181 {
182         static char buf[32];
183
184         if (size < 1024 / 2) {
185                 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
186         } else if (size < 1024 * 1024 / 2) {
187                 snprintf(buf, sizeof(buf), "%6.2fKB",
188                         (double)size / 1024);
189         } else if (size < 1024 * 1024 * 1024LL / 2) {
190                 snprintf(buf, sizeof(buf), "%6.2fMB",
191                         (double)size / (1024 * 1024));
192         } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
193                 snprintf(buf, sizeof(buf), "%6.2fGB",
194                         (double)size / (1024 * 1024 * 1024LL));
195         } else {
196                 snprintf(buf, sizeof(buf), "%6.2fTB",
197                         (double)size / (1024 * 1024 * 1024LL * 1024LL));
198         }
199         return(buf);
200 }
201
202 /*
203  * Convert a string to a 64 bit signed integer with various requirements.
204  */
205 static int64_t
206 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
207 {
208         int64_t val;
209         char *ptr;
210
211         val = strtoll(str, &ptr, 0);
212         switch(*ptr) {
213         case 't':
214         case 'T':
215                 val *= 1024;
216                 /* fall through */
217         case 'g':
218         case 'G':
219                 val *= 1024;
220                 /* fall through */
221         case 'm':
222         case 'M':
223                 val *= 1024;
224                 /* fall through */
225         case 'k':
226         case 'K':
227                 val *= 1024;
228                 break;
229         default:
230                 errx(1, "Unknown suffix in number '%s'\n", str);
231                 /* not reached */
232         }
233         if (ptr[1]) {
234                 errx(1, "Unknown suffix in number '%s'\n", str);
235                 /* not reached */
236         }
237         if (val < minval) {
238                 errx(1, "Value too small: %s, min is %s\n",
239                      str, sizetostr(minval));
240                 /* not reached */
241         }
242         if (val > maxval) {
243                 errx(1, "Value too large: %s, max is %s\n",
244                      str, sizetostr(maxval));
245                 /* not reached */
246         }
247         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
248                 errx(1, "Value not power of 2: %s\n", str);
249                 /* not reached */
250         }
251         if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
252                 errx(1, "Value not an integral multiple of %dK: %s", 
253                      HAMMER_BUFSIZE / 1024, str);
254                 /* not reached */
255         }
256         return(val);
257 }
258
259 /*
260  * Generate a transaction id
261  */
262 static hammer_tid_t
263 createtid(void)
264 {
265         static hammer_tid_t lasttid;
266         struct timeval tv;
267
268         if (lasttid == 0) {
269                 gettimeofday(&tv, NULL);
270                 lasttid = tv.tv_sec * 1000000000LL +
271                           tv.tv_usec * 1000LL;
272         }
273         return(lasttid++);
274 }
275
276 /*
277  * Check basic volume characteristics.  HAMMER filesystems use a minimum
278  * of a 16KB filesystem buffer size.
279  */
280 static
281 void
282 check_volume(struct volume_info *vol)
283 {
284         struct partinfo pinfo;
285         struct stat st;
286
287         /*
288          * Get basic information about the volume
289          */
290         vol->fd = open(vol->name, O_RDWR);
291         if (vol->fd < 0)
292                 err(1, "Unable to open %s R+W", vol->name);
293         if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
294                 /*
295                  * Allow the formatting of regular filews as HAMMER volumes
296                  */
297                 if (fstat(vol->fd, &st) < 0)
298                         err(1, "Unable to stat %s", vol->name);
299                 vol->size = st.st_size;
300                 vol->type = "REGFILE";
301         } else {
302                 /*
303                  * When formatting a block device as a HAMMER volume the
304                  * sector size must be compatible.  HAMMER uses 16384 byte
305                  * filesystem buffers.
306                  */
307                 if (pinfo.reserved_blocks) {
308                         errx(1, "HAMMER cannot be placed in a partition "
309                                 "which overlaps the disklabel or MBR");
310                 }
311                 if (pinfo.media_blksize > 16384 ||
312                     16384 % pinfo.media_blksize) {
313                         errx(1, "A media sector size of %d is not supported",
314                              pinfo.media_blksize);
315                 }
316
317                 vol->size = pinfo.media_size;
318                 vol->type = "DEVICE";
319         }
320         printf("Volume %d %s %-15s size %s\n",
321                vol->vol_no, vol->type, vol->name,
322                sizetostr(vol->size));
323
324         /*
325          * Reserve space for (future) header junk, setup our poor-man's
326          * bigblock allocator.
327          */
328         vol->vol_alloc = HAMMER_BUFSIZE * 16;
329 }
330
331 /*
332  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
333  */
334 static
335 void
336 format_volume(struct volume_info *vol, int nvols, const char *label)
337 {
338         struct volume_info *root_vol;
339         struct hammer_volume_ondisk *ondisk;
340         int64_t freeblks;
341
342         /*
343          * Initialize basic information in the on-disk volume structure.
344          */
345         ondisk = vol->ondisk;
346
347         ondisk->vol_fsid = Hammer_FSId;
348         ondisk->vol_fstype = Hammer_FSType;
349         snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
350         ondisk->vol_no = vol->vol_no;
351         ondisk->vol_count = nvols;
352         ondisk->vol_version = 1;
353
354         ondisk->vol_bot_beg = vol->vol_alloc;
355         vol->vol_alloc += BootAreaSize;
356         ondisk->vol_mem_beg = vol->vol_alloc;
357         vol->vol_alloc += MemAreaSize;
358
359         /*
360          * The remaining area is the zone 2 buffer allocation area.  These
361          * buffers
362          */
363         ondisk->vol_buf_beg = vol->vol_alloc;
364         ondisk->vol_buf_end = vol->size & ~(int64_t)HAMMER_BUFMASK;
365
366         if (ondisk->vol_buf_end < ondisk->vol_buf_beg) {
367                 errx(1, "volume %d %s is too small to hold the volume header",
368                      vol->vol_no, vol->name);
369         }
370
371         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
372                               HAMMER_BUFSIZE;
373         ondisk->vol_blocksize = HAMMER_BUFSIZE;
374
375         ondisk->vol_rootvol = RootVolNo;
376         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
377
378         vol->vol_free_off = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, 0);
379         vol->vol_free_end = HAMMER_ENCODE_RAW_BUFFER(vol->vol_no, (ondisk->vol_buf_end - ondisk->vol_buf_beg) & ~HAMMER_LARGEBLOCK_MASK64);
380
381         /*
382          * Format the root volume.
383          */
384         if (vol->vol_no == RootVolNo) {
385                 ondisk->vol0_next_tid = createtid();
386
387                 format_freemap(vol,
388                         &ondisk->vol0_blockmap[HAMMER_ZONE_FREEMAP_INDEX]);
389
390                 freeblks = initialize_freemap(vol);
391                 ondisk->vol0_stat_freebigblocks = freeblks;
392                 ondisk->vol0_stat_bigblocks = freeblks;
393
394                 format_blockmap(
395                         &ondisk->vol0_blockmap[HAMMER_ZONE_BTREE_INDEX],
396                         HAMMER_ZONE_BTREE);
397                 format_blockmap(
398                         &ondisk->vol0_blockmap[HAMMER_ZONE_RECORD_INDEX],
399                         HAMMER_ZONE_RECORD);
400                 format_blockmap(
401                         &ondisk->vol0_blockmap[HAMMER_ZONE_LARGE_DATA_INDEX],
402                         HAMMER_ZONE_LARGE_DATA);
403                 format_blockmap(
404                         &ondisk->vol0_blockmap[HAMMER_ZONE_SMALL_DATA_INDEX],
405                         HAMMER_ZONE_SMALL_DATA);
406
407                 format_undomap(ondisk);
408
409                 ondisk->vol0_btree_root = format_root();
410                 ++ondisk->vol0_stat_inodes;     /* root inode */
411         } else {
412                 freeblks = initialize_freemap(vol);
413                 root_vol = get_volume(RootVolNo);
414                 root_vol->cache.modified = 1;
415                 root_vol->ondisk->vol0_stat_freebigblocks += freeblks;
416                 root_vol->ondisk->vol0_stat_bigblocks += freeblks;
417                 rel_volume(root_vol);
418         }
419 }
420
421 /*
422  * Format the root directory.
423  */
424 static
425 hammer_off_t
426 format_root(void)
427 {
428         hammer_off_t btree_off;
429         hammer_off_t rec_off;
430         hammer_node_ondisk_t bnode;
431         hammer_record_ondisk_t rec;
432         struct hammer_inode_data *idata;
433         hammer_btree_elm_t elm;
434
435         bnode = alloc_btree_element(&btree_off);
436         rec = alloc_record_element(&rec_off, sizeof(*idata), (void **)&idata);
437
438         /*
439          * Populate the inode data and inode record for the root directory.
440          */
441         idata->version = HAMMER_INODE_DATA_VERSION;
442         idata->mode = 0755;
443
444         rec->base.base.btype = HAMMER_BTREE_TYPE_RECORD;
445         rec->base.base.obj_id = HAMMER_OBJID_ROOT;
446         rec->base.base.key = 0;
447         rec->base.base.create_tid = createtid();
448         rec->base.base.delete_tid = 0;
449         rec->base.base.rec_type = HAMMER_RECTYPE_INODE;
450         rec->base.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
451         /* rec->base.data_offset - initialized by alloc_record_element */
452         /* rec->base.data_len    - initialized by alloc_record_element */
453         rec->base.data_crc = crc32(idata, sizeof(*idata));
454         rec->inode.ino_atime  = rec->base.base.create_tid;
455         rec->inode.ino_mtime  = rec->base.base.create_tid;
456         rec->inode.ino_size   = 0;
457         rec->inode.ino_nlinks = 1;
458
459         /*
460          * Create the root of the B-Tree.  The root is a leaf node so we
461          * do not have to worry about boundary elements.
462          */
463         bnode->count = 1;
464         bnode->type = HAMMER_BTREE_TYPE_LEAF;
465
466         elm = &bnode->elms[0];
467         elm->base = rec->base.base;
468         elm->leaf.rec_offset = rec_off;
469         elm->leaf.data_offset = rec->base.data_off;
470         elm->leaf.data_len = rec->base.data_len;
471         elm->leaf.data_crc = rec->base.data_crc;
472         return(btree_off);
473 }
474