Treat iSCSI targets as block devices.
[yaboot.git] / second / fs_ext2.c
index 2f2bada89701c5059d5813015d746525217ed7ea..a8bdf6bdf6d8de71a3bdf11275317eb9d6f234b5 100644 (file)
@@ -1,29 +1,29 @@
-/* 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"
@@ -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,11 +62,12 @@ 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 */
+
 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);
@@ -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,47 +124,58 @@ 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;
 
      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");
+     if (file->device_kind != FILE_DEVICE_BLOCK
+         && file->device_kind != FILE_DEVICE_ISCSI) {
          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);
-     strcat(buffer, ":0");
+     if (_machine != _MACH_bplan)
+         strcat(buffer, ":0");
 
      DEBUG_F("<%s>\n", buffer);
 
@@ -176,7 +190,7 @@ ext2_open(  struct boot_file_t*     file,
          return FILE_IOERR;
      }
      ofopened = 1;
-       
+
      /* Open the ext2 filesystem */
      result = ext2fs_open (buffer, EXT2_FLAG_RW, 0, 0, linux_io_manager, &fs);
      if (result) {
@@ -203,7 +217,7 @@ ext2_open(  struct boot_file_t*     file,
          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);
@@ -227,7 +241,7 @@ ext2_open(  struct boot_file_t*     file,
          error = FILE_ERR_NOTFOUND;
          goto bail;
      }
-#endif 
+#endif
 
 #ifndef FAST_VERSION
      result = ext2fs_read_inode(fs, file->inode, &cur_inode);
@@ -246,7 +260,7 @@ ext2_open(  struct boot_file_t*     file,
      }
 #endif /* FAST_VERSION */
      file->pos = 0;
-       
+
      opened = 1;
 bail:
      if (!opened) {
@@ -259,7 +273,7 @@ bail:
               free(block_buffer);
          block_buffer = NULL;
          cur_file = NULL;
-           
+
          DEBUG_LEAVE_F(error);
          return error;
      }
@@ -284,7 +298,7 @@ read_dump_range(void)
      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;
@@ -295,7 +309,7 @@ read_dump_range(void)
          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);
@@ -345,7 +359,7 @@ read_iterator(ext2_filsys fs, blk_t *blocknr, int lg_block, void *private)
          ++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
@@ -420,7 +434,7 @@ read_iterator(ext2_filsys fs, blk_t *blocknr, int lg_block, void *private)
          read_range_count = 1;
          return (bs >= read_max) ? BLOCK_ABORT : 0;
      }
-    
+
 #ifdef VERBOSE_DEBUG
      DEBUG_F("\n");
 #endif
@@ -452,7 +466,7 @@ ext2_read(  struct boot_file_t*     file,
      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;
@@ -466,13 +480,13 @@ ext2_read(        struct boot_file_t*     file,
      }
      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;
 
@@ -480,11 +494,11 @@ ext2_read(        struct boot_file_t*     file,
      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);
@@ -517,7 +531,7 @@ ext2_read(  struct boot_file_t*     file,
          file->pos += s;
      }
      return read;
-#endif /* FAST_VERSION */      
+#endif /* FAST_VERSION */
 }
 
 static int
@@ -544,15 +558,25 @@ ext2_close(       struct boot_file_t*     file)
      if (fs)
          ext2fs_close(fs);
      fs = NULL;
-       
+
      prom_close(file->of_device);
      DEBUG_F("ext2_close called\n");
 
      opened = 0;
-           
+
      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;
@@ -584,11 +608,12 @@ 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);
          block_buffer = malloc(bs * 2);
-     } 
+     }
      return 0;
 }
 
@@ -599,9 +624,18 @@ static errcode_t linux_read_blk (io_channel channel, unsigned long block, int co
 
      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) {
@@ -621,9 +655,9 @@ static errcode_t linux_flush (io_channel channel)
      return 0;
 }
 
-/* 
+/*
  * Local variables:
- * c-file-style: "K&R"
+ * c-file-style: "k&r"
  * c-basic-offset: 5
  * End:
  */