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 148248293Sbrooks /* 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" }, 194214921Scognet { "maxbpf", &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; 239226275Snwhitehorn } else if (strcmp(var, "label") == 0) { 240226275Snwhitehorn strlcpy(ffs_opts->label, val, sizeof(ffs_opts->label)); 241226275Snwhitehorn 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 413185222Ssam /* calculate density if necessary */ 414214921Scognet if (ffs_opts->density == -1) 415214921Scognet ffs_opts->density = fsopts->size / fsopts->inodes + 1; 416185222Ssam 417185222Ssam if (debug & DEBUG_FS_VALIDATE) { 418185222Ssam printf("ffs_validate: after defaults set:\n"); 419185222Ssam ffs_dump_fsinfo(fsopts); 420185222Ssam printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n", 421185222Ssam dir, (long long)fsopts->size, (long long)fsopts->inodes); 422185222Ssam } 423185222Ssam sectorsize = fsopts->sectorsize; /* XXX - see earlier */ 424214921Scognet 425214921Scognet /* now check calculated sizes vs requested sizes */ 426214921Scognet if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) { 427214921Scognet errx(1, "`%s' size of %lld is larger than the maxsize of %lld.", 428214921Scognet dir, (long long)fsopts->size, (long long)fsopts->maxsize); 429214921Scognet } 430185222Ssam} 431185222Ssam 432185222Ssam 433185222Ssamstatic void 434185222Ssamffs_dump_fsinfo(fsinfo_t *f) 435185222Ssam{ 436185222Ssam 437214921Scognet ffs_opt_t *fs = f->fs_specific; 438214921Scognet 439185222Ssam printf("fsopts at %p\n", f); 440185222Ssam 441185222Ssam printf("\tsize %lld, inodes %lld, curinode %u\n", 442185222Ssam (long long)f->size, (long long)f->inodes, f->curinode); 443185222Ssam 444185222Ssam printf("\tminsize %lld, maxsize %lld\n", 445185222Ssam (long long)f->minsize, (long long)f->maxsize); 446185222Ssam printf("\tfree files %lld, freefile %% %d\n", 447185222Ssam (long long)f->freefiles, f->freefilepc); 448185222Ssam printf("\tfree blocks %lld, freeblock %% %d\n", 449185222Ssam (long long)f->freeblocks, f->freeblockpc); 450185222Ssam printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize); 451185222Ssam 452185222Ssam printf("\tbsize %d, fsize %d, cpg %d, density %d\n", 453214921Scognet fs->bsize, fs->fsize, fs->cpg, fs->density); 454185222Ssam printf("\tnsectors %d, rpm %d, minfree %d\n", 455214921Scognet fs->nsectors, fs->rpm, fs->minfree); 456185222Ssam printf("\tmaxcontig %d, maxbpg %d\n", 457214921Scognet fs->maxcontig, fs->maxbpg); 458185222Ssam printf("\toptimization %s\n", 459214921Scognet fs->optimization == FS_OPTSPACE ? "space" : "time"); 460185222Ssam} 461185222Ssam 462185222Ssam 463185222Ssamstatic int 464185222Ssamffs_create_image(const char *image, fsinfo_t *fsopts) 465185222Ssam{ 466214921Scognet#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS 467185222Ssam struct statvfs sfs; 468185222Ssam#endif 469185222Ssam struct fs *fs; 470185222Ssam char *buf; 471185222Ssam int i, bufsize; 472185222Ssam off_t bufrem; 473185222Ssam 474185222Ssam assert (image != NULL); 475185222Ssam assert (fsopts != NULL); 476185222Ssam 477185222Ssam /* create image */ 478214921Scognet if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0666)) 479185222Ssam == -1) { 480185222Ssam warn("Can't open `%s' for writing", image); 481185222Ssam return (-1); 482185222Ssam } 483185222Ssam 484185222Ssam /* zero image */ 485214921Scognet#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS 486185222Ssam if (fstatvfs(fsopts->fd, &sfs) == -1) { 487185222Ssam#endif 488185222Ssam bufsize = 8192; 489214921Scognet#if HAVE_STRUCT_STATVFS_F_IOSIZE && HAVE_FSTATVFS 490185222Ssam warn("can't fstatvfs `%s', using default %d byte chunk", 491185222Ssam image, bufsize); 492185222Ssam } else 493185222Ssam bufsize = sfs.f_iosize; 494185222Ssam#endif 495185222Ssam bufrem = fsopts->size; 496248293Sbrooks if (fsopts->sparse) { 497248293Sbrooks if (ftruncate(fsopts->fd, bufrem) == -1) { 498248293Sbrooks warn("sparse option disabled.\n"); 499248293Sbrooks fsopts->sparse = 0; 500248293Sbrooks } 501185222Ssam } 502248293Sbrooks if (fsopts->sparse) { 503248293Sbrooks /* File truncated at bufrem. Remaining is 0 */ 504248293Sbrooks bufrem = 0; 505248293Sbrooks buf = NULL; 506248293Sbrooks } else { 507248293Sbrooks if (debug & DEBUG_FS_CREATE_IMAGE) 508248293Sbrooks printf("zero-ing image `%s', %lld sectors, " 509248293Sbrooks "using %d byte chunks\n", image, (long long)bufrem, 510248293Sbrooks bufsize); 511248293Sbrooks if ((buf = calloc(1, bufsize)) == NULL) { 512248293Sbrooks warn("Can't create buffer for sector"); 513248293Sbrooks return (-1); 514248293Sbrooks } 515248293Sbrooks } 516185222Ssam while (bufrem > 0) { 517185222Ssam i = write(fsopts->fd, buf, MIN(bufsize, bufrem)); 518185222Ssam if (i == -1) { 519185222Ssam warn("zeroing image, %lld bytes to go", 520185222Ssam (long long)bufrem); 521214921Scognet free(buf); 522185222Ssam return (-1); 523185222Ssam } 524185222Ssam bufrem -= i; 525185222Ssam } 526248293Sbrooks if (buf) 527248293Sbrooks free(buf); 528185222Ssam 529185222Ssam /* make the file system */ 530185222Ssam if (debug & DEBUG_FS_CREATE_IMAGE) 531185222Ssam printf("calling mkfs(\"%s\", ...)\n", image); 532185222Ssam fs = ffs_mkfs(image, fsopts); 533185222Ssam fsopts->superblock = (void *)fs; 534185222Ssam if (debug & DEBUG_FS_CREATE_IMAGE) { 535185222Ssam time_t t; 536185222Ssam 537185222Ssam t = (time_t)((struct fs *)fsopts->superblock)->fs_time; 538185222Ssam printf("mkfs returned %p; fs_time %s", 539185222Ssam fsopts->superblock, ctime(&t)); 540185222Ssam printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n", 541185222Ssam (long long)fs->fs_cstotal.cs_nbfree, 542185222Ssam (long long)fs->fs_cstotal.cs_nffree, 543185222Ssam (long long)fs->fs_cstotal.cs_nifree, 544185222Ssam (long long)fs->fs_cstotal.cs_ndir); 545185222Ssam } 546185222Ssam 547185222Ssam if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) { 548185222Ssam warnx( 549185222Ssam "Image file `%s' has %lld free inodes; %lld are required.", 550185222Ssam image, 551214921Scognet (long long)(fs->fs_cstotal.cs_nifree + ROOTINO), 552185222Ssam (long long)fsopts->inodes); 553185222Ssam return (-1); 554185222Ssam } 555185222Ssam return (fsopts->fd); 556185222Ssam} 557185222Ssam 558185222Ssam 559185222Ssamstatic void 560185222Ssamffs_size_dir(fsnode *root, fsinfo_t *fsopts) 561185222Ssam{ 562185222Ssam struct direct tmpdir; 563185222Ssam fsnode * node; 564185222Ssam int curdirsize, this; 565214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 566185222Ssam 567185222Ssam /* node may be NULL (empty directory) */ 568185222Ssam assert(fsopts != NULL); 569214921Scognet assert(ffs_opts != NULL); 570185222Ssam 571185222Ssam if (debug & DEBUG_FS_SIZE_DIR) 572185222Ssam printf("ffs_size_dir: entry: bytes %lld inodes %lld\n", 573185222Ssam (long long)fsopts->size, (long long)fsopts->inodes); 574185222Ssam 575185222Ssam#define ADDDIRENT(e) do { \ 576185222Ssam tmpdir.d_namlen = strlen((e)); \ 577214921Scognet this = DIRSIZ_SWAP(0, &tmpdir, 0); \ 578185222Ssam if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ 579185222Ssam printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \ 580185222Ssam e, tmpdir.d_namlen, this, curdirsize); \ 581185222Ssam if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \ 582185222Ssam curdirsize = roundup(curdirsize, DIRBLKSIZ); \ 583185222Ssam curdirsize += this; \ 584185222Ssam if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \ 585185222Ssam printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \ 586185222Ssam e, tmpdir.d_namlen, this, curdirsize); \ 587185222Ssam} while (0); 588185222Ssam 589185222Ssam /* 590185222Ssam * XXX this needs to take into account extra space consumed 591185222Ssam * by indirect blocks, etc. 592185222Ssam */ 593185222Ssam#define ADDSIZE(x) do { \ 594214921Scognet fsopts->size += roundup((x), ffs_opts->fsize); \ 595185222Ssam} while (0); 596185222Ssam 597185222Ssam curdirsize = 0; 598185222Ssam for (node = root; node != NULL; node = node->next) { 599185222Ssam ADDDIRENT(node->name); 600185222Ssam if (node == root) { /* we're at "." */ 601185222Ssam assert(strcmp(node->name, ".") == 0); 602185222Ssam ADDDIRENT(".."); 603185222Ssam } else if ((node->inode->flags & FI_SIZED) == 0) { 604185222Ssam /* don't count duplicate names */ 605185222Ssam node->inode->flags |= FI_SIZED; 606185222Ssam if (debug & DEBUG_FS_SIZE_DIR_NODE) 607185222Ssam printf("ffs_size_dir: `%s' size %lld\n", 608185222Ssam node->name, 609185222Ssam (long long)node->inode->st.st_size); 610185222Ssam fsopts->inodes++; 611185222Ssam if (node->type == S_IFREG) 612185222Ssam ADDSIZE(node->inode->st.st_size); 613185222Ssam if (node->type == S_IFLNK) { 614185222Ssam int slen; 615185222Ssam 616185222Ssam slen = strlen(node->symlink) + 1; 617214921Scognet if (slen >= (ffs_opts->version == 1 ? 618185222Ssam MAXSYMLINKLEN_UFS1 : 619185222Ssam MAXSYMLINKLEN_UFS2)) 620185222Ssam ADDSIZE(slen); 621185222Ssam } 622185222Ssam } 623185222Ssam if (node->type == S_IFDIR) 624185222Ssam ffs_size_dir(node->child, fsopts); 625185222Ssam } 626185222Ssam ADDSIZE(curdirsize); 627185222Ssam 628185222Ssam if (debug & DEBUG_FS_SIZE_DIR) 629185222Ssam printf("ffs_size_dir: exit: size %lld inodes %lld\n", 630185222Ssam (long long)fsopts->size, (long long)fsopts->inodes); 631185222Ssam} 632185222Ssam 633185222Ssamstatic void * 634185222Ssamffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, 635185222Ssam fsnode *root, fsinfo_t *fsopts) 636185222Ssam{ 637185222Ssam int slen; 638185222Ssam void *membuf; 639185222Ssam 640185222Ssam memset(dinp, 0, sizeof(*dinp)); 641185222Ssam dinp->di_mode = cur->inode->st.st_mode; 642185222Ssam dinp->di_nlink = cur->inode->nlink; 643185222Ssam dinp->di_size = cur->inode->st.st_size; 644185222Ssam dinp->di_atime = cur->inode->st.st_atime; 645185222Ssam dinp->di_mtime = cur->inode->st.st_mtime; 646185222Ssam dinp->di_ctime = cur->inode->st.st_ctime; 647185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 648185222Ssam dinp->di_atimensec = cur->inode->st.st_atimensec; 649185222Ssam dinp->di_mtimensec = cur->inode->st.st_mtimensec; 650185222Ssam dinp->di_ctimensec = cur->inode->st.st_ctimensec; 651185222Ssam#endif 652185222Ssam#if HAVE_STRUCT_STAT_ST_FLAGS 653185222Ssam dinp->di_flags = cur->inode->st.st_flags; 654185222Ssam#endif 655185222Ssam#if HAVE_STRUCT_STAT_ST_GEN 656185222Ssam dinp->di_gen = cur->inode->st.st_gen; 657185222Ssam#endif 658185222Ssam dinp->di_uid = cur->inode->st.st_uid; 659185222Ssam dinp->di_gid = cur->inode->st.st_gid; 660185222Ssam /* not set: di_db, di_ib, di_blocks, di_spare */ 661185222Ssam 662185222Ssam membuf = NULL; 663185222Ssam if (cur == root) { /* "."; write dirbuf */ 664185222Ssam membuf = dbufp->buf; 665185222Ssam dinp->di_size = dbufp->size; 666185222Ssam } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { 667185222Ssam dinp->di_size = 0; /* a device */ 668185222Ssam dinp->di_rdev = 669185222Ssam ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap); 670185222Ssam } else if (S_ISLNK(cur->type)) { /* symlink */ 671185222Ssam slen = strlen(cur->symlink); 672185222Ssam if (slen < MAXSYMLINKLEN_UFS1) { /* short link */ 673185222Ssam memcpy(dinp->di_db, cur->symlink, slen); 674185222Ssam } else 675185222Ssam membuf = cur->symlink; 676185222Ssam dinp->di_size = slen; 677185222Ssam } 678185222Ssam return membuf; 679185222Ssam} 680185222Ssam 681185222Ssamstatic void * 682185222Ssamffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur, 683185222Ssam fsnode *root, fsinfo_t *fsopts) 684185222Ssam{ 685185222Ssam int slen; 686185222Ssam void *membuf; 687185222Ssam 688185222Ssam memset(dinp, 0, sizeof(*dinp)); 689185222Ssam dinp->di_mode = cur->inode->st.st_mode; 690185222Ssam dinp->di_nlink = cur->inode->nlink; 691185222Ssam dinp->di_size = cur->inode->st.st_size; 692185222Ssam dinp->di_atime = cur->inode->st.st_atime; 693185222Ssam dinp->di_mtime = cur->inode->st.st_mtime; 694185222Ssam dinp->di_ctime = cur->inode->st.st_ctime; 695185222Ssam#if HAVE_STRUCT_STAT_ST_MTIMENSEC 696185222Ssam dinp->di_atimensec = cur->inode->st.st_atimensec; 697185222Ssam dinp->di_mtimensec = cur->inode->st.st_mtimensec; 698185222Ssam dinp->di_ctimensec = cur->inode->st.st_ctimensec; 699185222Ssam#endif 700185222Ssam#if HAVE_STRUCT_STAT_ST_FLAGS 701185222Ssam dinp->di_flags = cur->inode->st.st_flags; 702185222Ssam#endif 703185222Ssam#if HAVE_STRUCT_STAT_ST_GEN 704185222Ssam dinp->di_gen = cur->inode->st.st_gen; 705185222Ssam#endif 706185222Ssam#if HAVE_STRUCT_STAT_BIRTHTIME 707185222Ssam dinp->di_birthtime = cur->inode->st.st_birthtime; 708185222Ssam dinp->di_birthnsec = cur->inode->st.st_birthtimensec; 709185222Ssam#endif 710185222Ssam dinp->di_uid = cur->inode->st.st_uid; 711185222Ssam dinp->di_gid = cur->inode->st.st_gid; 712185222Ssam /* not set: di_db, di_ib, di_blocks, di_spare */ 713185222Ssam 714185222Ssam membuf = NULL; 715185222Ssam if (cur == root) { /* "."; write dirbuf */ 716185222Ssam membuf = dbufp->buf; 717185222Ssam dinp->di_size = dbufp->size; 718185222Ssam } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) { 719185222Ssam dinp->di_size = 0; /* a device */ 720185222Ssam dinp->di_rdev = 721185222Ssam ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap); 722185222Ssam } else if (S_ISLNK(cur->type)) { /* symlink */ 723185222Ssam slen = strlen(cur->symlink); 724185222Ssam if (slen < MAXSYMLINKLEN_UFS2) { /* short link */ 725185222Ssam memcpy(dinp->di_db, cur->symlink, slen); 726185222Ssam } else 727185222Ssam membuf = cur->symlink; 728185222Ssam dinp->di_size = slen; 729185222Ssam } 730185222Ssam return membuf; 731185222Ssam} 732185222Ssam 733185222Ssamstatic int 734185222Ssamffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts) 735185222Ssam{ 736185222Ssam fsnode *cur; 737185222Ssam dirbuf_t dirbuf; 738185222Ssam union dinode din; 739185222Ssam void *membuf; 740185222Ssam char path[MAXPATHLEN + 1]; 741214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 742185222Ssam 743185222Ssam assert(dir != NULL); 744185222Ssam assert(root != NULL); 745185222Ssam assert(fsopts != NULL); 746214921Scognet assert(ffs_opts != NULL); 747185222Ssam 748185222Ssam (void)memset(&dirbuf, 0, sizeof(dirbuf)); 749185222Ssam 750185222Ssam if (debug & DEBUG_FS_POPULATE) 751185222Ssam printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root); 752185222Ssam 753185222Ssam /* 754185222Ssam * pass 1: allocate inode numbers, build directory `file' 755185222Ssam */ 756185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 757185222Ssam if ((cur->inode->flags & FI_ALLOCATED) == 0) { 758185222Ssam cur->inode->flags |= FI_ALLOCATED; 759185222Ssam if (cur == root && cur->parent != NULL) 760185222Ssam cur->inode->ino = cur->parent->inode->ino; 761185222Ssam else { 762185222Ssam cur->inode->ino = fsopts->curinode; 763185222Ssam fsopts->curinode++; 764185222Ssam } 765185222Ssam } 766185222Ssam ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap); 767185222Ssam if (cur == root) { /* we're at "."; add ".." */ 768185222Ssam ffs_make_dirbuf(&dirbuf, "..", 769185222Ssam cur->parent == NULL ? cur : cur->parent->first, 770185222Ssam fsopts->needswap); 771185222Ssam root->inode->nlink++; /* count my parent's link */ 772185222Ssam } else if (cur->child != NULL) 773185222Ssam root->inode->nlink++; /* count my child's link */ 774185222Ssam 775185222Ssam /* 776185222Ssam * XXX possibly write file and long symlinks here, 777185222Ssam * ensuring that blocks get written before inodes? 778185222Ssam * otoh, this isn't a real filesystem, so who 779185222Ssam * cares about ordering? :-) 780185222Ssam */ 781185222Ssam } 782185222Ssam if (debug & DEBUG_FS_POPULATE_DIRBUF) 783185222Ssam ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap); 784185222Ssam 785185222Ssam /* 786185222Ssam * pass 2: write out dirbuf, then non-directories at this level 787185222Ssam */ 788185222Ssam if (debug & DEBUG_FS_POPULATE) 789185222Ssam printf("ffs_populate_dir: PASS 2 dir %s\n", dir); 790185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 791185222Ssam if (cur->inode->flags & FI_WRITTEN) 792185222Ssam continue; /* skip hard-linked entries */ 793185222Ssam cur->inode->flags |= FI_WRITTEN; 794185222Ssam 795223306Smarcel if (cur->contents == NULL) { 796231942Sjkim if (snprintf(path, sizeof(path), "%s/%s/%s", cur->root, 797231942Sjkim cur->path, cur->name) >= (int)sizeof(path)) 798223306Smarcel errx(1, "Pathname too long."); 799223306Smarcel } 800185222Ssam 801185222Ssam if (cur->child != NULL) 802185222Ssam continue; /* child creates own inode */ 803185222Ssam 804185222Ssam /* build on-disk inode */ 805214921Scognet if (ffs_opts->version == 1) 806185222Ssam membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur, 807185222Ssam root, fsopts); 808185222Ssam else 809185222Ssam membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur, 810185222Ssam root, fsopts); 811185222Ssam 812185222Ssam if (debug & DEBUG_FS_POPULATE_NODE) { 813185222Ssam printf("ffs_populate_dir: writing ino %d, %s", 814185222Ssam cur->inode->ino, inode_type(cur->type)); 815185222Ssam if (cur->inode->nlink > 1) 816185222Ssam printf(", nlink %d", cur->inode->nlink); 817185222Ssam putchar('\n'); 818185222Ssam } 819185222Ssam 820185222Ssam if (membuf != NULL) { 821185222Ssam ffs_write_file(&din, cur->inode->ino, membuf, fsopts); 822185222Ssam } else if (S_ISREG(cur->type)) { 823223306Smarcel ffs_write_file(&din, cur->inode->ino, 824223306Smarcel (cur->contents) ? cur->contents : path, fsopts); 825185222Ssam } else { 826185222Ssam assert (! S_ISDIR(cur->type)); 827185222Ssam ffs_write_inode(&din, cur->inode->ino, fsopts); 828185222Ssam } 829185222Ssam } 830185222Ssam 831185222Ssam /* 832185222Ssam * pass 3: write out sub-directories 833185222Ssam */ 834185222Ssam if (debug & DEBUG_FS_POPULATE) 835185222Ssam printf("ffs_populate_dir: PASS 3 dir %s\n", dir); 836185222Ssam for (cur = root; cur != NULL; cur = cur->next) { 837185222Ssam if (cur->child == NULL) 838185222Ssam continue; 839185222Ssam if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name) 840185222Ssam >= sizeof(path)) 841185222Ssam errx(1, "Pathname too long."); 842185222Ssam if (! ffs_populate_dir(path, cur->child, fsopts)) 843185222Ssam return (0); 844185222Ssam } 845185222Ssam 846185222Ssam if (debug & DEBUG_FS_POPULATE) 847185222Ssam printf("ffs_populate_dir: DONE dir %s\n", dir); 848185222Ssam 849185222Ssam /* cleanup */ 850185222Ssam if (dirbuf.buf != NULL) 851185222Ssam free(dirbuf.buf); 852185222Ssam return (1); 853185222Ssam} 854185222Ssam 855185222Ssam 856185222Ssamstatic void 857185222Ssamffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts) 858185222Ssam{ 859185222Ssam int isfile, ffd; 860185222Ssam char *fbuf, *p; 861185222Ssam off_t bufleft, chunk, offset; 862214921Scognet ssize_t nread; 863185222Ssam struct inode in; 864185222Ssam struct buf * bp; 865214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 866185222Ssam 867185222Ssam assert (din != NULL); 868185222Ssam assert (buf != NULL); 869185222Ssam assert (fsopts != NULL); 870214921Scognet assert (ffs_opts != NULL); 871185222Ssam 872185222Ssam isfile = S_ISREG(DIP(din, mode)); 873185222Ssam fbuf = NULL; 874185222Ssam ffd = -1; 875214921Scognet p = NULL; 876185222Ssam 877185222Ssam in.i_fs = (struct fs *)fsopts->superblock; 878185222Ssam 879185222Ssam if (debug & DEBUG_FS_WRITE_FILE) { 880185222Ssam printf( 881185222Ssam "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld", 882185222Ssam ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT), 883185222Ssam (long long)DIP(din, size)); 884185222Ssam if (isfile) 885185222Ssam printf(", file '%s'\n", (char *)buf); 886185222Ssam else 887185222Ssam printf(", buffer %p\n", buf); 888185222Ssam } 889185222Ssam 890185222Ssam in.i_number = ino; 891185222Ssam in.i_size = DIP(din, size); 892214921Scognet if (ffs_opts->version == 1) 893185222Ssam memcpy(&in.i_din.ffs1_din, &din->ffs1_din, 894185222Ssam sizeof(in.i_din.ffs1_din)); 895185222Ssam else 896185222Ssam memcpy(&in.i_din.ffs2_din, &din->ffs2_din, 897185222Ssam sizeof(in.i_din.ffs2_din)); 898185222Ssam in.i_fd = fsopts->fd; 899185222Ssam 900185222Ssam if (DIP(din, size) == 0) 901185222Ssam goto write_inode_and_leave; /* mmm, cheating */ 902185222Ssam 903185222Ssam if (isfile) { 904214921Scognet if ((fbuf = malloc(ffs_opts->bsize)) == NULL) 905185222Ssam err(1, "Allocating memory for write buffer"); 906185222Ssam if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) { 907185222Ssam warn("Can't open `%s' for reading", (char *)buf); 908185222Ssam goto leave_ffs_write_file; 909185222Ssam } 910185222Ssam } else { 911185222Ssam p = buf; 912185222Ssam } 913185222Ssam 914185222Ssam chunk = 0; 915185222Ssam for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) { 916214921Scognet chunk = MIN(bufleft, ffs_opts->bsize); 917214921Scognet if (!isfile) 918214921Scognet ; 919214921Scognet else if ((nread = read(ffd, fbuf, chunk)) == -1) 920214921Scognet err(EXIT_FAILURE, "Reading `%s', %lld bytes to go", 921214921Scognet (char *)buf, (long long)bufleft); 922214921Scognet else if (nread != chunk) 923214921Scognet errx(EXIT_FAILURE, "Reading `%s', %lld bytes to go, " 924214921Scognet "read %zd bytes, expected %ju bytes, does " 925214921Scognet "metalog size= attribute mismatch source size?", 926214921Scognet (char *)buf, (long long)bufleft, nread, 927214921Scognet (uintmax_t)chunk); 928214921Scognet else 929185222Ssam p = fbuf; 930185222Ssam offset = DIP(din, size) - bufleft; 931185222Ssam if (debug & DEBUG_FS_WRITE_FILE_BLOCK) 932185222Ssam printf( 933185222Ssam "ffs_write_file: write %p offset %lld size %lld left %lld\n", 934185222Ssam p, (long long)offset, 935185222Ssam (long long)chunk, (long long)bufleft); 936185222Ssam /* 937185222Ssam * XXX if holey support is desired, do the check here 938185222Ssam * 939185222Ssam * XXX might need to write out last bit in fragroundup 940185222Ssam * sized chunk. however, ffs_balloc() handles this for us 941185222Ssam */ 942185222Ssam errno = ffs_balloc(&in, offset, chunk, &bp); 943185222Ssam bad_ffs_write_file: 944185222Ssam if (errno != 0) 945185222Ssam err(1, 946185222Ssam "Writing inode %d (%s), bytes %lld + %lld", 947185222Ssam ino, 948185222Ssam isfile ? (char *)buf : 949185222Ssam inode_type(DIP(din, mode) & S_IFMT), 950185222Ssam (long long)offset, (long long)chunk); 951185222Ssam memcpy(bp->b_data, p, chunk); 952185222Ssam errno = bwrite(bp); 953185222Ssam if (errno != 0) 954185222Ssam goto bad_ffs_write_file; 955185222Ssam brelse(bp); 956185222Ssam if (!isfile) 957185222Ssam p += chunk; 958185222Ssam } 959185222Ssam 960185222Ssam write_inode_and_leave: 961185222Ssam ffs_write_inode(&in.i_din, in.i_number, fsopts); 962185222Ssam 963185222Ssam leave_ffs_write_file: 964185222Ssam if (fbuf) 965185222Ssam free(fbuf); 966185222Ssam if (ffd != -1) 967185222Ssam close(ffd); 968185222Ssam} 969185222Ssam 970185222Ssam 971185222Ssamstatic void 972185222Ssamffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap) 973185222Ssam{ 974185222Ssam doff_t i; 975185222Ssam struct direct *de; 976185222Ssam uint16_t reclen; 977185222Ssam 978185222Ssam assert (dbuf != NULL); 979185222Ssam assert (dir != NULL); 980185222Ssam printf("ffs_dump_dirbuf: dir %s size %d cur %d\n", 981185222Ssam dir, dbuf->size, dbuf->cur); 982185222Ssam 983185222Ssam for (i = 0; i < dbuf->size; ) { 984185222Ssam de = (struct direct *)(dbuf->buf + i); 985185222Ssam reclen = ufs_rw16(de->d_reclen, needswap); 986185222Ssam printf( 987185222Ssam " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n", 988185222Ssam ufs_rw32(de->d_ino, needswap), 989185222Ssam inode_type(DTTOIF(de->d_type)), i, reclen, 990185222Ssam de->d_namlen, de->d_name); 991185222Ssam i += reclen; 992185222Ssam assert(reclen > 0); 993185222Ssam } 994185222Ssam} 995185222Ssam 996185222Ssamstatic void 997185222Ssamffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap) 998185222Ssam{ 999185222Ssam struct direct de, *dp; 1000185222Ssam uint16_t llen, reclen; 1001214921Scognet u_char *newbuf; 1002185222Ssam 1003185222Ssam assert (dbuf != NULL); 1004185222Ssam assert (name != NULL); 1005185222Ssam assert (node != NULL); 1006185222Ssam /* create direct entry */ 1007185222Ssam (void)memset(&de, 0, sizeof(de)); 1008185222Ssam de.d_ino = ufs_rw32(node->inode->ino, needswap); 1009185222Ssam de.d_type = IFTODT(node->type); 1010185222Ssam de.d_namlen = (uint8_t)strlen(name); 1011185222Ssam strcpy(de.d_name, name); 1012186261Ssam reclen = DIRSIZ_SWAP(0, &de, needswap); 1013185222Ssam de.d_reclen = ufs_rw16(reclen, needswap); 1014185222Ssam 1015185222Ssam dp = (struct direct *)(dbuf->buf + dbuf->cur); 1016185222Ssam llen = 0; 1017185222Ssam if (dp != NULL) 1018186261Ssam llen = DIRSIZ_SWAP(0, dp, needswap); 1019185222Ssam 1020185222Ssam if (debug & DEBUG_FS_MAKE_DIRBUF) 1021185222Ssam printf( 1022185222Ssam "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n" 1023185222Ssam " ino %d type %d reclen %d namlen %d name %.30s\n", 1024185222Ssam dbuf->size, dbuf->cur, llen, 1025185222Ssam ufs_rw32(de.d_ino, needswap), de.d_type, reclen, 1026185222Ssam de.d_namlen, de.d_name); 1027185222Ssam 1028185222Ssam if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) { 1029185222Ssam if (debug & DEBUG_FS_MAKE_DIRBUF) 1030185222Ssam printf("ffs_make_dirbuf: growing buf to %d\n", 1031185222Ssam dbuf->size + DIRBLKSIZ); 1032185222Ssam if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL) 1033185222Ssam err(1, "Allocating memory for directory buffer"); 1034185222Ssam dbuf->buf = newbuf; 1035185222Ssam dbuf->size += DIRBLKSIZ; 1036185222Ssam memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ); 1037185222Ssam dbuf->cur = dbuf->size - DIRBLKSIZ; 1038214921Scognet } else if (dp) { /* shrink end of previous */ 1039185222Ssam dp->d_reclen = ufs_rw16(llen,needswap); 1040185222Ssam dbuf->cur += llen; 1041185222Ssam } 1042185222Ssam dp = (struct direct *)(dbuf->buf + dbuf->cur); 1043185222Ssam memcpy(dp, &de, reclen); 1044185222Ssam dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap); 1045185222Ssam} 1046185222Ssam 1047185222Ssam/* 1048185222Ssam * cribbed from sys/ufs/ffs/ffs_alloc.c 1049185222Ssam */ 1050185222Ssamstatic void 1051185222Ssamffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts) 1052185222Ssam{ 1053185222Ssam char *buf; 1054185222Ssam struct ufs1_dinode *dp1; 1055185222Ssam struct ufs2_dinode *dp2, *dip; 1056185222Ssam struct cg *cgp; 1057185222Ssam struct fs *fs; 1058185222Ssam int cg, cgino, i; 1059185222Ssam daddr_t d; 1060185222Ssam char sbbuf[FFS_MAXBSIZE]; 1061185222Ssam int32_t initediblk; 1062214921Scognet ffs_opt_t *ffs_opts = fsopts->fs_specific; 1063185222Ssam 1064185222Ssam assert (dp != NULL); 1065185222Ssam assert (ino > 0); 1066185222Ssam assert (fsopts != NULL); 1067214921Scognet assert (ffs_opts != NULL); 1068185222Ssam 1069185222Ssam fs = (struct fs *)fsopts->superblock; 1070185222Ssam cg = ino_to_cg(fs, ino); 1071185222Ssam cgino = ino % fs->fs_ipg; 1072185222Ssam if (debug & DEBUG_FS_WRITE_INODE) 1073185222Ssam printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n", 1074185222Ssam dp, ino, cg, cgino); 1075185222Ssam 1076185222Ssam ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, 1077185222Ssam fsopts); 1078185222Ssam cgp = (struct cg *)sbbuf; 1079186261Ssam if (!cg_chkmagic_swap(cgp, fsopts->needswap)) 1080185222Ssam errx(1, "ffs_write_inode: cg %d: bad magic number", cg); 1081185222Ssam 1082186261Ssam assert (isclr(cg_inosused_swap(cgp, fsopts->needswap), cgino)); 1083185222Ssam 1084185222Ssam buf = malloc(fs->fs_bsize); 1085185222Ssam if (buf == NULL) 1086185222Ssam errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg); 1087185222Ssam 1088185222Ssam dp1 = (struct ufs1_dinode *)buf; 1089185222Ssam dp2 = (struct ufs2_dinode *)buf; 1090185222Ssam 1091185222Ssam if (fs->fs_cstotal.cs_nifree == 0) 1092185222Ssam errx(1, "ffs_write_inode: fs out of inodes for ino %u", 1093185222Ssam ino); 1094185222Ssam if (fs->fs_cs(fs, cg).cs_nifree == 0) 1095185222Ssam errx(1, 1096185222Ssam "ffs_write_inode: cg %d out of inodes for ino %u", 1097185222Ssam cg, ino); 1098186261Ssam setbit(cg_inosused_swap(cgp, fsopts->needswap), cgino); 1099185222Ssam ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap); 1100185222Ssam fs->fs_cstotal.cs_nifree--; 1101185222Ssam fs->fs_cs(fs, cg).cs_nifree--; 1102185222Ssam if (S_ISDIR(DIP(dp, mode))) { 1103185222Ssam ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap); 1104185222Ssam fs->fs_cstotal.cs_ndir++; 1105185222Ssam fs->fs_cs(fs, cg).cs_ndir++; 1106185222Ssam } 1107185222Ssam 1108185222Ssam /* 1109185222Ssam * Initialize inode blocks on the fly for UFS2. 1110185222Ssam */ 1111185222Ssam initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap); 1112214921Scognet if (ffs_opts->version == 2 && cgino + INOPB(fs) > initediblk && 1113185222Ssam initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) { 1114185222Ssam memset(buf, 0, fs->fs_bsize); 1115185222Ssam dip = (struct ufs2_dinode *)buf; 1116185222Ssam srandom(time(NULL)); 1117185222Ssam for (i = 0; i < INOPB(fs); i++) { 1118185222Ssam dip->di_gen = random() / 2 + 1; 1119185222Ssam dip++; 1120185222Ssam } 1121185222Ssam ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs, 1122185222Ssam cg * fs->fs_ipg + initediblk)), 1123185222Ssam fs->fs_bsize, buf, fsopts); 1124185222Ssam initediblk += INOPB(fs); 1125185222Ssam cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap); 1126185222Ssam } 1127185222Ssam 1128185222Ssam 1129185222Ssam ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf, 1130185222Ssam fsopts); 1131185222Ssam 1132185222Ssam /* now write inode */ 1133185222Ssam d = fsbtodb(fs, ino_to_fsba(fs, ino)); 1134185222Ssam ffs_rdfs(d, fs->fs_bsize, buf, fsopts); 1135185222Ssam if (fsopts->needswap) { 1136214921Scognet if (ffs_opts->version == 1) 1137185222Ssam ffs_dinode1_swap(&dp->ffs1_din, 1138185222Ssam &dp1[ino_to_fsbo(fs, ino)]); 1139185222Ssam else 1140185222Ssam ffs_dinode2_swap(&dp->ffs2_din, 1141185222Ssam &dp2[ino_to_fsbo(fs, ino)]); 1142185222Ssam } else { 1143214921Scognet if (ffs_opts->version == 1) 1144185222Ssam dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din; 1145185222Ssam else 1146185222Ssam dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din; 1147185222Ssam } 1148185222Ssam ffs_wtfs(d, fs->fs_bsize, buf, fsopts); 1149185222Ssam free(buf); 1150185222Ssam} 1151185222Ssam 1152185222Ssamvoid 1153185222Ssampanic(const char *fmt, ...) 1154185222Ssam{ 1155185222Ssam va_list ap; 1156185222Ssam 1157185222Ssam va_start(ap, fmt); 1158185222Ssam vwarnx(fmt, ap); 1159185222Ssam va_end(ap); 1160185222Ssam exit(1); 1161185222Ssam} 1162