diff options
Diffstat (limited to 'usr.sbin/makefs/ffs.c')
-rw-r--r-- | usr.sbin/makefs/ffs.c | 1161 |
1 files changed, 1161 insertions, 0 deletions
diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c new file mode 100644 index 0000000..0417d86 --- /dev/null +++ b/usr.sbin/makefs/ffs.c @@ -0,0 +1,1161 @@ +/* $NetBSD: ffs.c,v 1.44 2009/04/28 22:49:26 joerg Exp $ */ + +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Luke Mewburn for Wasabi Systems, Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Copyright (c) 1982, 1986, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <sys/mount.h> + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "makefs.h" +#include "ffs.h" + +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS +#include <sys/statvfs.h> +#endif + +#include <ufs/ufs/dinode.h> +#include <ufs/ufs/dir.h> +#include <ufs/ffs/fs.h> + + +#include "ffs/ufs_bswap.h" +#include "ffs/ufs_inode.h" +#include "ffs/newfs_extern.h" +#include "ffs/ffs_extern.h" + +#undef DIP +#define DIP(dp, field) \ + ((ffs_opts->version == 1) ? \ + (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field) + +/* + * Various file system defaults (cribbed from newfs(8)). + */ +#define DFL_FRAGSIZE 1024 /* fragment size */ +#define DFL_BLKSIZE 8192 /* block size */ +#define DFL_SECSIZE 512 /* sector size */ +#define DFL_CYLSPERGROUP 65536 /* cylinders per group */ +#define DFL_FRAGSPERINODE 4 /* fragments per inode */ +#define DFL_ROTDELAY 0 /* rotational delay */ +#define DFL_NRPOS 1 /* rotational positions */ +#define DFL_RPM 3600 /* rpm of disk */ +#define DFL_NSECTORS 64 /* # of sectors */ +#define DFL_NTRACKS 16 /* # of tracks */ + + +typedef struct { + u_char *buf; /* buf for directory */ + doff_t size; /* full size of buf */ + doff_t cur; /* offset of current entry */ +} dirbuf_t; + + +static int ffs_create_image(const char *, fsinfo_t *); +static void ffs_dump_fsinfo(fsinfo_t *); +static void ffs_dump_dirbuf(dirbuf_t *, const char *, int); +static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int); +static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *); +static void ffs_size_dir(fsnode *, fsinfo_t *); +static void ffs_validate(const char *, fsnode *, fsinfo_t *); +static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *); +static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *); +static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *, + fsnode *, fsinfo_t *); +static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *, + fsnode *, fsinfo_t *); + + + +int sectorsize; /* XXX: for buf.c::getblk() */ + + /* publicly visible functions */ + +void +ffs_prep_opts(fsinfo_t *fsopts) +{ + ffs_opt_t *ffs_opts; + + if ((ffs_opts = calloc(1, sizeof(ffs_opt_t))) == NULL) + err(1, "Allocating memory for ffs_options"); + + fsopts->fs_specific = ffs_opts; + + ffs_opts->bsize= -1; + ffs_opts->fsize= -1; + ffs_opts->cpg= -1; + ffs_opts->density= -1; + ffs_opts->minfree= -1; + ffs_opts->optimization= -1; + ffs_opts->maxcontig= -1; + ffs_opts->maxbpg= -1; + ffs_opts->avgfilesize= -1; + ffs_opts->avgfpdir= -1; + ffs_opts->version = 1; +} + +void +ffs_cleanup_opts(fsinfo_t *fsopts) +{ + if (fsopts->fs_specific) + free(fsopts->fs_specific); +} + +int +ffs_parse_opts(const char *option, fsinfo_t *fsopts) +{ + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + option_t ffs_options[] = { + { "bsize", &ffs_opts->bsize, 1, INT_MAX, + "block size" }, + { "fsize", &ffs_opts->fsize, 1, INT_MAX, + "fragment size" }, + { "density", &ffs_opts->density, 1, INT_MAX, + "bytes per inode" }, + { "minfree", &ffs_opts->minfree, 0, 99, + "minfree" }, + { "maxbpf", &ffs_opts->maxbpg, 1, INT_MAX, + "max blocks per file in a cg" }, + { "avgfilesize", &ffs_opts->avgfilesize,1, INT_MAX, + "expected average file size" }, + { "avgfpdir", &ffs_opts->avgfpdir, 1, INT_MAX, + "expected # of files per directory" }, + { "extent", &ffs_opts->maxbsize, 1, INT_MAX, + "maximum # extent size" }, + { "maxbpcg", &ffs_opts->maxblkspercg,1, INT_MAX, + "max # of blocks per group" }, + { "version", &ffs_opts->version, 1, 2, + "UFS version" }, + { .name = NULL } + }; + + char *var, *val; + int rv; + + assert(option != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_PARSE_OPTS) + printf("ffs_parse_opts: got `%s'\n", option); + + if ((var = strdup(option)) == NULL) + err(1, "Allocating memory for copy of option string"); + rv = 0; + + if ((val = strchr(var, '=')) == NULL) { + warnx("Option `%s' doesn't contain a value", var); + goto leave_ffs_parse_opts; + } + *val++ = '\0'; + + if (strcmp(var, "optimization") == 0) { + if (strcmp(val, "time") == 0) { + ffs_opts->optimization = FS_OPTTIME; + } else if (strcmp(val, "space") == 0) { + ffs_opts->optimization = FS_OPTSPACE; + } else { + warnx("Invalid optimization `%s'", val); + goto leave_ffs_parse_opts; + } + rv = 1; + } else if (strcmp(var, "label") == 0) { + strlcpy(ffs_opts->label, val, sizeof(ffs_opts->label)); + rv = 1; + } else + rv = set_option(ffs_options, var, val); + + leave_ffs_parse_opts: + if (var) + free(var); + return (rv); +} + + +void +ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + struct fs *superblock; + struct timeval start; + + assert(image != NULL); + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + + if (debug & DEBUG_FS_MAKEFS) + printf("ffs_makefs: image %s directory %s root %p\n", + image, dir, root); + + /* validate tree and options */ + TIMER_START(start); + ffs_validate(dir, root, fsopts); + TIMER_RESULTS(start, "ffs_validate"); + + printf("Calculated size of `%s': %lld bytes, %lld inodes\n", + image, (long long)fsopts->size, (long long)fsopts->inodes); + + /* create image */ + TIMER_START(start); + if (ffs_create_image(image, fsopts) == -1) + errx(1, "Image file `%s' not created.", image); + TIMER_RESULTS(start, "ffs_create_image"); + + fsopts->curinode = ROOTINO; + + if (debug & DEBUG_FS_MAKEFS) + putchar('\n'); + + /* populate image */ + printf("Populating `%s'\n", image); + TIMER_START(start); + if (! ffs_populate_dir(dir, root, fsopts)) + errx(1, "Image file `%s' not populated.", image); + TIMER_RESULTS(start, "ffs_populate_dir"); + + /* ensure no outstanding buffers remain */ + if (debug & DEBUG_FS_MAKEFS) + bcleanup(); + + /* update various superblock parameters */ + superblock = fsopts->superblock; + superblock->fs_fmod = 0; + superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir; + superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree; + superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree; + superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree; + + /* write out superblock; image is now complete */ + ffs_write_superblock(fsopts->superblock, fsopts); + if (close(fsopts->fd) == -1) + err(1, "Closing `%s'", image); + fsopts->fd = -1; + printf("Image `%s' complete\n", image); +} + + /* end of public functions */ + + +static void +ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + int32_t ncg = 1; +#if notyet + int32_t spc, nspf, ncyl, fssize; +#endif + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_VALIDATE) { + printf("ffs_validate: before defaults set:\n"); + ffs_dump_fsinfo(fsopts); + } + + /* set FFS defaults */ + if (fsopts->sectorsize == -1) + fsopts->sectorsize = DFL_SECSIZE; + if (ffs_opts->fsize == -1) + ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize); + if (ffs_opts->bsize == -1) + ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize); + if (ffs_opts->cpg == -1) + ffs_opts->cpg = DFL_CYLSPERGROUP; + else + ffs_opts->cpgflg = 1; + /* fsopts->density is set below */ + if (ffs_opts->nsectors == -1) + ffs_opts->nsectors = DFL_NSECTORS; + if (ffs_opts->minfree == -1) + ffs_opts->minfree = MINFREE; + if (ffs_opts->optimization == -1) + ffs_opts->optimization = DEFAULTOPT; + if (ffs_opts->maxcontig == -1) + ffs_opts->maxcontig = + MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / ffs_opts->bsize); + /* XXX ondisk32 */ + if (ffs_opts->maxbpg == -1) + ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t); + if (ffs_opts->avgfilesize == -1) + ffs_opts->avgfilesize = AVFILESIZ; + if (ffs_opts->avgfpdir == -1) + ffs_opts->avgfpdir = AFPDIR; + + /* calculate size of tree */ + ffs_size_dir(root, fsopts); + fsopts->inodes += ROOTINO; /* include first two inodes */ + + if (debug & DEBUG_FS_VALIDATE) + printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n", + (long long)fsopts->size, (long long)fsopts->inodes); + + /* add requested slop */ + fsopts->size += fsopts->freeblocks; + fsopts->inodes += fsopts->freefiles; + if (fsopts->freefilepc > 0) + fsopts->inodes = + fsopts->inodes * (100 + fsopts->freefilepc) / 100; + if (fsopts->freeblockpc > 0) + fsopts->size = + fsopts->size * (100 + fsopts->freeblockpc) / 100; + + /* add space needed for superblocks */ + /* + * The old SBOFF (SBLOCK_UFS1) is used here because makefs is + * typically used for small filesystems where space matters. + * XXX make this an option. + */ + fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg; + /* add space needed to store inodes, x3 for blockmaps, etc */ + if (ffs_opts->version == 1) + fsopts->size += ncg * DINODE1_SIZE * + roundup(fsopts->inodes / ncg, + ffs_opts->bsize / DINODE1_SIZE); + else + fsopts->size += ncg * DINODE2_SIZE * + roundup(fsopts->inodes / ncg, + ffs_opts->bsize / DINODE2_SIZE); + + /* add minfree */ + if (ffs_opts->minfree > 0) + fsopts->size = + fsopts->size * (100 + ffs_opts->minfree) / 100; + /* + * XXX any other fs slop to add, such as csum's, bitmaps, etc ?? + */ + + if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */ + fsopts->size = fsopts->minsize; + + /* round up to the next block */ + fsopts->size = roundup(fsopts->size, ffs_opts->bsize); + + /* calculate density if necessary */ + if (ffs_opts->density == -1) + ffs_opts->density = fsopts->size / fsopts->inodes + 1; + + if (debug & DEBUG_FS_VALIDATE) { + printf("ffs_validate: after defaults set:\n"); + ffs_dump_fsinfo(fsopts); + printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n", + dir, (long long)fsopts->size, (long long)fsopts->inodes); + } + sectorsize = fsopts->sectorsize; /* XXX - see earlier */ + + /* now check calculated sizes vs requested sizes */ + if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) { + errx(1, "`%s' size of %lld is larger than the maxsize of %lld.", + dir, (long long)fsopts->size, (long long)fsopts->maxsize); + } +} + + +static void +ffs_dump_fsinfo(fsinfo_t *f) +{ + + ffs_opt_t *fs = f->fs_specific; + + printf("fsopts at %p\n", f); + + printf("\tsize %lld, inodes %lld, curinode %u\n", + (long long)f->size, (long long)f->inodes, f->curinode); + + printf("\tminsize %lld, maxsize %lld\n", + (long long)f->minsize, (long long)f->maxsize); + printf("\tfree files %lld, freefile %% %d\n", + (long long)f->freefiles, f->freefilepc); + printf("\tfree blocks %lld, freeblock %% %d\n", + (long long)f->freeblocks, f->freeblockpc); + printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize); + + printf("\tbsize %d, fsize %d, cpg %d, density %d\n", + fs->bsize, fs->fsize, fs->cpg, fs->density); + printf("\tnsectors %d, rpm %d, minfree %d\n", + fs->nsectors, fs->rpm, fs->minfree); + printf("\tmaxcontig %d, maxbpg %d\n", + fs->maxcontig, fs->maxbpg); + printf("\toptimization %s\n", + fs->optimization == FS_OPTSPACE ? "space" : "time"); +} + + +static int +ffs_create_image(const char *image, fsinfo_t *fsopts) +{ +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS + struct statvfs sfs; +#endif + struct fs *fs; + char *buf; + int i, bufsize; + off_t bufrem; + + assert (image != NULL); + assert (fsopts != NULL); + + /* create image */ + if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) + == -1) { + warn("Can't open `%s' for writing", image); + return (-1); + } + + /* zero image */ +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS + if (fstatvfs(fsopts->fd, &sfs) == -1) { +#endif + bufsize = 8192; +#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS + warn("can't fstatvfs `%s', using default %d byte chunk", + image, bufsize); + } else + bufsize = sfs.f_iosize; +#endif + bufrem = fsopts->size; + if (fsopts->sparse) { + if (ftruncate(fsopts->fd, bufrem) == -1) { + warn("sparse option disabled.\n"); + fsopts->sparse = 0; + } + } + if (fsopts->sparse) { + /* File truncated at bufrem. Remaining is 0 */ + bufrem = 0; + buf = NULL; + } else { + if (debug & DEBUG_FS_CREATE_IMAGE) + printf("zero-ing image `%s', %lld sectors, " + "using %d byte chunks\n", image, (long long)bufrem, + bufsize); + if ((buf = calloc(1, bufsize)) == NULL) { + warn("Can't create buffer for sector"); + return (-1); + } + } + while (bufrem > 0) { + i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); + if (i == -1) { + warn("zeroing image, %lld bytes to go", + (long long)bufrem); + free(buf); + return (-1); + } + bufrem -= i; + } + if (buf) + free(buf); + + /* make the file system */ + if (debug & DEBUG_FS_CREATE_IMAGE) + printf("calling mkfs(\"%s\", ...)\n", image); + fs = ffs_mkfs(image, fsopts); + fsopts->superblock = (void *)fs; + if (debug & DEBUG_FS_CREATE_IMAGE) { + time_t t; + + t = (time_t)((struct fs *)fsopts->superblock)->fs_time; + printf("mkfs returned %p; fs_time %s", + fsopts->superblock, ctime(&t)); + printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n", + (long long)fs->fs_cstotal.cs_nbfree, + (long long)fs->fs_cstotal.cs_nffree, + (long long)fs->fs_cstotal.cs_nifree, + (long long)fs->fs_cstotal.cs_ndir); + } + + if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) { + warnx( + "Image file `%s' has %lld free inodes; %lld are required.", + image, + (long long)(fs->fs_cstotal.cs_nifree + ROOTINO), + (long long)fsopts->inodes); + return (-1); + } + return (fsopts->fd); +} + + +static void +ffs_size_dir(fsnode *root, fsinfo_t *fsopts) +{ + struct direct tmpdir; + fsnode * node; + int curdirsize, this; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + /* node may be NULL (empty directory) */ + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + if (debug & DEBUG_FS_SIZE_DIR) + printf("ffs_size_dir: entry: bytes %lld inodes %lld\n", + (long long)fsopts->size, (long long)fsopts->inodes); + +#define ADDDIRENT(e) do { \ + tmpdir.d_namlen = strlen((e)); \ + this = DIRSIZ_SWAP(0, &tmpdir, 0); \ + if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ + printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \ + e, tmpdir.d_namlen, this, curdirsize); \ + if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \ + curdirsize = roundup(curdirsize, DIRBLKSIZ); \ + curdirsize += this; \ + if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ + printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \ + e, tmpdir.d_namlen, this, curdirsize); \ +} while (0); + + /* + * XXX this needs to take into account extra space consumed + * by indirect blocks, etc. + */ +#define ADDSIZE(x) do { \ + fsopts->size += roundup((x), ffs_opts->fsize); \ +} while (0); + + curdirsize = 0; + for (node = root; node != NULL; node = node->next) { + ADDDIRENT(node->name); + if (node == root) { /* we're at "." */ + assert(strcmp(node->name, ".") == 0); + ADDDIRENT(".."); + } else if ((node->inode->flags & FI_SIZED) == 0) { + /* don't count duplicate names */ + node->inode->flags |= FI_SIZED; + if (debug & DEBUG_FS_SIZE_DIR_NODE) + printf("ffs_size_dir: `%s' size %lld\n", + node->name, + (long long)node->inode->st.st_size); + fsopts->inodes++; + if (node->type == S_IFREG) + ADDSIZE(node->inode->st.st_size); + if (node->type == S_IFLNK) { + int slen; + + slen = strlen(node->symlink) + 1; + if (slen >= (ffs_opts->version == 1 ? + MAXSYMLINKLEN_UFS1 : + MAXSYMLINKLEN_UFS2)) + ADDSIZE(slen); + } + } + if (node->type == S_IFDIR) + ffs_size_dir(node->child, fsopts); + } + ADDSIZE(curdirsize); + + if (debug & DEBUG_FS_SIZE_DIR) + printf("ffs_size_dir: exit: size %lld inodes %lld\n", + (long long)fsopts->size, (long long)fsopts->inodes); +} + +static void * +ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, + fsnode *root, fsinfo_t *fsopts) +{ + int slen; + void *membuf; + + memset(dinp, 0, sizeof(*dinp)); + dinp->di_mode = cur->inode->st.st_mode; + dinp->di_nlink = cur->inode->nlink; + dinp->di_size = cur->inode->st.st_size; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#if HAVE_STRUCT_STAT_ST_FLAGS + dinp->di_flags = cur->inode->st.st_flags; +#endif +#if HAVE_STRUCT_STAT_ST_GEN + dinp->di_gen = cur->inode->st.st_gen; +#endif + dinp->di_uid = cur->inode->st.st_uid; + dinp->di_gid = cur->inode->st.st_gid; + /* not set: di_db, di_ib, di_blocks, di_spare */ + + membuf = NULL; + if (cur == root) { /* "."; write dirbuf */ + membuf = dbufp->buf; + dinp->di_size = dbufp->size; + } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { + dinp->di_size = 0; /* a device */ + dinp->di_rdev = + ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap); + } else if (S_ISLNK(cur->type)) { /* symlink */ + slen = strlen(cur->symlink); + if (slen < MAXSYMLINKLEN_UFS1) { /* short link */ + memcpy(dinp->di_db, cur->symlink, slen); + } else + membuf = cur->symlink; + dinp->di_size = slen; + } + return membuf; +} + +static void * +ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, + fsnode *root, fsinfo_t *fsopts) +{ + int slen; + void *membuf; + + memset(dinp, 0, sizeof(*dinp)); + dinp->di_mode = cur->inode->st.st_mode; + dinp->di_nlink = cur->inode->nlink; + dinp->di_size = cur->inode->st.st_size; + dinp->di_atime = cur->inode->st.st_atime; + dinp->di_mtime = cur->inode->st.st_mtime; + dinp->di_ctime = cur->inode->st.st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + dinp->di_atimensec = cur->inode->st.st_atimensec; + dinp->di_mtimensec = cur->inode->st.st_mtimensec; + dinp->di_ctimensec = cur->inode->st.st_ctimensec; +#endif +#if HAVE_STRUCT_STAT_ST_FLAGS + dinp->di_flags = cur->inode->st.st_flags; +#endif +#if HAVE_STRUCT_STAT_ST_GEN + dinp->di_gen = cur->inode->st.st_gen; +#endif +#if HAVE_STRUCT_STAT_BIRTHTIME + dinp->di_birthtime = cur->inode->st.st_birthtime; + dinp->di_birthnsec = cur->inode->st.st_birthtimensec; +#endif + dinp->di_uid = cur->inode->st.st_uid; + dinp->di_gid = cur->inode->st.st_gid; + /* not set: di_db, di_ib, di_blocks, di_spare */ + + membuf = NULL; + if (cur == root) { /* "."; write dirbuf */ + membuf = dbufp->buf; + dinp->di_size = dbufp->size; + } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { + dinp->di_size = 0; /* a device */ + dinp->di_rdev = + ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap); + } else if (S_ISLNK(cur->type)) { /* symlink */ + slen = strlen(cur->symlink); + if (slen < MAXSYMLINKLEN_UFS2) { /* short link */ + memcpy(dinp->di_db, cur->symlink, slen); + } else + membuf = cur->symlink; + dinp->di_size = slen; + } + return membuf; +} + +static int +ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts) +{ + fsnode *cur; + dirbuf_t dirbuf; + union dinode din; + void *membuf; + char path[MAXPATHLEN + 1]; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert(dir != NULL); + assert(root != NULL); + assert(fsopts != NULL); + assert(ffs_opts != NULL); + + (void)memset(&dirbuf, 0, sizeof(dirbuf)); + + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root); + + /* + * pass 1: allocate inode numbers, build directory `file' + */ + for (cur = root; cur != NULL; cur = cur->next) { + if ((cur->inode->flags & FI_ALLOCATED) == 0) { + cur->inode->flags |= FI_ALLOCATED; + if (cur == root && cur->parent != NULL) + cur->inode->ino = cur->parent->inode->ino; + else { + cur->inode->ino = fsopts->curinode; + fsopts->curinode++; + } + } + ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap); + if (cur == root) { /* we're at "."; add ".." */ + ffs_make_dirbuf(&dirbuf, "..", + cur->parent == NULL ? cur : cur->parent->first, + fsopts->needswap); + root->inode->nlink++; /* count my parent's link */ + } else if (cur->child != NULL) + root->inode->nlink++; /* count my child's link */ + + /* + * XXX possibly write file and long symlinks here, + * ensuring that blocks get written before inodes? + * otoh, this isn't a real filesystem, so who + * cares about ordering? :-) + */ + } + if (debug & DEBUG_FS_POPULATE_DIRBUF) + ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap); + + /* + * pass 2: write out dirbuf, then non-directories at this level + */ + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 2 dir %s\n", dir); + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->inode->flags & FI_WRITTEN) + continue; /* skip hard-linked entries */ + cur->inode->flags |= FI_WRITTEN; + + if (cur->contents == NULL) { + if (snprintf(path, sizeof(path), "%s/%s/%s", cur->root, + cur->path, cur->name) >= (int)sizeof(path)) + errx(1, "Pathname too long."); + } + + if (cur->child != NULL) + continue; /* child creates own inode */ + + /* build on-disk inode */ + if (ffs_opts->version == 1) + membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur, + root, fsopts); + else + membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur, + root, fsopts); + + if (debug & DEBUG_FS_POPULATE_NODE) { + printf("ffs_populate_dir: writing ino %d, %s", + cur->inode->ino, inode_type(cur->type)); + if (cur->inode->nlink > 1) + printf(", nlink %d", cur->inode->nlink); + putchar('\n'); + } + + if (membuf != NULL) { + ffs_write_file(&din, cur->inode->ino, membuf, fsopts); + } else if (S_ISREG(cur->type)) { + ffs_write_file(&din, cur->inode->ino, + (cur->contents) ? cur->contents : path, fsopts); + } else { + assert (! S_ISDIR(cur->type)); + ffs_write_inode(&din, cur->inode->ino, fsopts); + } + } + + /* + * pass 3: write out sub-directories + */ + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: PASS 3 dir %s\n", dir); + for (cur = root; cur != NULL; cur = cur->next) { + if (cur->child == NULL) + continue; + if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) + >= sizeof(path)) + errx(1, "Pathname too long."); + if (! ffs_populate_dir(path, cur->child, fsopts)) + return (0); + } + + if (debug & DEBUG_FS_POPULATE) + printf("ffs_populate_dir: DONE dir %s\n", dir); + + /* cleanup */ + if (dirbuf.buf != NULL) + free(dirbuf.buf); + return (1); +} + + +static void +ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts) +{ + int isfile, ffd; + char *fbuf, *p; + off_t bufleft, chunk, offset; + ssize_t nread; + struct inode in; + struct buf * bp; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert (din != NULL); + assert (buf != NULL); + assert (fsopts != NULL); + assert (ffs_opts != NULL); + + isfile = S_ISREG(DIP(din, mode)); + fbuf = NULL; + ffd = -1; + p = NULL; + + in.i_fs = (struct fs *)fsopts->superblock; + + if (debug & DEBUG_FS_WRITE_FILE) { + printf( + "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld", + ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT), + (long long)DIP(din, size)); + if (isfile) + printf(", file '%s'\n", (char *)buf); + else + printf(", buffer %p\n", buf); + } + + in.i_number = ino; + in.i_size = DIP(din, size); + if (ffs_opts->version == 1) + memcpy(&in.i_din.ffs1_din, &din->ffs1_din, + sizeof(in.i_din.ffs1_din)); + else + memcpy(&in.i_din.ffs2_din, &din->ffs2_din, + sizeof(in.i_din.ffs2_din)); + in.i_fd = fsopts->fd; + + if (DIP(din, size) == 0) + goto write_inode_and_leave; /* mmm, cheating */ + + if (isfile) { + if ((fbuf = malloc(ffs_opts->bsize)) == NULL) + err(1, "Allocating memory for write buffer"); + if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) { + warn("Can't open `%s' for reading", (char *)buf); + goto leave_ffs_write_file; + } + } else { + p = buf; + } + + chunk = 0; + for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) { + chunk = MIN(bufleft, ffs_opts->bsize); + if (!isfile) + ; + else if ((nread = read(ffd, fbuf, chunk)) == -1) + err(EXIT_FAILURE, "Reading `%s', %lld bytes to go", + (char *)buf, (long long)bufleft); + else if (nread != chunk) + errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, " + "read %zd bytes, expected %ju bytes, does " + "metalog size= attribute mismatch source size?", + (char *)buf, (long long)bufleft, nread, + (uintmax_t)chunk); + else + p = fbuf; + offset = DIP(din, size) - bufleft; + if (debug & DEBUG_FS_WRITE_FILE_BLOCK) + printf( + "ffs_write_file: write %p offset %lld size %lld left %lld\n", + p, (long long)offset, + (long long)chunk, (long long)bufleft); + /* + * XXX if holey support is desired, do the check here + * + * XXX might need to write out last bit in fragroundup + * sized chunk. however, ffs_balloc() handles this for us + */ + errno = ffs_balloc(&in, offset, chunk, &bp); + bad_ffs_write_file: + if (errno != 0) + err(1, + "Writing inode %d (%s), bytes %lld + %lld", + ino, + isfile ? (char *)buf : + inode_type(DIP(din, mode) & S_IFMT), + (long long)offset, (long long)chunk); + memcpy(bp->b_data, p, chunk); + errno = bwrite(bp); + if (errno != 0) + goto bad_ffs_write_file; + brelse(bp); + if (!isfile) + p += chunk; + } + + write_inode_and_leave: + ffs_write_inode(&in.i_din, in.i_number, fsopts); + + leave_ffs_write_file: + if (fbuf) + free(fbuf); + if (ffd != -1) + close(ffd); +} + + +static void +ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap) +{ + doff_t i; + struct direct *de; + uint16_t reclen; + + assert (dbuf != NULL); + assert (dir != NULL); + printf("ffs_dump_dirbuf: dir %s size %d cur %d\n", + dir, dbuf->size, dbuf->cur); + + for (i = 0; i < dbuf->size; ) { + de = (struct direct *)(dbuf->buf + i); + reclen = ufs_rw16(de->d_reclen, needswap); + printf( + " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n", + ufs_rw32(de->d_ino, needswap), + inode_type(DTTOIF(de->d_type)), i, reclen, + de->d_namlen, de->d_name); + i += reclen; + assert(reclen > 0); + } +} + +static void +ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap) +{ + struct direct de, *dp; + uint16_t llen, reclen; + u_char *newbuf; + + assert (dbuf != NULL); + assert (name != NULL); + assert (node != NULL); + /* create direct entry */ + (void)memset(&de, 0, sizeof(de)); + de.d_ino = ufs_rw32(node->inode->ino, needswap); + de.d_type = IFTODT(node->type); + de.d_namlen = (uint8_t)strlen(name); + strcpy(de.d_name, name); + reclen = DIRSIZ_SWAP(0, &de, needswap); + de.d_reclen = ufs_rw16(reclen, needswap); + + dp = (struct direct *)(dbuf->buf + dbuf->cur); + llen = 0; + if (dp != NULL) + llen = DIRSIZ_SWAP(0, dp, needswap); + + if (debug & DEBUG_FS_MAKE_DIRBUF) + printf( + "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n" + " ino %d type %d reclen %d namlen %d name %.30s\n", + dbuf->size, dbuf->cur, llen, + ufs_rw32(de.d_ino, needswap), de.d_type, reclen, + de.d_namlen, de.d_name); + + if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) { + if (debug & DEBUG_FS_MAKE_DIRBUF) + printf("ffs_make_dirbuf: growing buf to %d\n", + dbuf->size + DIRBLKSIZ); + if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL) + err(1, "Allocating memory for directory buffer"); + dbuf->buf = newbuf; + dbuf->size += DIRBLKSIZ; + memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ); + dbuf->cur = dbuf->size - DIRBLKSIZ; + } else if (dp) { /* shrink end of previous */ + dp->d_reclen = ufs_rw16(llen,needswap); + dbuf->cur += llen; + } + dp = (struct direct *)(dbuf->buf + dbuf->cur); + memcpy(dp, &de, reclen); + dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap); +} + +/* + * cribbed from sys/ufs/ffs/ffs_alloc.c + */ +static void +ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts) +{ + char *buf; + struct ufs1_dinode *dp1; + struct ufs2_dinode *dp2, *dip; + struct cg *cgp; + struct fs *fs; + int cg, cgino, i; + daddr_t d; + char sbbuf[FFS_MAXBSIZE]; + int32_t initediblk; + ffs_opt_t *ffs_opts = fsopts->fs_specific; + + assert (dp != NULL); + assert (ino > 0); + assert (fsopts != NULL); + assert (ffs_opts != NULL); + + fs = (struct fs *)fsopts->superblock; + cg = ino_to_cg(fs, ino); + cgino = ino % fs->fs_ipg; + if (debug & DEBUG_FS_WRITE_INODE) + printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n", + dp, ino, cg, cgino); + + ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, + fsopts); + cgp = (struct cg *)sbbuf; + if (!cg_chkmagic_swap(cgp, fsopts->needswap)) + errx(1, "ffs_write_inode: cg %d: bad magic number", cg); + + assert (isclr(cg_inosused_swap(cgp, fsopts->needswap), cgino)); + + buf = malloc(fs->fs_bsize); + if (buf == NULL) + errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg); + + dp1 = (struct ufs1_dinode *)buf; + dp2 = (struct ufs2_dinode *)buf; + + if (fs->fs_cstotal.cs_nifree == 0) + errx(1, "ffs_write_inode: fs out of inodes for ino %u", + ino); + if (fs->fs_cs(fs, cg).cs_nifree == 0) + errx(1, + "ffs_write_inode: cg %d out of inodes for ino %u", + cg, ino); + setbit(cg_inosused_swap(cgp, fsopts->needswap), cgino); + ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap); + fs->fs_cstotal.cs_nifree--; + fs->fs_cs(fs, cg).cs_nifree--; + if (S_ISDIR(DIP(dp, mode))) { + ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap); + fs->fs_cstotal.cs_ndir++; + fs->fs_cs(fs, cg).cs_ndir++; + } + + /* + * Initialize inode blocks on the fly for UFS2. + */ + initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap); + if (ffs_opts->version == 2 && cgino + INOPB(fs) > initediblk && + initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) { + memset(buf, 0, fs->fs_bsize); + dip = (struct ufs2_dinode *)buf; + srandom(time(NULL)); + for (i = 0; i < INOPB(fs); i++) { + dip->di_gen = random() / 2 + 1; + dip++; + } + ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs, + cg * fs->fs_ipg + initediblk)), + fs->fs_bsize, buf, fsopts); + initediblk += INOPB(fs); + cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap); + } + + + ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, + fsopts); + + /* now write inode */ + d = fsbtodb(fs, ino_to_fsba(fs, ino)); + ffs_rdfs(d, fs->fs_bsize, buf, fsopts); + if (fsopts->needswap) { + if (ffs_opts->version == 1) + ffs_dinode1_swap(&dp->ffs1_din, + &dp1[ino_to_fsbo(fs, ino)]); + else + ffs_dinode2_swap(&dp->ffs2_din, + &dp2[ino_to_fsbo(fs, ino)]); + } else { + if (ffs_opts->version == 1) + dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din; + else + dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din; + } + ffs_wtfs(d, fs->fs_bsize, buf, fsopts); + free(buf); +} + +void +panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + vwarnx(fmt, ap); + va_end(ap); + exit(1); +} |