makefs - create a file system image from a directory tree.
It doesn't need any special privileges or kernel devices. Only ffs image creation is supported at this time, although makefs has been designed to allow the addition of other file system formats by writing new back-ends. This program was designed & implemented by Luke Mewburn of Wasabi Systems.
This commit is contained in:
parent
99147a7648
commit
6325773eff
926
usr.sbin/makefs/ffs.c
Normal file
926
usr.sbin/makefs/ffs.c
Normal file
@ -0,0 +1,926 @@
|
|||||||
|
/* $NetBSD: ffs.c,v 1.1.1.1 2001/10/26 06:19:00 lukem Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2001 Wasabi Systems, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Written by Luke Mewburn for Wasabi Systems, Inc.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed for the NetBSD Project by
|
||||||
|
* Wasabi Systems, Inc.
|
||||||
|
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
|
||||||
|
* or promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1982, 1986, 1989, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/mount.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "makefs.h"
|
||||||
|
|
||||||
|
#include <ufs/ffs/fs.h>
|
||||||
|
#include <ufs/ufs/dir.h>
|
||||||
|
#include <ufs/ufs/dinode.h>
|
||||||
|
#include <ufs/ufs/inode.h>
|
||||||
|
#include <ufs/ufs/ufs_bswap.h>
|
||||||
|
|
||||||
|
#include "ffs/newfs_extern.h"
|
||||||
|
#include "ffs/ffs_extern.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Various file system defaults (cribbed from newfs(8)).
|
||||||
|
*/
|
||||||
|
#define DFL_FRAGSIZE 1024 /* fragment size */
|
||||||
|
#define DFL_BLKSIZE 8192 /* block size */
|
||||||
|
#define DFL_SECSIZE 512 /* sector size */
|
||||||
|
#define DFL_CYLSPERGROUP 16 /* cylinders per group */
|
||||||
|
#define DFL_FRAGSPERINODE 4 /* fragments per inode - XXX */
|
||||||
|
#define DFL_ROTDELAY 0 /* rotational delay */
|
||||||
|
#define DFL_NRPOS 1 /* rotational positions */
|
||||||
|
#define DFL_RPM 3600 /* rpm of disk */
|
||||||
|
#define DFL_NSECTORS 64 /* # of sectors */
|
||||||
|
#define DFL_NTRACKS 16 /* # of tracks */
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u_char *buf; /* buf for directory */
|
||||||
|
doff_t size; /* full size of buf */
|
||||||
|
doff_t cur; /* offset of current entry */
|
||||||
|
} dirbuf_t;
|
||||||
|
|
||||||
|
|
||||||
|
static int ffs_create_image(const char *, fsinfo_t *);
|
||||||
|
static void ffs_dump_fsinfo(fsinfo_t *);
|
||||||
|
static void ffs_dump_dirbuf(dirbuf_t *, const char *, int);
|
||||||
|
static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int);
|
||||||
|
static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
|
||||||
|
static void ffs_size_dir(fsnode *, fsinfo_t *);
|
||||||
|
static void ffs_validate(const char *, fsnode *, fsinfo_t *);
|
||||||
|
static void ffs_write_file(struct dinode *, uint32_t, void *, fsinfo_t *);
|
||||||
|
static void ffs_write_inode(struct dinode *, uint32_t, const fsinfo_t *);
|
||||||
|
|
||||||
|
|
||||||
|
int sectorsize; /* XXX: for buf.c::getblk() */
|
||||||
|
|
||||||
|
/* publically visible functions */
|
||||||
|
|
||||||
|
int
|
||||||
|
ffs_parse_opts(const char *option, fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
option_t ffs_options[] = {
|
||||||
|
{ "bsize", &fsopts->bsize, 1, INT_MAX,
|
||||||
|
"block size" },
|
||||||
|
{ "fsize", &fsopts->fsize, 1, INT_MAX,
|
||||||
|
"fragment size" },
|
||||||
|
{ "cpg", &fsopts->cpg, 1, INT_MAX,
|
||||||
|
"cylinders per group" },
|
||||||
|
{ "density", &fsopts->density, 1, INT_MAX,
|
||||||
|
"bytes per inode" },
|
||||||
|
{ "ntracks", &fsopts->ntracks, 1, INT_MAX,
|
||||||
|
"number of tracks" },
|
||||||
|
{ "nsectors", &fsopts->nsectors, 1, INT_MAX,
|
||||||
|
"number of sectors" },
|
||||||
|
{ "rpm", &fsopts->rpm, 1, INT_MAX,
|
||||||
|
"revolutions per minute" },
|
||||||
|
{ "minfree", &fsopts->minfree, 0, 99,
|
||||||
|
"minfree" },
|
||||||
|
{ "rotdelay", &fsopts->rotdelay, 0, INT_MAX,
|
||||||
|
"rotational delay" },
|
||||||
|
{ "maxbpg", &fsopts->maxbpg, 1, INT_MAX,
|
||||||
|
"max blocks per cylgroup" },
|
||||||
|
{ "nrpos", &fsopts->nrpos, 1, INT_MAX,
|
||||||
|
"number of rotational positions" },
|
||||||
|
{ "avgfilesize", &fsopts->avgfilesize, 1, INT_MAX,
|
||||||
|
"expected average file size" },
|
||||||
|
{ "avgfpdir", &fsopts->avgfpdir, 1, INT_MAX,
|
||||||
|
"expected # of files per directory" },
|
||||||
|
{ NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
char *var, *val;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
(void)&ffs_options;
|
||||||
|
assert(option != NULL);
|
||||||
|
assert(fsopts != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_PARSE_OPTS)
|
||||||
|
printf("ffs_parse_opts: got `%s'\n", option);
|
||||||
|
|
||||||
|
if ((var = strdup(option)) == NULL)
|
||||||
|
err(1, "Allocating memory for copy of option string");
|
||||||
|
rv = 0;
|
||||||
|
|
||||||
|
if ((val = strchr(var, '=')) == NULL) {
|
||||||
|
warnx("Option `%s' doesn't contain a value", var);
|
||||||
|
goto leave_ffs_parse_opts;
|
||||||
|
}
|
||||||
|
*val++ = '\0';
|
||||||
|
|
||||||
|
if (strcmp(var, "optimization") == 0) {
|
||||||
|
if (strcmp(val, "time") == 0) {
|
||||||
|
fsopts->optimization = FS_OPTTIME;
|
||||||
|
} else if (strcmp(val, "space") == 0) {
|
||||||
|
fsopts->optimization = FS_OPTSPACE;
|
||||||
|
} else {
|
||||||
|
warnx("Invalid optimization `%s'", val);
|
||||||
|
goto leave_ffs_parse_opts;
|
||||||
|
}
|
||||||
|
rv = 1;
|
||||||
|
} else
|
||||||
|
rv = set_option(ffs_options, var, val);
|
||||||
|
|
||||||
|
leave_ffs_parse_opts:
|
||||||
|
if (var)
|
||||||
|
free(var);
|
||||||
|
return (rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
struct timeval start;
|
||||||
|
|
||||||
|
assert(image != NULL);
|
||||||
|
assert(dir != NULL);
|
||||||
|
assert(root != NULL);
|
||||||
|
assert(fsopts != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_MAKEFS)
|
||||||
|
printf("ffs_makefs: image %s directory %s root %p\n",
|
||||||
|
image, dir, root);
|
||||||
|
|
||||||
|
/* validate tree and options */
|
||||||
|
TIMER_START(start);
|
||||||
|
ffs_validate(dir, root, fsopts);
|
||||||
|
TIMER_RESULTS(start, "ffs_validate");
|
||||||
|
|
||||||
|
/* create image */
|
||||||
|
TIMER_START(start);
|
||||||
|
if (ffs_create_image(image, fsopts) == -1)
|
||||||
|
errx(1, "Image file `%s' not created.", image);
|
||||||
|
TIMER_RESULTS(start, "ffs_create_image");
|
||||||
|
|
||||||
|
fsopts->curinode = ROOTINO;
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_MAKEFS)
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
/* populate image */
|
||||||
|
TIMER_START(start);
|
||||||
|
if (! ffs_populate_dir(dir, root, fsopts))
|
||||||
|
errx(1, "Image file `%s' not populated.", image);
|
||||||
|
TIMER_RESULTS(start, "ffs_populate_dir");
|
||||||
|
|
||||||
|
/* ensure no outstanding buffers remain */
|
||||||
|
if (debug & DEBUG_FS_MAKEFS)
|
||||||
|
bcleanup();
|
||||||
|
|
||||||
|
/* write out superblock; image is now complete */
|
||||||
|
ffs_write_superblock(fsopts->superblock, fsopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end of public functions */
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
int32_t spc, nspf, ncyl, ncg, fssize;
|
||||||
|
|
||||||
|
assert(dir != NULL);
|
||||||
|
assert(root != NULL);
|
||||||
|
assert(fsopts != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_VALIDATE) {
|
||||||
|
printf("ffs_validate: before defaults set:\n");
|
||||||
|
ffs_dump_fsinfo(fsopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* set FFS defaults */
|
||||||
|
if (fsopts->sectorsize == 0)
|
||||||
|
fsopts->sectorsize = DFL_SECSIZE;
|
||||||
|
if (fsopts->fsize == 0)
|
||||||
|
fsopts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize);
|
||||||
|
if (fsopts->bsize == 0)
|
||||||
|
fsopts->bsize = MIN(DFL_BLKSIZE, 8 * fsopts->fsize);
|
||||||
|
if (fsopts->cpg == 0)
|
||||||
|
fsopts->cpg = DFL_CYLSPERGROUP;
|
||||||
|
/* fsopts->density is set below */
|
||||||
|
if (fsopts->ntracks == 0)
|
||||||
|
fsopts->ntracks = DFL_NTRACKS;
|
||||||
|
if (fsopts->nsectors == 0)
|
||||||
|
fsopts->nsectors = DFL_NSECTORS;
|
||||||
|
if (fsopts->rpm == 0)
|
||||||
|
fsopts->rpm = DFL_RPM;
|
||||||
|
if (fsopts->minfree == 0)
|
||||||
|
fsopts->minfree = MINFREE;
|
||||||
|
if (fsopts->optimization == 0)
|
||||||
|
fsopts->optimization = DEFAULTOPT;
|
||||||
|
if (fsopts->maxcontig == 0)
|
||||||
|
fsopts->maxcontig =
|
||||||
|
MAX(1, MIN(MAXPHYS, MAXBSIZE) / fsopts->bsize);
|
||||||
|
if (fsopts->rotdelay == 0)
|
||||||
|
fsopts->rotdelay = DFL_ROTDELAY;
|
||||||
|
if (fsopts->maxbpg == 0)
|
||||||
|
fsopts->maxbpg = fsopts->bsize / sizeof(ufs_daddr_t);
|
||||||
|
if (fsopts->nrpos == 0)
|
||||||
|
fsopts->nrpos = DFL_NRPOS;
|
||||||
|
if (fsopts->avgfilesize == 0)
|
||||||
|
fsopts->avgfilesize = AVFILESIZ;
|
||||||
|
if (fsopts->avgfpdir == 0)
|
||||||
|
fsopts->avgfpdir = AFPDIR;
|
||||||
|
|
||||||
|
/* calculate size of tree */
|
||||||
|
ffs_size_dir(root, fsopts);
|
||||||
|
fsopts->inodes += ROOTINO; /* include first two inodes */
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_VALIDATE)
|
||||||
|
printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n",
|
||||||
|
(long long)fsopts->size, (long long)fsopts->inodes);
|
||||||
|
|
||||||
|
/* add requested slop */
|
||||||
|
fsopts->size += fsopts->freeblocks;
|
||||||
|
fsopts->inodes += fsopts->freefiles;
|
||||||
|
if (fsopts->freefilepc > 0)
|
||||||
|
fsopts->inodes =
|
||||||
|
fsopts->inodes * (100 + fsopts->freefilepc) / 100;
|
||||||
|
if (fsopts->freeblockpc > 0)
|
||||||
|
fsopts->size =
|
||||||
|
fsopts->size * (100 + fsopts->freeblockpc) / 100;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* estimate number of cylinder groups
|
||||||
|
*/
|
||||||
|
spc = fsopts->nsectors * fsopts->ntracks;
|
||||||
|
nspf = fsopts->fsize / fsopts->sectorsize;
|
||||||
|
fssize = fsopts->size / fsopts->sectorsize;
|
||||||
|
ncyl = fssize * nspf / spc;
|
||||||
|
if (fssize * nspf > ncyl * spc)
|
||||||
|
ncyl++;
|
||||||
|
ncg = ncyl / fsopts->cpg;
|
||||||
|
if (ncg == 0)
|
||||||
|
ncg = 1;
|
||||||
|
if (debug & DEBUG_FS_VALIDATE)
|
||||||
|
printf(
|
||||||
|
"ffs_validate: spc %d nspf %d fssize %d ncyl %d ncg %d\n",
|
||||||
|
spc, nspf, fssize, ncyl, ncg);
|
||||||
|
|
||||||
|
/* add space needed for superblocks */
|
||||||
|
fsopts->size += (SBOFF + SBSIZE) * ncg;
|
||||||
|
/* add space needed to store inodes, x3 for blockmaps, etc */
|
||||||
|
fsopts->size += ncg * DINODE_SIZE * 3 *
|
||||||
|
roundup(fsopts->inodes / ncg, fsopts->bsize / DINODE_SIZE);
|
||||||
|
/* add minfree */
|
||||||
|
if (fsopts->minfree > 0)
|
||||||
|
fsopts->size =
|
||||||
|
fsopts->size * (100 + fsopts->minfree) / 100;
|
||||||
|
/*
|
||||||
|
* XXX any other fs slop to add, such as csum's, etc ??
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */
|
||||||
|
fsopts->size = fsopts->minsize;
|
||||||
|
|
||||||
|
/* round up to the next sector */
|
||||||
|
fsopts->size = roundup(fsopts->size, fsopts->sectorsize);
|
||||||
|
|
||||||
|
/* calculate density if necessary */
|
||||||
|
if (fsopts->density == 0)
|
||||||
|
fsopts->density = fsopts->size / fsopts->inodes + 1;
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_VALIDATE) {
|
||||||
|
printf("ffs_validate: after defaults set:\n");
|
||||||
|
ffs_dump_fsinfo(fsopts);
|
||||||
|
printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n",
|
||||||
|
dir, (long long)fsopts->size, (long long)fsopts->inodes);
|
||||||
|
}
|
||||||
|
sectorsize = fsopts->sectorsize; /* XXX - see earlier */
|
||||||
|
|
||||||
|
/* now check calculated sizes vs requested sizes */
|
||||||
|
if (fsopts->maxsize > 0 && fsopts->size > fsopts->maxsize) {
|
||||||
|
errx(1, "`%s' size of %lld is larger than the maxsize of %lld.",
|
||||||
|
dir, (long long)fsopts->size, (long long)fsopts->maxsize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ffs_dump_fsinfo(fsinfo_t *f)
|
||||||
|
{
|
||||||
|
|
||||||
|
printf("fsopts at %p\n", f);
|
||||||
|
|
||||||
|
printf("\tsize %lld, inodes %lld, curinode %u\n",
|
||||||
|
(long long)f->size, (long long)f->inodes, f->curinode);
|
||||||
|
|
||||||
|
printf("\tminsize %lld, maxsize %lld\n",
|
||||||
|
(long long)f->minsize, (long long)f->maxsize);
|
||||||
|
printf("\tfree files %lld, freefile %% %d\n",
|
||||||
|
(long long)f->freefiles, f->freefilepc);
|
||||||
|
printf("\tfree blocks %lld, freeblock %% %d\n",
|
||||||
|
(long long)f->freeblocks, f->freeblockpc);
|
||||||
|
printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize);
|
||||||
|
|
||||||
|
printf("\tbsize %d, fsize %d, cpg %d, density %d\n",
|
||||||
|
f->bsize, f->fsize, f->cpg, f->density);
|
||||||
|
printf("\tntracks %d, nsectors %d, rpm %d, minfree %d\n",
|
||||||
|
f->ntracks, f->nsectors, f->rpm, f->minfree);
|
||||||
|
printf("\tmaxcontig %d, rotdelay %d, maxbpg %d, nrpos %d\n",
|
||||||
|
f->maxcontig, f->rotdelay, f->maxbpg, f->nrpos);
|
||||||
|
printf("\toptimization %s\n",
|
||||||
|
f->optimization == FS_OPTSPACE ? "space" : "time");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ffs_create_image(const char *image, fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
struct statfs sfs;
|
||||||
|
struct fs *fs;
|
||||||
|
char *buf;
|
||||||
|
int i, bufsize;
|
||||||
|
off_t bufrem;
|
||||||
|
|
||||||
|
assert (image != NULL);
|
||||||
|
assert (fsopts != NULL);
|
||||||
|
|
||||||
|
/* create image */
|
||||||
|
if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0777))
|
||||||
|
== -1) {
|
||||||
|
warn("Can't open `%s' for writing", image);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* zero image */
|
||||||
|
if (fstatfs(fsopts->fd, &sfs) == -1) {
|
||||||
|
bufsize = 8192;
|
||||||
|
warn("can't fstatfs `%s', using default %d byte chunk",
|
||||||
|
image, bufsize);
|
||||||
|
} else
|
||||||
|
bufsize = sfs.f_iosize;
|
||||||
|
bufrem = fsopts->size;
|
||||||
|
if (debug & DEBUG_FS_CREATE_IMAGE)
|
||||||
|
printf(
|
||||||
|
"zero-ing image `%s', %lld sectors, using %d byte chunks\n",
|
||||||
|
image, (long long)bufrem, bufsize);
|
||||||
|
if ((buf = calloc(1, bufsize)) == NULL) {
|
||||||
|
warn("Can't create buffer for sector");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
while (bufrem > 0) {
|
||||||
|
i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
|
||||||
|
if (i == -1) {
|
||||||
|
warn("zeroing image, %lld bytes to go",
|
||||||
|
(long long)bufrem);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
bufrem -= i;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make the file system */
|
||||||
|
if (debug & DEBUG_FS_CREATE_IMAGE)
|
||||||
|
printf("calling mkfs(\"%s\", ...)\n", image);
|
||||||
|
fs = ffs_mkfs(image, fsopts);
|
||||||
|
fsopts->superblock = (void *)fs;
|
||||||
|
if (debug & DEBUG_FS_CREATE_IMAGE) {
|
||||||
|
time_t t;
|
||||||
|
|
||||||
|
t = ((struct fs *)fsopts->superblock)->fs_time;
|
||||||
|
printf("mkfs returned %p; fs_time %s",
|
||||||
|
fsopts->superblock, ctime(&t));
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((long long)fs->fs_ncg * fs->fs_ipg < fsopts->inodes) {
|
||||||
|
warnx(
|
||||||
|
"Image file `%s' only has %lld inodes, but %lld are required.",
|
||||||
|
image,
|
||||||
|
(long long)fs->fs_ncg * fs->fs_ipg,
|
||||||
|
(long long)fsopts->inodes);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
return (fsopts->fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
struct direct tmpdir;
|
||||||
|
fsnode * node;
|
||||||
|
int curdirsize, this;
|
||||||
|
|
||||||
|
/* node may be NULL (empty directory) */
|
||||||
|
assert(fsopts != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_SIZE_DIR)
|
||||||
|
printf("ffs_size_dir: entry: bytes %lld inodes %lld\n",
|
||||||
|
(long long)fsopts->size, (long long)fsopts->inodes);
|
||||||
|
|
||||||
|
#define ADDDIRENT(e) do { \
|
||||||
|
tmpdir.d_namlen = strlen((e)); \
|
||||||
|
this = DIRSIZ(0, &tmpdir, 0); \
|
||||||
|
if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \
|
||||||
|
printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \
|
||||||
|
e, tmpdir.d_namlen, this, curdirsize); \
|
||||||
|
if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \
|
||||||
|
curdirsize = roundup(curdirsize, DIRBLKSIZ); \
|
||||||
|
curdirsize += this; \
|
||||||
|
if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \
|
||||||
|
printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \
|
||||||
|
e, tmpdir.d_namlen, this, curdirsize); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX this must take into account extra space consumed
|
||||||
|
* by indirect blocks, etc.
|
||||||
|
*/
|
||||||
|
#define ADDSIZE(x) do { \
|
||||||
|
fsopts->size += roundup((x), fsopts->bsize); \
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
curdirsize = 0;
|
||||||
|
for (node = root; node != NULL; node = node->next) {
|
||||||
|
ADDDIRENT(node->name);
|
||||||
|
if (node == root) { /* we're at "." */
|
||||||
|
assert(strcmp(node->name, ".") == 0);
|
||||||
|
ADDDIRENT("..");
|
||||||
|
}
|
||||||
|
if (node->dup == NULL) { /* don't count duplicate names */
|
||||||
|
if (debug & DEBUG_FS_SIZE_DIR_NODE)
|
||||||
|
printf("ffs_size_dir: %s size %lld\n",
|
||||||
|
node->name,
|
||||||
|
(long long)node->statbuf.st_size);
|
||||||
|
fsopts->inodes++;
|
||||||
|
if (node->type == S_IFREG)
|
||||||
|
ADDSIZE(node->statbuf.st_size);
|
||||||
|
if (node->type == S_IFLNK) {
|
||||||
|
int slen;
|
||||||
|
|
||||||
|
slen = strlen(node->symlink) + 1;
|
||||||
|
if (slen >= MAXSYMLINKLEN)
|
||||||
|
ADDSIZE(slen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (node->type == S_IFDIR)
|
||||||
|
ffs_size_dir(node->child, fsopts);
|
||||||
|
}
|
||||||
|
ADDSIZE(curdirsize);
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_SIZE_DIR)
|
||||||
|
printf("ffs_size_dir: exit: size %lld inodes %lld\n",
|
||||||
|
(long long)fsopts->size, (long long)fsopts->inodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
fsnode *cur;
|
||||||
|
dirbuf_t dirbuf;
|
||||||
|
struct dinode din;
|
||||||
|
void *membuf;
|
||||||
|
char path[MAXPATHLEN + 1];
|
||||||
|
|
||||||
|
assert(dir != NULL);
|
||||||
|
assert(root != NULL);
|
||||||
|
assert(fsopts != NULL);
|
||||||
|
|
||||||
|
(void)memset(&dirbuf, 0, sizeof(dirbuf));
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_POPULATE)
|
||||||
|
printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pass 1: allocate inode numbers, build directory `file'
|
||||||
|
*/
|
||||||
|
for (cur = root; cur != NULL; cur = cur->next) {
|
||||||
|
if (cur->dup == NULL) {
|
||||||
|
if (cur == root && cur->parent)
|
||||||
|
cur->ino = cur->parent->ino;
|
||||||
|
else {
|
||||||
|
cur->ino = fsopts->curinode;
|
||||||
|
fsopts->curinode++;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
cur->ino = cur->dup->ino;
|
||||||
|
ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap);
|
||||||
|
if (cur == root) { /* we're at "."; add ".." */
|
||||||
|
ffs_make_dirbuf(&dirbuf, "..",
|
||||||
|
cur->parent == NULL ? cur : cur->parent->first,
|
||||||
|
fsopts->needswap);
|
||||||
|
root->nlink++; /* count my parent's link */
|
||||||
|
} else if (cur->child != NULL)
|
||||||
|
root->nlink++; /* count my child's link */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX possibly write file and long symlinks here,
|
||||||
|
* ensuring that blocks get written before inodes?
|
||||||
|
* otoh, this isn't a real filesystem, so who
|
||||||
|
* cares about ordering? :-)
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
if (debug & DEBUG_FS_POPULATE_DIRBUF)
|
||||||
|
ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pass 2: write out dirbuf, then non-directories at this level
|
||||||
|
*/
|
||||||
|
if (debug & DEBUG_FS_POPULATE)
|
||||||
|
printf("ffs_populate_dir: PASS 2 dir %s\n", dir);
|
||||||
|
for (cur = root; cur != NULL; cur = cur->next) {
|
||||||
|
if (cur->dup != NULL)
|
||||||
|
continue; /* skip hard-linked entries */
|
||||||
|
|
||||||
|
if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
|
||||||
|
>= sizeof(path))
|
||||||
|
errx(1, "Pathname too long.");
|
||||||
|
|
||||||
|
if (cur->child != NULL)
|
||||||
|
continue; /* child creates own inode */
|
||||||
|
|
||||||
|
/* build on-disk inode */
|
||||||
|
memset(&din, 0, sizeof(din));
|
||||||
|
din.di_mode = cur->statbuf.st_mode;
|
||||||
|
din.di_nlink = cur->nlink;
|
||||||
|
din.di_size = cur->statbuf.st_size;
|
||||||
|
din.di_atime = cur->statbuf.st_atime;
|
||||||
|
din.di_atimensec = cur->statbuf.st_atimensec;
|
||||||
|
din.di_mtime = cur->statbuf.st_mtime;
|
||||||
|
din.di_mtimensec = cur->statbuf.st_mtimensec;
|
||||||
|
din.di_ctime = cur->statbuf.st_ctime;
|
||||||
|
din.di_ctimensec = cur->statbuf.st_ctimensec;
|
||||||
|
din.di_flags = cur->statbuf.st_flags;
|
||||||
|
din.di_gen = cur->statbuf.st_gen;
|
||||||
|
din.di_uid = cur->statbuf.st_uid;
|
||||||
|
din.di_gid = cur->statbuf.st_gid;
|
||||||
|
/* not set: di_db, di_ib, di_blocks, di_spare */
|
||||||
|
|
||||||
|
membuf = NULL;
|
||||||
|
if (cur == root) { /* "."; write dirbuf */
|
||||||
|
membuf = dirbuf.buf;
|
||||||
|
din.di_size = dirbuf.size;
|
||||||
|
} else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
|
||||||
|
din.di_size = 0; /* a device */
|
||||||
|
din.di_rdev =
|
||||||
|
ufs_rw32(cur->statbuf.st_rdev, fsopts->needswap);
|
||||||
|
} else if (S_ISLNK(cur->type)) { /* symlink */
|
||||||
|
int slen;
|
||||||
|
|
||||||
|
slen = strlen(cur->symlink);
|
||||||
|
if (slen < MAXSYMLINKLEN) { /* short link */
|
||||||
|
memcpy(din.di_shortlink, cur->symlink, slen);
|
||||||
|
} else
|
||||||
|
membuf = cur->symlink;
|
||||||
|
din.di_size = slen;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_POPULATE_NODE) {
|
||||||
|
printf("ffs_populate_dir: writing ino %d, %s",
|
||||||
|
cur->ino, inode_type(cur->type));
|
||||||
|
if (cur->nlink > 1)
|
||||||
|
printf(", nlink %d", cur->nlink);
|
||||||
|
putchar('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (membuf != NULL) {
|
||||||
|
ffs_write_file(&din, cur->ino, membuf, fsopts);
|
||||||
|
} else if (S_ISREG(cur->type)) {
|
||||||
|
ffs_write_file(&din, cur->ino, path, fsopts);
|
||||||
|
} else {
|
||||||
|
assert (! S_ISDIR(cur->type));
|
||||||
|
ffs_write_inode(&din, cur->ino, fsopts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pass 3: write out sub-directories
|
||||||
|
*/
|
||||||
|
if (debug & DEBUG_FS_POPULATE)
|
||||||
|
printf("ffs_populate_dir: PASS 3 dir %s\n", dir);
|
||||||
|
for (cur = root; cur != NULL; cur = cur->next) {
|
||||||
|
if (cur->child == NULL)
|
||||||
|
continue;
|
||||||
|
if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
|
||||||
|
>= sizeof(path))
|
||||||
|
errx(1, "Pathname too long.");
|
||||||
|
if (! ffs_populate_dir(path, cur->child, fsopts))
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_POPULATE)
|
||||||
|
printf("ffs_populate_dir: DONE dir %s\n", dir);
|
||||||
|
|
||||||
|
/* cleanup */
|
||||||
|
if (dirbuf.buf != NULL)
|
||||||
|
free(dirbuf.buf);
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ffs_write_file(struct dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
int isfile, ffd;
|
||||||
|
char *fbuf, *p;
|
||||||
|
off_t bufleft, chunk, offset;
|
||||||
|
struct inode in;
|
||||||
|
struct buf * bp;
|
||||||
|
|
||||||
|
assert (din != NULL);
|
||||||
|
assert (buf != NULL);
|
||||||
|
assert (fsopts != NULL);
|
||||||
|
|
||||||
|
isfile = S_ISREG(din->di_mode);
|
||||||
|
fbuf = NULL;
|
||||||
|
ffd = -1;
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_WRITE_FILE) {
|
||||||
|
printf(
|
||||||
|
"ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld",
|
||||||
|
ino, din, isfile, inode_type(din->di_mode & S_IFMT),
|
||||||
|
(long long)din->di_size);
|
||||||
|
if (isfile)
|
||||||
|
printf(", file '%s'\n", (char *)buf);
|
||||||
|
else
|
||||||
|
printf(", buffer %p\n", buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
in.i_number = ino;
|
||||||
|
in.i_fs = (struct fs *)fsopts->superblock;
|
||||||
|
memcpy(&in.i_din.ffs_din, din, sizeof(in.i_din.ffs_din));
|
||||||
|
in.i_fd = fsopts->fd;
|
||||||
|
|
||||||
|
if (din->di_size == 0)
|
||||||
|
goto write_inode_and_leave; /* mmm, cheating */
|
||||||
|
|
||||||
|
if (isfile) {
|
||||||
|
if ((fbuf = malloc(fsopts->bsize)) == NULL)
|
||||||
|
err(1, "Allocating memory for write buffer");
|
||||||
|
if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) {
|
||||||
|
warn("Can't open `%s' for reading", (char *)buf);
|
||||||
|
goto leave_ffs_write_file;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
p = buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
chunk = 0;
|
||||||
|
for (bufleft = din->di_size; bufleft > 0; bufleft -= chunk) {
|
||||||
|
chunk = MIN(bufleft, fsopts->bsize);
|
||||||
|
if (isfile) {
|
||||||
|
if (read(ffd, fbuf, chunk) != chunk)
|
||||||
|
err(1, "reading %s, %lld bytes to go",
|
||||||
|
(char *)buf, (long long)bufleft);
|
||||||
|
p = fbuf;
|
||||||
|
}
|
||||||
|
offset = din->di_size - bufleft;
|
||||||
|
if (debug & DEBUG_FS_WRITE_FILE_BLOCK)
|
||||||
|
printf(
|
||||||
|
"ffs_write_file: write %p offset %lld size %lld left %lld\n",
|
||||||
|
p, (long long)offset,
|
||||||
|
(long long)chunk, (long long)bufleft);
|
||||||
|
/*
|
||||||
|
* XXX if holey support is desired, do the check here
|
||||||
|
*
|
||||||
|
* XXX might need to write out last bit in fragroundup
|
||||||
|
* sized chunk. however, ffs_balloc() handles this for us
|
||||||
|
*/
|
||||||
|
errno = ffs_balloc(&in, offset, chunk, &bp);
|
||||||
|
if (errno != 0)
|
||||||
|
err(1,
|
||||||
|
"ffs_write_file: ffs_balloc %lld %lld",
|
||||||
|
(long long)offset, (long long)chunk);
|
||||||
|
memcpy(bp->b_data, p, chunk);
|
||||||
|
errno = bwrite(bp);
|
||||||
|
if (errno != 0)
|
||||||
|
err(1, "ffs_write_file: bwrite");
|
||||||
|
brelse(bp);
|
||||||
|
if (!isfile)
|
||||||
|
p += chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
write_inode_and_leave:
|
||||||
|
ffs_write_inode(&in.i_din.ffs_din, in.i_number, fsopts);
|
||||||
|
|
||||||
|
leave_ffs_write_file:
|
||||||
|
if (fbuf)
|
||||||
|
free(fbuf);
|
||||||
|
if (ffd != -1)
|
||||||
|
close(ffd);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap)
|
||||||
|
{
|
||||||
|
doff_t i;
|
||||||
|
struct direct *de;
|
||||||
|
u_int16_t reclen;
|
||||||
|
|
||||||
|
assert (dbuf != NULL);
|
||||||
|
assert (dir != NULL);
|
||||||
|
printf("ffs_dump_dirbuf: dir %s size %d cur %d\n",
|
||||||
|
dir, dbuf->size, dbuf->cur);
|
||||||
|
|
||||||
|
for (i = 0; i < dbuf->size; ) {
|
||||||
|
de = (struct direct *)(dbuf->buf + i);
|
||||||
|
reclen = ufs_rw16(de->d_reclen, needswap);
|
||||||
|
printf(
|
||||||
|
" inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n",
|
||||||
|
ufs_rw32(de->d_ino, needswap),
|
||||||
|
inode_type(DTTOIF(de->d_type)), i, reclen,
|
||||||
|
de->d_namlen, de->d_name);
|
||||||
|
i += reclen;
|
||||||
|
assert(reclen > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap)
|
||||||
|
{
|
||||||
|
struct direct de, *dp;
|
||||||
|
u_int16_t llen, reclen;
|
||||||
|
|
||||||
|
assert (dbuf != NULL);
|
||||||
|
assert (name != NULL);
|
||||||
|
assert (node != NULL);
|
||||||
|
/* create direct entry */
|
||||||
|
(void)memset(&de, 0, sizeof(de));
|
||||||
|
de.d_ino = ufs_rw32(node->ino, needswap);
|
||||||
|
de.d_type = IFTODT(node->type);
|
||||||
|
de.d_namlen = (u_int8_t)strlen(name);
|
||||||
|
assert (de.d_namlen < (sizeof(de.d_name)));
|
||||||
|
strcpy(de.d_name, name);
|
||||||
|
reclen = DIRSIZ(0, &de, needswap);
|
||||||
|
de.d_reclen = ufs_rw16(reclen, needswap);
|
||||||
|
|
||||||
|
dp = (struct direct *)(dbuf->buf + dbuf->cur);
|
||||||
|
llen = 0;
|
||||||
|
if (dp != NULL)
|
||||||
|
llen = DIRSIZ(0, dp, needswap);
|
||||||
|
|
||||||
|
if (debug & DEBUG_FS_MAKE_DIRBUF)
|
||||||
|
printf(
|
||||||
|
"ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n"
|
||||||
|
" ino %d type %d reclen %d namlen %d name %.30s\n",
|
||||||
|
dbuf->size, dbuf->cur, llen,
|
||||||
|
ufs_rw32(de.d_ino, needswap), de.d_type, reclen,
|
||||||
|
de.d_namlen, de.d_name);
|
||||||
|
|
||||||
|
if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) {
|
||||||
|
dbuf->size += DIRBLKSIZ; /* need another chunk */
|
||||||
|
if (debug & DEBUG_FS_MAKE_DIRBUF)
|
||||||
|
printf("ffs_make_dirbuf: growing buf to %d\n",
|
||||||
|
dbuf->size);
|
||||||
|
if ((dbuf->buf = realloc(dbuf->buf, dbuf->size)) == NULL)
|
||||||
|
err(1, "Allocating memory for directory buffer");
|
||||||
|
memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ);
|
||||||
|
dbuf->cur = dbuf->size - DIRBLKSIZ;
|
||||||
|
} else { /* shrink end of previous */
|
||||||
|
dp->d_reclen = ufs_rw16(llen,needswap);
|
||||||
|
dbuf->cur += llen;
|
||||||
|
}
|
||||||
|
dp = (struct direct *)(dbuf->buf + dbuf->cur);
|
||||||
|
memcpy(dp, &de, reclen);
|
||||||
|
dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* cribbed from sys/ufs/ffs/ffs_alloc.c
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
ffs_write_inode(struct dinode *ip, uint32_t ino, const fsinfo_t *fsopts)
|
||||||
|
{
|
||||||
|
struct dinode ibuf[MAXINOPB];
|
||||||
|
struct cg *cgp;
|
||||||
|
struct fs *fs;
|
||||||
|
int cg, cgino;
|
||||||
|
daddr_t d;
|
||||||
|
char sbbuf[MAXBSIZE];
|
||||||
|
|
||||||
|
assert (ip != NULL);
|
||||||
|
assert (ino > 0);
|
||||||
|
assert (fsopts != NULL);
|
||||||
|
|
||||||
|
fs = (struct fs *)fsopts->superblock;
|
||||||
|
cg = ino_to_cg(fs, ino);
|
||||||
|
cgino = ino % fs->fs_ipg;
|
||||||
|
if (debug & DEBUG_FS_WRITE_INODE)
|
||||||
|
printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n",
|
||||||
|
ip, ino, cg, cgino);
|
||||||
|
|
||||||
|
ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
|
||||||
|
fsopts);
|
||||||
|
cgp = (struct cg *)sbbuf;
|
||||||
|
if (!cg_chkmagic(cgp, fsopts->needswap))
|
||||||
|
errx(1, "ffs_write_inode: cg %d: bad magic number", cg);
|
||||||
|
|
||||||
|
assert (isclr(cg_inosused(cgp, fsopts->needswap), cgino));
|
||||||
|
|
||||||
|
if (fs->fs_cstotal.cs_nifree == 0)
|
||||||
|
errx(1, "ffs_write_inode: fs out of inodes for ino %u",
|
||||||
|
ino);
|
||||||
|
if (fs->fs_cs(fs, cg).cs_nifree == 0)
|
||||||
|
errx(1,
|
||||||
|
"ffs_write_inode: cg %d out of inodes for ino %u",
|
||||||
|
cg, ino);
|
||||||
|
setbit(cg_inosused(cgp, fsopts->needswap), cgino);
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap);
|
||||||
|
fs->fs_cstotal.cs_nifree--;
|
||||||
|
fs->fs_cs(fs, cg).cs_nifree--;
|
||||||
|
if (S_ISDIR(ip->di_mode)) {
|
||||||
|
ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap);
|
||||||
|
fs->fs_cstotal.cs_ndir++;
|
||||||
|
fs->fs_cs(fs, cg).cs_ndir++;
|
||||||
|
}
|
||||||
|
ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
|
||||||
|
fsopts);
|
||||||
|
|
||||||
|
/* now write inode */
|
||||||
|
d = fsbtodb(fs, ino_to_fsba(fs, ino));
|
||||||
|
ffs_rdfs(d, fs->fs_bsize, ibuf, fsopts);
|
||||||
|
if (fsopts->needswap)
|
||||||
|
ffs_dinode_swap(ip, &ibuf[ino_to_fsbo(fs, ino)]);
|
||||||
|
else
|
||||||
|
ibuf[ino_to_fsbo(fs, ino)] = *ip;
|
||||||
|
ffs_wtfs(d, fs->fs_bsize, ibuf, fsopts);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
panic(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vwarnx(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
exit(1);
|
||||||
|
}
|
213
usr.sbin/makefs/ffs/buf.c
Normal file
213
usr.sbin/makefs/ffs/buf.c
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
/* $NetBSD: buf.c,v 1.1.1.1 2001/10/26 06:20:18 lukem Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2001 Wasabi Systems, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Written by Luke Mewburn for Wasabi Systems, Inc.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed for the NetBSD Project by
|
||||||
|
* Wasabi Systems, Inc.
|
||||||
|
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
|
||||||
|
* or promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <ufs/ufs/inode.h>
|
||||||
|
#include <ufs/ffs/fs.h>
|
||||||
|
|
||||||
|
#include "makefs.h"
|
||||||
|
|
||||||
|
#include "ffs/buf.h"
|
||||||
|
|
||||||
|
extern int sectorsize; /* XXX: from ffs.c & mkfs.c */
|
||||||
|
|
||||||
|
TAILQ_HEAD(buftailhead,buf) buftail;
|
||||||
|
|
||||||
|
int
|
||||||
|
bread(int fd, struct fs *fs, daddr_t blkno, int size, struct buf **bpp)
|
||||||
|
{
|
||||||
|
off_t offset;
|
||||||
|
ssize_t rv;
|
||||||
|
|
||||||
|
assert (fs != NULL);
|
||||||
|
assert (bpp != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_BUF_BREAD)
|
||||||
|
printf("bread: fs %p blkno %d size %d\n",
|
||||||
|
fs, blkno, size);
|
||||||
|
*bpp = getblk(fd, fs, blkno, size);
|
||||||
|
offset = (*bpp)->b_blkno * sectorsize; /* XXX */
|
||||||
|
if (debug & DEBUG_BUF_BREAD)
|
||||||
|
printf("bread: bp %p blkno %d offset %lld bcount %ld\n",
|
||||||
|
(*bpp), (*bpp)->b_blkno, (long long) offset,
|
||||||
|
(*bpp)->b_bcount);
|
||||||
|
if (lseek((*bpp)->b_fd, offset, SEEK_SET) == -1)
|
||||||
|
err(1, "bread: lseek %lld (%lld)",
|
||||||
|
(long long)(*bpp)->b_blkno, (long long)offset);
|
||||||
|
rv = read((*bpp)->b_fd, (*bpp)->b_data, (*bpp)->b_bcount);
|
||||||
|
if (debug & DEBUG_BUF_BREAD)
|
||||||
|
printf("bread: read %ld (%lld) returned %d\n",
|
||||||
|
(*bpp)->b_bcount, (long long)offset, (int)rv);
|
||||||
|
if (rv == -1) /* read error */
|
||||||
|
err(1, "bread: read %ld (%lld) returned %d",
|
||||||
|
(*bpp)->b_bcount, (long long)offset, (int)rv);
|
||||||
|
else if (rv != (*bpp)->b_bcount) /* short read */
|
||||||
|
err(1, "bread: read %ld (%lld) returned %d",
|
||||||
|
(*bpp)->b_bcount, (long long)offset, (int)rv);
|
||||||
|
else
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
brelse(struct buf *bp)
|
||||||
|
{
|
||||||
|
|
||||||
|
assert (bp != NULL);
|
||||||
|
assert (bp->b_data != NULL);
|
||||||
|
|
||||||
|
if (bp->b_lblkno < 0) {
|
||||||
|
/*
|
||||||
|
* XXX don't remove any buffers with negative logical block
|
||||||
|
* numbers (lblkno), so that we retain the mapping
|
||||||
|
* of negative lblkno -> real blkno that ffs_balloc()
|
||||||
|
* sets up.
|
||||||
|
*
|
||||||
|
* if we instead released these buffers, and implemented
|
||||||
|
* ufs_strategy() (and ufs_bmaparray()) and called those
|
||||||
|
* from bread() and bwrite() to convert the lblkno to
|
||||||
|
* a real blkno, we'd add a lot more code & complexity
|
||||||
|
* and reading off disk, for little gain, because this
|
||||||
|
* simple hack works for our purpose.
|
||||||
|
*/
|
||||||
|
bp->b_bcount = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&buftail, bp, b_tailq);
|
||||||
|
free(bp->b_data);
|
||||||
|
free(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
bwrite(struct buf *bp)
|
||||||
|
{
|
||||||
|
off_t offset;
|
||||||
|
ssize_t rv;
|
||||||
|
|
||||||
|
assert (bp != NULL);
|
||||||
|
offset = bp->b_blkno * sectorsize; /* XXX */
|
||||||
|
if (debug & DEBUG_BUF_BWRITE)
|
||||||
|
printf("bwrite: bp %p blkno %d offset %lld bcount %ld\n",
|
||||||
|
bp, bp->b_blkno, (long long) offset, bp->b_bcount);
|
||||||
|
if (lseek(bp->b_fd, offset, SEEK_SET) == -1)
|
||||||
|
return (errno);
|
||||||
|
rv = write(bp->b_fd, bp->b_data, bp->b_bcount);
|
||||||
|
if (debug & DEBUG_BUF_BWRITE)
|
||||||
|
printf("bwrite: write %ld (offset %lld) returned %lld\n",
|
||||||
|
bp->b_bcount, (long long)offset, (long long)rv);
|
||||||
|
if (rv == bp->b_bcount)
|
||||||
|
return (0);
|
||||||
|
else if (rv == -1) /* write error */
|
||||||
|
return (errno);
|
||||||
|
else /* short write ? */
|
||||||
|
return (EAGAIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
bcleanup(void)
|
||||||
|
{
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX this really shouldn't be necessary, but i'm curious to
|
||||||
|
* know why there's still some buffers lying around that
|
||||||
|
* aren't brelse()d
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (TAILQ_EMPTY(&buftail))
|
||||||
|
return;
|
||||||
|
|
||||||
|
printf("bcleanup: unflushed buffers:\n");
|
||||||
|
TAILQ_FOREACH(bp, &buftail, b_tailq) {
|
||||||
|
printf("\tlblkno %10d blkno %10d count %6ld bufsize %6ld\n",
|
||||||
|
bp->b_lblkno, bp->b_blkno, bp->b_bcount, bp->b_bufsize);
|
||||||
|
}
|
||||||
|
printf("bcleanup: done\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct buf *
|
||||||
|
getblk(int fd, struct fs *fs, daddr_t blkno, int size)
|
||||||
|
{
|
||||||
|
static int buftailinitted;
|
||||||
|
struct buf *bp;
|
||||||
|
|
||||||
|
assert (fs != NULL);
|
||||||
|
if (debug & DEBUG_BUF_GETBLK)
|
||||||
|
printf("getblk: fs %p blkno %d size %d\n", fs, blkno, size);
|
||||||
|
|
||||||
|
bp = NULL;
|
||||||
|
if (!buftailinitted) {
|
||||||
|
if (debug & DEBUG_BUF_GETBLK)
|
||||||
|
printf("getblk: initialising tailq\n");
|
||||||
|
TAILQ_INIT(&buftail);
|
||||||
|
buftailinitted = 1;
|
||||||
|
} else {
|
||||||
|
TAILQ_FOREACH(bp, &buftail, b_tailq) {
|
||||||
|
if (bp->b_lblkno != blkno)
|
||||||
|
continue;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (bp == NULL) {
|
||||||
|
if ((bp = calloc(1, sizeof(struct buf))) == NULL)
|
||||||
|
err(1, "getblk: calloc");
|
||||||
|
|
||||||
|
bp->b_bufsize = 0;
|
||||||
|
bp->b_blkno = bp->b_lblkno = blkno;
|
||||||
|
bp->b_fd = fd;
|
||||||
|
bp->b_fs = fs;
|
||||||
|
bp->b_data = NULL;
|
||||||
|
TAILQ_INSERT_HEAD(&buftail, bp, b_tailq);
|
||||||
|
}
|
||||||
|
bp->b_bcount = size;
|
||||||
|
if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) {
|
||||||
|
bp->b_bufsize = size;
|
||||||
|
if ((bp->b_data = realloc(bp->b_data, bp->b_bufsize)) == NULL)
|
||||||
|
err(1, "getblk: realloc b_data %ld", bp->b_bcount);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bp);
|
||||||
|
}
|
65
usr.sbin/makefs/ffs/buf.h
Normal file
65
usr.sbin/makefs/ffs/buf.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/* $NetBSD: buf.h,v 1.1.1.1 2001/10/26 06:20:21 lukem Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2001 Wasabi Systems, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Written by Luke Mewburn for Wasabi Systems, Inc.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed for the NetBSD Project by
|
||||||
|
* Wasabi Systems, Inc.
|
||||||
|
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
|
||||||
|
* or promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _FFS_BUF_H
|
||||||
|
#define _FFS_BUF_H
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
|
||||||
|
struct buf {
|
||||||
|
void * b_data;
|
||||||
|
long b_bufsize;
|
||||||
|
long b_bcount;
|
||||||
|
daddr_t b_blkno;
|
||||||
|
daddr_t b_lblkno;
|
||||||
|
int b_fd;
|
||||||
|
struct fs * b_fs;
|
||||||
|
|
||||||
|
TAILQ_ENTRY(buf) b_tailq;
|
||||||
|
};
|
||||||
|
|
||||||
|
void bcleanup(void);
|
||||||
|
int bread(int, struct fs *, daddr_t, int, struct buf **);
|
||||||
|
void brelse(struct buf *);
|
||||||
|
int bwrite(struct buf *);
|
||||||
|
struct buf * getblk(int, struct fs *, daddr_t, int);
|
||||||
|
|
||||||
|
#define bdwrite(bp) bwrite(bp)
|
||||||
|
#define clrbuf(bp) memset((bp)->b_data, 0, (u_int)(bp)->b_bcount)
|
||||||
|
|
||||||
|
#endif /* _FFS_BUF_H */
|
726
usr.sbin/makefs/ffs/ffs_alloc.c
Normal file
726
usr.sbin/makefs/ffs/ffs_alloc.c
Normal file
@ -0,0 +1,726 @@
|
|||||||
|
/* $NetBSD: ffs_alloc.c,v 1.1.1.1 2001/10/26 06:21:35 lukem Exp $ */
|
||||||
|
/* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem Exp */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1982, 1986, 1989, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <ufs/ufs/ufs_bswap.h>
|
||||||
|
#include <ufs/ufs/inode.h>
|
||||||
|
#include <ufs/ffs/fs.h>
|
||||||
|
|
||||||
|
#include "ffs/buf.h"
|
||||||
|
#include "ffs/ffs_extern.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int scanc(u_int, const u_char *, const u_char *, int);
|
||||||
|
|
||||||
|
static ufs_daddr_t ffs_alloccg(struct inode *, int, ufs_daddr_t, int);
|
||||||
|
static ufs_daddr_t ffs_alloccgblk(struct inode *, struct buf *, ufs_daddr_t);
|
||||||
|
static u_long ffs_hashalloc(struct inode *, int, long, int,
|
||||||
|
ufs_daddr_t (*)(struct inode *, int, ufs_daddr_t, int));
|
||||||
|
static ufs_daddr_t ffs_mapsearch(struct fs *, struct cg *, ufs_daddr_t, int);
|
||||||
|
|
||||||
|
/* in ffs_tables.c */
|
||||||
|
extern const int inside[], around[];
|
||||||
|
extern const u_char * const fragtbl[];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a block in the file system.
|
||||||
|
*
|
||||||
|
* The size of the requested block is given, which must be some
|
||||||
|
* multiple of fs_fsize and <= fs_bsize.
|
||||||
|
* A preference may be optionally specified. If a preference is given
|
||||||
|
* the following hierarchy is used to allocate a block:
|
||||||
|
* 1) allocate the requested block.
|
||||||
|
* 2) allocate a rotationally optimal block in the same cylinder.
|
||||||
|
* 3) allocate a block in the same cylinder group.
|
||||||
|
* 4) quadradically rehash into other cylinder groups, until an
|
||||||
|
* available block is located.
|
||||||
|
* If no block preference is given the following hierarchy is used
|
||||||
|
* to allocate a block:
|
||||||
|
* 1) allocate a block in the cylinder group that contains the
|
||||||
|
* inode for the file.
|
||||||
|
* 2) quadradically rehash into other cylinder groups, until an
|
||||||
|
* available block is located.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ffs_alloc(struct inode *ip, ufs_daddr_t lbn, ufs_daddr_t bpref, int size,
|
||||||
|
ufs_daddr_t *bnp)
|
||||||
|
{
|
||||||
|
struct fs *fs = ip->i_fs;
|
||||||
|
ufs_daddr_t bno;
|
||||||
|
int cg;
|
||||||
|
|
||||||
|
*bnp = 0;
|
||||||
|
if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) {
|
||||||
|
errx(1, "ffs_alloc: bad size: bsize %d size %d",
|
||||||
|
fs->fs_bsize, size);
|
||||||
|
}
|
||||||
|
if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
|
||||||
|
goto nospace;
|
||||||
|
if (bpref >= fs->fs_size)
|
||||||
|
bpref = 0;
|
||||||
|
if (bpref == 0)
|
||||||
|
cg = ino_to_cg(fs, ip->i_number);
|
||||||
|
else
|
||||||
|
cg = dtog(fs, bpref);
|
||||||
|
bno = (ufs_daddr_t)ffs_hashalloc(ip, cg, (long)bpref, size,
|
||||||
|
ffs_alloccg);
|
||||||
|
if (bno > 0) {
|
||||||
|
ip->i_ffs_blocks += btodb(size);
|
||||||
|
*bnp = bno;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
nospace:
|
||||||
|
return (ENOSPC);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Select the desired position for the next block in a file. The file is
|
||||||
|
* logically divided into sections. The first section is composed of the
|
||||||
|
* direct blocks. Each additional section contains fs_maxbpg blocks.
|
||||||
|
*
|
||||||
|
* If no blocks have been allocated in the first section, the policy is to
|
||||||
|
* request a block in the same cylinder group as the inode that describes
|
||||||
|
* the file. If no blocks have been allocated in any other section, the
|
||||||
|
* policy is to place the section in a cylinder group with a greater than
|
||||||
|
* average number of free blocks. An appropriate cylinder group is found
|
||||||
|
* by using a rotor that sweeps the cylinder groups. When a new group of
|
||||||
|
* blocks is needed, the sweep begins in the cylinder group following the
|
||||||
|
* cylinder group from which the previous allocation was made. The sweep
|
||||||
|
* continues until a cylinder group with greater than the average number
|
||||||
|
* of free blocks is found. If the allocation is for the first block in an
|
||||||
|
* indirect block, the information on the previous allocation is unavailable;
|
||||||
|
* here a best guess is made based upon the logical block number being
|
||||||
|
* allocated.
|
||||||
|
*
|
||||||
|
* If a section is already partially allocated, the policy is to
|
||||||
|
* contiguously allocate fs_maxcontig blocks. The end of one of these
|
||||||
|
* contiguous blocks and the beginning of the next is physically separated
|
||||||
|
* so that the disk head will be in transit between them for at least
|
||||||
|
* fs_rotdelay milliseconds. This is to allow time for the processor to
|
||||||
|
* schedule another I/O transfer.
|
||||||
|
*/
|
||||||
|
ufs_daddr_t
|
||||||
|
ffs_blkpref(struct inode *ip, ufs_daddr_t lbn, int indx, ufs_daddr_t *bap)
|
||||||
|
{
|
||||||
|
struct fs *fs;
|
||||||
|
int cg;
|
||||||
|
int avgbfree, startcg;
|
||||||
|
ufs_daddr_t nextblk;
|
||||||
|
|
||||||
|
fs = ip->i_fs;
|
||||||
|
if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
|
||||||
|
if (lbn < NDADDR + NINDIR(fs)) {
|
||||||
|
cg = ino_to_cg(fs, ip->i_number);
|
||||||
|
return (fs->fs_fpg * cg + fs->fs_frag);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Find a cylinder with greater than average number of
|
||||||
|
* unused data blocks.
|
||||||
|
*/
|
||||||
|
if (indx == 0 || bap[indx - 1] == 0)
|
||||||
|
startcg =
|
||||||
|
ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
|
||||||
|
else
|
||||||
|
startcg = dtog(fs,
|
||||||
|
ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
|
||||||
|
startcg %= fs->fs_ncg;
|
||||||
|
avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
|
||||||
|
for (cg = startcg; cg < fs->fs_ncg; cg++)
|
||||||
|
if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
|
||||||
|
fs->fs_cgrotor = cg;
|
||||||
|
return (fs->fs_fpg * cg + fs->fs_frag);
|
||||||
|
}
|
||||||
|
for (cg = 0; cg <= startcg; cg++)
|
||||||
|
if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
|
||||||
|
fs->fs_cgrotor = cg;
|
||||||
|
return (fs->fs_fpg * cg + fs->fs_frag);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* One or more previous blocks have been laid out. If less
|
||||||
|
* than fs_maxcontig previous blocks are contiguous, the
|
||||||
|
* next block is requested contiguously, otherwise it is
|
||||||
|
* requested rotationally delayed by fs_rotdelay milliseconds.
|
||||||
|
*/
|
||||||
|
nextblk = ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
|
||||||
|
if (indx < fs->fs_maxcontig ||
|
||||||
|
ufs_rw32(bap[indx - fs->fs_maxcontig], UFS_FSNEEDSWAP(fs)) +
|
||||||
|
blkstofrags(fs, fs->fs_maxcontig) != nextblk)
|
||||||
|
return (nextblk);
|
||||||
|
if (fs->fs_rotdelay != 0)
|
||||||
|
/*
|
||||||
|
* Here we convert ms of delay to frags as:
|
||||||
|
* (frags) = (ms) * (rev/sec) * (sect/rev) /
|
||||||
|
* ((sect/frag) * (ms/sec))
|
||||||
|
* then round up to the next block.
|
||||||
|
*/
|
||||||
|
nextblk += roundup(fs->fs_rotdelay * fs->fs_rps * fs->fs_nsect /
|
||||||
|
(NSPF(fs) * 1000), fs->fs_frag);
|
||||||
|
return (nextblk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Implement the cylinder overflow algorithm.
|
||||||
|
*
|
||||||
|
* The policy implemented by this algorithm is:
|
||||||
|
* 1) allocate the block in its requested cylinder group.
|
||||||
|
* 2) quadradically rehash on the cylinder group number.
|
||||||
|
* 3) brute force search for a free block.
|
||||||
|
*
|
||||||
|
* `size': size for data blocks, mode for inodes
|
||||||
|
*/
|
||||||
|
/*VARARGS5*/
|
||||||
|
static u_long
|
||||||
|
ffs_hashalloc(struct inode *ip, int cg, long pref, int size,
|
||||||
|
ufs_daddr_t (*allocator)(struct inode *, int, ufs_daddr_t, int))
|
||||||
|
{
|
||||||
|
struct fs *fs;
|
||||||
|
long result;
|
||||||
|
int i, icg = cg;
|
||||||
|
|
||||||
|
fs = ip->i_fs;
|
||||||
|
/*
|
||||||
|
* 1: preferred cylinder group
|
||||||
|
*/
|
||||||
|
result = (*allocator)(ip, cg, pref, size);
|
||||||
|
if (result)
|
||||||
|
return (result);
|
||||||
|
/*
|
||||||
|
* 2: quadratic rehash
|
||||||
|
*/
|
||||||
|
for (i = 1; i < fs->fs_ncg; i *= 2) {
|
||||||
|
cg += i;
|
||||||
|
if (cg >= fs->fs_ncg)
|
||||||
|
cg -= fs->fs_ncg;
|
||||||
|
result = (*allocator)(ip, cg, 0, size);
|
||||||
|
if (result)
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* 3: brute force search
|
||||||
|
* Note that we start at i == 2, since 0 was checked initially,
|
||||||
|
* and 1 is always checked in the quadratic rehash.
|
||||||
|
*/
|
||||||
|
cg = (icg + 2) % fs->fs_ncg;
|
||||||
|
for (i = 2; i < fs->fs_ncg; i++) {
|
||||||
|
result = (*allocator)(ip, cg, 0, size);
|
||||||
|
if (result)
|
||||||
|
return (result);
|
||||||
|
cg++;
|
||||||
|
if (cg == fs->fs_ncg)
|
||||||
|
cg = 0;
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine whether a block can be allocated.
|
||||||
|
*
|
||||||
|
* Check to see if a block of the appropriate size is available,
|
||||||
|
* and if it is, allocate it.
|
||||||
|
*/
|
||||||
|
static ufs_daddr_t
|
||||||
|
ffs_alloccg(struct inode *ip, int cg, ufs_daddr_t bpref, int size)
|
||||||
|
{
|
||||||
|
struct cg *cgp;
|
||||||
|
struct buf *bp;
|
||||||
|
ufs_daddr_t bno, blkno;
|
||||||
|
int error, frags, allocsiz, i;
|
||||||
|
struct fs *fs = ip->i_fs;
|
||||||
|
const int needswap = UFS_FSNEEDSWAP(fs);
|
||||||
|
|
||||||
|
if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize)
|
||||||
|
return (0);
|
||||||
|
error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)),
|
||||||
|
(int)fs->fs_cgsize, &bp);
|
||||||
|
if (error) {
|
||||||
|
brelse(bp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
cgp = (struct cg *)bp->b_data;
|
||||||
|
if (!cg_chkmagic(cgp, needswap) ||
|
||||||
|
(cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) {
|
||||||
|
brelse(bp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (size == fs->fs_bsize) {
|
||||||
|
bno = ffs_alloccgblk(ip, bp, bpref);
|
||||||
|
bdwrite(bp);
|
||||||
|
return (bno);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* check to see if any fragments are already available
|
||||||
|
* allocsiz is the size which will be allocated, hacking
|
||||||
|
* it down to a smaller size if necessary
|
||||||
|
*/
|
||||||
|
frags = numfrags(fs, size);
|
||||||
|
for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++)
|
||||||
|
if (cgp->cg_frsum[allocsiz] != 0)
|
||||||
|
break;
|
||||||
|
if (allocsiz == fs->fs_frag) {
|
||||||
|
/*
|
||||||
|
* no fragments were available, so a block will be
|
||||||
|
* allocated, and hacked up
|
||||||
|
*/
|
||||||
|
if (cgp->cg_cs.cs_nbfree == 0) {
|
||||||
|
brelse(bp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
bno = ffs_alloccgblk(ip, bp, bpref);
|
||||||
|
bpref = dtogd(fs, bno);
|
||||||
|
for (i = frags; i < fs->fs_frag; i++)
|
||||||
|
setbit(cg_blksfree(cgp, needswap), bpref + i);
|
||||||
|
i = fs->fs_frag - frags;
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
|
||||||
|
fs->fs_cstotal.cs_nffree += i;
|
||||||
|
fs->fs_cs(fs, cg).cs_nffree += i;
|
||||||
|
fs->fs_fmod = 1;
|
||||||
|
ufs_add32(cgp->cg_frsum[i], 1, needswap);
|
||||||
|
bdwrite(bp);
|
||||||
|
return (bno);
|
||||||
|
}
|
||||||
|
bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
|
||||||
|
for (i = 0; i < frags; i++)
|
||||||
|
clrbit(cg_blksfree(cgp, needswap), bno + i);
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap);
|
||||||
|
fs->fs_cstotal.cs_nffree -= frags;
|
||||||
|
fs->fs_cs(fs, cg).cs_nffree -= frags;
|
||||||
|
fs->fs_fmod = 1;
|
||||||
|
ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap);
|
||||||
|
if (frags != allocsiz)
|
||||||
|
ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap);
|
||||||
|
blkno = cg * fs->fs_fpg + bno;
|
||||||
|
bdwrite(bp);
|
||||||
|
return blkno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a block in a cylinder group.
|
||||||
|
*
|
||||||
|
* This algorithm implements the following policy:
|
||||||
|
* 1) allocate the requested block.
|
||||||
|
* 2) allocate a rotationally optimal block in the same cylinder.
|
||||||
|
* 3) allocate the next available block on the block rotor for the
|
||||||
|
* specified cylinder group.
|
||||||
|
* Note that this routine only allocates fs_bsize blocks; these
|
||||||
|
* blocks may be fragmented by the routine that allocates them.
|
||||||
|
*/
|
||||||
|
static ufs_daddr_t
|
||||||
|
ffs_alloccgblk(struct inode *ip, struct buf *bp, ufs_daddr_t bpref)
|
||||||
|
{
|
||||||
|
struct cg *cgp;
|
||||||
|
ufs_daddr_t bno, blkno;
|
||||||
|
int cylno, pos, delta;
|
||||||
|
short *cylbp;
|
||||||
|
int i;
|
||||||
|
struct fs *fs = ip->i_fs;
|
||||||
|
const int needswap = UFS_FSNEEDSWAP(fs);
|
||||||
|
|
||||||
|
cgp = (struct cg *)bp->b_data;
|
||||||
|
if (bpref == 0 || dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) {
|
||||||
|
bpref = ufs_rw32(cgp->cg_rotor, needswap);
|
||||||
|
goto norot;
|
||||||
|
}
|
||||||
|
bpref = blknum(fs, bpref);
|
||||||
|
bpref = dtogd(fs, bpref);
|
||||||
|
/*
|
||||||
|
* if the requested block is available, use it
|
||||||
|
*/
|
||||||
|
if (ffs_isblock(fs, cg_blksfree(cgp, needswap),
|
||||||
|
fragstoblks(fs, bpref))) {
|
||||||
|
bno = bpref;
|
||||||
|
goto gotit;
|
||||||
|
}
|
||||||
|
if (fs->fs_nrpos <= 1 || fs->fs_cpc == 0) {
|
||||||
|
/*
|
||||||
|
* Block layout information is not available.
|
||||||
|
* Leaving bpref unchanged means we take the
|
||||||
|
* next available free block following the one
|
||||||
|
* we just allocated. Hopefully this will at
|
||||||
|
* least hit a track cache on drives of unknown
|
||||||
|
* geometry (e.g. SCSI).
|
||||||
|
*/
|
||||||
|
goto norot;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* check for a block available on the same cylinder
|
||||||
|
*/
|
||||||
|
cylno = cbtocylno(fs, bpref);
|
||||||
|
if (cg_blktot(cgp, needswap)[cylno] == 0)
|
||||||
|
goto norot;
|
||||||
|
/*
|
||||||
|
* check the summary information to see if a block is
|
||||||
|
* available in the requested cylinder starting at the
|
||||||
|
* requested rotational position and proceeding around.
|
||||||
|
*/
|
||||||
|
cylbp = cg_blks(fs, cgp, cylno, needswap);
|
||||||
|
pos = cbtorpos(fs, bpref);
|
||||||
|
for (i = pos; i < fs->fs_nrpos; i++)
|
||||||
|
if (ufs_rw16(cylbp[i], needswap) > 0)
|
||||||
|
break;
|
||||||
|
if (i == fs->fs_nrpos)
|
||||||
|
for (i = 0; i < pos; i++)
|
||||||
|
if (ufs_rw16(cylbp[i], needswap) > 0)
|
||||||
|
break;
|
||||||
|
if (ufs_rw16(cylbp[i], needswap) > 0) {
|
||||||
|
/*
|
||||||
|
* found a rotational position, now find the actual
|
||||||
|
* block. A panic if none is actually there.
|
||||||
|
*/
|
||||||
|
pos = cylno % fs->fs_cpc;
|
||||||
|
bno = (cylno - pos) * fs->fs_spc / NSPB(fs);
|
||||||
|
if (fs_postbl(fs, pos)[i] == -1) {
|
||||||
|
errx(1,
|
||||||
|
"ffs_alloccgblk: cyl groups corrupted: pos %d i %d",
|
||||||
|
pos, i);
|
||||||
|
}
|
||||||
|
for (i = fs_postbl(fs, pos)[i];; ) {
|
||||||
|
if (ffs_isblock(fs, cg_blksfree(cgp, needswap), bno + i)) {
|
||||||
|
bno = blkstofrags(fs, (bno + i));
|
||||||
|
goto gotit;
|
||||||
|
}
|
||||||
|
delta = fs_rotbl(fs)[i];
|
||||||
|
if (delta <= 0 ||
|
||||||
|
delta + i > fragstoblks(fs, fs->fs_fpg))
|
||||||
|
break;
|
||||||
|
i += delta;
|
||||||
|
}
|
||||||
|
errx(1, "ffs_alloccgblk: can't find blk in cyl: pos %d i %d",
|
||||||
|
pos, i);
|
||||||
|
}
|
||||||
|
norot:
|
||||||
|
/*
|
||||||
|
* no blocks in the requested cylinder, so take next
|
||||||
|
* available one in this cylinder group.
|
||||||
|
*/
|
||||||
|
bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag);
|
||||||
|
if (bno < 0)
|
||||||
|
return (0);
|
||||||
|
cgp->cg_rotor = ufs_rw32(bno, needswap);
|
||||||
|
gotit:
|
||||||
|
blkno = fragstoblks(fs, bno);
|
||||||
|
ffs_clrblock(fs, cg_blksfree(cgp, needswap), (long)blkno);
|
||||||
|
ffs_clusteracct(fs, cgp, blkno, -1);
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap);
|
||||||
|
fs->fs_cstotal.cs_nbfree--;
|
||||||
|
fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--;
|
||||||
|
cylno = cbtocylno(fs, bno);
|
||||||
|
ufs_add16(cg_blks(fs, cgp, cylno, needswap)[cbtorpos(fs, bno)], -1,
|
||||||
|
needswap);
|
||||||
|
ufs_add32(cg_blktot(cgp, needswap)[cylno], -1, needswap);
|
||||||
|
fs->fs_fmod = 1;
|
||||||
|
blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno;
|
||||||
|
return (blkno);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a block or fragment.
|
||||||
|
*
|
||||||
|
* The specified block or fragment is placed back in the
|
||||||
|
* free map. If a fragment is deallocated, a possible
|
||||||
|
* block reassembly is checked.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ffs_blkfree(struct inode *ip, ufs_daddr_t bno, long size)
|
||||||
|
{
|
||||||
|
struct cg *cgp;
|
||||||
|
struct buf *bp;
|
||||||
|
ufs_daddr_t blkno;
|
||||||
|
int i, error, cg, blk, frags, bbase;
|
||||||
|
struct fs *fs = ip->i_fs;
|
||||||
|
const int needswap = UFS_FSNEEDSWAP(fs);
|
||||||
|
|
||||||
|
if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0 ||
|
||||||
|
fragnum(fs, bno) + numfrags(fs, size) > fs->fs_frag) {
|
||||||
|
errx(1, "blkfree: bad size: bno %u bsize %d size %ld",
|
||||||
|
bno, fs->fs_bsize, size);
|
||||||
|
}
|
||||||
|
cg = dtog(fs, bno);
|
||||||
|
if ((u_int)bno >= fs->fs_size) {
|
||||||
|
warnx("bad block %d, ino %d\n", bno, ip->i_number);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)),
|
||||||
|
(int)fs->fs_cgsize, &bp);
|
||||||
|
if (error) {
|
||||||
|
brelse(bp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cgp = (struct cg *)bp->b_data;
|
||||||
|
if (!cg_chkmagic(cgp, needswap)) {
|
||||||
|
brelse(bp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
bno = dtogd(fs, bno);
|
||||||
|
if (size == fs->fs_bsize) {
|
||||||
|
blkno = fragstoblks(fs, bno);
|
||||||
|
if (!ffs_isfreeblock(fs, cg_blksfree(cgp, needswap), blkno)) {
|
||||||
|
errx(1, "blkfree: freeing free block %d", bno);
|
||||||
|
}
|
||||||
|
ffs_setblock(fs, cg_blksfree(cgp, needswap), blkno);
|
||||||
|
ffs_clusteracct(fs, cgp, blkno, 1);
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
|
||||||
|
fs->fs_cstotal.cs_nbfree++;
|
||||||
|
fs->fs_cs(fs, cg).cs_nbfree++;
|
||||||
|
i = cbtocylno(fs, bno);
|
||||||
|
ufs_add16(cg_blks(fs, cgp, i, needswap)[cbtorpos(fs, bno)], 1,
|
||||||
|
needswap);
|
||||||
|
ufs_add32(cg_blktot(cgp, needswap)[i], 1, needswap);
|
||||||
|
} else {
|
||||||
|
bbase = bno - fragnum(fs, bno);
|
||||||
|
/*
|
||||||
|
* decrement the counts associated with the old frags
|
||||||
|
*/
|
||||||
|
blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase);
|
||||||
|
ffs_fragacct(fs, blk, cgp->cg_frsum, -1, needswap);
|
||||||
|
/*
|
||||||
|
* deallocate the fragment
|
||||||
|
*/
|
||||||
|
frags = numfrags(fs, size);
|
||||||
|
for (i = 0; i < frags; i++) {
|
||||||
|
if (isset(cg_blksfree(cgp, needswap), bno + i)) {
|
||||||
|
errx(1, "blkfree: freeing free frag: block %d",
|
||||||
|
bno + i);
|
||||||
|
}
|
||||||
|
setbit(cg_blksfree(cgp, needswap), bno + i);
|
||||||
|
}
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
|
||||||
|
fs->fs_cstotal.cs_nffree += i;
|
||||||
|
fs->fs_cs(fs, cg).cs_nffree += i;
|
||||||
|
/*
|
||||||
|
* add back in counts associated with the new frags
|
||||||
|
*/
|
||||||
|
blk = blkmap(fs, cg_blksfree(cgp, needswap), bbase);
|
||||||
|
ffs_fragacct(fs, blk, cgp->cg_frsum, 1, needswap);
|
||||||
|
/*
|
||||||
|
* if a complete block has been reassembled, account for it
|
||||||
|
*/
|
||||||
|
blkno = fragstoblks(fs, bbase);
|
||||||
|
if (ffs_isblock(fs, cg_blksfree(cgp, needswap), blkno)) {
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap);
|
||||||
|
fs->fs_cstotal.cs_nffree -= fs->fs_frag;
|
||||||
|
fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
|
||||||
|
ffs_clusteracct(fs, cgp, blkno, 1);
|
||||||
|
ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
|
||||||
|
fs->fs_cstotal.cs_nbfree++;
|
||||||
|
fs->fs_cs(fs, cg).cs_nbfree++;
|
||||||
|
i = cbtocylno(fs, bbase);
|
||||||
|
ufs_add16(cg_blks(fs, cgp, i, needswap)[cbtorpos(fs,
|
||||||
|
bbase)], 1,
|
||||||
|
needswap);
|
||||||
|
ufs_add32(cg_blktot(cgp, needswap)[i], 1, needswap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fs->fs_fmod = 1;
|
||||||
|
bdwrite(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
scanc(u_int size, const u_char *cp, const u_char table[], int mask)
|
||||||
|
{
|
||||||
|
const u_char *end = &cp[size];
|
||||||
|
|
||||||
|
while (cp < end && (table[*cp] & mask) == 0)
|
||||||
|
cp++;
|
||||||
|
return (end - cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find a block of the specified size in the specified cylinder group.
|
||||||
|
*
|
||||||
|
* It is a panic if a request is made to find a block if none are
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
static ufs_daddr_t
|
||||||
|
ffs_mapsearch(struct fs *fs, struct cg *cgp, ufs_daddr_t bpref, int allocsiz)
|
||||||
|
{
|
||||||
|
ufs_daddr_t bno;
|
||||||
|
int start, len, loc, i;
|
||||||
|
int blk, field, subfield, pos;
|
||||||
|
int ostart, olen;
|
||||||
|
const int needswap = UFS_FSNEEDSWAP(fs);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* find the fragment by searching through the free block
|
||||||
|
* map for an appropriate bit pattern
|
||||||
|
*/
|
||||||
|
if (bpref)
|
||||||
|
start = dtogd(fs, bpref) / NBBY;
|
||||||
|
else
|
||||||
|
start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY;
|
||||||
|
len = howmany(fs->fs_fpg, NBBY) - start;
|
||||||
|
ostart = start;
|
||||||
|
olen = len;
|
||||||
|
loc = scanc((u_int)len,
|
||||||
|
(const u_char *)&cg_blksfree(cgp, needswap)[start],
|
||||||
|
(const u_char *)fragtbl[fs->fs_frag],
|
||||||
|
(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
|
||||||
|
if (loc == 0) {
|
||||||
|
len = start + 1;
|
||||||
|
start = 0;
|
||||||
|
loc = scanc((u_int)len,
|
||||||
|
(const u_char *)&cg_blksfree(cgp, needswap)[0],
|
||||||
|
(const u_char *)fragtbl[fs->fs_frag],
|
||||||
|
(1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
|
||||||
|
if (loc == 0) {
|
||||||
|
errx(1,
|
||||||
|
"ffs_alloccg: map corrupted: start %d len %d offset %d %ld",
|
||||||
|
ostart, olen,
|
||||||
|
ufs_rw32(cgp->cg_freeoff, needswap),
|
||||||
|
(long)cg_blksfree(cgp, needswap) - (long)cgp);
|
||||||
|
/* NOTREACHED */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bno = (start + len - loc) * NBBY;
|
||||||
|
cgp->cg_frotor = ufs_rw32(bno, needswap);
|
||||||
|
/*
|
||||||
|
* found the byte in the map
|
||||||
|
* sift through the bits to find the selected frag
|
||||||
|
*/
|
||||||
|
for (i = bno + NBBY; bno < i; bno += fs->fs_frag) {
|
||||||
|
blk = blkmap(fs, cg_blksfree(cgp, needswap), bno);
|
||||||
|
blk <<= 1;
|
||||||
|
field = around[allocsiz];
|
||||||
|
subfield = inside[allocsiz];
|
||||||
|
for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) {
|
||||||
|
if ((blk & field) == subfield)
|
||||||
|
return (bno + pos);
|
||||||
|
field <<= 1;
|
||||||
|
subfield <<= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errx(1, "ffs_alloccg: block not in map: bno %d", bno);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update the cluster map because of an allocation or free.
|
||||||
|
*
|
||||||
|
* Cnt == 1 means free; cnt == -1 means allocating.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
ffs_clusteracct(struct fs *fs, struct cg *cgp, ufs_daddr_t blkno, int cnt)
|
||||||
|
{
|
||||||
|
int32_t *sump;
|
||||||
|
int32_t *lp;
|
||||||
|
u_char *freemapp, *mapp;
|
||||||
|
int i, start, end, forw, back, map, bit;
|
||||||
|
const int needswap = UFS_FSNEEDSWAP(fs);
|
||||||
|
|
||||||
|
if (fs->fs_contigsumsize <= 0)
|
||||||
|
return;
|
||||||
|
freemapp = cg_clustersfree(cgp, needswap);
|
||||||
|
sump = cg_clustersum(cgp, needswap);
|
||||||
|
/*
|
||||||
|
* Allocate or clear the actual block.
|
||||||
|
*/
|
||||||
|
if (cnt > 0)
|
||||||
|
setbit(freemapp, blkno);
|
||||||
|
else
|
||||||
|
clrbit(freemapp, blkno);
|
||||||
|
/*
|
||||||
|
* Find the size of the cluster going forward.
|
||||||
|
*/
|
||||||
|
start = blkno + 1;
|
||||||
|
end = start + fs->fs_contigsumsize;
|
||||||
|
if (end >= ufs_rw32(cgp->cg_nclusterblks, needswap))
|
||||||
|
end = ufs_rw32(cgp->cg_nclusterblks, needswap);
|
||||||
|
mapp = &freemapp[start / NBBY];
|
||||||
|
map = *mapp++;
|
||||||
|
bit = 1 << (start % NBBY);
|
||||||
|
for (i = start; i < end; i++) {
|
||||||
|
if ((map & bit) == 0)
|
||||||
|
break;
|
||||||
|
if ((i & (NBBY - 1)) != (NBBY - 1)) {
|
||||||
|
bit <<= 1;
|
||||||
|
} else {
|
||||||
|
map = *mapp++;
|
||||||
|
bit = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
forw = i - start;
|
||||||
|
/*
|
||||||
|
* Find the size of the cluster going backward.
|
||||||
|
*/
|
||||||
|
start = blkno - 1;
|
||||||
|
end = start - fs->fs_contigsumsize;
|
||||||
|
if (end < 0)
|
||||||
|
end = -1;
|
||||||
|
mapp = &freemapp[start / NBBY];
|
||||||
|
map = *mapp--;
|
||||||
|
bit = 1 << (start % NBBY);
|
||||||
|
for (i = start; i > end; i--) {
|
||||||
|
if ((map & bit) == 0)
|
||||||
|
break;
|
||||||
|
if ((i & (NBBY - 1)) != 0) {
|
||||||
|
bit >>= 1;
|
||||||
|
} else {
|
||||||
|
map = *mapp--;
|
||||||
|
bit = 1 << (NBBY - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
back = start - i;
|
||||||
|
/*
|
||||||
|
* Account for old cluster and the possibly new forward and
|
||||||
|
* back clusters.
|
||||||
|
*/
|
||||||
|
i = back + forw + 1;
|
||||||
|
if (i > fs->fs_contigsumsize)
|
||||||
|
i = fs->fs_contigsumsize;
|
||||||
|
ufs_add32(sump[i], cnt, needswap);
|
||||||
|
if (back > 0)
|
||||||
|
ufs_add32(sump[back], -cnt, needswap);
|
||||||
|
if (forw > 0)
|
||||||
|
ufs_add32(sump[forw], -cnt, needswap);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Update cluster summary information.
|
||||||
|
*/
|
||||||
|
lp = &sump[fs->fs_contigsumsize];
|
||||||
|
for (i = fs->fs_contigsumsize; i > 0; i--)
|
||||||
|
if (ufs_rw32(*lp--, needswap) > 0)
|
||||||
|
break;
|
||||||
|
fs->fs_maxcluster[ufs_rw32(cgp->cg_cgx, needswap)] = i;
|
||||||
|
}
|
304
usr.sbin/makefs/ffs/ffs_balloc.c
Normal file
304
usr.sbin/makefs/ffs/ffs_balloc.c
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/* $NetBSD: ffs_balloc.c,v 1.1.1.1 2001/10/26 06:21:43 lukem Exp $ */
|
||||||
|
/* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1982, 1986, 1989, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)ffs_balloc.c 8.8 (Berkeley) 6/16/95
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <ufs/ufs/ufs_bswap.h>
|
||||||
|
#include <ufs/ufs/inode.h>
|
||||||
|
#include <ufs/ffs/fs.h>
|
||||||
|
|
||||||
|
#include "ffs/buf.h"
|
||||||
|
#include "ffs/ffs_extern.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Balloc defines the structure of file system storage
|
||||||
|
* by allocating the physical blocks on a device given
|
||||||
|
* the inode and the logical block number in a file.
|
||||||
|
*
|
||||||
|
* Assume: flags == B_SYNC | B_CLRBUF
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
|
||||||
|
{
|
||||||
|
ufs_daddr_t lbn;
|
||||||
|
int size;
|
||||||
|
ufs_daddr_t nb;
|
||||||
|
struct buf *bp, *nbp;
|
||||||
|
struct fs *fs = ip->i_fs;
|
||||||
|
struct indir indirs[NIADDR + 2];
|
||||||
|
ufs_daddr_t newb, *bap, pref;
|
||||||
|
int osize, nsize, num, i, error;
|
||||||
|
ufs_daddr_t *allocib, *allocblk, allociblk[NIADDR + 1];
|
||||||
|
const int needswap = UFS_FSNEEDSWAP(fs);
|
||||||
|
|
||||||
|
lbn = lblkno(fs, offset);
|
||||||
|
size = blkoff(fs, offset) + bufsize;
|
||||||
|
if (bpp != NULL) {
|
||||||
|
*bpp = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(size <= fs->fs_bsize);
|
||||||
|
if (lbn < 0)
|
||||||
|
return (EFBIG);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the next write will extend the file into a new block,
|
||||||
|
* and the file is currently composed of a fragment
|
||||||
|
* this fragment has to be extended to be a full block.
|
||||||
|
*/
|
||||||
|
|
||||||
|
nb = lblkno(fs, ip->i_ffs_size);
|
||||||
|
if (nb < NDADDR && nb < lbn) {
|
||||||
|
osize = blksize(fs, ip, nb);
|
||||||
|
if (osize < fs->fs_bsize && osize > 0) {
|
||||||
|
warnx("need to ffs_realloccg; not supported!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The first NDADDR blocks are direct blocks
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (lbn < NDADDR) {
|
||||||
|
nb = ufs_rw32(ip->i_ffs_db[lbn], needswap);
|
||||||
|
if (nb != 0 && ip->i_ffs_size >= lblktosize(fs, lbn + 1)) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The block is an already-allocated direct block
|
||||||
|
* and the file already extends past this block,
|
||||||
|
* thus this must be a whole block.
|
||||||
|
* Just read the block (if requested).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (bpp != NULL) {
|
||||||
|
error = bread(ip->i_fd, ip->i_fs, lbn,
|
||||||
|
fs->fs_bsize, bpp);
|
||||||
|
if (error) {
|
||||||
|
brelse(*bpp);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
if (nb != 0) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Consider need to reallocate a fragment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
osize = fragroundup(fs, blkoff(fs, ip->i_ffs_size));
|
||||||
|
nsize = fragroundup(fs, size);
|
||||||
|
if (nsize <= osize) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The existing block is already
|
||||||
|
* at least as big as we want.
|
||||||
|
* Just read the block (if requested).
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (bpp != NULL) {
|
||||||
|
error = bread(ip->i_fd, ip->i_fs, lbn,
|
||||||
|
osize, bpp);
|
||||||
|
if (error) {
|
||||||
|
brelse(*bpp);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
warnx("need to ffs_realloccg; not supported!");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* the block was not previously allocated,
|
||||||
|
* allocate a new block or fragment.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (ip->i_ffs_size < lblktosize(fs, lbn + 1))
|
||||||
|
nsize = fragroundup(fs, size);
|
||||||
|
else
|
||||||
|
nsize = fs->fs_bsize;
|
||||||
|
error = ffs_alloc(ip, lbn,
|
||||||
|
ffs_blkpref(ip, lbn, (int)lbn, &ip->i_ffs_db[0]),
|
||||||
|
nsize, &newb);
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
if (bpp != NULL) {
|
||||||
|
bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize);
|
||||||
|
bp->b_blkno = fsbtodb(fs, newb);
|
||||||
|
clrbuf(bp);
|
||||||
|
*bpp = bp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ip->i_ffs_db[lbn] = ufs_rw32(newb, needswap);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Determine the number of levels of indirection.
|
||||||
|
*/
|
||||||
|
pref = 0;
|
||||||
|
if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
|
||||||
|
return(error);
|
||||||
|
|
||||||
|
if (num < 1) {
|
||||||
|
warnx("ffs_balloc: ufs_getlbns returned indirect block");
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Fetch the first indirect block allocating if necessary.
|
||||||
|
*/
|
||||||
|
--num;
|
||||||
|
nb = ufs_rw32(ip->i_ffs_ib[indirs[0].in_off], needswap);
|
||||||
|
allocib = NULL;
|
||||||
|
allocblk = allociblk;
|
||||||
|
if (nb == 0) {
|
||||||
|
pref = ffs_blkpref(ip, lbn, 0, (ufs_daddr_t *)0);
|
||||||
|
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
|
||||||
|
if (error)
|
||||||
|
return (error);
|
||||||
|
nb = newb;
|
||||||
|
*allocblk++ = nb;
|
||||||
|
bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize);
|
||||||
|
bp->b_blkno = fsbtodb(fs, nb);
|
||||||
|
clrbuf(bp);
|
||||||
|
/*
|
||||||
|
* Write synchronously so that indirect blocks
|
||||||
|
* never point at garbage.
|
||||||
|
*/
|
||||||
|
if ((error = bwrite(bp)) != 0)
|
||||||
|
goto fail;
|
||||||
|
allocib = &ip->i_ffs_ib[indirs[0].in_off];
|
||||||
|
*allocib = ufs_rw32(nb, needswap);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Fetch through the indirect blocks, allocating as necessary.
|
||||||
|
*/
|
||||||
|
for (i = 1;;) {
|
||||||
|
error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn,
|
||||||
|
(int)fs->fs_bsize, &bp);
|
||||||
|
if (error) {
|
||||||
|
brelse(bp);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
bap = (ufs_daddr_t *)bp->b_data;
|
||||||
|
nb = ufs_rw32(bap[indirs[i].in_off], needswap);
|
||||||
|
if (i == num)
|
||||||
|
break;
|
||||||
|
i++;
|
||||||
|
if (nb != 0) {
|
||||||
|
brelse(bp);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (pref == 0)
|
||||||
|
pref = ffs_blkpref(ip, lbn, 0, (ufs_daddr_t *)0);
|
||||||
|
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
|
||||||
|
if (error) {
|
||||||
|
brelse(bp);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
nb = newb;
|
||||||
|
*allocblk++ = nb;
|
||||||
|
nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn,
|
||||||
|
fs->fs_bsize);
|
||||||
|
nbp->b_blkno = fsbtodb(fs, nb);
|
||||||
|
clrbuf(nbp);
|
||||||
|
/*
|
||||||
|
* Write synchronously so that indirect blocks
|
||||||
|
* never point at garbage.
|
||||||
|
*/
|
||||||
|
if ((error = bwrite(nbp)) != 0) {
|
||||||
|
brelse(bp);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap);
|
||||||
|
/*
|
||||||
|
* If required, write synchronously, otherwise use
|
||||||
|
* delayed write.
|
||||||
|
*/
|
||||||
|
bwrite(bp);
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
* Get the data block, allocating if necessary.
|
||||||
|
*/
|
||||||
|
if (nb == 0) {
|
||||||
|
pref = ffs_blkpref(ip, lbn, indirs[num].in_off, &bap[0]);
|
||||||
|
error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
|
||||||
|
if (error) {
|
||||||
|
brelse(bp);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
nb = newb;
|
||||||
|
*allocblk++ = nb;
|
||||||
|
if (bpp != NULL) {
|
||||||
|
nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize);
|
||||||
|
nbp->b_blkno = fsbtodb(fs, nb);
|
||||||
|
clrbuf(nbp);
|
||||||
|
*bpp = nbp;
|
||||||
|
}
|
||||||
|
bap[indirs[num].in_off] = ufs_rw32(nb, needswap);
|
||||||
|
/*
|
||||||
|
* If required, write synchronously, otherwise use
|
||||||
|
* delayed write.
|
||||||
|
*/
|
||||||
|
bwrite(bp);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
brelse(bp);
|
||||||
|
if (bpp != NULL) {
|
||||||
|
error = bread(ip->i_fd, ip->i_fs, lbn,
|
||||||
|
(int)fs->fs_bsize, &nbp);
|
||||||
|
if (error) {
|
||||||
|
brelse(nbp);
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
*bpp = nbp;
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
fail:
|
||||||
|
return (error);
|
||||||
|
}
|
79
usr.sbin/makefs/ffs/ffs_extern.h
Normal file
79
usr.sbin/makefs/ffs/ffs_extern.h
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
/* $NetBSD: ffs_extern.h,v 1.1.1.1 2001/10/26 06:21:48 lukem Exp $ */
|
||||||
|
/* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */
|
||||||
|
|
||||||
|
/*-
|
||||||
|
* Copyright (c) 1991, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "ffs/buf.h"
|
||||||
|
|
||||||
|
#define i_fd i_modrev /* XXX: stuff this somewhere */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Structure used to pass around logical block paths generated by
|
||||||
|
* ufs_getlbns and used by truncate and bmap code.
|
||||||
|
*/
|
||||||
|
struct indir {
|
||||||
|
ufs_daddr_t in_lbn; /* Logical block number. */
|
||||||
|
int in_off; /* Offset in buffer. */
|
||||||
|
int in_exists; /* Flag if the block exists. */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ffs.c */
|
||||||
|
void panic(const char *, ...)
|
||||||
|
__attribute__((__noreturn__,__format__(__printf__,1,2)));
|
||||||
|
|
||||||
|
/* ffs_alloc.c */
|
||||||
|
int ffs_alloc(struct inode *, ufs_daddr_t, ufs_daddr_t, int, ufs_daddr_t *);
|
||||||
|
ufs_daddr_t ffs_blkpref(struct inode *, ufs_daddr_t, int, ufs_daddr_t *);
|
||||||
|
void ffs_blkfree(struct inode *, ufs_daddr_t, long);
|
||||||
|
void ffs_clusteracct(struct fs *, struct cg *, ufs_daddr_t, int);
|
||||||
|
|
||||||
|
/* ffs_balloc.c */
|
||||||
|
int ffs_balloc(struct inode *, off_t, int, struct buf **);
|
||||||
|
|
||||||
|
/* ffs_bswap.c */
|
||||||
|
void ffs_sb_swap(struct fs*, struct fs *);
|
||||||
|
void ffs_dinode_swap(struct dinode *, struct dinode *);
|
||||||
|
void ffs_csum_swap(struct csum *, struct csum *, int);
|
||||||
|
|
||||||
|
/* ffs_subr.c */
|
||||||
|
void ffs_fragacct(struct fs *, int, int32_t[], int, int);
|
||||||
|
int ffs_isblock(struct fs *, u_char *, ufs_daddr_t);
|
||||||
|
int ffs_isfreeblock(struct fs *, u_char *, ufs_daddr_t);
|
||||||
|
void ffs_clrblock(struct fs *, u_char *, ufs_daddr_t);
|
||||||
|
void ffs_setblock(struct fs *, u_char *, ufs_daddr_t);
|
||||||
|
|
||||||
|
/* ufs_bmap.c */
|
||||||
|
int ufs_getlbns(struct inode *, ufs_daddr_t, struct indir *, int *);
|
1017
usr.sbin/makefs/ffs/mkfs.c
Normal file
1017
usr.sbin/makefs/ffs/mkfs.c
Normal file
File diff suppressed because it is too large
Load Diff
41
usr.sbin/makefs/ffs/newfs_extern.h
Normal file
41
usr.sbin/makefs/ffs/newfs_extern.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
/* $NetBSD: newfs_extern.h,v 1.1.1.1 2001/10/26 06:22:10 lukem Exp $ */
|
||||||
|
/* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1997 Christos Zoulas. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by Christos Zoulas.
|
||||||
|
* 4. The name of the author may not be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||||
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||||
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||||
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||||
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* prototypes */
|
||||||
|
struct fs *ffs_mkfs(const char *, const fsinfo_t *);
|
||||||
|
void ffs_write_superblock(struct fs *, const fsinfo_t *);
|
||||||
|
void ffs_rdfs(daddr_t, int, void *, const fsinfo_t *);
|
||||||
|
void ffs_wtfs(daddr_t, int, void *, const fsinfo_t *);
|
||||||
|
|
||||||
|
#ifndef MAXINOPB
|
||||||
|
#define MAXINOPB (MAXBSIZE / DINODE_SIZE)
|
||||||
|
#endif
|
141
usr.sbin/makefs/ffs/ufs_bmap.c
Normal file
141
usr.sbin/makefs/ffs/ufs_bmap.c
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
/* $NetBSD: ufs_bmap.c,v 1.1.1.1 2001/10/26 06:22:21 lukem Exp $ */
|
||||||
|
/* From: NetBSD: ufs_bmap.c,v 1.10 2000/11/27 08:39:57 chs Exp */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 1989, 1991, 1993
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
* (c) UNIX System Laboratories, Inc.
|
||||||
|
* All or some portions of this file are derived from material licensed
|
||||||
|
* to the University of California by American Telephone and Telegraph
|
||||||
|
* Co. or Unix System Laboratories, Inc. and are reproduced herein with
|
||||||
|
* the permission of UNIX System Laboratories, Inc.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* @(#)ufs_bmap.c 8.8 (Berkeley) 8/11/95
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <ufs/ufs/ufs_bswap.h>
|
||||||
|
#include <ufs/ufs/inode.h>
|
||||||
|
#include <ufs/ffs/fs.h>
|
||||||
|
|
||||||
|
#include "ffs/buf.h"
|
||||||
|
#include "ffs/ffs_extern.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create an array of logical block number/offset pairs which represent the
|
||||||
|
* path of indirect blocks required to access a data block. The first "pair"
|
||||||
|
* contains the logical block number of the appropriate single, double or
|
||||||
|
* triple indirect block and the offset into the inode indirect block array.
|
||||||
|
* Note, the logical block number of the inode single/double/triple indirect
|
||||||
|
* block appears twice in the array, once with the offset into the i_ffs_ib and
|
||||||
|
* once with the offset into the page itself.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
ufs_getlbns(struct inode *ip, ufs_daddr_t bn, struct indir *ap, int *nump)
|
||||||
|
{
|
||||||
|
long metalbn, realbn;
|
||||||
|
int64_t blockcnt;
|
||||||
|
int lbc;
|
||||||
|
int i, numlevels, off;
|
||||||
|
u_long lognindir;
|
||||||
|
|
||||||
|
lognindir = ffs(NINDIR(ip->i_fs)) - 1;
|
||||||
|
if (nump)
|
||||||
|
*nump = 0;
|
||||||
|
numlevels = 0;
|
||||||
|
realbn = bn;
|
||||||
|
if ((long)bn < 0)
|
||||||
|
bn = -(long)bn;
|
||||||
|
|
||||||
|
/* The first NDADDR blocks are direct blocks. */
|
||||||
|
if (bn < NDADDR)
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Determine the number of levels of indirection. After this loop
|
||||||
|
* is done, blockcnt indicates the number of data blocks possible
|
||||||
|
* at the given level of indirection, and NIADDR - i is the number
|
||||||
|
* of levels of indirection needed to locate the requested block.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bn -= NDADDR;
|
||||||
|
for (lbc = 0, i = NIADDR;; i--, bn -= blockcnt) {
|
||||||
|
if (i == 0)
|
||||||
|
return (EFBIG);
|
||||||
|
|
||||||
|
lbc += lognindir;
|
||||||
|
blockcnt = (int64_t)1 << lbc;
|
||||||
|
|
||||||
|
if (bn < blockcnt)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the address of the first meta-block. */
|
||||||
|
if (realbn >= 0)
|
||||||
|
metalbn = -(realbn - bn + NIADDR - i);
|
||||||
|
else
|
||||||
|
metalbn = -(-realbn - bn + NIADDR - i);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* At each iteration, off is the offset into the bap array which is
|
||||||
|
* an array of disk addresses at the current level of indirection.
|
||||||
|
* The logical block number and the offset in that block are stored
|
||||||
|
* into the argument array.
|
||||||
|
*/
|
||||||
|
ap->in_lbn = metalbn;
|
||||||
|
ap->in_off = off = NIADDR - i;
|
||||||
|
ap->in_exists = 0;
|
||||||
|
ap++;
|
||||||
|
for (++numlevels; i <= NIADDR; i++) {
|
||||||
|
/* If searching for a meta-data block, quit when found. */
|
||||||
|
if (metalbn == realbn)
|
||||||
|
break;
|
||||||
|
|
||||||
|
lbc -= lognindir;
|
||||||
|
blockcnt = (int64_t)1 << lbc;
|
||||||
|
off = (bn >> lbc) & (NINDIR(ip->i_fs) - 1);
|
||||||
|
|
||||||
|
++numlevels;
|
||||||
|
ap->in_lbn = metalbn;
|
||||||
|
ap->in_off = off;
|
||||||
|
ap->in_exists = 0;
|
||||||
|
++ap;
|
||||||
|
|
||||||
|
metalbn -= -1 + off * blockcnt;
|
||||||
|
}
|
||||||
|
if (nump)
|
||||||
|
*nump = numlevels;
|
||||||
|
return (0);
|
||||||
|
}
|
516
usr.sbin/makefs/walk.c
Normal file
516
usr.sbin/makefs/walk.c
Normal file
@ -0,0 +1,516 @@
|
|||||||
|
/* $NetBSD: walk.c,v 1.1.1.1 2001/10/26 06:16:19 lukem Exp $ */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright 2001 Wasabi Systems, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Written by Luke Mewburn for Wasabi Systems, Inc.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed for the NetBSD Project by
|
||||||
|
* Wasabi Systems, Inc.
|
||||||
|
* 4. The name of Wasabi Systems, Inc. may not be used to endorse
|
||||||
|
* or promote products derived from this software without specific prior
|
||||||
|
* written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
|
||||||
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The function link_check() was inspired from NetBSD's usr.bin/du/du.c,
|
||||||
|
* which has the following copyright notice:
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 1989, 1993, 1994
|
||||||
|
* The Regents of the University of California. All rights reserved.
|
||||||
|
*
|
||||||
|
* This code is derived from software contributed to Berkeley by
|
||||||
|
* Chris Newcomb.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* 3. All advertising materials mentioning features or use of this software
|
||||||
|
* must display the following acknowledgement:
|
||||||
|
* This product includes software developed by the University of
|
||||||
|
* California, Berkeley and its contributors.
|
||||||
|
* 4. Neither the name of the University nor the names of its contributors
|
||||||
|
* may be used to endorse or promote products derived from this software
|
||||||
|
* without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/param.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "makefs.h"
|
||||||
|
#include "mtree.h"
|
||||||
|
|
||||||
|
static void apply_specdir(const char *, NODE *, fsnode *);
|
||||||
|
static void apply_specentry(const char *, NODE *, fsnode *);
|
||||||
|
static fsnode *create_fsnode(const char *, struct stat *);
|
||||||
|
static fsnode *link_check(fsnode *);
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* walk_dir --
|
||||||
|
* build a tree of fsnodes from `dir', with a parent fsnode of `parent'
|
||||||
|
* (which may be NULL for the root of the tree).
|
||||||
|
* each "level" is a directory, with the "." entry guaranteed to be
|
||||||
|
* at the start of the list, and without ".." entries.
|
||||||
|
*/
|
||||||
|
fsnode *
|
||||||
|
walk_dir(const char *dir, fsnode *parent)
|
||||||
|
{
|
||||||
|
fsnode *first, *cur, *prev;
|
||||||
|
DIR *dirp;
|
||||||
|
struct dirent *dent;
|
||||||
|
char path[MAXPATHLEN + 1];
|
||||||
|
struct stat stbuf;
|
||||||
|
|
||||||
|
assert(dir != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_WALK_DIR)
|
||||||
|
printf("walk_dir: %s %p\n", dir, parent);
|
||||||
|
if ((dirp = opendir(dir)) == NULL)
|
||||||
|
err(1, "Can't opendir `%s'", dir);
|
||||||
|
first = prev = NULL;
|
||||||
|
while ((dent = readdir(dirp)) != NULL) {
|
||||||
|
if (strcmp(dent->d_name, "..") == 0)
|
||||||
|
continue;
|
||||||
|
if (debug & DEBUG_WALK_DIR_NODE)
|
||||||
|
printf("scanning %s/%s\n", dir, dent->d_name);
|
||||||
|
if (snprintf(path, sizeof(path), "%s/%s", dir, dent->d_name)
|
||||||
|
>= sizeof(path))
|
||||||
|
errx(1, "Pathname too long.");
|
||||||
|
if (lstat(path, &stbuf) == -1)
|
||||||
|
err(1, "Can't lstat `%s'", path);
|
||||||
|
if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
|
||||||
|
if (debug & DEBUG_WALK_DIR_NODE)
|
||||||
|
printf(" skipping socket %s\n", path);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cur = create_fsnode(dent->d_name, &stbuf);
|
||||||
|
cur->parent = parent;
|
||||||
|
if (strcmp(dent->d_name, ".") == 0) {
|
||||||
|
/* ensure "." is at the start of the list */
|
||||||
|
cur->next = first;
|
||||||
|
first = cur;
|
||||||
|
if (! prev)
|
||||||
|
prev = cur;
|
||||||
|
} else { /* not "." */
|
||||||
|
if (prev)
|
||||||
|
prev->next = cur;
|
||||||
|
prev = cur;
|
||||||
|
if (!first)
|
||||||
|
first = cur;
|
||||||
|
if (S_ISDIR(cur->type)) {
|
||||||
|
cur->child = walk_dir(path, cur);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cur->statbuf.st_nlink > 1) {
|
||||||
|
cur->dup = link_check(cur);
|
||||||
|
if (cur->dup)
|
||||||
|
cur->dup->nlink++;
|
||||||
|
}
|
||||||
|
if (S_ISLNK(cur->type)) {
|
||||||
|
char slink[PATH_MAX+1];
|
||||||
|
int llen;
|
||||||
|
|
||||||
|
llen = readlink(path, slink, PATH_MAX);
|
||||||
|
if (llen == -1)
|
||||||
|
err(1, "Readlink `%s'", path);
|
||||||
|
slink[llen] = '\0';
|
||||||
|
if ((cur->symlink = strdup(slink)) == NULL)
|
||||||
|
err(1, "Memory allocation error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (cur = first; cur != NULL; cur = cur->next)
|
||||||
|
cur->first = first;
|
||||||
|
if (closedir(dirp) == -1)
|
||||||
|
err(1, "Can't closedir `%s'", dir);
|
||||||
|
return (first);
|
||||||
|
}
|
||||||
|
|
||||||
|
static fsnode *
|
||||||
|
create_fsnode(const char *name, struct stat *statbuf)
|
||||||
|
{
|
||||||
|
fsnode *cur;
|
||||||
|
|
||||||
|
if ((cur = calloc(1, sizeof(fsnode))) == NULL ||
|
||||||
|
(cur->name = strdup(name)) == NULL)
|
||||||
|
err(1, "Memory allocation error");
|
||||||
|
cur->statbuf = *statbuf;
|
||||||
|
cur->type = (cur->statbuf.st_mode & S_IFMT);
|
||||||
|
cur->nlink = 1;
|
||||||
|
return (cur);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* apply_specfile --
|
||||||
|
* read in the mtree(8) specfile, and apply it to the tree
|
||||||
|
* at dir,parent. parameters in parent on equivalent types
|
||||||
|
* will be changed to those found in specfile, and missing
|
||||||
|
* entries will be added.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
apply_specfile(const char *specfile, const char *dir, fsnode *parent)
|
||||||
|
{
|
||||||
|
struct timeval start;
|
||||||
|
FILE *fp;
|
||||||
|
NODE *root;
|
||||||
|
|
||||||
|
assert(specfile != NULL);
|
||||||
|
assert(parent != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_APPLY_SPECFILE)
|
||||||
|
printf("apply_specfile: %s, %s %p\n", specfile, dir, parent);
|
||||||
|
|
||||||
|
/* read in the specfile */
|
||||||
|
if ((fp = fopen(specfile, "r")) == NULL)
|
||||||
|
err(1, "Can't open `%s'", specfile);
|
||||||
|
TIMER_START(start);
|
||||||
|
root = spec(fp);
|
||||||
|
TIMER_RESULTS(start, "spec");
|
||||||
|
if (fclose(fp) == EOF)
|
||||||
|
err(1, "Can't close `%s'", specfile);
|
||||||
|
|
||||||
|
/* perform some sanity checks */
|
||||||
|
if (root == NULL)
|
||||||
|
errx(1, "Specfile `%s' did not contain a tree", specfile);
|
||||||
|
assert(strcmp(root->name, ".") == 0);
|
||||||
|
assert(root->type == F_DIR);
|
||||||
|
|
||||||
|
/* merge in the changes */
|
||||||
|
apply_specdir(dir, root, parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode)
|
||||||
|
{
|
||||||
|
char path[MAXPATHLEN + 1];
|
||||||
|
NODE *curnode;
|
||||||
|
fsnode *curfsnode;
|
||||||
|
|
||||||
|
assert(specnode != NULL);
|
||||||
|
assert(dirnode != NULL);
|
||||||
|
|
||||||
|
if (debug & DEBUG_APPLY_SPECFILE)
|
||||||
|
printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode);
|
||||||
|
|
||||||
|
if (specnode->type != F_DIR)
|
||||||
|
errx(1, "Specfile node `%s/%s' is not a directory",
|
||||||
|
dir, specnode->name);
|
||||||
|
if (dirnode->type != S_IFDIR)
|
||||||
|
errx(1, "Directory node `%s/%s' is not a directory",
|
||||||
|
dir, dirnode->name);
|
||||||
|
|
||||||
|
apply_specentry(dir, specnode, dirnode);
|
||||||
|
|
||||||
|
/* now walk specnode->child matching up with dirnode */
|
||||||
|
for (curnode = specnode->child; curnode != NULL;
|
||||||
|
curnode = curnode->next) {
|
||||||
|
if (debug & DEBUG_APPLY_SPECENTRY)
|
||||||
|
printf("apply_specdir: spec %s\n",
|
||||||
|
curnode->name);
|
||||||
|
for (curfsnode = dirnode->next; curfsnode != NULL;
|
||||||
|
curfsnode = curfsnode->next) {
|
||||||
|
if (debug & DEBUG_APPLY_SPECENTRY)
|
||||||
|
printf("apply_specdir: dirent %s\n",
|
||||||
|
curfsnode->name);
|
||||||
|
if (strcmp(curnode->name, curfsnode->name) == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (curfsnode == NULL) { /* need new entry */
|
||||||
|
struct stat stbuf;
|
||||||
|
|
||||||
|
/* check that enough info is provided */
|
||||||
|
#define NODETEST(t, m) \
|
||||||
|
if (!(t)) \
|
||||||
|
errx(1, "`%s/%s': %s not provided", \
|
||||||
|
dir, curnode->name, m)
|
||||||
|
NODETEST(curnode->flags & F_TYPE, "type");
|
||||||
|
NODETEST(curnode->flags & F_MODE, "mode");
|
||||||
|
/* XXX: require F_TIME ? */
|
||||||
|
NODETEST(curnode->flags & F_GID ||
|
||||||
|
curnode->flags & F_GNAME, "group");
|
||||||
|
NODETEST(curnode->flags & F_UID ||
|
||||||
|
curnode->flags & F_UNAME, "user");
|
||||||
|
if (curnode->type == F_BLOCK || curnode->type == F_CHAR)
|
||||||
|
NODETEST(curnode->flags & F_DEV,
|
||||||
|
"device number");
|
||||||
|
#undef NODETEST
|
||||||
|
|
||||||
|
if (debug & DEBUG_APPLY_SPECFILE)
|
||||||
|
printf("apply_specdir: adding %s\n",
|
||||||
|
curnode->name);
|
||||||
|
/* build minimal fsnode */
|
||||||
|
memset(&stbuf, 0, sizeof(stbuf));
|
||||||
|
stbuf.st_mode = nodetoino(curnode->type);
|
||||||
|
stbuf.st_mtime = stbuf.st_atime =
|
||||||
|
stbuf.st_ctime = start_time.tv_sec;
|
||||||
|
stbuf.st_mtimensec = stbuf.st_atimensec =
|
||||||
|
stbuf.st_ctimensec = start_time.tv_nsec;
|
||||||
|
curfsnode = create_fsnode(curnode->name, &stbuf);
|
||||||
|
curfsnode->parent = dirnode->parent;
|
||||||
|
curfsnode->first = dirnode;
|
||||||
|
curfsnode->next = dirnode->next;
|
||||||
|
dirnode->next = curfsnode;
|
||||||
|
if (curfsnode->type == S_IFDIR) {
|
||||||
|
/* for dirs, make "." entry as well */
|
||||||
|
curfsnode->child = create_fsnode(".", &stbuf);
|
||||||
|
curfsnode->child->parent = curfsnode;
|
||||||
|
curfsnode->child->first = curfsnode->child;
|
||||||
|
}
|
||||||
|
if (curfsnode->type == S_IFLNK) {
|
||||||
|
assert(specnode->slink != NULL);
|
||||||
|
/* for symlinks, copy the target */
|
||||||
|
if ((curfsnode->symlink =
|
||||||
|
strdup(curnode->slink)) == NULL)
|
||||||
|
err(1, "Memory allocation error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apply_specentry(dir, curnode, curfsnode);
|
||||||
|
if (curnode->type == F_DIR) {
|
||||||
|
if (curfsnode->type != S_IFDIR)
|
||||||
|
errx(1, "`%s/%s' is not a directory",
|
||||||
|
dir, curfsnode->name);
|
||||||
|
assert (curfsnode->child != NULL);
|
||||||
|
if (snprintf(path, sizeof(path), "%s/%s",
|
||||||
|
dir, curnode->name) >= sizeof(path))
|
||||||
|
errx(1, "Pathname too long.");
|
||||||
|
apply_specdir(path, curnode, curfsnode->child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode)
|
||||||
|
{
|
||||||
|
|
||||||
|
assert(specnode != NULL);
|
||||||
|
assert(dirnode != NULL);
|
||||||
|
|
||||||
|
if (nodetoino(specnode->type) != dirnode->type)
|
||||||
|
errx(1, "`%s/%s' type mismatch: specfile %s, tree %s",
|
||||||
|
dir, specnode->name, inode_type(nodetoino(specnode->type)),
|
||||||
|
inode_type(dirnode->type));
|
||||||
|
|
||||||
|
if (debug & DEBUG_APPLY_SPECENTRY)
|
||||||
|
printf("apply_specentry: %s/%s\n", dir, dirnode->name);
|
||||||
|
|
||||||
|
#define ASEPRINT(t, b, o, n) \
|
||||||
|
if (debug & DEBUG_APPLY_SPECENTRY) \
|
||||||
|
printf("\t\t\tchanging %s from " b " to " b "\n", \
|
||||||
|
t, o, n)
|
||||||
|
|
||||||
|
if (specnode->flags & (F_GID | F_GNAME)) {
|
||||||
|
ASEPRINT("gid", "%d",
|
||||||
|
dirnode->statbuf.st_gid, specnode->st_gid);
|
||||||
|
dirnode->statbuf.st_gid = specnode->st_gid;
|
||||||
|
}
|
||||||
|
if (specnode->flags & F_MODE) {
|
||||||
|
ASEPRINT("mode", "%#o",
|
||||||
|
dirnode->statbuf.st_mode & ALLPERMS, specnode->st_mode);
|
||||||
|
dirnode->statbuf.st_mode &= ~ALLPERMS;
|
||||||
|
dirnode->statbuf.st_mode |= (specnode->st_mode & ALLPERMS);
|
||||||
|
}
|
||||||
|
/* XXX: ignoring F_NLINK for now */
|
||||||
|
if (specnode->flags & F_SIZE) {
|
||||||
|
ASEPRINT("size", "%lld",
|
||||||
|
(long long)dirnode->statbuf.st_size,
|
||||||
|
(long long)specnode->st_size);
|
||||||
|
dirnode->statbuf.st_size = specnode->st_size;
|
||||||
|
}
|
||||||
|
if (specnode->flags & F_SLINK) {
|
||||||
|
assert(dirnode->symlink != NULL);
|
||||||
|
assert(specnode->slink != NULL);
|
||||||
|
ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink);
|
||||||
|
free(dirnode->symlink);
|
||||||
|
if ((dirnode->symlink = strdup(specnode->slink)) == NULL)
|
||||||
|
err(1, "Memory allocation error");
|
||||||
|
}
|
||||||
|
if (specnode->flags & F_TIME) {
|
||||||
|
ASEPRINT("time", "%ld",
|
||||||
|
dirnode->statbuf.st_mtime, specnode->st_mtime);
|
||||||
|
dirnode->statbuf.st_mtime = specnode->st_mtime;
|
||||||
|
dirnode->statbuf.st_mtimensec = specnode->st_mtimensec;
|
||||||
|
dirnode->statbuf.st_atime = specnode->st_mtime;
|
||||||
|
dirnode->statbuf.st_atimensec = specnode->st_mtimensec;
|
||||||
|
dirnode->statbuf.st_ctime = start_time.tv_sec;
|
||||||
|
dirnode->statbuf.st_ctimensec = start_time.tv_nsec;
|
||||||
|
}
|
||||||
|
if (specnode->flags & (F_UID | F_UNAME)) {
|
||||||
|
ASEPRINT("uid", "%d",
|
||||||
|
dirnode->statbuf.st_uid, specnode->st_uid);
|
||||||
|
dirnode->statbuf.st_uid = specnode->st_uid;
|
||||||
|
}
|
||||||
|
if (specnode->flags & F_FLAGS) {
|
||||||
|
ASEPRINT("flags", "%#lX",
|
||||||
|
(u_long)dirnode->statbuf.st_flags,
|
||||||
|
(u_long)specnode->st_flags);
|
||||||
|
dirnode->statbuf.st_flags = specnode->st_flags;
|
||||||
|
}
|
||||||
|
if (specnode->flags & F_DEV) {
|
||||||
|
ASEPRINT("rdev", "%#x",
|
||||||
|
dirnode->statbuf.st_rdev, specnode->st_rdev);
|
||||||
|
dirnode->statbuf.st_rdev = specnode->st_rdev;
|
||||||
|
}
|
||||||
|
#undef ASEPRINT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* dump_fsnodes --
|
||||||
|
* dump the fsnodes from `cur', based in the directory `dir'
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
dump_fsnodes(const char *dir, fsnode *root)
|
||||||
|
{
|
||||||
|
fsnode *cur;
|
||||||
|
char path[MAXPATHLEN + 1];
|
||||||
|
|
||||||
|
assert (dir != NULL);
|
||||||
|
printf("dump_fsnodes: %s %p\n", dir, root);
|
||||||
|
for (cur = root; cur != NULL; cur = cur->next) {
|
||||||
|
if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
|
||||||
|
>= sizeof(path))
|
||||||
|
errx(1, "Pathname too long.");
|
||||||
|
|
||||||
|
if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
|
||||||
|
printf("cur=%8p parent=%8p first=%8p ",
|
||||||
|
cur, cur->parent, cur->first);
|
||||||
|
printf("%7s: %s", inode_type(cur->type), path);
|
||||||
|
if (S_ISLNK(cur->type)) {
|
||||||
|
assert(cur->symlink != NULL);
|
||||||
|
printf(" -> %s", cur->symlink);
|
||||||
|
} else {
|
||||||
|
assert (cur->symlink == NULL);
|
||||||
|
}
|
||||||
|
if (cur->dup != NULL) {
|
||||||
|
printf(", hard-linked to %s", cur->dup->name);
|
||||||
|
}
|
||||||
|
if (cur->nlink > 1)
|
||||||
|
printf(", nlinks=%d", cur->nlink);
|
||||||
|
putchar('\n');
|
||||||
|
|
||||||
|
if (cur->child) {
|
||||||
|
assert (cur->type == S_IFDIR);
|
||||||
|
dump_fsnodes(path, cur->child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printf("dump_fsnodes: finished %s\n", dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inode_type --
|
||||||
|
* for a given inode type `mode', return a descriptive string.
|
||||||
|
* for most cases, uses inotype() from mtree/misc.c
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
inode_type(mode_t mode)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (mode & S_IFLNK)
|
||||||
|
return ("symlink"); /* inotype() returns "link"... */
|
||||||
|
return (inotype(mode));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int32_t dev;
|
||||||
|
int32_t ino;
|
||||||
|
fsnode *dup;
|
||||||
|
} dupnode;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* link_check --
|
||||||
|
* return pointer to fsnode matching `entry's st_ino & st_dev if it exists,
|
||||||
|
* otherwise add `entry' to table and return NULL
|
||||||
|
*/
|
||||||
|
static fsnode *
|
||||||
|
link_check(fsnode *entry)
|
||||||
|
{
|
||||||
|
static dupnode *dups;
|
||||||
|
static int ndups, maxdups;
|
||||||
|
|
||||||
|
int i;
|
||||||
|
|
||||||
|
assert (entry != NULL);
|
||||||
|
|
||||||
|
/* XXX; maybe traverse in reverse for speed? */
|
||||||
|
for (i = 0; i < ndups; i++) {
|
||||||
|
if (dups[i].dev == entry->statbuf.st_dev &&
|
||||||
|
dups[i].ino == entry->statbuf.st_ino) {
|
||||||
|
if (debug & DEBUG_WALK_DIR_LINKCHECK)
|
||||||
|
printf(
|
||||||
|
"link_check: %s (%d,%d) linked to %s\n",
|
||||||
|
entry->name, entry->statbuf.st_dev,
|
||||||
|
entry->statbuf.st_ino, dups[i].dup->name);
|
||||||
|
return (dups[i].dup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (debug & DEBUG_WALK_DIR_LINKCHECK)
|
||||||
|
printf("link_check: no match for %s (%d, %d)\n",
|
||||||
|
entry->name, entry->statbuf.st_dev, entry->statbuf.st_ino);
|
||||||
|
if (ndups == maxdups) {
|
||||||
|
maxdups += 128;
|
||||||
|
if ((dups = realloc(dups, sizeof(dupnode) * maxdups)) == NULL)
|
||||||
|
err(1, "Memory allocation error");
|
||||||
|
}
|
||||||
|
dups[ndups].dev = entry->statbuf.st_dev;
|
||||||
|
dups[ndups].ino = entry->statbuf.st_ino;
|
||||||
|
dups[ndups].dup = entry;
|
||||||
|
ndups++;
|
||||||
|
|
||||||
|
return (NULL);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user