HAMMER 15/many - user utility infrastructure, refactor alists, misc
[dragonfly.git] / sbin / newfs_hammer / newfs_hammer.c
CommitLineData
9a8bf4a7
MD
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 *
61aeeb33 34 * $DragonFly: src/sbin/newfs_hammer/newfs_hammer.c,v 1.12 2008/01/03 06:48:48 dillon Exp $
9a8bf4a7
MD
35 */
36
ed3afcca 37#include "newfs_hammer.h"
9a8bf4a7 38
ed3afcca 39static int64_t getsize(const char *str, int64_t minval, int64_t maxval, int pw);
9a8bf4a7 40static const char *sizetostr(off_t size);
ed3afcca
MD
41static void check_volume(struct volume_info *vol);
42static void format_volume(struct volume_info *vol, int nvols,const char *label);
43static int32_t format_cluster(struct volume_info *vol, int isroot);
44static void format_root(struct cluster_info *cluster);
45static void usage(void);
9a8bf4a7 46
9a8bf4a7
MD
47int
48main(int ac, char **av)
49{
50 int i;
51 int ch;
9a8bf4a7
MD
52 u_int32_t status;
53 off_t total;
ed3afcca 54 int64_t max_volume_size;
9a8bf4a7
MD
55 const char *label = NULL;
56
57 /*
ed3afcca
MD
58 * Sanity check basic filesystem structures. No cookies for us
59 * if it gets broken!
9a8bf4a7
MD
60 */
61 assert(sizeof(struct hammer_almeta) == HAMMER_ALMETA_SIZE);
62 assert(sizeof(struct hammer_fsbuf_head) == HAMMER_FSBUF_HEAD_SIZE);
63 assert(sizeof(struct hammer_volume_ondisk) <= HAMMER_BUFSIZE);
64 assert(sizeof(struct hammer_cluster_ondisk) <= HAMMER_BUFSIZE);
65 assert(sizeof(struct hammer_fsbuf_data) == HAMMER_BUFSIZE);
66 assert(sizeof(struct hammer_fsbuf_recs) == HAMMER_BUFSIZE);
67 assert(sizeof(struct hammer_fsbuf_btree) == HAMMER_BUFSIZE);
ed3afcca 68 assert(sizeof(union hammer_fsbuf_ondisk) == HAMMER_BUFSIZE);
9a8bf4a7
MD
69
70 /*
71 * Generate a filesysem id and lookup the filesystem type
72 */
ed3afcca 73 uuidgen(&Hammer_FSId, 1);
9a8bf4a7
MD
74 uuid_name_lookup(&Hammer_FSType, "DragonFly HAMMER", &status);
75 if (status != uuid_s_ok) {
ed3afcca
MD
76 errx(1, "uuids file does not have the DragonFly "
77 "HAMMER filesystem type");
9a8bf4a7
MD
78 }
79
61aeeb33 80 init_alist_templates();
9a8bf4a7
MD
81
82 /*
83 * Parse arguments
84 */
a89aec1b 85 while ((ch = getopt(ac, av, "L:b:c:m:S")) != -1) {
9a8bf4a7
MD
86 switch(ch) {
87 case 'L':
88 label = optarg;
89 break;
a89aec1b
MD
90 case 'b':
91 BootAreaSize = getsize(optarg,
92 HAMMER_BUFSIZE,
93 HAMMER_BOOT_MAXBYTES, 2);
94 break;
620822d2 95 case 'c':
ed3afcca
MD
96 ClusterSize = getsize(optarg,
97 HAMMER_BUFSIZE * 256LL,
98 HAMMER_CLU_MAXBYTES, 1);
99 break;
a89aec1b
MD
100 case 'm':
101 MemAreaSize = getsize(optarg,
102 HAMMER_BUFSIZE,
103 HAMMER_MEM_MAXBYTES, 2);
104 break;
ed3afcca 105 case 'S':
9a8bf4a7 106 /*
ed3afcca 107 * Force the use of super-clusters
9a8bf4a7 108 */
ed3afcca 109 UsingSuperClusters = 1;
9a8bf4a7
MD
110 break;
111 default:
112 usage();
113 break;
114 }
115 }
116
117 if (label == NULL) {
118 fprintf(stderr,
119 "newfs_hammer: A filesystem label must be specified\n");
120 exit(1);
121 }
122
123 /*
124 * Collect volume information
125 */
126 ac -= optind;
127 av += optind;
ed3afcca 128 NumVolumes = ac;
9a8bf4a7 129
9a8bf4a7 130 total = 0;
ed3afcca
MD
131 for (i = 0; i < NumVolumes; ++i) {
132 struct volume_info *vol;
133
61aeeb33 134 vol = setup_volume(i, av[i], 1, O_RDWR);
ed3afcca
MD
135
136 /*
137 * Load up information on the volume and initialize
138 * its remaining fields.
139 */
140 check_volume(vol);
141 total += vol->size;
9a8bf4a7
MD
142 }
143
144 /*
ed3afcca 145 * Calculate the size of a cluster. A cluster is broken
9a8bf4a7
MD
146 * down into 256 chunks which must be at least filesystem buffer
147 * sized. This gives us a minimum chunk size of around 4MB.
148 */
ed3afcca
MD
149 if (ClusterSize == 0) {
150 ClusterSize = HAMMER_BUFSIZE * 256;
151 while (ClusterSize < total / NumVolumes / 256 &&
152 ClusterSize < HAMMER_CLU_MAXBYTES) {
153 ClusterSize <<= 1;
9a8bf4a7
MD
154 }
155 }
156
a89aec1b
MD
157 /*
158 * Calculate defaults for the boot and memory area sizes.
159 */
160 if (BootAreaSize == 0) {
161 BootAreaSize = HAMMER_BOOT_NOMBYTES;
162 while (BootAreaSize > total / NumVolumes / 256)
163 BootAreaSize >>= 1;
164 if (BootAreaSize < HAMMER_BOOT_MINBYTES)
165 BootAreaSize = 0;
166 } else if (BootAreaSize < HAMMER_BOOT_MINBYTES) {
167 BootAreaSize = HAMMER_BOOT_MINBYTES;
168 }
169 if (MemAreaSize == 0) {
170 MemAreaSize = HAMMER_MEM_NOMBYTES;
171 while (MemAreaSize > total / NumVolumes / 256)
172 MemAreaSize >>= 1;
173 if (MemAreaSize < HAMMER_MEM_MINBYTES)
174 MemAreaSize = 0;
175 } else if (MemAreaSize < HAMMER_MEM_MINBYTES) {
176 MemAreaSize = HAMMER_MEM_MINBYTES;
177 }
178
9a8bf4a7
MD
179 printf("---------------------------------------------\n");
180 printf("%d volume%s total size %s\n",
ed3afcca
MD
181 NumVolumes, (NumVolumes == 1 ? "" : "s"), sizetostr(total));
182 printf("cluster-size: %s\n", sizetostr(ClusterSize));
183
184 if (UsingSuperClusters) {
185 max_volume_size = (int64_t)HAMMER_VOL_MAXSUPERCLUSTERS * \
186 HAMMER_SCL_MAXCLUSTERS * ClusterSize;
187 } else {
a89aec1b 188 max_volume_size = HAMMER_VOL_MAXCLUSTERS * ClusterSize;
ed3afcca
MD
189 }
190 printf("max-volume-size: %s\n", sizetostr(max_volume_size));
191
9a8bf4a7 192 printf("max-filesystem-size: %s\n",
ed3afcca
MD
193 (max_volume_size * 32768LL < max_volume_size) ?
194 "Unlimited" :
195 sizetostr(max_volume_size * 32768LL));
a89aec1b
MD
196 printf("boot-area-size: %s\n", sizetostr(BootAreaSize));
197 printf("memory-log-size: %s\n", sizetostr(MemAreaSize));
9a8bf4a7
MD
198 printf("\n");
199
200 /*
ed3afcca 201 * Format the volumes.
9a8bf4a7 202 */
ed3afcca
MD
203 for (i = 0; i < NumVolumes; ++i) {
204 format_volume(get_volume(i), NumVolumes, label);
9a8bf4a7 205 }
ed3afcca 206 flush_all_volumes();
9a8bf4a7
MD
207 return(0);
208}
209
210static
211void
212usage(void)
213{
214 fprintf(stderr, "newfs_hammer vol0 [vol1 ...]\n");
215 exit(1);
216}
217
218/*
219 * Convert the size in bytes to a human readable string.
220 */
221static const char *
222sizetostr(off_t size)
223{
224 static char buf[32];
225
226 if (size < 1024 / 2) {
227 snprintf(buf, sizeof(buf), "%6.2f", (double)size);
228 } else if (size < 1024 * 1024 / 2) {
229 snprintf(buf, sizeof(buf), "%6.2fKB",
230 (double)size / 1024);
231 } else if (size < 1024 * 1024 * 1024LL / 2) {
232 snprintf(buf, sizeof(buf), "%6.2fMB",
233 (double)size / (1024 * 1024));
234 } else if (size < 1024 * 1024 * 1024LL * 1024LL / 2) {
235 snprintf(buf, sizeof(buf), "%6.2fGB",
236 (double)size / (1024 * 1024 * 1024LL));
237 } else {
238 snprintf(buf, sizeof(buf), "%6.2fTB",
239 (double)size / (1024 * 1024 * 1024LL * 1024LL));
240 }
241 return(buf);
242}
243
ed3afcca
MD
244/*
245 * Convert a string to a 64 bit signed integer with various requirements.
246 */
9a8bf4a7 247static int64_t
ed3afcca 248getsize(const char *str, int64_t minval, int64_t maxval, int powerof2)
9a8bf4a7
MD
249{
250 int64_t val;
251 char *ptr;
252
253 val = strtoll(str, &ptr, 0);
254 switch(*ptr) {
255 case 't':
256 case 'T':
257 val *= 1024;
258 /* fall through */
259 case 'g':
260 case 'G':
261 val *= 1024;
262 /* fall through */
263 case 'm':
264 case 'M':
265 val *= 1024;
266 /* fall through */
267 case 'k':
268 case 'K':
269 val *= 1024;
270 break;
271 default:
ed3afcca
MD
272 errx(1, "Unknown suffix in number '%s'\n", str);
273 /* not reached */
9a8bf4a7
MD
274 }
275 if (ptr[1]) {
ed3afcca
MD
276 errx(1, "Unknown suffix in number '%s'\n", str);
277 /* not reached */
9a8bf4a7
MD
278 }
279 if (val < minval) {
ed3afcca
MD
280 errx(1, "Value too small: %s, min is %s\n",
281 str, sizetostr(minval));
282 /* not reached */
9a8bf4a7
MD
283 }
284 if (val > maxval) {
ed3afcca
MD
285 errx(1, "Value too large: %s, max is %s\n",
286 str, sizetostr(maxval));
287 /* not reached */
288 }
a89aec1b 289 if ((powerof2 & 1) && (val ^ (val - 1)) != ((val << 1) - 1)) {
ed3afcca
MD
290 errx(1, "Value not power of 2: %s\n", str);
291 /* not reached */
9a8bf4a7 292 }
a89aec1b
MD
293 if ((powerof2 & 2) && (val & HAMMER_BUFMASK)) {
294 errx(1, "Value not an integral multiple of %dK: %s",
295 HAMMER_BUFSIZE / 1024, str);
296 /* not reached */
297 }
9a8bf4a7
MD
298 return(val);
299}
300
ed3afcca
MD
301/*
302 * Generate a transaction id
303 */
9a8bf4a7
MD
304static hammer_tid_t
305createtid(void)
306{
307 static hammer_tid_t lasttid;
308 struct timeval tv;
309
310 if (lasttid == 0) {
311 gettimeofday(&tv, NULL);
312 lasttid = tv.tv_sec * 1000000000LL +
313 tv.tv_usec * 1000LL;
314 }
315 return(lasttid++);
316}
317
9a8bf4a7
MD
318/*
319 * Check basic volume characteristics. HAMMER filesystems use a minimum
320 * of a 16KB filesystem buffer size.
321 */
322static
323void
ed3afcca 324check_volume(struct volume_info *vol)
9a8bf4a7
MD
325{
326 struct partinfo pinfo;
327 struct stat st;
328
329 /*
330 * Get basic information about the volume
331 */
ed3afcca
MD
332 vol->fd = open(vol->name, O_RDWR);
333 if (vol->fd < 0)
334 err(1, "Unable to open %s R+W", vol->name);
335 if (ioctl(vol->fd, DIOCGPART, &pinfo) < 0) {
9a8bf4a7
MD
336 /*
337 * Allow the formatting of regular filews as HAMMER volumes
338 */
ed3afcca
MD
339 if (fstat(vol->fd, &st) < 0)
340 err(1, "Unable to stat %s", vol->name);
341 vol->size = st.st_size;
342 vol->type = "REGFILE";
9a8bf4a7
MD
343 } else {
344 /*
345 * When formatting a block device as a HAMMER volume the
346 * sector size must be compatible. HAMMER uses 16384 byte
347 * filesystem buffers.
348 */
349 if (pinfo.reserved_blocks) {
ed3afcca
MD
350 errx(1, "HAMMER cannot be placed in a partition "
351 "which overlaps the disklabel or MBR");
9a8bf4a7
MD
352 }
353 if (pinfo.media_blksize > 16384 ||
354 16384 % pinfo.media_blksize) {
ed3afcca
MD
355 errx(1, "A media sector size of %d is not supported",
356 pinfo.media_blksize);
9a8bf4a7
MD
357 }
358
ed3afcca
MD
359 vol->size = pinfo.media_size;
360 vol->type = "DEVICE";
9a8bf4a7 361 }
ed3afcca
MD
362 printf("Volume %d %s %-15s size %s\n",
363 vol->vol_no, vol->type, vol->name,
364 sizetostr(vol->size));
365
366 /*
367 * Strictly speaking we do not need to enable super clusters unless
368 * we have volumes > 2TB, but turning them on doesn't really hurt
369 * and if we don't the user may get confused if he tries to expand
370 * the size of an existing volume.
371 */
372 if (vol->size > 200LL * 1024 * 1024 * 1024 && !UsingSuperClusters) {
373 UsingSuperClusters = 1;
374 printf("Enabling super-clusters\n");
375 }
376
377 /*
a89aec1b 378 * Reserve space for (future) header junk
ed3afcca 379 */
a89aec1b 380 vol->vol_alloc = HAMMER_BUFSIZE * 16;
9a8bf4a7
MD
381}
382
383/*
384 * Format a HAMMER volume. Cluster 0 will be initially placed in volume 0.
385 */
386static
387void
ed3afcca 388format_volume(struct volume_info *vol, int nvols, const char *label)
9a8bf4a7 389{
ed3afcca 390 struct hammer_volume_ondisk *ondisk;
9a8bf4a7
MD
391 int32_t nclusters;
392 int32_t minclsize;
ed3afcca
MD
393 int32_t nscl_groups;
394 int64_t scl_group_size;
395 int64_t scl_header_size;
396 int64_t n64;
9a8bf4a7 397
ed3afcca
MD
398 /*
399 * The last cluster in a volume may wind up truncated. It must be
400 * at least minclsize to really be workable as a cluster.
401 */
a89aec1b 402 minclsize = (int32_t)(ClusterSize / 4);
ed3afcca
MD
403 if (minclsize < HAMMER_BUFSIZE * 64)
404 minclsize = HAMMER_BUFSIZE * 64;
9a8bf4a7 405
ed3afcca
MD
406 /*
407 * Initialize basic information in the on-disk volume structure.
408 */
409 ondisk = vol->ondisk;
410
411 ondisk->vol_fsid = Hammer_FSId;
412 ondisk->vol_fstype = Hammer_FSType;
413 snprintf(ondisk->vol_name, sizeof(ondisk->vol_name), "%s", label);
414 ondisk->vol_no = vol->vol_no;
415 ondisk->vol_count = nvols;
416 ondisk->vol_version = 1;
a89aec1b 417 ondisk->vol_clsize = (int32_t)ClusterSize;
620822d2
MD
418 if (UsingSuperClusters)
419 ondisk->vol_flags = HAMMER_VOLF_USINGSUPERCL;
9a8bf4a7 420
a89aec1b
MD
421 ondisk->vol_bot_beg = vol->vol_alloc;
422 vol->vol_alloc += BootAreaSize;
423 ondisk->vol_mem_beg = vol->vol_alloc;
424 vol->vol_alloc += MemAreaSize;
425 ondisk->vol_clo_beg = vol->vol_alloc;
426 ondisk->vol_clo_end = vol->size;
9a8bf4a7 427
a89aec1b 428 if (ondisk->vol_clo_end < ondisk->vol_clo_beg) {
ed3afcca
MD
429 errx(1, "volume %d %s is too small to hold the volume header",
430 vol->vol_no, vol->name);
9a8bf4a7
MD
431 }
432
433 /*
ed3afcca
MD
434 * Our A-lists have been initialized but are marked all-allocated.
435 * Calculate the actual number of clusters in the volume and free
436 * them to get the filesystem ready for work. The clusters will
437 * be initialized on-demand.
438 *
439 * If using super-clusters we must still calculate nclusters but
440 * we only need to initialize superclusters that are not going
441 * to wind up in the all-free state, which will only be the last
442 * supercluster. hammer_alist_free() will recurse into the
443 * supercluster infrastructure and create the necessary superclusters.
444 *
445 * NOTE: The nclusters calculation ensures that the volume EOF does
446 * not occur in the middle of a supercluster buffer array.
9a8bf4a7 447 */
ed3afcca
MD
448 if (UsingSuperClusters) {
449 /*
450 * Figure out how many full super-cluster groups we will have.
451 * This calculation does not include the partial supercluster
452 * group at the end.
453 */
454 scl_header_size = (int64_t)HAMMER_BUFSIZE *
455 HAMMER_VOL_SUPERCLUSTER_GROUP;
456 scl_group_size = scl_header_size +
457 (int64_t)HAMMER_VOL_SUPERCLUSTER_GROUP *
458 ClusterSize * HAMMER_SCL_MAXCLUSTERS;
a89aec1b 459 nscl_groups = (ondisk->vol_clo_end - ondisk->vol_clo_beg) /
ed3afcca
MD
460 scl_group_size;
461 nclusters = nscl_groups * HAMMER_SCL_MAXCLUSTERS *
462 HAMMER_VOL_SUPERCLUSTER_GROUP;
463
464 /*
465 * Figure out how much space we have left and calculate the
466 * remaining number of clusters.
467 */
a89aec1b 468 n64 = (ondisk->vol_clo_end - ondisk->vol_clo_beg) -
ed3afcca
MD
469 (nscl_groups * scl_group_size);
470 if (n64 > scl_header_size) {
471 nclusters += (n64 + minclsize) / ClusterSize;
472 }
473 printf("%d clusters, %d full super-cluster groups\n",
474 nclusters, nscl_groups);
61aeeb33
MD
475 hammer_alist_init(&vol->clu_alist, 0, nclusters,
476 HAMMER_ASTATE_FREE);
ed3afcca 477 } else {
a89aec1b
MD
478 nclusters = (ondisk->vol_clo_end - ondisk->vol_clo_beg +
479 minclsize) / ClusterSize;
ed3afcca
MD
480 if (nclusters > HAMMER_VOL_MAXCLUSTERS) {
481 errx(1, "Volume is too large, max %s\n",
a89aec1b 482 sizetostr(nclusters * ClusterSize));
ed3afcca 483 }
61aeeb33
MD
484 hammer_alist_init(&vol->clu_alist, 0, nclusters,
485 HAMMER_ASTATE_FREE);
9a8bf4a7 486 }
ed3afcca 487 ondisk->vol_nclusters = nclusters;
fbc6e32a
MD
488 ondisk->vol_nblocks = nclusters * ClusterSize / HAMMER_BUFSIZE -
489 nclusters;
490 ondisk->vol_blocksize = HAMMER_BUFSIZE;
9a8bf4a7
MD
491
492 /*
493 * Place the root cluster in volume 0.
494 */
ed3afcca 495 ondisk->vol_rootvol = 0;
61aeeb33 496 if (ondisk->vol_no == (int)ondisk->vol_rootvol) {
620822d2 497 ondisk->vol0_root_clu_id = format_cluster(vol, 1);
ed3afcca 498 ondisk->vol0_recid = 1;
9a8bf4a7 499 /* global next TID */
ed3afcca 500 ondisk->vol0_nexttid = createtid();
9a8bf4a7 501 }
9a8bf4a7
MD
502}
503
504/*
505 * Format a hammer cluster. Returns byte offset in volume of cluster.
506 */
507static
508int32_t
ed3afcca 509format_cluster(struct volume_info *vol, int isroot)
9a8bf4a7 510{
9a8bf4a7 511 hammer_tid_t clu_id = createtid();
ed3afcca
MD
512 struct cluster_info *cluster;
513 struct hammer_cluster_ondisk *ondisk;
514 int nbuffers;
515 int clno;
9a8bf4a7 516
ed3afcca
MD
517 /*
518 * Allocate a cluster
519 */
ff8ba5f2 520 clno = hammer_alist_alloc(&vol->clu_alist, 1);
9a8bf4a7
MD
521 if (clno == HAMMER_ALIST_BLOCK_NONE) {
522 fprintf(stderr, "volume %d %s has insufficient space\n",
ed3afcca 523 vol->vol_no, vol->name);
9a8bf4a7
MD
524 exit(1);
525 }
61aeeb33 526 cluster = get_cluster(vol, clno, 1);
ed3afcca
MD
527 printf("allocate cluster id=%016llx %d@%08llx\n",
528 clu_id, clno, cluster->clu_offset);
529
530 ondisk = cluster->ondisk;
531
532 ondisk->vol_fsid = vol->ondisk->vol_fsid;
533 ondisk->vol_fstype = vol->ondisk->vol_fstype;
534 ondisk->clu_gen = 1;
535 ondisk->clu_id = clu_id;
536 ondisk->clu_no = clno;
537 ondisk->clu_flags = 0;
538 ondisk->clu_start = HAMMER_BUFSIZE;
539 if (vol->size - cluster->clu_offset > ClusterSize)
a89aec1b 540 ondisk->clu_limit = (u_int32_t)ClusterSize;
9a8bf4a7 541 else
ed3afcca 542 ondisk->clu_limit = (u_int32_t)(vol->size - cluster->clu_offset);
9a8bf4a7
MD
543
544 /*
545 * In-band filesystem buffer management A-List. The first filesystem
546 * buffer is the cluster header itself.
547 */
ed3afcca
MD
548 nbuffers = ondisk->clu_limit / HAMMER_BUFSIZE;
549 hammer_alist_free(&cluster->alist_master, 1, nbuffers - 1);
550 printf("cluster %d has %d buffers\n", cluster->clu_no, nbuffers);
9a8bf4a7
MD
551
552 /*
ed3afcca
MD
553 * Buffer Iterators in elements. Each buffer has 256 elements.
554 * The data and B-Tree indices are forward allocations while the
555 * record index allocates backwards.
9a8bf4a7 556 */
ed3afcca
MD
557 ondisk->idx_data = 1 * HAMMER_FSBUF_MAXBLKS;
558 ondisk->idx_index = 0 * HAMMER_FSBUF_MAXBLKS;
559 ondisk->idx_record = nbuffers * HAMMER_FSBUF_MAXBLKS;
9a8bf4a7 560
dfdcc871
MD
561 /*
562 * Iterator for whole-buffer data allocations. The iterator is
563 * the buf_no.
564 */
565 ondisk->idx_ldata = 1;
566
620822d2
MD
567 /*
568 * Initialize root cluster's parent cluster info. -1's
569 * indicate we are the root cluster and no parent exists.
570 */
571 ondisk->clu_btree_parent_vol_no = -1;
572 ondisk->clu_btree_parent_clu_no = -1;
8cd0a023
MD
573 ondisk->clu_btree_parent_offset = -1;
574 ondisk->clu_btree_parent_clu_gen = -1;
9a8bf4a7
MD
575
576 /*
577 * Cluster 0 is the root cluster. Set the B-Tree range for this
578 * cluster to the entire key space and format the root directory.
48f71000
MD
579 *
580 * Note that delete_tid for the ending range must be set to 0,
581 * 0 indicates 'not deleted', aka 'the most recent'. See
582 * hammer_btree_cmp() in sys/vfs/hammer/hammer_btree.c.
8cd0a023
MD
583 *
584 * The root cluster's key space represents the entire key space for
585 * the filesystem. The btree_end element appears to be inclusive
586 * only because we can't overflow our variables. It's actually
587 * non-inclusive... that is, it is a right-side boundary element.
9a8bf4a7 588 */
ed3afcca
MD
589 if (isroot) {
590 ondisk->clu_btree_beg.obj_id = -0x8000000000000000LL;
591 ondisk->clu_btree_beg.key = -0x8000000000000000LL;
592 ondisk->clu_btree_beg.create_tid = 0;
593 ondisk->clu_btree_beg.delete_tid = 0;
594 ondisk->clu_btree_beg.rec_type = 0;
595 ondisk->clu_btree_beg.obj_type = 0;
596
597 ondisk->clu_btree_end.obj_id = 0x7FFFFFFFFFFFFFFFLL;
598 ondisk->clu_btree_end.key = 0x7FFFFFFFFFFFFFFFLL;
599 ondisk->clu_btree_end.create_tid = 0xFFFFFFFFFFFFFFFFULL;
48f71000 600 ondisk->clu_btree_end.delete_tid = 0; /* special case */
ed3afcca 601 ondisk->clu_btree_end.rec_type = 0xFFFFU;
8cd0a023 602 ondisk->clu_btree_end.obj_type = 0;
ed3afcca
MD
603
604 format_root(cluster);
9a8bf4a7
MD
605 }
606
607 /*
608 * Write-out and update the index, record, and cluster buffers
609 */
9a8bf4a7
MD
610 return(clno);
611}
612
613/*
ed3afcca 614 * Format the root directory.
9a8bf4a7
MD
615 */
616static
617void
ed3afcca 618format_root(struct cluster_info *cluster)
9a8bf4a7 619{
ed3afcca
MD
620 int32_t btree_off;
621 int32_t rec_off;
622 int32_t data_off;
8cd0a023 623 hammer_node_ondisk_t bnode;
ed3afcca
MD
624 union hammer_record_ondisk *rec;
625 struct hammer_inode_data *idata;
8cd0a023 626 hammer_btree_elm_t elm;
ed3afcca
MD
627
628 bnode = alloc_btree_element(cluster, &btree_off);
629 rec = alloc_record_element(cluster, &rec_off);
630 idata = alloc_data_element(cluster, sizeof(*idata), &data_off);
9a8bf4a7
MD
631
632 /*
ed3afcca 633 * Populate the inode data and inode record for the root directory.
9a8bf4a7 634 */
ed3afcca
MD
635 idata->version = HAMMER_INODE_DATA_VERSION;
636 idata->mode = 0755;
637
7a04d74f 638 rec->base.base.obj_id = HAMMER_OBJID_ROOT;
620822d2
MD
639 rec->base.base.key = 0;
640 rec->base.base.create_tid = createtid();
641 rec->base.base.delete_tid = 0;
642 rec->base.base.rec_type = HAMMER_RECTYPE_INODE;
643 rec->base.base.obj_type = HAMMER_OBJTYPE_DIRECTORY;
ed3afcca
MD
644 rec->base.data_offset = data_off;
645 rec->base.data_len = sizeof(*idata);
646 rec->base.data_crc = crc32(idata, sizeof(*idata));
620822d2
MD
647 rec->inode.ino_atime = rec->base.base.create_tid;
648 rec->inode.ino_mtime = rec->base.base.create_tid;
ed3afcca
MD
649 rec->inode.ino_size = 0;
650 rec->inode.ino_nlinks = 1;
9a8bf4a7 651
fbc6e32a
MD
652 ++cluster->volume->ondisk->vol0_stat_inodes;
653
9a8bf4a7 654 /*
ed3afcca 655 * Assign the cluster's root B-Tree node.
9a8bf4a7 656 */
ed3afcca
MD
657 assert(cluster->ondisk->clu_btree_root == 0);
658 cluster->ondisk->clu_btree_root = btree_off;
9a8bf4a7
MD
659
660 /*
620822d2
MD
661 * Create the root of the B-Tree. The root is a leaf node so we
662 * do not have to worry about boundary elements.
9a8bf4a7 663 */
8cd0a023
MD
664 bnode->count = 1;
665 bnode->type = HAMMER_BTREE_TYPE_LEAF;
620822d2 666
8cd0a023 667 elm = &bnode->elms[0];
620822d2 668 elm->base = rec->base.base;
8cd0a023
MD
669 elm->leaf.rec_offset = rec_off;
670 elm->leaf.data_offset = rec->base.data_offset;
671 elm->leaf.data_len = rec->base.data_len;
672 elm->leaf.data_crc = rec->base.data_crc;
9a8bf4a7
MD
673}
674