-/* ext2 filesystem
-
- Copyright (C) 1999 Benjamin Herrenschmidt
-
- Adapted from quik/silo
-
- Copyright (C) 1996 Maurizio Plaza
- 1996 Jakub Jelinek
-
- 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., 675 Mass Ave, Cambridge, MA 02139, USA.
-
- Note: This version is way too slow due to the way we use bmap. This
- should be replaced by an iterator.
-*/
+/*
+ * fs_ext2.c - an implementation for the Ext2/Ext3 filesystem
+ *
+ * Copyright (C) 2001, 2002 Ethan Benson
+ *
+ * Copyright (C) 1999 Benjamin Herrenschmidt
+ *
+ * Adapted from quik/silo
+ *
+ * Copyright (C) 1996 Maurizio Plaza
+ * 1996 Jakub Jelinek
+ *
+ * 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 "ctype.h"
#include "types.h"
};
/* IO manager structure for the ext2 library */
-
+
static errcode_t linux_open (const char *name, int flags, io_channel * channel);
static errcode_t linux_close (io_channel channel);
static errcode_t linux_set_blksize (io_channel channel, int blksize);
static int opened = 0; /* We can't open twice ! */
static unsigned int bs; /* Blocksize */
static unsigned long long doff; /* Byte offset where partition starts */
+static unsigned long long dend; /* Byte offset where partition ends */
static ino_t root,cwd;
static ext2_filsys fs = 0;
static struct boot_file_t* cur_file;
int error = FILE_ERR_NOTFOUND;
static char buffer[1024];
int ofopened = 0;
-
+
DEBUG_ENTER;
DEBUG_OPEN;
if (opened) {
- prom_printf("ext2_open() : fs busy\n");
DEBUG_LEAVE(FILE_ERR_FSBUSY);
return FILE_ERR_FSBUSY;
}
if (file->device_kind != FILE_DEVICE_BLOCK) {
- prom_printf("Can't open ext2 filesystem on non-block device\n");
DEBUG_LEAVE(FILE_ERR_BADDEV);
return FILE_ERR_BADDEV;
}
fs = NULL;
-
+
/* We don't care too much about the device block size since we run
* thru the deblocker. We may have to change that is we plan to be
* compatible with older versions of OF
*/
bs = 1024;
- doff = 0;
- if (part)
+
+ /*
+ * On the other hand, we do care about the actual size of the
+ * partition, reads or seeks past the end may cause undefined
+ * behavior on some devices. A netapp that tries to seek and
+ * read past the end of the lun takes ~30 secs to recover per
+ * attempt.
+ */
+ doff = dend = 0;
+ if (part) {
doff = (unsigned long long)(part->part_start) * part->blocksize;
+ dend = doff + (unsigned long long)part->part_size * part->blocksize;
+ }
cur_file = file;
- DEBUG_F("partition offset: %Lu\n", doff);
+ DEBUG_F("partition offset: %Lx, end: %Lx\n", doff, dend);
/* Open the OF device for the entire disk */
strncpy(buffer, dev_name, 1020);
return FILE_IOERR;
}
ofopened = 1;
-
+
/* Open the ext2 filesystem */
result = ext2fs_open (buffer, EXT2_FLAG_RW, 0, 0, linux_io_manager, &fs);
if (result) {
error = FILE_IOERR;
goto bail;
}
-
+
/* Lookup file by pathname */
root = cwd = EXT2_ROOT_INO;
result = ext2fs_namei_follow(fs, root, cwd, file_name, &file->inode);
error = FILE_ERR_NOTFOUND;
goto bail;
}
-#endif
+#endif
#ifndef FAST_VERSION
result = ext2fs_read_inode(fs, file->inode, &cur_inode);
}
#endif /* FAST_VERSION */
file->pos = 0;
-
+
opened = 1;
bail:
if (!opened) {
free(block_buffer);
block_buffer = NULL;
cur_file = NULL;
-
+
DEBUG_LEAVE_F(error);
return error;
}
if ((count * bs) > read_max)
count--;
if (count) {
- size = count * bs;
+ size = count * bs;
read_result = io_channel_read_blk(fs->io, read_range_start, count, read_buffer);
if (read_result)
return BLOCK_ABORT;
read_range_count -= count;
read_range_start += count;
read_last_logical += count;
- }
+ }
/* Handle remaining block */
if (read_max && read_range_count) {
read_result = io_channel_read_blk(fs->io, read_range_start, 1, block_buffer);
++read_range_count;
return ((read_range_count * bs) >= read_max) ? BLOCK_ABORT : 0;
}
-
+
/* Range doesn't match. Dump existing range */
if (read_range_start) {
#ifdef VERBOSE_DEBUG
read_range_count = 1;
return (bs >= read_max) ? BLOCK_ABORT : 0;
}
-
+
#ifdef VERBOSE_DEBUG
DEBUG_F("\n");
#endif
read_max = size;
read_buffer = (unsigned char*)buffer;
read_result = 0;
-
+
retval = ext2fs_block_iterate(fs, file->inode, 0, 0, read_iterator, 0);
if (retval == BLOCK_ABORT)
retval = read_result;
}
if (retval)
prom_printf ("ext2: i/o error %ld in read\n", (long) retval);
-
+
return read_total;
#else /* FAST_VERSION */
int status;
unsigned int read = 0;
-
+
if (!opened)
return FILE_IOERR;
DEBUG_F("ext_read() from pos 0x%x, size: 0x%x\n", file->pos, size);
- while(size) {
+ while(size) {
blk_t fblock = file->pos / bs;
blk_t pblock;
unsigned int blkorig, s, b;
-
+
pblock = 0;
status = ext2fs_bmap(fs, file->inode, &cur_inode,
block_buffer, 0, fblock, &pblock);
file->pos += s;
}
return read;
-#endif /* FAST_VERSION */
+#endif /* FAST_VERSION */
}
static int
if (fs)
ext2fs_close(fs);
fs = NULL;
-
+
prom_close(file->of_device);
DEBUG_F("ext2_close called\n");
opened = 0;
-
+
return 0;
}
static errcode_t linux_set_blksize (io_channel channel, int blksize)
{
+ DEBUG_F("bs set to 0x%x\n", blksize);
channel->block_size = bs = blksize;
if (block_buffer) {
free(block_buffer);
block_buffer = malloc(bs * 2);
- }
+ }
return 0;
}
if (count == 0)
return 0;
-
+
tempb = (((unsigned long long) block) *
((unsigned long long)bs)) + (unsigned long long)doff;
+ /*
+ * Only test tempb exceeding dend if dend is set to allow things
+ * like boot: hd:0,\xxxx
+ */
+ if (dend && tempb > dend) {
+ DEBUG_F("\nSeek error on block %lx, tempb=%Lx\n", block, tempb >> 9);
+ return EXT2_ET_LLSEEK_FAILED;
+ }
+
size = (count < 0) ? -count : count * bs;
prom_lseek(cur_file->of_device, tempb);
if (prom_read(cur_file->of_device, data, size) != size) {
return 0;
}
-/*
+/*
* Local variables:
* c-file-style: "k&r"
* c-basic-offset: 5