blob: c7c1ed256ac695a122f9c10d0337e6bd961177f4 [file] [log] [blame]
/* Filesystem interface abstraction.
Copyright (C) 1996 Maurizio Plaza
1996,1997,1999 Jakub Jelinek
2001 Ben Collins
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
USA. */
#include <sys/types.h>
#include <silo.h>
#include <file.h>
#include <stringops.h>
#include <setjmp.h>
ext2_filsys fs = 0;
unsigned int bs;
void *filebuffer;
ino_t root, cwd;
static int do_gunzip = 0;
static unsigned int *gzipped_blocks;
static unsigned int *cur_gzipped_block;
static char *filelimit;
static int first_block;
static int block_no;
static int block_cnt;
static int last_blockcnt;
static char *gunzip_buffer;
static char *gunzip_inp;
static char *gunzip_endbuf;
static char *match;
/* Externally provided filesystem interfaces */
extern struct fs_ops ext2_fs_ops;
extern struct fs_ops iso_fs_ops;
extern struct fs_ops rom_fs_ops;
extern struct fs_ops ufs_fs_ops;
/* Array of our supported ops */
static struct fs_ops *silo_fs_ops[] = {
&ext2_fs_ops,
&iso_fs_ops,
&rom_fs_ops,
&ufs_fs_ops,
NULL,
};
static struct fs_ops *cur_ops;
extern jmp_buf gunzip_env;
void register_silo_inode (unsigned int mtime, unsigned int size,
unsigned int mode, unsigned int uid,
unsigned int gid, const char *name,
const char *symlink)
{
struct silo_inode *sino = (struct silo_inode *)filebuffer;
void *p;
int name_len = strlen(name);
if (match != NULL)
if (strlen(match) > name_len || strncmp(match, name, strlen(match)))
return;
strncpy((char *)sino->name, name, name_len);
sino->name[name_len] = 0;
sino->mtime = mtime;
sino->size = size;
sino->mode = mode;
sino->uid = uid;
sino->gid = gid;
p = strchr((char *)sino->name, 0) + 1;
if (symlink) {
strncpy ((char *)p, symlink, size);
((char *)p)[size] = 0;
p += size + 1;
}
if ((long)p & 3) p += 4 - ((long)p & 3);
sino->inolen = p - filebuffer;
filebuffer = p;
return;
}
static unsigned char get_gzip_input (void)
{
if (gunzip_inp < gunzip_endbuf)
return *gunzip_inp++;
{
int count = 1;
unsigned int first = *cur_gzipped_block++;
if (!first) {
printf ("\nDecompression error: ran out of compressed data\n");
longjmp (gunzip_env, 1);
}
if (first == 0xffffffff) {
/* Hole */
while (*cur_gzipped_block == 0xffffffff && count < 16) {
count++;
cur_gzipped_block++;
}
memset (gunzip_buffer, 0, count * bs);
} else {
while (*cur_gzipped_block == first + count && count < 16) {
count++;
cur_gzipped_block++;
}
if (io_channel_read_blk (fs->io, first, count, gunzip_buffer)) {
printf ("\nRead error\n");
longjmp (gunzip_env, 1);
}
}
gunzip_endbuf = gunzip_buffer + count * bs;
gunzip_inp = gunzip_buffer;
}
return *gunzip_inp++;
}
static void unget_gzip_input (void)
{
if (gunzip_inp > gunzip_buffer)
gunzip_inp--;
else {
printf ("\nInternal error\n");
longjmp (gunzip_env, 1);
}
}
static int gunzipped_len = 0;
static int do_rotate = 0;
static void rotate (void)
{
static int i = 0, slowdown = 0;
static char rot[] = "\\|/-";
if (!do_rotate)
return;
if (slowdown++ % 4)
return;
printf ("%c\b", rot[i % 4]);
i++;
}
int dump_block (blk_t * blocknr, int blockcnt)
{
if (blockcnt < 0)
return 0;
rotate();
if (!first_block && do_gunzip) {
if (blockcnt != last_blockcnt + 1) {
int i;
for (i = blockcnt - last_blockcnt - 1; i > 0; i--)
*cur_gzipped_block++ = 0xffffffff;
}
*cur_gzipped_block++ = *blocknr;
last_blockcnt = blockcnt;
return 0;
}
if (*blocknr || block_no) {
if (first_block || !*blocknr || blockcnt != last_blockcnt + 1 || (block_no && *blocknr != block_no + block_cnt)) {
if (first_block) {
block_no = *blocknr;
block_cnt = 1;
if (blockcnt) {
silo_fatal("File cannot have a hole at beginning");
return BLOCK_ABORT;
}
last_blockcnt = -1;
}
if ((char *)filebuffer + (block_cnt + ((*blocknr) ? (blockcnt - last_blockcnt - 1) : 0)) * bs > filelimit) {
silo_fatal("Image too large to fit in destination");
return BLOCK_ABORT;
}
if (block_cnt > 0 && io_channel_read_blk (fs->io, block_no, block_cnt, filebuffer))
return BLOCK_ABORT;
if (first_block) {
first_block = 0;
last_blockcnt = 0;
if (*(unsigned char *)filebuffer == 037 &&
(((unsigned char *)filebuffer)[1] == 0213 ||
((unsigned char *)filebuffer)[1] == 0236)) { /* gzip magic */
unsigned long sa = (unsigned long)&_start;
gunzip_buffer = malloc (16 * bs);
memcpy (gunzip_buffer, filebuffer, bs);
gzipped_blocks = (unsigned int *) malloc ((sa / 512) * sizeof (int));
cur_gzipped_block = gzipped_blocks;
*cur_gzipped_block++ = *blocknr;
printf ("Uncompressing image...\n");
return 0;
}
do_gunzip = 0;
filebuffer += bs;
block_no = 0;
block_cnt = 0;
return 0;
}
filebuffer += block_cnt * bs;
if (*blocknr && blockcnt && blockcnt != last_blockcnt + 1) {
memset (filebuffer, 0, (blockcnt - last_blockcnt - 1) * bs);
filebuffer += (blockcnt - last_blockcnt - 1) * bs;
}
block_no = 0;
}
if (*blocknr) {
if (!block_no) {
block_no = *blocknr;
block_cnt = 1;
} else
block_cnt++;
last_blockcnt = blockcnt;
}
}
return 0;
}
int dump_finish (void)
{
if (block_no) {
blk_t tmp = 0;
if (dump_block (&tmp, 0))
return 0;
}
if (do_gunzip) {
*cur_gzipped_block++ = 0;
cur_gzipped_block = gzipped_blocks + 1;
gunzip_endbuf = gunzip_buffer + bs;
gunzip_inp = gunzip_buffer;
if ((gunzipped_len = decompress (filebuffer, filelimit, get_gzip_input, unget_gzip_input)) < 0) {
free (gzipped_blocks);
free (gunzip_buffer);
return 0;
}
}
return 1;
}
static int dump_device_range (char *filename, char *bogusdev, int *len,
void (*lenfunc)(int, char **, char **))
{
/* Range of blocks on physical block device */
int start = 0, end = -1;
char *p;
bs = 512;
p = strchr (filename, '-');
filename++;
if (p && *filename >= '0' && *filename <= '9') {
*p = 0;
start = atoi (filename);
filename = p + 1;
p = strchr (filename, ']');
if (p && *filename >= '0' && *filename <= '9' && !p[1]) {
*p = 0;
end = atoi (filename);
}
}
if (end == -1) {
if (prom_vers == PROM_V0)
printf ("\nRanges of physical blocks are specified as {device_name}{partno}[xx-yy]"
"\nwhere {} means optional part and xx is the starting block"
"\n(chunk of 512 bytes) and yy end (not inclusive, i.e. yy won't be read)\n");
else
printf ("\nRanges of physical blocks are specified as {prom_path;}{partno}[xx-yy]"
"\nwhere {} means optional part, partno defaults to 0 (i.e. whole disk)"
"\nand xx is the starting block (chunk of 512 bytes) and yy end (not"
"\ninclusive, i.e. yy won't be read)\n");
return 0;
}
if (lenfunc)
(*lenfunc)((end - start) << 9, (char **)&filebuffer, (char **)&filelimit);
fs = (ext2_filsys) malloc (sizeof (struct struct_ext2_filsys));
if (fs) {
if (!((struct struct_io_manager *)(silo_io_manager))->open (bogusdev, 0, &fs->io)) {
blk_t tmp;
first_block = do_gunzip;
block_no = 0;
last_blockcnt = 0;
block_cnt = 0;
for (tmp = start; tmp < end; tmp++) {
if (dump_block (&tmp, tmp - start))
break;
}
if (tmp == end && dump_finish ()) {
if (len) {
if (do_gunzip)
*len = gunzipped_len;
else
*len = (end - start) << 9;
}
return 1;
}
}
}
printf ("\nInternal error while loading physical blocks from device\n");
return 0;
}
int silo_load_file(char *device, int partno, char *filename, unsigned char *buffer,
unsigned char *limit, int *len, int cmd,
void (*lenfunc)(int, char **, char **))
{
struct silo_inode *sino;
int retval = 0, i;
int size = -1;
char bogusdev[] = "/dev/sdaX";
char *bogdev;
void *mmark;
char *dir = NULL;
size_t fn_len;
mark (&mmark);
if (!device)
device = silo_disk_get_bootdevice();
bogdev = bogusdev;
if (prom_vers == PROM_V0) {
if (device[0] == 'f' && device[1] == 'd') {
device = "fd()";
bogdev = "/dev/fd0";
} else
bogusdev[8] = partno + '0';
} else
bogusdev[8] = partno + '0';
if (silo_disk_setdisk(device) < 0)
goto done_2;
do_gunzip = cmd & LOADFILE_GZIP;
if (cmd & ~LOADFILE_GZIP)
do_rotate = 0;
else
do_rotate = 1;
filebuffer = buffer;
filelimit = (char *)limit;
if (*filename == '[') {
if (cmd & LOADFILE_LS) {
if (!(cmd & LOADFILE_QUIET))
printf ("You cannot ls a device range\n");
goto done_2;
}
solaris = 0;
retval = dump_device_range (filename, bogdev, len, lenfunc);
goto done_2;
}
solaris = 0; /* The UFS module will set this if needed */
for (i = 0; silo_fs_ops[i]; i++)
if (silo_fs_ops[i]->open(bogdev))
break;
if (!silo_fs_ops[i]) {
if (!(cmd & LOADFILE_QUIET))
silo_fatal("Unable to open filesystem");
goto done_2;
} else
cur_ops = silo_fs_ops[i];
if (cmd & LOADFILE_LS && cur_ops->ls == NULL) {
if (!(cmd & LOADFILE_MATCH))
printf("\nls is not supported for the `%s' filesystems\n", cur_ops->name);
goto done_1;
}
fn_len = strlen(filename);
/* Find the inode for the filename. If we are matching, always parse
* basedir and basename. */
if (cmd & LOADFILE_MATCH && fn_len > 1 && filename[fn_len - 1] != '/') {
dir = strdup(filename);
if ((match = strrchr(dir, '/')) != NULL && strlen(match) > 1) {
char *base = "/";
if (match != dir) base = dir;
*match = '\0';
match++;
retval = cur_ops->namei_follow (base);
}
} else {
match = NULL;
retval = cur_ops->namei_follow (filename);
}
if (retval) {
if (!(cmd & LOADFILE_QUIET)) {
printf ("\nCannot find %s (", dir != NULL ? dir : filename);
cur_ops->print_error (retval);
printf (")\n");
}
retval = 0;
goto done_1;
}
if (lenfunc) {
do_gunzip = 0;
size = cur_ops->ino_size();
(*lenfunc)(size, (char **)&filebuffer, (char **)&filelimit);
do_gunzip = cmd & LOADFILE_GZIP;
}
first_block = do_gunzip;
last_blockcnt = 0;
block_no = 0;
block_cnt = 0;
retval = 0;
if (cur_ops->have_inode) {
if (cmd & LOADFILE_LS) {
if ((retval = cur_ops->ls())) {
if (!(cmd & LOADFILE_MATCH)) {
printf("\nError: could not list (");
cur_ops->print_error(retval);
printf(").\n");
}
retval = 0;
} else {
sino = (struct silo_inode *)filebuffer;
sino->inolen = 0;
retval = 1;
}
} else {
retval = cur_ops->dump();
if (!retval && !(cmd & LOADFILE_MATCH))
printf("\nError loading %s\n", filename);
}
}
if (retval && len) {
if (size != -1)
*len = size;
else if (do_gunzip)
*len = gunzipped_len;
else
*len = cur_ops->ino_size();
}
done_1:
if (dir) free(dir);
cur_ops->close();
done_2:
release (mmark);
return retval;
}