| /* 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; |
| } |