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