]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - usr.sbin/makefs/ffs.c
MFC r294102:
[FreeBSD/stable/8.git] / usr.sbin / makefs / ffs.c
1 /*      $NetBSD: ffs.c,v 1.44 2009/04/28 22:49:26 joerg Exp $   */
2
3 /*
4  * Copyright (c) 2001 Wasabi Systems, Inc.
5  * All rights reserved.
6  *
7  * Written by Luke Mewburn for Wasabi Systems, Inc.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *      This product includes software developed for the NetBSD Project by
20  *      Wasabi Systems, Inc.
21  * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22  *    or promote products derived from this software without specific prior
23  *    written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL WASABI SYSTEMS, INC
29  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  */
37 /*
38  * Copyright (c) 1982, 1986, 1989, 1993
39  *      The Regents of the University of California.  All rights reserved.
40  *
41  * Redistribution and use in source and binary forms, with or without
42  * modification, are permitted provided that the following conditions
43  * are met:
44  * 1. Redistributions of source code must retain the above copyright
45  *    notice, this list of conditions and the following disclaimer.
46  * 2. Redistributions in binary form must reproduce the above copyright
47  *    notice, this list of conditions and the following disclaimer in the
48  *    documentation and/or other materials provided with the distribution.
49  * 3. Neither the name of the University nor the names of its contributors
50  *    may be used to endorse or promote products derived from this software
51  *    without specific prior written permission.
52  *
53  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
54  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
57  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63  * SUCH DAMAGE.
64  *
65  *      @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
66  */
67
68 #include <sys/cdefs.h>
69 __FBSDID("$FreeBSD$");
70
71 #include <sys/param.h>
72
73 #include <sys/mount.h>
74
75 #include <assert.h>
76 #include <errno.h>
77 #include <fcntl.h>
78 #include <stdarg.h>
79 #include <stdint.h>
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <unistd.h>
84
85 #include "makefs.h"
86 #include "ffs.h"
87
88 #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
89 #include <sys/statvfs.h>
90 #endif
91
92 #include <ufs/ufs/dinode.h>
93 #include <ufs/ufs/dir.h>
94 #include <ufs/ffs/fs.h>
95
96
97 #include "ffs/ufs_bswap.h"
98 #include "ffs/ufs_inode.h"
99 #include "ffs/newfs_extern.h"
100 #include "ffs/ffs_extern.h"
101
102 #undef DIP
103 #define DIP(dp, field) \
104         ((ffs_opts->version == 1) ? \
105         (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field)
106
107 /*
108  * Various file system defaults (cribbed from newfs(8)).
109  */
110 #define DFL_FRAGSIZE            1024            /* fragment size */
111 #define DFL_BLKSIZE             8192            /* block size */
112 #define DFL_SECSIZE             512             /* sector size */
113 #define DFL_CYLSPERGROUP        65536           /* cylinders per group */
114 #define DFL_FRAGSPERINODE       4               /* fragments per inode */
115 #define DFL_ROTDELAY            0               /* rotational delay */
116 #define DFL_NRPOS               1               /* rotational positions */
117 #define DFL_RPM                 3600            /* rpm of disk */
118 #define DFL_NSECTORS            64              /* # of sectors */
119 #define DFL_NTRACKS             16              /* # of tracks */
120
121
122 typedef struct {
123         u_char          *buf;           /* buf for directory */
124         doff_t          size;           /* full size of buf */
125         doff_t          cur;            /* offset of current entry */
126 } dirbuf_t;
127
128
129 static  int     ffs_create_image(const char *, fsinfo_t *);
130 static  void    ffs_dump_fsinfo(fsinfo_t *);
131 static  void    ffs_dump_dirbuf(dirbuf_t *, const char *, int);
132 static  void    ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int);
133 static  int     ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
134 static  void    ffs_size_dir(fsnode *, fsinfo_t *);
135 static  void    ffs_validate(const char *, fsnode *, fsinfo_t *);
136 static  void    ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *);
137 static  void    ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *);
138 static  void    *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *,
139                                  fsnode *, fsinfo_t *);
140 static  void    *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *,
141                                  fsnode *, fsinfo_t *);
142
143
144
145 int     sectorsize;             /* XXX: for buf.c::getblk() */
146
147         /* publically visible functions */
148
149 void
150 ffs_prep_opts(fsinfo_t *fsopts)
151 {
152         ffs_opt_t *ffs_opts;
153
154         if ((ffs_opts = calloc(1, sizeof(ffs_opt_t))) == NULL)
155                 err(1, "Allocating memory for ffs_options");
156
157         fsopts->fs_specific = ffs_opts;
158
159         ffs_opts->bsize= -1;
160         ffs_opts->fsize= -1;
161         ffs_opts->cpg= -1;
162         ffs_opts->density= -1;
163         ffs_opts->minfree= -1;
164         ffs_opts->optimization= -1;
165         ffs_opts->maxcontig= -1;
166         ffs_opts->maxbpg= -1;
167         ffs_opts->avgfilesize= -1;
168         ffs_opts->avgfpdir= -1;
169         ffs_opts->version = 1;
170 }
171
172 void
173 ffs_cleanup_opts(fsinfo_t *fsopts)
174 {
175         if (fsopts->fs_specific)
176                 free(fsopts->fs_specific);
177 }
178
179 int
180 ffs_parse_opts(const char *option, fsinfo_t *fsopts)
181 {
182         ffs_opt_t       *ffs_opts = fsopts->fs_specific;
183
184         option_t ffs_options[] = {
185                 { "bsize",      &ffs_opts->bsize,       1,      INT_MAX,
186                                         "block size" },
187                 { "fsize",      &ffs_opts->fsize,       1,      INT_MAX,
188                                         "fragment size" },
189                 { "density",    &ffs_opts->density,     1,      INT_MAX,
190                                         "bytes per inode" },
191                 { "minfree",    &ffs_opts->minfree,     0,      99,
192                                         "minfree" },
193                 { "maxbpf",     &ffs_opts->maxbpg,      1,      INT_MAX,
194                                         "max blocks per file in a cg" },
195                 { "avgfilesize", &ffs_opts->avgfilesize,1,      INT_MAX,
196                                         "expected average file size" },
197                 { "avgfpdir",   &ffs_opts->avgfpdir,    1,      INT_MAX,
198                                         "expected # of files per directory" },
199                 { "extent",     &ffs_opts->maxbsize,    1,      INT_MAX,
200                                         "maximum # extent size" },
201                 { "maxbpcg",    &ffs_opts->maxblkspercg,1,      INT_MAX,
202                                         "max # of blocks per group" },
203                 { "version",    &ffs_opts->version,     1,      2,
204                                         "UFS version" },
205                 { .name = NULL }
206         };
207
208         char    *var, *val;
209         int     rv;
210
211         assert(option != NULL);
212         assert(fsopts != NULL);
213         assert(ffs_opts != NULL);
214
215         if (debug & DEBUG_FS_PARSE_OPTS)
216                 printf("ffs_parse_opts: got `%s'\n", option);
217
218         if ((var = strdup(option)) == NULL)
219                 err(1, "Allocating memory for copy of option string");
220         rv = 0;
221
222         if ((val = strchr(var, '=')) == NULL) {
223                 warnx("Option `%s' doesn't contain a value", var);
224                 goto leave_ffs_parse_opts;
225         }
226         *val++ = '\0';
227
228         if (strcmp(var, "optimization") == 0) {
229                 if (strcmp(val, "time") == 0) {
230                         ffs_opts->optimization = FS_OPTTIME;
231                 } else if (strcmp(val, "space") == 0) {
232                         ffs_opts->optimization = FS_OPTSPACE;
233                 } else {
234                         warnx("Invalid optimization `%s'", val);
235                         goto leave_ffs_parse_opts;
236                 }
237                 rv = 1;
238         } else
239                 rv = set_option(ffs_options, var, val);
240
241  leave_ffs_parse_opts:
242         if (var)
243                 free(var);
244         return (rv);
245 }
246
247
248 void
249 ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
250 {
251         struct fs       *superblock;
252         struct timeval  start;
253
254         assert(image != NULL);
255         assert(dir != NULL);
256         assert(root != NULL);
257         assert(fsopts != NULL);
258
259         if (debug & DEBUG_FS_MAKEFS)
260                 printf("ffs_makefs: image %s directory %s root %p\n",
261                     image, dir, root);
262
263                 /* validate tree and options */
264         TIMER_START(start);
265         ffs_validate(dir, root, fsopts);
266         TIMER_RESULTS(start, "ffs_validate");
267
268         printf("Calculated size of `%s': %lld bytes, %lld inodes\n",
269             image, (long long)fsopts->size, (long long)fsopts->inodes);
270
271                 /* create image */
272         TIMER_START(start);
273         if (ffs_create_image(image, fsopts) == -1)
274                 errx(1, "Image file `%s' not created.", image);
275         TIMER_RESULTS(start, "ffs_create_image");
276
277         fsopts->curinode = ROOTINO;
278
279         if (debug & DEBUG_FS_MAKEFS)
280                 putchar('\n');
281
282                 /* populate image */
283         printf("Populating `%s'\n", image);
284         TIMER_START(start);
285         if (! ffs_populate_dir(dir, root, fsopts))
286                 errx(1, "Image file `%s' not populated.", image);
287         TIMER_RESULTS(start, "ffs_populate_dir");
288
289                 /* ensure no outstanding buffers remain */
290         if (debug & DEBUG_FS_MAKEFS)
291                 bcleanup();
292
293                 /* update various superblock parameters */
294         superblock = fsopts->superblock;
295         superblock->fs_fmod = 0;
296         superblock->fs_old_cstotal.cs_ndir   = superblock->fs_cstotal.cs_ndir;
297         superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree;
298         superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree;
299         superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree;
300
301                 /* write out superblock; image is now complete */
302         ffs_write_superblock(fsopts->superblock, fsopts);
303         if (close(fsopts->fd) == -1)
304                 err(1, "Closing `%s'", image);
305         fsopts->fd = -1;
306         printf("Image `%s' complete\n", image);
307 }
308
309         /* end of public functions */
310
311
312 static void
313 ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
314 {
315         int32_t ncg = 1;
316 #if notyet
317         int32_t spc, nspf, ncyl, fssize;
318 #endif
319         ffs_opt_t       *ffs_opts = fsopts->fs_specific;
320
321         assert(dir != NULL);
322         assert(root != NULL);
323         assert(fsopts != NULL);
324         assert(ffs_opts != NULL);
325
326         if (debug & DEBUG_FS_VALIDATE) {
327                 printf("ffs_validate: before defaults set:\n");
328                 ffs_dump_fsinfo(fsopts);
329         }
330
331                 /* set FFS defaults */
332         if (fsopts->sectorsize == -1)
333                 fsopts->sectorsize = DFL_SECSIZE;
334         if (ffs_opts->fsize == -1)
335                 ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize);
336         if (ffs_opts->bsize == -1)
337                 ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize);
338         if (ffs_opts->cpg == -1)
339                 ffs_opts->cpg = DFL_CYLSPERGROUP;
340         else
341                 ffs_opts->cpgflg = 1;
342                                 /* fsopts->density is set below */
343         if (ffs_opts->nsectors == -1)
344                 ffs_opts->nsectors = DFL_NSECTORS;
345         if (ffs_opts->minfree == -1)
346                 ffs_opts->minfree = MINFREE;
347         if (ffs_opts->optimization == -1)
348                 ffs_opts->optimization = DEFAULTOPT;
349         if (ffs_opts->maxcontig == -1)
350                 ffs_opts->maxcontig =
351                     MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / ffs_opts->bsize);
352         /* XXX ondisk32 */
353         if (ffs_opts->maxbpg == -1)
354                 ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t);
355         if (ffs_opts->avgfilesize == -1)
356                 ffs_opts->avgfilesize = AVFILESIZ;
357         if (ffs_opts->avgfpdir == -1)
358                 ffs_opts->avgfpdir = AFPDIR;
359
360                 /* calculate size of tree */
361         ffs_size_dir(root, fsopts);
362         fsopts->inodes += ROOTINO;              /* include first two inodes */
363
364         if (debug & DEBUG_FS_VALIDATE)
365                 printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n",
366                     (long long)fsopts->size, (long long)fsopts->inodes);
367
368                 /* add requested slop */
369         fsopts->size += fsopts->freeblocks;
370         fsopts->inodes += fsopts->freefiles;
371         if (fsopts->freefilepc > 0)
372                 fsopts->inodes =
373                     fsopts->inodes * (100 + fsopts->freefilepc) / 100;
374         if (fsopts->freeblockpc > 0)
375                 fsopts->size =
376                     fsopts->size * (100 + fsopts->freeblockpc) / 100;
377
378                 /* add space needed for superblocks */
379         /*
380          * The old SBOFF (SBLOCK_UFS1) is used here because makefs is
381          * typically used for small filesystems where space matters.
382          * XXX make this an option.
383          */
384         fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg;
385                 /* add space needed to store inodes, x3 for blockmaps, etc */
386         if (ffs_opts->version == 1)
387                 fsopts->size += ncg * DINODE1_SIZE *
388                     roundup(fsopts->inodes / ncg, 
389                         ffs_opts->bsize / DINODE1_SIZE);
390         else
391                 fsopts->size += ncg * DINODE2_SIZE *
392                     roundup(fsopts->inodes / ncg, 
393                         ffs_opts->bsize / DINODE2_SIZE);
394
395                 /* add minfree */
396         if (ffs_opts->minfree > 0)
397                 fsopts->size =
398                     fsopts->size * (100 + ffs_opts->minfree) / 100;
399         /*
400          * XXX  any other fs slop to add, such as csum's, bitmaps, etc ??
401          */
402
403         if (fsopts->size < fsopts->minsize)     /* ensure meets minimum size */
404                 fsopts->size = fsopts->minsize;
405
406                 /* round up to the next block */
407         fsopts->size = roundup(fsopts->size, ffs_opts->bsize);
408
409                 /* calculate density if necessary */
410         if (ffs_opts->density == -1)
411                 ffs_opts->density = fsopts->size / fsopts->inodes + 1;
412
413         if (debug & DEBUG_FS_VALIDATE) {
414                 printf("ffs_validate: after defaults set:\n");
415                 ffs_dump_fsinfo(fsopts);
416                 printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n",
417                     dir, (long long)fsopts->size, (long long)fsopts->inodes);
418         }
419         sectorsize = fsopts->sectorsize;        /* XXX - see earlier */
420
421                 /* now check calculated sizes vs requested sizes */
422         if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) {
423                 errx(1, "`%s' size of %lld is larger than the maxsize of %lld.",
424                     dir, (long long)fsopts->size, (long long)fsopts->maxsize);
425         }
426 }
427
428
429 static void
430 ffs_dump_fsinfo(fsinfo_t *f)
431 {
432
433         ffs_opt_t       *fs = f->fs_specific;
434
435         printf("fsopts at %p\n", f);
436
437         printf("\tsize %lld, inodes %lld, curinode %u\n",
438             (long long)f->size, (long long)f->inodes, f->curinode);
439
440         printf("\tminsize %lld, maxsize %lld\n",
441             (long long)f->minsize, (long long)f->maxsize);
442         printf("\tfree files %lld, freefile %% %d\n",
443             (long long)f->freefiles, f->freefilepc);
444         printf("\tfree blocks %lld, freeblock %% %d\n",
445             (long long)f->freeblocks, f->freeblockpc);
446         printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize);
447
448         printf("\tbsize %d, fsize %d, cpg %d, density %d\n",
449             fs->bsize, fs->fsize, fs->cpg, fs->density);
450         printf("\tnsectors %d, rpm %d, minfree %d\n",
451             fs->nsectors, fs->rpm, fs->minfree);
452         printf("\tmaxcontig %d, maxbpg %d\n",
453             fs->maxcontig, fs->maxbpg);
454         printf("\toptimization %s\n",
455             fs->optimization == FS_OPTSPACE ? "space" : "time");
456 }
457
458
459 static int
460 ffs_create_image(const char *image, fsinfo_t *fsopts)
461 {
462 #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
463         struct statvfs  sfs;
464 #endif
465         struct fs       *fs;
466         char    *buf;
467         int     i, bufsize;
468         off_t   bufrem;
469
470         assert (image != NULL);
471         assert (fsopts != NULL);
472
473                 /* create image */
474         if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666))
475             == -1) {
476                 warn("Can't open `%s' for writing", image);
477                 return (-1);
478         }
479
480                 /* zero image */
481 #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
482         if (fstatvfs(fsopts->fd, &sfs) == -1) {
483 #endif
484                 bufsize = 8192;
485 #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS
486                 warn("can't fstatvfs `%s', using default %d byte chunk",
487                     image, bufsize);
488         } else
489                 bufsize = sfs.f_iosize;
490 #endif
491         bufrem = fsopts->size;
492         if (debug & DEBUG_FS_CREATE_IMAGE)
493                 printf(
494                     "zero-ing image `%s', %lld sectors, using %d byte chunks\n",
495                     image, (long long)bufrem, bufsize);
496         if ((buf = calloc(1, bufsize)) == NULL) {
497                 warn("Can't create buffer for sector");
498                 return (-1);
499         }
500         while (bufrem > 0) {
501                 i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
502                 if (i == -1) {
503                         warn("zeroing image, %lld bytes to go",
504                             (long long)bufrem);
505                         free(buf);
506                         return (-1);
507                 }
508                 bufrem -= i;
509         }
510         free(buf);
511
512                 /* make the file system */
513         if (debug & DEBUG_FS_CREATE_IMAGE)
514                 printf("calling mkfs(\"%s\", ...)\n", image);
515         fs = ffs_mkfs(image, fsopts);
516         fsopts->superblock = (void *)fs;
517         if (debug & DEBUG_FS_CREATE_IMAGE) {
518                 time_t t;
519
520                 t = (time_t)((struct fs *)fsopts->superblock)->fs_time;
521                 printf("mkfs returned %p; fs_time %s",
522                     fsopts->superblock, ctime(&t));
523                 printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n",
524                     (long long)fs->fs_cstotal.cs_nbfree,
525                     (long long)fs->fs_cstotal.cs_nffree,
526                     (long long)fs->fs_cstotal.cs_nifree,
527                     (long long)fs->fs_cstotal.cs_ndir);
528         }
529
530         if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) {
531                 warnx(
532                 "Image file `%s' has %lld free inodes; %lld are required.",
533                     image,
534                     (long long)(fs->fs_cstotal.cs_nifree + ROOTINO),
535                     (long long)fsopts->inodes);
536                 return (-1);
537         }
538         return (fsopts->fd);
539 }
540
541
542 static void
543 ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
544 {
545         struct direct   tmpdir;
546         fsnode *        node;
547         int             curdirsize, this;
548         ffs_opt_t       *ffs_opts = fsopts->fs_specific;
549
550         /* node may be NULL (empty directory) */
551         assert(fsopts != NULL);
552         assert(ffs_opts != NULL);
553
554         if (debug & DEBUG_FS_SIZE_DIR)
555                 printf("ffs_size_dir: entry: bytes %lld inodes %lld\n",
556                     (long long)fsopts->size, (long long)fsopts->inodes);
557
558 #define ADDDIRENT(e) do {                                               \
559         tmpdir.d_namlen = strlen((e));                                  \
560         this = DIRSIZ_SWAP(0, &tmpdir, 0);                                      \
561         if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT)                       \
562                 printf("ADDDIRENT: was: %s (%d) this %d cur %d\n",      \
563                     e, tmpdir.d_namlen, this, curdirsize);              \
564         if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ))         \
565                 curdirsize = roundup(curdirsize, DIRBLKSIZ);            \
566         curdirsize += this;                                             \
567         if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT)                       \
568                 printf("ADDDIRENT: now: %s (%d) this %d cur %d\n",      \
569                     e, tmpdir.d_namlen, this, curdirsize);              \
570 } while (0);
571
572         /*
573          * XXX  this needs to take into account extra space consumed
574          *      by indirect blocks, etc.
575          */
576 #define ADDSIZE(x) do {                                                 \
577         fsopts->size += roundup((x), ffs_opts->fsize);                  \
578 } while (0);
579
580         curdirsize = 0;
581         for (node = root; node != NULL; node = node->next) {
582                 ADDDIRENT(node->name);
583                 if (node == root) {                     /* we're at "." */
584                         assert(strcmp(node->name, ".") == 0);
585                         ADDDIRENT("..");
586                 } else if ((node->inode->flags & FI_SIZED) == 0) {
587                                 /* don't count duplicate names */
588                         node->inode->flags |= FI_SIZED;
589                         if (debug & DEBUG_FS_SIZE_DIR_NODE)
590                                 printf("ffs_size_dir: `%s' size %lld\n",
591                                     node->name,
592                                     (long long)node->inode->st.st_size);
593                         fsopts->inodes++;
594                         if (node->type == S_IFREG)
595                                 ADDSIZE(node->inode->st.st_size);
596                         if (node->type == S_IFLNK) {
597                                 int     slen;
598
599                                 slen = strlen(node->symlink) + 1;
600                                 if (slen >= (ffs_opts->version == 1 ?
601                                                 MAXSYMLINKLEN_UFS1 :
602                                                 MAXSYMLINKLEN_UFS2))
603                                         ADDSIZE(slen);
604                         }
605                 }
606                 if (node->type == S_IFDIR)
607                         ffs_size_dir(node->child, fsopts);
608         }
609         ADDSIZE(curdirsize);
610
611         if (debug & DEBUG_FS_SIZE_DIR)
612                 printf("ffs_size_dir: exit: size %lld inodes %lld\n",
613                     (long long)fsopts->size, (long long)fsopts->inodes);
614 }
615
616 static void *
617 ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
618                  fsnode *root, fsinfo_t *fsopts)
619 {
620         int slen;
621         void *membuf;
622
623         memset(dinp, 0, sizeof(*dinp));
624         dinp->di_mode = cur->inode->st.st_mode;
625         dinp->di_nlink = cur->inode->nlink;
626         dinp->di_size = cur->inode->st.st_size;
627         dinp->di_atime = cur->inode->st.st_atime;
628         dinp->di_mtime = cur->inode->st.st_mtime;
629         dinp->di_ctime = cur->inode->st.st_ctime;
630 #if HAVE_STRUCT_STAT_ST_MTIMENSEC
631         dinp->di_atimensec = cur->inode->st.st_atimensec;
632         dinp->di_mtimensec = cur->inode->st.st_mtimensec;
633         dinp->di_ctimensec = cur->inode->st.st_ctimensec;
634 #endif
635 #if HAVE_STRUCT_STAT_ST_FLAGS
636         dinp->di_flags = cur->inode->st.st_flags;
637 #endif
638 #if HAVE_STRUCT_STAT_ST_GEN
639         dinp->di_gen = cur->inode->st.st_gen;
640 #endif
641         dinp->di_uid = cur->inode->st.st_uid;
642         dinp->di_gid = cur->inode->st.st_gid;
643                 /* not set: di_db, di_ib, di_blocks, di_spare */
644
645         membuf = NULL;
646         if (cur == root) {                      /* "."; write dirbuf */
647                 membuf = dbufp->buf;
648                 dinp->di_size = dbufp->size;
649         } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
650                 dinp->di_size = 0;      /* a device */
651                 dinp->di_rdev =
652                     ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap);
653         } else if (S_ISLNK(cur->type)) {        /* symlink */
654                 slen = strlen(cur->symlink);
655                 if (slen < MAXSYMLINKLEN_UFS1) {        /* short link */
656                         memcpy(dinp->di_db, cur->symlink, slen);
657                 } else
658                         membuf = cur->symlink;
659                 dinp->di_size = slen;
660         }
661         return membuf;
662 }
663
664 static void *
665 ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
666                  fsnode *root, fsinfo_t *fsopts)
667 {
668         int slen;
669         void *membuf;
670
671         memset(dinp, 0, sizeof(*dinp));
672         dinp->di_mode = cur->inode->st.st_mode;
673         dinp->di_nlink = cur->inode->nlink;
674         dinp->di_size = cur->inode->st.st_size;
675         dinp->di_atime = cur->inode->st.st_atime;
676         dinp->di_mtime = cur->inode->st.st_mtime;
677         dinp->di_ctime = cur->inode->st.st_ctime;
678 #if HAVE_STRUCT_STAT_ST_MTIMENSEC
679         dinp->di_atimensec = cur->inode->st.st_atimensec;
680         dinp->di_mtimensec = cur->inode->st.st_mtimensec;
681         dinp->di_ctimensec = cur->inode->st.st_ctimensec;
682 #endif
683 #if HAVE_STRUCT_STAT_ST_FLAGS
684         dinp->di_flags = cur->inode->st.st_flags;
685 #endif
686 #if HAVE_STRUCT_STAT_ST_GEN
687         dinp->di_gen = cur->inode->st.st_gen;
688 #endif
689 #if HAVE_STRUCT_STAT_BIRTHTIME
690         dinp->di_birthtime = cur->inode->st.st_birthtime;
691         dinp->di_birthnsec = cur->inode->st.st_birthtimensec;
692 #endif
693         dinp->di_uid = cur->inode->st.st_uid;
694         dinp->di_gid = cur->inode->st.st_gid;
695                 /* not set: di_db, di_ib, di_blocks, di_spare */
696
697         membuf = NULL;
698         if (cur == root) {                      /* "."; write dirbuf */
699                 membuf = dbufp->buf;
700                 dinp->di_size = dbufp->size;
701         } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
702                 dinp->di_size = 0;      /* a device */
703                 dinp->di_rdev =
704                     ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap);
705         } else if (S_ISLNK(cur->type)) {        /* symlink */
706                 slen = strlen(cur->symlink);
707                 if (slen < MAXSYMLINKLEN_UFS2) {        /* short link */
708                         memcpy(dinp->di_db, cur->symlink, slen);
709                 } else
710                         membuf = cur->symlink;
711                 dinp->di_size = slen;
712         }
713         return membuf;
714 }
715
716 static int
717 ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts)
718 {
719         fsnode          *cur;
720         dirbuf_t        dirbuf;
721         union dinode    din;
722         void            *membuf;
723         char            path[MAXPATHLEN + 1];
724         ffs_opt_t       *ffs_opts = fsopts->fs_specific;
725
726         assert(dir != NULL);
727         assert(root != NULL);
728         assert(fsopts != NULL);
729         assert(ffs_opts != NULL);
730
731         (void)memset(&dirbuf, 0, sizeof(dirbuf));
732
733         if (debug & DEBUG_FS_POPULATE)
734                 printf("ffs_populate_dir: PASS 1  dir %s node %p\n", dir, root);
735
736                 /*
737                  * pass 1: allocate inode numbers, build directory `file'
738                  */
739         for (cur = root; cur != NULL; cur = cur->next) {
740                 if ((cur->inode->flags & FI_ALLOCATED) == 0) {
741                         cur->inode->flags |= FI_ALLOCATED;
742                         if (cur == root && cur->parent != NULL)
743                                 cur->inode->ino = cur->parent->inode->ino;
744                         else {
745                                 cur->inode->ino = fsopts->curinode;
746                                 fsopts->curinode++;
747                         }
748                 }
749                 ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap);
750                 if (cur == root) {              /* we're at "."; add ".." */
751                         ffs_make_dirbuf(&dirbuf, "..",
752                             cur->parent == NULL ? cur : cur->parent->first,
753                             fsopts->needswap);
754                         root->inode->nlink++;   /* count my parent's link */
755                 } else if (cur->child != NULL)
756                         root->inode->nlink++;   /* count my child's link */
757
758                 /*
759                  * XXX  possibly write file and long symlinks here,
760                  *      ensuring that blocks get written before inodes?
761                  *      otoh, this isn't a real filesystem, so who
762                  *      cares about ordering? :-)
763                  */
764         }
765         if (debug & DEBUG_FS_POPULATE_DIRBUF)
766                 ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap);
767
768                 /*
769                  * pass 2: write out dirbuf, then non-directories at this level
770                  */
771         if (debug & DEBUG_FS_POPULATE)
772                 printf("ffs_populate_dir: PASS 2  dir %s\n", dir);
773         for (cur = root; cur != NULL; cur = cur->next) {
774                 if (cur->inode->flags & FI_WRITTEN)
775                         continue;               /* skip hard-linked entries */
776                 cur->inode->flags |= FI_WRITTEN;
777
778                 if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
779                     >= sizeof(path))
780                         errx(1, "Pathname too long.");
781
782                 if (cur->child != NULL)
783                         continue;               /* child creates own inode */
784
785                                 /* build on-disk inode */
786                 if (ffs_opts->version == 1)
787                         membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur,
788                             root, fsopts);
789                 else
790                         membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur,
791                             root, fsopts);
792
793                 if (debug & DEBUG_FS_POPULATE_NODE) {
794                         printf("ffs_populate_dir: writing ino %d, %s",
795                             cur->inode->ino, inode_type(cur->type));
796                         if (cur->inode->nlink > 1)
797                                 printf(", nlink %d", cur->inode->nlink);
798                         putchar('\n');
799                 }
800
801                 if (membuf != NULL) {
802                         ffs_write_file(&din, cur->inode->ino, membuf, fsopts);
803                 } else if (S_ISREG(cur->type)) {
804                         ffs_write_file(&din, cur->inode->ino, path, fsopts);
805                 } else {
806                         assert (! S_ISDIR(cur->type));
807                         ffs_write_inode(&din, cur->inode->ino, fsopts);
808                 }
809         }
810
811                 /*
812                  * pass 3: write out sub-directories
813                  */
814         if (debug & DEBUG_FS_POPULATE)
815                 printf("ffs_populate_dir: PASS 3  dir %s\n", dir);
816         for (cur = root; cur != NULL; cur = cur->next) {
817                 if (cur->child == NULL)
818                         continue;
819                 if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
820                     >= sizeof(path))
821                         errx(1, "Pathname too long.");
822                 if (! ffs_populate_dir(path, cur->child, fsopts))
823                         return (0);
824         }
825
826         if (debug & DEBUG_FS_POPULATE)
827                 printf("ffs_populate_dir: DONE dir %s\n", dir);
828
829                 /* cleanup */
830         if (dirbuf.buf != NULL)
831                 free(dirbuf.buf);
832         return (1);
833 }
834
835
836 static void
837 ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts)
838 {
839         int     isfile, ffd;
840         char    *fbuf, *p;
841         off_t   bufleft, chunk, offset;
842         ssize_t nread;
843         struct inode    in;
844         struct buf *    bp;
845         ffs_opt_t       *ffs_opts = fsopts->fs_specific;
846
847         assert (din != NULL);
848         assert (buf != NULL);
849         assert (fsopts != NULL);
850         assert (ffs_opts != NULL);
851
852         isfile = S_ISREG(DIP(din, mode));
853         fbuf = NULL;
854         ffd = -1;
855         p = NULL;
856
857         in.i_fs = (struct fs *)fsopts->superblock;
858
859         if (debug & DEBUG_FS_WRITE_FILE) {
860                 printf(
861                     "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld",
862                     ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT),
863                     (long long)DIP(din, size));
864                 if (isfile)
865                         printf(", file '%s'\n", (char *)buf);
866                 else
867                         printf(", buffer %p\n", buf);
868         }
869
870         in.i_number = ino;
871         in.i_size = DIP(din, size);
872         if (ffs_opts->version == 1)
873                 memcpy(&in.i_din.ffs1_din, &din->ffs1_din,
874                     sizeof(in.i_din.ffs1_din));
875         else
876                 memcpy(&in.i_din.ffs2_din, &din->ffs2_din,
877                     sizeof(in.i_din.ffs2_din));
878         in.i_fd = fsopts->fd;
879
880         if (DIP(din, size) == 0)
881                 goto write_inode_and_leave;             /* mmm, cheating */
882
883         if (isfile) {
884                 if ((fbuf = malloc(ffs_opts->bsize)) == NULL)
885                         err(1, "Allocating memory for write buffer");
886                 if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) {
887                         warn("Can't open `%s' for reading", (char *)buf);
888                         goto leave_ffs_write_file;
889                 }
890         } else {
891                 p = buf;
892         }
893
894         chunk = 0;
895         for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) {
896                 chunk = MIN(bufleft, ffs_opts->bsize);
897                 if (!isfile)
898                         ;
899                 else if ((nread = read(ffd, fbuf, chunk)) == -1)
900                         err(EXIT_FAILURE, "Reading `%s', %lld bytes to go",
901                             (char *)buf, (long long)bufleft);
902                 else if (nread != chunk)
903                         errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, "
904                             "read %zd bytes, expected %ju bytes, does "
905                             "metalog size= attribute mismatch source size?",
906                             (char *)buf, (long long)bufleft, nread,
907                             (uintmax_t)chunk);
908                 else
909                         p = fbuf;
910                 offset = DIP(din, size) - bufleft;
911                 if (debug & DEBUG_FS_WRITE_FILE_BLOCK)
912                         printf(
913                 "ffs_write_file: write %p offset %lld size %lld left %lld\n",
914                             p, (long long)offset,
915                             (long long)chunk, (long long)bufleft);
916         /*
917          * XXX  if holey support is desired, do the check here
918          *
919          * XXX  might need to write out last bit in fragroundup
920          *      sized chunk. however, ffs_balloc() handles this for us
921          */
922                 errno = ffs_balloc(&in, offset, chunk, &bp);
923  bad_ffs_write_file:
924                 if (errno != 0)
925                         err(1,
926                             "Writing inode %d (%s), bytes %lld + %lld",
927                             ino,
928                             isfile ? (char *)buf :
929                               inode_type(DIP(din, mode) & S_IFMT),
930                             (long long)offset, (long long)chunk);
931                 memcpy(bp->b_data, p, chunk);
932                 errno = bwrite(bp);
933                 if (errno != 0)
934                         goto bad_ffs_write_file;
935                 brelse(bp);
936                 if (!isfile)
937                         p += chunk;
938         }
939   
940  write_inode_and_leave:
941         ffs_write_inode(&in.i_din, in.i_number, fsopts);
942
943  leave_ffs_write_file:
944         if (fbuf)
945                 free(fbuf);
946         if (ffd != -1)
947                 close(ffd);
948 }
949
950
951 static void
952 ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap)
953 {
954         doff_t          i;
955         struct direct   *de;
956         uint16_t        reclen;
957
958         assert (dbuf != NULL);
959         assert (dir != NULL);
960         printf("ffs_dump_dirbuf: dir %s size %d cur %d\n",
961             dir, dbuf->size, dbuf->cur);
962
963         for (i = 0; i < dbuf->size; ) {
964                 de = (struct direct *)(dbuf->buf + i);
965                 reclen = ufs_rw16(de->d_reclen, needswap);
966                 printf(
967             " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n",
968                     ufs_rw32(de->d_ino, needswap),
969                     inode_type(DTTOIF(de->d_type)), i, reclen,
970                     de->d_namlen, de->d_name);
971                 i += reclen;
972                 assert(reclen > 0);
973         }
974 }
975
976 static void
977 ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap)
978 {
979         struct direct   de, *dp;
980         uint16_t        llen, reclen;
981         u_char          *newbuf;
982
983         assert (dbuf != NULL);
984         assert (name != NULL);
985         assert (node != NULL);
986                                         /* create direct entry */
987         (void)memset(&de, 0, sizeof(de));
988         de.d_ino = ufs_rw32(node->inode->ino, needswap);
989         de.d_type = IFTODT(node->type);
990         de.d_namlen = (uint8_t)strlen(name);
991         strcpy(de.d_name, name);
992         reclen = DIRSIZ_SWAP(0, &de, needswap);
993         de.d_reclen = ufs_rw16(reclen, needswap);
994
995         dp = (struct direct *)(dbuf->buf + dbuf->cur);
996         llen = 0;
997         if (dp != NULL)
998                 llen = DIRSIZ_SWAP(0, dp, needswap);
999
1000         if (debug & DEBUG_FS_MAKE_DIRBUF)
1001                 printf(
1002                     "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n"
1003                     "  ino %d type %d reclen %d namlen %d name %.30s\n",
1004                     dbuf->size, dbuf->cur, llen,
1005                     ufs_rw32(de.d_ino, needswap), de.d_type, reclen,
1006                     de.d_namlen, de.d_name);
1007
1008         if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) {
1009                 if (debug & DEBUG_FS_MAKE_DIRBUF)
1010                         printf("ffs_make_dirbuf: growing buf to %d\n",
1011                             dbuf->size + DIRBLKSIZ);
1012                 if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL)
1013                         err(1, "Allocating memory for directory buffer");
1014                 dbuf->buf = newbuf;
1015                 dbuf->size += DIRBLKSIZ;
1016                 memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ);
1017                 dbuf->cur = dbuf->size - DIRBLKSIZ;
1018         } else if (dp) {                        /* shrink end of previous */
1019                 dp->d_reclen = ufs_rw16(llen,needswap);
1020                 dbuf->cur += llen;
1021         }
1022         dp = (struct direct *)(dbuf->buf + dbuf->cur);
1023         memcpy(dp, &de, reclen);
1024         dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap);
1025 }
1026
1027 /*
1028  * cribbed from sys/ufs/ffs/ffs_alloc.c
1029  */
1030 static void
1031 ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts)
1032 {
1033         char            *buf;
1034         struct ufs1_dinode *dp1;
1035         struct ufs2_dinode *dp2, *dip;
1036         struct cg       *cgp;
1037         struct fs       *fs;
1038         int             cg, cgino, i;
1039         daddr_t         d;
1040         char            sbbuf[FFS_MAXBSIZE];
1041         int32_t         initediblk;
1042         ffs_opt_t       *ffs_opts = fsopts->fs_specific;
1043
1044         assert (dp != NULL);
1045         assert (ino > 0);
1046         assert (fsopts != NULL);
1047         assert (ffs_opts != NULL);
1048
1049         fs = (struct fs *)fsopts->superblock;
1050         cg = ino_to_cg(fs, ino);
1051         cgino = ino % fs->fs_ipg;
1052         if (debug & DEBUG_FS_WRITE_INODE)
1053                 printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n",
1054                     dp, ino, cg, cgino);
1055
1056         ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
1057             fsopts);
1058         cgp = (struct cg *)sbbuf;
1059         if (!cg_chkmagic_swap(cgp, fsopts->needswap))
1060                 errx(1, "ffs_write_inode: cg %d: bad magic number", cg);
1061
1062         assert (isclr(cg_inosused_swap(cgp, fsopts->needswap), cgino));
1063
1064         buf = malloc(fs->fs_bsize);
1065         if (buf == NULL)
1066                 errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg);
1067
1068         dp1 = (struct ufs1_dinode *)buf;
1069         dp2 = (struct ufs2_dinode *)buf;
1070
1071         if (fs->fs_cstotal.cs_nifree == 0)
1072                 errx(1, "ffs_write_inode: fs out of inodes for ino %u",
1073                     ino);
1074         if (fs->fs_cs(fs, cg).cs_nifree == 0)
1075                 errx(1,
1076                     "ffs_write_inode: cg %d out of inodes for ino %u",
1077                     cg, ino);
1078         setbit(cg_inosused_swap(cgp, fsopts->needswap), cgino);
1079         ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap);
1080         fs->fs_cstotal.cs_nifree--;
1081         fs->fs_cs(fs, cg).cs_nifree--;
1082         if (S_ISDIR(DIP(dp, mode))) {
1083                 ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap);
1084                 fs->fs_cstotal.cs_ndir++;
1085                 fs->fs_cs(fs, cg).cs_ndir++; 
1086         }
1087
1088         /*
1089          * Initialize inode blocks on the fly for UFS2.
1090          */
1091         initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap);
1092         if (ffs_opts->version == 2 && cgino + INOPB(fs) > initediblk &&
1093             initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) {
1094                 memset(buf, 0, fs->fs_bsize);
1095                 dip = (struct ufs2_dinode *)buf;
1096                 srandom(time(NULL));
1097                 for (i = 0; i < INOPB(fs); i++) {
1098                         dip->di_gen = random() / 2 + 1;
1099                         dip++;
1100                 }
1101                 ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs,
1102                                   cg * fs->fs_ipg + initediblk)),
1103                     fs->fs_bsize, buf, fsopts);
1104                 initediblk += INOPB(fs);
1105                 cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap);
1106         }
1107
1108
1109         ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
1110             fsopts);
1111
1112                                         /* now write inode */
1113         d = fsbtodb(fs, ino_to_fsba(fs, ino));
1114         ffs_rdfs(d, fs->fs_bsize, buf, fsopts);
1115         if (fsopts->needswap) {
1116                 if (ffs_opts->version == 1)
1117                         ffs_dinode1_swap(&dp->ffs1_din,
1118                             &dp1[ino_to_fsbo(fs, ino)]);
1119                 else
1120                         ffs_dinode2_swap(&dp->ffs2_din,
1121                             &dp2[ino_to_fsbo(fs, ino)]);
1122         } else {
1123                 if (ffs_opts->version == 1)
1124                         dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din;
1125                 else
1126                         dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din;
1127         }
1128         ffs_wtfs(d, fs->fs_bsize, buf, fsopts);
1129         free(buf);
1130 }
1131
1132 void
1133 panic(const char *fmt, ...)
1134 {
1135         va_list ap;
1136
1137         va_start(ap, fmt);
1138         vwarnx(fmt, ap);
1139         va_end(ap);
1140         exit(1);
1141 }