diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index 2e32248b4..5046b8a28 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -70,7 +70,11 @@ struct grub_btrfs_data
   unsigned int sblock_number;
   grub_uint64_t tree;
   grub_uint64_t inode;
+
+  /* Cached extent data.  */
   grub_uint64_t extstart;
+  grub_uint64_t extino;
+  grub_uint64_t exttree;
   struct grub_btrfs_extent_data *extent;
 };
 
@@ -603,6 +607,139 @@ grub_btrfs_mount (grub_disk_t disk)
   return NULL;
 }
 
+static grub_err_t
+grub_btrfs_read_inode (struct grub_btrfs_data *data, grub_disk_t disk,
+		       struct grub_btrfs_inode *inode, grub_uint64_t num,
+		       grub_uint64_t tree)
+{
+  struct grub_btrfs_key key_in, key_out;
+  grub_disk_addr_t elemaddr;
+  grub_size_t elemsize;
+  grub_err_t err;
+
+  key_in.object_id = num;
+  key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM;
+  key_in.offset = 0;
+
+  err = lower_bound (data,disk, &key_in, &key_out, tree,
+		     &elemaddr, &elemsize, NULL);
+  if (err)
+    return err;
+  if (num != key_out.object_id
+      || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM)
+    return grub_error (GRUB_ERR_BAD_FS, "inode not found");
+
+  return grub_btrfs_read_logical (data, disk, elemaddr, inode, sizeof (*inode));
+}
+
+static grub_ssize_t
+grub_btrfs_extent_read (struct grub_btrfs_data *data, grub_disk_t disk,
+			grub_uint64_t ino, grub_uint64_t tree,
+			grub_off_t pos0, char *buf, grub_size_t len)
+{
+  grub_off_t pos = pos0;
+  while (len)
+    {
+      grub_size_t csize;
+      grub_err_t err;
+      grub_off_t extoff;
+      if (!data->extent || data->extstart > pos || data->extino != ino
+	  || data->exttree != tree
+	  || grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos)
+	{
+	  struct grub_btrfs_key key_in, key_out;
+	  grub_disk_addr_t elemaddr;
+	  grub_size_t elemsize;
+	  grub_free (data->extent);
+	  key_in.object_id = ino;
+	  key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM;
+	  key_in.offset = grub_cpu_to_le64 (pos);
+	  err = lower_bound (data, disk, &key_in, &key_out, tree,
+			     &elemaddr, &elemsize, NULL);
+	  if (err)
+	    return -1;
+	  if (key_out.object_id != ino
+	      || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM)
+	    {
+	      grub_error (GRUB_ERR_BAD_FS, "extent not found");
+	      return -1;
+	    }
+	  data->extstart = grub_le_to_cpu64 (key_out.offset);
+	  data->extent = grub_malloc (elemsize);
+	  data->extino = ino;
+	  data->exttree = tree;
+	  if (!data->extent)
+	    return grub_errno;
+
+	  err = grub_btrfs_read_logical (data, disk, elemaddr,
+					 data->extent, elemsize);
+	  if (err)
+	    return err;
+	  if (grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos)
+	    return grub_error (GRUB_ERR_BAD_FS, "extent not found");
+	  grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%"
+			PRIxGRUB_UINT64_T "\n",
+			grub_le_to_cpu64 (key_out.offset),
+			grub_le_to_cpu64 (data->extent->size));
+	}
+      csize = grub_le_to_cpu64 (data->extent->size)
+	+ grub_le_to_cpu64 (data->extstart) - pos;
+      extoff = pos - data->extstart;
+      if (csize > len)
+	csize = len;
+
+      if (data->extent->encryption)
+	{
+	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+		      "encryption not supported");
+	  return -1;
+	}
+
+      if (data->extent->compression)
+	{
+	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+		      "compression not supported");
+	  return -1;
+	}
+
+
+      if (data->extent->encoding)
+	{
+	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+		      "encoding not supported");	
+	  return -1;
+	}
+
+      switch (data->extent->type)
+	{
+	case GRUB_BTRFS_EXTENT_INLINE:
+	  grub_memcpy (buf, data->extent->inl + extoff, csize);
+	  break;
+	case GRUB_BTRFS_EXTENT_REGULAR:
+	  if (!data->extent->laddr)
+	    {
+	      grub_memset (buf, 0, csize);
+	      break;
+	    }
+	  err = grub_btrfs_read_logical (data, disk,
+					 grub_le_to_cpu64 (data->extent->laddr)
+					 + extoff,
+					 buf, csize);
+	  if (err)
+	    return -1;
+	  break;
+	default:
+	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
+		      "unsupported extent type 0x%x", data->extent->type);	
+	  return -1;
+	}
+      buf += csize;
+      pos += csize;
+      len -= csize;
+    }
+  return pos - pos0;
+}
+
 static grub_err_t
 find_path (struct grub_btrfs_data *data,
 	   grub_disk_t disk,
@@ -616,15 +753,17 @@ find_path (struct grub_btrfs_data *data,
   grub_size_t allocated = 0;
   struct grub_btrfs_dir_item *direl = NULL;
   struct grub_btrfs_key key_out;
-  int skip_default = 1;
+  int skip_default;
   const char *ctoken;
   grub_size_t ctokenlen;
+  char *path_alloc = NULL;
 
   *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
   *tree = data->sblock.root_tree;
   key->object_id = data->sblock.root_dir_objectid;
   key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
   key->offset = 0;
+  skip_default = 1;
 
   while (1)
     {
@@ -647,7 +786,10 @@ find_path (struct grub_btrfs_data *data,
 	}
 
       if (*type != GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
-      	return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
+	{
+	  grub_free (path_alloc);
+	  return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
+	}
 
       key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
       key->offset = grub_cpu_to_le64 (~grub_getcrc32c (1, ctoken, ctokenlen));
@@ -657,11 +799,13 @@ find_path (struct grub_btrfs_data *data,
       if (err)
 	{
 	  grub_free (direl);
+	  grub_free (path_alloc);
 	  return err;
 	}
       if (key_cmp (key, &key_out) != 0)
 	{
 	  grub_free (direl);
+	  grub_free (path_alloc);
 	  return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
 	}
 
@@ -672,7 +816,10 @@ find_path (struct grub_btrfs_data *data,
 	  grub_free (direl);
 	  direl = grub_malloc (allocated + 1);
 	  if (!direl)
-	    return grub_errno;
+	    {
+	      grub_free (path_alloc);
+	      return grub_errno;
+	    }
 	}
 
       err = grub_btrfs_read_logical (data, disk, elemaddr,
@@ -680,6 +827,7 @@ find_path (struct grub_btrfs_data *data,
       if (err)
 	{
 	  grub_free (direl);
+	  grub_free (path_alloc);
 	  return err;
 	}
 
@@ -701,19 +849,60 @@ find_path (struct grub_btrfs_data *data,
 	  >= (grub_ssize_t) elemsize)
 	{
 	  grub_free (direl);
+	  grub_free (path_alloc);
 	  return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
 	}
 
       if (!skip_default)
 	path = slash;
       skip_default = 0;
-      *type = cdirel->type;
-      if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
+      if (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_SYMLINK)
 	{
-	  grub_free (direl);
-	  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-			     "symlinks not supported");
+	  struct grub_btrfs_inode inode;
+	  char *tmp;
+	  err = grub_btrfs_read_inode (data, disk, &inode,
+				       cdirel->key.object_id, *tree);
+	  if (err)
+	    {
+	      grub_free (direl);
+	      grub_free (path_alloc);
+	      return err;
+	    }
+	  tmp = grub_malloc (grub_le_to_cpu64 (inode.size)
+			     + grub_strlen (path) + 1);
+	  if (!tmp)
+	    {
+	      grub_free (direl);
+	      grub_free (path_alloc);
+	      return grub_errno;
+	    }
+
+	  if (grub_btrfs_extent_read (data, disk, cdirel->key.object_id,
+				      *tree, 0, tmp,
+				      grub_le_to_cpu64 (inode.size))
+	      != (grub_ssize_t) grub_le_to_cpu64 (inode.size))
+	    {
+	      grub_free (direl);
+	      grub_free (path_alloc);
+	      grub_free (tmp);
+	      return grub_errno;
+	    }
+	  grub_memcpy (tmp + grub_le_to_cpu64 (inode.size), path, 
+		       grub_strlen (path) + 1);
+	  grub_free (path_alloc);
+	  path = path_alloc = tmp;
+	  if (path[0] == '/')
+	    {
+	      *type = GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY;
+	      *tree = data->sblock.root_tree;
+	      key->object_id = data->sblock.root_dir_objectid;
+	      key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
+	      key->offset = 0;
+	      skip_default = 1;
+	    }
+	  continue;
 	}
+      *type = cdirel->type;
 
       switch (cdirel->key.type)
 	{
@@ -724,14 +913,26 @@ find_path (struct grub_btrfs_data *data,
 			       data->sblock.root_tree,
 			       &elemaddr, &elemsize, NULL);
 	    if (err)
-	      return err;
+	      {
+		grub_free (direl);
+		grub_free (path_alloc);
+		return err;
+	      }
 	    if (cdirel->key.object_id != key_out.object_id
 		|| cdirel->key.type != key_out.type)
-	      return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
+	      {
+		grub_free (direl);
+		grub_free (path_alloc);
+		return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
+	      }
 	    err = grub_btrfs_read_logical (data, disk, elemaddr,
 					   &ri, sizeof (ri));
 	    if (err)
-	      return err;
+	      {
+		grub_free (direl);
+		grub_free (path_alloc);
+		return err;
+	      }
 	    key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
 	    key->offset = 0;
 	    key->object_id = GRUB_BTRFS_OBJECT_ID_CHUNK;
@@ -740,19 +941,24 @@ find_path (struct grub_btrfs_data *data,
 	  }
 	case GRUB_BTRFS_ITEM_TYPE_INODE_ITEM:
 	  if (*slash && *type == GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
-	    return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
+	    {
+	      grub_free (direl);
+	      grub_free (path_alloc);
+	      return grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
+	    }
 	  *key = cdirel->key;
 	  if (*type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY)
-	    key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;	    
+	    key->type = GRUB_BTRFS_ITEM_TYPE_DIR_ITEM;
 	  break;
 	default:
+	  grub_free (path_alloc);
+	  grub_free (direl);
 	  return grub_error (GRUB_ERR_BAD_FS, "unrecognised object type 0x%x", 
 			     cdirel->key.type);
 	}
     }
 
   grub_free (direl);
-
   return GRUB_ERR_NONE;
 }
 
@@ -857,12 +1063,10 @@ static grub_err_t
 grub_btrfs_open (struct grub_file *file, const char *name)
 {
   struct grub_btrfs_data *data = grub_btrfs_mount (file->device->disk);
-  struct grub_btrfs_key key_in, key_out;
   grub_err_t err;
-  grub_disk_addr_t elemaddr;
-  grub_size_t elemsize;
   struct grub_btrfs_inode inode;
   grub_uint8_t type;
+  struct grub_btrfs_key key_in;
 
   if (!data)
     return grub_errno;
@@ -874,28 +1078,24 @@ grub_btrfs_open (struct grub_file *file, const char *name)
       return err;
     }
   if (type != GRUB_BTRFS_DIR_ITEM_TYPE_REGULAR)
-    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
+    {
+      grub_free (data);
+      return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
+    }
+
   data->inode = key_in.object_id;
-  key_in.type = GRUB_BTRFS_ITEM_TYPE_INODE_ITEM;
-
-  err = lower_bound (data, file->device->disk, &key_in, &key_out, 
-		     data->tree,
-		     &elemaddr, &elemsize, NULL);
+  err = grub_btrfs_read_inode (data, file->device->disk, &inode, data->inode,
+			       data->tree);
   if (err)
-    return err;
-  if (data->inode != key_out.object_id
-      || key_out.type != GRUB_BTRFS_ITEM_TYPE_INODE_ITEM)
-    return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
-
-  err = grub_btrfs_read_logical (data, file->device->disk, elemaddr,
-				 &inode, sizeof (inode));
-  if (err)
-    return err;
+    {
+      grub_free (data);
+      return err;
+    }
 
   file->data = data;
   file->size = grub_le_to_cpu64 (inode.size);
 
-  return GRUB_ERR_NONE;
+  return err;
 }
 
 static grub_err_t
@@ -913,106 +1113,9 @@ static grub_ssize_t
 grub_btrfs_read (grub_file_t file, char *buf, grub_size_t len)
 {
   struct grub_btrfs_data *data = file->data;
-  grub_off_t pos = file->offset;
 
-  while (len)
-    {
-      grub_size_t csize;
-      grub_err_t err;
-      grub_off_t extoff;
-      if (!data->extent || data->extstart > pos ||
-	  grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos)
-	{
-	  struct grub_btrfs_key key_in, key_out;
-	  grub_disk_addr_t elemaddr;
-	  grub_size_t elemsize;
-	  grub_free (data->extent);
-	  key_in.object_id = data->inode;
-	  key_in.type = GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM;
-	  key_in.offset = grub_cpu_to_le64 (pos);
-	  err = lower_bound (data, file->device->disk, &key_in, &key_out, 
-			     data->tree,
-			     &elemaddr, &elemsize, NULL);
-	  if (err)
-	    return -1;
-	  if (key_out.object_id != data->inode
-	      || key_out.type != GRUB_BTRFS_ITEM_TYPE_EXTENT_ITEM)
-	    {
-	      grub_error (GRUB_ERR_BAD_FS, "extent not found");
-	      return -1;
-	    }
-	  data->extstart = grub_le_to_cpu64 (key_out.offset);
-	  data->extent = grub_malloc (elemsize);
-	  if (!data->extent)
-	    return grub_errno;
-
-	  err = grub_btrfs_read_logical (data, file->device->disk, elemaddr,
-					 data->extent, elemsize);
-	  if (err)
-	    return err;
-	  if (grub_le_to_cpu64 (data->extent->size) + data->extstart <= pos)
-	    return grub_error (GRUB_ERR_BAD_FS, "extent not found");
-	  grub_dprintf ("btrfs", "extent 0x%" PRIxGRUB_UINT64_T "+0x%"
-			PRIxGRUB_UINT64_T "\n",
-			grub_le_to_cpu64 (key_out.offset),
-			grub_le_to_cpu64 (data->extent->size));
-	}
-      csize = grub_le_to_cpu64 (data->extent->size)
-	+ grub_le_to_cpu64 (data->extstart) - pos;
-      extoff = pos - data->extstart;
-      if (csize > len)
-	csize = len;
-
-      if (data->extent->encryption)
-	{
-	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-		      "encryption not supported");
-	  return -1;
-	}
-
-      if (data->extent->compression)
-	{
-	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-		      "compression not supported");
-	  return -1;
-	}
-
-
-      if (data->extent->encoding)
-	{
-	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-		      "encoding not supported");	
-	  return -1;
-	}
-
-      switch (data->extent->type)
-	{
-	case GRUB_BTRFS_EXTENT_INLINE:
-	  grub_memcpy (buf, data->extent->inl + extoff, csize);
-	  break;
-	case GRUB_BTRFS_EXTENT_REGULAR:
-	  if (!data->extent->laddr)
-	    {
-	      grub_memset (buf, 0, csize);
-	      break;
-	    }
-	  err = grub_btrfs_read_logical (data, file->device->disk,
-					 grub_le_to_cpu64 (data->extent->laddr)
-					 + extoff,
-					 buf, csize);
-	  if (err)
-	    return -1;
-	  break;
-	default:
-	  grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
-		      "unsupported extent type 0x%x", data->extent->type);	
-	  return -1;
-	}
-      buf += csize;
-      pos += csize;
-      len -= csize;
-    }
-  return pos - file->offset;
+  return grub_btrfs_extent_read (data, file->device->disk, data->inode,
+				 data->tree, file->offset, buf, len);
 }
 
 static grub_err_t