HAMMER 27/many: Major surgery - change allocation model
[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.17 2008/02/08 08:30:58 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
61         /*
62          * Generate a filesysem id and lookup the filesystem type
63          */
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");
69         }
70
71         /*
72          * Parse arguments
73          */
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;
92                 }
93         }
94
95         if (label == NULL) {
96                 fprintf(stderr,
97                         "newfs_hammer: A filesystem label must be specified\n");
98                 exit(1);
99         }
100
101         /*
102          * Collect volume information
103          */
104         ac -= optind;
105         av += optind;
106         NumVolumes = ac;
107         RootVolNo = 0;
108
109         total = 0;
110         for (i = 0; i < NumVolumes; ++i) {
111                 struct volume_info *vol;
112
113                 vol = setup_volume(i, av[i], 1, O_RDWR);
114
115                 /*
116                  * Load up information on the volume and initialize
117                  * its remaining fields.
118                  */
119                 check_volume(vol);
120                 total += vol->size;
121         }
122
123         /*
124          * Calculate defaults for the boot and memory area sizes.
125          */
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;
134         }
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;
143         }
144
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");
151
152         /*
153          * Format the volumes.
154          */
155         for (i = 0; i < NumVolumes; ++i) {
156                 format_volume(get_volume(i), NumVolumes, label);
157         }
158         flush_all_volumes();
159         return(0);
160 }
161
162 static
163 void
164 usage(void)
165 {
166         fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
167         exit(1);
168 }
169
170 /*
171  * Convert the size in bytes to a human readable string.
172  */
173 static const char *
174 sizetostr(off_t size)
175 {
176         static char buf[32];
177
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));
192         }
193         return(buf);
194 }
195
196 /*
197  * Convert a string to a 64 bit signed integer with various requirements.
198  */
199 static int64_t
200 getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
201 {
202         int64_t val;
203         char *ptr;
204
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 */
226         }
227         if (ptr[1]) {
228                 errx(1, "Unknown suffix in number '%s'\n", str);
229                 /* not reached */
230         }
231         if (val < minval) {
232                 errx(1, "Value too small: %s, min is %s\n",
233                      str, sizetostr(minval));
234                 /* not reached */
235         }
236         if (val > maxval) {
237                 errx(1, "Value too large: %s, max is %s\n",
238                      str, sizetostr(maxval));
239                 /* not reached */
240         }
241         if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
242                 errx(1, "Value not power of 2: %s\n", str);
243                 /* not reached */
244         }
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 */
249         }
250         return(val);
251 }
252
253 /*
254  * Generate a transaction id
255  */
256 static hammer_tid_t
257 createtid(void)
258 {
259         static hammer_tid_t lasttid;
260         struct timeval tv;
261
262         if (lasttid == 0) {
263                 gettimeofday(&tv, NULL);
264                 lasttid = tv.tv_sec * 1000000000LL +
265                           tv.tv_usec * 1000LL;
266         }
267         return(lasttid++);
268 }
269
270 /*
271  * Check basic volume characteristics.  HAMMER filesystems use a minimum
272  * of a 16KB filesystem buffer size.
273  */
274 static
275 void
276 check_volume(struct volume_info *vol)
277 {
278         struct partinfo pinfo;
279         struct stat st;
280
281         /*
282          * Get basic information about the volume
283          */
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) {
288                 /*
289                  * Allow the formatting of regular filews as HAMMER volumes
290                  */
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 {
296                 /*
297                  * When formatting a block device as a HAMMER volume the
298                  * sector size must be compatible.  HAMMER uses 16384 byte
299                  * filesystem buffers.
300                  */
301                 if (pinfo.reserved_blocks) {
302                         errx(1, "HAMMER cannot be placed in a partition "
303                                 "which overlaps the disklabel or MBR");
304                 }
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);
309                 }
310
311                 vol->size = pinfo.media_size;
312                 vol->type = "DEVICE";
313         }
314         printf("Volume %d %s %-15s size %s\n",
315                vol->vol_no, vol->type, vol->name,
316                sizetostr(vol->size));
317
318         /*
319          * Reserve space for (future) header junk
320          */
321         vol->vol_alloc = HAMMER_BUFSIZE * 16;
322 }
323
324 /*
325  * Format a HAMMER volume.  Cluster 0 will be initially placed in volume 0.
326  */
327 static
328 void
329 format_volume(struct volume_info *vol, int nvols, const char *label)
330 {
331         struct hammer_volume_ondisk *ondisk;
332
333         /*
334          * Initialize basic information in the on-disk volume structure.
335          */
336         ondisk = vol->ondisk;
337
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;
344
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;
351
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);
355         }
356
357         ondisk->vol_nblocks = (ondisk->vol_buf_end - ondisk->vol_buf_beg) /
358                               HAMMER_BUFSIZE;
359         ondisk->vol_blocksize = HAMMER_BUFSIZE;
360
361         /*
362          * Assign the root volume to volume 0.
363          */
364         ondisk->vol_rootvol = 0;
365         ondisk->vol_signature = HAMMER_FSBUF_VOLUME;
366         if (ondisk->vol_no == (int)ondisk->vol_rootvol) {
367                 /*
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)
371                  */
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;
376
377                 ondisk->vol0_btree_root = format_root();
378                 ++ondisk->vol0_stat_inodes;     /* root inode */
379
380         }
381 }
382
383 /*
384  * Format the root directory.
385  */
386 static
387 hammer_off_t
388 format_root(void)
389 {
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;
396
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);
401
402         /*
403          * Populate the inode data and inode record for the root directory.
404          */
405         idata->version = HAMMER_INODE_DATA_VERSION;
406         idata->mode = 0755;
407
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;
422
423         /*
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.
426          */
427         bnode->count = 1;
428         bnode->type = HAMMER_BTREE_TYPE_LEAF;
429
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);
437 }
438