31004e
From 1eb3760d2821962f09cbbe2e4fa0d75a4e1a761c Mon Sep 17 00:00:00 2001
a5bd9f
From: Colin Watson <cjwatson@ubuntu.com>
a5bd9f
Date: Mon, 21 Jan 2013 01:33:46 +0000
f74b50
Subject: [PATCH 126/482] Remove nested functions from filesystem directory
a5bd9f
 iterators.
a5bd9f
a5bd9f
* include/grub/fs.h (grub_fs_dir_hook_t): New type.
a5bd9f
(struct grub_fs.dir): Add hook_data argument.
a5bd9f
a5bd9f
Update all implementations and callers.
a5bd9f
---
a5bd9f
 ChangeLog                     |   9 +
a5bd9f
 grub-core/commands/ls.c       | 204 +++++++++++----------
a5bd9f
 grub-core/commands/test.c     | 305 ++++++++++++++++---------------
a5bd9f
 grub-core/commands/wildcard.c | 158 +++++++++-------
a5bd9f
 grub-core/fs/affs.c           | 201 ++++++++++----------
a5bd9f
 grub-core/fs/bfs.c            |  95 ++++++----
a5bd9f
 grub-core/fs/btrfs.c          |   5 +-
a5bd9f
 grub-core/fs/cpio.c           |   5 +-
a5bd9f
 grub-core/fs/ext2.c           |  91 ++++-----
a5bd9f
 grub-core/fs/fat.c            |  16 +-
a5bd9f
 grub-core/fs/fshelp.c         | 307 ++++++++++++++++---------------
a5bd9f
 grub-core/fs/hfs.c            |   9 +-
a5bd9f
 grub-core/fs/hfsplus.c        |  60 +++---
a5bd9f
 grub-core/fs/iso9660.c        |  54 +++---
a5bd9f
 grub-core/fs/jfs.c            |   5 +-
a5bd9f
 grub-core/fs/minix.c          |   5 +-
a5bd9f
 grub-core/fs/nilfs2.c         |  90 ++++-----
a5bd9f
 grub-core/fs/ntfs.c           |  67 +++----
a5bd9f
 grub-core/fs/reiserfs.c       |  54 +++---
a5bd9f
 grub-core/fs/romfs.c          |  51 +++---
a5bd9f
 grub-core/fs/sfs.c            | 137 +++++++-------
a5bd9f
 grub-core/fs/squash4.c        |  57 +++---
a5bd9f
 grub-core/fs/udf.c            | 109 +++++------
a5bd9f
 grub-core/fs/ufs.c            |   5 +-
a5bd9f
 grub-core/fs/xfs.c            | 140 +++++++-------
a5bd9f
 grub-core/fs/zfs/zfs.c        | 416 +++++++++++++++++++++++-------------------
a5bd9f
 grub-core/kern/corecmd.c      |   5 +-
a5bd9f
 grub-core/kern/emu/hostfs.c   |   5 +-
a5bd9f
 grub-core/kern/fs.c           |  13 +-
a5bd9f
 grub-core/loader/xnu.c        | 205 ++++++++++++---------
a5bd9f
 grub-core/net/net.c           |   4 +-
a5bd9f
 grub-core/normal/completion.c |   5 +-
a5bd9f
 include/grub/fs.h             |   7 +-
a5bd9f
 include/grub/fshelp.h         |  15 +-
a5bd9f
 util/grub-mount.c             | 142 ++++++++------
a5bd9f
 35 files changed, 1664 insertions(+), 1392 deletions(-)
a5bd9f
a5bd9f
diff --git a/ChangeLog b/ChangeLog
a5bd9f
index afc2d38..c975de1 100644
a5bd9f
--- a/ChangeLog
a5bd9f
+++ b/ChangeLog
a5bd9f
@@ -1,5 +1,14 @@
a5bd9f
 2013-01-21  Colin Watson  <cjwatson@ubuntu.com>
a5bd9f
 
a5bd9f
+	Remove nested functions from filesystem directory iterators.
a5bd9f
+
a5bd9f
+	* include/grub/fs.h (grub_fs_dir_hook_t): New type.
a5bd9f
+	(struct grub_fs.dir): Add hook_data argument.
a5bd9f
+
a5bd9f
+	Update all implementations and callers.
a5bd9f
+
a5bd9f
+2013-01-21  Colin Watson  <cjwatson@ubuntu.com>
a5bd9f
+
a5bd9f
 	* docs/grub.texi (Multi-boot manual config): Fix typo for
a5bd9f
 	"recommended".
a5bd9f
 
a5bd9f
diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c
a5bd9f
index 7929747..0b86619 100644
a5bd9f
--- a/grub-core/commands/ls.c
a5bd9f
+++ b/grub-core/commands/ls.c
a5bd9f
@@ -85,114 +85,126 @@ grub_ls_list_devices (int longlist)
a5bd9f
   return 0;
a5bd9f
 }
a5bd9f
 
a5bd9f
-static grub_err_t
a5bd9f
-grub_ls_list_files (char *dirname, int longlist, int all, int human)
a5bd9f
+/* Context for grub_ls_list_files.  */
a5bd9f
+struct grub_ls_list_files_ctx
a5bd9f
 {
a5bd9f
-  char *device_name;
a5bd9f
-  grub_fs_t fs;
a5bd9f
-  const char *path;
a5bd9f
-  grub_device_t dev;
a5bd9f
+  char *dirname;
a5bd9f
+  int all;
a5bd9f
+  int human;
a5bd9f
+};
a5bd9f
+
a5bd9f
+/* Helper for grub_ls_list_files.  */
a5bd9f
+static int
a5bd9f
+print_files (const char *filename, const struct grub_dirhook_info *info,
a5bd9f
+	     void *data)
a5bd9f
+{
a5bd9f
+  struct grub_ls_list_files_ctx *ctx = data;
a5bd9f
 
a5bd9f
-  auto int print_files (const char *filename,
a5bd9f
-			const struct grub_dirhook_info *info);
a5bd9f
-  auto int print_files_long (const char *filename,
a5bd9f
-			     const struct grub_dirhook_info *info);
a5bd9f
+  if (ctx->all || filename[0] != '.')
a5bd9f
+    grub_printf ("%s%s ", filename, info->dir ? "/" : "");
a5bd9f
 
a5bd9f
-  int print_files (const char *filename, const struct grub_dirhook_info *info)
a5bd9f
-    {
a5bd9f
-      if (all || filename[0] != '.')
a5bd9f
-	grub_printf ("%s%s ", filename, info->dir ? "/" : "");
a5bd9f
+  return 0;
a5bd9f
+}
a5bd9f
 
a5bd9f
-      return 0;
a5bd9f
-    }
a5bd9f
+/* Helper for grub_ls_list_files.  */
a5bd9f
+static int
a5bd9f
+print_files_long (const char *filename, const struct grub_dirhook_info *info,
a5bd9f
+		  void *data)
a5bd9f
+{
a5bd9f
+  struct grub_ls_list_files_ctx *ctx = data;
a5bd9f
+
a5bd9f
+  if ((! ctx->all) && (filename[0] == '.'))
a5bd9f
+    return 0;
a5bd9f
 
a5bd9f
-  int print_files_long (const char *filename,
a5bd9f
-			const struct grub_dirhook_info *info)
a5bd9f
+  if (! info->dir)
a5bd9f
     {
a5bd9f
-      if ((! all) && (filename[0] == '.'))
a5bd9f
-	return 0;
a5bd9f
+      grub_file_t file;
a5bd9f
+      char *pathname;
a5bd9f
 
a5bd9f
-      if (! info->dir)
a5bd9f
-	{
a5bd9f
-	  grub_file_t file;
a5bd9f
-	  char *pathname;
a5bd9f
+      if (ctx->dirname[grub_strlen (ctx->dirname) - 1] == '/')
a5bd9f
+	pathname = grub_xasprintf ("%s%s", ctx->dirname, filename);
a5bd9f
+      else
a5bd9f
+	pathname = grub_xasprintf ("%s/%s", ctx->dirname, filename);
a5bd9f
 
a5bd9f
-	  if (dirname[grub_strlen (dirname) - 1] == '/')
a5bd9f
-	    pathname = grub_xasprintf ("%s%s", dirname, filename);
a5bd9f
-	  else
a5bd9f
-	    pathname = grub_xasprintf ("%s/%s", dirname, filename);
a5bd9f
+      if (!pathname)
a5bd9f
+	return 1;
a5bd9f
 
a5bd9f
-	  if (!pathname)
a5bd9f
-	    return 1;
a5bd9f
+      /* XXX: For ext2fs symlinks are detected as files while they
a5bd9f
+	 should be reported as directories.  */
a5bd9f
+      grub_file_filter_disable_compression ();
a5bd9f
+      file = grub_file_open (pathname);
a5bd9f
+      if (! file)
a5bd9f
+	{
a5bd9f
+	  grub_errno = 0;
a5bd9f
+	  grub_free (pathname);
a5bd9f
+	  return 0;
a5bd9f
+	}
a5bd9f
 
a5bd9f
-	  /* XXX: For ext2fs symlinks are detected as files while they
a5bd9f
-	     should be reported as directories.  */
a5bd9f
-	  grub_file_filter_disable_compression ();
a5bd9f
-	  file = grub_file_open (pathname);
a5bd9f
-	  if (! file)
a5bd9f
+      if (! ctx->human)
a5bd9f
+	grub_printf ("%-12llu", (unsigned long long) file->size);
a5bd9f
+      else
a5bd9f
+	{
a5bd9f
+	  grub_uint64_t fsize = file->size * 100ULL;
a5bd9f
+	  grub_uint64_t fsz = file->size;
a5bd9f
+	  int units = 0;
a5bd9f
+	  char buf[20];
a5bd9f
+
a5bd9f
+	  while (fsz / 1024)
a5bd9f
 	    {
a5bd9f
-	      grub_errno = 0;
a5bd9f
-	      grub_free (pathname);
a5bd9f
-	      return 0;
a5bd9f
+	      fsize = (fsize + 512) / 1024;
a5bd9f
+	      fsz /= 1024;
a5bd9f
+	      units++;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
-	  if (! human)
a5bd9f
-	    grub_printf ("%-12llu", (unsigned long long) file->size);
a5bd9f
-	  else
a5bd9f
+	  if (units)
a5bd9f
 	    {
a5bd9f
-	      grub_uint64_t fsize = file->size * 100ULL;
a5bd9f
-	      grub_uint64_t fsz = file->size;
a5bd9f
-	      int units = 0;
a5bd9f
-	      char buf[20];
a5bd9f
-
a5bd9f
-	      while (fsz / 1024)
a5bd9f
-		{
a5bd9f
-		  fsize = (fsize + 512) / 1024;
a5bd9f
-		  fsz /= 1024;
a5bd9f
-		  units++;
a5bd9f
-		}
a5bd9f
-
a5bd9f
-	      if (units)
a5bd9f
-		{
a5bd9f
-		  grub_uint64_t whole, fraction;
a5bd9f
-
a5bd9f
-		  whole = grub_divmod64 (fsize, 100, &fraction);
a5bd9f
-		  grub_snprintf (buf, sizeof (buf),
a5bd9f
-				 "%" PRIuGRUB_UINT64_T
a5bd9f
-				 ".%02" PRIuGRUB_UINT64_T "%c", whole, fraction,
a5bd9f
-				 grub_human_sizes[units]);
a5bd9f
-		  grub_printf ("%-12s", buf);
a5bd9f
-		}
a5bd9f
-	      else
a5bd9f
-		grub_printf ("%-12llu", (unsigned long long) file->size);
a5bd9f
-
a5bd9f
+	      grub_uint64_t whole, fraction;
a5bd9f
+
a5bd9f
+	      whole = grub_divmod64 (fsize, 100, &fraction);
a5bd9f
+	      grub_snprintf (buf, sizeof (buf),
a5bd9f
+			     "%" PRIuGRUB_UINT64_T
a5bd9f
+			     ".%02" PRIuGRUB_UINT64_T "%c", whole, fraction,
a5bd9f
+			     grub_human_sizes[units]);
a5bd9f
+	      grub_printf ("%-12s", buf);
a5bd9f
 	    }
a5bd9f
-	  grub_file_close (file);
a5bd9f
-	  grub_free (pathname);
a5bd9f
-	}
a5bd9f
-      else
a5bd9f
-	grub_printf ("%-12s", _("DIR"));
a5bd9f
-
a5bd9f
-      if (info->mtimeset)
a5bd9f
-	{
a5bd9f
-	  struct grub_datetime datetime;
a5bd9f
-	  grub_unixtime2datetime (info->mtime, &datetime);
a5bd9f
-	  if (human)
a5bd9f
-	    grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ",
a5bd9f
-			 datetime.year, datetime.month, datetime.day,
a5bd9f
-			 datetime.hour, datetime.minute,
a5bd9f
-			 datetime.second,
a5bd9f
-			 grub_get_weekday_name (&datetime));
a5bd9f
 	  else
a5bd9f
-	    grub_printf (" %04d%02d%02d%02d%02d%02d ",
a5bd9f
-			 datetime.year, datetime.month,
a5bd9f
-			 datetime.day, datetime.hour,
a5bd9f
-			 datetime.minute, datetime.second);
a5bd9f
+	    grub_printf ("%-12llu", (unsigned long long) file->size);
a5bd9f
+
a5bd9f
 	}
a5bd9f
-      grub_printf ("%s%s\n", filename, info->dir ? "/" : "");
a5bd9f
+      grub_file_close (file);
a5bd9f
+      grub_free (pathname);
a5bd9f
+    }
a5bd9f
+  else
a5bd9f
+    grub_printf ("%-12s", _("DIR"));
a5bd9f
 
a5bd9f
-      return 0;
a5bd9f
+  if (info->mtimeset)
a5bd9f
+    {
a5bd9f
+      struct grub_datetime datetime;
a5bd9f
+      grub_unixtime2datetime (info->mtime, &datetime);
a5bd9f
+      if (ctx->human)
a5bd9f
+	grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ",
a5bd9f
+		     datetime.year, datetime.month, datetime.day,
a5bd9f
+		     datetime.hour, datetime.minute,
a5bd9f
+		     datetime.second,
a5bd9f
+		     grub_get_weekday_name (&datetime));
a5bd9f
+      else
a5bd9f
+	grub_printf (" %04d%02d%02d%02d%02d%02d ",
a5bd9f
+		     datetime.year, datetime.month,
a5bd9f
+		     datetime.day, datetime.hour,
a5bd9f
+		     datetime.minute, datetime.second);
a5bd9f
     }
a5bd9f
+  grub_printf ("%s%s\n", filename, info->dir ? "/" : "");
a5bd9f
+
a5bd9f
+  return 0;
a5bd9f
+}
a5bd9f
+
a5bd9f
+static grub_err_t
a5bd9f
+grub_ls_list_files (char *dirname, int longlist, int all, int human)
a5bd9f
+{
a5bd9f
+  char *device_name;
a5bd9f
+  grub_fs_t fs;
a5bd9f
+  const char *path;
a5bd9f
+  grub_device_t dev;
a5bd9f
 
a5bd9f
   device_name = grub_file_get_device_name (dirname);
a5bd9f
   dev = grub_device_open (device_name);
a5bd9f
@@ -221,10 +233,16 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human)
a5bd9f
     }
a5bd9f
   else if (fs)
a5bd9f
     {
a5bd9f
+      struct grub_ls_list_files_ctx ctx = {
a5bd9f
+	.dirname = dirname,
a5bd9f
+	.all = all,
a5bd9f
+	.human = human
a5bd9f
+      };
a5bd9f
+
a5bd9f
       if (longlist)
a5bd9f
-	(fs->dir) (dev, path, print_files_long);
a5bd9f
+	(fs->dir) (dev, path, print_files_long, &ctx);
a5bd9f
       else
a5bd9f
-	(fs->dir) (dev, path, print_files);
a5bd9f
+	(fs->dir) (dev, path, print_files, &ctx);
a5bd9f
 
a5bd9f
       if (grub_errno == GRUB_ERR_BAD_FILE_TYPE
a5bd9f
 	  && path[grub_strlen (path) - 1] != '/')
a5bd9f
@@ -250,9 +268,9 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human)
a5bd9f
 	  all = 1;
a5bd9f
 	  grub_memset (&info, 0, sizeof (info));
a5bd9f
 	  if (longlist)
a5bd9f
-	    print_files_long (p, &info);
a5bd9f
+	    print_files_long (p, &info, &ctx);
a5bd9f
 	  else
a5bd9f
-	    print_files (p, &info);
a5bd9f
+	    print_files (p, &info, &ctx);
a5bd9f
 
a5bd9f
 	  grub_free (dirname);
a5bd9f
 	}
a5bd9f
diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c
a5bd9f
index 3a0e0e0..e3347ee 100644
a5bd9f
--- a/grub-core/commands/test.c
a5bd9f
+++ b/grub-core/commands/test.c
a5bd9f
@@ -38,114 +38,125 @@ grub_strtosl (char *arg, char **end, int base)
a5bd9f
   return grub_strtoul (arg, end, base);
a5bd9f
 }
a5bd9f
 
a5bd9f
-/* Parse a test expression starting from *argn. */
a5bd9f
-static int
a5bd9f
-test_parse (char **args, int *argn, int argc)
a5bd9f
+/* Context for test_parse.  */
a5bd9f
+struct test_parse_ctx
a5bd9f
 {
a5bd9f
-  int ret = 0, discard = 0, invert = 0;
a5bd9f
+  int ret, discard, invert;
a5bd9f
   int file_exists;
a5bd9f
   struct grub_dirhook_info file_info;
a5bd9f
+  char *filename;
a5bd9f
+};
a5bd9f
+
a5bd9f
+/* Take care of discarding and inverting. */
a5bd9f
+static void
a5bd9f
+update_val (int val, struct test_parse_ctx *ctx)
a5bd9f
+{
a5bd9f
+  if (! ctx->discard)
a5bd9f
+    ctx->ret = ctx->invert ? ! val : val;
a5bd9f
+  ctx->invert = ctx->discard = 0;
a5bd9f
+}
a5bd9f
+
a5bd9f
+/* A hook for iterating directories. */
a5bd9f
+static int
a5bd9f
+find_file (const char *cur_filename, const struct grub_dirhook_info *info,
a5bd9f
+	   void *data)
a5bd9f
+{
a5bd9f
+  struct test_parse_ctx *ctx = data;
a5bd9f
+
a5bd9f
+  if ((info->case_insensitive ? grub_strcasecmp (cur_filename, ctx->filename)
a5bd9f
+       : grub_strcmp (cur_filename, ctx->filename)) == 0)
a5bd9f
+    {
a5bd9f
+      ctx->file_info = *info;
a5bd9f
+      ctx->file_exists = 1;
a5bd9f
+      return 1;
a5bd9f
+    }
a5bd9f
+  return 0;
a5bd9f
+}
a5bd9f
+
a5bd9f
+/* Check if file exists and fetch its information. */
a5bd9f
+static void
a5bd9f
+get_fileinfo (char *path, struct test_parse_ctx *ctx)
a5bd9f
+{
a5bd9f
+  char *pathname;
a5bd9f
+  char *device_name;
a5bd9f
+  grub_fs_t fs;
a5bd9f
+  grub_device_t dev;
a5bd9f
+
a5bd9f
+  ctx->file_exists = 0;
a5bd9f
+  device_name = grub_file_get_device_name (path);
a5bd9f
+  dev = grub_device_open (device_name);
a5bd9f
+  if (! dev)
a5bd9f
+    {
a5bd9f
+      grub_free (device_name);
a5bd9f
+      return;
a5bd9f
+    }
a5bd9f
 
a5bd9f
-  auto void update_val (int val);
a5bd9f
-  auto void get_fileinfo (char *pathname);
a5bd9f
-
a5bd9f
-  /* Take care of discarding and inverting. */
a5bd9f
-  void update_val (int val)
a5bd9f
-  {
a5bd9f
-    if (! discard)
a5bd9f
-      ret = invert ? ! val : val;
a5bd9f
-    invert = discard = 0;
a5bd9f
-  }
a5bd9f
-
a5bd9f
-  /* Check if file exists and fetch its information. */
a5bd9f
-  void get_fileinfo (char *path)
a5bd9f
-  {
a5bd9f
-    char *filename, *pathname;
a5bd9f
-    char *device_name;
a5bd9f
-    grub_fs_t fs;
a5bd9f
-    grub_device_t dev;
a5bd9f
-
a5bd9f
-    /* A hook for iterating directories. */
a5bd9f
-    auto int find_file (const char *cur_filename,
a5bd9f
-			const struct grub_dirhook_info *info);
a5bd9f
-    int find_file (const char *cur_filename,
a5bd9f
-		   const struct grub_dirhook_info *info)
a5bd9f
+  fs = grub_fs_probe (dev);
a5bd9f
+  if (! fs)
a5bd9f
     {
a5bd9f
-      if ((info->case_insensitive ? grub_strcasecmp (cur_filename, filename)
a5bd9f
-	   : grub_strcmp (cur_filename, filename)) == 0)
a5bd9f
+      grub_free (device_name);
a5bd9f
+      grub_device_close (dev);
a5bd9f
+      return;
a5bd9f
+    }
a5bd9f
+
a5bd9f
+  pathname = grub_strchr (path, ')');
a5bd9f
+  if (! pathname)
a5bd9f
+    pathname = path;
a5bd9f
+  else
a5bd9f
+    pathname++;
a5bd9f
+
a5bd9f
+  /* Remove trailing '/'. */
a5bd9f
+  while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
a5bd9f
+    pathname[grub_strlen (pathname) - 1] = 0;
a5bd9f
+
a5bd9f
+  /* Split into path and filename. */
a5bd9f
+  ctx->filename = grub_strrchr (pathname, '/');
a5bd9f
+  if (! ctx->filename)
a5bd9f
+    {
a5bd9f
+      path = grub_strdup ("/");
a5bd9f
+      ctx->filename = pathname;
a5bd9f
+    }
a5bd9f
+  else
a5bd9f
+    {
a5bd9f
+      ctx->filename++;
a5bd9f
+      path = grub_strdup (pathname);
a5bd9f
+      path[ctx->filename - pathname] = 0;
a5bd9f
+    }
a5bd9f
+
a5bd9f
+  /* It's the whole device. */
a5bd9f
+  if (! *pathname)
a5bd9f
+    {
a5bd9f
+      ctx->file_exists = 1;
a5bd9f
+      grub_memset (&ctx->file_info, 0, sizeof (ctx->file_info));
a5bd9f
+      /* Root is always a directory. */
a5bd9f
+      ctx->file_info.dir = 1;
a5bd9f
+
a5bd9f
+      /* Fetch writing time. */
a5bd9f
+      ctx->file_info.mtimeset = 0;
a5bd9f
+      if (fs->mtime)
a5bd9f
 	{
a5bd9f
-	  file_info = *info;
a5bd9f
-	  file_exists = 1;
a5bd9f
-	  return 1;
a5bd9f
+	  if (! fs->mtime (dev, &ctx->file_info.mtime))
a5bd9f
+	    ctx->file_info.mtimeset = 1;
a5bd9f
+	  grub_errno = GRUB_ERR_NONE;
a5bd9f
 	}
a5bd9f
-      return 0;
a5bd9f
     }
a5bd9f
+  else
a5bd9f
+    (fs->dir) (dev, path, find_file, ctx);
a5bd9f
+
a5bd9f
+  grub_device_close (dev);
a5bd9f
+  grub_free (path);
a5bd9f
+  grub_free (device_name);
a5bd9f
+}
a5bd9f
 
a5bd9f
-    file_exists = 0;
a5bd9f
-    device_name = grub_file_get_device_name (path);
a5bd9f
-    dev = grub_device_open (device_name);
a5bd9f
-    if (! dev)
a5bd9f
-      {
a5bd9f
-	grub_free (device_name);
a5bd9f
-	return;
a5bd9f
-      }
a5bd9f
-
a5bd9f
-    fs = grub_fs_probe (dev);
a5bd9f
-    if (! fs)
a5bd9f
-      {
a5bd9f
-	grub_free (device_name);
a5bd9f
-	grub_device_close (dev);
a5bd9f
-	return;
a5bd9f
-      }
a5bd9f
-
a5bd9f
-    pathname = grub_strchr (path, ')');
a5bd9f
-    if (! pathname)
a5bd9f
-      pathname = path;
a5bd9f
-    else
a5bd9f
-      pathname++;
a5bd9f
-
a5bd9f
-    /* Remove trailing '/'. */
a5bd9f
-    while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
a5bd9f
-      pathname[grub_strlen (pathname) - 1] = 0;
a5bd9f
-
a5bd9f
-    /* Split into path and filename. */
a5bd9f
-    filename = grub_strrchr (pathname, '/');
a5bd9f
-    if (! filename)
a5bd9f
-      {
a5bd9f
-	path = grub_strdup ("/");
a5bd9f
-	filename = pathname;
a5bd9f
-      }
a5bd9f
-    else
a5bd9f
-      {
a5bd9f
-	filename++;
a5bd9f
-	path = grub_strdup (pathname);
a5bd9f
-	path[filename - pathname] = 0;
a5bd9f
-      }
a5bd9f
-
a5bd9f
-    /* It's the whole device. */
a5bd9f
-    if (! *pathname)
a5bd9f
-      {
a5bd9f
-	file_exists = 1;
a5bd9f
-	grub_memset (&file_info, 0, sizeof (file_info));
a5bd9f
-	/* Root is always a directory. */
a5bd9f
-	file_info.dir = 1;
a5bd9f
-
a5bd9f
-	/* Fetch writing time. */
a5bd9f
-	file_info.mtimeset = 0;
a5bd9f
-	if (fs->mtime)
a5bd9f
-	  {
a5bd9f
-	    if (! fs->mtime (dev, &file_info.mtime))
a5bd9f
-	      file_info.mtimeset = 1;
a5bd9f
-	    grub_errno = GRUB_ERR_NONE;
a5bd9f
-	  }
a5bd9f
-      }
a5bd9f
-    else
a5bd9f
-      (fs->dir) (dev, path, find_file);
a5bd9f
-
a5bd9f
-    grub_device_close (dev);
a5bd9f
-    grub_free (path);
a5bd9f
-    grub_free (device_name);
a5bd9f
-  }
a5bd9f
+/* Parse a test expression starting from *argn. */
a5bd9f
+static int
a5bd9f
+test_parse (char **args, int *argn, int argc)
a5bd9f
+{
a5bd9f
+  struct test_parse_ctx ctx = {
a5bd9f
+    .ret = 0,
a5bd9f
+    .discard = 0,
a5bd9f
+    .invert = 0
a5bd9f
+  };
a5bd9f
 
a5bd9f
   /* Here we have the real parsing. */
a5bd9f
   while (*argn < argc)
a5bd9f
@@ -157,14 +168,16 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "=") == 0
a5bd9f
 	      || grub_strcmp (args[*argn + 1], "==") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0);
a5bd9f
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0,
a5bd9f
+			  &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "!=") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0);
a5bd9f
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0,
a5bd9f
+			  &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -172,28 +185,32 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  /* GRUB extension: lexicographical sorting. */
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "<") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0);
a5bd9f
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0,
a5bd9f
+			  &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "<=") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0);
a5bd9f
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0,
a5bd9f
+			  &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], ">") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0);
a5bd9f
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0,
a5bd9f
+			  &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], ">=") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0);
a5bd9f
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0,
a5bd9f
+			  &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -202,7 +219,7 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "-eq") == 0)
a5bd9f
 	    {
a5bd9f
 	      update_val (grub_strtosl (args[*argn], 0, 0)
a5bd9f
-			  == grub_strtosl (args[*argn + 2], 0, 0));
a5bd9f
+			  == grub_strtosl (args[*argn + 2], 0, 0), &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -210,7 +227,7 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "-ge") == 0)
a5bd9f
 	    {
a5bd9f
 	      update_val (grub_strtosl (args[*argn], 0, 0)
a5bd9f
-			  >= grub_strtosl (args[*argn + 2], 0, 0));
a5bd9f
+			  >= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -218,7 +235,7 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "-gt") == 0)
a5bd9f
 	    {
a5bd9f
 	      update_val (grub_strtosl (args[*argn], 0, 0)
a5bd9f
-			  > grub_strtosl (args[*argn + 2], 0, 0));
a5bd9f
+			  > grub_strtosl (args[*argn + 2], 0, 0), &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -226,7 +243,7 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "-le") == 0)
a5bd9f
 	    {
a5bd9f
 	      update_val (grub_strtosl (args[*argn], 0, 0)
a5bd9f
-		      <= grub_strtosl (args[*argn + 2], 0, 0));
a5bd9f
+		      <= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -234,7 +251,7 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "-lt") == 0)
a5bd9f
 	    {
a5bd9f
 	      update_val (grub_strtosl (args[*argn], 0, 0)
a5bd9f
-			  < grub_strtosl (args[*argn + 2], 0, 0));
a5bd9f
+			  < grub_strtosl (args[*argn + 2], 0, 0), &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -242,7 +259,7 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  if (grub_strcmp (args[*argn + 1], "-ne") == 0)
a5bd9f
 	    {
a5bd9f
 	      update_val (grub_strtosl (args[*argn], 0, 0)
a5bd9f
-			  != grub_strtosl (args[*argn + 2], 0, 0));
a5bd9f
+			  != grub_strtosl (args[*argn + 2], 0, 0), &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -265,10 +282,10 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 
a5bd9f
 	      if (grub_strcmp (args[*argn + 1], "-pgt") == 0)
a5bd9f
 		update_val (grub_strtoul (args[*argn] + i, 0, 0)
a5bd9f
-			    > grub_strtoul (args[*argn + 2] + i, 0, 0));
a5bd9f
+			    > grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
a5bd9f
 	      else
a5bd9f
 		update_val (grub_strtoul (args[*argn] + i, 0, 0)
a5bd9f
-			    < grub_strtoul (args[*argn + 2] + i, 0, 0));
a5bd9f
+			    < grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -283,22 +300,24 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	      int bias = 0;
a5bd9f
 
a5bd9f
 	      /* Fetch fileinfo. */
a5bd9f
-	      get_fileinfo (args[*argn]);
a5bd9f
-	      file1 = file_info;
a5bd9f
-	      file1exists = file_exists;
a5bd9f
-	      get_fileinfo (args[*argn + 2]);
a5bd9f
+	      get_fileinfo (args[*argn], &ctx);
a5bd9f
+	      file1 = ctx.file_info;
a5bd9f
+	      file1exists = ctx.file_exists;
a5bd9f
+	      get_fileinfo (args[*argn + 2], &ctx);
a5bd9f
 
a5bd9f
 	      if (args[*argn + 1][3])
a5bd9f
 		bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
a5bd9f
 
a5bd9f
 	      if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0)
a5bd9f
-		update_val ((file1exists && ! file_exists)
a5bd9f
-			    || (file1.mtimeset && file_info.mtimeset
a5bd9f
-				&& file1.mtime + bias > file_info.mtime));
a5bd9f
+		update_val ((file1exists && ! ctx.file_exists)
a5bd9f
+			    || (file1.mtimeset && ctx.file_info.mtimeset
a5bd9f
+				&& file1.mtime + bias > ctx.file_info.mtime),
a5bd9f
+			    &ctx);
a5bd9f
 	      else
a5bd9f
-		update_val ((! file1exists && file_exists)
a5bd9f
-			    || (file1.mtimeset && file_info.mtimeset
a5bd9f
-				&& file1.mtime + bias < file_info.mtime));
a5bd9f
+		update_val ((! file1exists && ctx.file_exists)
a5bd9f
+			    || (file1.mtimeset && ctx.file_info.mtimeset
a5bd9f
+				&& file1.mtime + bias < ctx.file_info.mtime),
a5bd9f
+			    &ctx);
a5bd9f
 	      (*argn) += 3;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -310,27 +329,27 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	  /* File tests. */
a5bd9f
 	  if (grub_strcmp (args[*argn], "-d") == 0)
a5bd9f
 	    {
a5bd9f
-	      get_fileinfo (args[*argn + 1]);
a5bd9f
-	      update_val (file_exists && file_info.dir);
a5bd9f
+	      get_fileinfo (args[*argn + 1], &ctx);
a5bd9f
+	      update_val (ctx.file_exists && ctx.file_info.dir, &ctx);
a5bd9f
 	      (*argn) += 2;
a5bd9f
-	      return ret;
a5bd9f
+	      return ctx.ret;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  if (grub_strcmp (args[*argn], "-e") == 0)
a5bd9f
 	    {
a5bd9f
-	      get_fileinfo (args[*argn + 1]);
a5bd9f
-	      update_val (file_exists);
a5bd9f
+	      get_fileinfo (args[*argn + 1], &ctx);
a5bd9f
+	      update_val (ctx.file_exists, &ctx);
a5bd9f
 	      (*argn) += 2;
a5bd9f
-	      return ret;
a5bd9f
+	      return ctx.ret;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  if (grub_strcmp (args[*argn], "-f") == 0)
a5bd9f
 	    {
a5bd9f
-	      get_fileinfo (args[*argn + 1]);
a5bd9f
+	      get_fileinfo (args[*argn + 1], &ctx);
a5bd9f
 	      /* FIXME: check for other types. */
a5bd9f
-	      update_val (file_exists && ! file_info.dir);
a5bd9f
+	      update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx);
a5bd9f
 	      (*argn) += 2;
a5bd9f
-	      return ret;
a5bd9f
+	      return ctx.ret;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  if (grub_strcmp (args[*argn], "-s") == 0)
a5bd9f
@@ -338,25 +357,25 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
 	      grub_file_t file;
a5bd9f
 	      grub_file_filter_disable_compression ();
a5bd9f
 	      file = grub_file_open (args[*argn + 1]);
a5bd9f
-	      update_val (file && (grub_file_size (file) != 0));
a5bd9f
+	      update_val (file && (grub_file_size (file) != 0), &ctx);
a5bd9f
 	      if (file)
a5bd9f
 		grub_file_close (file);
a5bd9f
 	      grub_errno = GRUB_ERR_NONE;
a5bd9f
 	      (*argn) += 2;
a5bd9f
-	      return ret;
a5bd9f
+	      return ctx.ret;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
 	  /* String tests. */
a5bd9f
 	  if (grub_strcmp (args[*argn], "-n") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (args[*argn + 1][0]);
a5bd9f
+	      update_val (args[*argn + 1][0], &ctx);
a5bd9f
 
a5bd9f
 	      (*argn) += 2;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
 	  if (grub_strcmp (args[*argn], "-z") == 0)
a5bd9f
 	    {
a5bd9f
-	      update_val (! args[*argn + 1][0]);
a5bd9f
+	      update_val (! args[*argn + 1][0], &ctx);
a5bd9f
 	      (*argn) += 2;
a5bd9f
 	      continue;
a5bd9f
 	    }
a5bd9f
@@ -368,42 +387,42 @@ test_parse (char **args, int *argn, int argc)
a5bd9f
       if (grub_strcmp (args[*argn], ")") == 0)
a5bd9f
 	{
a5bd9f
 	  (*argn)++;
a5bd9f
-	  return ret;
a5bd9f
+	  return ctx.ret;
a5bd9f
 	}
a5bd9f
       /* Recursively invoke if parenthesis. */
a5bd9f
       if (grub_strcmp (args[*argn], "(") == 0)
a5bd9f
 	{
a5bd9f
 	  (*argn)++;
a5bd9f
-	  update_val (test_parse (args, argn, argc));
a5bd9f
+	  update_val (test_parse (args, argn, argc), &ctx);
a5bd9f
 	  continue;
a5bd9f
 	}
a5bd9f
 
a5bd9f
       if (grub_strcmp (args[*argn], "!") == 0)
a5bd9f
 	{
a5bd9f
-	  invert = ! invert;
a5bd9f
+	  ctx.invert = ! ctx.invert;
a5bd9f
 	  (*argn)++;
a5bd9f
 	  continue;
a5bd9f
 	}
a5bd9f
       if (grub_strcmp (args[*argn], "-a") == 0)
a5bd9f
 	{
a5bd9f
 	  /* If current value is 0 second value is to be discarded. */
a5bd9f
-	  discard = ! ret;
a5bd9f
+	  ctx.discard = ! ctx.ret;
a5bd9f
 	  (*argn)++;
a5bd9f
 	  continue;
a5bd9f
 	}
a5bd9f
       if (grub_strcmp (args[*argn], "-o") == 0)
a5bd9f
 	{
a5bd9f
 	  /* If current value is 1 second value is to be discarded. */
a5bd9f
-	  discard = ret;
a5bd9f
+	  ctx.discard = ctx.ret;
a5bd9f
 	  (*argn)++;
a5bd9f
 	  continue;
a5bd9f
 	}
a5bd9f
 
a5bd9f
       /* No test found. Interpret if as just a string. */
a5bd9f
-      update_val (args[*argn][0]);
a5bd9f
+      update_val (args[*argn][0], &ctx);
a5bd9f
       (*argn)++;
a5bd9f
     }
a5bd9f
-  return ret;
a5bd9f
+  return ctx.ret;
a5bd9f
 }
a5bd9f
 
a5bd9f
 static grub_err_t
a5bd9f
diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c
a5bd9f
index 633de51..2807f80 100644
a5bd9f
--- a/grub-core/commands/wildcard.c
a5bd9f
+++ b/grub-core/commands/wildcard.c
a5bd9f
@@ -279,63 +279,75 @@ match_devices (const regex_t *regexp, int noparts)
a5bd9f
   return 0;
a5bd9f
 }
a5bd9f
 
a5bd9f
-static char **
a5bd9f
-match_files (const char *prefix, const char *suffix, const char *end,
a5bd9f
-	     const regex_t *regexp)
a5bd9f
+/* Context for match_files.  */
a5bd9f
+struct match_files_ctx
a5bd9f
 {
a5bd9f
-  int i;
a5bd9f
+  const regex_t *regexp;
a5bd9f
   char **files;
a5bd9f
   unsigned nfile;
a5bd9f
   char *dir;
a5bd9f
-  const char *path;
a5bd9f
-  char *device_name;
a5bd9f
-  grub_fs_t fs;
a5bd9f
-  grub_device_t dev;
a5bd9f
+};
a5bd9f
 
a5bd9f
-  auto int match (const char *name, const struct grub_dirhook_info *info);
a5bd9f
-  int match (const char *name, const struct grub_dirhook_info *info)
a5bd9f
-  {
a5bd9f
-    char **t;
a5bd9f
-    char *buffer;
a5bd9f
+/* Helper for match_files.  */
a5bd9f
+static int
a5bd9f
+match_files_iter (const char *name, const struct grub_dirhook_info *info,
a5bd9f
+		  void *data)
a5bd9f
+{
a5bd9f
+  struct match_files_ctx *ctx = data;
a5bd9f
+  char **t;
a5bd9f
+  char *buffer;
a5bd9f
 
a5bd9f
-    /* skip . and .. names */
a5bd9f
-    if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
a5bd9f
-      return 0;
a5bd9f
+  /* skip . and .. names */
a5bd9f
+  if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
a5bd9f
+    return 0;
a5bd9f
 
a5bd9f
-    grub_dprintf ("expand", "matching: %s in %s\n", name, dir);
a5bd9f
-    if (regexec (regexp, name, 0, 0, 0))
a5bd9f
-      return 0;
a5bd9f
+  grub_dprintf ("expand", "matching: %s in %s\n", name, ctx->dir);
a5bd9f
+  if (regexec (ctx->regexp, name, 0, 0, 0))
a5bd9f
+    return 0;
a5bd9f
 
a5bd9f
-    grub_dprintf ("expand", "matched\n");
a5bd9f
+  grub_dprintf ("expand", "matched\n");
a5bd9f
 
a5bd9f
-    buffer = grub_xasprintf ("%s%s", dir, name);
a5bd9f
-    if (! buffer)
a5bd9f
+  buffer = grub_xasprintf ("%s%s", ctx->dir, name);
a5bd9f
+  if (! buffer)
a5bd9f
+    return 1;
a5bd9f
+
a5bd9f
+  t = grub_realloc (ctx->files, sizeof (char*) * (ctx->nfile + 2));
a5bd9f
+  if (! t)
a5bd9f
+    {
a5bd9f
+      grub_free (buffer);
a5bd9f
       return 1;
a5bd9f
+    }
a5bd9f
 
a5bd9f
-    t = grub_realloc (files, sizeof (char*) * (nfile + 2));
a5bd9f
-    if (! t)
a5bd9f
-      {
a5bd9f
-	grub_free (buffer);
a5bd9f
-	return 1;
a5bd9f
-      }
a5bd9f
+  ctx->files = t;
a5bd9f
+  ctx->files[ctx->nfile++] = buffer;
a5bd9f
+  ctx->files[ctx->nfile] = 0;
a5bd9f
+  return 0;
a5bd9f
+}
a5bd9f
 
a5bd9f
-    files = t;
a5bd9f
-    files[nfile++] = buffer;
a5bd9f
-    files[nfile] = 0;
a5bd9f
-    return 0;
a5bd9f
-  }
a5bd9f
+static char **
a5bd9f
+match_files (const char *prefix, const char *suffix, const char *end,
a5bd9f
+	     const regex_t *regexp)
a5bd9f
+{
a5bd9f
+  struct match_files_ctx ctx = {
a5bd9f
+    .regexp = regexp,
a5bd9f
+    .nfile = 0,
a5bd9f
+    .files = 0
a5bd9f
+  };
a5bd9f
+  int i;
a5bd9f
+  const char *path;
a5bd9f
+  char *device_name;
a5bd9f
+  grub_fs_t fs;
a5bd9f
+  grub_device_t dev;
a5bd9f
 
a5bd9f
-  nfile = 0;
a5bd9f
-  files = 0;
a5bd9f
   dev = 0;
a5bd9f
   device_name = 0;
a5bd9f
   grub_error_push ();
a5bd9f
 
a5bd9f
-  dir = make_dir (prefix, suffix, end);
a5bd9f
-  if (! dir)
a5bd9f
+  ctx.dir = make_dir (prefix, suffix, end);
a5bd9f
+  if (! ctx.dir)
a5bd9f
     goto fail;
a5bd9f
 
a5bd9f
-  device_name = grub_file_get_device_name (dir);
a5bd9f
+  device_name = grub_file_get_device_name (ctx.dir);
a5bd9f
   dev = grub_device_open (device_name);
a5bd9f
   if (! dev)
a5bd9f
     goto fail;
a5bd9f
@@ -344,33 +356,33 @@ match_files (const char *prefix, const char *suffix, const char *end,
a5bd9f
   if (! fs)
a5bd9f
     goto fail;
a5bd9f
 
a5bd9f
-  if (dir[0] == '(')
a5bd9f
+  if (ctx.dir[0] == '(')
a5bd9f
     {
a5bd9f
-      path = grub_strchr (dir, ')');
a5bd9f
+      path = grub_strchr (ctx.dir, ')');
a5bd9f
       if (!path)
a5bd9f
 	goto fail;
a5bd9f
       path++;
a5bd9f
     }
a5bd9f
   else
a5bd9f
-    path = dir;
a5bd9f
+    path = ctx.dir;
a5bd9f
 
a5bd9f
-  if (fs->dir (dev, path, match))
a5bd9f
+  if (fs->dir (dev, path, match_files_iter, &ctx))
a5bd9f
     goto fail;
a5bd9f
 
a5bd9f
-  grub_free (dir);
a5bd9f
+  grub_free (ctx.dir);
a5bd9f
   grub_device_close (dev);
a5bd9f
   grub_free (device_name);
a5bd9f
   grub_error_pop ();
a5bd9f
-  return files;
a5bd9f
+  return ctx.files;
a5bd9f
 
a5bd9f
  fail:
a5bd9f
 
a5bd9f
-  grub_free (dir);
a5bd9f
+  grub_free (ctx.dir);
a5bd9f
 
a5bd9f
-  for (i = 0; files && files[i]; i++)
a5bd9f
-    grub_free (files[i]);
a5bd9f
+  for (i = 0; ctx.files && ctx.files[i]; i++)
a5bd9f
+    grub_free (ctx.files[i]);
a5bd9f
 
a5bd9f
-  grub_free (files);
a5bd9f
+  grub_free (ctx.files);
a5bd9f
 
a5bd9f
   if (dev)
a5bd9f
     grub_device_close (dev);
a5bd9f
@@ -381,28 +393,42 @@ match_files (const char *prefix, const char *suffix, const char *end,
a5bd9f
   return 0;
a5bd9f
 }
a5bd9f
 
a5bd9f
+/* Context for check_file.  */
a5bd9f
+struct check_file_ctx
a5bd9f
+{
a5bd9f
+  const char *basename;
a5bd9f
+  int found;
a5bd9f
+};
a5bd9f
+
a5bd9f
+/* Helper for check_file.  */
a5bd9f
+static int
a5bd9f
+check_file_iter (const char *name, const struct grub_dirhook_info *info,
a5bd9f
+		 void *data)
a5bd9f
+{
a5bd9f
+  struct check_file_ctx *ctx = data;
a5bd9f
+
a5bd9f
+  if (ctx->basename[0] == 0
a5bd9f
+      || (info->case_insensitive ? grub_strcasecmp (name, ctx->basename) == 0
a5bd9f
+	  : grub_strcmp (name, ctx->basename) == 0))
a5bd9f
+    {
a5bd9f
+      ctx->found = 1;
a5bd9f
+      return 1;
a5bd9f
+    }
a5bd9f
+  
a5bd9f
+  return 0;
a5bd9f
+}
a5bd9f
+
a5bd9f
 static int
a5bd9f
 check_file (const char *dir, const char *basename)
a5bd9f
 {
a5bd9f
+  struct check_file_ctx ctx = {
a5bd9f
+    .basename = basename,
a5bd9f
+    .found = 0
a5bd9f
+  };
a5bd9f
   grub_fs_t fs;
a5bd9f
   grub_device_t dev;
a5bd9f
-  int found = 0;
a5bd9f
   const char *device_name, *path;
a5bd9f
 
a5bd9f
-  auto int match (const char *name, const struct grub_dirhook_info *info);
a5bd9f
-  int match (const char *name, const struct grub_dirhook_info *info)
a5bd9f
-  {
a5bd9f
-    if (basename[0] == 0
a5bd9f
-	|| (info->case_insensitive ? grub_strcasecmp (name, basename) == 0
a5bd9f
-	    : grub_strcmp (name, basename) == 0))
a5bd9f
-      {
a5bd9f
-	found = 1;
a5bd9f
-	return 1;
a5bd9f
-      }
a5bd9f
-    
a5bd9f
-    return 0;
a5bd9f
-  }
a5bd9f
-
a5bd9f
   device_name = grub_file_get_device_name (dir);
a5bd9f
   dev = grub_device_open (device_name);
a5bd9f
   if (! dev)
a5bd9f
@@ -422,14 +448,14 @@ check_file (const char *dir, const char *basename)
a5bd9f
   else
a5bd9f
     path = dir;
a5bd9f
 
a5bd9f
-  fs->dir (dev, path[0] ? path : "/", match);
a5bd9f
+  fs->dir (dev, path[0] ? path : "/", check_file_iter, &ctx);
a5bd9f
   if (grub_errno == 0 && basename[0] == 0)
a5bd9f
-    found = 1;
a5bd9f
+    ctx.found = 1;
a5bd9f
 
a5bd9f
  fail:
a5bd9f
   grub_errno = 0;
a5bd9f
 
a5bd9f
-  return found;
a5bd9f
+  return ctx.found;
a5bd9f
 }
a5bd9f
 
a5bd9f
 static void
a5bd9f
diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c
a5bd9f
index 848a455..6c49e5d 100644
a5bd9f
--- a/grub-core/fs/affs.c
a5bd9f
+++ b/grub-core/fs/affs.c
a5bd9f
@@ -316,93 +316,93 @@ grub_affs_read_symlink (grub_fshelp_node_t node)
a5bd9f
 }
a5bd9f
 
a5bd9f
 
a5bd9f
+/* Helper for grub_affs_iterate_dir.  */
a5bd9f
 static int
a5bd9f
-grub_affs_iterate_dir (grub_fshelp_node_t dir,
a5bd9f
-		       int NESTED_FUNC_ATTR
a5bd9f
-		       (*hook) (const char *filename,
a5bd9f
-				enum grub_fshelp_filetype filetype,
a5bd9f
-				grub_fshelp_node_t node))
a5bd9f
+grub_affs_create_node (grub_fshelp_node_t dir,
a5bd9f
+		       grub_fshelp_iterate_dir_hook_t hook, void *hook_data,
a5bd9f
+		       struct grub_fshelp_node **node,
a5bd9f
+		       grub_uint32_t **hashtable,
a5bd9f
+		       grub_uint32_t block, const struct grub_affs_file *fil)
a5bd9f
 {
a5bd9f
-  unsigned int i;
a5bd9f
-  struct grub_affs_file file;
a5bd9f
-  struct grub_fshelp_node *node = 0;
a5bd9f
   struct grub_affs_data *data = dir->data;
a5bd9f
-  grub_uint32_t *hashtable;
a5bd9f
-
a5bd9f
-  auto int NESTED_FUNC_ATTR grub_affs_create_node (grub_uint32_t block,
a5bd9f
-						   const struct grub_affs_file *fil);
a5bd9f
+  int type;
a5bd9f
+  grub_uint8_t name_u8[sizeof (fil->name) * GRUB_MAX_UTF8_PER_LATIN1 + 1];
a5bd9f
+  grub_size_t len;
a5bd9f
+  unsigned int nest;
a5bd9f
 
a5bd9f
-  int NESTED_FUNC_ATTR grub_affs_create_node (grub_uint32_t block,
a5bd9f
-					      const struct grub_affs_file *fil)
a5bd9f
+  *node = grub_zalloc (sizeof (**node));
a5bd9f
+  if (!*node)
a5bd9f
     {
a5bd9f
-      int type;
a5bd9f
-      grub_uint8_t name_u8[sizeof (fil->name) * GRUB_MAX_UTF8_PER_LATIN1 + 1];
a5bd9f
-      grub_size_t len;
a5bd9f
-      unsigned int nest;
a5bd9f
-
a5bd9f
-      node = grub_zalloc (sizeof (*node));
a5bd9f
-      if (!node)
a5bd9f
-	{
a5bd9f
-	  grub_free (hashtable);
a5bd9f
-	  return 1;
a5bd9f
-	}
a5bd9f
+      grub_free (*hashtable);
a5bd9f
+      return 1;
a5bd9f
+    }
a5bd9f
 
a5bd9f
-      node->data = data;
a5bd9f
-      node->block = block;
a5bd9f
-      node->parent = dir;
a5bd9f
-
a5bd9f
-      len = fil->namelen;
a5bd9f
-      if (len > sizeof (fil->name))
a5bd9f
-	len = sizeof (fil->name);
a5bd9f
-      *grub_latin1_to_utf8 (name_u8, fil->name, len) = '\0';
a5bd9f
-      
a5bd9f
-      node->di = *fil;
a5bd9f
-      for (nest = 0; nest < 8; nest++)
a5bd9f
+  (*node)->data = data;
a5bd9f
+  (*node)->block = block;
a5bd9f
+  (*node)->parent = dir;
a5bd9f
+
a5bd9f
+  len = fil->namelen;
a5bd9f
+  if (len > sizeof (fil->name))
a5bd9f
+    len = sizeof (fil->name);
a5bd9f
+  *grub_latin1_to_utf8 (name_u8, fil->name, len) = '\0';
a5bd9f
+  
a5bd9f
+  (*node)->di = *fil;
a5bd9f
+  for (nest = 0; nest < 8; nest++)
a5bd9f
+    {
a5bd9f
+      switch ((*node)->di.type)
a5bd9f
 	{
a5bd9f
-	  switch (node->di.type)
a5bd9f
-	    {
a5bd9f
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_REG):
a5bd9f
-	      type = GRUB_FSHELP_REG;
a5bd9f
-	      break;
a5bd9f
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_DIR):
a5bd9f
-	      type = GRUB_FSHELP_DIR;
a5bd9f
-	      break;
a5bd9f
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_SYMLINK):
a5bd9f
-	      type = GRUB_FSHELP_SYMLINK;
a5bd9f
-	      break;
a5bd9f
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_HARDLINK):
a5bd9f
-	      {
a5bd9f
-		grub_err_t err;
a5bd9f
-		node->block = grub_be_to_cpu32 (node->di.hardlink);
a5bd9f
-		err = grub_disk_read (data->disk,
a5bd9f
-				      (((grub_uint64_t) node->block + 1) << data->log_blocksize)
a5bd9f
-				      - 1,
a5bd9f
-				      GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION,
a5bd9f
-				      sizeof (node->di), (char *) &node->di);
a5bd9f
-		if (err)
a5bd9f
-		  return 1;
a5bd9f
-		continue;
a5bd9f
-	      }
a5bd9f
-	    default:
a5bd9f
-	      return 0;
a5bd9f
-	    }
a5bd9f
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_REG):
a5bd9f
+	  type = GRUB_FSHELP_REG;
a5bd9f
+	  break;
a5bd9f
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_DIR):
a5bd9f
+	  type = GRUB_FSHELP_DIR;
a5bd9f
+	  break;
a5bd9f
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_SYMLINK):
a5bd9f
+	  type = GRUB_FSHELP_SYMLINK;
a5bd9f
 	  break;
a5bd9f
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_HARDLINK):
a5bd9f
+	  {
a5bd9f
+	    grub_err_t err;
a5bd9f
+	    (*node)->block = grub_be_to_cpu32 ((*node)->di.hardlink);
a5bd9f
+	    err = grub_disk_read (data->disk,
a5bd9f
+				  (((grub_uint64_t) (*node)->block + 1) << data->log_blocksize)
a5bd9f
+				  - 1,
a5bd9f
+				  GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION,
a5bd9f
+				  sizeof ((*node)->di), (char *) &(*node)->di);
a5bd9f
+	    if (err)
a5bd9f
+	      return 1;
a5bd9f
+	    continue;
a5bd9f
+	  }
a5bd9f
+	default:
a5bd9f
+	  return 0;
a5bd9f
 	}
a5bd9f
+      break;
a5bd9f
+    }
a5bd9f
 
a5bd9f
-      if (nest == 8)
a5bd9f
-	return 0;
a5bd9f
+  if (nest == 8)
a5bd9f
+    return 0;
a5bd9f
 
a5bd9f
-      type |= GRUB_FSHELP_CASE_INSENSITIVE;
a5bd9f
+  type |= GRUB_FSHELP_CASE_INSENSITIVE;
a5bd9f
 
a5bd9f
-      if (hook ((char *) name_u8, type, node))
a5bd9f
-	{
a5bd9f
-	  grub_free (hashtable);
a5bd9f
-	  node = 0;
a5bd9f
-	  return 1;
a5bd9f
-	}
a5bd9f
-      node = 0;
a5bd9f
-      return 0;
a5bd9f
+  if (hook ((char *) name_u8, type, *node, hook_data))
a5bd9f
+    {
a5bd9f
+      grub_free (*hashtable);
a5bd9f
+      *node = 0;
a5bd9f
+      return 1;
a5bd9f
     }
a5bd9f
+  *node = 0;
a5bd9f
+  return 0;
a5bd9f
+}
a5bd9f
+
a5bd9f
+static int
a5bd9f
+grub_affs_iterate_dir (grub_fshelp_node_t dir,
a5bd9f
+		       grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
a5bd9f
+{
a5bd9f
+  unsigned int i;
a5bd9f
+  struct grub_affs_file file;
a5bd9f
+  struct grub_fshelp_node *node = 0;
a5bd9f
+  struct grub_affs_data *data = dir->data;
a5bd9f
+  grub_uint32_t *hashtable;
a5bd9f
 
a5bd9f
   /* Create the directory entries for `.' and `..'.  */
a5bd9f
   node = grub_zalloc (sizeof (*node));
a5bd9f
@@ -410,7 +410,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
a5bd9f
     return 1;
a5bd9f
     
a5bd9f
   *node = *dir;
a5bd9f
-  if (hook (".", GRUB_FSHELP_DIR, node))
a5bd9f
+  if (hook (".", GRUB_FSHELP_DIR, node, hook_data))
a5bd9f
     return 1;
a5bd9f
   if (dir->parent)
a5bd9f
     {
a5bd9f
@@ -418,7 +418,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
a5bd9f
       if (!node)
a5bd9f
 	return 1;
a5bd9f
       *node = *dir->parent;
a5bd9f
-      if (hook ("..", GRUB_FSHELP_DIR, node))
a5bd9f
+      if (hook ("..", GRUB_FSHELP_DIR, node, hook_data))
a5bd9f
 	return 1;
a5bd9f
     }
a5bd9f
 
a5bd9f
@@ -454,7 +454,8 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
a5bd9f
 	  if (grub_errno)
a5bd9f
 	    goto fail;
a5bd9f
 
a5bd9f
-	  if (grub_affs_create_node (next, &file))
a5bd9f
+	  if (grub_affs_create_node (dir, hook, hook_data, &node, &hashtable,
a5bd9f
+				     next, &file))
a5bd9f
 	    return 1;
a5bd9f
 
a5bd9f
 	  next = grub_be_to_cpu32 (file.next);
a5bd9f
@@ -545,31 +546,37 @@ aftime2ctime (const struct grub_affs_time *t)
a5bd9f
     + 8 * 365 * 86400 + 86400 * 2;
a5bd9f
 }
a5bd9f
 
a5bd9f
+/* Context for grub_affs_dir.  */
a5bd9f
+struct grub_affs_dir_ctx
a5bd9f
+{
a5bd9f
+  grub_fs_dir_hook_t hook;
a5bd9f
+  void *hook_data;
a5bd9f
+};
a5bd9f
+
a5bd9f
+/* Helper for grub_affs_dir.  */
a5bd9f
+static int
a5bd9f
+grub_affs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
a5bd9f
+		    grub_fshelp_node_t node, void *data)
a5bd9f
+{
a5bd9f
+  struct grub_affs_dir_ctx *ctx = data;
a5bd9f
+  struct grub_dirhook_info info;
a5bd9f
+
a5bd9f
+  grub_memset (&info, 0, sizeof (info));
a5bd9f
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
a5bd9f
+  info.mtimeset = 1;
a5bd9f
+  info.mtime = aftime2ctime (&node->di.mtime);
a5bd9f
+  grub_free (node);
a5bd9f
+  return ctx->hook (filename, &info, ctx->hook_data);
a5bd9f
+}
a5bd9f
+
a5bd9f
 static grub_err_t
a5bd9f
 grub_affs_dir (grub_device_t device, const char *path,
a5bd9f
-	       int (*hook) (const char *filename,
a5bd9f
-			    const struct grub_dirhook_info *info))
a5bd9f
+	       grub_fs_dir_hook_t hook, void *hook_data)
a5bd9f
 {
a5bd9f
+  struct grub_affs_dir_ctx ctx = { hook, hook_data };
a5bd9f
   struct grub_affs_data *data = 0;
a5bd9f
   struct grub_fshelp_node *fdiro = 0;
a5bd9f
 
a5bd9f
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
a5bd9f
-				     enum grub_fshelp_filetype filetype,
a5bd9f
-				     grub_fshelp_node_t node);
a5bd9f
-
a5bd9f
-  int NESTED_FUNC_ATTR iterate (const char *filename,
a5bd9f
-				enum grub_fshelp_filetype filetype,
a5bd9f
-				grub_fshelp_node_t node)
a5bd9f
-    {
a5bd9f
-      struct grub_dirhook_info info;
a5bd9f
-      grub_memset (&info, 0, sizeof (info));
a5bd9f
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
a5bd9f
-      info.mtimeset = 1;
a5bd9f
-      info.mtime = aftime2ctime (&node->di.mtime);
a5bd9f
-      grub_free (node);
a5bd9f
-      return hook (filename, &info);
a5bd9f
-    }
a5bd9f
-
a5bd9f
   grub_dl_ref (my_mod);
a5bd9f
 
a5bd9f
   data = grub_affs_mount (device->disk);
a5bd9f
@@ -581,7 +588,7 @@ grub_affs_dir (grub_device_t device, const char *path,
a5bd9f
   if (grub_errno)
a5bd9f
     goto fail;
a5bd9f
 
a5bd9f
-  grub_affs_iterate_dir (fdiro, iterate);
a5bd9f
+  grub_affs_iterate_dir (fdiro, grub_affs_dir_iter, &ctx);
a5bd9f
 
a5bd9f
  fail:
a5bd9f
   if (data && fdiro != &data->diropen)
a5bd9f
diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c
a5bd9f
index 318dc3e..fa2fc3f 100644
a5bd9f
--- a/grub-core/fs/bfs.c
a5bd9f
+++ b/grub-core/fs/bfs.c
a5bd9f
@@ -173,6 +173,15 @@ struct grub_bfs_data
a5bd9f
   struct grub_bfs_inode ino[0];
a5bd9f
 };
a5bd9f
 
a5bd9f
+/* Context for grub_bfs_dir.  */
a5bd9f
+struct grub_bfs_dir_ctx
a5bd9f
+{
a5bd9f
+  grub_device_t device;
a5bd9f
+  grub_fs_dir_hook_t hook;
a5bd9f
+  void *hook_data;
a5bd9f
+  struct grub_bfs_superblock sb;
a5bd9f
+};
a5bd9f
+
a5bd9f
 static grub_err_t
a5bd9f
 read_extent (grub_disk_t disk,
a5bd9f
 	     const struct grub_bfs_superblock *sb,
a5bd9f
@@ -413,7 +422,9 @@ static int
a5bd9f
 iterate_in_b_tree (grub_disk_t disk,
a5bd9f
 		   const struct grub_bfs_superblock *sb,
a5bd9f
 		   const struct grub_bfs_inode *ino,
a5bd9f
-		   int NESTED_FUNC_ATTR (*hook) (const char *name, grub_uint64_t value))
a5bd9f
+		   int (*hook) (const char *name, grub_uint64_t value,
a5bd9f
+				struct grub_bfs_dir_ctx *ctx),
a5bd9f
+		   struct grub_bfs_dir_ctx *ctx)
a5bd9f
 {
a5bd9f
   struct grub_bfs_btree_header head;
a5bd9f
   grub_err_t err;
a5bd9f
@@ -496,7 +507,8 @@ iterate_in_b_tree (grub_disk_t disk,
a5bd9f
 	      end = grub_bfs_to_cpu_treehead (node.total_key_len);
a5bd9f
 	    c = key_data[end];
a5bd9f
 	    key_data[end] = 0;
a5bd9f
-	    if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i])))
a5bd9f
+	    if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i]),
a5bd9f
+		      ctx))
a5bd9f
 	      return 1;
a5bd9f
 	    key_data[end] = c;
a5bd9f
 	  }
a5bd9f
@@ -844,46 +856,52 @@ mount (grub_disk_t disk, struct grub_bfs_superblock *sb)
a5bd9f
   return GRUB_ERR_NONE;
a5bd9f
 }
a5bd9f
 
a5bd9f
-static grub_err_t
a5bd9f
-grub_bfs_dir (grub_device_t device, const char *path,
a5bd9f
-	      int (*hook_in) (const char *filename,
a5bd9f
-			      const struct grub_dirhook_info * info))
a5bd9f
+/* Helper for grub_bfs_dir.  */
a5bd9f
+static int
a5bd9f
+grub_bfs_dir_iter (const char *name, grub_uint64_t value,
a5bd9f
+		   struct grub_bfs_dir_ctx *ctx)
a5bd9f
 {
a5bd9f
-  struct grub_bfs_superblock sb;
a5bd9f
-  grub_err_t err;
a5bd9f
-  auto int NESTED_FUNC_ATTR hook (const char *name, grub_uint64_t value);
a5bd9f
-
a5bd9f
-  int NESTED_FUNC_ATTR hook (const char *name, grub_uint64_t value)
a5bd9f
+  grub_err_t err2;
a5bd9f
+  union
a5bd9f
   {
a5bd9f
-    grub_err_t err2;
a5bd9f
-    union
a5bd9f
-    {
a5bd9f
-      struct grub_bfs_inode ino;
a5bd9f
-      grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
a5bd9f
-    } ino;
a5bd9f
-    struct grub_dirhook_info info;
a5bd9f
+    struct grub_bfs_inode ino;
a5bd9f
+    grub_uint8_t raw[grub_bfs_to_cpu32 (ctx->sb.bsize)];
a5bd9f
+  } ino;
a5bd9f
+  struct grub_dirhook_info info;
a5bd9f
 
a5bd9f
-    err2 = grub_disk_read (device->disk, value
a5bd9f
-			   << (grub_bfs_to_cpu32 (sb.log2_bsize)
a5bd9f
-			       - GRUB_DISK_SECTOR_BITS), 0,
a5bd9f
-			   grub_bfs_to_cpu32 (sb.bsize), (char *) ino.raw);
a5bd9f
-    if (err2)
a5bd9f
-      {
a5bd9f
-	grub_print_error ();
a5bd9f
-	return 0;
a5bd9f
-      }
a5bd9f
+  err2 = grub_disk_read (ctx->device->disk, value
a5bd9f
+			 << (grub_bfs_to_cpu32 (ctx->sb.log2_bsize)
a5bd9f
+			     - GRUB_DISK_SECTOR_BITS), 0,
a5bd9f
+			 grub_bfs_to_cpu32 (ctx->sb.bsize), (char *) ino.raw);
a5bd9f
+  if (err2)
a5bd9f
+    {
a5bd9f
+      grub_print_error ();
a5bd9f
+      return 0;
a5bd9f
+    }
a5bd9f
 
a5bd9f
-    info.mtimeset = 1;
a5bd9f
+  info.mtimeset = 1;
a5bd9f
 #ifdef MODE_AFS
a5bd9f
-    info.mtime =
a5bd9f
-      grub_divmod64 (grub_bfs_to_cpu64 (ino.ino.mtime), 1000000, 0);
a5bd9f
+  info.mtime =
a5bd9f
+    grub_divmod64 (grub_bfs_to_cpu64 (ino.ino.mtime), 1000000, 0);
a5bd9f
 #else
a5bd9f
-    info.mtime = grub_bfs_to_cpu64 (ino.ino.mtime) >> 16;
a5bd9f
+  info.mtime = grub_bfs_to_cpu64 (ino.ino.mtime) >> 16;
a5bd9f
 #endif
a5bd9f
-    info.dir = ((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) == ATTR_DIR);
a5bd9f
-    return hook_in (name, &info);
a5bd9f
-  }
a5bd9f
-  err = mount (device->disk, &sb);
a5bd9f
+  info.dir = ((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) == ATTR_DIR);
a5bd9f
+  return ctx->hook (name, &info, ctx->hook_data);
a5bd9f
+}
a5bd9f
+
a5bd9f
+static grub_err_t
a5bd9f
+grub_bfs_dir (grub_device_t device, const char *path,
a5bd9f
+	      grub_fs_dir_hook_t hook, void *hook_data)
a5bd9f
+{
a5bd9f
+  struct grub_bfs_dir_ctx ctx = {
a5bd9f
+    .device = device,
a5bd9f
+    .hook = hook,
a5bd9f
+    .hook_data = hook_data
a5bd9f
+  };
a5bd9f
+  grub_err_t err;
a5bd9f
+
a5bd9f
+  err = mount (device->disk, &ctx.sb);
a5bd9f
   if (err)
a5bd9f
     return err;
a5bd9f
 
a5bd9f
@@ -891,14 +909,15 @@ grub_bfs_dir (grub_device_t device, const char *path,
a5bd9f
     union
a5bd9f
     {
a5bd9f
       struct grub_bfs_inode ino;
a5bd9f
-      grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
a5bd9f
+      grub_uint8_t raw[grub_bfs_to_cpu32 (ctx.sb.bsize)];
a5bd9f
     } ino;
a5bd9f
-    err = find_file (path, device->disk, &sb, &ino.ino);
a5bd9f
+    err = find_file (path, device->disk, &ctx.sb, &ino.ino);
a5bd9f
     if (err)
a5bd9f
       return err;
a5bd9f
     if (((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) != ATTR_DIR))
a5bd9f
       return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
a5bd9f
-    iterate_in_b_tree (device->disk, &sb, &ino.ino, hook);
a5bd9f
+    iterate_in_b_tree (device->disk, &ctx.sb, &ino.ino, grub_bfs_dir_iter,
a5bd9f
+		       &ctx);
a5bd9f
   }
a5bd9f
 
a5bd9f
   return grub_errno;
a5bd9f
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
a5bd9f
index bcc75ba..196f301 100644
a5bd9f
--- a/grub-core/fs/btrfs.c
a5bd9f
+++ b/grub-core/fs/btrfs.c
a5bd9f
@@ -1491,8 +1491,7 @@ find_path (struct grub_btrfs_data *data,
a5bd9f
 
a5bd9f
 static grub_err_t
a5bd9f
 grub_btrfs_dir (grub_device_t device, const char *path,
a5bd9f
-		int (*hook) (const char *filename,
a5bd9f
-			     const struct grub_dirhook_info *info))
a5bd9f
+		grub_fs_dir_hook_t hook, void *hook_data)
a5bd9f
 {
a5bd9f
   struct grub_btrfs_data *data = grub_btrfs_mount (device);
a5bd9f
   struct grub_btrfs_key key_in, key_out;
a5bd9f
@@ -1586,7 +1585,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
a5bd9f
 	  c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
a5bd9f
 	  cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
a5bd9f
 	  info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY);
a5bd9f
-	  if (hook (cdirel->name, &info))
a5bd9f
+	  if (hook (cdirel->name, &info, hook_data))
a5bd9f
 	    goto out;
a5bd9f
 	  cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
a5bd9f
 	}
a5bd9f
diff --git a/grub-core/fs/cpio.c b/grub-core/fs/cpio.c
a5bd9f
index e9236cd..71d7fa4 100644
a5bd9f
--- a/grub-core/fs/cpio.c
a5bd9f
+++ b/grub-core/fs/cpio.c
a5bd9f
@@ -513,8 +513,7 @@ handle_symlink (struct grub_cpio_data *data,
a5bd9f
 
a5bd9f
 static grub_err_t
a5bd9f
 grub_cpio_dir (grub_device_t device, const char *path_in,
a5bd9f
-	       int (*hook) (const char *filename,
a5bd9f
-			    const struct grub_dirhook_info *info))
a5bd9f
+	       grub_fs_dir_hook_t hook, void *hook_data)
a5bd9f
 {
a5bd9f
   struct grub_cpio_data *data;
a5bd9f
   grub_disk_addr_t ofs;
a5bd9f
@@ -575,7 +574,7 @@ grub_cpio_dir (grub_device_t device, const char *path_in,
a5bd9f
 	      info.mtime = mtime;
a5bd9f
 	      info.mtimeset = 1;
a5bd9f
 
a5bd9f
-	      if (hook (n, &info))
a5bd9f
+	      if (hook (n, &info, hook_data))
a5bd9f
 		{
a5bd9f
 		  grub_free (name);
a5bd9f
 		  goto fail;
a5bd9f
diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c
a5bd9f
index cf2e2f4..0ebde35 100644
a5bd9f
--- a/grub-core/fs/ext2.c
a5bd9f
+++ b/grub-core/fs/ext2.c
a5bd9f
@@ -692,10 +692,7 @@ grub_ext2_read_symlink (grub_fshelp_node_t node)
a5bd9f
 
a5bd9f
 static int
a5bd9f
 grub_ext2_iterate_dir (grub_fshelp_node_t dir,
a5bd9f
-		       int NESTED_FUNC_ATTR
a5bd9f
-		       (*hook) (const char *filename,
a5bd9f
-				enum grub_fshelp_filetype filetype,
a5bd9f
-				grub_fshelp_node_t node))
a5bd9f
+		       grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
a5bd9f
 {
a5bd9f
   unsigned int fpos = 0;
a5bd9f
   struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
a5bd9f
@@ -777,7 +774,7 @@ grub_ext2_iterate_dir (grub_fshelp_node_t dir,
a5bd9f
 		type = GRUB_FSHELP_REG;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
-	  if (hook (filename, type, fdiro))
a5bd9f
+	  if (hook (filename, type, fdiro, hook_data))
a5bd9f
 	    return 1;
a5bd9f
 	}
a5bd9f
 
a5bd9f
@@ -858,59 +855,69 @@ grub_ext2_read (grub_file_t file, char *buf, grub_size_t len)
a5bd9f
 }
a5bd9f
 
a5bd9f
 
a5bd9f
-static grub_err_t
a5bd9f
-grub_ext2_dir (grub_device_t device, const char *path,
a5bd9f
-	       int (*hook) (const char *filename,
a5bd9f
-			    const struct grub_dirhook_info *info))
a5bd9f
+/* Context for grub_ext2_dir.  */
a5bd9f
+struct grub_ext2_dir_ctx
a5bd9f
 {
a5bd9f
-  struct grub_ext2_data *data = 0;
a5bd9f
-  struct grub_fshelp_node *fdiro = 0;
a5bd9f
+  grub_fs_dir_hook_t hook;
a5bd9f
+  void *hook_data;
a5bd9f
+  struct grub_ext2_data *data;
a5bd9f
+};
a5bd9f
 
a5bd9f
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
a5bd9f
-				     enum grub_fshelp_filetype filetype,
a5bd9f
-				     grub_fshelp_node_t node);
a5bd9f
+/* Helper for grub_ext2_dir.  */
a5bd9f
+static int
a5bd9f
+grub_ext2_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
a5bd9f
+		    grub_fshelp_node_t node, void *data)
a5bd9f
+{
a5bd9f
+  struct grub_ext2_dir_ctx *ctx = data;
a5bd9f
+  struct grub_dirhook_info info;
a5bd9f
 
a5bd9f
-  int NESTED_FUNC_ATTR iterate (const char *filename,
a5bd9f
-				enum grub_fshelp_filetype filetype,
a5bd9f
-				grub_fshelp_node_t node)
a5bd9f
+  grub_memset (&info, 0, sizeof (info));
a5bd9f
+  if (! node->inode_read)
a5bd9f
     {
a5bd9f
-      struct grub_dirhook_info info;
a5bd9f
-      grub_memset (&info, 0, sizeof (info));
a5bd9f
-      if (! node->inode_read)
a5bd9f
-	{
a5bd9f
-	  grub_ext2_read_inode (data, node->ino, &node->inode);
a5bd9f
-	  if (!grub_errno)
a5bd9f
-	    node->inode_read = 1;
a5bd9f
-	  grub_errno = GRUB_ERR_NONE;
a5bd9f
-	}
a5bd9f
-      if (node->inode_read)
a5bd9f
-	{
a5bd9f
-	  info.mtimeset = 1;
a5bd9f
-	  info.mtime = grub_le_to_cpu32 (node->inode.mtime);
a5bd9f
-	}
a5bd9f
-
a5bd9f
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
a5bd9f
-      grub_free (node);
a5bd9f
-      return hook (filename, &info);
a5bd9f
+      grub_ext2_read_inode (ctx->data, node->ino, &node->inode);
a5bd9f
+      if (!grub_errno)
a5bd9f
+	node->inode_read = 1;
a5bd9f
+      grub_errno = GRUB_ERR_NONE;
a5bd9f
     }
a5bd9f
+  if (node->inode_read)
a5bd9f
+    {
a5bd9f
+      info.mtimeset = 1;
a5bd9f
+      info.mtime = grub_le_to_cpu32 (node->inode.mtime);
a5bd9f
+    }
a5bd9f
+
a5bd9f
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
a5bd9f
+  grub_free (node);
a5bd9f
+  return ctx->hook (filename, &info, ctx->hook_data);
a5bd9f
+}
a5bd9f
+
a5bd9f
+static grub_err_t
a5bd9f
+grub_ext2_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
a5bd9f
+	       void *hook_data)
a5bd9f
+{
a5bd9f
+  struct grub_ext2_dir_ctx ctx = {
a5bd9f
+    .hook = hook,
a5bd9f
+    .hook_data = hook_data
a5bd9f
+  };
a5bd9f
+  struct grub_fshelp_node *fdiro = 0;
a5bd9f
 
a5bd9f
   grub_dl_ref (my_mod);
a5bd9f
 
a5bd9f
-  data = grub_ext2_mount (device->disk);
a5bd9f
-  if (! data)
a5bd9f
+  ctx.data = grub_ext2_mount (device->disk);
a5bd9f
+  if (! ctx.data)
a5bd9f
     goto fail;
a5bd9f
 
a5bd9f
-  grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir,
a5bd9f
-			 grub_ext2_read_symlink, GRUB_FSHELP_DIR);
a5bd9f
+  grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro,
a5bd9f
+			 grub_ext2_iterate_dir, grub_ext2_read_symlink,
a5bd9f
+			 GRUB_FSHELP_DIR);
a5bd9f
   if (grub_errno)
a5bd9f
     goto fail;
a5bd9f
 
a5bd9f
-  grub_ext2_iterate_dir (fdiro, iterate);
a5bd9f
+  grub_ext2_iterate_dir (fdiro, grub_ext2_dir_iter, &ctx);
a5bd9f
 
a5bd9f
  fail:
a5bd9f
-  if (fdiro != &data->diropen)
a5bd9f
+  if (fdiro != &ctx.data->diropen)
a5bd9f
     grub_free (fdiro);
a5bd9f
-  grub_free (data);
a5bd9f
+  grub_free (ctx.data);
a5bd9f
 
a5bd9f
   grub_dl_unref (my_mod);
a5bd9f
 
a5bd9f
diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c
a5bd9f
index 119fc94..7664153 100644
a5bd9f
--- a/grub-core/fs/fat.c
a5bd9f
+++ b/grub-core/fs/fat.c
a5bd9f
@@ -844,8 +844,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
a5bd9f
 static char *
a5bd9f
 grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
a5bd9f
 		   const char *path, const char *origpath,
a5bd9f
-		   int (*hook) (const char *filename,
a5bd9f
-				const struct grub_dirhook_info *info))
a5bd9f
+		   grub_fs_dir_hook_t hook, void *hook_data)
a5bd9f
 {
a5bd9f
   char *dirname, *dirp;
a5bd9f
   int call_hook;
a5bd9f
@@ -905,7 +904,7 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
a5bd9f
 #endif
a5bd9f
       if (*dirname == '\0' && call_hook)
a5bd9f
 	{
a5bd9f
-	  if (hook (ctxt.filename, &info))
a5bd9f
+	  if (hook (ctxt.filename, &info, hook_data))
a5bd9f
 	    break;
a5bd9f
 	  else
a5bd9f
 	    continue;
a5bd9f
@@ -926,7 +925,7 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
a5bd9f
 	  data->cur_cluster_num = ~0U;
a5bd9f
 
a5bd9f
 	  if (call_hook)
a5bd9f
-	    hook (ctxt.filename, &info);
a5bd9f
+	    hook (ctxt.filename, &info, hook_data);
a5bd9f
 
a5bd9f
 	  break;
a5bd9f
 	}
a5bd9f
@@ -946,9 +945,8 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
a5bd9f
 }
a5bd9f
 
a5bd9f
 static grub_err_t
a5bd9f
-grub_fat_dir (grub_device_t device, const char *path,
a5bd9f
-	      int (*hook) (const char *filename,
a5bd9f
-			   const struct grub_dirhook_info *info))
a5bd9f
+grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
a5bd9f
+	      void *hook_data)
a5bd9f
 {
a5bd9f
   struct grub_fat_data *data = 0;
a5bd9f
   grub_disk_t disk = device->disk;
a5bd9f
@@ -976,7 +974,7 @@ grub_fat_dir (grub_device_t device, const char *path,
a5bd9f
 
a5bd9f
   do
a5bd9f
     {
a5bd9f
-      p = grub_fat_find_dir (disk, data, p, path, hook);
a5bd9f
+      p = grub_fat_find_dir (disk, data, p, path, hook, hook_data);
a5bd9f
     }
a5bd9f
   while (p && grub_errno == GRUB_ERR_NONE);
a5bd9f
 
a5bd9f
@@ -1004,7 +1002,7 @@ grub_fat_open (grub_file_t file, const char *name)
a5bd9f
 
a5bd9f
   do
a5bd9f
     {
a5bd9f
-      p = grub_fat_find_dir (file->device->disk, data, p, name, 0);
a5bd9f
+      p = grub_fat_find_dir (file->device->disk, data, p, name, 0, 0);
a5bd9f
       if (grub_errno != GRUB_ERR_NONE)
a5bd9f
 	goto fail;
a5bd9f
     }
a5bd9f
diff --git a/grub-core/fs/fshelp.c b/grub-core/fs/fshelp.c
a5bd9f
index 21a72de..7e557c3 100644
a5bd9f
--- a/grub-core/fs/fshelp.c
a5bd9f
+++ b/grub-core/fs/fshelp.c
a5bd9f
@@ -27,199 +27,214 @@
a5bd9f
 
a5bd9f
 GRUB_MOD_LICENSE ("GPLv3+");
a5bd9f
 
a5bd9f
-/* Lookup the node PATH.  The node ROOTNODE describes the root of the
a5bd9f
-   directory tree.  The node found is returned in FOUNDNODE, which is
a5bd9f
-   either a ROOTNODE or a new malloc'ed node.  ITERATE_DIR is used to
a5bd9f
-   iterate over all directory entries in the current node.
a5bd9f
-   READ_SYMLINK is used to read the symlink if a node is a symlink.
a5bd9f
-   EXPECTTYPE is the type node that is expected by the called, an
a5bd9f
-   error is generated if the node is not of the expected type.  Make
a5bd9f
-   sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
a5bd9f
-   because GCC has a nasty bug when using regparm=3.  */
a5bd9f
-grub_err_t
a5bd9f
-grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
a5bd9f
-		       grub_fshelp_node_t *foundnode,
a5bd9f
-		       int (*iterate_dir) (grub_fshelp_node_t dir,
a5bd9f
-					   int NESTED_FUNC_ATTR (*hook)
a5bd9f
-					   (const char *filename,
a5bd9f
-					    enum grub_fshelp_filetype filetype,
a5bd9f
-					    grub_fshelp_node_t node)),
a5bd9f
-		       char *(*read_symlink) (grub_fshelp_node_t node),
a5bd9f
-		       enum grub_fshelp_filetype expecttype)
a5bd9f
+typedef int (*iterate_dir_func) (grub_fshelp_node_t dir,
a5bd9f
+				 grub_fshelp_iterate_dir_hook_t hook,
a5bd9f
+				 void *data);
a5bd9f
+typedef char *(*read_symlink_func) (grub_fshelp_node_t node);
a5bd9f
+
a5bd9f
+/* Context for grub_fshelp_find_file.  */
a5bd9f
+struct grub_fshelp_find_file_ctx
a5bd9f
 {
a5bd9f
-  grub_err_t err;
a5bd9f
-  enum grub_fshelp_filetype foundtype = GRUB_FSHELP_DIR;
a5bd9f
-  int symlinknest = 0;
a5bd9f
+  const char *path;
a5bd9f
+  grub_fshelp_node_t rootnode, currroot, currnode, oldnode;
a5bd9f
+  enum grub_fshelp_filetype foundtype;
a5bd9f
+  int symlinknest;
a5bd9f
+  char *name;
a5bd9f
+  enum grub_fshelp_filetype type;
a5bd9f
+};
a5bd9f
+
a5bd9f
+/* Helper for find_file_iter.  */
a5bd9f
+static void
a5bd9f
+free_node (grub_fshelp_node_t node, struct grub_fshelp_find_file_ctx *ctx)
a5bd9f
+{
a5bd9f
+  if (node != ctx->rootnode && node != ctx->currroot)
a5bd9f
+    grub_free (node);
a5bd9f
+}
a5bd9f
 
a5bd9f
-  auto grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
a5bd9f
-					      grub_fshelp_node_t currroot,
a5bd9f
-					      grub_fshelp_node_t *currfound);
a5bd9f
+/* Helper for grub_fshelp_find_file.  */
a5bd9f
+static int
a5bd9f
+find_file_iter (const char *filename, enum grub_fshelp_filetype filetype,
a5bd9f
+		grub_fshelp_node_t node, void *data)
a5bd9f
+{
a5bd9f
+  struct grub_fshelp_find_file_ctx *ctx = data;
a5bd9f
 
a5bd9f
-  grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
a5bd9f
-					 grub_fshelp_node_t currroot,
a5bd9f
-					 grub_fshelp_node_t *currfound)
a5bd9f
+  if (filetype == GRUB_FSHELP_UNKNOWN ||
a5bd9f
+      (grub_strcmp (ctx->name, filename) &&
a5bd9f
+       (! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) ||
a5bd9f
+	grub_strcasecmp (ctx->name, filename))))
a5bd9f
     {
a5bd9f
-      char fpath[grub_strlen (currpath) + 1];
a5bd9f
-      char *name = fpath;
a5bd9f
-      char *next;
a5bd9f
-      enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
a5bd9f
-      grub_fshelp_node_t currnode = currroot;
a5bd9f
-      grub_fshelp_node_t oldnode = currroot;
a5bd9f
+      grub_free (node);
a5bd9f
+      return 0;
a5bd9f
+    }
a5bd9f
 
a5bd9f
-      auto int NESTED_FUNC_ATTR iterate (const char *filename,
a5bd9f
-					 enum grub_fshelp_filetype filetype,
a5bd9f
-					 grub_fshelp_node_t node);
a5bd9f
+  /* The node is found, stop iterating over the nodes.  */
a5bd9f
+  ctx->type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
a5bd9f
+  ctx->oldnode = ctx->currnode;
a5bd9f
+  ctx->currnode = node;
a5bd9f
 
a5bd9f
-      auto void free_node (grub_fshelp_node_t node);
a5bd9f
+  return 1;
a5bd9f
+}
a5bd9f
 
a5bd9f
-      void free_node (grub_fshelp_node_t node)
a5bd9f
-	{
a5bd9f
-          if (node != rootnode && node != currroot)
a5bd9f
-	    grub_free (node);
a5bd9f
-	}
a5bd9f
+static grub_err_t
a5bd9f
+find_file (const char *currpath, grub_fshelp_node_t currroot,
a5bd9f
+	   grub_fshelp_node_t *currfound,
a5bd9f
+	   iterate_dir_func iterate_dir, read_symlink_func read_symlink,
a5bd9f
+	   struct grub_fshelp_find_file_ctx *ctx)
a5bd9f
+{
a5bd9f
+  char fpath[grub_strlen (currpath) + 1];
a5bd9f
+  char *next;
a5bd9f
 
a5bd9f
-      int NESTED_FUNC_ATTR iterate (const char *filename,
a5bd9f
-				    enum grub_fshelp_filetype filetype,
a5bd9f
-				    grub_fshelp_node_t node)
a5bd9f
-	{
a5bd9f
-	  if (filetype == GRUB_FSHELP_UNKNOWN ||
a5bd9f
-              (grub_strcmp (name, filename) &&
a5bd9f
-               (! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) ||
a5bd9f
-                grub_strcasecmp (name, filename))))
a5bd9f
-	    {
a5bd9f
-	      grub_free (node);
a5bd9f
-	      return 0;
a5bd9f
-	    }
a5bd9f
+  ctx->currroot = currroot;
a5bd9f
+  ctx->name = fpath;
a5bd9f
+  ctx->type = GRUB_FSHELP_DIR;
a5bd9f
+  ctx->currnode = currroot;
a5bd9f
+  ctx->oldnode = currroot;
a5bd9f
 
a5bd9f
-	  /* The node is found, stop iterating over the nodes.  */
a5bd9f
-	  type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
a5bd9f
-	  oldnode = currnode;
a5bd9f
-	  currnode = node;
a5bd9f
+  grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
a5bd9f
 
a5bd9f
-	  return 1;
a5bd9f
-	}
a5bd9f
+  /* Remove all leading slashes.  */
a5bd9f
+  while (*ctx->name == '/')
a5bd9f
+    ctx->name++;
a5bd9f
 
a5bd9f
-      grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
a5bd9f
+  if (! *ctx->name)
a5bd9f
+    {
a5bd9f
+      *currfound = ctx->currnode;
a5bd9f
+      return 0;
a5bd9f
+    }
a5bd9f
 
a5bd9f
-      /* Remove all leading slashes.  */
a5bd9f
-      while (*name == '/')
a5bd9f
-	name++;
a5bd9f
+  for (;;)
a5bd9f
+    {
a5bd9f
+      int found;
a5bd9f
 
a5bd9f
-      if (! *name)
a5bd9f
+      /* Extract the actual part from the pathname.  */
a5bd9f
+      next = grub_strchr (ctx->name, '/');
a5bd9f
+      if (next)
a5bd9f
 	{
a5bd9f
-	  *currfound = currnode;
a5bd9f
-	  return 0;
a5bd9f
+	  /* Remove all leading slashes.  */
a5bd9f
+	  while (*next == '/')
a5bd9f
+	    *(next++) = '\0';
a5bd9f
 	}
a5bd9f
 
a5bd9f
-      for (;;)
a5bd9f
+      /* At this point it is expected that the current node is a
a5bd9f
+	 directory, check if this is true.  */
a5bd9f
+      if (ctx->type != GRUB_FSHELP_DIR)
a5bd9f
 	{
a5bd9f
-	  int found;
a5bd9f
+	  free_node (ctx->currnode, ctx);
a5bd9f
+	  ctx->currnode = 0;
a5bd9f
+	  return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
a5bd9f
+	}
a5bd9f
 
a5bd9f
-	  /* Extract the actual part from the pathname.  */
a5bd9f
-	  next = grub_strchr (name, '/');
a5bd9f
-	  if (next)
a5bd9f
-	    {
a5bd9f
-	      /* Remove all leading slashes.  */
a5bd9f
-	      while (*next == '/')
a5bd9f
-		*(next++) = '\0';
a5bd9f
-	    }
a5bd9f
+      /* Iterate over the directory.  */
a5bd9f
+      found = iterate_dir (ctx->currnode, find_file_iter, ctx);
a5bd9f
+      if (! found)
a5bd9f
+	{
a5bd9f
+	  free_node (ctx->currnode, ctx);
a5bd9f
+	  ctx->currnode = 0;
a5bd9f
+	  if (grub_errno)
a5bd9f
+	    return grub_errno;
a5bd9f
+
a5bd9f
+	  break;
a5bd9f
+	}
a5bd9f
+
a5bd9f
+      /* Read in the symlink and follow it.  */
a5bd9f
+      if (ctx->type == GRUB_FSHELP_SYMLINK)
a5bd9f
+	{
a5bd9f
+	  char *symlink;
a5bd9f
 
a5bd9f
-	  /* At this point it is expected that the current node is a
a5bd9f
-	     directory, check if this is true.  */
a5bd9f
-	  if (type != GRUB_FSHELP_DIR)
a5bd9f
+	  /* Test if the symlink does not loop.  */
a5bd9f
+	  if (++ctx->symlinknest == 8)
a5bd9f
 	    {
a5bd9f
-	      free_node (currnode);
a5bd9f
-	      currnode = 0;
a5bd9f
-	      return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
a5bd9f
+	      free_node (ctx->currnode, ctx);
a5bd9f
+	      free_node (ctx->oldnode, ctx);
a5bd9f
+	      ctx->currnode = 0;
a5bd9f
+	      return grub_error (GRUB_ERR_SYMLINK_LOOP,
a5bd9f
+				 N_("too deep nesting of symlinks"));
a5bd9f
 	    }
a5bd9f
 
a5bd9f
-	  /* Iterate over the directory.  */
a5bd9f
-	  found = iterate_dir (currnode, iterate);
a5bd9f
-	  if (! found)
a5bd9f
-	    {
a5bd9f
-	      free_node (currnode);
a5bd9f
-	      currnode = 0;
a5bd9f
-	      if (grub_errno)
a5bd9f
-		return grub_errno;
a5bd9f
+	  symlink = read_symlink (ctx->currnode);
a5bd9f
+	  free_node (ctx->currnode, ctx);
a5bd9f
+	  ctx->currnode = 0;
a5bd9f
 
a5bd9f
-	      break;
a5bd9f
+	  if (!symlink)
a5bd9f
+	    {
a5bd9f
+	      free_node (ctx->oldnode, ctx);
a5bd9f
+	      return grub_errno;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
-	  /* Read in the symlink and follow it.  */
a5bd9f
-	  if (type == GRUB_FSHELP_SYMLINK)
a5bd9f
+	  /* The symlink is an absolute path, go back to the root inode.  */
a5bd9f
+	  if (symlink[0] == '/')
a5bd9f
 	    {
a5bd9f
-	      char *symlink;
a5bd9f
-
a5bd9f
-	      /* Test if the symlink does not loop.  */
a5bd9f
-	      if (++symlinknest == 8)
a5bd9f
-		{
a5bd9f
-		  free_node (currnode);
a5bd9f
-		  free_node (oldnode);
a5bd9f
-		  currnode = 0;
a5bd9f
-		  return grub_error (GRUB_ERR_SYMLINK_LOOP,
a5bd9f
-                                     N_("too deep nesting of symlinks"));
a5bd9f
-		}
a5bd9f
-
a5bd9f
-	      symlink = read_symlink (currnode);
a5bd9f
-	      free_node (currnode);
a5bd9f
-	      currnode = 0;
a5bd9f
-
a5bd9f
-	      if (!symlink)
a5bd9f
-		{
a5bd9f
-		  free_node (oldnode);
a5bd9f
-		  return grub_errno;
a5bd9f
-		}
a5bd9f
-
a5bd9f
-	      /* The symlink is an absolute path, go back to the root inode.  */
a5bd9f
-	      if (symlink[0] == '/')
a5bd9f
-		{
a5bd9f
-		  free_node (oldnode);
a5bd9f
-		  oldnode = rootnode;
a5bd9f
-		}
a5bd9f
-
a5bd9f
-	      /* Lookup the node the symlink points to.  */
a5bd9f
-	      find_file (symlink, oldnode, &currnode);
a5bd9f
-	      type = foundtype;
a5bd9f
-	      grub_free (symlink);
a5bd9f
-
a5bd9f
-	      if (grub_errno)
a5bd9f
-		{
a5bd9f
-		  free_node (oldnode);
a5bd9f
-		  return grub_errno;
a5bd9f
-		}
a5bd9f
+	      free_node (ctx->oldnode, ctx);
a5bd9f
+	      ctx->oldnode = ctx->rootnode;
a5bd9f
 	    }
a5bd9f
 
a5bd9f
-	  if (oldnode != currnode)
a5bd9f
-	    free_node (oldnode);
a5bd9f
+	  /* Lookup the node the symlink points to.  */
a5bd9f
+	  find_file (symlink, ctx->oldnode, &ctx->currnode,
a5bd9f
+		     iterate_dir, read_symlink, ctx);
a5bd9f
+	  ctx->type = ctx->foundtype;
a5bd9f
+	  grub_free (symlink);
a5bd9f
 
a5bd9f
-	  /* Found the node!  */
a5bd9f
-	  if (! next || *next == '\0')
a5bd9f
+	  if (grub_errno)
a5bd9f
 	    {
a5bd9f
-	      *currfound = currnode;
a5bd9f
-	      foundtype = type;
a5bd9f
-	      return 0;
a5bd9f
+	      free_node (ctx->oldnode, ctx);
a5bd9f
+	      return grub_errno;
a5bd9f
 	    }
a5bd9f
+	}
a5bd9f
+
a5bd9f
+      if (ctx->oldnode != ctx->currnode)
a5bd9f
+	free_node (ctx->oldnode, ctx);
a5bd9f
 
a5bd9f
-	  name = next;
a5bd9f
+      /* Found the node!  */
a5bd9f
+      if (! next || *next == '\0')
a5bd9f
+	{
a5bd9f
+	  *currfound = ctx->currnode;
a5bd9f
+	  ctx->foundtype = ctx->type;
a5bd9f
+	  return 0;
a5bd9f
 	}
a5bd9f
 
a5bd9f
-      return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path);
a5bd9f
+      ctx->name = next;
a5bd9f
     }
a5bd9f
 
a5bd9f
+  return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
a5bd9f
+		     ctx->path);
a5bd9f
+}
a5bd9f
+
a5bd9f
+/* Lookup the node PATH.  The node ROOTNODE describes the root of the
a5bd9f
+   directory tree.  The node found is returned in FOUNDNODE, which is
a5bd9f
+   either a ROOTNODE or a new malloc'ed node.  ITERATE_DIR is used to
a5bd9f
+   iterate over all directory entries in the current node.
a5bd9f
+   READ_SYMLINK is used to read the symlink if a node is a symlink.
a5bd9f
+   EXPECTTYPE is the type node that is expected by the called, an
a5bd9f
+   error is generated if the node is not of the expected type.  */
a5bd9f
+grub_err_t
a5bd9f
+grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
a5bd9f
+		       grub_fshelp_node_t *foundnode,
a5bd9f
+		       iterate_dir_func iterate_dir,
a5bd9f
+		       read_symlink_func read_symlink,
a5bd9f
+		       enum grub_fshelp_filetype expecttype)
a5bd9f
+{
a5bd9f
+  struct grub_fshelp_find_file_ctx ctx = {
a5bd9f
+    .path = path,
a5bd9f
+    .rootnode = rootnode,
a5bd9f
+    .foundtype = GRUB_FSHELP_DIR,
a5bd9f
+    .symlinknest = 0
a5bd9f
+  };
a5bd9f
+  grub_err_t err;
a5bd9f
+
a5bd9f
   if (!path || path[0] != '/')
a5bd9f
     {
a5bd9f
       grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path);
a5bd9f
       return grub_errno;
a5bd9f
     }
a5bd9f
 
a5bd9f
-  err = find_file (path, rootnode, foundnode);
a5bd9f
+  err = find_file (path, rootnode, foundnode, iterate_dir, read_symlink, &ctx);
a5bd9f
   if (err)
a5bd9f
     return err;
a5bd9f
 
a5bd9f
   /* Check if the node that was found was of the expected type.  */
a5bd9f
-  if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
a5bd9f
+  if (expecttype == GRUB_FSHELP_REG && ctx.foundtype != expecttype)
a5bd9f
     return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
a5bd9f
-  else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
a5bd9f
+  else if (expecttype == GRUB_FSHELP_DIR && ctx.foundtype != expecttype)
a5bd9f
     return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
a5bd9f
 
a5bd9f
   return 0;
a5bd9f
diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c
a5bd9f
index 0a249cc..9ed3330 100644
a5bd9f
--- a/grub-core/fs/hfs.c
a5bd9f
+++ b/grub-core/fs/hfs.c
a5bd9f
@@ -1151,9 +1151,8 @@ grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
a5bd9f
 
a5bd9f
 
a5bd9f
 static grub_err_t
a5bd9f
-grub_hfs_dir (grub_device_t device, const char *path,
a5bd9f
-		  int (*hook) (const char *filename,
a5bd9f
-			       const struct grub_dirhook_info *info))
a5bd9f
+grub_hfs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
a5bd9f
+	      void *hook_data)
a5bd9f
 {
a5bd9f
   int inode;
a5bd9f
 
a5bd9f
@@ -1184,14 +1183,14 @@ grub_hfs_dir (grub_device_t device, const char *path,
a5bd9f
 	  info.dir = 1;
a5bd9f
 	  info.mtimeset = 1;
a5bd9f
 	  info.mtime = grub_be_to_cpu32 (drec->mtime) - 2082844800;
a5bd9f
-	  return hook (fname, &info);
a5bd9f
+	  return hook (fname, &info, hook_data);
a5bd9f
 	}
a5bd9f
       if (frec->type == GRUB_HFS_FILETYPE_FILE)
a5bd9f
 	{
a5bd9f
 	  info.dir = 0;