diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c index cc0f640e3c9c..17682dea2a4b 100644 --- a/usr.sbin/makefs/ffs.c +++ b/usr.sbin/makefs/ffs.c @@ -1,1205 +1,1204 @@ /* $NetBSD: ffs.c,v 1.45 2011/10/09 22:49:26 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * 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$"); #if HAVE_NBTOOL_CONFIG_H #include "nbtool_config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #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 clrbuf #include "makefs.h" #include "ffs.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 4096 /* fragment size */ #define DFL_BLKSIZE 32768 /* 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" }, { 's', "softupdates", &ffs_opts->softupdates, OPT_INT32, 0, 1, "enable softupdates" }, { .name = NULL } }; ffs_opts->bsize= -1; ffs_opts->fsize= -1; ffs_opts->cpg= -1; ffs_opts->density= -1; ffs_opts->min_inodes= false; 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; ffs_opts->softupdates = 0; 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); /* if user wants no free space, use minimum number of inodes */ if (fsopts->minsize == 0 && fsopts->freeblockpc == 0 && fsopts->freeblocks == 0) ((ffs_opt_t *)fsopts->fs_specific)->min_inodes = true; /* 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) { #ifdef 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 superblock, cylblock and to store inodes. * All of those segments are aligned to the block size. * XXX: This has to match calculations done in ffs_mkfs. */ if (ffs_opts->version == 1) { fsopts->size += roundup(SBLOCK_UFS1 + SBLOCKSIZE, ffs_opts->bsize); fsopts->size += roundup(SBLOCKSIZE, ffs_opts->bsize); fsopts->size += ffs_opts->bsize; fsopts->size += DINODE1_SIZE * roundup(fsopts->inodes, ffs_opts->bsize / DINODE1_SIZE); } else { fsopts->size += roundup(SBLOCK_UFS2 + SBLOCKSIZE, ffs_opts->bsize); fsopts->size += roundup(SBLOCKSIZE, ffs_opts->bsize); fsopts->size += ffs_opts->bsize; fsopts->size += DINODE2_SIZE * roundup(fsopts->inodes, 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 to just fit inodes if no free space */ 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; int oflags = O_RDWR | O_CREAT; time_t tstamp; assert (image != NULL); assert (fsopts != NULL); /* create image */ if (fsopts->offset == 0) oflags |= O_TRUNC; if ((fsopts->fd = open(image, oflags, 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); } if (fsopts->offset != 0) if (lseek(fsopts->fd, fsopts->offset, SEEK_SET) == -1) { warn("can't seek"); free(buf); 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); 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 + (off_t)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); #define ADDSIZE(x) do { \ if ((size_t)(x) < UFS_NDADDR * (size_t)ffs_opts->bsize) { \ fsopts->size += roundup((x), ffs_opts->fsize); \ } else { \ /* Count space consumed by indirecttion blocks. */ \ fsopts->size += ffs_opts->bsize * \ (howmany((x), UFS_NDADDR * ffs_opts->bsize) - 1); \ /* \ * If the file is big enough to use indirect blocks, \ * we allocate bsize block for trailing data. \ */ \ fsopts->size += roundup((x), ffs_opts->bsize); \ } \ } 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_shortlink, 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_BIRTHTIME dinp->di_birthtime = st->st_birthtime; #else dinp->di_birthtime = st->st_ctime; #endif #if HAVE_STRUCT_STAT_ST_MTIMENSEC dinp->di_atimensec = st->st_atimensec; dinp->di_mtimensec = st->st_mtimensec; dinp->di_ctimensec = st->st_ctimensec; #if HAVE_STRUCT_STAT_BIRTHTIME dinp->di_birthnsec = st->st_birthtimensec; #else dinp->di_birthnsec = st->st_ctimensec; #endif #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_shortlink, 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 ((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 m_buf * bp; ffs_opt_t *ffs_opts = fsopts->fs_specific; struct m_vnode vp = { fsopts, NULL }; 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; in.i_devvp = (void *)&vp; 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)); 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)) == -1) { err(EXIT_FAILURE, "Can't open `%s' for reading", (char *)buf); } } 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); 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; uint32_t i; daddr_t d; char sbbuf[FFS_MAXBSIZE]; uint32_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); while (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); } diff --git a/usr.sbin/makefs/ffs/buf.c b/usr.sbin/makefs/ffs/buf.c index fe6eaed93d04..3d03e9866b3b 100644 --- a/usr.sbin/makefs/ffs/buf.c +++ b/usr.sbin/makefs/ffs/buf.c @@ -1,217 +1,222 @@ /* $NetBSD: buf.c,v 1.13 2004/06/20 22:20:18 jmc Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * 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 "makefs.h" #include "buf.h" static TAILQ_HEAD(buftailhead, m_buf) buftail; int bread(struct m_vnode *vp, daddr_t blkno, int size, struct ucred *u1 __unused, struct m_buf **bpp) { off_t offset; ssize_t rv; fsinfo_t *fs = vp->fs; assert (bpp != NULL); if (debug & DEBUG_BUF_BREAD) printf("%s: blkno %lld size %d\n", __func__, (long long)blkno, size); *bpp = getblk(vp, blkno, size, 0, 0, 0); offset = (off_t)(*bpp)->b_blkno * fs->sectorsize + fs->offset; if (debug & DEBUG_BUF_BREAD) printf("%s: blkno %lld offset %lld bcount %ld\n", __func__, (long long)(*bpp)->b_blkno, (long long) offset, (*bpp)->b_bcount); if (lseek((*bpp)->b_fs->fd, offset, SEEK_SET) == -1) err(1, "%s: lseek %lld (%lld)", __func__, (long long)(*bpp)->b_blkno, (long long)offset); rv = read((*bpp)->b_fs->fd, (*bpp)->b_data, (size_t)(*bpp)->b_bcount); if (debug & DEBUG_BUF_BREAD) printf("%s: read %ld (%lld) returned %d\n", __func__, (*bpp)->b_bcount, (long long)offset, (int)rv); if (rv == -1) /* read error */ err(1, "%s: read %ld (%lld) returned %d", __func__, (*bpp)->b_bcount, (long long)offset, (int)rv); else if (rv != (*bpp)->b_bcount) /* short read */ err(1, "%s: read %ld (%lld) returned %d", __func__, (*bpp)->b_bcount, (long long)offset, (int)rv); else return (0); } void brelse(struct m_buf *bp) { assert (bp != NULL); assert (bp->b_data != NULL); if (bp->b_lblkno < 0) { /* * XXX don't remove any buffers with negative logical block * numbers (lblkno), so that we retain the mapping * of negative lblkno -> real blkno that ffs_balloc() * sets up. * * if we instead released these buffers, and implemented * ufs_strategy() (and ufs_bmaparray()) and called those * from bread() and bwrite() to convert the lblkno to * a real blkno, we'd add a lot more code & complexity * and reading off disk, for little gain, because this * simple hack works for our purpose. */ bp->b_bcount = 0; return; } TAILQ_REMOVE(&buftail, bp, b_tailq); free(bp->b_data); free(bp); } int bwrite(struct m_buf *bp) { off_t offset; ssize_t rv; + size_t bytes; + int e; fsinfo_t *fs = bp->b_fs; assert (bp != NULL); offset = (off_t)bp->b_blkno * fs->sectorsize + fs->offset; + bytes = (size_t)bp->b_bcount; if (debug & DEBUG_BUF_BWRITE) - printf("%s: blkno %lld offset %lld bcount %ld\n", __func__, - (long long)bp->b_blkno, (long long) offset, - bp->b_bcount); - if (lseek(bp->b_fs->fd, offset, SEEK_SET) == -1) + printf("%s: blkno %lld offset %lld bcount %zu\n", __func__, + (long long)bp->b_blkno, (long long) offset, bytes); + if (lseek(bp->b_fs->fd, offset, SEEK_SET) == -1) { + brelse(bp); return (errno); - rv = write(bp->b_fs->fd, bp->b_data, bp->b_bcount); + } + rv = write(bp->b_fs->fd, bp->b_data, bytes); + e = errno; if (debug & DEBUG_BUF_BWRITE) printf("%s: write %ld (offset %lld) returned %lld\n", __func__, bp->b_bcount, (long long)offset, (long long)rv); - if (rv == bp->b_bcount) + brelse(bp); + if (rv == (ssize_t)bytes) return (0); - else if (rv == -1) /* write error */ - return (errno); - else /* short write ? */ - return (EAGAIN); + if (rv == -1) /* write error */ + return (e); + return (EAGAIN); } void bcleanup(void) { struct m_buf *bp; /* * XXX this really shouldn't be necessary, but i'm curious to * know why there's still some buffers lying around that * aren't brelse()d */ if (TAILQ_EMPTY(&buftail)) return; printf("%s: unflushed buffers:\n", __func__); TAILQ_FOREACH(bp, &buftail, b_tailq) { printf("\tlblkno %10lld blkno %10lld count %6ld bufsize %6ld\n", (long long)bp->b_lblkno, (long long)bp->b_blkno, bp->b_bcount, bp->b_bufsize); } printf("%s: done\n", __func__); } struct m_buf * getblk(struct m_vnode *vp, daddr_t blkno, int size, int u1 __unused, int u2 __unused, int u3 __unused) { static int buftailinitted; struct m_buf *bp; void *n; if (debug & DEBUG_BUF_GETBLK) printf("%s: blkno %lld size %d\n", __func__, (long long)blkno, size); bp = NULL; if (!buftailinitted) { if (debug & DEBUG_BUF_GETBLK) printf("%s: initialising tailq\n", __func__); TAILQ_INIT(&buftail); buftailinitted = 1; } else { TAILQ_FOREACH(bp, &buftail, b_tailq) { if (bp->b_lblkno != blkno) continue; break; } } if (bp == NULL) { bp = ecalloc(1, sizeof(*bp)); bp->b_bufsize = 0; bp->b_blkno = bp->b_lblkno = blkno; bp->b_fs = vp->fs; bp->b_data = NULL; TAILQ_INSERT_HEAD(&buftail, bp, b_tailq); } bp->b_bcount = size; if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) { n = erealloc(bp->b_data, (size_t)size); memset(n, 0, (size_t)size); bp->b_data = n; bp->b_bufsize = size; } return (bp); } diff --git a/usr.sbin/makefs/msdos/msdosfs_vnops.c b/usr.sbin/makefs/msdos/msdosfs_vnops.c index c04ed6f742a1..5bc9b495c586 100644 --- a/usr.sbin/makefs/msdos/msdosfs_vnops.c +++ b/usr.sbin/makefs/msdos/msdosfs_vnops.c @@ -1,645 +1,644 @@ /* $NetBSD: msdosfs_vnops.c,v 1.19 2017/04/13 17:10:12 christos Exp $ */ /*- * SPDX-License-Identifier: BSD-4-Clause * * Copyright (C) 1994, 1995, 1997 Wolfgang Solfrank. * Copyright (C) 1994, 1995, 1997 TooLs GmbH. * All rights reserved. * Original code by Paul Popelka (paulp@uts.amdahl.com) (see below). * * 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 by TooLs GmbH. * 4. The name of TooLs GmbH may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``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 TOOLS GMBH 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. */ /*- * Written by Paul Popelka (paulp@uts.amdahl.com) * * You can do anything you want with this software, just don't say you wrote * it, and don't remove this notice. * * This software is provided "as is". * * The author supplies this software to be publicly redistributed on the * understanding that the author is not responsible for the correct * functioning of this software in any circumstances and is not liable for * any damages caused by this software. * * October 1992 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "msdos/denode.h" #include #include #include "makefs.h" #include "msdos.h" /* * Some general notes: * * In the ufs filesystem the inodes, superblocks, and indirect blocks are * read/written using the vnode for the filesystem. Blocks that represent * the contents of a file are read/written using the vnode for the file * (including directories when they are read/written as files). This * presents problems for the dos filesystem because data that should be in * an inode (if dos had them) resides in the directory itself. Since we * must update directory entries without the benefit of having the vnode * for the directory we must use the vnode for the filesystem. This means * that when a directory is actually read/written (via read, write, or * readdir, or seek) we must use the vnode for the filesystem instead of * the vnode for the directory as would happen in ufs. This is to insure we * retrieve the correct block from the buffer cache since the hash value is * based upon the vnode address and the desired block number. */ static int msdosfs_wfile(const char *, struct denode *, fsnode *); static void unix2fattime(const struct timespec *tsp, uint16_t *ddp, uint16_t *dtp); static void msdosfs_times(struct denode *dep, const struct stat *st) { if (stampst.st_ino) st = &stampst; #ifdef HAVE_STRUCT_STAT_BIRTHTIME unix2fattime(&st->st_birthtim, &dep->de_CDate, &dep->de_CTime); #else unix2fattime(&st->st_ctim, &dep->de_CDate, &dep->de_CTime); #endif unix2fattime(&st->st_atim, &dep->de_ADate, NULL); unix2fattime(&st->st_mtim, &dep->de_MDate, &dep->de_MTime); } static void unix2fattime(const struct timespec *tsp, uint16_t *ddp, uint16_t *dtp) { time_t t1; struct tm lt = {0}; t1 = tsp->tv_sec; localtime_r(&t1, <); unsigned long fat_time = ((lt.tm_year - 80) << 25) | ((lt.tm_mon + 1) << 21) | (lt.tm_mday << 16) | (lt.tm_hour << 11) | (lt.tm_min << 5) | (lt.tm_sec >> 1); if (ddp != NULL) *ddp = (uint16_t)(fat_time >> 16); if (dtp != NULL) *dtp = (uint16_t)fat_time; } /* * When we search a directory the blocks containing directory entries are * read and examined. The directory entries contain information that would * normally be in the inode of a unix filesystem. This means that some of * a directory's contents may also be in memory resident denodes (sort of * an inode). This can cause problems if we are searching while some other * process is modifying a directory. To prevent one process from accessing * incompletely modified directory information we depend upon being the * sole owner of a directory block. bread/brelse provide this service. * This being the case, when a process modifies a directory it must first * acquire the disk block that contains the directory entry to be modified. * Then update the disk block and the denode, and then write the disk block * out to disk. This way disk blocks containing directory entries and in * memory denode's will be in synch. */ static int msdosfs_findslot(struct denode *dp, struct componentname *cnp) { daddr_t bn; int error; int slotcount; int slotoffset = 0; int frcn; u_long cluster; int blkoff; u_int diroff; int blsize; struct msdosfsmount *pmp; struct m_buf *bp = 0; struct direntry *dep; u_char dosfilename[12]; int wincnt = 1; int chksum = -1, chksum_ok; int olddos = 1; pmp = dp->de_pmp; switch (unix2dosfn((const u_char *)cnp->cn_nameptr, dosfilename, cnp->cn_namelen, 0)) { case 0: return (EINVAL); case 1: break; case 2: wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1; break; case 3: olddos = 0; wincnt = winSlotCnt((const u_char *)cnp->cn_nameptr, cnp->cn_namelen) + 1; break; } if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) wincnt = 1; /* * Suppress search for slots unless creating * file and at end of pathname, in which case * we watch for a place to put the new file in * case it doesn't already exist. */ slotcount = 0; MSDOSFS_DPRINTF(("%s(): dos filename: %s\n", __func__, dosfilename)); /* * Search the directory pointed at by vdp for the name pointed at * by cnp->cn_nameptr. */ /* * The outer loop ranges over the clusters that make up the * directory. Note that the root directory is different from all * other directories. It has a fixed number of blocks that are not * part of the pool of allocatable clusters. So, we treat it a * little differently. The root directory starts at "cluster" 0. */ diroff = 0; for (frcn = 0; diroff < dp->de_FileSize; frcn++) { if ((error = pcbmap(dp, frcn, &bn, &cluster, &blsize)) != 0) { if (error == E2BIG) break; return (error); } error = bread((void *)pmp->pm_devvp, bn, blsize, 0, &bp); if (error) { return (error); } for (blkoff = 0; blkoff < blsize; blkoff += sizeof(struct direntry), diroff += sizeof(struct direntry)) { dep = (struct direntry *)(bp->b_data + blkoff); /* * If the slot is empty and we are still looking * for an empty then remember this one. If the * slot is not empty then check to see if it * matches what we are looking for. If the slot * has never been filled with anything, then the * remainder of the directory has never been used, * so there is no point in searching it. */ if (dep->deName[0] == SLOT_EMPTY || dep->deName[0] == SLOT_DELETED) { /* * Drop memory of previous long matches */ chksum = -1; if (slotcount < wincnt) { slotcount++; slotoffset = diroff; } if (dep->deName[0] == SLOT_EMPTY) { brelse(bp); goto notfound; } } else { /* * If there wasn't enough space for our * winentries, forget about the empty space */ if (slotcount < wincnt) slotcount = 0; /* * Check for Win95 long filename entry */ if (dep->deAttributes == ATTR_WIN95) { if (pmp->pm_flags & MSDOSFSMNT_SHORTNAME) continue; chksum = winChkName( (const u_char *)cnp->cn_nameptr, cnp->cn_namelen, (struct winentry *)dep, chksum); continue; } /* * Ignore volume labels (anywhere, not just * the root directory). */ if (dep->deAttributes & ATTR_VOLUME) { chksum = -1; continue; } /* * Check for a checksum or name match */ chksum_ok = (chksum == winChksum(dep->deName)); if (!chksum_ok && (!olddos || memcmp(dosfilename, dep->deName, 11))) { chksum = -1; continue; } MSDOSFS_DPRINTF(("%s(): match blkoff %d, diroff %u\n", __func__, blkoff, diroff)); /* * Remember where this directory * entry came from for whoever did * this lookup. */ dp->de_fndoffset = diroff; dp->de_fndcnt = 0; return EEXIST; } } /* for (blkoff = 0; .... */ /* * Release the buffer holding the directory cluster just * searched. */ brelse(bp); } /* for (frcn = 0; ; frcn++) */ notfound: /* * We hold no disk buffers at this point. */ /* * If we get here we didn't find the entry we were looking for. But * that's ok if we are creating or renaming and are at the end of * the pathname and the directory hasn't been removed. */ MSDOSFS_DPRINTF(("%s(): refcnt %ld, slotcount %d, slotoffset %d\n", __func__, dp->de_refcnt, slotcount, slotoffset)); /* * Fixup the slot description to point to the place where * we might put the new DOS direntry (putting the Win95 * long name entries before that) */ if (!slotcount) { slotcount = 1; slotoffset = diroff; } if (wincnt > slotcount) { slotoffset += sizeof(struct direntry) * (wincnt - slotcount); } /* * Return an indication of where the new directory * entry should be put. */ dp->de_fndoffset = slotoffset; dp->de_fndcnt = wincnt - 1; /* * We return with the directory locked, so that * the parameters we set up above will still be * valid if we actually decide to do a direnter(). * We return ni_vp == NULL to indicate that the entry * does not currently exist; we leave a pointer to * the (locked) directory inode in ndp->ni_dvp. * * NB - if the directory is unlocked, then this * information cannot be used. */ return 0; } /* * Create a regular file. On entry the directory to contain the file being * created is locked. We must release before we return. */ struct denode * msdosfs_mkfile(const char *path, struct denode *pdep, fsnode *node) { struct componentname cn; struct denode ndirent; struct denode *dep; int error; struct stat *st = &node->inode->st; cn.cn_nameptr = node->name; cn.cn_namelen = strlen(node->name); MSDOSFS_DPRINTF(("%s(name %s, mode 0%o size %zu)\n", __func__, node->name, st->st_mode, (size_t)st->st_size)); /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndoffset >= pdep->de_FileSize) { error = ENOSPC; goto bad; } /* * Create a directory entry for the file, then call createde() to * have it installed. NOTE: DOS files are always executable. We * use the absence of the owner write bit to make the file * readonly. */ memset(&ndirent, 0, sizeof(ndirent)); if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) goto bad; ndirent.de_Attributes = (st->st_mode & S_IWUSR) ? ATTR_ARCHIVE : ATTR_ARCHIVE | ATTR_READONLY; ndirent.de_StartCluster = 0; ndirent.de_FileSize = 0; ndirent.de_pmp = pdep->de_pmp; ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; msdosfs_times(&ndirent, &node->inode->st); if ((error = msdosfs_findslot(pdep, &cn)) != 0) goto bad; if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) goto bad; if ((error = msdosfs_wfile(path, dep, node)) != 0) goto bad; return dep; bad: errno = error; return NULL; } static int msdosfs_updatede(struct denode *dep) { struct m_buf *bp; struct direntry *dirp; int error; dep->de_flag &= ~DE_MODIFIED; error = m_readde(dep, &bp, &dirp); if (error) return error; DE_EXTERNALIZE(dirp, dep); error = bwrite(bp); return error; } /* * Write data to a file or directory. */ static int msdosfs_wfile(const char *path, struct denode *dep, fsnode *node) { int error, fd; size_t osize = dep->de_FileSize; struct stat *st = &node->inode->st; size_t nsize, offs; struct msdosfsmount *pmp = dep->de_pmp; struct m_buf *bp; char *dat; u_long cn = 0; error = 0; /* XXX: gcc/vax */ MSDOSFS_DPRINTF(("%s(diroff %lu, dirclust %lu, startcluster %lu)\n", __func__, dep->de_diroffset, dep->de_dirclust, dep->de_StartCluster)); if (st->st_size == 0) return 0; /* Don't bother to try to write files larger than the fs limit */ if (st->st_size > MSDOSFS_FILESIZE_MAX) return EFBIG; nsize = st->st_size; MSDOSFS_DPRINTF(("%s(nsize=%zu, osize=%zu)\n", __func__, nsize, osize)); if (nsize > osize) { if ((error = deextend(dep, nsize, NULL)) != 0) return error; if ((error = msdosfs_updatede(dep)) != 0) return error; } if ((fd = open(path, O_RDONLY)) == -1) { error = errno; fprintf(stderr, "open %s: %s\n", path, strerror(error)); return error; } if ((dat = mmap(0, nsize, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) { error = errno; fprintf(stderr, "%s: mmap %s: %s\n", __func__, node->name, strerror(error)); close(fd); goto out; } close(fd); for (offs = 0; offs < nsize;) { int blsize, cpsize; daddr_t bn; u_long on = offs & pmp->pm_crbomask; if ((error = pcbmap(dep, cn++, &bn, NULL, &blsize)) != 0) { MSDOSFS_DPRINTF(("%s: pcbmap %lu", __func__, (unsigned long)bn)); goto out; } MSDOSFS_DPRINTF(("%s(cn=%lu, bn=%llu, blsize=%d)\n", __func__, cn, (unsigned long long)bn, blsize)); if ((error = bread((void *)pmp->pm_devvp, bn, blsize, 0, &bp)) != 0) { MSDOSFS_DPRINTF(("bread %d\n", error)); goto out; } cpsize = MIN((nsize - offs), blsize - on); memcpy(bp->b_data + on, dat + offs, cpsize); bwrite(bp); - brelse(bp); offs += cpsize; } munmap(dat, nsize); return 0; out: munmap(dat, nsize); return error; } static const struct { struct direntry dot; struct direntry dotdot; } dosdirtemplate = { { ". ", /* the . entry */ ATTR_DIRECTORY, /* file attribute */ 0, /* reserved */ 0, { 0, 0 }, { 0, 0 }, /* create time & date */ { 0, 0 }, /* access date */ { 0, 0 }, /* high bits of start cluster */ { 210, 4 }, { 210, 4 }, /* modify time & date */ { 0, 0 }, /* startcluster */ { 0, 0, 0, 0 } /* filesize */ }, { ".. ", /* the .. entry */ ATTR_DIRECTORY, /* file attribute */ 0, /* reserved */ 0, { 0, 0 }, { 0, 0 }, /* create time & date */ { 0, 0 }, /* access date */ { 0, 0 }, /* high bits of start cluster */ { 210, 4 }, { 210, 4 }, /* modify time & date */ { 0, 0 }, /* startcluster */ { 0, 0, 0, 0 } /* filesize */ } }; struct denode * msdosfs_mkdire(const char *path __unused, struct denode *pdep, fsnode *node) { struct denode ndirent; struct denode *dep; struct componentname cn; struct msdosfsmount *pmp = pdep->de_pmp; int error; u_long newcluster, pcl, bn; struct direntry *denp; struct m_buf *bp; cn.cn_nameptr = node->name; cn.cn_namelen = strlen(node->name); /* * If this is the root directory and there is no space left we * can't do anything. This is because the root directory can not * change size. */ if (pdep->de_StartCluster == MSDOSFSROOT && pdep->de_fndoffset >= pdep->de_FileSize) { error = ENOSPC; goto bad2; } /* * Allocate a cluster to hold the about to be created directory. */ error = clusteralloc(pmp, 0, 1, CLUST_EOFE, &newcluster, NULL); if (error) goto bad2; memset(&ndirent, 0, sizeof(ndirent)); ndirent.de_pmp = pmp; ndirent.de_flag = DE_ACCESS | DE_CREATE | DE_UPDATE; msdosfs_times(&ndirent, &node->inode->st); /* * Now fill the cluster with the "." and ".." entries. And write * the cluster to disk. This way it is there for the parent * directory to be pointing at if there were a crash. */ bn = cntobn(pmp, newcluster); MSDOSFS_DPRINTF(("%s(newcluster %lu, bn=%lu)\n", __func__, newcluster, bn)); /* always succeeds */ bp = getblk((void *)pmp->pm_devvp, bn, pmp->pm_bpcluster, 0, 0, 0); memset(bp->b_data, 0, pmp->pm_bpcluster); memcpy(bp->b_data, &dosdirtemplate, sizeof dosdirtemplate); denp = (struct direntry *)bp->b_data; putushort(denp[0].deStartCluster, newcluster); putushort(denp[0].deCDate, ndirent.de_CDate); putushort(denp[0].deCTime, ndirent.de_CTime); denp[0].deCHundredth = ndirent.de_CHun; putushort(denp[0].deADate, ndirent.de_ADate); putushort(denp[0].deMDate, ndirent.de_MDate); putushort(denp[0].deMTime, ndirent.de_MTime); pcl = pdep->de_StartCluster; MSDOSFS_DPRINTF(("%s(pcl %lu, rootdirblk=%lu)\n", __func__, pcl, pmp->pm_rootdirblk)); if (FAT32(pmp) && pcl == pmp->pm_rootdirblk) pcl = 0; putushort(denp[1].deStartCluster, pcl); putushort(denp[1].deCDate, ndirent.de_CDate); putushort(denp[1].deCTime, ndirent.de_CTime); denp[1].deCHundredth = ndirent.de_CHun; putushort(denp[1].deADate, ndirent.de_ADate); putushort(denp[1].deMDate, ndirent.de_MDate); putushort(denp[1].deMTime, ndirent.de_MTime); if (FAT32(pmp)) { putushort(denp[0].deHighClust, newcluster >> 16); putushort(denp[1].deHighClust, pdep->de_StartCluster >> 16); } else { putushort(denp[0].deHighClust, 0); putushort(denp[1].deHighClust, 0); } if ((error = bwrite(bp)) != 0) goto bad; /* * Now build up a directory entry pointing to the newly allocated * cluster. This will be written to an empty slot in the parent * directory. */ if ((error = uniqdosname(pdep, &cn, ndirent.de_Name)) != 0) goto bad; ndirent.de_Attributes = ATTR_DIRECTORY; ndirent.de_StartCluster = newcluster; ndirent.de_FileSize = 0; ndirent.de_pmp = pdep->de_pmp; if ((error = msdosfs_findslot(pdep, &cn)) != 0) goto bad; if ((error = createde(&ndirent, pdep, &dep, &cn)) != 0) goto bad; if ((error = msdosfs_updatede(dep)) != 0) goto bad; return dep; bad: clusterfree(pmp, newcluster); bad2: errno = error; return NULL; }