]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
* grub-core/fs/sfs.c (grub_sfs_rblock): New fields createtime and
authorVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 7 May 2012 17:07:16 +0000 (19:07 +0200)
committerVladimir 'phcoder' Serbinenko <phcoder@gmail.com>
Mon, 7 May 2012 17:07:16 +0000 (19:07 +0200)
flags.
(FLAGS_CASE_SENSITIVE): New enum value.
(cache_entry): New struct.
(grub_fshelp_node): Add fields cache_off, next_extent, cache_allocated,
cache_size and cache.
(grub_sfs_data): Remove blocksize. All users switched to log_blocksize.
Add log_blocksize and fshelp_flags.
(grub_sfs_read_extent): Handle non-512 blocks.
(grub_sfs_read_block): Add cаche and handle non-512 blocks.
(grub_sfs_read_file): Handle non-512 blocks.
(grub_sfs_mount): Handle non-512 blocks. Fill log_blocksize and
fshelp_flags.
(grub_sfs_read_symlink): Handle non-512 blocks.
(grub_sfs_iterate_dir): Init new fields. Mark as case-insensitive.
(grub_sfs_dir): Free cache.
(grub_sfs_close): Likewise.

ChangeLog
grub-core/fs/sfs.c

index 38ffad77ed15307426b2f2a922091a6a0e265319..c685afaad78121b126da6ca49addb87f93218c3a 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,23 @@
+2012-05-07  Vladimir Serbinenko  <phcoder@gmail.com>
+
+       * grub-core/fs/sfs.c (grub_sfs_rblock): New fields createtime and
+       flags.
+       (FLAGS_CASE_SENSITIVE): New enum value.
+       (cache_entry): New struct.
+       (grub_fshelp_node): Add fields cache_off, next_extent, cache_allocated,
+       cache_size and cache.
+       (grub_sfs_data): Remove blocksize. All users switched to log_blocksize.
+       Add log_blocksize and fshelp_flags.
+       (grub_sfs_read_extent): Handle non-512 blocks.
+       (grub_sfs_read_block): Add cаche and handle non-512 blocks.
+       (grub_sfs_read_file): Handle non-512 blocks.
+       (grub_sfs_mount): Handle non-512 blocks. Fill log_blocksize and
+       fshelp_flags.
+       (grub_sfs_read_symlink): Handle non-512 blocks.
+       (grub_sfs_iterate_dir): Init new fields. Mark as case-insensitive.
+       (grub_sfs_dir): Free cache.
+       (grub_sfs_close): Likewise.
+
 2012-05-06  Vladimir Serbinenko  <phcoder@gmail.com>
 
        * grub-core/fs/bfs.c (read_bfs_file): Fix overflow with over 2TiB
index 94e9cc459787a722e44671d3919ecd3aa8c6f456..a8bc00a065619d93cd264910faa05ff3b9099227 100644 (file)
@@ -42,7 +42,9 @@ struct grub_sfs_rblock
 {
   struct grub_sfs_bheader header;
   grub_uint32_t version;
-  grub_uint8_t unused1[36];
+  grub_uint32_t createtime;
+  grub_uint8_t flags;
+  grub_uint8_t unused1[31];
   grub_uint32_t blocksize;
   grub_uint8_t unused2[40];
   grub_uint8_t unused3[8];
@@ -50,6 +52,11 @@ struct grub_sfs_rblock
   grub_uint32_t btree;
 } __attribute__ ((packed));
 
+enum
+  {
+    FLAGS_CASE_SENSITIVE = 0x80
+  };
+
 /* A SFS object container.  */
 struct grub_sfs_obj
 {
@@ -117,12 +124,23 @@ struct grub_sfs_btree
 
 \f
 
+struct cache_entry
+{
+  grub_uint32_t off;
+  grub_uint32_t block;
+};
+
 struct grub_fshelp_node
 {
   struct grub_sfs_data *data;
   grub_uint32_t block;
   grub_uint32_t size;
   grub_uint32_t mtime;
+  grub_uint32_t cache_off;
+  grub_uint32_t next_extent;
+  grub_size_t cache_allocated;
+  grub_size_t cache_size;
+  struct cache_entry *cache;
 };
 
 /* Information about a "mounted" sfs filesystem.  */
@@ -132,8 +150,10 @@ struct grub_sfs_data
   struct grub_fshelp_node diropen;
   grub_disk_t disk;
 
-  /* Blocksize in sectors.  */
-  unsigned int blocksize;
+  /* Log of blocksize in sectors.  */
+  int log_blocksize;
+
+  int fshelp_flags;
 
   /* Label of the filesystem.  */
   char *label;
@@ -154,7 +174,7 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
   int i;
   grub_uint32_t next;
 
-  treeblock = grub_malloc (data->blocksize);
+  treeblock = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
   if (!block)
     return 0;
 
@@ -164,7 +184,10 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
   /* Handle this level in the btree.  */
   do
     {
-      grub_disk_read (data->disk, next, 0, data->blocksize, treeblock);
+      grub_disk_read (data->disk,
+                     ((grub_disk_addr_t) next) << data->log_blocksize,
+                     0, GRUB_DISK_SECTOR_SIZE << data->log_blocksize,
+                     treeblock);
       if (grub_errno)
        {
          grub_free (treeblock);
@@ -213,27 +236,100 @@ grub_sfs_read_extent (struct grub_sfs_data *data, unsigned int block,
 static grub_disk_addr_t
 grub_sfs_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
 {
-  grub_uint32_t blk = node->block;
+  grub_uint32_t blk;
   grub_uint32_t size = 0;
   grub_uint32_t next = 0;
+  grub_disk_addr_t off;
+  struct grub_sfs_data *data = node->data;
+
+  /* In case of the first block we don't have to lookup the
+     extent, the minimum size is always 1.  */
+  if (fileblock == 0)
+    return node->block;
+
+  if (!node->cache)
+    {
+      grub_size_t cache_size;
+      /* Assume half-max extents (32768 sectors).  */
+      cache_size = ((node->size >> (data->log_blocksize + GRUB_DISK_SECTOR_BITS
+                                   + 15))
+                   + 3);
+      if (cache_size < 8)
+       cache_size = 8;
+
+      node->cache_off = 0;
+      node->next_extent = node->block;
+      node->cache_size = 0;
+
+      node->cache = grub_malloc (sizeof (node->cache[0]) * cache_size);
+      if (!node->cache)
+       {
+         grub_errno = 0;
+         node->cache_allocated = 0;
+       }
+      else
+       {
+         node->cache_allocated = cache_size;
+         node->cache[0].off = 0;
+         node->cache[0].block = node->block;
+       }
+    }
+
+  if (fileblock < node->cache_off)
+    {
+      unsigned int i = 0;
+      int j, lg;
+      for (lg = 0; node->cache_size >> lg; lg++);
+
+      for (j = lg - 1; j >= 0; j--)
+       if ((i | (1 << j)) < node->cache_size
+           && node->cache[(i | (1 << j))].off <= fileblock)
+         i |= (1 << j);
+      return node->cache[i].block + fileblock - node->cache[i].off;
+    }
+
+  off = node->cache_off;
+  blk = node->next_extent;
 
   while (blk)
     {
       grub_err_t err;
 
-      /* In case of the first block we don't have to lookup the
-        extent, the minimum size is always 1.  */
-      if (fileblock == 0)
-       return blk;
-
       err = grub_sfs_read_extent (node->data, blk, &size, &next);
       if (err)
        return 0;
 
-      if (fileblock < size)
-       return fileblock + blk;
+      if (node->cache && node->cache_size >= node->cache_allocated)
+       {
+         struct cache_entry *e = node->cache;
+         e = grub_realloc (node->cache,node->cache_allocated * 2
+                           * sizeof (e[0]));
+         if (!e)
+           {
+             grub_errno = 0;
+             grub_free (node->cache);
+             node->cache = 0;
+           }
+         else
+           {
+             node->cache_allocated *= 2;
+             node->cache = e;
+           }
+       }
+
+      if (node->cache)
+       {
+         node->cache_off = off + size;
+         node->next_extent = next;
+         node->cache[node->cache_size].off = off;
+         node->cache[node->cache_size].block = blk;
+         node->cache_size++;
+       }
+
+      if (fileblock - off < size)
+       return fileblock - off + blk;
 
-      fileblock -= size;
+      off += size;
 
       blk = next;
     }
@@ -255,7 +351,7 @@ grub_sfs_read_file (grub_fshelp_node_t node,
 {
   return grub_fshelp_read_file (node->data->disk, node, read_hook,
                                pos, len, buf, grub_sfs_read_block,
-                               node->size, 0);
+                               node->size, node->data->log_blocksize);
 }
 
 
@@ -278,20 +374,31 @@ grub_sfs_mount (grub_disk_t disk)
     goto fail;
 
   /* Make sure this is a sfs filesystem.  */
-  if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4))
+  if (grub_strncmp ((char *) (data->rblock.header.magic), "SFS", 4)
+      || data->rblock.blocksize == 0
+      || (data->rblock.blocksize & (data->rblock.blocksize - 1)) != 0
+      || (data->rblock.blocksize & grub_cpu_to_be32_compile_time (0xf00001ff)))
     {
       grub_error (GRUB_ERR_BAD_FS, "not a SFS filesystem");
       goto fail;
     }
 
-  data->blocksize = grub_be_to_cpu32 (data->rblock.blocksize);
-  rootobjc_data = grub_malloc (data->blocksize);
+  for (data->log_blocksize = 9;
+       (1U << data->log_blocksize) < grub_be_to_cpu32 (data->rblock.blocksize);
+       data->log_blocksize++);
+  data->log_blocksize -= GRUB_DISK_SECTOR_BITS;
+  if (data->rblock.flags & FLAGS_CASE_SENSITIVE)
+    data->fshelp_flags = 0;
+  else
+    data->fshelp_flags = GRUB_FSHELP_CASE_INSENSITIVE;
+  rootobjc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
   if (! rootobjc_data)
     goto fail;
 
   /* Read the root object container.  */
-  grub_disk_read (disk, grub_be_to_cpu32 (data->rblock.rootobject), 0,
-                 data->blocksize, rootobjc_data);
+  grub_disk_read (disk, ((grub_disk_addr_t) grub_be_to_cpu32 (data->rblock.rootobject))
+                 << data->log_blocksize, 0,
+                 GRUB_DISK_SECTOR_SIZE << data->log_blocksize, rootobjc_data);
   if (grub_errno)
     goto fail;
 
@@ -301,6 +408,7 @@ grub_sfs_mount (grub_disk_t disk)
   data->diropen.size = 0;
   data->diropen.block = blk;
   data->diropen.data = data;
+  data->diropen.cache = 0;
   data->disk = disk;
   data->label = grub_strdup ((char *) (rootobjc->objects[0].filename));
 
@@ -324,11 +432,13 @@ grub_sfs_read_symlink (grub_fshelp_node_t node)
   char *symlink;
   char *block;
 
-  block = grub_malloc (data->blocksize);
+  block = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
   if (!block)
     return 0;
 
-  grub_disk_read (data->disk, node->block, 0, data->blocksize, block);
+  grub_disk_read (data->disk, ((grub_disk_addr_t) node->block)
+                 << data->log_blocksize,
+                 0, GRUB_DISK_SECTOR_SIZE << data->log_blocksize, block);
   if (grub_errno)
     {
       grub_free (block);
@@ -337,7 +447,8 @@ grub_sfs_read_symlink (grub_fshelp_node_t node)
 
   /* This is just a wild guess, but it always worked for me.  How the
      SLNK block looks like is not documented in the SFS docs.  */
-  symlink = grub_strndup (&block[24], data->blocksize - 24);
+  symlink = grub_strndup (&block[24],
+                         (GRUB_DISK_SECTOR_SIZE << data->log_blocksize) - 24);
   grub_free (block);
   if (!symlink)
     return 0;
@@ -360,13 +471,13 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
   grub_uint32_t pos;
 
   auto int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name,
-                                                 int block,
-                                                 int size, int type,
+                                                 grub_uint32_t block,
+                                                 grub_uint32_t size, int type,
                                                  grub_uint32_t mtime);
 
   int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name,
-                                            int block,
-                                            int size, int type,
+                                            grub_uint32_t block,
+                                            grub_uint32_t size, int type,
                                             grub_uint32_t mtime)
     {
       grub_size_t len = grub_strlen (name);
@@ -386,15 +497,20 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
       node->size = size;
       node->block = block;
       node->mtime = mtime;
+      node->cache = 0;
+      node->cache_off = 0;
+      node->next_extent = block;
+      node->cache_size = 0;
+      node->cache_allocated = 0;
 
       *grub_latin1_to_utf8 (name_u8, (const grub_uint8_t *) name, len) = '\0';
 
-      ret = hook ((char *) name_u8, type, node);
+      ret = hook ((char *) name_u8, type | data->fshelp_flags, node);
       grub_free (name_u8);
       return ret;
     }
 
-  objc_data = grub_malloc (data->blocksize);
+  objc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
   if (!objc_data)
     goto fail;
 
@@ -402,7 +518,9 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
      every block.  */
   while (next)
     {
-      grub_disk_read (data->disk, next, 0, data->blocksize, objc_data);
+      grub_disk_read (data->disk, ((grub_disk_addr_t) next)
+                     << data->log_blocksize, 0,
+                     GRUB_DISK_SECTOR_SIZE << data->log_blocksize, objc_data);
       if (grub_errno)
        goto fail;
 
@@ -411,7 +529,8 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
       pos = (char *) &objc->objects[0] - (char *) objc;
 
       /* Iterate over all entries in this block.  */
-      while (pos + sizeof (struct grub_sfs_obj) < data->blocksize)
+      while (pos + sizeof (struct grub_sfs_obj)
+            < (1U << (GRUB_DISK_SECTOR_BITS + data->log_blocksize)))
        {
          struct grub_sfs_obj *obj;
          obj = (struct grub_sfs_obj *) ((char *) objc + pos);
@@ -510,6 +629,7 @@ grub_sfs_close (grub_file_t file)
 {
   struct grub_sfs_data *data = (struct grub_sfs_data *) file->data;
 
+  grub_free (data->diropen.cache);
   grub_free (data->label);
   grub_free (data);
 
@@ -551,6 +671,7 @@ grub_sfs_dir (grub_device_t device, const char *path,
       info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
       info.mtime = node->mtime + 8 * 365 * 86400 + 86400 * 2;
       info.mtimeset = 1;
+      grub_free (node->cache);
       grub_free (node);
       return hook (filename, &info);
     }