1214921Scognet/* $NetBSD: ffs.c,v 1.44 2009/04/28 22:49:26 joerg Exp $ */ 2185222Ssam 3185222Ssam/* 4185222Ssam * Copyright (c) 2001 Wasabi Systems, Inc. 5185222Ssam * All rights reserved. 6185222Ssam * 7185222Ssam * Written by Luke Mewburn for Wasabi Systems, Inc. 8185222Ssam * 9185222Ssam * Redistribution and use in source and binary forms, with or without 10185222Ssam * modification, are permitted provided that the following conditions 11185222Ssam * are met: 12185222Ssam * 1. Redistributions of source code must retain the above copyright 13185222Ssam * notice, this list of conditions and the following disclaimer. 14185222Ssam * 2. Redistributions in binary form must reproduce the above copyright 15185222Ssam * notice, this list of conditions and the following disclaimer in the 16185222Ssam * documentation and/or other materials provided with the distribution. 17185222Ssam * 3. All advertising materials mentioning features or use of this software 18185222Ssam * must display the following acknowledgement: 19185222Ssam * This product includes software developed for the NetBSD Project by 20185222Ssam * Wasabi Systems, Inc. 21185222Ssam * 4. The name of Wasabi Systems, Inc. may not be used to endorse 22185222Ssam * or promote products derived from this software without specific prior 23185222Ssam * written permission. 24185222Ssam * 25185222Ssam * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND 26185222Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 27185222Ssam * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 28185222Ssam * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC 29185222Ssam * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 30185222Ssam * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 31185222Ssam * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32185222Ssam * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 33185222Ssam * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 34185222Ssam * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 35185222Ssam * POSSIBILITY OF SUCH DAMAGE. 36185222Ssam */ 37185222Ssam/* 38185222Ssam * Copyright (c) 1982, 1986, 1989, 1993 39185222Ssam * The Regents of the University of California. All rights reserved. 40185222Ssam * 41185222Ssam * Redistribution and use in source and binary forms, with or without 42185222Ssam * modification, are permitted provided that the following conditions 43185222Ssam * are met: 44185222Ssam * 1. Redistributions of source code must retain the above copyright 45185222Ssam * notice, this list of conditions and the following disclaimer. 46185222Ssam * 2. Redistributions in binary form must reproduce the above copyright 47185222Ssam * notice, this list of conditions and the following disclaimer in the 48185222Ssam * documentation and/or other materials provided with the distribution. 49185222Ssam * 3. Neither the name of the University nor the names of its contributors 50185222Ssam * may be used to endorse or promote products derived from this software 51185222Ssam * without specific prior written permission. 52185222Ssam * 53185222Ssam * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 54185222Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 55185222Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 56185222Ssam * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 57185222Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 58185222Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 59185222Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 60185222Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 61185222Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 62185222Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 63185222Ssam * SUCH DAMAGE. 64185222Ssam * 65185222Ssam * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95 66185222Ssam */ 67185222Ssam 68185222Ssam#include <sys/cdefs.h> 69186334Ssam__FBSDID("$FreeBSD$"); 70185222Ssam 71185222Ssam#include <sys/param.h> 72185222Ssam 73185222Ssam#include <sys/mount.h> 74185222Ssam 75185222Ssam#include <assert.h> 76185222Ssam#include <errno.h> 77185222Ssam#include <fcntl.h> 78185222Ssam#include <stdarg.h> 79214921Scognet#include <stdint.h> 80185222Ssam#include <stdio.h> 81185222Ssam#include <stdlib.h> 82185222Ssam#include <string.h> 83217769Smckusick#include <time.h> 84185222Ssam#include <unistd.h> 85185222Ssam 86185222Ssam#include "makefs.h" 87214921Scognet#include "ffs.h" 88185222Ssam 89214921Scognet#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS 90214921Scognet#include <sys/statvfs.h> 91214921Scognet#endif 92214921Scognet 93185222Ssam#include <ufs/ufs/dinode.h> 94185222Ssam#include <ufs/ufs/dir.h> 95185222Ssam#include <ufs/ffs/fs.h> 96185222Ssam 97214921Scognet 98186261Ssam#include "ffs/ufs_bswap.h" 99185222Ssam#include "ffs/ufs_inode.h" 100185222Ssam#include "ffs/newfs_extern.h" 101185222Ssam#include "ffs/ffs_extern.h" 102185222Ssam 103185222Ssam#undef DIP 104185222Ssam#define DIP(dp, field) \ 105214921Scognet ((ffs_opts->version == 1) ? \ 106185222Ssam (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field) 107185222Ssam 108185222Ssam/* 109185222Ssam * Various file system defaults (cribbed from newfs(8)). 110185222Ssam */ 111185222Ssam#define DFL_FRAGSIZE 1024 /* fragment size */ 112185222Ssam#define DFL_BLKSIZE 8192 /* block size */ 113185222Ssam#define DFL_SECSIZE 512 /* sector size */ 114185222Ssam#define DFL_CYLSPERGROUP 65536 /* cylinders per group */ 115185222Ssam#define DFL_FRAGSPERINODE 4 /* fragments per inode */ 116185222Ssam#define DFL_ROTDELAY 0 /* rotational delay */ 117185222Ssam#define DFL_NRPOS 1 /* rotational positions */ 118185222Ssam#define DFL_RPM 3600 /* rpm of disk */ 119185222Ssam#define DFL_NSECTORS 64 /* # of sectors */ 120185222Ssam#define DFL_NTRACKS 16 /* # of tracks */ 121185222Ssam 122185222Ssam 123185222Ssamtypedef struct { 124185222Ssam u_char *buf; /* buf for directory */ 125185222Ssam doff_t size; /* full size of buf */ 126185222Ssam doff_t cur; /* offset of current entry */ 127185222Ssam} dirbuf_t; 128185222Ssam 129185222Ssam 130185222Ssamstatic int ffs_create_image(const char *, fsinfo_t *); 131185222Ssamstatic void ffs_dump_fsinfo(fsinfo_t *); 132185222Ssamstatic void ffs_dump_dirbuf(dirbuf_t *, const char *, int); 133185222Ssamstatic void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int); 134185222Ssamstatic int ffs_populate_dir(const char *, fsnode *, fsinfo_t *); 135185222Ssamstatic void ffs_size_dir(fsnode *, fsinfo_t *); 136185222Ssamstatic void ffs_validate(const char *, fsnode *, fsinfo_t *); 137185222Ssamstatic void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *); 138185222Ssamstatic void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *); 139185222Ssamstatic void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *, 140185222Ssam fsnode *, fsinfo_t *); 141185222Ssamstatic void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *, 142185222Ssam fsnode *, fsinfo_t *); 143185222Ssam 144185222Ssam 145185222Ssam 146185222Ssamint sectorsize; /* XXX: for buf.c::getblk() */ 147185222Ssam 148228990Suqs /* publicly visible functions */ 149185222Ssam 150214921Scognetvoid 151214921Scognetffs_prep_opts(fsinfo_t *fsopts) 152214921Scognet{ 153214921Scognet ffs_opt_t *ffs_opts; 154214921Scognet 155214921Scognet if ((ffs_opts = calloc(1, sizeof(ffs_opt_t))) == NULL) 156214921Scognet err(1, "Allocating memory for ffs_options"); 157214921Scognet 158214921Scognet fsopts->fs_specific = ffs_opts; 159214921Scognet 160214921Scognet ffs_opts->bsize= -1; 161214921Scognet ffs_opts->fsize= -1; 162214921Scognet ffs_opts->cpg= -1; 163214921Scognet ffs_opts->density= -1; 164214921Scognet ffs_opts->minfree= -1; 165214921Scognet ffs_opts->optimization= -1; 166214921Scognet ffs_opts->maxcontig= -1; 167214921Scognet ffs_opts->maxbpg= -1; 168214921Scognet ffs_opts->avgfilesize= -1; 169214921Scognet ffs_opts->avgfpdir= -1; 170214921Scognet ffs_opts->version = 1; 171214921Scognet} 172214921Scognet 173214921Scognetvoid 174214921Scognetffs_cleanup_opts(fsinfo_t *fsopts) 175214921Scognet{ 176214921Scognet if (fsopts->fs_specific) 177214921Scognet free(fsopts->fs_specific); 178214921Scognet} 179214921Scognet 180185222Ssamint 181185222Ssamffs_parse_opts(const char *option, fsinfo_t *fsopts) 182185222Ssam{ 183214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 184214921Scognet 185185222Ssam option_t ffs_options[] = { 186214921Scognet { "bsize", &ffs_opts->bsize, 1, INT_MAX, 187185222Ssam "block size" }, 188214921Scognet { "fsize", &ffs_opts->fsize, 1, INT_MAX, 189185222Ssam "fragment size" }, 190214921Scognet { "density", &ffs_opts->density, 1, INT_MAX, 191185222Ssam "bytes per inode" }, 192214921Scognet { "minfree", &ffs_opts->minfree, 0, 99, 193185222Ssam "minfree" }, 194277375Sbrueffer { "maxbpg", &ffs_opts->maxbpg, 1, INT_MAX, 195185222Ssam "max blocks per file in a cg" }, 196214921Scognet { "avgfilesize", &ffs_opts->avgfilesize,1, INT_MAX, 197185222Ssam "expected average file size" }, 198214921Scognet { "avgfpdir", &ffs_opts->avgfpdir, 1, INT_MAX, 199185222Ssam "expected # of files per directory" }, 200214921Scognet { "extent", &ffs_opts->maxbsize, 1, INT_MAX, 201185222Ssam "maximum # extent size" }, 202214921Scognet { "maxbpcg", &ffs_opts->maxblkspercg,1, INT_MAX, 203185222Ssam "max # of blocks per group" }, 204214921Scognet { "version", &ffs_opts->version, 1, 2, 205185222Ssam "UFS version" }, 206214921Scognet { .name = NULL } 207185222Ssam }; 208185222Ssam 209185222Ssam char *var, *val; 210185222Ssam int rv; 211185222Ssam 212185222Ssam assert(option != NULL); 213185222Ssam assert(fsopts != NULL); 214214921Scognet assert(ffs_opts != NULL); 215185222Ssam 216185222Ssam if (debug & DEBUG_FS_PARSE_OPTS) 217185222Ssam printf("ffs_parse_opts: got `%s'\n", option); 218185222Ssam 219185222Ssam if ((var = strdup(option)) == NULL) 220185222Ssam err(1, "Allocating memory for copy of option string"); 221185222Ssam rv = 0; 222185222Ssam 223185222Ssam if ((val = strchr(var, '=')) == NULL) { 224185222Ssam warnx("Option `%s' doesn't contain a value", var); 225185222Ssam goto leave_ffs_parse_opts; 226185222Ssam } 227185222Ssam *val++ = '\0'; 228185222Ssam 229185222Ssam if (strcmp(var, "optimization") == 0) { 230185222Ssam if (strcmp(val, "time") == 0) { 231214921Scognet ffs_opts->optimization = FS_OPTTIME; 232185222Ssam } else if (strcmp(val, "space") == 0) { 233214921Scognet ffs_opts->optimization = FS_OPTSPACE; 234185222Ssam } else { 235185222Ssam warnx("Invalid optimization `%s'", val); 236185222Ssam goto leave_ffs_parse_opts; 237185222Ssam } 238185222Ssam rv = 1; 239226169Snwhitehorn } else if (strcmp(var, "label") == 0) { 240226169Snwhitehorn strlcpy(ffs_opts->label, val, sizeof(ffs_opts->label)); 241226169Snwhitehorn rv = 1; 242185222Ssam } else 243185222Ssam rv = set_option(ffs_options, var, val); 244185222Ssam 245185222Ssam leave_ffs_parse_opts: 246185222Ssam if (var) 247185222Ssam free(var); 248185222Ssam return (rv); 249185222Ssam} 250185222Ssam 251185222Ssam 252185222Ssamvoid 253185222Ssamffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts) 254185222Ssam{ 255185222Ssam struct fs *superblock; 256185222Ssam struct timeval start; 257185222Ssam 258185222Ssam assert(image != NULL); 259185222Ssam assert(dir != NULL); 260185222Ssam assert(root != NULL); 261185222Ssam assert(fsopts != NULL); 262185222Ssam 263185222Ssam if (debug & DEBUG_FS_MAKEFS) 264185222Ssam printf("ffs_makefs: image %s directory %s root %p\n", 265185222Ssam image, dir, root); 266185222Ssam 267185222Ssam /* validate tree and options */ 268185222Ssam TIMER_START(start); 269185222Ssam ffs_validate(dir, root, fsopts); 270185222Ssam TIMER_RESULTS(start, "ffs_validate"); 271185222Ssam 272185222Ssam printf("Calculated size of `%s': %lld bytes, %lld inodes\n", 273185222Ssam image, (long long)fsopts->size, (long long)fsopts->inodes); 274185222Ssam 275185222Ssam /* create image */ 276185222Ssam TIMER_START(start); 277185222Ssam if (ffs_create_image(image, fsopts) == -1) 278185222Ssam errx(1, "Image file `%s' not created.", image); 279185222Ssam TIMER_RESULTS(start, "ffs_create_image"); 280185222Ssam 281185222Ssam fsopts->curinode = ROOTINO; 282185222Ssam 283185222Ssam if (debug & DEBUG_FS_MAKEFS) 284185222Ssam putchar('\n'); 285185222Ssam 286185222Ssam /* populate image */ 287185222Ssam printf("Populating `%s'\n", image); 288185222Ssam TIMER_START(start); 289185222Ssam if (! ffs_populate_dir(dir, root, fsopts)) 290185222Ssam errx(1, "Image file `%s' not populated.", image); 291185222Ssam TIMER_RESULTS(start, "ffs_populate_dir"); 292185222Ssam 293185222Ssam /* ensure no outstanding buffers remain */ 294185222Ssam if (debug & DEBUG_FS_MAKEFS) 295185222Ssam bcleanup(); 296185222Ssam 297185222Ssam /* update various superblock parameters */ 298185222Ssam superblock = fsopts->superblock; 299185222Ssam superblock->fs_fmod = 0; 300185222Ssam superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir; 301185222Ssam superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree; 302185222Ssam superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree; 303185222Ssam superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree; 304185222Ssam 305185222Ssam /* write out superblock; image is now complete */ 306185222Ssam ffs_write_superblock(fsopts->superblock, fsopts); 307185222Ssam if (close(fsopts->fd) == -1) 308185222Ssam err(1, "Closing `%s'", image); 309185222Ssam fsopts->fd = -1; 310185222Ssam printf("Image `%s' complete\n", image); 311185222Ssam} 312185222Ssam 313185222Ssam /* end of public functions */ 314185222Ssam 315185222Ssam 316185222Ssamstatic void 317185222Ssamffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts) 318185222Ssam{ 319185222Ssam int32_t ncg = 1; 320185222Ssam#if notyet 321185222Ssam int32_t spc, nspf, ncyl, fssize; 322185222Ssam#endif 323214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 324185222Ssam 325185222Ssam assert(dir != NULL); 326185222Ssam assert(root != NULL); 327185222Ssam assert(fsopts != NULL); 328214921Scognet assert(ffs_opts != NULL); 329185222Ssam 330185222Ssam if (debug & DEBUG_FS_VALIDATE) { 331185222Ssam printf("ffs_validate: before defaults set:\n"); 332185222Ssam ffs_dump_fsinfo(fsopts); 333185222Ssam } 334185222Ssam 335185222Ssam /* set FFS defaults */ 336185222Ssam if (fsopts->sectorsize == -1) 337185222Ssam fsopts->sectorsize = DFL_SECSIZE; 338214921Scognet if (ffs_opts->fsize == -1) 339214921Scognet ffs_opts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize); 340214921Scognet if (ffs_opts->bsize == -1) 341214921Scognet ffs_opts->bsize = MIN(DFL_BLKSIZE, 8 * ffs_opts->fsize); 342214921Scognet if (ffs_opts->cpg == -1) 343214921Scognet ffs_opts->cpg = DFL_CYLSPERGROUP; 344185222Ssam else 345214921Scognet ffs_opts->cpgflg = 1; 346185222Ssam /* fsopts->density is set below */ 347214921Scognet if (ffs_opts->nsectors == -1) 348214921Scognet ffs_opts->nsectors = DFL_NSECTORS; 349214921Scognet if (ffs_opts->minfree == -1) 350214921Scognet ffs_opts->minfree = MINFREE; 351214921Scognet if (ffs_opts->optimization == -1) 352214921Scognet ffs_opts->optimization = DEFAULTOPT; 353214921Scognet if (ffs_opts->maxcontig == -1) 354214921Scognet ffs_opts->maxcontig = 355214921Scognet MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / ffs_opts->bsize); 356185222Ssam /* XXX ondisk32 */ 357214921Scognet if (ffs_opts->maxbpg == -1) 358214921Scognet ffs_opts->maxbpg = ffs_opts->bsize / sizeof(int32_t); 359214921Scognet if (ffs_opts->avgfilesize == -1) 360214921Scognet ffs_opts->avgfilesize = AVFILESIZ; 361214921Scognet if (ffs_opts->avgfpdir == -1) 362214921Scognet ffs_opts->avgfpdir = AFPDIR; 363185222Ssam 364185222Ssam /* calculate size of tree */ 365185222Ssam ffs_size_dir(root, fsopts); 366185222Ssam fsopts->inodes += ROOTINO; /* include first two inodes */ 367185222Ssam 368185222Ssam if (debug & DEBUG_FS_VALIDATE) 369185222Ssam printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n", 370185222Ssam (long long)fsopts->size, (long long)fsopts->inodes); 371185222Ssam 372185222Ssam /* add requested slop */ 373185222Ssam fsopts->size += fsopts->freeblocks; 374185222Ssam fsopts->inodes += fsopts->freefiles; 375185222Ssam if (fsopts->freefilepc > 0) 376185222Ssam fsopts->inodes = 377185222Ssam fsopts->inodes * (100 + fsopts->freefilepc) / 100; 378185222Ssam if (fsopts->freeblockpc > 0) 379185222Ssam fsopts->size = 380185222Ssam fsopts->size * (100 + fsopts->freeblockpc) / 100; 381185222Ssam 382185222Ssam /* add space needed for superblocks */ 383185222Ssam /* 384185222Ssam * The old SBOFF (SBLOCK_UFS1) is used here because makefs is 385185222Ssam * typically used for small filesystems where space matters. 386185222Ssam * XXX make this an option. 387185222Ssam */ 388185222Ssam fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg; 389185222Ssam /* add space needed to store inodes, x3 for blockmaps, etc */ 390214921Scognet if (ffs_opts->version == 1) 391185222Ssam fsopts->size += ncg * DINODE1_SIZE * 392214921Scognet roundup(fsopts->inodes / ncg, 393214921Scognet ffs_opts->bsize / DINODE1_SIZE); 394185222Ssam else 395185222Ssam fsopts->size += ncg * DINODE2_SIZE * 396214921Scognet roundup(fsopts->inodes / ncg, 397214921Scognet ffs_opts->bsize / DINODE2_SIZE); 398185222Ssam 399185222Ssam /* add minfree */ 400214921Scognet if (ffs_opts->minfree > 0) 401185222Ssam fsopts->size = 402214921Scognet fsopts->size * (100 + ffs_opts->minfree) / 100; 403185222Ssam /* 404185222Ssam * XXX any other fs slop to add, such as csum's, bitmaps, etc ?? 405185222Ssam */ 406185222Ssam 407185222Ssam if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */ 408185222Ssam fsopts->size = fsopts->minsize; 409185222Ssam 410185222Ssam /* round up to the next block */ 411214921Scognet fsopts->size = roundup(fsopts->size, ffs_opts->bsize); 412185222Ssam 413290589Sngie /* round up to requested block size, if any */ 414290589Sngie if (fsopts->roundup > 0) 415290589Sngie fsopts->size = roundup(fsopts->size, fsopts->roundup); 416290589Sngie 417185222Ssam /* calculate density if necessary */ 418214921Scognet if (ffs_opts->density == -1) 419214921Scognet ffs_opts->density = fsopts->size / fsopts->inodes + 1; 420185222Ssam 421185222Ssam if (debug & DEBUG_FS_VALIDATE) { 422185222Ssam printf("ffs_validate: after defaults set:\n"); 423185222Ssam ffs_dump_fsinfo(fsopts); 424185222Ssam printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n", 425185222Ssam dir, (long long)fsopts->size, (long long)fsopts->inodes); 426185222Ssam } 427185222Ssam sectorsize = fsopts->sectorsize; /* XXX - see earlier */ 428214921Scognet 429214921Scognet /* now check calculated sizes vs requested sizes */ 430214921Scognet if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) { 431214921Scognet errx(1, "`%s' size of %lld is larger than the maxsize of %lld.", 432214921Scognet dir, (long long)fsopts->size, (long long)fsopts->maxsize); 433214921Scognet } 434185222Ssam} 435185222Ssam 436185222Ssam 437185222Ssamstatic void 438185222Ssamffs_dump_fsinfo(fsinfo_t *f) 439185222Ssam{ 440185222Ssam 441214921Scognet ffs_opt_t *fs = f->fs_specific; 442214921Scognet 443185222Ssam printf("fsopts at %p\n", f); 444185222Ssam 445185222Ssam printf("\tsize %lld, inodes %lld, curinode %u\n", 446185222Ssam (long long)f->size, (long long)f->inodes, f->curinode); 447185222Ssam 448185222Ssam printf("\tminsize %lld, maxsize %lld\n", 449185222Ssam (long long)f->minsize, (long long)f->maxsize); 450185222Ssam printf("\tfree files %lld, freefile %% %d\n", 451185222Ssam (long long)f->freefiles, f->freefilepc); 452185222Ssam printf("\tfree blocks %lld, freeblock %% %d\n", 453185222Ssam (long long)f->freeblocks, f->freeblockpc); 454185222Ssam printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize); 455185222Ssam 456185222Ssam printf("\tbsize %d, fsize %d, cpg %d, density %d\n", 457214921Scognet fs->bsize, fs->fsize, fs->cpg, fs->density); 458185222Ssam printf("\tnsectors %d, rpm %d, minfree %d\n", 459214921Scognet fs->nsectors, fs->rpm, fs->minfree); 460185222Ssam printf("\tmaxcontig %d, maxbpg %d\n", 461214921Scognet fs->maxcontig, fs->maxbpg); 462185222Ssam printf("\toptimization %s\n", 463214921Scognet fs->optimization == FS_OPTSPACE ? "space" : "time"); 464185222Ssam} 465185222Ssam 466185222Ssam 467185222Ssamstatic int 468185222Ssamffs_create_image(const char *image, fsinfo_t *fsopts) 469185222Ssam{ 470214921Scognet#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS 471185222Ssam struct statvfs sfs; 472185222Ssam#endif 473185222Ssam struct fs *fs; 474185222Ssam char *buf; 475185222Ssam int i, bufsize; 476185222Ssam off_t bufrem; 477185222Ssam 478185222Ssam assert (image != NULL); 479185222Ssam assert (fsopts != NULL); 480185222Ssam 481185222Ssam /* create image */ 482214921Scognet if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) 483185222Ssam == -1) { 484185222Ssam warn("Can't open `%s' for writing", image); 485185222Ssam return (-1); 486185222Ssam } 487185222Ssam 488185222Ssam /* zero image */ 489214921Scognet#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS 490185222Ssam if (fstatvfs(fsopts->fd, &sfs) == -1) { 491185222Ssam#endif 492185222Ssam bufsize = 8192; 493214921Scognet#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS 494185222Ssam warn("can't fstatvfs `%s', using default %d byte chunk", 495185222Ssam image, bufsize); 496185222Ssam } else 497185222Ssam bufsize = sfs.f_iosize; 498185222Ssam#endif 499185222Ssam bufrem = fsopts->size; 500239562Shrs if (fsopts->sparse) { 501239562Shrs if (ftruncate(fsopts->fd, bufrem) == -1) { 502239562Shrs warn("sparse option disabled.\n"); 503239562Shrs fsopts->sparse = 0; 504239562Shrs } 505185222Ssam } 506239562Shrs if (fsopts->sparse) { 507239562Shrs /* File truncated at bufrem. Remaining is 0 */ 508239562Shrs bufrem = 0; 509239562Shrs buf = NULL; 510239562Shrs } else { 511239562Shrs if (debug & DEBUG_FS_CREATE_IMAGE) 512239562Shrs printf("zero-ing image `%s', %lld sectors, " 513239562Shrs "using %d byte chunks\n", image, (long long)bufrem, 514239562Shrs bufsize); 515239562Shrs if ((buf = calloc(1, bufsize)) == NULL) { 516239562Shrs warn("Can't create buffer for sector"); 517239562Shrs return (-1); 518239562Shrs } 519239562Shrs } 520185222Ssam while (bufrem > 0) { 521185222Ssam i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); 522185222Ssam if (i == -1) { 523185222Ssam warn("zeroing image, %lld bytes to go", 524185222Ssam (long long)bufrem); 525214921Scognet free(buf); 526185222Ssam return (-1); 527185222Ssam } 528185222Ssam bufrem -= i; 529185222Ssam } 530239562Shrs if (buf) 531239562Shrs free(buf); 532185222Ssam 533185222Ssam /* make the file system */ 534185222Ssam if (debug & DEBUG_FS_CREATE_IMAGE) 535185222Ssam printf("calling mkfs(\"%s\", ...)\n", image); 536185222Ssam fs = ffs_mkfs(image, fsopts); 537185222Ssam fsopts->superblock = (void *)fs; 538185222Ssam if (debug & DEBUG_FS_CREATE_IMAGE) { 539185222Ssam time_t t; 540185222Ssam 541185222Ssam t = (time_t)((struct fs *)fsopts->superblock)->fs_time; 542185222Ssam printf("mkfs returned %p; fs_time %s", 543185222Ssam fsopts->superblock, ctime(&t)); 544185222Ssam printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n", 545185222Ssam (long long)fs->fs_cstotal.cs_nbfree, 546185222Ssam (long long)fs->fs_cstotal.cs_nffree, 547185222Ssam (long long)fs->fs_cstotal.cs_nifree, 548185222Ssam (long long)fs->fs_cstotal.cs_ndir); 549185222Ssam } 550185222Ssam 551185222Ssam if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) { 552185222Ssam warnx( 553185222Ssam "Image file `%s' has %lld free inodes; %lld are required.", 554185222Ssam image, 555214921Scognet (long long)(fs->fs_cstotal.cs_nifree + ROOTINO), 556185222Ssam (long long)fsopts->inodes); 557185222Ssam return (-1); 558185222Ssam } 559185222Ssam return (fsopts->fd); 560185222Ssam} 561185222Ssam 562185222Ssam 563185222Ssamstatic void 564185222Ssamffs_size_dir(fsnode *root, fsinfo_t *fsopts) 565185222Ssam{ 566185222Ssam struct direct tmpdir; 567185222Ssam fsnode * node; 568185222Ssam int curdirsize, this; 569214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 570185222Ssam 571185222Ssam /* node may be NULL (empty directory) */ 572185222Ssam assert(fsopts != NULL); 573214921Scognet assert(ffs_opts != NULL); 574185222Ssam 575185222Ssam if (debug & DEBUG_FS_SIZE_DIR) 576185222Ssam printf("ffs_size_dir: entry: bytes %lld inodes %lld\n", 577185222Ssam (long long)fsopts->size, (long long)fsopts->inodes); 578185222Ssam 579185222Ssam#define ADDDIRENT(e) do { \ 580185222Ssam tmpdir.d_namlen = strlen((e)); \ 581214921Scognet this = DIRSIZ_SWAP(0, &tmpdir, 0); \ 582185222Ssam if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ 583185222Ssam printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \ 584185222Ssam e, tmpdir.d_namlen, this, curdirsize); \ 585185222Ssam if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \ 586185222Ssam curdirsize = roundup(curdirsize, DIRBLKSIZ); \ 587185222Ssam curdirsize += this; \ 588185222Ssam if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ 589185222Ssam printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \ 590185222Ssam e, tmpdir.d_namlen, this, curdirsize); \ 591185222Ssam} while (0); 592185222Ssam 593185222Ssam /* 594185222Ssam * XXX this needs to take into account extra space consumed 595185222Ssam * by indirect blocks, etc. 596185222Ssam */ 597185222Ssam#define ADDSIZE(x) do { \ 598214921Scognet fsopts->size += roundup((x), ffs_opts->fsize); \ 599185222Ssam} while (0); 600185222Ssam 601185222Ssam curdirsize = 0; 602185222Ssam for (node = root; node != NULL; node = node->next) { 603185222Ssam ADDDIRENT(node->name); 604185222Ssam if (node == root) { /* we're at "." */ 605185222Ssam assert(strcmp(node->name, ".") == 0); 606185222Ssam ADDDIRENT(".."); 607185222Ssam } else if ((node->inode->flags & FI_SIZED) == 0) { 608185222Ssam /* don't count duplicate names */ 609185222Ssam node->inode->flags |= FI_SIZED; 610185222Ssam if (debug & DEBUG_FS_SIZE_DIR_NODE) 611185222Ssam printf("ffs_size_dir: `%s' size %lld\n", 612185222Ssam node->name, 613185222Ssam (long long)node->inode->st.st_size); 614185222Ssam fsopts->inodes++; 615185222Ssam if (node->type == S_IFREG) 616185222Ssam ADDSIZE(node->inode->st.st_size); 617185222Ssam if (node->type == S_IFLNK) { 618185222Ssam int slen; 619185222Ssam 620185222Ssam slen = strlen(node->symlink) + 1; 621214921Scognet if (slen >= (ffs_opts->version == 1 ? 622185222Ssam MAXSYMLINKLEN_UFS1 : 623185222Ssam MAXSYMLINKLEN_UFS2)) 624185222Ssam ADDSIZE(slen); 625185222Ssam } 626185222Ssam } 627185222Ssam if (node->type == S_IFDIR) 628185222Ssam ffs_size_dir(node->child, fsopts); 629185222Ssam } 630185222Ssam ADDSIZE(curdirsize); 631185222Ssam 632185222Ssam if (debug & DEBUG_FS_SIZE_DIR) 633185222Ssam printf("ffs_size_dir: exit: size %lld inodes %lld\n", 634185222Ssam (long long)fsopts->size, (long long)fsopts->inodes); 635185222Ssam} 636185222Ssam 637185222Ssamstatic void * 638185222Ssamffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, 639185222Ssam fsnode *root, fsinfo_t *fsopts) 640185222Ssam{ 641185222Ssam int slen; 642185222Ssam void *membuf; 643185222Ssam 644185222Ssam memset(dinp, 0, sizeof(*dinp)); 645185222Ssam dinp->di_mode = cur->inode->st.st_mode; 646185222Ssam dinp->di_nlink = cur->inode->nlink; 647185222Ssam dinp->di_size = cur->inode->st.st_size; 648185222Ssam dinp->di_atime = cur->inode->st.st_atime; 649185222Ssam dinp->di_mtime = cur->inode->st.st_mtime; 650185222Ssam dinp->di_ctime = cur->inode->st.st_ctime; 651185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 652185222Ssam dinp->di_atimensec = cur->inode->st.st_atimensec; 653185222Ssam dinp->di_mtimensec = cur->inode->st.st_mtimensec; 654185222Ssam dinp->di_ctimensec = cur->inode->st.st_ctimensec; 655185222Ssam#endif 656185222Ssam#if HAVE_STRUCT_STAT_ST_FLAGS 657185222Ssam dinp->di_flags = cur->inode->st.st_flags; 658185222Ssam#endif 659185222Ssam#if HAVE_STRUCT_STAT_ST_GEN 660185222Ssam dinp->di_gen = cur->inode->st.st_gen; 661185222Ssam#endif 662185222Ssam dinp->di_uid = cur->inode->st.st_uid; 663185222Ssam dinp->di_gid = cur->inode->st.st_gid; 664185222Ssam /* not set: di_db, di_ib, di_blocks, di_spare */ 665185222Ssam 666185222Ssam membuf = NULL; 667185222Ssam if (cur == root) { /* "."; write dirbuf */ 668185222Ssam membuf = dbufp->buf; 669185222Ssam dinp->di_size = dbufp->size; 670185222Ssam } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { 671185222Ssam dinp->di_size = 0; /* a device */ 672185222Ssam dinp->di_rdev = 673185222Ssam ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap); 674185222Ssam } else if (S_ISLNK(cur->type)) { /* symlink */ 675185222Ssam slen = strlen(cur->symlink); 676185222Ssam if (slen < MAXSYMLINKLEN_UFS1) { /* short link */ 677185222Ssam memcpy(dinp->di_db, cur->symlink, slen); 678185222Ssam } else 679185222Ssam membuf = cur->symlink; 680185222Ssam dinp->di_size = slen; 681185222Ssam } 682185222Ssam return membuf; 683185222Ssam} 684185222Ssam 685185222Ssamstatic void * 686185222Ssamffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, 687185222Ssam fsnode *root, fsinfo_t *fsopts) 688185222Ssam{ 689185222Ssam int slen; 690185222Ssam void *membuf; 691185222Ssam 692185222Ssam memset(dinp, 0, sizeof(*dinp)); 693185222Ssam dinp->di_mode = cur->inode->st.st_mode; 694185222Ssam dinp->di_nlink = cur->inode->nlink; 695185222Ssam dinp->di_size = cur->inode->st.st_size; 696185222Ssam dinp->di_atime = cur->inode->st.st_atime; 697185222Ssam dinp->di_mtime = cur->inode->st.st_mtime; 698185222Ssam dinp->di_ctime = cur->inode->st.st_ctime; 699185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 700185222Ssam dinp->di_atimensec = cur->inode->st.st_atimensec; 701185222Ssam dinp->di_mtimensec = cur->inode->st.st_mtimensec; 702185222Ssam dinp->di_ctimensec = cur->inode->st.st_ctimensec; 703185222Ssam#endif 704185222Ssam#if HAVE_STRUCT_STAT_ST_FLAGS 705185222Ssam dinp->di_flags = cur->inode->st.st_flags; 706185222Ssam#endif 707185222Ssam#if HAVE_STRUCT_STAT_ST_GEN 708185222Ssam dinp->di_gen = cur->inode->st.st_gen; 709185222Ssam#endif 710185222Ssam#if HAVE_STRUCT_STAT_BIRTHTIME 711185222Ssam dinp->di_birthtime = cur->inode->st.st_birthtime; 712185222Ssam dinp->di_birthnsec = cur->inode->st.st_birthtimensec; 713185222Ssam#endif 714185222Ssam dinp->di_uid = cur->inode->st.st_uid; 715185222Ssam dinp->di_gid = cur->inode->st.st_gid; 716185222Ssam /* not set: di_db, di_ib, di_blocks, di_spare */ 717185222Ssam 718185222Ssam membuf = NULL; 719185222Ssam if (cur == root) { /* "."; write dirbuf */ 720185222Ssam membuf = dbufp->buf; 721185222Ssam dinp->di_size = dbufp->size; 722185222Ssam } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { 723185222Ssam dinp->di_size = 0; /* a device */ 724185222Ssam dinp->di_rdev = 725185222Ssam ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap); 726185222Ssam } else if (S_ISLNK(cur->type)) { /* symlink */ 727185222Ssam slen = strlen(cur->symlink); 728185222Ssam if (slen < MAXSYMLINKLEN_UFS2) { /* short link */ 729185222Ssam memcpy(dinp->di_db, cur->symlink, slen); 730185222Ssam } else 731185222Ssam membuf = cur->symlink; 732185222Ssam dinp->di_size = slen; 733185222Ssam } 734185222Ssam return membuf; 735185222Ssam} 736185222Ssam 737185222Ssamstatic int 738185222Ssamffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts) 739185222Ssam{ 740185222Ssam fsnode *cur; 741185222Ssam dirbuf_t dirbuf; 742185222Ssam union dinode din; 743185222Ssam void *membuf; 744185222Ssam char path[MAXPATHLEN + 1]; 745214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 746185222Ssam 747185222Ssam assert(dir != NULL); 748185222Ssam assert(root != NULL); 749185222Ssam assert(fsopts != NULL); 750214921Scognet assert(ffs_opts != NULL); 751185222Ssam 752185222Ssam (void)memset(&dirbuf, 0, sizeof(dirbuf)); 753185222Ssam 754185222Ssam if (debug & DEBUG_FS_POPULATE) 755185222Ssam printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root); 756185222Ssam 757185222Ssam /* 758185222Ssam * pass 1: allocate inode numbers, build directory `file' 759185222Ssam */ 760185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 761185222Ssam if ((cur->inode->flags & FI_ALLOCATED) == 0) { 762185222Ssam cur->inode->flags |= FI_ALLOCATED; 763185222Ssam if (cur == root && cur->parent != NULL) 764185222Ssam cur->inode->ino = cur->parent->inode->ino; 765185222Ssam else { 766185222Ssam cur->inode->ino = fsopts->curinode; 767185222Ssam fsopts->curinode++; 768185222Ssam } 769185222Ssam } 770185222Ssam ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap); 771185222Ssam if (cur == root) { /* we're at "."; add ".." */ 772185222Ssam ffs_make_dirbuf(&dirbuf, "..", 773185222Ssam cur->parent == NULL ? cur : cur->parent->first, 774185222Ssam fsopts->needswap); 775185222Ssam root->inode->nlink++; /* count my parent's link */ 776185222Ssam } else if (cur->child != NULL) 777185222Ssam root->inode->nlink++; /* count my child's link */ 778185222Ssam 779185222Ssam /* 780185222Ssam * XXX possibly write file and long symlinks here, 781185222Ssam * ensuring that blocks get written before inodes? 782185222Ssam * otoh, this isn't a real filesystem, so who 783185222Ssam * cares about ordering? :-) 784185222Ssam */ 785185222Ssam } 786185222Ssam if (debug & DEBUG_FS_POPULATE_DIRBUF) 787185222Ssam ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap); 788185222Ssam 789185222Ssam /* 790185222Ssam * pass 2: write out dirbuf, then non-directories at this level 791185222Ssam */ 792185222Ssam if (debug & DEBUG_FS_POPULATE) 793185222Ssam printf("ffs_populate_dir: PASS 2 dir %s\n", dir); 794185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 795185222Ssam if (cur->inode->flags & FI_WRITTEN) 796185222Ssam continue; /* skip hard-linked entries */ 797185222Ssam cur->inode->flags |= FI_WRITTEN; 798185222Ssam 799223306Smarcel if (cur->contents == NULL) { 800230795Sjkim if (snprintf(path, sizeof(path), "%s/%s/%s", cur->root, 801230795Sjkim cur->path, cur->name) >= (int)sizeof(path)) 802223306Smarcel errx(1, "Pathname too long."); 803223306Smarcel } 804185222Ssam 805185222Ssam if (cur->child != NULL) 806185222Ssam continue; /* child creates own inode */ 807185222Ssam 808185222Ssam /* build on-disk inode */ 809214921Scognet if (ffs_opts->version == 1) 810185222Ssam membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur, 811185222Ssam root, fsopts); 812185222Ssam else 813185222Ssam membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur, 814185222Ssam root, fsopts); 815185222Ssam 816185222Ssam if (debug & DEBUG_FS_POPULATE_NODE) { 817185222Ssam printf("ffs_populate_dir: writing ino %d, %s", 818185222Ssam cur->inode->ino, inode_type(cur->type)); 819185222Ssam if (cur->inode->nlink > 1) 820185222Ssam printf(", nlink %d", cur->inode->nlink); 821185222Ssam putchar('\n'); 822185222Ssam } 823185222Ssam 824185222Ssam if (membuf != NULL) { 825185222Ssam ffs_write_file(&din, cur->inode->ino, membuf, fsopts); 826185222Ssam } else if (S_ISREG(cur->type)) { 827223306Smarcel ffs_write_file(&din, cur->inode->ino, 828223306Smarcel (cur->contents) ? cur->contents : path, fsopts); 829185222Ssam } else { 830185222Ssam assert (! S_ISDIR(cur->type)); 831185222Ssam ffs_write_inode(&din, cur->inode->ino, fsopts); 832185222Ssam } 833185222Ssam } 834185222Ssam 835185222Ssam /* 836185222Ssam * pass 3: write out sub-directories 837185222Ssam */ 838185222Ssam if (debug & DEBUG_FS_POPULATE) 839185222Ssam printf("ffs_populate_dir: PASS 3 dir %s\n", dir); 840185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 841185222Ssam if (cur->child == NULL) 842185222Ssam continue; 843185222Ssam if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) 844185222Ssam >= sizeof(path)) 845185222Ssam errx(1, "Pathname too long."); 846185222Ssam if (! ffs_populate_dir(path, cur->child, fsopts)) 847185222Ssam return (0); 848185222Ssam } 849185222Ssam 850185222Ssam if (debug & DEBUG_FS_POPULATE) 851185222Ssam printf("ffs_populate_dir: DONE dir %s\n", dir); 852185222Ssam 853185222Ssam /* cleanup */ 854185222Ssam if (dirbuf.buf != NULL) 855185222Ssam free(dirbuf.buf); 856185222Ssam return (1); 857185222Ssam} 858185222Ssam 859185222Ssam 860185222Ssamstatic void 861185222Ssamffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts) 862185222Ssam{ 863185222Ssam int isfile, ffd; 864185222Ssam char *fbuf, *p; 865185222Ssam off_t bufleft, chunk, offset; 866214921Scognet ssize_t nread; 867185222Ssam struct inode in; 868185222Ssam struct buf * bp; 869214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 870185222Ssam 871185222Ssam assert (din != NULL); 872185222Ssam assert (buf != NULL); 873185222Ssam assert (fsopts != NULL); 874214921Scognet assert (ffs_opts != NULL); 875185222Ssam 876185222Ssam isfile = S_ISREG(DIP(din, mode)); 877185222Ssam fbuf = NULL; 878185222Ssam ffd = -1; 879214921Scognet p = NULL; 880185222Ssam 881185222Ssam in.i_fs = (struct fs *)fsopts->superblock; 882185222Ssam 883185222Ssam if (debug & DEBUG_FS_WRITE_FILE) { 884185222Ssam printf( 885185222Ssam "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld", 886185222Ssam ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT), 887185222Ssam (long long)DIP(din, size)); 888185222Ssam if (isfile) 889185222Ssam printf(", file '%s'\n", (char *)buf); 890185222Ssam else 891185222Ssam printf(", buffer %p\n", buf); 892185222Ssam } 893185222Ssam 894185222Ssam in.i_number = ino; 895185222Ssam in.i_size = DIP(din, size); 896214921Scognet if (ffs_opts->version == 1) 897185222Ssam memcpy(&in.i_din.ffs1_din, &din->ffs1_din, 898185222Ssam sizeof(in.i_din.ffs1_din)); 899185222Ssam else 900185222Ssam memcpy(&in.i_din.ffs2_din, &din->ffs2_din, 901185222Ssam sizeof(in.i_din.ffs2_din)); 902185222Ssam in.i_fd = fsopts->fd; 903185222Ssam 904185222Ssam if (DIP(din, size) == 0) 905185222Ssam goto write_inode_and_leave; /* mmm, cheating */ 906185222Ssam 907185222Ssam if (isfile) { 908214921Scognet if ((fbuf = malloc(ffs_opts->bsize)) == NULL) 909185222Ssam err(1, "Allocating memory for write buffer"); 910185222Ssam if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) { 911185222Ssam warn("Can't open `%s' for reading", (char *)buf); 912185222Ssam goto leave_ffs_write_file; 913185222Ssam } 914185222Ssam } else { 915185222Ssam p = buf; 916185222Ssam } 917185222Ssam 918185222Ssam chunk = 0; 919185222Ssam for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) { 920214921Scognet chunk = MIN(bufleft, ffs_opts->bsize); 921214921Scognet if (!isfile) 922214921Scognet ; 923214921Scognet else if ((nread = read(ffd, fbuf, chunk)) == -1) 924214921Scognet err(EXIT_FAILURE, "Reading `%s', %lld bytes to go", 925214921Scognet (char *)buf, (long long)bufleft); 926214921Scognet else if (nread != chunk) 927214921Scognet errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, " 928214921Scognet "read %zd bytes, expected %ju bytes, does " 929214921Scognet "metalog size= attribute mismatch source size?", 930214921Scognet (char *)buf, (long long)bufleft, nread, 931214921Scognet (uintmax_t)chunk); 932214921Scognet else 933185222Ssam p = fbuf; 934185222Ssam offset = DIP(din, size) - bufleft; 935185222Ssam if (debug & DEBUG_FS_WRITE_FILE_BLOCK) 936185222Ssam printf( 937185222Ssam "ffs_write_file: write %p offset %lld size %lld left %lld\n", 938185222Ssam p, (long long)offset, 939185222Ssam (long long)chunk, (long long)bufleft); 940185222Ssam /* 941185222Ssam * XXX if holey support is desired, do the check here 942185222Ssam * 943185222Ssam * XXX might need to write out last bit in fragroundup 944185222Ssam * sized chunk. however, ffs_balloc() handles this for us 945185222Ssam */ 946185222Ssam errno = ffs_balloc(&in, offset, chunk, &bp); 947185222Ssam bad_ffs_write_file: 948185222Ssam if (errno != 0) 949185222Ssam err(1, 950185222Ssam "Writing inode %d (%s), bytes %lld + %lld", 951185222Ssam ino, 952185222Ssam isfile ? (char *)buf : 953185222Ssam inode_type(DIP(din, mode) & S_IFMT), 954185222Ssam (long long)offset, (long long)chunk); 955185222Ssam memcpy(bp->b_data, p, chunk); 956185222Ssam errno = bwrite(bp); 957185222Ssam if (errno != 0) 958185222Ssam goto bad_ffs_write_file; 959185222Ssam brelse(bp); 960185222Ssam if (!isfile) 961185222Ssam p += chunk; 962185222Ssam } 963185222Ssam 964185222Ssam write_inode_and_leave: 965185222Ssam ffs_write_inode(&in.i_din, in.i_number, fsopts); 966185222Ssam 967185222Ssam leave_ffs_write_file: 968185222Ssam if (fbuf) 969185222Ssam free(fbuf); 970185222Ssam if (ffd != -1) 971185222Ssam close(ffd); 972185222Ssam} 973185222Ssam 974185222Ssam 975185222Ssamstatic void 976185222Ssamffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap) 977185222Ssam{ 978185222Ssam doff_t i; 979185222Ssam struct direct *de; 980185222Ssam uint16_t reclen; 981185222Ssam 982185222Ssam assert (dbuf != NULL); 983185222Ssam assert (dir != NULL); 984185222Ssam printf("ffs_dump_dirbuf: dir %s size %d cur %d\n", 985185222Ssam dir, dbuf->size, dbuf->cur); 986185222Ssam 987185222Ssam for (i = 0; i < dbuf->size; ) { 988185222Ssam de = (struct direct *)(dbuf->buf + i); 989185222Ssam reclen = ufs_rw16(de->d_reclen, needswap); 990185222Ssam printf( 991185222Ssam " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n", 992185222Ssam ufs_rw32(de->d_ino, needswap), 993185222Ssam inode_type(DTTOIF(de->d_type)), i, reclen, 994185222Ssam de->d_namlen, de->d_name); 995185222Ssam i += reclen; 996185222Ssam assert(reclen > 0); 997185222Ssam } 998185222Ssam} 999185222Ssam 1000185222Ssamstatic void 1001185222Ssamffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap) 1002185222Ssam{ 1003185222Ssam struct direct de, *dp; 1004185222Ssam uint16_t llen, reclen; 1005214921Scognet u_char *newbuf; 1006185222Ssam 1007185222Ssam assert (dbuf != NULL); 1008185222Ssam assert (name != NULL); 1009185222Ssam assert (node != NULL); 1010185222Ssam /* create direct entry */ 1011185222Ssam (void)memset(&de, 0, sizeof(de)); 1012185222Ssam de.d_ino = ufs_rw32(node->inode->ino, needswap); 1013185222Ssam de.d_type = IFTODT(node->type); 1014185222Ssam de.d_namlen = (uint8_t)strlen(name); 1015185222Ssam strcpy(de.d_name, name); 1016186261Ssam reclen = DIRSIZ_SWAP(0, &de, needswap); 1017185222Ssam de.d_reclen = ufs_rw16(reclen, needswap); 1018185222Ssam 1019185222Ssam dp = (struct direct *)(dbuf->buf + dbuf->cur); 1020185222Ssam llen = 0; 1021185222Ssam if (dp != NULL) 1022186261Ssam llen = DIRSIZ_SWAP(0, dp, needswap); 1023185222Ssam 1024185222Ssam if (debug & DEBUG_FS_MAKE_DIRBUF) 1025185222Ssam printf( 1026185222Ssam "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n" 1027185222Ssam " ino %d type %d reclen %d namlen %d name %.30s\n", 1028185222Ssam dbuf->size, dbuf->cur, llen, 1029185222Ssam ufs_rw32(de.d_ino, needswap), de.d_type, reclen, 1030185222Ssam de.d_namlen, de.d_name); 1031185222Ssam 1032185222Ssam if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) { 1033185222Ssam if (debug & DEBUG_FS_MAKE_DIRBUF) 1034185222Ssam printf("ffs_make_dirbuf: growing buf to %d\n", 1035185222Ssam dbuf->size + DIRBLKSIZ); 1036185222Ssam if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL) 1037185222Ssam err(1, "Allocating memory for directory buffer"); 1038185222Ssam dbuf->buf = newbuf; 1039185222Ssam dbuf->size += DIRBLKSIZ; 1040185222Ssam memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ); 1041185222Ssam dbuf->cur = dbuf->size - DIRBLKSIZ; 1042214921Scognet } else if (dp) { /* shrink end of previous */ 1043185222Ssam dp->d_reclen = ufs_rw16(llen,needswap); 1044185222Ssam dbuf->cur += llen; 1045185222Ssam } 1046185222Ssam dp = (struct direct *)(dbuf->buf + dbuf->cur); 1047185222Ssam memcpy(dp, &de, reclen); 1048185222Ssam dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap); 1049185222Ssam} 1050185222Ssam 1051185222Ssam/* 1052185222Ssam * cribbed from sys/ufs/ffs/ffs_alloc.c 1053185222Ssam */ 1054185222Ssamstatic void 1055185222Ssamffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts) 1056185222Ssam{ 1057185222Ssam char *buf; 1058185222Ssam struct ufs1_dinode *dp1; 1059185222Ssam struct ufs2_dinode *dp2, *dip; 1060185222Ssam struct cg *cgp; 1061185222Ssam struct fs *fs; 1062185222Ssam int cg, cgino, i; 1063185222Ssam daddr_t d; 1064185222Ssam char sbbuf[FFS_MAXBSIZE]; 1065185222Ssam int32_t initediblk; 1066214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 1067185222Ssam 1068185222Ssam assert (dp != NULL); 1069185222Ssam assert (ino > 0); 1070185222Ssam assert (fsopts != NULL); 1071214921Scognet assert (ffs_opts != NULL); 1072185222Ssam 1073185222Ssam fs = (struct fs *)fsopts->superblock; 1074185222Ssam cg = ino_to_cg(fs, ino); 1075185222Ssam cgino = ino % fs->fs_ipg; 1076185222Ssam if (debug & DEBUG_FS_WRITE_INODE) 1077185222Ssam printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n", 1078185222Ssam dp, ino, cg, cgino); 1079185222Ssam 1080185222Ssam ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, 1081185222Ssam fsopts); 1082185222Ssam cgp = (struct cg *)sbbuf; 1083186261Ssam if (!cg_chkmagic_swap(cgp, fsopts->needswap)) 1084185222Ssam errx(1, "ffs_write_inode: cg %d: bad magic number", cg); 1085185222Ssam 1086186261Ssam assert (isclr(cg_inosused_swap(cgp, fsopts->needswap), cgino)); 1087185222Ssam 1088185222Ssam buf = malloc(fs->fs_bsize); 1089185222Ssam if (buf == NULL) 1090185222Ssam errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg); 1091185222Ssam 1092185222Ssam dp1 = (struct ufs1_dinode *)buf; 1093185222Ssam dp2 = (struct ufs2_dinode *)buf; 1094185222Ssam 1095185222Ssam if (fs->fs_cstotal.cs_nifree == 0) 1096185222Ssam errx(1, "ffs_write_inode: fs out of inodes for ino %u", 1097185222Ssam ino); 1098185222Ssam if (fs->fs_cs(fs, cg).cs_nifree == 0) 1099185222Ssam errx(1, 1100185222Ssam "ffs_write_inode: cg %d out of inodes for ino %u", 1101185222Ssam cg, ino); 1102186261Ssam setbit(cg_inosused_swap(cgp, fsopts->needswap), cgino); 1103185222Ssam ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap); 1104185222Ssam fs->fs_cstotal.cs_nifree--; 1105185222Ssam fs->fs_cs(fs, cg).cs_nifree--; 1106185222Ssam if (S_ISDIR(DIP(dp, mode))) { 1107185222Ssam ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap); 1108185222Ssam fs->fs_cstotal.cs_ndir++; 1109185222Ssam fs->fs_cs(fs, cg).cs_ndir++; 1110185222Ssam } 1111185222Ssam 1112185222Ssam /* 1113185222Ssam * Initialize inode blocks on the fly for UFS2. 1114185222Ssam */ 1115185222Ssam initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap); 1116214921Scognet if (ffs_opts->version == 2 && cgino + INOPB(fs) > initediblk && 1117185222Ssam initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) { 1118185222Ssam memset(buf, 0, fs->fs_bsize); 1119185222Ssam dip = (struct ufs2_dinode *)buf; 1120185222Ssam srandom(time(NULL)); 1121185222Ssam for (i = 0; i < INOPB(fs); i++) { 1122185222Ssam dip->di_gen = random() / 2 + 1; 1123185222Ssam dip++; 1124185222Ssam } 1125185222Ssam ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs, 1126185222Ssam cg * fs->fs_ipg + initediblk)), 1127185222Ssam fs->fs_bsize, buf, fsopts); 1128185222Ssam initediblk += INOPB(fs); 1129185222Ssam cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap); 1130185222Ssam } 1131185222Ssam 1132185222Ssam 1133185222Ssam ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, 1134185222Ssam fsopts); 1135185222Ssam 1136185222Ssam /* now write inode */ 1137185222Ssam d = fsbtodb(fs, ino_to_fsba(fs, ino)); 1138185222Ssam ffs_rdfs(d, fs->fs_bsize, buf, fsopts); 1139185222Ssam if (fsopts->needswap) { 1140214921Scognet if (ffs_opts->version == 1) 1141185222Ssam ffs_dinode1_swap(&dp->ffs1_din, 1142185222Ssam &dp1[ino_to_fsbo(fs, ino)]); 1143185222Ssam else 1144185222Ssam ffs_dinode2_swap(&dp->ffs2_din, 1145185222Ssam &dp2[ino_to_fsbo(fs, ino)]); 1146185222Ssam } else { 1147214921Scognet if (ffs_opts->version == 1) 1148185222Ssam dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din; 1149185222Ssam else 1150185222Ssam dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din; 1151185222Ssam } 1152185222Ssam ffs_wtfs(d, fs->fs_bsize, buf, fsopts); 1153185222Ssam free(buf); 1154185222Ssam} 1155185222Ssam 1156185222Ssamvoid 1157185222Ssampanic(const char *fmt, ...) 1158185222Ssam{ 1159185222Ssam va_list ap; 1160185222Ssam 1161185222Ssam va_start(ap, fmt); 1162185222Ssam vwarnx(fmt, ap); 1163185222Ssam va_end(ap); 1164185222Ssam exit(1); 1165185222Ssam} 1166