930 lines
26 KiB
C
930 lines
26 KiB
C
/*
|
|
* Utility for dealing with 2.xBSD filesystem images.
|
|
*
|
|
* Copyright (C) 2006-2014 Serge Vakulenko, <serge@vak.ru>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software
|
|
* and its documentation for any purpose and without fee is hereby
|
|
* granted, provided that the above copyright notice appear in all
|
|
* copies and that both that the copyright notice and this
|
|
* permission notice and warranty disclaimer appear in supporting
|
|
* documentation, and that the name of the author not be used in
|
|
* advertising or publicity pertaining to distribution of the
|
|
* software without specific, written prior permission.
|
|
*
|
|
* The author disclaim all warranties with regard to this
|
|
* software, including all implied warranties of merchantability
|
|
* and fitness. In no event shall the author be liable for any
|
|
* special, indirect or consequential damages or any damages
|
|
* whatsoever resulting from loss of use, data or profits, whether
|
|
* in an action of contract, negligence or other tortious action,
|
|
* arising out of or in connection with the use or performance of
|
|
* this software.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <errno.h>
|
|
#include <getopt.h>
|
|
#include <fts.h>
|
|
#include "bsdfs.h"
|
|
#include "manifest.h"
|
|
|
|
int verbose;
|
|
int extract;
|
|
int add;
|
|
int newfs;
|
|
int check;
|
|
int fix;
|
|
int mount;
|
|
int scan;
|
|
int repartition;
|
|
unsigned kbytes;
|
|
unsigned swap_kbytes;
|
|
unsigned pindex;
|
|
|
|
static const char *program_version =
|
|
"BSD 2.x file system utility, version 1.2\n"
|
|
"Copyright (C) 2011-2014 Serge Vakulenko";
|
|
|
|
static const char *program_bug_address = "<serge@vak.ru>";
|
|
|
|
static struct option program_options[] = {
|
|
{ "help", no_argument, 0, 'h' },
|
|
{ "version", no_argument, 0, 'V' },
|
|
{ "verbose", no_argument, 0, 'v' },
|
|
{ "add", no_argument, 0, 'a' },
|
|
{ "extract", no_argument, 0, 'x' },
|
|
{ "check", no_argument, 0, 'c' },
|
|
{ "fix", no_argument, 0, 'f' },
|
|
{ "mount", no_argument, 0, 'm' },
|
|
{ "scan", no_argument, 0, 'S' },
|
|
{ "new", no_argument, 0, 'n' },
|
|
{ "size", required_argument, 0, 's' },
|
|
{ "manifest", required_argument, 0, 'M' },
|
|
{ "partition", required_argument, 0, 'p' },
|
|
{ "repartition", required_argument, 0, 'r' },
|
|
{ 0 }
|
|
};
|
|
|
|
static void print_help (char *progname)
|
|
{
|
|
char *p = strrchr (progname, '/');
|
|
if (p)
|
|
progname = p+1;
|
|
|
|
printf ("%s\n", program_version);
|
|
printf ("This program is free software; it comes with ABSOLUTELY NO WARRANTY;\n"
|
|
"see the BSD 3-Clause License for more details.\n");
|
|
printf ("\n");
|
|
printf ("Usage:\n");
|
|
printf (" %s [--verbose] [--partition=n] disk.img\n", progname);
|
|
printf (" %s --check [--fix] [--partition=n] disk.img\n", progname);
|
|
printf (" %s --new [--size=kbytes | --partition=n] [--manifest=file] disk.img [dir]\n", progname);
|
|
#ifdef ENABLE_FUSE
|
|
printf (" %s --mount [--partition=n] disk.img dir\n", progname);
|
|
#endif
|
|
printf (" %s --add [--partition=n] disk.img files...\n", progname);
|
|
printf (" %s --extract [--partition=n] disk.img\n", progname);
|
|
printf (" %s --repartition=format disk.img\n", progname);
|
|
printf (" %s --scan dir > file\n", progname);
|
|
printf ("\n");
|
|
printf ("Options:\n");
|
|
printf (" -c, --check Check filesystem, use -c -f to fix.\n");
|
|
printf (" -f, --fix Fix bugs in filesystem.\n");
|
|
printf (" -n, --new Create new filesystem; add files from\n");
|
|
printf (" specified directory and manifest (optional)\n");
|
|
printf (" -s NUM, --size=NUM Size of filesystem in kbytes.\n");
|
|
printf (" -M file, --manifest=file\n");
|
|
printf (" List of files and attributes to create.\n");
|
|
#ifdef ENABLE_FUSE
|
|
printf (" -m, --mount Mount the filesystem.\n");
|
|
#endif
|
|
printf (" -a, --add Add files to filesystem.\n");
|
|
printf (" -x, --extract Extract all files.\n");
|
|
printf (" -r format, --repartition=format\n");
|
|
printf (" Install new partition table.\n");
|
|
printf (" -p NUM, --partition=NUM\n");
|
|
printf (" Select a partition.\n");
|
|
printf (" -S, --scan Create a manifest from directory contents.\n");
|
|
printf (" -v, --verbose Be verbose.\n");
|
|
printf (" -V, --version Print version information and then exit.\n");
|
|
printf (" -h, --help Print this message.\n");
|
|
printf ("\n");
|
|
printf ("Report bugs to \"%s\".\n", program_bug_address);
|
|
}
|
|
|
|
void print_inode (fs_inode_t *inode,
|
|
char *dirname, char *filename, FILE *out)
|
|
{
|
|
fprintf (out, "%s/%s", dirname, filename);
|
|
switch (inode->mode & INODE_MODE_FMT) {
|
|
case INODE_MODE_FDIR:
|
|
if (filename[0] != 0)
|
|
fprintf (out, "/");
|
|
break;
|
|
case INODE_MODE_FCHR:
|
|
fprintf (out, " - char %d %d",
|
|
inode->addr[1] >> 8, inode->addr[1] & 0xff);
|
|
break;
|
|
case INODE_MODE_FBLK:
|
|
fprintf (out, " - block %d %d",
|
|
inode->addr[1] >> 8, inode->addr[1] & 0xff);
|
|
break;
|
|
default:
|
|
fprintf (out, " - %lu bytes", inode->size);
|
|
break;
|
|
}
|
|
fprintf (out, "\n");
|
|
}
|
|
|
|
void print_indirect_block (fs_t *fs, unsigned int bno, FILE *out)
|
|
{
|
|
unsigned short nb;
|
|
unsigned char data [BSDFS_BSIZE];
|
|
int i;
|
|
|
|
fprintf (out, " [%d]", bno);
|
|
if (! fs_read_block (fs, bno, data)) {
|
|
fprintf (stderr, "read error at block %d\n", bno);
|
|
return;
|
|
}
|
|
for (i=0; i<BSDFS_BSIZE-2; i+=2) {
|
|
nb = data [i+1] << 8 | data [i];
|
|
if (nb)
|
|
fprintf (out, " %d", nb);
|
|
}
|
|
}
|
|
|
|
void print_double_indirect_block (fs_t *fs, unsigned int bno, FILE *out)
|
|
{
|
|
unsigned short nb;
|
|
unsigned char data [BSDFS_BSIZE];
|
|
int i;
|
|
|
|
fprintf (out, " [%d]", bno);
|
|
if (! fs_read_block (fs, bno, data)) {
|
|
fprintf (stderr, "read error at block %d\n", bno);
|
|
return;
|
|
}
|
|
for (i=0; i<BSDFS_BSIZE-2; i+=2) {
|
|
nb = data [i+1] << 8 | data [i];
|
|
if (nb)
|
|
print_indirect_block (fs, nb, out);
|
|
}
|
|
}
|
|
|
|
void print_triple_indirect_block (fs_t *fs, unsigned int bno, FILE *out)
|
|
{
|
|
unsigned short nb;
|
|
unsigned char data [BSDFS_BSIZE];
|
|
int i;
|
|
|
|
fprintf (out, " [%d]", bno);
|
|
if (! fs_read_block (fs, bno, data)) {
|
|
fprintf (stderr, "read error at block %d\n", bno);
|
|
return;
|
|
}
|
|
for (i=0; i<BSDFS_BSIZE-2; i+=2) {
|
|
nb = data [i+1] << 8 | data [i];
|
|
if (nb)
|
|
print_indirect_block (fs, nb, out);
|
|
}
|
|
}
|
|
|
|
void print_inode_blocks (fs_inode_t *inode, FILE *out)
|
|
{
|
|
int i;
|
|
|
|
if ((inode->mode & INODE_MODE_FMT) == INODE_MODE_FCHR ||
|
|
(inode->mode & INODE_MODE_FMT) == INODE_MODE_FBLK)
|
|
return;
|
|
|
|
fprintf (out, " ");
|
|
for (i=0; i<NDADDR; ++i) {
|
|
if (inode->addr[i] == 0)
|
|
continue;
|
|
fprintf (out, " %d", inode->addr[i]);
|
|
}
|
|
if (inode->addr[NDADDR] != 0)
|
|
print_indirect_block (inode->fs, inode->addr[NDADDR], out);
|
|
if (inode->addr[NDADDR+1] != 0)
|
|
print_double_indirect_block (inode->fs,
|
|
inode->addr[NDADDR+1], out);
|
|
if (inode->addr[NDADDR+2] != 0)
|
|
print_triple_indirect_block (inode->fs,
|
|
inode->addr[NDADDR+2], out);
|
|
fprintf (out, "\n");
|
|
}
|
|
|
|
void extract_inode (fs_inode_t *inode, char *path)
|
|
{
|
|
int fd, n, mode;
|
|
unsigned long offset;
|
|
unsigned char data [BSDFS_BSIZE];
|
|
|
|
/* Allow read/write for user. */
|
|
mode = (inode->mode & 0777) | 0600;
|
|
fd = open (path, O_CREAT | O_RDWR, mode);
|
|
if (fd < 0) {
|
|
perror (path);
|
|
return;
|
|
}
|
|
for (offset = 0; offset < inode->size; offset += BSDFS_BSIZE) {
|
|
n = inode->size - offset;
|
|
if (n > BSDFS_BSIZE)
|
|
n = BSDFS_BSIZE;
|
|
if (! fs_inode_read (inode, offset, data, n)) {
|
|
fprintf (stderr, "%s: read error at offset %ld\n",
|
|
path, offset);
|
|
break;
|
|
}
|
|
if (write (fd, data, n) != n) {
|
|
fprintf (stderr, "%s: write error\n", path);
|
|
break;
|
|
}
|
|
}
|
|
close (fd);
|
|
}
|
|
|
|
void extract_symlink (fs_inode_t *inode, char *path)
|
|
{
|
|
char data [BSDFS_BSIZE];
|
|
|
|
if (inode->size >= BSDFS_BSIZE) {
|
|
fprintf (stderr, "%s: too long symlink\n", path);
|
|
return;
|
|
}
|
|
if (! fs_inode_read (inode, 0, (unsigned char*)data, inode->size)) {
|
|
fprintf (stderr, "%s: error reading symlink\n", path);
|
|
return;
|
|
}
|
|
data[inode->size] = 0;
|
|
if (symlink(data, path) < 0) {
|
|
perror (path);
|
|
return;
|
|
}
|
|
}
|
|
|
|
void extractor (fs_inode_t *dir, fs_inode_t *inode,
|
|
char *dirname, char *filename, void *arg)
|
|
{
|
|
FILE *out = arg;
|
|
char *path, *relpath;
|
|
|
|
if (verbose)
|
|
print_inode (inode, dirname, filename, out);
|
|
|
|
if ((inode->mode & INODE_MODE_FMT) != INODE_MODE_FDIR &&
|
|
(inode->mode & INODE_MODE_FMT) != INODE_MODE_FREG &&
|
|
(inode->mode & INODE_MODE_FMT) != INODE_MODE_FLNK)
|
|
return;
|
|
|
|
path = alloca (strlen (dirname) + strlen (filename) + 2);
|
|
strcpy (path, dirname);
|
|
strcat (path, "/");
|
|
strcat (path, filename);
|
|
for (relpath=path; *relpath == '/'; relpath++)
|
|
continue;
|
|
|
|
switch (inode->mode & INODE_MODE_FMT) {
|
|
case INODE_MODE_FDIR:
|
|
if (mkdir (relpath, 0775) < 0 && errno != EEXIST)
|
|
perror (relpath);
|
|
/* Scan subdirectory. */
|
|
fs_directory_scan (inode, path, extractor, arg);
|
|
break;
|
|
case INODE_MODE_FREG:
|
|
extract_inode (inode, relpath);
|
|
break;
|
|
case INODE_MODE_FLNK:
|
|
extract_symlink (inode, relpath);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void scanner (fs_inode_t *dir, fs_inode_t *inode,
|
|
char *dirname, char *filename, void *arg)
|
|
{
|
|
FILE *out = arg;
|
|
char *path;
|
|
|
|
print_inode (inode, dirname, filename, out);
|
|
|
|
if (verbose > 1) {
|
|
/* Print a list of blocks. */
|
|
print_inode_blocks (inode, out);
|
|
if (verbose > 2) {
|
|
fs_inode_print (inode, out);
|
|
printf ("--------\n");
|
|
}
|
|
}
|
|
if ((inode->mode & INODE_MODE_FMT) == INODE_MODE_FDIR &&
|
|
inode->number != BSDFS_ROOT_INODE) {
|
|
/* Scan subdirectory. */
|
|
path = alloca (strlen (dirname) + strlen (filename) + 2);
|
|
strcpy (path, dirname);
|
|
strcat (path, "/");
|
|
strcat (path, filename);
|
|
fs_directory_scan (inode, path, scanner, arg);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Create a directory.
|
|
*/
|
|
void add_directory (fs_t *fs, char *name, int mode, int owner, int group)
|
|
{
|
|
fs_inode_t dir, parent;
|
|
char buf [BSDFS_BSIZE], *p;
|
|
|
|
/* Open parent directory. */
|
|
strcpy (buf, name);
|
|
p = strrchr (buf, '/');
|
|
if (p)
|
|
*p = 0;
|
|
else
|
|
*buf = 0;
|
|
if (! fs_inode_lookup (fs, &parent, buf)) {
|
|
fprintf (stderr, "%s: cannot open directory\n", buf);
|
|
return;
|
|
}
|
|
|
|
/* Create directory. */
|
|
mode &= 07777;
|
|
mode |= INODE_MODE_FDIR;
|
|
int done = fs_inode_create (fs, &dir, name, mode);
|
|
if (! done) {
|
|
fprintf (stderr, "%s: directory inode create failed\n", name);
|
|
return;
|
|
}
|
|
if (done == 1) {
|
|
/* The directory already existed. */
|
|
return;
|
|
}
|
|
dir.uid = owner;
|
|
dir.gid = group;
|
|
fs_inode_save (&dir, 1);
|
|
|
|
/* Make parent link '..' */
|
|
strcpy (buf, name);
|
|
strcat (buf, "/..");
|
|
if (! fs_inode_link (fs, &dir, buf, parent.number)) {
|
|
fprintf (stderr, "%s: dotdot link failed\n", name);
|
|
return;
|
|
}
|
|
if (! fs_inode_get (fs, &parent, parent.number)) {
|
|
fprintf (stderr, "inode %d: cannot open parent\n", parent.number);
|
|
return;
|
|
}
|
|
++parent.nlink;
|
|
fs_inode_save (&parent, 1);
|
|
/*printf ("*** inode %d: increment link counter to %d\n", parent.number, parent.nlink);*/
|
|
}
|
|
|
|
/*
|
|
* Create a device node.
|
|
*/
|
|
void add_device (fs_t *fs, char *name, int mode, int owner, int group,
|
|
int type, int majr, int minr)
|
|
{
|
|
fs_inode_t dev;
|
|
|
|
mode &= 07777;
|
|
mode |= (type == 'b') ? INODE_MODE_FBLK : INODE_MODE_FCHR;
|
|
if (! fs_inode_create (fs, &dev, name, mode)) {
|
|
fprintf (stderr, "%s: device inode create failed\n", name);
|
|
return;
|
|
}
|
|
dev.addr[1] = majr << 8 | minr;
|
|
dev.uid = owner;
|
|
dev.gid = group;
|
|
time (&dev.mtime);
|
|
fs_inode_save (&dev, 1);
|
|
}
|
|
|
|
/*
|
|
* Copy regular file to filesystem.
|
|
*/
|
|
void add_file (fs_t *fs, const char *path, const char *dirname,
|
|
int mode, int owner, int group)
|
|
{
|
|
fs_file_t file;
|
|
FILE *fd;
|
|
char accpath [BSDFS_BSIZE];
|
|
unsigned char data [BSDFS_BSIZE];
|
|
struct stat st;
|
|
int len;
|
|
|
|
if (dirname && *dirname) {
|
|
/* Concatenate directory name and file name. */
|
|
strcpy (accpath, dirname);
|
|
len = strlen (accpath);
|
|
if (accpath[len-1] != '/' && path[0] != '/')
|
|
strcat (accpath, "/");
|
|
strcat (accpath, path);
|
|
} else {
|
|
/* Use filename relative to current directory. */
|
|
strcpy (accpath, path);
|
|
}
|
|
fd = fopen (accpath, "r");
|
|
if (! fd) {
|
|
perror (accpath);
|
|
return;
|
|
}
|
|
fstat (fileno(fd), &st);
|
|
if (mode == -1)
|
|
mode = st.st_mode;
|
|
mode &= 07777;
|
|
mode |= INODE_MODE_FREG;
|
|
if (! fs_file_create (fs, &file, path, mode)) {
|
|
fprintf (stderr, "%s: cannot create\n", path);
|
|
return;
|
|
}
|
|
for (;;) {
|
|
len = fread (data, 1, sizeof (data), fd);
|
|
/* printf ("read %d bytes from %s\n", len, accpath);*/
|
|
if (len < 0)
|
|
perror (accpath);
|
|
if (len <= 0)
|
|
break;
|
|
if (! fs_file_write (&file, data, len)) {
|
|
fprintf (stderr, "%s: write error\n", path);
|
|
break;
|
|
}
|
|
}
|
|
file.inode.uid = owner;
|
|
file.inode.gid = group;
|
|
file.inode.mtime = st.st_mtime;
|
|
file.inode.dirty = 1;
|
|
fs_file_close (&file);
|
|
fclose (fd);
|
|
}
|
|
|
|
/*
|
|
* Create a symlink.
|
|
*/
|
|
void add_symlink (fs_t *fs, const char *path, const char *link,
|
|
int mode, int owner, int group)
|
|
{
|
|
fs_file_t file;
|
|
int len;
|
|
|
|
mode &= 07777;
|
|
mode |= INODE_MODE_FLNK;
|
|
if (! fs_file_create (fs, &file, path, mode)) {
|
|
fprintf (stderr, "%s: cannot create\n", path);
|
|
return;
|
|
}
|
|
len = strlen (link);
|
|
if (! fs_file_write (&file, (unsigned char*) link, len)) {
|
|
fprintf (stderr, "%s: write error\n", path);
|
|
return;
|
|
}
|
|
file.inode.uid = owner;
|
|
file.inode.gid = group;
|
|
time (&file.inode.mtime);
|
|
file.inode.dirty = 1;
|
|
fs_file_close (&file);
|
|
}
|
|
|
|
/*
|
|
* Create a hard link.
|
|
*/
|
|
void add_hardlink (fs_t *fs, const char *path, const char *link)
|
|
{
|
|
fs_inode_t source, target;
|
|
|
|
/* Find source. */
|
|
if (! fs_inode_lookup (fs, &source, link)) {
|
|
fprintf (stderr, "%s: link source not found\n", link);
|
|
return;
|
|
}
|
|
if ((source.mode & INODE_MODE_FMT) == INODE_MODE_FDIR) {
|
|
fprintf (stderr, "%s: cannot link directories\n", link);
|
|
return;
|
|
}
|
|
|
|
/* Create target link. */
|
|
if (! fs_inode_link (fs, &target, path, source.number)) {
|
|
fprintf (stderr, "%s: link failed\n", path);
|
|
return;
|
|
}
|
|
source.nlink++;
|
|
fs_inode_save (&source, 1);
|
|
}
|
|
|
|
/*
|
|
* Create a file/device/directory in the filesystem.
|
|
* When name is ended by slash as "name/", directory is created.
|
|
*/
|
|
void add_object (fs_t *fs, char *name)
|
|
{
|
|
int majr, minr;
|
|
char type;
|
|
char *p;
|
|
|
|
if (verbose) {
|
|
printf ("%s\n", name);
|
|
}
|
|
p = strrchr (name, '/');
|
|
if (p && p[1] == 0) {
|
|
*p = 0;
|
|
add_directory (fs, name, 0777, 0, 0);
|
|
return;
|
|
}
|
|
p = strrchr (name, '!');
|
|
if (p) {
|
|
*p++ = 0;
|
|
if (sscanf (p, "%c%d:%d", &type, &majr, &minr) != 3 ||
|
|
(type != 'c' && type != 'b') ||
|
|
majr < 0 || majr > 255 || minr < 0 || minr > 255) {
|
|
fprintf (stderr, "%s: invalid device specification\n", p);
|
|
fprintf (stderr, "expected c<major>:<minor> or b<major>:<minor>\n");
|
|
return;
|
|
}
|
|
add_device (fs, name, 0666, 0, 0, type, majr, minr);
|
|
return;
|
|
}
|
|
add_file (fs, name, 0, -1, 0, 0);
|
|
}
|
|
|
|
/*
|
|
* Add the contents from the specified directory.
|
|
* Use the optional manifest file.
|
|
*/
|
|
void add_contents (fs_t *fs, const char *dirname, const char *manifest)
|
|
{
|
|
manifest_t m;
|
|
void *cursor;
|
|
char *path, *link;
|
|
int filetype, mode, owner, group, majr, minr;
|
|
int ndirs = 0, nfiles = 0, nlinks = 0, nsymlinks = 0, ndevs = 0;
|
|
|
|
if (manifest) {
|
|
/* Load manifest from file. */
|
|
if (! manifest_load (&m, manifest)) {
|
|
fprintf (stderr, "%s: cannot read\n", manifest);
|
|
return;
|
|
}
|
|
} else {
|
|
/* Create manifest from directory contents. */
|
|
if (! manifest_scan (&m, dirname)) {
|
|
fprintf (stderr, "%s: cannot read\n", dirname);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* For every file in the manifest,
|
|
* add it to the target filesystem. */
|
|
cursor = 0;
|
|
while ((filetype = manifest_iterate (&m, &cursor, &path, &link, &mode,
|
|
&owner, &group, &majr, &minr)) != 0)
|
|
{
|
|
switch (filetype) {
|
|
case 'd':
|
|
add_directory (fs, path, mode, owner, group);
|
|
ndirs++;
|
|
break;
|
|
case 'f':
|
|
add_file (fs, path, dirname, mode, owner, group);
|
|
nfiles++;
|
|
break;
|
|
case 'l':
|
|
add_hardlink (fs, path, link);
|
|
nlinks++;
|
|
break;
|
|
case 's':
|
|
add_symlink (fs, path, link, mode, owner, group);
|
|
nsymlinks++;
|
|
break;
|
|
case 'b':
|
|
add_device (fs, path, mode, owner, group, 'b', majr, minr);
|
|
ndevs++;
|
|
break;
|
|
case 'c':
|
|
add_device (fs, path, mode, owner, group, 'c', majr, minr);
|
|
ndevs++;
|
|
break;
|
|
}
|
|
}
|
|
fs_sync (fs, 0);
|
|
fs_close (fs);
|
|
printf ("Installed %u directories, %u files, %u devices, %u links, %u symlinks\n",
|
|
ndirs, nfiles, ndevs, nlinks, nsymlinks);
|
|
}
|
|
|
|
void create_partition_table (const char *filename, char *format)
|
|
{
|
|
unsigned char buf [512], *entry;
|
|
unsigned pindex, offset;
|
|
int fd, activated = 0;
|
|
|
|
/* Initialize an empty partition table. */
|
|
memset (buf, 0, sizeof(buf));
|
|
buf[510] = 0x55;
|
|
buf[511] = 0xaa;
|
|
|
|
/* Parse format string and fill partition entries. */
|
|
offset = 2;
|
|
char *p = strtok (format, ":");
|
|
for (pindex=1; p && pindex<=4; pindex++) {
|
|
char *q, *endptr;
|
|
unsigned type, len;
|
|
|
|
/* Split into type and length. */
|
|
q = strchr (p, '=');
|
|
if (! q) {
|
|
fprintf (stderr, "%s: wrong format '%s' for partition %u\n",
|
|
filename, p, pindex);
|
|
exit(-1);
|
|
}
|
|
*q++ = 0;
|
|
|
|
/* Get length in sectors. */
|
|
len = strtoul (q, &endptr, 0);
|
|
if (*endptr == 'k')
|
|
len *= 2;
|
|
else if (*endptr == 'm' || *endptr == 'M')
|
|
len *= 2*1024;
|
|
else if (*endptr != '\0') {
|
|
fprintf (stderr, "%s: wrong length '%s' for partition %u\n",
|
|
filename, q, pindex);
|
|
exit(-1);
|
|
}
|
|
|
|
/* Get type of partition. */
|
|
if (strcmp (p, "fs") == 0)
|
|
type = 0xb7;
|
|
else if (strcmp (p, "swap") == 0)
|
|
type = 0xb8;
|
|
else {
|
|
type = strtoul (p, &endptr, 16);
|
|
if (*endptr != '\0') {
|
|
fprintf (stderr, "%s: wrong type '%s' for partition %u\n",
|
|
filename, p, pindex);
|
|
exit(-1);
|
|
}
|
|
}
|
|
printf ("Allocated partition %u, type %02x, start %u, size %u sectors\n",
|
|
pindex, type, offset, len);
|
|
|
|
/* Fill partition entry. */
|
|
entry = &buf [446 + (pindex-1)*16];
|
|
entry [4] = type;
|
|
*(unsigned*) &entry [8] = offset;
|
|
*(unsigned*) &entry [12] = len;
|
|
|
|
/* Make first FS active. */
|
|
if (type == 0xb7 && ! activated) {
|
|
entry [0] = 0x80;
|
|
activated = 1;
|
|
}
|
|
|
|
p = strtok (NULL, ":");
|
|
offset += len;
|
|
}
|
|
|
|
/* Open or create the file. */
|
|
fd = open (filename, O_CREAT | O_RDWR, 0666);
|
|
if (fd < 0) {
|
|
fprintf (stderr, "%s: cannot create file\n", filename);
|
|
exit(-1);
|
|
}
|
|
|
|
/* Write the partition table to file. */
|
|
if (write (fd, buf, 512) != 512) {
|
|
fprintf (stderr, "%s: error writing partition table\n", filename);
|
|
exit(-1);
|
|
}
|
|
}
|
|
|
|
int main (int argc, char **argv)
|
|
{
|
|
int i, key;
|
|
fs_t fs;
|
|
fs_inode_t inode;
|
|
manifest_t m;
|
|
const char *manifest = 0;
|
|
char *partition_format = 0;
|
|
|
|
for (;;) {
|
|
key = getopt_long (argc, argv, "vaxmSncfs:M:p:r:",
|
|
program_options, 0);
|
|
if (key == -1)
|
|
break;
|
|
switch (key) {
|
|
case 'v':
|
|
++verbose;
|
|
break;
|
|
case 'a':
|
|
++add;
|
|
break;
|
|
case 'x':
|
|
++extract;
|
|
break;
|
|
case 'n':
|
|
++newfs;
|
|
break;
|
|
case 'c':
|
|
++check;
|
|
break;
|
|
case 'f':
|
|
++fix;
|
|
break;
|
|
case 'm':
|
|
++mount;
|
|
break;
|
|
case 'S':
|
|
++scan;
|
|
break;
|
|
case 's':
|
|
kbytes = strtol (optarg, 0, 0);
|
|
break;
|
|
case 'M':
|
|
manifest = optarg;
|
|
break;
|
|
case 'p':
|
|
pindex = strtol (optarg, 0, 0);
|
|
if (pindex < 1 || pindex > 4) {
|
|
fprintf (stderr, "Incorrect partition index %u\n", pindex);
|
|
return -1;
|
|
}
|
|
break;
|
|
case 'r':
|
|
++repartition;
|
|
partition_format = optarg;
|
|
break;
|
|
case 'V':
|
|
printf ("%s\n", program_version);
|
|
return 0;
|
|
case 'h':
|
|
print_help (argv[0]);
|
|
return 0;
|
|
default:
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
}
|
|
i = optind;
|
|
if (extract + newfs + check + add + mount + scan + repartition > 1) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
|
|
if (newfs) {
|
|
/* Create new filesystem. */
|
|
if (i != argc-1 && i != argc-2) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
if (! pindex && kbytes < BSDFS_BSIZE * 10 / 1024) {
|
|
/* Need at least 10 blocks. */
|
|
if (! kbytes)
|
|
fprintf (stderr, "%s: size not specified\n", argv[i]);
|
|
else
|
|
fprintf (stderr, "%s: too small\n", argv[i]);
|
|
return -1;
|
|
}
|
|
|
|
if (! fs_create (&fs, argv[i], pindex ? -pindex : kbytes, 0)) {
|
|
fprintf (stderr, "%s: cannot create filesystem\n", argv[i]);
|
|
return -1;
|
|
}
|
|
if (pindex)
|
|
printf ("Created filesystem at partition %u of %s - %u kbytes\n",
|
|
pindex, argv[i], fs.part_nsectors/2);
|
|
else
|
|
printf ("Created filesystem %s - %u kbytes\n", argv[i], kbytes);
|
|
|
|
if (i == argc-2) {
|
|
/* Add the contents from the specified directory.
|
|
* Use the optional manifest file. */
|
|
add_contents (&fs, argv[i+1], manifest);
|
|
}
|
|
fs_close (&fs);
|
|
return 0;
|
|
}
|
|
|
|
if (check) {
|
|
/* Check filesystem for errors, and optionally fix them. */
|
|
if (i != argc-1) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
if (! fs_open (&fs, argv[i], fix, pindex)) {
|
|
fprintf (stderr, "%s: cannot open\n", argv[i]);
|
|
return -1;
|
|
}
|
|
fs_check (&fs);
|
|
fs_close (&fs);
|
|
return 0;
|
|
}
|
|
|
|
if (scan) {
|
|
/* Create a manifest from directory contents. */
|
|
if (i != argc-1) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
if (! manifest_scan (&m, argv[i])) {
|
|
fprintf (stderr, "%s: cannot read\n", argv[i]);
|
|
return -1;
|
|
}
|
|
manifest_print (&m);
|
|
return 0;
|
|
}
|
|
|
|
if (repartition) {
|
|
/* Install a new partition table. */
|
|
if (i != argc-1) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
create_partition_table (argv[i], partition_format);
|
|
return 0;
|
|
}
|
|
|
|
/* Add or extract or info. */
|
|
if (i >= argc) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
if (! fs_open (&fs, argv[i], (add + mount != 0), pindex)) {
|
|
fprintf (stderr, "%s: cannot open filesystem\n", argv[i]);
|
|
return -1;
|
|
}
|
|
|
|
if (extract) {
|
|
/* Extract all files to current directory. */
|
|
if (i != argc-1) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
if (! fs_inode_get (&fs, &inode, BSDFS_ROOT_INODE)) {
|
|
fprintf (stderr, "%s: cannot get inode 1\n", argv[i]);
|
|
return -1;
|
|
}
|
|
fs_directory_scan (&inode, "", extractor, (void*) stdout);
|
|
fs_close (&fs);
|
|
return 0;
|
|
}
|
|
|
|
if (add) {
|
|
/* Add files i+1..argc-1 to filesystem. */
|
|
if (i >= argc) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
while (++i < argc)
|
|
add_object (&fs, argv[i]);
|
|
fs_sync (&fs, 0);
|
|
fs_close (&fs);
|
|
return 0;
|
|
}
|
|
|
|
if (mount) {
|
|
/* Mount the filesystem. */
|
|
if (i != argc-2) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
#ifdef ENABLE_FUSE
|
|
return fs_mount(&fs, argv[i+1]);
|
|
#else
|
|
fprintf (stderr, "fsutil: -m, --mount options are not supported in this version.\n");
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
/* Print the structure of flesystem. */
|
|
if (i != argc-1) {
|
|
print_help (argv[0]);
|
|
return -1;
|
|
}
|
|
fs_print (&fs, stdout);
|
|
if (verbose) {
|
|
printf ("--------\n");
|
|
if (! fs_inode_get (&fs, &inode, BSDFS_ROOT_INODE)) {
|
|
fprintf (stderr, "%s: cannot get inode 1\n", argv[i]);
|
|
return -1;
|
|
}
|
|
printf ("/\n");
|
|
if (verbose > 1) {
|
|
/* Print a list of blocks. */
|
|
print_inode_blocks (&inode, stdout);
|
|
if (verbose > 2) {
|
|
fs_inode_print (&inode, stdout);
|
|
printf ("--------\n");
|
|
}
|
|
}
|
|
fs_directory_scan (&inode, "", scanner, (void*) stdout);
|
|
}
|
|
fs_close (&fs);
|
|
return 0;
|
|
}
|