Index: head/usr.sbin/makefs/ffs.c =================================================================== --- head/usr.sbin/makefs/ffs.c (revision 317943) +++ head/usr.sbin/makefs/ffs.c (revision 317944) @@ -1,1165 +1,1165 @@ /* $NetBSD: ffs.c,v 1.45 2011/10/09 22:49:26 christos 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 __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "makefs.h" #include "ffs.h" #if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS #include #endif #include #include #include #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 *); /* publicly visible functions */ void ffs_prep_opts(fsinfo_t *fsopts) { ffs_opt_t *ffs_opts = ecalloc(1, sizeof(*ffs_opts)); const option_t ffs_options[] = { { 'b', "bsize", &ffs_opts->bsize, OPT_INT32, 1, INT_MAX, "block size" }, { 'f', "fsize", &ffs_opts->fsize, OPT_INT32, 1, INT_MAX, "fragment size" }, { 'd', "density", &ffs_opts->density, OPT_INT32, 1, INT_MAX, "bytes per inode" }, { 'm', "minfree", &ffs_opts->minfree, OPT_INT32, 0, 99, "minfree" }, { 'M', "maxbpg", &ffs_opts->maxbpg, OPT_INT32, 1, INT_MAX, "max blocks per file in a cg" }, { 'a', "avgfilesize", &ffs_opts->avgfilesize, OPT_INT32, 1, INT_MAX, "expected average file size" }, { 'n', "avgfpdir", &ffs_opts->avgfpdir, OPT_INT32, 1, INT_MAX, "expected # of files per directory" }, { 'x', "extent", &ffs_opts->maxbsize, OPT_INT32, 1, INT_MAX, "maximum # extent size" }, { 'g', "maxbpcg", &ffs_opts->maxblkspercg, OPT_INT32, 1, INT_MAX, "max # of blocks per group" }, { 'v', "version", &ffs_opts->version, OPT_INT32, 1, 2, "UFS version" }, { 'o', "optimization", NULL, OPT_STRBUF, 0, 0, "Optimization (time|space)" }, { 'l', "label", ffs_opts->label, OPT_STRARRAY, 1, sizeof(ffs_opts->label), "UFS label" }, { .name = NULL } }; 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; fsopts->fs_specific = ffs_opts; fsopts->fs_options = copy_opts(ffs_options); } void ffs_cleanup_opts(fsinfo_t *fsopts) { free(fsopts->fs_specific); free(fsopts->fs_options); } int ffs_parse_opts(const char *option, fsinfo_t *fsopts) { ffs_opt_t *ffs_opts = fsopts->fs_specific; option_t *ffs_options = fsopts->fs_options; char buf[1024]; 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); rv = set_option(ffs_options, option, buf, sizeof(buf)); if (rv == -1) return 0; if (ffs_options[rv].name == NULL) abort(); switch (ffs_options[rv].letter) { case 'o': if (strcmp(buf, "time") == 0) { ffs_opts->optimization = FS_OPTTIME; } else if (strcmp(buf, "space") == 0) { ffs_opts->optimization = FS_OPTSPACE; } else { warnx("Invalid optimization `%s'", buf); return 0; } break; default: break; } return 1; } 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 = UFS_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; if (fsopts->maxsize > 0 && roundup(fsopts->minsize, ffs_opts->bsize) > fsopts->maxsize) errx(1, "`%s' minsize of %lld rounded up to ffs bsize of %d " "exceeds maxsize %lld. Lower bsize, or round the minimum " "and maximum sizes to bsize.", dir, (long long)fsopts->minsize, ffs_opts->bsize, (long long)fsopts->maxsize); /* calculate size of tree */ ffs_size_dir(root, fsopts); fsopts->inodes += UFS_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); /* round up to requested block size, if any */ if (fsopts->roundup > 0) fsopts->size = roundup(fsopts->size, fsopts->roundup); /* 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); } /* 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; time_t tstamp; 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."); 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); buf = ecalloc(1, bufsize); } 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); if (stampst.st_ino != 0) tstamp = stampst.st_ctime; else tstamp = start_time.tv_sec; srandom(tstamp); fs = ffs_mkfs(image, fsopts, tstamp); 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 + UFS_ROOTINO < fsopts->inodes) { warnx( "Image file `%s' has %lld free inodes; %lld are required.", image, (long long)(fs->fs_cstotal.cs_nifree + UFS_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) { size_t slen; slen = strlen(node->symlink) + 1; if (slen >= (ffs_opts->version == 1 ? UFS1_MAXSYMLINKLEN : UFS2_MAXSYMLINKLEN)) 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) { size_t slen; void *membuf; struct stat *st = stampst.st_ino != 0 ? &stampst : &cur->inode->st; 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; #if HAVE_STRUCT_STAT_ST_FLAGS dinp->di_flags = cur->inode->st.st_flags; #endif dinp->di_gen = random(); dinp->di_uid = cur->inode->st.st_uid; dinp->di_gid = cur->inode->st.st_gid; dinp->di_atime = st->st_atime; dinp->di_mtime = st->st_mtime; dinp->di_ctime = st->st_ctime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_atimensec = st->st_atimensec; dinp->di_mtimensec = st->st_mtimensec; dinp->di_ctimensec = st->st_ctimensec; #endif /* 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 < UFS1_MAXSYMLINKLEN) { /* 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) { size_t slen; void *membuf; struct stat *st = stampst.st_ino != 0 ? &stampst : &cur->inode->st; 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; #if HAVE_STRUCT_STAT_ST_FLAGS dinp->di_flags = cur->inode->st.st_flags; #endif dinp->di_gen = random(); dinp->di_uid = cur->inode->st.st_uid; dinp->di_gid = cur->inode->st.st_gid; dinp->di_atime = st->st_atime; dinp->di_mtime = st->st_mtime; dinp->di_ctime = st->st_ctime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_atimensec = st->st_atimensec; dinp->di_mtimensec = st->st_mtimensec; dinp->di_ctimensec = st->st_ctimensec; #endif #if HAVE_STRUCT_STAT_BIRTHTIME dinp->di_birthtime = st->st_birthtime; dinp->di_birthnsec = st->st_birthtimensec; #endif /* 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 < UFS2_MAXSYMLINKLEN) { /* 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)) + if ((size_t)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) { fbuf = emalloc(ffs_opts->bsize); 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, 0); 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); newbuf = erealloc(dbuf->buf, dbuf->size + DIRBLKSIZ); 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 = emalloc(fs->fs_bsize); 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; for (i = 0; i < INOPB(fs); i++) { dip->di_gen = random(); 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); } Index: head/usr.sbin/makefs/walk.c =================================================================== --- head/usr.sbin/makefs/walk.c (revision 317943) +++ head/usr.sbin/makefs/walk.c (revision 317944) @@ -1,695 +1,695 @@ /* $NetBSD: walk.c,v 1.24 2008/12/28 21:51:46 christos 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "makefs.h" #include "mtree.h" #include "extern.h" static void apply_specdir(const char *, NODE *, fsnode *, int); static void apply_specentry(const char *, NODE *, fsnode *); static fsnode *create_fsnode(const char *, const char *, const char *, struct stat *); /* * walk_dir -- * build a tree of fsnodes from `root' and `dir', with a parent * fsnode of `parent' (which may be NULL for the root of the tree). * append the tree to a fsnode of `join' if it is not NULL. * each "level" is a directory, with the "." entry guaranteed to be * at the start of the list, and without ".." entries. */ fsnode * walk_dir(const char *root, const char *dir, fsnode *parent, fsnode *join) { fsnode *first, *cur, *prev, *last; DIR *dirp; struct dirent *dent; char path[MAXPATHLEN + 1]; struct stat stbuf; char *name, *rp; int dot, len; assert(root != NULL); assert(dir != NULL); - len = snprintf(path, sizeof(path), "%s/%s", root, dir); + len = (size_t)snprintf(path, sizeof(path), "%s/%s", root, dir); if (len >= (int)sizeof(path)) errx(1, "Pathname too long."); if (debug & DEBUG_WALK_DIR) printf("walk_dir: %s %p\n", path, parent); if ((dirp = opendir(path)) == NULL) err(1, "Can't opendir `%s'", path); rp = path + strlen(root) + 1; if (join != NULL) { first = cur = join; while (cur->next != NULL) cur = cur->next; prev = cur; } else first = prev = NULL; last = prev; while ((dent = readdir(dirp)) != NULL) { name = dent->d_name; dot = 0; if (name[0] == '.') switch (name[1]) { case '\0': /* "." */ if (join != NULL) continue; dot = 1; break; case '.': /* ".." */ if (name[2] == '\0') continue; /* FALLTHROUGH */ default: dot = 0; } if (debug & DEBUG_WALK_DIR_NODE) printf("scanning %s/%s/%s\n", root, dir, name); if (snprintf(path + len, sizeof(path) - len, "/%s", name) >= (int)sizeof(path) - len) errx(1, "Pathname too long."); if (lstat(path, &stbuf) == -1) err(1, "Can't lstat `%s'", path); #ifdef S_ISSOCK if (S_ISSOCK(stbuf.st_mode & S_IFMT)) { if (debug & DEBUG_WALK_DIR_NODE) printf(" skipping socket %s\n", path); continue; } #endif if (join != NULL) { cur = join->next; for (;;) { if (cur == NULL || strcmp(cur->name, name) == 0) break; if (cur == last) { cur = NULL; break; } cur = cur->next; } if (cur != NULL) { if (S_ISDIR(cur->type) && S_ISDIR(stbuf.st_mode)) { if (debug & DEBUG_WALK_DIR_NODE) printf("merging %s with %p\n", path, cur->child); cur->child = walk_dir(root, rp, cur, cur->child); continue; } errx(1, "Can't merge %s `%s' with existing %s", inode_type(stbuf.st_mode), path, inode_type(cur->type)); } } cur = create_fsnode(root, dir, name, &stbuf); cur->parent = parent; if (dot) { /* ensure "." is at the start of the list */ cur->next = first; first = cur; if (! prev) prev = cur; cur->first = first; } else { /* not "." */ if (prev) prev->next = cur; prev = cur; if (!first) first = cur; cur->first = first; if (S_ISDIR(cur->type)) { cur->child = walk_dir(root, rp, cur, NULL); continue; } } if (stbuf.st_nlink > 1) { fsinode *curino; curino = link_check(cur->inode); if (curino != NULL) { free(cur->inode); cur->inode = curino; cur->inode->nlink++; if (debug & DEBUG_WALK_DIR_LINKCHECK) printf("link_check: found [%llu, %llu]\n", (unsigned long long)curino->st.st_dev, (unsigned long long)curino->st.st_ino); } } if (S_ISLNK(cur->type)) { char slink[PATH_MAX+1]; int llen; llen = readlink(path, slink, sizeof(slink) - 1); if (llen == -1) err(1, "Readlink `%s'", path); slink[llen] = '\0'; cur->symlink = estrdup(slink); } } assert(first != NULL); if (join == NULL) for (cur = first->next; cur != NULL; cur = cur->next) cur->first = first; if (closedir(dirp) == -1) err(1, "Can't closedir `%s/%s'", root, dir); return (first); } static fsnode * create_fsnode(const char *root, const char *path, const char *name, struct stat *stbuf) { fsnode *cur; cur = ecalloc(1, sizeof(*cur)); cur->path = estrdup(path); cur->name = estrdup(name); cur->inode = ecalloc(1, sizeof(*cur->inode)); cur->root = root; cur->type = stbuf->st_mode & S_IFMT; cur->inode->nlink = 1; cur->inode->st = *stbuf; if (stampst.st_ino) { cur->inode->st.st_atime = stampst.st_atime; cur->inode->st.st_mtime = stampst.st_mtime; cur->inode->st.st_ctime = stampst.st_ctime; #if HAVE_STRUCT_STAT_ST_MTIMENSEC cur->inode->st.st_atimensec = stampst.st_atimensec; cur->inode->st.st_mtimensec = stampst.st_mtimensec; cur->inode->st.st_ctimensec = stampst.st_ctimensec; #endif #if HAVE_STRUCT_STAT_BIRTHTIME cur->inode->st.st_birthtime = stampst.st_birthtime; cur->inode->st.st_birthtimensec = stampst.st_birthtimensec; #endif } return (cur); } /* * free_fsnodes -- * Removes node from tree and frees it and all of * its descendants. */ void free_fsnodes(fsnode *node) { fsnode *cur, *next; assert(node != NULL); /* for ".", start with actual parent node */ if (node->first == node) { assert(node->name[0] == '.' && node->name[1] == '\0'); if (node->parent) { assert(node->parent->child == node); node = node->parent; } } /* Find ourselves in our sibling list and unlink */ if (node->first != node) { for (cur = node->first; cur->next; cur = cur->next) { if (cur->next == node) { cur->next = node->next; node->next = NULL; break; } } } for (cur = node; cur != NULL; cur = next) { next = cur->next; if (cur->child) { cur->child->parent = NULL; free_fsnodes(cur->child); } if (cur->inode->nlink-- == 1) free(cur->inode); if (cur->symlink) free(cur->symlink); free(cur->path); free(cur->name); free(cur); } } /* * apply_specfile -- * read in the mtree(8) specfile, and apply it to the tree * at dir,parent. parameters in parent on equivalent types * will be changed to those found in specfile, and missing * entries will be added. */ void apply_specfile(const char *specfile, const char *dir, fsnode *parent, int speconly) { struct timeval start; FILE *fp; NODE *root; assert(specfile != NULL); assert(parent != NULL); if (debug & DEBUG_APPLY_SPECFILE) printf("apply_specfile: %s, %s %p\n", specfile, dir, parent); /* read in the specfile */ if ((fp = fopen(specfile, "r")) == NULL) err(1, "Can't open `%s'", specfile); TIMER_START(start); root = spec(fp); TIMER_RESULTS(start, "spec"); if (fclose(fp) == EOF) err(1, "Can't close `%s'", specfile); /* perform some sanity checks */ if (root == NULL) errx(1, "Specfile `%s' did not contain a tree", specfile); assert(strcmp(root->name, ".") == 0); assert(root->type == F_DIR); /* merge in the changes */ apply_specdir(dir, root, parent, speconly); free_nodes(root); } static void apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode, int speconly) { char path[MAXPATHLEN + 1]; NODE *curnode; fsnode *curfsnode; assert(specnode != NULL); assert(dirnode != NULL); if (debug & DEBUG_APPLY_SPECFILE) printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode); if (specnode->type != F_DIR) errx(1, "Specfile node `%s/%s' is not a directory", dir, specnode->name); if (dirnode->type != S_IFDIR) errx(1, "Directory node `%s/%s' is not a directory", dir, dirnode->name); apply_specentry(dir, specnode, dirnode); /* Remove any filesystem nodes not found in specfile */ /* XXX inefficient. This is O^2 in each dir and it would * have been better never to have walked this part of the tree * to begin with */ if (speconly) { fsnode *next; assert(dirnode->name[0] == '.' && dirnode->name[1] == '\0'); for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = next) { next = curfsnode->next; for (curnode = specnode->child; curnode != NULL; curnode = curnode->next) { if (strcmp(curnode->name, curfsnode->name) == 0) break; } if (curnode == NULL) { if (debug & DEBUG_APPLY_SPECONLY) { printf("apply_specdir: trimming %s/%s %p\n", dir, curfsnode->name, curfsnode); } free_fsnodes(curfsnode); } } } /* now walk specnode->child matching up with dirnode */ for (curnode = specnode->child; curnode != NULL; curnode = curnode->next) { if (debug & DEBUG_APPLY_SPECENTRY) printf("apply_specdir: spec %s\n", curnode->name); for (curfsnode = dirnode->next; curfsnode != NULL; curfsnode = curfsnode->next) { #if 0 /* too verbose for now */ if (debug & DEBUG_APPLY_SPECENTRY) printf("apply_specdir: dirent %s\n", curfsnode->name); #endif if (strcmp(curnode->name, curfsnode->name) == 0) break; } if (snprintf(path, sizeof(path), "%s/%s", dir, curnode->name) >= sizeof(path)) errx(1, "Pathname too long."); if (curfsnode == NULL) { /* need new entry */ struct stat stbuf; /* * don't add optional spec entries * that lack an existing fs entry */ if ((curnode->flags & F_OPT) && lstat(path, &stbuf) == -1) continue; /* check that enough info is provided */ #define NODETEST(t, m) \ if (!(t)) \ errx(1, "`%s': %s not provided", path, m) NODETEST(curnode->flags & F_TYPE, "type"); NODETEST(curnode->flags & F_MODE, "mode"); /* XXX: require F_TIME ? */ NODETEST(curnode->flags & F_GID || curnode->flags & F_GNAME, "group"); NODETEST(curnode->flags & F_UID || curnode->flags & F_UNAME, "user"); /* if (curnode->type == F_BLOCK || curnode->type == F_CHAR) NODETEST(curnode->flags & F_DEV, "device number");*/ #undef NODETEST if (debug & DEBUG_APPLY_SPECFILE) printf("apply_specdir: adding %s\n", curnode->name); /* build minimal fsnode */ memset(&stbuf, 0, sizeof(stbuf)); stbuf.st_mode = nodetoino(curnode->type); stbuf.st_nlink = 1; stbuf.st_mtime = stbuf.st_atime = stbuf.st_ctime = start_time.tv_sec; #if HAVE_STRUCT_STAT_ST_MTIMENSEC stbuf.st_mtimensec = stbuf.st_atimensec = stbuf.st_ctimensec = start_time.tv_nsec; #endif curfsnode = create_fsnode(".", ".", curnode->name, &stbuf); curfsnode->parent = dirnode->parent; curfsnode->first = dirnode; curfsnode->next = dirnode->next; dirnode->next = curfsnode; if (curfsnode->type == S_IFDIR) { /* for dirs, make "." entry as well */ curfsnode->child = create_fsnode(".", ".", ".", &stbuf); curfsnode->child->parent = curfsnode; curfsnode->child->first = curfsnode->child; } if (curfsnode->type == S_IFLNK) { assert(curnode->slink != NULL); /* for symlinks, copy the target */ curfsnode->symlink = estrdup(curnode->slink); } } apply_specentry(dir, curnode, curfsnode); if (curnode->type == F_DIR) { if (curfsnode->type != S_IFDIR) errx(1, "`%s' is not a directory", path); assert (curfsnode->child != NULL); apply_specdir(path, curnode, curfsnode->child, speconly); } } } static void apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode) { assert(specnode != NULL); assert(dirnode != NULL); if (nodetoino(specnode->type) != dirnode->type) errx(1, "`%s/%s' type mismatch: specfile %s, tree %s", dir, specnode->name, inode_type(nodetoino(specnode->type)), inode_type(dirnode->type)); if (debug & DEBUG_APPLY_SPECENTRY) printf("apply_specentry: %s/%s\n", dir, dirnode->name); #define ASEPRINT(t, b, o, n) \ if (debug & DEBUG_APPLY_SPECENTRY) \ printf("\t\t\tchanging %s from " b " to " b "\n", \ t, o, n) if (specnode->flags & (F_GID | F_GNAME)) { ASEPRINT("gid", "%d", dirnode->inode->st.st_gid, specnode->st_gid); dirnode->inode->st.st_gid = specnode->st_gid; } if (specnode->flags & F_MODE) { ASEPRINT("mode", "%#o", dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode); dirnode->inode->st.st_mode &= ~ALLPERMS; dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS); } /* XXX: ignoring F_NLINK for now */ if (specnode->flags & F_SIZE) { ASEPRINT("size", "%lld", (long long)dirnode->inode->st.st_size, (long long)specnode->st_size); dirnode->inode->st.st_size = specnode->st_size; } if (specnode->flags & F_SLINK) { assert(dirnode->symlink != NULL); assert(specnode->slink != NULL); ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink); free(dirnode->symlink); dirnode->symlink = estrdup(specnode->slink); } if (specnode->flags & F_TIME) { ASEPRINT("time", "%ld", (long)dirnode->inode->st.st_mtime, (long)specnode->st_mtimespec.tv_sec); dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec; dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec; dirnode->inode->st.st_ctime = start_time.tv_sec; #if HAVE_STRUCT_STAT_ST_MTIMENSEC dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec; dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec; dirnode->inode->st.st_ctimensec = start_time.tv_nsec; #endif } if (specnode->flags & (F_UID | F_UNAME)) { ASEPRINT("uid", "%d", dirnode->inode->st.st_uid, specnode->st_uid); dirnode->inode->st.st_uid = specnode->st_uid; } #if HAVE_STRUCT_STAT_ST_FLAGS if (specnode->flags & F_FLAGS) { ASEPRINT("flags", "%#lX", (unsigned long)dirnode->inode->st.st_flags, (unsigned long)specnode->st_flags); dirnode->inode->st.st_flags = specnode->st_flags; } #endif /* if (specnode->flags & F_DEV) { ASEPRINT("rdev", "%#llx", (unsigned long long)dirnode->inode->st.st_rdev, (unsigned long long)specnode->st_rdev); dirnode->inode->st.st_rdev = specnode->st_rdev; }*/ #undef ASEPRINT dirnode->flags |= FSNODE_F_HASSPEC; } /* * dump_fsnodes -- * dump the fsnodes from `cur' */ void dump_fsnodes(fsnode *root) { fsnode *cur; char path[MAXPATHLEN + 1]; printf("dump_fsnodes: %s %p\n", root->path, root); for (cur = root; cur != NULL; cur = cur->next) { if (snprintf(path, sizeof(path), "%s/%s", cur->path, cur->name) >= (int)sizeof(path)) errx(1, "Pathname too long."); if (debug & DEBUG_DUMP_FSNODES_VERBOSE) printf("cur=%8p parent=%8p first=%8p ", cur, cur->parent, cur->first); printf("%7s: %s", inode_type(cur->type), path); if (S_ISLNK(cur->type)) { assert(cur->symlink != NULL); printf(" -> %s", cur->symlink); } else { assert (cur->symlink == NULL); } if (cur->inode->nlink > 1) printf(", nlinks=%d", cur->inode->nlink); putchar('\n'); if (cur->child) { assert (cur->type == S_IFDIR); dump_fsnodes(cur->child); } } printf("dump_fsnodes: finished %s/%s\n", root->path, root->name); } /* * inode_type -- * for a given inode type `mode', return a descriptive string. * for most cases, uses inotype() from mtree/misc.c */ const char * inode_type(mode_t mode) { if (S_ISREG(mode)) return ("file"); if (S_ISLNK(mode)) return ("symlink"); if (S_ISDIR(mode)) return ("dir"); if (S_ISLNK(mode)) return ("link"); if (S_ISFIFO(mode)) return ("fifo"); if (S_ISSOCK(mode)) return ("socket"); /* XXX should not happen but handle them */ if (S_ISCHR(mode)) return ("char"); if (S_ISBLK(mode)) return ("block"); return ("unknown"); } /* * link_check -- * return pointer to fsinode matching `entry's st_ino & st_dev if it exists, * otherwise add `entry' to table and return NULL */ /* This was borrowed from du.c and tweaked to keep an fsnode * pointer instead. -- dbj@netbsd.org */ fsinode * link_check(fsinode *entry) { static struct entry { fsinode *data; } *htable; static int htshift; /* log(allocated size) */ static int htmask; /* allocated size - 1 */ static int htused; /* 2*number of insertions */ int h, h2; uint64_t tmp; /* this constant is (1<<64)/((1+sqrt(5))/2) * aka (word size)/(golden ratio) */ const uint64_t HTCONST = 11400714819323198485ULL; const int HTBITS = 64; /* Never store zero in hashtable */ assert(entry); /* Extend hash table if necessary, keep load under 0.5 */ if (htused<<1 >= htmask) { struct entry *ohtable; if (!htable) htshift = 10; /* starting hashtable size */ else htshift++; /* exponential hashtable growth */ htmask = (1 << htshift) - 1; htused = 0; ohtable = htable; htable = ecalloc(htmask+1, sizeof(*htable)); /* populate newly allocated hashtable */ if (ohtable) { int i; for (i = 0; i <= htmask>>1; i++) if (ohtable[i].data) link_check(ohtable[i].data); free(ohtable); } } /* multiplicative hashing */ tmp = entry->st.st_dev; tmp <<= HTBITS>>1; tmp |= entry->st.st_ino; tmp *= HTCONST; h = tmp >> (HTBITS - htshift); h2 = 1 | ( tmp >> (HTBITS - (htshift<<1) - 1)); /* must be odd */ /* open address hashtable search with double hash probing */ while (htable[h].data) { if ((htable[h].data->st.st_ino == entry->st.st_ino) && (htable[h].data->st.st_dev == entry->st.st_dev)) { return htable[h].data; } h = (h + h2) & htmask; } /* Insert the current entry into hashtable */ htable[h].data = entry; htused++; return NULL; }