Add optional ino_size filesystem backend function
[yaboot.git] / second / fs_ext2.c
index 729e2f4a64b55a7c90a920e013be2eab32128cc0..c86907ebb89464681194da73c951bc56ad0981eb 100644 (file)
@@ -35,6 +35,7 @@
 #include "fs.h"
 #include "errors.h"
 #include "debug.h"
+#include "bootinfo.h"
 
 #define FAST_VERSION
 #define MAX_READ_RANGE 256
@@ -45,15 +46,15 @@ typedef int FILE;
 #include "ext2fs/ext2fs.h"
 
 static int ext2_open(  struct boot_file_t*     file,
-                       const char*             dev_name,
                        struct partition_t*     part,
-                       const char*             file_name);
+                       struct boot_fspec_t*    fspec);
 static int ext2_read(  struct boot_file_t*     file,
                        unsigned int            size,
                        void*                   buffer);
 static int ext2_seek(  struct boot_file_t*     file,
                        unsigned int            newpos);
 static int ext2_close( struct boot_file_t*     file);
+static unsigned int ext2_ino_size(struct boot_file_t *file);
 
 struct fs_t ext2_filesystem =
 {
@@ -61,7 +62,8 @@ struct fs_t ext2_filesystem =
      ext2_open,
      ext2_read,
      ext2_seek,
-     ext2_close
+     ext2_close,
+     ext2_ino_size,
 };
 
 /* IO manager structure for the ext2 library */
@@ -93,6 +95,7 @@ static io_manager linux_io_manager = &struct_linux_manager;
 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;
@@ -121,14 +124,15 @@ void com_err (const char *a, long i, const char *fmt,...)
 
 static int
 ext2_open(     struct boot_file_t*     file,
-               const char*             dev_name,
                struct partition_t*     part,
-               const char*             file_name)
+               struct boot_fspec_t*    fspec)
 {
      int result = 0;
      int error = FILE_ERR_NOTFOUND;
      static char buffer[1024];
      int ofopened = 0;
+     char *dev_name = fspec->dev;
+     char *file_name = fspec->file;
 
      DEBUG_ENTER;
      DEBUG_OPEN;
@@ -149,17 +153,28 @@ ext2_open(        struct boot_file_t*     file,
       * 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);
-     strcat(buffer, ":0");
+     if (_machine != _MACH_bplan)
+         strcat(buffer, ":0");
 
      DEBUG_F("<%s>\n", buffer);
 
@@ -551,6 +566,16 @@ ext2_close(        struct boot_file_t*     file)
      return 0;
 }
 
+static unsigned int ext2_ino_size(struct boot_file_t *file)
+{
+    struct ext2_inode ei;
+
+    if (ext2fs_read_inode(fs, file->inode, &ei))
+       return 0;
+
+    return ei.i_size;
+}
+
 static errcode_t linux_open (const char *name, int flags, io_channel * channel)
 {
      io_channel io;
@@ -582,6 +607,7 @@ static errcode_t linux_close (io_channel channel)
 
 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);
@@ -600,6 +626,15 @@ static errcode_t linux_read_blk (io_channel channel, unsigned long block, int co
 
      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) {