grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] squash4: fix handling of fragments and sparse files


From: Andrei Borzenkov
Subject: [PATCH] squash4: fix handling of fragments and sparse files
Date: Sat, 18 Feb 2017 11:16:43 +0300

1. Do not assume block list and fragment are mutually exclusive. Squash
can pack file tail as fragment (unless -no-fragments is specified); so
check read offset and read either from block list or from fragments as
appropriate.

2. Support sparse files with zero blocks.

3. Fix fragment read - frag.offset is absolute fragment position,
not offset relative to ino.chunk.

Reported and tested by Carlo Caione <address@hidden>

---

@Vladimir: we need regression tests for both of these cases. We could real small
files only by accident - block list was zero, so it appeared to work.

@Carlo, please test. I'm surprised it worked for you even without fragments as
your file is sparse and grub immediately choked on the first zero block.

 grub-core/fs/squash4.c | 55 +++++++++++++++++++++++++++++++++-----------------
 1 file changed, 36 insertions(+), 19 deletions(-)

diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c
index b97b344..deee71a 100644
--- a/grub-core/fs/squash4.c
+++ b/grub-core/fs/squash4.c
@@ -823,7 +823,12 @@ direct_read (struct grub_squash_data *data,
       curread = data->blksz - boff;
       if (curread > len)
        curread = len;
-      if (!(ino->block_sizes[i]
+      if (!ino->block_sizes[i])
+       {
+         /* Sparse block */
+         grub_memset (buf, '\0', curread);
+       }
+      else if (!(ino->block_sizes[i]
            & grub_cpu_to_le32_compile_time (SQUASH_BLOCK_UNCOMPRESSED)))
        {
          char *block;
@@ -873,36 +878,57 @@ direct_read (struct grub_squash_data *data,
 
 
 static grub_ssize_t
-grub_squash_read_data (struct grub_squash_data *data, 
-                      struct grub_squash_cache_inode *ino,
-                      grub_off_t off, char *buf, grub_size_t len)
+grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
 {
+  struct grub_squash_data *data = file->data;
+  struct grub_squash_cache_inode *ino = &data->ino;
+  grub_off_t off = file->offset;
   grub_err_t err;
   grub_uint64_t a = 0, b;
   grub_uint32_t fragment = 0;
   int compressed = 0;
   struct grub_squash_frag_desc frag;
+  grub_off_t blk_len;
+  grub_uint64_t mask = grub_le_to_cpu32 (data->sb.block_size) - 1;
+  grub_size_t orig_len = len;
 
   switch (ino->ino.type)
     {
     case grub_cpu_to_le16_compile_time (SQUASH_TYPE_LONG_REGULAR):
-      a = grub_le_to_cpu64 (ino->ino.long_file.chunk);
       fragment = grub_le_to_cpu32 (ino->ino.long_file.fragment);
       break;
     case grub_cpu_to_le16_compile_time (SQUASH_TYPE_REGULAR):
-      a = grub_le_to_cpu32 (ino->ino.file.chunk);
       fragment = grub_le_to_cpu32 (ino->ino.file.fragment);
       break;
     }
 
-  if (fragment == 0xffffffff)
-    return direct_read (data, ino, off, buf, len);
+  /* Squash may pack file tail as fragment. So read initial part directly and
+     get tail from fragments */
+  blk_len  = fragment == 0xffffffff ? file->size : file->size & ~mask;
+  if (off < blk_len)
+    {
+      grub_size_t read_len = blk_len - off;
+      grub_ssize_t res;
+
+      if (read_len > len)
+       read_len = len;
+      res = direct_read (data, ino, off, buf, read_len);
+      if ((grub_size_t) res != read_len)
+       return -1; /* FIXME: is short read possible here? */
+      len -= read_len;
+      if (!len)
+       return read_len;
+      buf += read_len;
+      off = 0;
+    }
+  else
+    off -= blk_len;
  
   err = read_chunk (data, &frag, sizeof (frag),
                    data->fragments, sizeof (frag) * fragment);
   if (err)
     return -1;
-  a += grub_le_to_cpu64 (frag.offset);
+  a = grub_le_to_cpu64 (frag.offset);
   compressed = !(frag.size & grub_cpu_to_le32_compile_time 
(SQUASH_BLOCK_UNCOMPRESSED));
   if (ino->ino.type == grub_cpu_to_le16_compile_time 
(SQUASH_TYPE_LONG_REGULAR))
     b = grub_le_to_cpu32 (ino->ino.long_file.offset) + off;
@@ -943,16 +969,7 @@ grub_squash_read_data (struct grub_squash_data *data,
       if (err)
        return -1;
     }
-  return len;
-}
-
-static grub_ssize_t
-grub_squash_read (grub_file_t file, char *buf, grub_size_t len)
-{
-  struct grub_squash_data *data = file->data;
-
-  return grub_squash_read_data (data, &data->ino,
-                               file->offset, buf, len);
+  return orig_len;
 }
 
 static grub_err_t
-- 
tg: (2fb8cd2..) u/squash-tail-fragment (depends on: master)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]