+ return grub_btrfs_read_logical (data, elemaddr, inode, sizeof (*inode), 0);
+}
+
+static grub_ssize_t
+grub_btrfs_lzo_decompress(char *ibuf, grub_size_t isize, grub_off_t off,
+ char *obuf, grub_size_t osize)
+{
+ grub_uint32_t total_size, cblock_size;
+ grub_size_t ret = 0;
+ char *ibuf0 = ibuf;
+
+ total_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
+ ibuf += sizeof (total_size);
+
+ if (isize < total_size)
+ return -1;
+
+ /* Jump forward to first block with requested data. */
+ while (off >= GRUB_BTRFS_LZO_BLOCK_SIZE)
+ {
+ /* Don't let following uint32_t cross the page boundary. */
+ if (((ibuf - ibuf0) & 0xffc) == 0xffc)
+ ibuf = ((ibuf - ibuf0 + 3) & ~3) + ibuf0;
+
+ cblock_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
+ ibuf += sizeof (cblock_size);
+
+ if (cblock_size > GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE)
+ return -1;
+
+ off -= GRUB_BTRFS_LZO_BLOCK_SIZE;
+ ibuf += cblock_size;
+ }
+
+ while (osize > 0)
+ {
+ lzo_uint usize = GRUB_BTRFS_LZO_BLOCK_SIZE;
+
+ /* Don't let following uint32_t cross the page boundary. */
+ if (((ibuf - ibuf0) & 0xffc) == 0xffc)
+ ibuf = ((ibuf - ibuf0 + 3) & ~3) + ibuf0;
+
+ cblock_size = grub_le_to_cpu32 (grub_get_unaligned32 (ibuf));
+ ibuf += sizeof (cblock_size);
+
+ if (cblock_size > GRUB_BTRFS_LZO_BLOCK_MAX_CSIZE)
+ return -1;
+
+ /* Block partially filled with requested data. */
+ if (off > 0 || osize < GRUB_BTRFS_LZO_BLOCK_SIZE)
+ {
+ grub_size_t to_copy = GRUB_BTRFS_LZO_BLOCK_SIZE - off;
+ grub_uint8_t *buf;
+
+ if (to_copy > osize)
+ to_copy = osize;
+
+ buf = grub_malloc (GRUB_BTRFS_LZO_BLOCK_SIZE);
+ if (!buf)
+ return -1;
+
+ if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, buf, &usize,
+ NULL) != LZO_E_OK)
+ {
+ grub_free (buf);
+ return -1;
+ }
+
+ if (to_copy > usize)
+ to_copy = usize;
+ grub_memcpy(obuf, buf + off, to_copy);
+
+ osize -= to_copy;
+ ret += to_copy;
+ obuf += to_copy;
+ ibuf += cblock_size;
+ off = 0;
+
+ grub_free (buf);
+ continue;
+ }
+
+ /* Decompress whole block directly to output buffer. */
+ if (lzo1x_decompress_safe ((lzo_bytep)ibuf, cblock_size, (lzo_bytep)obuf,
+ &usize, NULL) != LZO_E_OK)
+ return -1;
+
+ osize -= usize;
+ ret += usize;
+ obuf += usize;
+ ibuf += cblock_size;
+ }
+
+ return ret;