Blob Blame Raw
From 1eb3760d2821962f09cbbe2e4fa0d75a4e1a761c Mon Sep 17 00:00:00 2001
From: Colin Watson <cjwatson@ubuntu.com>
Date: Mon, 21 Jan 2013 01:33:46 +0000
Subject: [PATCH 126/482] Remove nested functions from filesystem directory
 iterators.

* include/grub/fs.h (grub_fs_dir_hook_t): New type.
(struct grub_fs.dir): Add hook_data argument.

Update all implementations and callers.
---
 ChangeLog                     |   9 +
 grub-core/commands/ls.c       | 204 +++++++++++----------
 grub-core/commands/test.c     | 305 ++++++++++++++++---------------
 grub-core/commands/wildcard.c | 158 +++++++++-------
 grub-core/fs/affs.c           | 201 ++++++++++----------
 grub-core/fs/bfs.c            |  95 ++++++----
 grub-core/fs/btrfs.c          |   5 +-
 grub-core/fs/cpio.c           |   5 +-
 grub-core/fs/ext2.c           |  91 ++++-----
 grub-core/fs/fat.c            |  16 +-
 grub-core/fs/fshelp.c         | 307 ++++++++++++++++---------------
 grub-core/fs/hfs.c            |   9 +-
 grub-core/fs/hfsplus.c        |  60 +++---
 grub-core/fs/iso9660.c        |  54 +++---
 grub-core/fs/jfs.c            |   5 +-
 grub-core/fs/minix.c          |   5 +-
 grub-core/fs/nilfs2.c         |  90 ++++-----
 grub-core/fs/ntfs.c           |  67 +++----
 grub-core/fs/reiserfs.c       |  54 +++---
 grub-core/fs/romfs.c          |  51 +++---
 grub-core/fs/sfs.c            | 137 +++++++-------
 grub-core/fs/squash4.c        |  57 +++---
 grub-core/fs/udf.c            | 109 +++++------
 grub-core/fs/ufs.c            |   5 +-
 grub-core/fs/xfs.c            | 140 +++++++-------
 grub-core/fs/zfs/zfs.c        | 416 +++++++++++++++++++++++-------------------
 grub-core/kern/corecmd.c      |   5 +-
 grub-core/kern/emu/hostfs.c   |   5 +-
 grub-core/kern/fs.c           |  13 +-
 grub-core/loader/xnu.c        | 205 ++++++++++++---------
 grub-core/net/net.c           |   4 +-
 grub-core/normal/completion.c |   5 +-
 include/grub/fs.h             |   7 +-
 include/grub/fshelp.h         |  15 +-
 util/grub-mount.c             | 142 ++++++++------
 35 files changed, 1664 insertions(+), 1392 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index afc2d38..c975de1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,14 @@
 2013-01-21  Colin Watson  <cjwatson@ubuntu.com>
 
+	Remove nested functions from filesystem directory iterators.
+
+	* include/grub/fs.h (grub_fs_dir_hook_t): New type.
+	(struct grub_fs.dir): Add hook_data argument.
+
+	Update all implementations and callers.
+
+2013-01-21  Colin Watson  <cjwatson@ubuntu.com>
+
 	* docs/grub.texi (Multi-boot manual config): Fix typo for
 	"recommended".
 
diff --git a/grub-core/commands/ls.c b/grub-core/commands/ls.c
index 7929747..0b86619 100644
--- a/grub-core/commands/ls.c
+++ b/grub-core/commands/ls.c
@@ -85,114 +85,126 @@ grub_ls_list_devices (int longlist)
   return 0;
 }
 
-static grub_err_t
-grub_ls_list_files (char *dirname, int longlist, int all, int human)
+/* Context for grub_ls_list_files.  */
+struct grub_ls_list_files_ctx
 {
-  char *device_name;
-  grub_fs_t fs;
-  const char *path;
-  grub_device_t dev;
+  char *dirname;
+  int all;
+  int human;
+};
+
+/* Helper for grub_ls_list_files.  */
+static int
+print_files (const char *filename, const struct grub_dirhook_info *info,
+	     void *data)
+{
+  struct grub_ls_list_files_ctx *ctx = data;
 
-  auto int print_files (const char *filename,
-			const struct grub_dirhook_info *info);
-  auto int print_files_long (const char *filename,
-			     const struct grub_dirhook_info *info);
+  if (ctx->all || filename[0] != '.')
+    grub_printf ("%s%s ", filename, info->dir ? "/" : "");
 
-  int print_files (const char *filename, const struct grub_dirhook_info *info)
-    {
-      if (all || filename[0] != '.')
-	grub_printf ("%s%s ", filename, info->dir ? "/" : "");
+  return 0;
+}
 
-      return 0;
-    }
+/* Helper for grub_ls_list_files.  */
+static int
+print_files_long (const char *filename, const struct grub_dirhook_info *info,
+		  void *data)
+{
+  struct grub_ls_list_files_ctx *ctx = data;
+
+  if ((! ctx->all) && (filename[0] == '.'))
+    return 0;
 
-  int print_files_long (const char *filename,
-			const struct grub_dirhook_info *info)
+  if (! info->dir)
     {
-      if ((! all) && (filename[0] == '.'))
-	return 0;
+      grub_file_t file;
+      char *pathname;
 
-      if (! info->dir)
-	{
-	  grub_file_t file;
-	  char *pathname;
+      if (ctx->dirname[grub_strlen (ctx->dirname) - 1] == '/')
+	pathname = grub_xasprintf ("%s%s", ctx->dirname, filename);
+      else
+	pathname = grub_xasprintf ("%s/%s", ctx->dirname, filename);
 
-	  if (dirname[grub_strlen (dirname) - 1] == '/')
-	    pathname = grub_xasprintf ("%s%s", dirname, filename);
-	  else
-	    pathname = grub_xasprintf ("%s/%s", dirname, filename);
+      if (!pathname)
+	return 1;
 
-	  if (!pathname)
-	    return 1;
+      /* XXX: For ext2fs symlinks are detected as files while they
+	 should be reported as directories.  */
+      grub_file_filter_disable_compression ();
+      file = grub_file_open (pathname);
+      if (! file)
+	{
+	  grub_errno = 0;
+	  grub_free (pathname);
+	  return 0;
+	}
 
-	  /* XXX: For ext2fs symlinks are detected as files while they
-	     should be reported as directories.  */
-	  grub_file_filter_disable_compression ();
-	  file = grub_file_open (pathname);
-	  if (! file)
+      if (! ctx->human)
+	grub_printf ("%-12llu", (unsigned long long) file->size);
+      else
+	{
+	  grub_uint64_t fsize = file->size * 100ULL;
+	  grub_uint64_t fsz = file->size;
+	  int units = 0;
+	  char buf[20];
+
+	  while (fsz / 1024)
 	    {
-	      grub_errno = 0;
-	      grub_free (pathname);
-	      return 0;
+	      fsize = (fsize + 512) / 1024;
+	      fsz /= 1024;
+	      units++;
 	    }
 
-	  if (! human)
-	    grub_printf ("%-12llu", (unsigned long long) file->size);
-	  else
+	  if (units)
 	    {
-	      grub_uint64_t fsize = file->size * 100ULL;
-	      grub_uint64_t fsz = file->size;
-	      int units = 0;
-	      char buf[20];
-
-	      while (fsz / 1024)
-		{
-		  fsize = (fsize + 512) / 1024;
-		  fsz /= 1024;
-		  units++;
-		}
-
-	      if (units)
-		{
-		  grub_uint64_t whole, fraction;
-
-		  whole = grub_divmod64 (fsize, 100, &fraction);
-		  grub_snprintf (buf, sizeof (buf),
-				 "%" PRIuGRUB_UINT64_T
-				 ".%02" PRIuGRUB_UINT64_T "%c", whole, fraction,
-				 grub_human_sizes[units]);
-		  grub_printf ("%-12s", buf);
-		}
-	      else
-		grub_printf ("%-12llu", (unsigned long long) file->size);
-
+	      grub_uint64_t whole, fraction;
+
+	      whole = grub_divmod64 (fsize, 100, &fraction);
+	      grub_snprintf (buf, sizeof (buf),
+			     "%" PRIuGRUB_UINT64_T
+			     ".%02" PRIuGRUB_UINT64_T "%c", whole, fraction,
+			     grub_human_sizes[units]);
+	      grub_printf ("%-12s", buf);
 	    }
-	  grub_file_close (file);
-	  grub_free (pathname);
-	}
-      else
-	grub_printf ("%-12s", _("DIR"));
-
-      if (info->mtimeset)
-	{
-	  struct grub_datetime datetime;
-	  grub_unixtime2datetime (info->mtime, &datetime);
-	  if (human)
-	    grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ",
-			 datetime.year, datetime.month, datetime.day,
-			 datetime.hour, datetime.minute,
-			 datetime.second,
-			 grub_get_weekday_name (&datetime));
 	  else
-	    grub_printf (" %04d%02d%02d%02d%02d%02d ",
-			 datetime.year, datetime.month,
-			 datetime.day, datetime.hour,
-			 datetime.minute, datetime.second);
+	    grub_printf ("%-12llu", (unsigned long long) file->size);
+
 	}
-      grub_printf ("%s%s\n", filename, info->dir ? "/" : "");
+      grub_file_close (file);
+      grub_free (pathname);
+    }
+  else
+    grub_printf ("%-12s", _("DIR"));
 
-      return 0;
+  if (info->mtimeset)
+    {
+      struct grub_datetime datetime;
+      grub_unixtime2datetime (info->mtime, &datetime);
+      if (ctx->human)
+	grub_printf (" %d-%02d-%02d %02d:%02d:%02d %-11s ",
+		     datetime.year, datetime.month, datetime.day,
+		     datetime.hour, datetime.minute,
+		     datetime.second,
+		     grub_get_weekday_name (&datetime));
+      else
+	grub_printf (" %04d%02d%02d%02d%02d%02d ",
+		     datetime.year, datetime.month,
+		     datetime.day, datetime.hour,
+		     datetime.minute, datetime.second);
     }
+  grub_printf ("%s%s\n", filename, info->dir ? "/" : "");
+
+  return 0;
+}
+
+static grub_err_t
+grub_ls_list_files (char *dirname, int longlist, int all, int human)
+{
+  char *device_name;
+  grub_fs_t fs;
+  const char *path;
+  grub_device_t dev;
 
   device_name = grub_file_get_device_name (dirname);
   dev = grub_device_open (device_name);
@@ -221,10 +233,16 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human)
     }
   else if (fs)
     {
+      struct grub_ls_list_files_ctx ctx = {
+	.dirname = dirname,
+	.all = all,
+	.human = human
+      };
+
       if (longlist)
-	(fs->dir) (dev, path, print_files_long);
+	(fs->dir) (dev, path, print_files_long, &ctx);
       else
-	(fs->dir) (dev, path, print_files);
+	(fs->dir) (dev, path, print_files, &ctx);
 
       if (grub_errno == GRUB_ERR_BAD_FILE_TYPE
 	  && path[grub_strlen (path) - 1] != '/')
@@ -250,9 +268,9 @@ grub_ls_list_files (char *dirname, int longlist, int all, int human)
 	  all = 1;
 	  grub_memset (&info, 0, sizeof (info));
 	  if (longlist)
-	    print_files_long (p, &info);
+	    print_files_long (p, &info, &ctx);
 	  else
-	    print_files (p, &info);
+	    print_files (p, &info, &ctx);
 
 	  grub_free (dirname);
 	}
diff --git a/grub-core/commands/test.c b/grub-core/commands/test.c
index 3a0e0e0..e3347ee 100644
--- a/grub-core/commands/test.c
+++ b/grub-core/commands/test.c
@@ -38,114 +38,125 @@ grub_strtosl (char *arg, char **end, int base)
   return grub_strtoul (arg, end, base);
 }
 
-/* Parse a test expression starting from *argn. */
-static int
-test_parse (char **args, int *argn, int argc)
+/* Context for test_parse.  */
+struct test_parse_ctx
 {
-  int ret = 0, discard = 0, invert = 0;
+  int ret, discard, invert;
   int file_exists;
   struct grub_dirhook_info file_info;
+  char *filename;
+};
+
+/* Take care of discarding and inverting. */
+static void
+update_val (int val, struct test_parse_ctx *ctx)
+{
+  if (! ctx->discard)
+    ctx->ret = ctx->invert ? ! val : val;
+  ctx->invert = ctx->discard = 0;
+}
+
+/* A hook for iterating directories. */
+static int
+find_file (const char *cur_filename, const struct grub_dirhook_info *info,
+	   void *data)
+{
+  struct test_parse_ctx *ctx = data;
+
+  if ((info->case_insensitive ? grub_strcasecmp (cur_filename, ctx->filename)
+       : grub_strcmp (cur_filename, ctx->filename)) == 0)
+    {
+      ctx->file_info = *info;
+      ctx->file_exists = 1;
+      return 1;
+    }
+  return 0;
+}
+
+/* Check if file exists and fetch its information. */
+static void
+get_fileinfo (char *path, struct test_parse_ctx *ctx)
+{
+  char *pathname;
+  char *device_name;
+  grub_fs_t fs;
+  grub_device_t dev;
+
+  ctx->file_exists = 0;
+  device_name = grub_file_get_device_name (path);
+  dev = grub_device_open (device_name);
+  if (! dev)
+    {
+      grub_free (device_name);
+      return;
+    }
 
-  auto void update_val (int val);
-  auto void get_fileinfo (char *pathname);
-
-  /* Take care of discarding and inverting. */
-  void update_val (int val)
-  {
-    if (! discard)
-      ret = invert ? ! val : val;
-    invert = discard = 0;
-  }
-
-  /* Check if file exists and fetch its information. */
-  void get_fileinfo (char *path)
-  {
-    char *filename, *pathname;
-    char *device_name;
-    grub_fs_t fs;
-    grub_device_t dev;
-
-    /* A hook for iterating directories. */
-    auto int find_file (const char *cur_filename,
-			const struct grub_dirhook_info *info);
-    int find_file (const char *cur_filename,
-		   const struct grub_dirhook_info *info)
+  fs = grub_fs_probe (dev);
+  if (! fs)
     {
-      if ((info->case_insensitive ? grub_strcasecmp (cur_filename, filename)
-	   : grub_strcmp (cur_filename, filename)) == 0)
+      grub_free (device_name);
+      grub_device_close (dev);
+      return;
+    }
+
+  pathname = grub_strchr (path, ')');
+  if (! pathname)
+    pathname = path;
+  else
+    pathname++;
+
+  /* Remove trailing '/'. */
+  while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
+    pathname[grub_strlen (pathname) - 1] = 0;
+
+  /* Split into path and filename. */
+  ctx->filename = grub_strrchr (pathname, '/');
+  if (! ctx->filename)
+    {
+      path = grub_strdup ("/");
+      ctx->filename = pathname;
+    }
+  else
+    {
+      ctx->filename++;
+      path = grub_strdup (pathname);
+      path[ctx->filename - pathname] = 0;
+    }
+
+  /* It's the whole device. */
+  if (! *pathname)
+    {
+      ctx->file_exists = 1;
+      grub_memset (&ctx->file_info, 0, sizeof (ctx->file_info));
+      /* Root is always a directory. */
+      ctx->file_info.dir = 1;
+
+      /* Fetch writing time. */
+      ctx->file_info.mtimeset = 0;
+      if (fs->mtime)
 	{
-	  file_info = *info;
-	  file_exists = 1;
-	  return 1;
+	  if (! fs->mtime (dev, &ctx->file_info.mtime))
+	    ctx->file_info.mtimeset = 1;
+	  grub_errno = GRUB_ERR_NONE;
 	}
-      return 0;
     }
+  else
+    (fs->dir) (dev, path, find_file, ctx);
+
+  grub_device_close (dev);
+  grub_free (path);
+  grub_free (device_name);
+}
 
-    file_exists = 0;
-    device_name = grub_file_get_device_name (path);
-    dev = grub_device_open (device_name);
-    if (! dev)
-      {
-	grub_free (device_name);
-	return;
-      }
-
-    fs = grub_fs_probe (dev);
-    if (! fs)
-      {
-	grub_free (device_name);
-	grub_device_close (dev);
-	return;
-      }
-
-    pathname = grub_strchr (path, ')');
-    if (! pathname)
-      pathname = path;
-    else
-      pathname++;
-
-    /* Remove trailing '/'. */
-    while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
-      pathname[grub_strlen (pathname) - 1] = 0;
-
-    /* Split into path and filename. */
-    filename = grub_strrchr (pathname, '/');
-    if (! filename)
-      {
-	path = grub_strdup ("/");
-	filename = pathname;
-      }
-    else
-      {
-	filename++;
-	path = grub_strdup (pathname);
-	path[filename - pathname] = 0;
-      }
-
-    /* It's the whole device. */
-    if (! *pathname)
-      {
-	file_exists = 1;
-	grub_memset (&file_info, 0, sizeof (file_info));
-	/* Root is always a directory. */
-	file_info.dir = 1;
-
-	/* Fetch writing time. */
-	file_info.mtimeset = 0;
-	if (fs->mtime)
-	  {
-	    if (! fs->mtime (dev, &file_info.mtime))
-	      file_info.mtimeset = 1;
-	    grub_errno = GRUB_ERR_NONE;
-	  }
-      }
-    else
-      (fs->dir) (dev, path, find_file);
-
-    grub_device_close (dev);
-    grub_free (path);
-    grub_free (device_name);
-  }
+/* Parse a test expression starting from *argn. */
+static int
+test_parse (char **args, int *argn, int argc)
+{
+  struct test_parse_ctx ctx = {
+    .ret = 0,
+    .discard = 0,
+    .invert = 0
+  };
 
   /* Here we have the real parsing. */
   while (*argn < argc)
@@ -157,14 +168,16 @@ test_parse (char **args, int *argn, int argc)
 	  if (grub_strcmp (args[*argn + 1], "=") == 0
 	      || grub_strcmp (args[*argn + 1], "==") == 0)
 	    {
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0);
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0,
+			  &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
 
 	  if (grub_strcmp (args[*argn + 1], "!=") == 0)
 	    {
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0);
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0,
+			  &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -172,28 +185,32 @@ test_parse (char **args, int *argn, int argc)
 	  /* GRUB extension: lexicographical sorting. */
 	  if (grub_strcmp (args[*argn + 1], "<") == 0)
 	    {
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0);
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0,
+			  &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
 
 	  if (grub_strcmp (args[*argn + 1], "<=") == 0)
 	    {
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0);
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0,
+			  &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
 
 	  if (grub_strcmp (args[*argn + 1], ">") == 0)
 	    {
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0);
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0,
+			  &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
 
 	  if (grub_strcmp (args[*argn + 1], ">=") == 0)
 	    {
-	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0);
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0,
+			  &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -202,7 +219,7 @@ test_parse (char **args, int *argn, int argc)
 	  if (grub_strcmp (args[*argn + 1], "-eq") == 0)
 	    {
 	      update_val (grub_strtosl (args[*argn], 0, 0)
-			  == grub_strtosl (args[*argn + 2], 0, 0));
+			  == grub_strtosl (args[*argn + 2], 0, 0), &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -210,7 +227,7 @@ test_parse (char **args, int *argn, int argc)
 	  if (grub_strcmp (args[*argn + 1], "-ge") == 0)
 	    {
 	      update_val (grub_strtosl (args[*argn], 0, 0)
-			  >= grub_strtosl (args[*argn + 2], 0, 0));
+			  >= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -218,7 +235,7 @@ test_parse (char **args, int *argn, int argc)
 	  if (grub_strcmp (args[*argn + 1], "-gt") == 0)
 	    {
 	      update_val (grub_strtosl (args[*argn], 0, 0)
-			  > grub_strtosl (args[*argn + 2], 0, 0));
+			  > grub_strtosl (args[*argn + 2], 0, 0), &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -226,7 +243,7 @@ test_parse (char **args, int *argn, int argc)
 	  if (grub_strcmp (args[*argn + 1], "-le") == 0)
 	    {
 	      update_val (grub_strtosl (args[*argn], 0, 0)
-		      <= grub_strtosl (args[*argn + 2], 0, 0));
+		      <= grub_strtosl (args[*argn + 2], 0, 0), &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -234,7 +251,7 @@ test_parse (char **args, int *argn, int argc)
 	  if (grub_strcmp (args[*argn + 1], "-lt") == 0)
 	    {
 	      update_val (grub_strtosl (args[*argn], 0, 0)
-			  < grub_strtosl (args[*argn + 2], 0, 0));
+			  < grub_strtosl (args[*argn + 2], 0, 0), &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -242,7 +259,7 @@ test_parse (char **args, int *argn, int argc)
 	  if (grub_strcmp (args[*argn + 1], "-ne") == 0)
 	    {
 	      update_val (grub_strtosl (args[*argn], 0, 0)
-			  != grub_strtosl (args[*argn + 2], 0, 0));
+			  != grub_strtosl (args[*argn + 2], 0, 0), &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -265,10 +282,10 @@ test_parse (char **args, int *argn, int argc)
 
 	      if (grub_strcmp (args[*argn + 1], "-pgt") == 0)
 		update_val (grub_strtoul (args[*argn] + i, 0, 0)
-			    > grub_strtoul (args[*argn + 2] + i, 0, 0));
+			    > grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
 	      else
 		update_val (grub_strtoul (args[*argn] + i, 0, 0)
-			    < grub_strtoul (args[*argn + 2] + i, 0, 0));
+			    < grub_strtoul (args[*argn + 2] + i, 0, 0), &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -283,22 +300,24 @@ test_parse (char **args, int *argn, int argc)
 	      int bias = 0;
 
 	      /* Fetch fileinfo. */
-	      get_fileinfo (args[*argn]);
-	      file1 = file_info;
-	      file1exists = file_exists;
-	      get_fileinfo (args[*argn + 2]);
+	      get_fileinfo (args[*argn], &ctx);
+	      file1 = ctx.file_info;
+	      file1exists = ctx.file_exists;
+	      get_fileinfo (args[*argn + 2], &ctx);
 
 	      if (args[*argn + 1][3])
 		bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
 
 	      if (grub_memcmp (args[*argn + 1], "-nt", 3) == 0)
-		update_val ((file1exists && ! file_exists)
-			    || (file1.mtimeset && file_info.mtimeset
-				&& file1.mtime + bias > file_info.mtime));
+		update_val ((file1exists && ! ctx.file_exists)
+			    || (file1.mtimeset && ctx.file_info.mtimeset
+				&& file1.mtime + bias > ctx.file_info.mtime),
+			    &ctx);
 	      else
-		update_val ((! file1exists && file_exists)
-			    || (file1.mtimeset && file_info.mtimeset
-				&& file1.mtime + bias < file_info.mtime));
+		update_val ((! file1exists && ctx.file_exists)
+			    || (file1.mtimeset && ctx.file_info.mtimeset
+				&& file1.mtime + bias < ctx.file_info.mtime),
+			    &ctx);
 	      (*argn) += 3;
 	      continue;
 	    }
@@ -310,27 +329,27 @@ test_parse (char **args, int *argn, int argc)
 	  /* File tests. */
 	  if (grub_strcmp (args[*argn], "-d") == 0)
 	    {
-	      get_fileinfo (args[*argn + 1]);
-	      update_val (file_exists && file_info.dir);
+	      get_fileinfo (args[*argn + 1], &ctx);
+	      update_val (ctx.file_exists && ctx.file_info.dir, &ctx);
 	      (*argn) += 2;
-	      return ret;
+	      return ctx.ret;
 	    }
 
 	  if (grub_strcmp (args[*argn], "-e") == 0)
 	    {
-	      get_fileinfo (args[*argn + 1]);
-	      update_val (file_exists);
+	      get_fileinfo (args[*argn + 1], &ctx);
+	      update_val (ctx.file_exists, &ctx);
 	      (*argn) += 2;
-	      return ret;
+	      return ctx.ret;
 	    }
 
 	  if (grub_strcmp (args[*argn], "-f") == 0)
 	    {
-	      get_fileinfo (args[*argn + 1]);
+	      get_fileinfo (args[*argn + 1], &ctx);
 	      /* FIXME: check for other types. */
-	      update_val (file_exists && ! file_info.dir);
+	      update_val (ctx.file_exists && ! ctx.file_info.dir, &ctx);
 	      (*argn) += 2;
-	      return ret;
+	      return ctx.ret;
 	    }
 
 	  if (grub_strcmp (args[*argn], "-s") == 0)
@@ -338,25 +357,25 @@ test_parse (char **args, int *argn, int argc)
 	      grub_file_t file;
 	      grub_file_filter_disable_compression ();
 	      file = grub_file_open (args[*argn + 1]);
-	      update_val (file && (grub_file_size (file) != 0));
+	      update_val (file && (grub_file_size (file) != 0), &ctx);
 	      if (file)
 		grub_file_close (file);
 	      grub_errno = GRUB_ERR_NONE;
 	      (*argn) += 2;
-	      return ret;
+	      return ctx.ret;
 	    }
 
 	  /* String tests. */
 	  if (grub_strcmp (args[*argn], "-n") == 0)
 	    {
-	      update_val (args[*argn + 1][0]);
+	      update_val (args[*argn + 1][0], &ctx);
 
 	      (*argn) += 2;
 	      continue;
 	    }
 	  if (grub_strcmp (args[*argn], "-z") == 0)
 	    {
-	      update_val (! args[*argn + 1][0]);
+	      update_val (! args[*argn + 1][0], &ctx);
 	      (*argn) += 2;
 	      continue;
 	    }
@@ -368,42 +387,42 @@ test_parse (char **args, int *argn, int argc)
       if (grub_strcmp (args[*argn], ")") == 0)
 	{
 	  (*argn)++;
-	  return ret;
+	  return ctx.ret;
 	}
       /* Recursively invoke if parenthesis. */
       if (grub_strcmp (args[*argn], "(") == 0)
 	{
 	  (*argn)++;
-	  update_val (test_parse (args, argn, argc));
+	  update_val (test_parse (args, argn, argc), &ctx);
 	  continue;
 	}
 
       if (grub_strcmp (args[*argn], "!") == 0)
 	{
-	  invert = ! invert;
+	  ctx.invert = ! ctx.invert;
 	  (*argn)++;
 	  continue;
 	}
       if (grub_strcmp (args[*argn], "-a") == 0)
 	{
 	  /* If current value is 0 second value is to be discarded. */
-	  discard = ! ret;
+	  ctx.discard = ! ctx.ret;
 	  (*argn)++;
 	  continue;
 	}
       if (grub_strcmp (args[*argn], "-o") == 0)
 	{
 	  /* If current value is 1 second value is to be discarded. */
-	  discard = ret;
+	  ctx.discard = ctx.ret;
 	  (*argn)++;
 	  continue;
 	}
 
       /* No test found. Interpret if as just a string. */
-      update_val (args[*argn][0]);
+      update_val (args[*argn][0], &ctx);
       (*argn)++;
     }
-  return ret;
+  return ctx.ret;
 }
 
 static grub_err_t
diff --git a/grub-core/commands/wildcard.c b/grub-core/commands/wildcard.c
index 633de51..2807f80 100644
--- a/grub-core/commands/wildcard.c
+++ b/grub-core/commands/wildcard.c
@@ -279,63 +279,75 @@ match_devices (const regex_t *regexp, int noparts)
   return 0;
 }
 
-static char **
-match_files (const char *prefix, const char *suffix, const char *end,
-	     const regex_t *regexp)
+/* Context for match_files.  */
+struct match_files_ctx
 {
-  int i;
+  const regex_t *regexp;
   char **files;
   unsigned nfile;
   char *dir;
-  const char *path;
-  char *device_name;
-  grub_fs_t fs;
-  grub_device_t dev;
+};
 
-  auto int match (const char *name, const struct grub_dirhook_info *info);
-  int match (const char *name, const struct grub_dirhook_info *info)
-  {
-    char **t;
-    char *buffer;
+/* Helper for match_files.  */
+static int
+match_files_iter (const char *name, const struct grub_dirhook_info *info,
+		  void *data)
+{
+  struct match_files_ctx *ctx = data;
+  char **t;
+  char *buffer;
 
-    /* skip . and .. names */
-    if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
-      return 0;
+  /* skip . and .. names */
+  if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
+    return 0;
 
-    grub_dprintf ("expand", "matching: %s in %s\n", name, dir);
-    if (regexec (regexp, name, 0, 0, 0))
-      return 0;
+  grub_dprintf ("expand", "matching: %s in %s\n", name, ctx->dir);
+  if (regexec (ctx->regexp, name, 0, 0, 0))
+    return 0;
 
-    grub_dprintf ("expand", "matched\n");
+  grub_dprintf ("expand", "matched\n");
 
-    buffer = grub_xasprintf ("%s%s", dir, name);
-    if (! buffer)
+  buffer = grub_xasprintf ("%s%s", ctx->dir, name);
+  if (! buffer)
+    return 1;
+
+  t = grub_realloc (ctx->files, sizeof (char*) * (ctx->nfile + 2));
+  if (! t)
+    {
+      grub_free (buffer);
       return 1;
+    }
 
-    t = grub_realloc (files, sizeof (char*) * (nfile + 2));
-    if (! t)
-      {
-	grub_free (buffer);
-	return 1;
-      }
+  ctx->files = t;
+  ctx->files[ctx->nfile++] = buffer;
+  ctx->files[ctx->nfile] = 0;
+  return 0;
+}
 
-    files = t;
-    files[nfile++] = buffer;
-    files[nfile] = 0;
-    return 0;
-  }
+static char **
+match_files (const char *prefix, const char *suffix, const char *end,
+	     const regex_t *regexp)
+{
+  struct match_files_ctx ctx = {
+    .regexp = regexp,
+    .nfile = 0,
+    .files = 0
+  };
+  int i;
+  const char *path;
+  char *device_name;
+  grub_fs_t fs;
+  grub_device_t dev;
 
-  nfile = 0;
-  files = 0;
   dev = 0;
   device_name = 0;
   grub_error_push ();
 
-  dir = make_dir (prefix, suffix, end);
-  if (! dir)
+  ctx.dir = make_dir (prefix, suffix, end);
+  if (! ctx.dir)
     goto fail;
 
-  device_name = grub_file_get_device_name (dir);
+  device_name = grub_file_get_device_name (ctx.dir);
   dev = grub_device_open (device_name);
   if (! dev)
     goto fail;
@@ -344,33 +356,33 @@ match_files (const char *prefix, const char *suffix, const char *end,
   if (! fs)
     goto fail;
 
-  if (dir[0] == '(')
+  if (ctx.dir[0] == '(')
     {
-      path = grub_strchr (dir, ')');
+      path = grub_strchr (ctx.dir, ')');
       if (!path)
 	goto fail;
       path++;
     }
   else
-    path = dir;
+    path = ctx.dir;
 
-  if (fs->dir (dev, path, match))
+  if (fs->dir (dev, path, match_files_iter, &ctx))
     goto fail;
 
-  grub_free (dir);
+  grub_free (ctx.dir);
   grub_device_close (dev);
   grub_free (device_name);
   grub_error_pop ();
-  return files;
+  return ctx.files;
 
  fail:
 
-  grub_free (dir);
+  grub_free (ctx.dir);
 
-  for (i = 0; files && files[i]; i++)
-    grub_free (files[i]);
+  for (i = 0; ctx.files && ctx.files[i]; i++)
+    grub_free (ctx.files[i]);
 
-  grub_free (files);
+  grub_free (ctx.files);
 
   if (dev)
     grub_device_close (dev);
@@ -381,28 +393,42 @@ match_files (const char *prefix, const char *suffix, const char *end,
   return 0;
 }
 
+/* Context for check_file.  */
+struct check_file_ctx
+{
+  const char *basename;
+  int found;
+};
+
+/* Helper for check_file.  */
+static int
+check_file_iter (const char *name, const struct grub_dirhook_info *info,
+		 void *data)
+{
+  struct check_file_ctx *ctx = data;
+
+  if (ctx->basename[0] == 0
+      || (info->case_insensitive ? grub_strcasecmp (name, ctx->basename) == 0
+	  : grub_strcmp (name, ctx->basename) == 0))
+    {
+      ctx->found = 1;
+      return 1;
+    }
+  
+  return 0;
+}
+
 static int
 check_file (const char *dir, const char *basename)
 {
+  struct check_file_ctx ctx = {
+    .basename = basename,
+    .found = 0
+  };
   grub_fs_t fs;
   grub_device_t dev;
-  int found = 0;
   const char *device_name, *path;
 
-  auto int match (const char *name, const struct grub_dirhook_info *info);
-  int match (const char *name, const struct grub_dirhook_info *info)
-  {
-    if (basename[0] == 0
-	|| (info->case_insensitive ? grub_strcasecmp (name, basename) == 0
-	    : grub_strcmp (name, basename) == 0))
-      {
-	found = 1;
-	return 1;
-      }
-    
-    return 0;
-  }
-
   device_name = grub_file_get_device_name (dir);
   dev = grub_device_open (device_name);
   if (! dev)
@@ -422,14 +448,14 @@ check_file (const char *dir, const char *basename)
   else
     path = dir;
 
-  fs->dir (dev, path[0] ? path : "/", match);
+  fs->dir (dev, path[0] ? path : "/", check_file_iter, &ctx);
   if (grub_errno == 0 && basename[0] == 0)
-    found = 1;
+    ctx.found = 1;
 
  fail:
   grub_errno = 0;
 
-  return found;
+  return ctx.found;
 }
 
 static void
diff --git a/grub-core/fs/affs.c b/grub-core/fs/affs.c
index 848a455..6c49e5d 100644
--- a/grub-core/fs/affs.c
+++ b/grub-core/fs/affs.c
@@ -316,93 +316,93 @@ grub_affs_read_symlink (grub_fshelp_node_t node)
 }
 
 
+/* Helper for grub_affs_iterate_dir.  */
 static int
-grub_affs_iterate_dir (grub_fshelp_node_t dir,
-		       int NESTED_FUNC_ATTR
-		       (*hook) (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node))
+grub_affs_create_node (grub_fshelp_node_t dir,
+		       grub_fshelp_iterate_dir_hook_t hook, void *hook_data,
+		       struct grub_fshelp_node **node,
+		       grub_uint32_t **hashtable,
+		       grub_uint32_t block, const struct grub_affs_file *fil)
 {
-  unsigned int i;
-  struct grub_affs_file file;
-  struct grub_fshelp_node *node = 0;
   struct grub_affs_data *data = dir->data;
-  grub_uint32_t *hashtable;
-
-  auto int NESTED_FUNC_ATTR grub_affs_create_node (grub_uint32_t block,
-						   const struct grub_affs_file *fil);
+  int type;
+  grub_uint8_t name_u8[sizeof (fil->name) * GRUB_MAX_UTF8_PER_LATIN1 + 1];
+  grub_size_t len;
+  unsigned int nest;
 
-  int NESTED_FUNC_ATTR grub_affs_create_node (grub_uint32_t block,
-					      const struct grub_affs_file *fil)
+  *node = grub_zalloc (sizeof (**node));
+  if (!*node)
     {
-      int type;
-      grub_uint8_t name_u8[sizeof (fil->name) * GRUB_MAX_UTF8_PER_LATIN1 + 1];
-      grub_size_t len;
-      unsigned int nest;
-
-      node = grub_zalloc (sizeof (*node));
-      if (!node)
-	{
-	  grub_free (hashtable);
-	  return 1;
-	}
+      grub_free (*hashtable);
+      return 1;
+    }
 
-      node->data = data;
-      node->block = block;
-      node->parent = dir;
-
-      len = fil->namelen;
-      if (len > sizeof (fil->name))
-	len = sizeof (fil->name);
-      *grub_latin1_to_utf8 (name_u8, fil->name, len) = '\0';
-      
-      node->di = *fil;
-      for (nest = 0; nest < 8; nest++)
+  (*node)->data = data;
+  (*node)->block = block;
+  (*node)->parent = dir;
+
+  len = fil->namelen;
+  if (len > sizeof (fil->name))
+    len = sizeof (fil->name);
+  *grub_latin1_to_utf8 (name_u8, fil->name, len) = '\0';
+  
+  (*node)->di = *fil;
+  for (nest = 0; nest < 8; nest++)
+    {
+      switch ((*node)->di.type)
 	{
-	  switch (node->di.type)
-	    {
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_REG):
-	      type = GRUB_FSHELP_REG;
-	      break;
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_DIR):
-	      type = GRUB_FSHELP_DIR;
-	      break;
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_SYMLINK):
-	      type = GRUB_FSHELP_SYMLINK;
-	      break;
-	    case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_HARDLINK):
-	      {
-		grub_err_t err;
-		node->block = grub_be_to_cpu32 (node->di.hardlink);
-		err = grub_disk_read (data->disk,
-				      (((grub_uint64_t) node->block + 1) << data->log_blocksize)
-				      - 1,
-				      GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION,
-				      sizeof (node->di), (char *) &node->di);
-		if (err)
-		  return 1;
-		continue;
-	      }
-	    default:
-	      return 0;
-	    }
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_REG):
+	  type = GRUB_FSHELP_REG;
+	  break;
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_DIR):
+	  type = GRUB_FSHELP_DIR;
+	  break;
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_SYMLINK):
+	  type = GRUB_FSHELP_SYMLINK;
 	  break;
+	case grub_cpu_to_be32_compile_time (GRUB_AFFS_FILETYPE_HARDLINK):
+	  {
+	    grub_err_t err;
+	    (*node)->block = grub_be_to_cpu32 ((*node)->di.hardlink);
+	    err = grub_disk_read (data->disk,
+				  (((grub_uint64_t) (*node)->block + 1) << data->log_blocksize)
+				  - 1,
+				  GRUB_DISK_SECTOR_SIZE - GRUB_AFFS_FILE_LOCATION,
+				  sizeof ((*node)->di), (char *) &(*node)->di);
+	    if (err)
+	      return 1;
+	    continue;
+	  }
+	default:
+	  return 0;
 	}
+      break;
+    }
 
-      if (nest == 8)
-	return 0;
+  if (nest == 8)
+    return 0;
 
-      type |= GRUB_FSHELP_CASE_INSENSITIVE;
+  type |= GRUB_FSHELP_CASE_INSENSITIVE;
 
-      if (hook ((char *) name_u8, type, node))
-	{
-	  grub_free (hashtable);
-	  node = 0;
-	  return 1;
-	}
-      node = 0;
-      return 0;
+  if (hook ((char *) name_u8, type, *node, hook_data))
+    {
+      grub_free (*hashtable);
+      *node = 0;
+      return 1;
     }
+  *node = 0;
+  return 0;
+}
+
+static int
+grub_affs_iterate_dir (grub_fshelp_node_t dir,
+		       grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  unsigned int i;
+  struct grub_affs_file file;
+  struct grub_fshelp_node *node = 0;
+  struct grub_affs_data *data = dir->data;
+  grub_uint32_t *hashtable;
 
   /* Create the directory entries for `.' and `..'.  */
   node = grub_zalloc (sizeof (*node));
@@ -410,7 +410,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
     return 1;
     
   *node = *dir;
-  if (hook (".", GRUB_FSHELP_DIR, node))
+  if (hook (".", GRUB_FSHELP_DIR, node, hook_data))
     return 1;
   if (dir->parent)
     {
@@ -418,7 +418,7 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
       if (!node)
 	return 1;
       *node = *dir->parent;
-      if (hook ("..", GRUB_FSHELP_DIR, node))
+      if (hook ("..", GRUB_FSHELP_DIR, node, hook_data))
 	return 1;
     }
 
@@ -454,7 +454,8 @@ grub_affs_iterate_dir (grub_fshelp_node_t dir,
 	  if (grub_errno)
 	    goto fail;
 
-	  if (grub_affs_create_node (next, &file))
+	  if (grub_affs_create_node (dir, hook, hook_data, &node, &hashtable,
+				     next, &file))
 	    return 1;
 
 	  next = grub_be_to_cpu32 (file.next);
@@ -545,31 +546,37 @@ aftime2ctime (const struct grub_affs_time *t)
     + 8 * 365 * 86400 + 86400 * 2;
 }
 
+/* Context for grub_affs_dir.  */
+struct grub_affs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_affs_dir.  */
+static int
+grub_affs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		    grub_fshelp_node_t node, void *data)
+{
+  struct grub_affs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = 1;
+  info.mtime = aftime2ctime (&node->di.mtime);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_affs_dir (grub_device_t device, const char *path,
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info))
+	       grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_affs_dir_ctx ctx = { hook, hook_data };
   struct grub_affs_data *data = 0;
   struct grub_fshelp_node *fdiro = 0;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-    {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      info.mtimeset = 1;
-      info.mtime = aftime2ctime (&node->di.mtime);
-      grub_free (node);
-      return hook (filename, &info);
-    }
-
   grub_dl_ref (my_mod);
 
   data = grub_affs_mount (device->disk);
@@ -581,7 +588,7 @@ grub_affs_dir (grub_device_t device, const char *path,
   if (grub_errno)
     goto fail;
 
-  grub_affs_iterate_dir (fdiro, iterate);
+  grub_affs_iterate_dir (fdiro, grub_affs_dir_iter, &ctx);
 
  fail:
   if (data && fdiro != &data->diropen)
diff --git a/grub-core/fs/bfs.c b/grub-core/fs/bfs.c
index 318dc3e..fa2fc3f 100644
--- a/grub-core/fs/bfs.c
+++ b/grub-core/fs/bfs.c
@@ -173,6 +173,15 @@ struct grub_bfs_data
   struct grub_bfs_inode ino[0];
 };
 
+/* Context for grub_bfs_dir.  */
+struct grub_bfs_dir_ctx
+{
+  grub_device_t device;
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+  struct grub_bfs_superblock sb;
+};
+
 static grub_err_t
 read_extent (grub_disk_t disk,
 	     const struct grub_bfs_superblock *sb,
@@ -413,7 +422,9 @@ static int
 iterate_in_b_tree (grub_disk_t disk,
 		   const struct grub_bfs_superblock *sb,
 		   const struct grub_bfs_inode *ino,
-		   int NESTED_FUNC_ATTR (*hook) (const char *name, grub_uint64_t value))
+		   int (*hook) (const char *name, grub_uint64_t value,
+				struct grub_bfs_dir_ctx *ctx),
+		   struct grub_bfs_dir_ctx *ctx)
 {
   struct grub_bfs_btree_header head;
   grub_err_t err;
@@ -496,7 +507,8 @@ iterate_in_b_tree (grub_disk_t disk,
 	      end = grub_bfs_to_cpu_treehead (node.total_key_len);
 	    c = key_data[end];
 	    key_data[end] = 0;
-	    if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i])))
+	    if (hook (key_data + start, grub_bfs_to_cpu64 (key_values[i]),
+		      ctx))
 	      return 1;
 	    key_data[end] = c;
 	  }
@@ -844,46 +856,52 @@ mount (grub_disk_t disk, struct grub_bfs_superblock *sb)
   return GRUB_ERR_NONE;
 }
 
-static grub_err_t
-grub_bfs_dir (grub_device_t device, const char *path,
-	      int (*hook_in) (const char *filename,
-			      const struct grub_dirhook_info * info))
+/* Helper for grub_bfs_dir.  */
+static int
+grub_bfs_dir_iter (const char *name, grub_uint64_t value,
+		   struct grub_bfs_dir_ctx *ctx)
 {
-  struct grub_bfs_superblock sb;
-  grub_err_t err;
-  auto int NESTED_FUNC_ATTR hook (const char *name, grub_uint64_t value);
-
-  int NESTED_FUNC_ATTR hook (const char *name, grub_uint64_t value)
+  grub_err_t err2;
+  union
   {
-    grub_err_t err2;
-    union
-    {
-      struct grub_bfs_inode ino;
-      grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
-    } ino;
-    struct grub_dirhook_info info;
+    struct grub_bfs_inode ino;
+    grub_uint8_t raw[grub_bfs_to_cpu32 (ctx->sb.bsize)];
+  } ino;
+  struct grub_dirhook_info info;
 
-    err2 = grub_disk_read (device->disk, value
-			   << (grub_bfs_to_cpu32 (sb.log2_bsize)
-			       - GRUB_DISK_SECTOR_BITS), 0,
-			   grub_bfs_to_cpu32 (sb.bsize), (char *) ino.raw);
-    if (err2)
-      {
-	grub_print_error ();
-	return 0;
-      }
+  err2 = grub_disk_read (ctx->device->disk, value
+			 << (grub_bfs_to_cpu32 (ctx->sb.log2_bsize)
+			     - GRUB_DISK_SECTOR_BITS), 0,
+			 grub_bfs_to_cpu32 (ctx->sb.bsize), (char *) ino.raw);
+  if (err2)
+    {
+      grub_print_error ();
+      return 0;
+    }
 
-    info.mtimeset = 1;
+  info.mtimeset = 1;
 #ifdef MODE_AFS
-    info.mtime =
-      grub_divmod64 (grub_bfs_to_cpu64 (ino.ino.mtime), 1000000, 0);
+  info.mtime =
+    grub_divmod64 (grub_bfs_to_cpu64 (ino.ino.mtime), 1000000, 0);
 #else
-    info.mtime = grub_bfs_to_cpu64 (ino.ino.mtime) >> 16;
+  info.mtime = grub_bfs_to_cpu64 (ino.ino.mtime) >> 16;
 #endif
-    info.dir = ((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) == ATTR_DIR);
-    return hook_in (name, &info);
-  }
-  err = mount (device->disk, &sb);
+  info.dir = ((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) == ATTR_DIR);
+  return ctx->hook (name, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_bfs_dir (grub_device_t device, const char *path,
+	      grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_bfs_dir_ctx ctx = {
+    .device = device,
+    .hook = hook,
+    .hook_data = hook_data
+  };
+  grub_err_t err;
+
+  err = mount (device->disk, &ctx.sb);
   if (err)
     return err;
 
@@ -891,14 +909,15 @@ grub_bfs_dir (grub_device_t device, const char *path,
     union
     {
       struct grub_bfs_inode ino;
-      grub_uint8_t raw[grub_bfs_to_cpu32 (sb.bsize)];
+      grub_uint8_t raw[grub_bfs_to_cpu32 (ctx.sb.bsize)];
     } ino;
-    err = find_file (path, device->disk, &sb, &ino.ino);
+    err = find_file (path, device->disk, &ctx.sb, &ino.ino);
     if (err)
       return err;
     if (((grub_bfs_to_cpu32 (ino.ino.mode) & ATTR_TYPE) != ATTR_DIR))
       return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
-    iterate_in_b_tree (device->disk, &sb, &ino.ino, hook);
+    iterate_in_b_tree (device->disk, &ctx.sb, &ino.ino, grub_bfs_dir_iter,
+		       &ctx);
   }
 
   return grub_errno;
diff --git a/grub-core/fs/btrfs.c b/grub-core/fs/btrfs.c
index bcc75ba..196f301 100644
--- a/grub-core/fs/btrfs.c
+++ b/grub-core/fs/btrfs.c
@@ -1491,8 +1491,7 @@ find_path (struct grub_btrfs_data *data,
 
 static grub_err_t
 grub_btrfs_dir (grub_device_t device, const char *path,
-		int (*hook) (const char *filename,
-			     const struct grub_dirhook_info *info))
+		grub_fs_dir_hook_t hook, void *hook_data)
 {
   struct grub_btrfs_data *data = grub_btrfs_mount (device);
   struct grub_btrfs_key key_in, key_out;
@@ -1586,7 +1585,7 @@ grub_btrfs_dir (grub_device_t device, const char *path,
 	  c = cdirel->name[grub_le_to_cpu16 (cdirel->n)];
 	  cdirel->name[grub_le_to_cpu16 (cdirel->n)] = 0;
 	  info.dir = (cdirel->type == GRUB_BTRFS_DIR_ITEM_TYPE_DIRECTORY);
-	  if (hook (cdirel->name, &info))
+	  if (hook (cdirel->name, &info, hook_data))
 	    goto out;
 	  cdirel->name[grub_le_to_cpu16 (cdirel->n)] = c;
 	}
diff --git a/grub-core/fs/cpio.c b/grub-core/fs/cpio.c
index e9236cd..71d7fa4 100644
--- a/grub-core/fs/cpio.c
+++ b/grub-core/fs/cpio.c
@@ -513,8 +513,7 @@ handle_symlink (struct grub_cpio_data *data,
 
 static grub_err_t
 grub_cpio_dir (grub_device_t device, const char *path_in,
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info))
+	       grub_fs_dir_hook_t hook, void *hook_data)
 {
   struct grub_cpio_data *data;
   grub_disk_addr_t ofs;
@@ -575,7 +574,7 @@ grub_cpio_dir (grub_device_t device, const char *path_in,
 	      info.mtime = mtime;
 	      info.mtimeset = 1;
 
-	      if (hook (n, &info))
+	      if (hook (n, &info, hook_data))
 		{
 		  grub_free (name);
 		  goto fail;
diff --git a/grub-core/fs/ext2.c b/grub-core/fs/ext2.c
index cf2e2f4..0ebde35 100644
--- a/grub-core/fs/ext2.c
+++ b/grub-core/fs/ext2.c
@@ -692,10 +692,7 @@ grub_ext2_read_symlink (grub_fshelp_node_t node)
 
 static int
 grub_ext2_iterate_dir (grub_fshelp_node_t dir,
-		       int NESTED_FUNC_ATTR
-		       (*hook) (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node))
+		       grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   unsigned int fpos = 0;
   struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
@@ -777,7 +774,7 @@ grub_ext2_iterate_dir (grub_fshelp_node_t dir,
 		type = GRUB_FSHELP_REG;
 	    }
 
-	  if (hook (filename, type, fdiro))
+	  if (hook (filename, type, fdiro, hook_data))
 	    return 1;
 	}
 
@@ -858,59 +855,69 @@ grub_ext2_read (grub_file_t file, char *buf, grub_size_t len)
 }
 
 
-static grub_err_t
-grub_ext2_dir (grub_device_t device, const char *path,
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info))
+/* Context for grub_ext2_dir.  */
+struct grub_ext2_dir_ctx
 {
-  struct grub_ext2_data *data = 0;
-  struct grub_fshelp_node *fdiro = 0;
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+  struct grub_ext2_data *data;
+};
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
+/* Helper for grub_ext2_dir.  */
+static int
+grub_ext2_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		    grub_fshelp_node_t node, void *data)
+{
+  struct grub_ext2_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
 
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
+  grub_memset (&info, 0, sizeof (info));
+  if (! node->inode_read)
     {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      if (! node->inode_read)
-	{
-	  grub_ext2_read_inode (data, node->ino, &node->inode);
-	  if (!grub_errno)
-	    node->inode_read = 1;
-	  grub_errno = GRUB_ERR_NONE;
-	}
-      if (node->inode_read)
-	{
-	  info.mtimeset = 1;
-	  info.mtime = grub_le_to_cpu32 (node->inode.mtime);
-	}
-
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      grub_free (node);
-      return hook (filename, &info);
+      grub_ext2_read_inode (ctx->data, node->ino, &node->inode);
+      if (!grub_errno)
+	node->inode_read = 1;
+      grub_errno = GRUB_ERR_NONE;
     }
+  if (node->inode_read)
+    {
+      info.mtimeset = 1;
+      info.mtime = grub_le_to_cpu32 (node->inode.mtime);
+    }
+
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_ext2_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
+	       void *hook_data)
+{
+  struct grub_ext2_dir_ctx ctx = {
+    .hook = hook,
+    .hook_data = hook_data
+  };
+  struct grub_fshelp_node *fdiro = 0;
 
   grub_dl_ref (my_mod);
 
-  data = grub_ext2_mount (device->disk);
-  if (! data)
+  ctx.data = grub_ext2_mount (device->disk);
+  if (! ctx.data)
     goto fail;
 
-  grub_fshelp_find_file (path, &data->diropen, &fdiro, grub_ext2_iterate_dir,
-			 grub_ext2_read_symlink, GRUB_FSHELP_DIR);
+  grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro,
+			 grub_ext2_iterate_dir, grub_ext2_read_symlink,
+			 GRUB_FSHELP_DIR);
   if (grub_errno)
     goto fail;
 
-  grub_ext2_iterate_dir (fdiro, iterate);
+  grub_ext2_iterate_dir (fdiro, grub_ext2_dir_iter, &ctx);
 
  fail:
-  if (fdiro != &data->diropen)
+  if (fdiro != &ctx.data->diropen)
     grub_free (fdiro);
-  grub_free (data);
+  grub_free (ctx.data);
 
   grub_dl_unref (my_mod);
 
diff --git a/grub-core/fs/fat.c b/grub-core/fs/fat.c
index 119fc94..7664153 100644
--- a/grub-core/fs/fat.c
+++ b/grub-core/fs/fat.c
@@ -844,8 +844,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
 static char *
 grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
 		   const char *path, const char *origpath,
-		   int (*hook) (const char *filename,
-				const struct grub_dirhook_info *info))
+		   grub_fs_dir_hook_t hook, void *hook_data)
 {
   char *dirname, *dirp;
   int call_hook;
@@ -905,7 +904,7 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
 #endif
       if (*dirname == '\0' && call_hook)
 	{
-	  if (hook (ctxt.filename, &info))
+	  if (hook (ctxt.filename, &info, hook_data))
 	    break;
 	  else
 	    continue;
@@ -926,7 +925,7 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
 	  data->cur_cluster_num = ~0U;
 
 	  if (call_hook)
-	    hook (ctxt.filename, &info);
+	    hook (ctxt.filename, &info, hook_data);
 
 	  break;
 	}
@@ -946,9 +945,8 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
 }
 
 static grub_err_t
-grub_fat_dir (grub_device_t device, const char *path,
-	      int (*hook) (const char *filename,
-			   const struct grub_dirhook_info *info))
+grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
+	      void *hook_data)
 {
   struct grub_fat_data *data = 0;
   grub_disk_t disk = device->disk;
@@ -976,7 +974,7 @@ grub_fat_dir (grub_device_t device, const char *path,
 
   do
     {
-      p = grub_fat_find_dir (disk, data, p, path, hook);
+      p = grub_fat_find_dir (disk, data, p, path, hook, hook_data);
     }
   while (p && grub_errno == GRUB_ERR_NONE);
 
@@ -1004,7 +1002,7 @@ grub_fat_open (grub_file_t file, const char *name)
 
   do
     {
-      p = grub_fat_find_dir (file->device->disk, data, p, name, 0);
+      p = grub_fat_find_dir (file->device->disk, data, p, name, 0, 0);
       if (grub_errno != GRUB_ERR_NONE)
 	goto fail;
     }
diff --git a/grub-core/fs/fshelp.c b/grub-core/fs/fshelp.c
index 21a72de..7e557c3 100644
--- a/grub-core/fs/fshelp.c
+++ b/grub-core/fs/fshelp.c
@@ -27,199 +27,214 @@
 
 GRUB_MOD_LICENSE ("GPLv3+");
 
-/* Lookup the node PATH.  The node ROOTNODE describes the root of the
-   directory tree.  The node found is returned in FOUNDNODE, which is
-   either a ROOTNODE or a new malloc'ed node.  ITERATE_DIR is used to
-   iterate over all directory entries in the current node.
-   READ_SYMLINK is used to read the symlink if a node is a symlink.
-   EXPECTTYPE is the type node that is expected by the called, an
-   error is generated if the node is not of the expected type.  Make
-   sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
-   because GCC has a nasty bug when using regparm=3.  */
-grub_err_t
-grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
-		       grub_fshelp_node_t *foundnode,
-		       int (*iterate_dir) (grub_fshelp_node_t dir,
-					   int NESTED_FUNC_ATTR (*hook)
-					   (const char *filename,
-					    enum grub_fshelp_filetype filetype,
-					    grub_fshelp_node_t node)),
-		       char *(*read_symlink) (grub_fshelp_node_t node),
-		       enum grub_fshelp_filetype expecttype)
+typedef int (*iterate_dir_func) (grub_fshelp_node_t dir,
+				 grub_fshelp_iterate_dir_hook_t hook,
+				 void *data);
+typedef char *(*read_symlink_func) (grub_fshelp_node_t node);
+
+/* Context for grub_fshelp_find_file.  */
+struct grub_fshelp_find_file_ctx
 {
-  grub_err_t err;
-  enum grub_fshelp_filetype foundtype = GRUB_FSHELP_DIR;
-  int symlinknest = 0;
+  const char *path;
+  grub_fshelp_node_t rootnode, currroot, currnode, oldnode;
+  enum grub_fshelp_filetype foundtype;
+  int symlinknest;
+  char *name;
+  enum grub_fshelp_filetype type;
+};
+
+/* Helper for find_file_iter.  */
+static void
+free_node (grub_fshelp_node_t node, struct grub_fshelp_find_file_ctx *ctx)
+{
+  if (node != ctx->rootnode && node != ctx->currroot)
+    grub_free (node);
+}
 
-  auto grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
-					      grub_fshelp_node_t currroot,
-					      grub_fshelp_node_t *currfound);
+/* Helper for grub_fshelp_find_file.  */
+static int
+find_file_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		grub_fshelp_node_t node, void *data)
+{
+  struct grub_fshelp_find_file_ctx *ctx = data;
 
-  grub_err_t NESTED_FUNC_ATTR find_file (const char *currpath,
-					 grub_fshelp_node_t currroot,
-					 grub_fshelp_node_t *currfound)
+  if (filetype == GRUB_FSHELP_UNKNOWN ||
+      (grub_strcmp (ctx->name, filename) &&
+       (! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) ||
+	grub_strcasecmp (ctx->name, filename))))
     {
-      char fpath[grub_strlen (currpath) + 1];
-      char *name = fpath;
-      char *next;
-      enum grub_fshelp_filetype type = GRUB_FSHELP_DIR;
-      grub_fshelp_node_t currnode = currroot;
-      grub_fshelp_node_t oldnode = currroot;
+      grub_free (node);
+      return 0;
+    }
 
-      auto int NESTED_FUNC_ATTR iterate (const char *filename,
-					 enum grub_fshelp_filetype filetype,
-					 grub_fshelp_node_t node);
+  /* The node is found, stop iterating over the nodes.  */
+  ctx->type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
+  ctx->oldnode = ctx->currnode;
+  ctx->currnode = node;
 
-      auto void free_node (grub_fshelp_node_t node);
+  return 1;
+}
 
-      void free_node (grub_fshelp_node_t node)
-	{
-          if (node != rootnode && node != currroot)
-	    grub_free (node);
-	}
+static grub_err_t
+find_file (const char *currpath, grub_fshelp_node_t currroot,
+	   grub_fshelp_node_t *currfound,
+	   iterate_dir_func iterate_dir, read_symlink_func read_symlink,
+	   struct grub_fshelp_find_file_ctx *ctx)
+{
+  char fpath[grub_strlen (currpath) + 1];
+  char *next;
 
-      int NESTED_FUNC_ATTR iterate (const char *filename,
-				    enum grub_fshelp_filetype filetype,
-				    grub_fshelp_node_t node)
-	{
-	  if (filetype == GRUB_FSHELP_UNKNOWN ||
-              (grub_strcmp (name, filename) &&
-               (! (filetype & GRUB_FSHELP_CASE_INSENSITIVE) ||
-                grub_strcasecmp (name, filename))))
-	    {
-	      grub_free (node);
-	      return 0;
-	    }
+  ctx->currroot = currroot;
+  ctx->name = fpath;
+  ctx->type = GRUB_FSHELP_DIR;
+  ctx->currnode = currroot;
+  ctx->oldnode = currroot;
 
-	  /* The node is found, stop iterating over the nodes.  */
-	  type = filetype & ~GRUB_FSHELP_CASE_INSENSITIVE;
-	  oldnode = currnode;
-	  currnode = node;
+  grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
 
-	  return 1;
-	}
+  /* Remove all leading slashes.  */
+  while (*ctx->name == '/')
+    ctx->name++;
 
-      grub_strncpy (fpath, currpath, grub_strlen (currpath) + 1);
+  if (! *ctx->name)
+    {
+      *currfound = ctx->currnode;
+      return 0;
+    }
 
-      /* Remove all leading slashes.  */
-      while (*name == '/')
-	name++;
+  for (;;)
+    {
+      int found;
 
-      if (! *name)
+      /* Extract the actual part from the pathname.  */
+      next = grub_strchr (ctx->name, '/');
+      if (next)
 	{
-	  *currfound = currnode;
-	  return 0;
+	  /* Remove all leading slashes.  */
+	  while (*next == '/')
+	    *(next++) = '\0';
 	}
 
-      for (;;)
+      /* At this point it is expected that the current node is a
+	 directory, check if this is true.  */
+      if (ctx->type != GRUB_FSHELP_DIR)
 	{
-	  int found;
+	  free_node (ctx->currnode, ctx);
+	  ctx->currnode = 0;
+	  return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
+	}
 
-	  /* Extract the actual part from the pathname.  */
-	  next = grub_strchr (name, '/');
-	  if (next)
-	    {
-	      /* Remove all leading slashes.  */
-	      while (*next == '/')
-		*(next++) = '\0';
-	    }
+      /* Iterate over the directory.  */
+      found = iterate_dir (ctx->currnode, find_file_iter, ctx);
+      if (! found)
+	{
+	  free_node (ctx->currnode, ctx);
+	  ctx->currnode = 0;
+	  if (grub_errno)
+	    return grub_errno;
+
+	  break;
+	}
+
+      /* Read in the symlink and follow it.  */
+      if (ctx->type == GRUB_FSHELP_SYMLINK)
+	{
+	  char *symlink;
 
-	  /* At this point it is expected that the current node is a
-	     directory, check if this is true.  */
-	  if (type != GRUB_FSHELP_DIR)
+	  /* Test if the symlink does not loop.  */
+	  if (++ctx->symlinknest == 8)
 	    {
-	      free_node (currnode);
-	      currnode = 0;
-	      return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
+	      free_node (ctx->currnode, ctx);
+	      free_node (ctx->oldnode, ctx);
+	      ctx->currnode = 0;
+	      return grub_error (GRUB_ERR_SYMLINK_LOOP,
+				 N_("too deep nesting of symlinks"));
 	    }
 
-	  /* Iterate over the directory.  */
-	  found = iterate_dir (currnode, iterate);
-	  if (! found)
-	    {
-	      free_node (currnode);
-	      currnode = 0;
-	      if (grub_errno)
-		return grub_errno;
+	  symlink = read_symlink (ctx->currnode);
+	  free_node (ctx->currnode, ctx);
+	  ctx->currnode = 0;
 
-	      break;
+	  if (!symlink)
+	    {
+	      free_node (ctx->oldnode, ctx);
+	      return grub_errno;
 	    }
 
-	  /* Read in the symlink and follow it.  */
-	  if (type == GRUB_FSHELP_SYMLINK)
+	  /* The symlink is an absolute path, go back to the root inode.  */
+	  if (symlink[0] == '/')
 	    {
-	      char *symlink;
-
-	      /* Test if the symlink does not loop.  */
-	      if (++symlinknest == 8)
-		{
-		  free_node (currnode);
-		  free_node (oldnode);
-		  currnode = 0;
-		  return grub_error (GRUB_ERR_SYMLINK_LOOP,
-                                     N_("too deep nesting of symlinks"));
-		}
-
-	      symlink = read_symlink (currnode);
-	      free_node (currnode);
-	      currnode = 0;
-
-	      if (!symlink)
-		{
-		  free_node (oldnode);
-		  return grub_errno;
-		}
-
-	      /* The symlink is an absolute path, go back to the root inode.  */
-	      if (symlink[0] == '/')
-		{
-		  free_node (oldnode);
-		  oldnode = rootnode;
-		}
-
-	      /* Lookup the node the symlink points to.  */
-	      find_file (symlink, oldnode, &currnode);
-	      type = foundtype;
-	      grub_free (symlink);
-
-	      if (grub_errno)
-		{
-		  free_node (oldnode);
-		  return grub_errno;
-		}
+	      free_node (ctx->oldnode, ctx);
+	      ctx->oldnode = ctx->rootnode;
 	    }
 
-	  if (oldnode != currnode)
-	    free_node (oldnode);
+	  /* Lookup the node the symlink points to.  */
+	  find_file (symlink, ctx->oldnode, &ctx->currnode,
+		     iterate_dir, read_symlink, ctx);
+	  ctx->type = ctx->foundtype;
+	  grub_free (symlink);
 
-	  /* Found the node!  */
-	  if (! next || *next == '\0')
+	  if (grub_errno)
 	    {
-	      *currfound = currnode;
-	      foundtype = type;
-	      return 0;
+	      free_node (ctx->oldnode, ctx);
+	      return grub_errno;
 	    }
+	}
+
+      if (ctx->oldnode != ctx->currnode)
+	free_node (ctx->oldnode, ctx);
 
-	  name = next;
+      /* Found the node!  */
+      if (! next || *next == '\0')
+	{
+	  *currfound = ctx->currnode;
+	  ctx->foundtype = ctx->type;
+	  return 0;
 	}
 
-      return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), path);
+      ctx->name = next;
     }
 
+  return grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"),
+		     ctx->path);
+}
+
+/* Lookup the node PATH.  The node ROOTNODE describes the root of the
+   directory tree.  The node found is returned in FOUNDNODE, which is
+   either a ROOTNODE or a new malloc'ed node.  ITERATE_DIR is used to
+   iterate over all directory entries in the current node.
+   READ_SYMLINK is used to read the symlink if a node is a symlink.
+   EXPECTTYPE is the type node that is expected by the called, an
+   error is generated if the node is not of the expected type.  */
+grub_err_t
+grub_fshelp_find_file (const char *path, grub_fshelp_node_t rootnode,
+		       grub_fshelp_node_t *foundnode,
+		       iterate_dir_func iterate_dir,
+		       read_symlink_func read_symlink,
+		       enum grub_fshelp_filetype expecttype)
+{
+  struct grub_fshelp_find_file_ctx ctx = {
+    .path = path,
+    .rootnode = rootnode,
+    .foundtype = GRUB_FSHELP_DIR,
+    .symlinknest = 0
+  };
+  grub_err_t err;
+
   if (!path || path[0] != '/')
     {
       grub_error (GRUB_ERR_BAD_FILENAME, N_("invalid file name `%s'"), path);
       return grub_errno;
     }
 
-  err = find_file (path, rootnode, foundnode);
+  err = find_file (path, rootnode, foundnode, iterate_dir, read_symlink, &ctx);
   if (err)
     return err;
 
   /* Check if the node that was found was of the expected type.  */
-  if (expecttype == GRUB_FSHELP_REG && foundtype != expecttype)
+  if (expecttype == GRUB_FSHELP_REG && ctx.foundtype != expecttype)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
-  else if (expecttype == GRUB_FSHELP_DIR && foundtype != expecttype)
+  else if (expecttype == GRUB_FSHELP_DIR && ctx.foundtype != expecttype)
     return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
 
   return 0;
diff --git a/grub-core/fs/hfs.c b/grub-core/fs/hfs.c
index 0a249cc..9ed3330 100644
--- a/grub-core/fs/hfs.c
+++ b/grub-core/fs/hfs.c
@@ -1151,9 +1151,8 @@ grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
 
 
 static grub_err_t
-grub_hfs_dir (grub_device_t device, const char *path,
-		  int (*hook) (const char *filename,
-			       const struct grub_dirhook_info *info))
+grub_hfs_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
+	      void *hook_data)
 {
   int inode;
 
@@ -1184,14 +1183,14 @@ grub_hfs_dir (grub_device_t device, const char *path,
 	  info.dir = 1;
 	  info.mtimeset = 1;
 	  info.mtime = grub_be_to_cpu32 (drec->mtime) - 2082844800;
-	  return hook (fname, &info);
+	  return hook (fname, &info, hook_data);
 	}
       if (frec->type == GRUB_HFS_FILETYPE_FILE)
 	{
 	  info.dir = 0;
 	  info.mtimeset = 1;
 	  info.mtime = grub_be_to_cpu32 (frec->mtime) - 2082844800;
-	  return hook (fname, &info);
+	  return hook (fname, &info, hook_data);
 	}
 
       return 0;
diff --git a/grub-core/fs/hfsplus.c b/grub-core/fs/hfsplus.c
index 9812464..dcca581 100644
--- a/grub-core/fs/hfsplus.c
+++ b/grub-core/fs/hfsplus.c
@@ -766,10 +766,7 @@ grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
 
 static int
 grub_hfsplus_iterate_dir (grub_fshelp_node_t dir,
-			  int NESTED_FUNC_ATTR
-			  (*hook) (const char *filename,
-				   enum grub_fshelp_filetype filetype,
-				   grub_fshelp_node_t node))
+			  grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   int ret = 0;
 
@@ -825,7 +822,7 @@ grub_hfsplus_iterate_dir (grub_fshelp_node_t dir,
 	  node->size = 0;
 	  node->fileid = grub_be_to_cpu32 (fileinfo->parentid);
 
-	  ret = hook ("..", GRUB_FSHELP_DIR, node);
+	  ret = hook ("..", GRUB_FSHELP_DIR, node, hook_data);
 	  return ret;
 	}
 
@@ -878,7 +875,7 @@ grub_hfsplus_iterate_dir (grub_fshelp_node_t dir,
       node->size = grub_be_to_cpu64 (fileinfo->data.size);
       node->fileid = grub_be_to_cpu32 (fileinfo->fileid);
 
-      ret = hook (filename, type, node);
+      ret = hook (filename, type, node, hook_data);
 
       grub_free (filename);
 
@@ -895,7 +892,7 @@ grub_hfsplus_iterate_dir (grub_fshelp_node_t dir,
     if (!fsnode)
       return 1;
     *fsnode = *dir;
-    if (hook (".", GRUB_FSHELP_DIR, fsnode))
+    if (hook (".", GRUB_FSHELP_DIR, fsnode, hook_data))
       return 1;
   }
 
@@ -978,32 +975,39 @@ grub_hfsplus_read (grub_file_t file, char *buf, grub_size_t len)
 				     file->offset, len, buf);
 }
 
+/* Context for grub_hfsplus_dir.  */
+struct grub_hfsplus_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_hfsplus_dir.  */
+static int
+grub_hfsplus_dir_iter (const char *filename,
+		       enum grub_fshelp_filetype filetype,
+		       grub_fshelp_node_t node, void *data)
+{
+  struct grub_hfsplus_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = 1;
+  info.mtime = node->mtime;
+  info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_hfsplus_dir (grub_device_t device, const char *path,
-		  int (*hook) (const char *filename,
-			       const struct grub_dirhook_info *info))
+		  grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_hfsplus_dir_ctx ctx = { hook, hook_data };
   struct grub_hfsplus_data *data = 0;
   struct grub_fshelp_node *fdiro = 0;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-    {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      info.mtimeset = 1;
-      info.mtime = node->mtime;
-      info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE);
-      grub_free (node);
-      return hook (filename, &info);
-    }
-
   grub_dl_ref (my_mod);
 
   data = grub_hfsplus_mount (device->disk);
@@ -1018,7 +1022,7 @@ grub_hfsplus_dir (grub_device_t device, const char *path,
     goto fail;
 
   /* Iterate over all entries in this directory.  */
-  grub_hfsplus_iterate_dir (fdiro, iterate);
+  grub_hfsplus_iterate_dir (fdiro, grub_hfsplus_dir_iter, &ctx);
 
  fail:
   if (data && fdiro != &data->dirroot)
diff --git a/grub-core/fs/iso9660.c b/grub-core/fs/iso9660.c
index 547e156..e37553d 100644
--- a/grub-core/fs/iso9660.c
+++ b/grub-core/fs/iso9660.c
@@ -521,10 +521,7 @@ get_node_size (grub_fshelp_node_t node)
 
 static int
 grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
-			  int NESTED_FUNC_ATTR
-			  (*hook) (const char *filename,
-				   enum grub_fshelp_filetype filetype,
-				   grub_fshelp_node_t node))
+			  grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   struct grub_iso9660_dir dirent;
   grub_off_t offset = 0;
@@ -828,7 +825,7 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
 	    symlink = 0;
 	    was_continue = 0;
 	  }
-	if (hook (filename, type, node))
+	if (hook (filename, type, node, hook_data))
 	  {
 	    if (filename_alloc)
 	      grub_free (filename);
@@ -844,32 +841,39 @@ grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
 
 
 
+/* Context for grub_iso9660_dir.  */
+struct grub_iso9660_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_iso9660_dir.  */
+static int
+grub_iso9660_dir_iter (const char *filename,
+		       enum grub_fshelp_filetype filetype,
+		       grub_fshelp_node_t node, void *data)
+{
+  struct grub_iso9660_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = !!iso9660_to_unixtime2 (&node->dirents[0].mtime, &info.mtime);
+
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_iso9660_dir (grub_device_t device, const char *path,
-		  int (*hook) (const char *filename,
-			       const struct grub_dirhook_info *info))
+		  grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_iso9660_dir_ctx ctx = { hook, hook_data };
   struct grub_iso9660_data *data = 0;
   struct grub_fshelp_node rootnode;
   struct grub_fshelp_node *foundnode;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-    {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      info.mtimeset = !!iso9660_to_unixtime2 (&node->dirents[0].mtime, &info.mtime);
-
-      grub_free (node);
-      return hook (filename, &info);
-    }
-
   grub_dl_ref (my_mod);
 
   data = grub_iso9660_mount (device->disk);
@@ -891,7 +895,7 @@ grub_iso9660_dir (grub_device_t device, const char *path,
     goto fail;
 
   /* List the files in the directory.  */
-  grub_iso9660_iterate_dir (foundnode, iterate);
+  grub_iso9660_iterate_dir (foundnode, grub_iso9660_dir_iter, &ctx);
 
   if (foundnode != &rootnode)
     grub_free (foundnode);
diff --git a/grub-core/fs/jfs.c b/grub-core/fs/jfs.c
index b98a5a3..7c17192 100644
--- a/grub-core/fs/jfs.c
+++ b/grub-core/fs/jfs.c
@@ -799,8 +799,7 @@ grub_jfs_lookup_symlink (struct grub_jfs_data *data, grub_uint32_t ino)
 
 static grub_err_t
 grub_jfs_dir (grub_device_t device, const char *path,
-	      int (*hook) (const char *filename,
-			   const struct grub_dirhook_info *info))
+	      grub_fs_dir_hook_t hook, void *hook_data)
 {
   struct grub_jfs_data *data = 0;
   struct grub_jfs_diropen *diro = 0;
@@ -832,7 +831,7 @@ grub_jfs_dir (grub_device_t device, const char *path,
 		  & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR;
       info.mtimeset = 1;
       info.mtime = grub_le_to_cpu32 (inode.mtime.sec);
-      if (hook (diro->name, &info))
+      if (hook (diro->name, &info, hook_data))
 	goto fail;
     }
 
diff --git a/grub-core/fs/minix.c b/grub-core/fs/minix.c
index a622533..9655211 100644
--- a/grub-core/fs/minix.c
+++ b/grub-core/fs/minix.c
@@ -536,8 +536,7 @@ grub_minix_mount (grub_disk_t disk)
 
 static grub_err_t
 grub_minix_dir (grub_device_t device, const char *path,
-		  int (*hook) (const char *filename,
-			       const struct grub_dirhook_info *info))
+		grub_fs_dir_hook_t hook, void *hook_data)
 {
   struct grub_minix_data *data = 0;
   unsigned int pos = 0;
@@ -590,7 +589,7 @@ grub_minix_dir (grub_device_t device, const char *path,
       info.mtimeset = 1;
       info.mtime = grub_minix_to_cpu32 (data->inode.mtime);
 
-      if (hook (filename, &info) ? 1 : 0)
+      if (hook (filename, &info, hook_data) ? 1 : 0)
 	break;
 
       /* Load the old inode back in.  */
diff --git a/grub-core/fs/nilfs2.c b/grub-core/fs/nilfs2.c
index 5b34486..9bd4444 100644
--- a/grub-core/fs/nilfs2.c
+++ b/grub-core/fs/nilfs2.c
@@ -870,10 +870,7 @@ grub_nilfs2_read_symlink (grub_fshelp_node_t node)
 
 static int
 grub_nilfs2_iterate_dir (grub_fshelp_node_t dir,
-			 int NESTED_FUNC_ATTR
-			 (*hook) (const char *filename,
-				  enum grub_fshelp_filetype filetype,
-				  grub_fshelp_node_t node))
+			 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   grub_off_t fpos = 0;
   struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
@@ -957,7 +954,7 @@ grub_nilfs2_iterate_dir (grub_fshelp_node_t dir,
 		type = GRUB_FSHELP_REG;
 	    }
 
-	  if (hook (filename, type, fdiro))
+	  if (hook (filename, type, fdiro, hook_data))
 	    return 1;
 	}
 
@@ -1032,60 +1029,69 @@ grub_nilfs2_read (grub_file_t file, char *buf, grub_size_t len)
 				file->offset, len, buf);
 }
 
+/* Context for grub_nilfs2_dir.  */
+struct grub_nilfs2_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+  struct grub_nilfs2_data *data;
+};
+
+/* Helper for grub_nilfs2_dir.  */
+static int
+grub_nilfs2_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		      grub_fshelp_node_t node, void *data)
+{
+  struct grub_nilfs2_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  if (!node->inode_read)
+    {
+      grub_nilfs2_read_inode (ctx->data, node->ino, &node->inode);
+      if (!grub_errno)
+	node->inode_read = 1;
+      grub_errno = GRUB_ERR_NONE;
+    }
+  if (node->inode_read)
+    {
+      info.mtimeset = 1;
+      info.mtime = grub_le_to_cpu64 (node->inode.i_mtime);
+    }
+
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_nilfs2_dir (grub_device_t device, const char *path,
-		 int (*hook) (const char *filename,
-			      const struct grub_dirhook_info * info))
+		 grub_fs_dir_hook_t hook, void *hook_data)
 {
-  struct grub_nilfs2_data *data = 0;
+  struct grub_nilfs2_dir_ctx ctx = {
+    .hook = hook,
+    .hook_data = hook_data
+  };
   struct grub_fshelp_node *fdiro = 0;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-  {
-    struct grub_dirhook_info info;
-    grub_memset (&info, 0, sizeof (info));
-    if (!node->inode_read)
-      {
-	grub_nilfs2_read_inode (data, node->ino, &node->inode);
-	if (!grub_errno)
-	  node->inode_read = 1;
-	grub_errno = GRUB_ERR_NONE;
-      }
-    if (node->inode_read)
-      {
-	info.mtimeset = 1;
-	info.mtime = grub_le_to_cpu64 (node->inode.i_mtime);
-      }
-
-    info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-    grub_free (node);
-    return hook (filename, &info);
-  }
-
   grub_dl_ref (my_mod);
 
-  data = grub_nilfs2_mount (device->disk);
-  if (!data)
+  ctx.data = grub_nilfs2_mount (device->disk);
+  if (!ctx.data)
     goto fail;
 
-  grub_fshelp_find_file (path, &data->diropen, &fdiro,
+  grub_fshelp_find_file (path, &ctx.data->diropen, &fdiro,
 			 grub_nilfs2_iterate_dir, grub_nilfs2_read_symlink,
 			 GRUB_FSHELP_DIR);
   if (grub_errno)
     goto fail;
 
-  grub_nilfs2_iterate_dir (fdiro, iterate);
+  grub_nilfs2_iterate_dir (fdiro, grub_nilfs2_dir_iter, &ctx);
 
 fail:
-  if (fdiro != &data->diropen)
+  if (fdiro != &ctx.data->diropen)
     grub_free (fdiro);
-  grub_free (data);
+  grub_free (ctx.data);
 
   grub_dl_unref (my_mod);
 
diff --git a/grub-core/fs/ntfs.c b/grub-core/fs/ntfs.c
index e7861d8..7ac46f9 100644
--- a/grub-core/fs/ntfs.c
+++ b/grub-core/fs/ntfs.c
@@ -600,10 +600,7 @@ free_file (struct grub_ntfs_file *mft)
 
 static int
 list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos,
-	   int NESTED_FUNC_ATTR
-	   (*hook) (const char *filename,
-		    enum grub_fshelp_filetype filetype,
-		    grub_fshelp_node_t node))
+	   grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   grub_uint8_t *np;
   int ns;
@@ -667,7 +664,7 @@ list_file (struct grub_ntfs_file *diro, grub_uint8_t *pos,
           if (namespace)
             type |= GRUB_FSHELP_CASE_INSENSITIVE;
 
-	  if (hook (ustr, type, fdiro))
+	  if (hook (ustr, type, fdiro, hook_data))
 	    {
 	      grub_free (ustr);
 	      return 1;
@@ -778,10 +775,7 @@ grub_ntfs_read_symlink (grub_fshelp_node_t node)
 
 static int
 grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
-		       int NESTED_FUNC_ATTR
-		       (*hook) (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node))
+		       grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   grub_uint8_t *bitmap;
   struct grub_ntfs_attr attr, *at;
@@ -824,7 +818,7 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
     }
 
   cur_pos += 0x10;		/* Skip index root */
-  ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook);
+  ret = list_file (mft, cur_pos + u16at (cur_pos, 0), hook, hook_data);
   if (ret)
     goto done;
 
@@ -909,7 +903,8 @@ grub_ntfs_iterate_dir (grub_fshelp_node_t dir,
 		  || (fixup (indx, mft->data->idx_size,
 			     (const grub_uint8_t *) "INDX")))
 		goto done;
-	      ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)], hook);
+	      ret = list_file (mft, &indx[0x18 + u16at (indx, 0x18)],
+			       hook, hook_data);
 	      if (ret)
 		goto done;
 	    }
@@ -1017,33 +1012,39 @@ fail:
   return 0;
 }
 
+/* Context for grub_ntfs_dir.  */
+struct grub_ntfs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_ntfs_dir.  */
+static int
+grub_ntfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		    grub_fshelp_node_t node, void *data)
+{
+  struct grub_ntfs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = 1;
+  info.mtime = grub_divmod64 (node->mtime, 10000000, 0) 
+    - 86400ULL * 365 * (1970 - 1601)
+    - 86400ULL * ((1970 - 1601) / 4) + 86400ULL * ((1970 - 1601) / 100);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_ntfs_dir (grub_device_t device, const char *path,
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info))
+	       grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_ntfs_dir_ctx ctx = { hook, hook_data };
   struct grub_ntfs_data *data = 0;
   struct grub_fshelp_node *fdiro = 0;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-  {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      info.mtimeset = 1;
-      info.mtime = grub_divmod64 (node->mtime, 10000000, 0) 
-	- 86400ULL * 365 * (1970 - 1601)
-	- 86400ULL * ((1970 - 1601) / 4) + 86400ULL * ((1970 - 1601) / 100);
-      grub_free (node);
-      return hook (filename, &info);
-  }
-
   grub_dl_ref (my_mod);
 
   data = grub_ntfs_mount (device->disk);
@@ -1056,7 +1057,7 @@ grub_ntfs_dir (grub_device_t device, const char *path,
   if (grub_errno)
     goto fail;
 
-  grub_ntfs_iterate_dir (fdiro, iterate);
+  grub_ntfs_iterate_dir (fdiro, grub_ntfs_dir_iter, &ctx);
 
 fail:
   if ((fdiro) && (fdiro != &data->cmft))
diff --git a/grub-core/fs/reiserfs.c b/grub-core/fs/reiserfs.c
index 26adf23..686e4da 100644
--- a/grub-core/fs/reiserfs.c
+++ b/grub-core/fs/reiserfs.c
@@ -718,10 +718,8 @@ grub_reiserfs_mount (grub_disk_t disk)
 /* Call HOOK for each file in directory ITEM.  */
 static int
 grub_reiserfs_iterate_dir (grub_fshelp_node_t item,
-                           int NESTED_FUNC_ATTR
-                           (*hook) (const char *filename,
-                                    enum grub_fshelp_filetype filetype,
-                                    grub_fshelp_node_t node))
+                           grub_fshelp_iterate_dir_hook_t hook,
+			   void *hook_data)
 {
   struct grub_reiserfs_data *data = item->data;
   struct grub_reiserfs_block_header *block_header = 0;
@@ -946,7 +944,7 @@ grub_reiserfs_iterate_dir (grub_fshelp_node_t item,
 		  goto next;
 		}
 	    }
-	  if (hook (entry_name, entry_type, entry_item))
+	  if (hook (entry_name, entry_type, entry_item, hook_data))
 	    {
 	      grub_dprintf ("reiserfs", "Found : %s, type=%d\n",
 			    entry_name, entry_type);
@@ -1254,32 +1252,40 @@ grub_reiserfs_close (grub_file_t file)
   return GRUB_ERR_NONE;
 }
 
+/* Context for grub_reiserfs_dir.  */
+struct grub_reiserfs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_reiserfs_dir.  */
+static int
+grub_reiserfs_dir_iter (const char *filename,
+			enum grub_fshelp_filetype filetype,
+			grub_fshelp_node_t node, void *data)
+{
+  struct grub_reiserfs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = 1;
+  info.mtime = node->mtime;
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 /* Call HOOK with each file under DIR.  */
 static grub_err_t
 grub_reiserfs_dir (grub_device_t device, const char *path,
-                   int (*hook) (const char *filename,
-				const struct grub_dirhook_info *info))
+                   grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_reiserfs_dir_ctx ctx = { hook, hook_data };
   struct grub_reiserfs_data *data = 0;
   struct grub_fshelp_node root, *found;
   struct grub_reiserfs_key root_key;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-                                     enum grub_fshelp_filetype filetype,
-                                     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-                                enum grub_fshelp_filetype filetype,
-                                grub_fshelp_node_t node)
-    {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      info.mtimeset = 1;
-      info.mtime = node->mtime;
-      grub_free (node);
-      return hook (filename, &info);
-    }
   grub_dl_ref (my_mod);
   data = grub_reiserfs_mount (device->disk);
   if (! data)
@@ -1300,7 +1306,7 @@ grub_reiserfs_dir (grub_device_t device, const char *path,
                          grub_reiserfs_read_symlink, GRUB_FSHELP_DIR);
   if (grub_errno)
     goto fail;
-  grub_reiserfs_iterate_dir (found, iterate);
+  grub_reiserfs_iterate_dir (found, grub_reiserfs_dir_iter, &ctx);
   grub_free (data);
   grub_dl_unref (my_mod);
   return GRUB_ERR_NONE;
diff --git a/grub-core/fs/romfs.c b/grub-core/fs/romfs.c
index b30caef..b79b1e1 100644
--- a/grub-core/fs/romfs.c
+++ b/grub-core/fs/romfs.c
@@ -171,10 +171,7 @@ grub_romfs_read_symlink (grub_fshelp_node_t node)
 
 static int
 grub_romfs_iterate_dir (grub_fshelp_node_t dir,
-			int NESTED_FUNC_ATTR
-			(*hook) (const char *filename,
-				 enum grub_fshelp_filetype filetype,
-				 grub_fshelp_node_t node))
+			grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   grub_disk_addr_t caddr;
   struct grub_romfs_file_header hdr;
@@ -306,7 +303,7 @@ grub_romfs_iterate_dir (grub_fshelp_node_t dir,
 	  }
 	}
 
-      if (hook ((char *) name, filetype, node))
+      if (hook ((char *) name, filetype, node, hook_data))
 	{
 	  grub_free (name);
 	  return 1;
@@ -316,30 +313,36 @@ grub_romfs_iterate_dir (grub_fshelp_node_t dir,
   return 0;
 }
 
+/* Context for grub_romfs_dir.  */
+struct grub_romfs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_romfs_dir.  */
+static int
+grub_romfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		     grub_fshelp_node_t node, void *data)
+{
+  struct grub_romfs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_romfs_dir (grub_device_t device, const char *path,
-		int (*hook) (const char *filename,
-			     const struct grub_dirhook_info *info))
+		grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_romfs_dir_ctx ctx = { hook, hook_data };
   struct grub_romfs_data *data = 0;
   struct grub_fshelp_node *fdiro = 0, start;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-    {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      grub_free (node);
-      return hook (filename, &info);
-    }
-
   data = grub_romfs_mount (device);
   if (! data)
     goto fail;
@@ -352,7 +355,7 @@ grub_romfs_dir (grub_device_t device, const char *path,
   if (grub_errno)
     goto fail;
 
-  grub_romfs_iterate_dir (fdiro, iterate);
+  grub_romfs_iterate_dir (fdiro, grub_romfs_dir_iter, &ctx);
 
  fail:
   grub_free (data);
diff --git a/grub-core/fs/sfs.c b/grub-core/fs/sfs.c
index f7cdb08..fed17d3 100644
--- a/grub-core/fs/sfs.c
+++ b/grub-core/fs/sfs.c
@@ -460,12 +460,48 @@ grub_sfs_read_symlink (grub_fshelp_node_t node)
   return symlink;
 }
 
+/* Helper for grub_sfs_iterate_dir.  */
+static int
+grub_sfs_create_node (struct grub_fshelp_node **node,
+		      struct grub_sfs_data *data,
+		      const char *name,
+		      grub_uint32_t block, grub_uint32_t size, int type,
+		      grub_uint32_t mtime,
+		      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  grub_size_t len = grub_strlen (name);
+  grub_uint8_t *name_u8;
+  int ret;
+  *node = grub_malloc (sizeof (**node));
+  if (!*node)
+    return 1;
+  name_u8 = grub_malloc (len * GRUB_MAX_UTF8_PER_LATIN1 + 1);
+  if (!name_u8)
+    {
+      grub_free (*node);
+      return 1;
+    }
+
+  (*node)->data = data;
+  (*node)->size = size;
+  (*node)->block = block;
+  (*node)->mtime = mtime;
+  (*node)->cache = 0;
+  (*node)->cache_off = 0;
+  (*node)->next_extent = block;
+  (*node)->cache_size = 0;
+  (*node)->cache_allocated = 0;
+
+  *grub_latin1_to_utf8 (name_u8, (const grub_uint8_t *) name, len) = '\0';
+
+  ret = hook ((char *) name_u8, type | data->fshelp_flags, *node, hook_data);
+  grub_free (name_u8);
+  return ret;
+}
+
 static int
 grub_sfs_iterate_dir (grub_fshelp_node_t dir,
-		       int NESTED_FUNC_ATTR
-		       (*hook) (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node))
+		      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   struct grub_fshelp_node *node = 0;
   struct grub_sfs_data *data = dir->data;
@@ -474,46 +510,6 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
   unsigned int next = dir->block;
   grub_uint32_t pos;
 
-  auto int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name,
-						  grub_uint32_t block,
-						  grub_uint32_t size, int type,
-						  grub_uint32_t mtime);
-
-  int NESTED_FUNC_ATTR grub_sfs_create_node (const char *name,
-					     grub_uint32_t block,
-					     grub_uint32_t size, int type,
-					     grub_uint32_t mtime)
-    {
-      grub_size_t len = grub_strlen (name);
-      grub_uint8_t *name_u8;
-      int ret;
-      node = grub_malloc (sizeof (*node));
-      if (!node)
-	return 1;
-      name_u8 = grub_malloc (len * GRUB_MAX_UTF8_PER_LATIN1 + 1);
-      if (!name_u8)
-	{
-	  grub_free (node);
-	  return 1;
-	}
-
-      node->data = data;
-      node->size = size;
-      node->block = block;
-      node->mtime = mtime;
-      node->cache = 0;
-      node->cache_off = 0;
-      node->next_extent = block;
-      node->cache_size = 0;
-      node->cache_allocated = 0;
-
-      *grub_latin1_to_utf8 (name_u8, (const grub_uint8_t *) name, len) = '\0';
-
-      ret = hook ((char *) name_u8, type | data->fshelp_flags, node);
-      grub_free (name_u8);
-      return ret;
-    }
-
   objc_data = grub_malloc (GRUB_DISK_SECTOR_SIZE << data->log_blocksize);
   if (!objc_data)
     goto fail;
@@ -570,9 +566,10 @@ grub_sfs_iterate_dir (grub_fshelp_node_t dir,
 	  else
 	    block = grub_be_to_cpu32 (obj->file_dir.file.first_block);
 
-	  if (grub_sfs_create_node (filename, block,
+	  if (grub_sfs_create_node (&node, data, filename, block,
 				    grub_be_to_cpu32 (obj->file_dir.file.size),
-				    type, grub_be_to_cpu32 (obj->mtime)))
+				    type, grub_be_to_cpu32 (obj->mtime),
+				    hook, hook_data))
 	    {
 	      grub_free (objc_data);
 	      return 1;
@@ -654,32 +651,38 @@ grub_sfs_read (grub_file_t file, char *buf, grub_size_t len)
 }
 
 
+/* Context for grub_sfs_dir.  */
+struct grub_sfs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_sfs_dir.  */
+static int
+grub_sfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		   grub_fshelp_node_t node, void *data)
+{
+  struct grub_sfs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtime = node->mtime + 8 * 365 * 86400 + 86400 * 2;
+  info.mtimeset = 1;
+  grub_free (node->cache);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_sfs_dir (grub_device_t device, const char *path,
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info))
+	      grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_sfs_dir_ctx ctx = { hook, hook_data };
   struct grub_sfs_data *data = 0;
   struct grub_fshelp_node *fdiro = 0;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-    {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      info.mtime = node->mtime + 8 * 365 * 86400 + 86400 * 2;
-      info.mtimeset = 1;
-      grub_free (node->cache);
-      grub_free (node);
-      return hook (filename, &info);
-    }
-
   grub_dl_ref (my_mod);
 
   data = grub_sfs_mount (device->disk);
@@ -691,7 +694,7 @@ grub_sfs_dir (grub_device_t device, const char *path,
   if (grub_errno)
     goto fail;
 
-  grub_sfs_iterate_dir (fdiro, iterate);
+  grub_sfs_iterate_dir (fdiro, grub_sfs_dir_iter, &ctx);
 
  fail:
   if (data && fdiro != &data->diropen)
diff --git a/grub-core/fs/squash4.c b/grub-core/fs/squash4.c
index 44af0f8..cb3cc3a 100644
--- a/grub-core/fs/squash4.c
+++ b/grub-core/fs/squash4.c
@@ -478,10 +478,7 @@ grub_squash_read_symlink (grub_fshelp_node_t node)
 
 static int
 grub_squash_iterate_dir (grub_fshelp_node_t dir,
-			 int NESTED_FUNC_ATTR
-			 (*hook) (const char *filename,
-				  enum grub_fshelp_filetype filetype,
-				  grub_fshelp_node_t node))
+			 grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   grub_uint32_t off;
   grub_uint32_t endoff;
@@ -514,7 +511,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir,
       return 0;
     grub_memcpy (node, dir,
 		 sizeof (*node) + dir->stsize * sizeof (dir->stack[0]));
-    if (hook (".", GRUB_FSHELP_DIR, node))
+    if (hook (".", GRUB_FSHELP_DIR, node, hook_data))
       return 1;
 
     if (dir->stsize != 1)
@@ -536,7 +533,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir,
 	if (err)
 	  return 0;
 
-	if (hook ("..", GRUB_FSHELP_DIR, node))
+	if (hook ("..", GRUB_FSHELP_DIR, node, hook_data))
 	  return 1;
       }
   }
@@ -604,7 +601,7 @@ grub_squash_iterate_dir (grub_fshelp_node_t dir,
 	  node->stack[node->stsize].ino_chunk = grub_le_to_cpu32 (dh.ino_chunk);
 	  node->stack[node->stsize].ino_offset = grub_le_to_cpu16 (di.ino_offset);
 	  node->stsize++;
-	  r = hook (buf, filetype, node);
+	  r = hook (buf, filetype, node, hook_data);
 
 	  grub_free (buf);
 	  if (r)
@@ -640,28 +637,34 @@ squash_unmount (struct grub_squash_data *data)
 }
 
 
-static grub_err_t
-grub_squash_dir (grub_device_t device, const char *path,
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info))
+/* Context for grub_squash_dir.  */
+struct grub_squash_dir_ctx
 {
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
 
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-    {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      info.mtimeset = 1;
-      info.mtime = grub_le_to_cpu32 (node->ino.mtime);
-      grub_free (node);
-      return hook (filename, &info);
-    }
+/* Helper for grub_squash_dir.  */
+static int
+grub_squash_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		      grub_fshelp_node_t node, void *data)
+{
+  struct grub_squash_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = 1;
+  info.mtime = grub_le_to_cpu32 (node->ino.mtime);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
 
+static grub_err_t
+grub_squash_dir (grub_device_t device, const char *path,
+		 grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_squash_dir_ctx ctx = { hook, hook_data };
   struct grub_squash_data *data = 0;
   struct grub_fshelp_node *fdiro = 0;
   struct grub_fshelp_node root;
@@ -678,7 +681,7 @@ grub_squash_dir (grub_device_t device, const char *path,
   grub_fshelp_find_file (path, &root, &fdiro, grub_squash_iterate_dir,
 			 grub_squash_read_symlink, GRUB_FSHELP_DIR);
   if (!grub_errno)
-    grub_squash_iterate_dir (fdiro, iterate);
+    grub_squash_iterate_dir (fdiro, grub_squash_dir_iter, &ctx);
 
   squash_unmount (data);
 
diff --git a/grub-core/fs/udf.c b/grub-core/fs/udf.c
index 8e28d41..b7f3afb 100644
--- a/grub-core/fs/udf.c
+++ b/grub-core/fs/udf.c
@@ -843,10 +843,7 @@ read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
 
 static int
 grub_udf_iterate_dir (grub_fshelp_node_t dir,
-		      int NESTED_FUNC_ATTR
-		      (*hook) (const char *filename,
-			       enum grub_fshelp_filetype filetype,
-			       grub_fshelp_node_t node))
+		      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
 {
   grub_fshelp_node_t child;
   struct grub_udf_file_ident dirent;
@@ -859,7 +856,7 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
   /* The current directory is not stored.  */
   grub_memcpy (child, dir, get_fshelp_size (dir->data));
 
-  if (hook (".", GRUB_FSHELP_DIR, child))
+  if (hook (".", GRUB_FSHELP_DIR, child, hook_data))
     return 1;
 
   while (offset < U64 (dir->block.fe.file_size))
@@ -887,7 +884,7 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
           if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT)
 	    {
 	      /* This is the parent directory.  */
-	      if (hook ("..", GRUB_FSHELP_DIR, child))
+	      if (hook ("..", GRUB_FSHELP_DIR, child, hook_data))
 	        return 1;
 	    }
           else
@@ -911,7 +908,7 @@ grub_udf_iterate_dir (grub_fshelp_node_t dir,
 	      if (!filename)
 		grub_print_error ();
 
-	      if (filename && hook (filename, type, child))
+	      if (filename && hook (filename, type, child, hook_data))
 		{
 		  grub_free (filename);
 		  return 1;
@@ -1012,58 +1009,64 @@ grub_udf_read_symlink (grub_fshelp_node_t node)
   return NULL;
 }
 
+/* Context for grub_udf_dir.  */
+struct grub_udf_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_udf_dir.  */
+static int
+grub_udf_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		   grub_fshelp_node_t node, void *data)
+{
+  struct grub_udf_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+  const struct grub_udf_timestamp *tstamp = NULL;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE)
+    tstamp = &node->block.fe.modification_time;
+  else if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_EFE)
+    tstamp = &node->block.efe.modification_time;
+
+  if (tstamp && (U16 (tstamp->type_and_timezone) & 0xf000) == 0x1000)
+    {
+      grub_int16_t tz;
+      struct grub_datetime datetime;
+
+      datetime.year = U16 (tstamp->year);
+      datetime.month = tstamp->month;
+      datetime.day = tstamp->day;
+      datetime.hour = tstamp->hour;
+      datetime.minute = tstamp->minute;
+      datetime.second = tstamp->second;
+
+      tz = U16 (tstamp->type_and_timezone) & 0xfff;
+      if (tz & 0x800)
+	tz |= 0xf000;
+      if (tz == -2047)
+	tz = 0;
+
+      info.mtimeset = !!grub_datetime2unixtime (&datetime, &info.mtime);
+
+      info.mtime -= 60 * tz;
+    }
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
 static grub_err_t
 grub_udf_dir (grub_device_t device, const char *path,
-	      int (*hook) (const char *filename,
-			   const struct grub_dirhook_info *info))
+	      grub_fs_dir_hook_t hook, void *hook_data)
 {
+  struct grub_udf_dir_ctx ctx = { hook, hook_data };
   struct grub_udf_data *data = 0;
   struct grub_fshelp_node *rootnode = 0;
   struct grub_fshelp_node *foundnode = 0;
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
-
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
-  {
-      struct grub_dirhook_info info;
-      const struct grub_udf_timestamp *tstamp = NULL;
-      grub_memset (&info, 0, sizeof (info));
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE)
-	tstamp = &node->block.fe.modification_time;
-      else if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_EFE)
-	tstamp = &node->block.efe.modification_time;
-
-      if (tstamp && (U16 (tstamp->type_and_timezone) & 0xf000) == 0x1000)
-	{
-	  grub_int16_t tz;
-	  struct grub_datetime datetime;
-
-	  datetime.year = U16 (tstamp->year);
-	  datetime.month = tstamp->month;
-	  datetime.day = tstamp->day;
-	  datetime.hour = tstamp->hour;
-	  datetime.minute = tstamp->minute;
-	  datetime.second = tstamp->second;
-
-	  tz = U16 (tstamp->type_and_timezone) & 0xfff;
-	  if (tz & 0x800)
-	    tz |= 0xf000;
-	  if (tz == -2047)
-	    tz = 0;
-
-	  info.mtimeset = !!grub_datetime2unixtime (&datetime, &info.mtime);
-  
-	  info.mtime -= 60 * tz;
-	}
-      grub_free (node);
-      return hook (filename, &info);
-  }
-
   grub_dl_ref (my_mod);
 
   data = grub_udf_mount (device->disk);
@@ -1083,7 +1086,7 @@ grub_udf_dir (grub_device_t device, const char *path,
 			     GRUB_FSHELP_DIR))
     goto fail;
 
-  grub_udf_iterate_dir (foundnode, iterate);
+  grub_udf_iterate_dir (foundnode, grub_udf_dir_iter, &ctx);
 
   if (foundnode != rootnode)
     grub_free (foundnode);
diff --git a/grub-core/fs/ufs.c b/grub-core/fs/ufs.c
index 74a4a40..089a5c6 100644
--- a/grub-core/fs/ufs.c
+++ b/grub-core/fs/ufs.c
@@ -625,8 +625,7 @@ grub_ufs_mount (grub_disk_t disk)
 
 static grub_err_t
 grub_ufs_dir (grub_device_t device, const char *path,
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info))
+	      grub_fs_dir_hook_t hook, void *hook_data)
 {
   struct grub_ufs_data *data;
   unsigned int pos = 0;
@@ -697,7 +696,7 @@ grub_ufs_dir (grub_device_t device, const char *path,
 #endif
 	info.mtimeset = 1;
 
-	if (hook (filename, &info))
+	if (hook (filename, &info, hook_data))
 	  break;
       }
 
diff --git a/grub-core/fs/xfs.c b/grub-core/fs/xfs.c
index 1ed048f..49d2a89 100644
--- a/grub-core/fs/xfs.c
+++ b/grub-core/fs/xfs.c
@@ -443,47 +443,57 @@ grub_xfs_mode_to_filetype (grub_uint16_t mode)
 }
 
 
-static int
-grub_xfs_iterate_dir (grub_fshelp_node_t dir,
-		       int NESTED_FUNC_ATTR
-		       (*hook) (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node))
+/* Context for grub_xfs_iterate_dir.  */
+struct grub_xfs_iterate_dir_ctx
 {
-  struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
-  auto int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, const char *filename);
+  grub_fshelp_iterate_dir_hook_t hook;
+  void *hook_data;
+  struct grub_fshelp_node *diro;
+};
 
-  int NESTED_FUNC_ATTR call_hook (grub_uint64_t ino, const char *filename)
-    {
-      struct grub_fshelp_node *fdiro;
-      grub_err_t err;
-
-      fdiro = grub_malloc (sizeof (struct grub_fshelp_node)
-			   - sizeof (struct grub_xfs_inode)
-			   + (1 << diro->data->sblock.log2_inode));
-      if (!fdiro)
-	{
-	  grub_print_error ();
-	  return 0;
-	}
+/* Helper for grub_xfs_iterate_dir.  */
+static int iterate_dir_call_hook (grub_uint64_t ino, const char *filename,
+				  struct grub_xfs_iterate_dir_ctx *ctx)
+{
+  struct grub_fshelp_node *fdiro;
+  grub_err_t err;
 
-      /* The inode should be read, otherwise the filetype can
-	 not be determined.  */
-      fdiro->ino = ino;
-      fdiro->inode_read = 1;
-      fdiro->data = diro->data;
-      err = grub_xfs_read_inode (diro->data, ino, &fdiro->inode);
-      if (err)
-	{
-	  grub_print_error ();
-	  return 0;
-	}
+  fdiro = grub_malloc (sizeof (struct grub_fshelp_node)
+		       - sizeof (struct grub_xfs_inode)
+		       + (1 << ctx->diro->data->sblock.log2_inode));
+  if (!fdiro)
+    {
+      grub_print_error ();
+      return 0;
+    }
 
-      return hook (filename,
-		   grub_xfs_mode_to_filetype (fdiro->inode.mode),
-		   fdiro);
+  /* The inode should be read, otherwise the filetype can
+     not be determined.  */
+  fdiro->ino = ino;
+  fdiro->inode_read = 1;
+  fdiro->data = ctx->diro->data;
+  err = grub_xfs_read_inode (ctx->diro->data, ino, &fdiro->inode);
+  if (err)
+    {
+      grub_print_error ();
+      return 0;
     }
 
+  return ctx->hook (filename, grub_xfs_mode_to_filetype (fdiro->inode.mode),
+		    fdiro, ctx->hook_data);
+}
+
+static int
+grub_xfs_iterate_dir (grub_fshelp_node_t dir,
+		      grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  struct grub_fshelp_node *diro = (struct grub_fshelp_node *) dir;
+  struct grub_xfs_iterate_dir_ctx ctx = {
+    .hook = hook,
+    .hook_data = hook_data,
+    .diro = diro
+  };
+
   switch (diro->inode.format)
     {
     case XFS_INODE_FORMAT_INO:
@@ -508,10 +518,10 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
 	  }
 
 	/* Synthesize the direntries for `.' and `..'.  */
-	if (call_hook (diro->ino, "."))
+	if (iterate_dir_call_hook (diro->ino, ".", &ctx))
 	  return 1;
 
-	if (call_hook (parent, ".."))
+	if (iterate_dir_call_hook (parent, "..", &ctx))
 	  return 1;
 
 	for (i = 0; i < diro->inode.data.dir.dirhead.count; i++)
@@ -541,7 +551,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
 
 	    grub_memcpy (name, de->name, de->len);
 	    name[de->len] = '\0';
-	    if (call_hook (ino, name))
+	    if (iterate_dir_call_hook (ino, name, &ctx))
 	      return 1;
 
 	    de = ((struct grub_xfs_dir_entry *)
@@ -619,7 +629,7 @@ grub_xfs_iterate_dir (grub_fshelp_node_t dir,
 		   is not used by GRUB.  So it can be overwritten.  */
 		filename[direntry->len] = '\0';
 
-		if (call_hook (direntry->inode, filename))
+		if (iterate_dir_call_hook (direntry->inode, filename, &ctx))
 		  {
 		    grub_free (dirblock);
 		    return 1;
@@ -703,33 +713,39 @@ grub_xfs_mount (grub_disk_t disk)
 }
 
 
-static grub_err_t
-grub_xfs_dir (grub_device_t device, const char *path,
-	      int (*hook) (const char *filename,
-			   const struct grub_dirhook_info *info))
+/* Context for grub_xfs_dir.  */
+struct grub_xfs_dir_ctx
 {
-  struct grub_xfs_data *data = 0;
-  struct grub_fshelp_node *fdiro = 0;
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
 
-  auto int NESTED_FUNC_ATTR iterate (const char *filename,
-				     enum grub_fshelp_filetype filetype,
-				     grub_fshelp_node_t node);
+/* Helper for grub_xfs_dir.  */
+static int
+grub_xfs_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+		   grub_fshelp_node_t node, void *data)
+{
+  struct grub_xfs_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
 
-  int NESTED_FUNC_ATTR iterate (const char *filename,
-				enum grub_fshelp_filetype filetype,
-				grub_fshelp_node_t node)
+  grub_memset (&info, 0, sizeof (info));
+  if (node->inode_read)
     {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-      if (node->inode_read)
-	{
-	  info.mtimeset = 1;
-	  info.mtime = grub_be_to_cpu32 (node->inode.mtime.sec);
-	}
-      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
-      grub_free (node);
-      return hook (filename, &info);
+      info.mtimeset = 1;
+      info.mtime = grub_be_to_cpu32 (node->inode.mtime.sec);
     }
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_xfs_dir (grub_device_t device, const char *path,
+	      grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_xfs_dir_ctx ctx = { hook, hook_data };
+  struct grub_xfs_data *data = 0;
+  struct grub_fshelp_node *fdiro = 0;
 
   grub_dl_ref (my_mod);
 
@@ -742,7 +758,7 @@ grub_xfs_dir (grub_device_t device, const char *path,
   if (grub_errno)
     goto fail;
 
-  grub_xfs_iterate_dir (fdiro, iterate);
+  grub_xfs_iterate_dir (fdiro, grub_xfs_dir_iter, &ctx);
 
  fail:
   if (fdiro != &data->diropen)
diff --git a/grub-core/fs/zfs/zfs.c b/grub-core/fs/zfs/zfs.c
index 6ef6db3..822d65b 100644
--- a/grub-core/fs/zfs/zfs.c
+++ b/grub-core/fs/zfs/zfs.c
@@ -253,6 +253,14 @@ struct grub_zfs_data
   grub_uint64_t guid;
 };
 
+/* Context for grub_zfs_dir.  */
+struct grub_zfs_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+  struct grub_zfs_data *data;
+};
+
 grub_err_t (*grub_zfs_decrypt) (grub_crypto_cipher_handle_t cipher,
 				grub_uint64_t algo,
 				void *nonce,
@@ -1790,8 +1798,9 @@ mzap_lookup (mzap_phys_t * zapobj, grub_zfs_endian_t endian,
 
 static int
 mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize, 
-	      int NESTED_FUNC_ATTR (*hook) (const char *name, 
-					    grub_uint64_t val))
+	      int (*hook) (const char *name, grub_uint64_t val,
+			   struct grub_zfs_dir_ctx *ctx),
+	      struct grub_zfs_dir_ctx *ctx)
 {
   int i, chunks;
   mzap_ent_phys_t *mzap_ent = zapobj->mz_chunk;
@@ -1803,7 +1812,7 @@ mzap_iterate (mzap_phys_t * zapobj, grub_zfs_endian_t endian, int objsize,
 		    mzap_ent[i].mze_name, (long long)mzap_ent[i].mze_value,
 		    (int)mzap_ent[i].mze_cd);
       if (hook (mzap_ent[i].mze_name, 
-		grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian)))
+		grub_zfs_to_cpu64 (mzap_ent[i].mze_value, endian), ctx))
 	return 1;
     }
 
@@ -2054,12 +2063,11 @@ fzap_lookup (dnode_end_t * zap_dnode, zap_phys_t * zap,
 static int
 fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
 	      grub_size_t name_elem_length,
-	      int NESTED_FUNC_ATTR (*hook) (const void *name,
-					    grub_size_t name_length,
-					    const void *val_in,
-					    grub_size_t nelem,
-					    grub_size_t elemsize), 
-	      struct grub_zfs_data *data)
+	      int (*hook) (const void *name, grub_size_t name_length,
+			   const void *val_in,
+			   grub_size_t nelem, grub_size_t elemsize,
+			   void *data),
+	      void *hook_data, struct grub_zfs_data *data)
 {
   zap_leaf_phys_t *l;
   void *l_in;
@@ -2158,7 +2166,7 @@ fzap_iterate (dnode_end_t * zap_dnode, zap_phys_t * zap,
 	      }
 
 	    if (hook (buf, le->le_name_length,
-		      val, le->le_value_length, le->le_int_size))
+		      val, le->le_value_length, le->le_int_size, hook_data))
 	      {
 		grub_free (l);
 		return 1;
@@ -2221,11 +2229,35 @@ zap_lookup (dnode_end_t * zap_dnode, const char *name, grub_uint64_t *val,
   return grub_error (GRUB_ERR_BAD_FS, "unknown ZAP type");
 }
 
+/* Context for zap_iterate_u64.  */
+struct zap_iterate_u64_ctx
+{
+  int (*hook) (const char *, grub_uint64_t, struct grub_zfs_dir_ctx *);
+  struct grub_zfs_dir_ctx *dir_ctx;
+};
+
+/* Helper for zap_iterate_u64.  */
+static int
+zap_iterate_u64_transform (const void *name,
+			   grub_size_t namelen __attribute__ ((unused)),
+			   const void *val_in,
+			   grub_size_t nelem,
+			   grub_size_t elemsize,
+			   void *data)
+{
+  struct zap_iterate_u64_ctx *ctx = data;
+
+  if (elemsize != sizeof (grub_uint64_t) || nelem != 1)
+    return 0;
+  return ctx->hook (name, grub_be_to_cpu64 (*(const grub_uint64_t *) val_in),
+		    ctx->dir_ctx);
+}
+
 static int
 zap_iterate_u64 (dnode_end_t * zap_dnode, 
-		 int NESTED_FUNC_ATTR (*hook) (const char *name,
-					       grub_uint64_t val),
-		 struct grub_zfs_data *data)
+		 int (*hook) (const char *name, grub_uint64_t val,
+			      struct grub_zfs_dir_ctx *ctx),
+		 struct grub_zfs_data *data, struct grub_zfs_dir_ctx *ctx)
 {
   grub_uint64_t block_type;
   int size;
@@ -2234,23 +2266,6 @@ zap_iterate_u64 (dnode_end_t * zap_dnode,
   int ret;
   grub_zfs_endian_t endian;
 
-  auto int NESTED_FUNC_ATTR transform (const void *name,
-				       grub_size_t namelen,
-				       const void *val_in,
-				       grub_size_t nelem,
-				       grub_size_t elemsize);
-
-  int NESTED_FUNC_ATTR transform (const void *name,
-				  grub_size_t namelen __attribute__ ((unused)),
-				  const void *val_in,
-				  grub_size_t nelem,
-				  grub_size_t elemsize)
-  {
-    if (elemsize != sizeof (grub_uint64_t) || nelem != 1)
-      return 0;
-    return hook (name, grub_be_to_cpu64 (*(const grub_uint64_t *) val_in));
-  }
-
   /* Read in the first block of the zap object data. */
   size = grub_zfs_to_cpu16 (zap_dnode->dn.dn_datablkszsec, zap_dnode->endian) << SPA_MINBLOCKSHIFT;
   err = dmu_read (zap_dnode, 0, &zapbuf, &endian, data);
@@ -2263,15 +2278,21 @@ zap_iterate_u64 (dnode_end_t * zap_dnode,
   if (block_type == ZBT_MICRO)
     {
       grub_dprintf ("zfs", "micro zap\n");
-      ret = mzap_iterate (zapbuf, endian, size, hook);
+      ret = mzap_iterate (zapbuf, endian, size, hook, ctx);
       grub_free (zapbuf);
       return ret;
     }
   else if (block_type == ZBT_HEADER)
     {
+      struct zap_iterate_u64_ctx transform_ctx = {
+	.hook = hook,
+	.dir_ctx = ctx
+      };
+
       grub_dprintf ("zfs", "fat zap\n");
       /* this is a fat zap */
-      ret = fzap_iterate (zap_dnode, zapbuf, 1, transform, data);
+      ret = fzap_iterate (zap_dnode, zapbuf, 1,
+			  zap_iterate_u64_transform, &transform_ctx, data);
       grub_free (zapbuf);
       return ret;
     }
@@ -2282,12 +2303,11 @@ zap_iterate_u64 (dnode_end_t * zap_dnode,
 static int
 zap_iterate (dnode_end_t * zap_dnode, 
 	     grub_size_t nameelemlen,
-	     int NESTED_FUNC_ATTR (*hook) (const void *name,
-					   grub_size_t namelen,
-					   const void *val_in,
-					   grub_size_t nelem,
-					   grub_size_t elemsize),
-	     struct grub_zfs_data *data)
+	     int (*hook) (const void *name, grub_size_t namelen,
+			  const void *val_in,
+			  grub_size_t nelem, grub_size_t elemsize,
+			  void *data),
+	     void *hook_data, struct grub_zfs_data *data)
 {
   grub_uint64_t block_type;
   void *zapbuf;
@@ -2312,7 +2332,8 @@ zap_iterate (dnode_end_t * zap_dnode,
     {
       grub_dprintf ("zfs", "fat zap\n");
       /* this is a fat zap */
-      ret = fzap_iterate (zap_dnode, zapbuf, nameelemlen, hook, data);
+      ret = fzap_iterate (zap_dnode, zapbuf, nameelemlen, hook, hook_data,
+			  data);
       grub_free (zapbuf);
       return ret;
     }
@@ -2826,6 +2847,61 @@ make_mdn (dnode_end_t * mdn, struct grub_zfs_data *data)
   return GRUB_ERR_NONE;
 }
 
+/* Context for dnode_get_fullpath.  */
+struct dnode_get_fullpath_ctx
+{
+  struct subvolume *subvol;
+  grub_uint64_t salt;
+  int keyn;
+};
+
+/* Helper for dnode_get_fullpath.  */
+static int
+count_zap_keys (const void *name __attribute__ ((unused)),
+		grub_size_t namelen __attribute__ ((unused)),
+		const void *val_in __attribute__ ((unused)),
+		grub_size_t nelem __attribute__ ((unused)),
+		grub_size_t elemsize __attribute__ ((unused)),
+		void *data)
+{
+  struct dnode_get_fullpath_ctx *ctx = data;
+
+  ctx->subvol->nkeys++;
+  return 0;
+}
+
+/* Helper for dnode_get_fullpath.  */
+static int
+load_zap_key (const void *name, grub_size_t namelen, const void *val_in,
+	      grub_size_t nelem, grub_size_t elemsize, void *data)
+{
+  struct dnode_get_fullpath_ctx *ctx = data;
+
+  if (namelen != 1)
+    {
+      grub_dprintf ("zfs", "Unexpected key index size %" PRIuGRUB_SIZE "\n",
+		    namelen);
+      return 0;
+    }
+
+  if (elemsize != 1)
+    {
+      grub_dprintf ("zfs", "Unexpected key element size %" PRIuGRUB_SIZE "\n",
+		    elemsize);
+      return 0;
+    }
+
+  ctx->subvol->keyring[ctx->keyn].txg =
+    grub_be_to_cpu64 (*(grub_uint64_t *) name);
+  ctx->subvol->keyring[ctx->keyn].algo =
+    grub_le_to_cpu64 (*(grub_uint64_t *) val_in);
+  ctx->subvol->keyring[ctx->keyn].cipher =
+    grub_zfs_load_key (val_in, nelem, ctx->salt,
+		       ctx->subvol->keyring[ctx->keyn].algo);
+  ctx->keyn++;
+  return 0;
+}
+
 static grub_err_t
 dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 		    dnode_end_t * dn, int *isfs,
@@ -2835,57 +2911,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
   const char *ptr_at, *filename;
   grub_uint64_t headobj;
   grub_uint64_t keychainobj;
-  grub_uint64_t salt;
   grub_err_t err;
-  int keyn = 0;
-
-  auto int NESTED_FUNC_ATTR count_zap_keys (const void *name,
-					  grub_size_t namelen,
-					  const void *val_in,
-					  grub_size_t nelem,
-					  grub_size_t elemsize);
-  int NESTED_FUNC_ATTR count_zap_keys (const void *name __attribute__ ((unused)),
-				       grub_size_t namelen __attribute__ ((unused)),
-				       const void *val_in __attribute__ ((unused)),
-				       grub_size_t nelem __attribute__ ((unused)),
-				       grub_size_t elemsize __attribute__ ((unused)))
-  {
-    subvol->nkeys++;
-    return 0;
-  }
-
-  auto int NESTED_FUNC_ATTR load_zap_key (const void *name,
-					  grub_size_t namelen,
-					  const void *val_in,
-					  grub_size_t nelem,
-					  grub_size_t elemsize);
-  int NESTED_FUNC_ATTR load_zap_key (const void *name,
-				     grub_size_t namelen,
-				     const void *val_in,
-				     grub_size_t nelem,
-				     grub_size_t elemsize)
-  {
-    if (namelen != 1)
-      {
-	grub_dprintf ("zfs", "Unexpected key index size %" PRIuGRUB_SIZE "\n",
-		      namelen);
-	return 0;
-      }
-
-    if (elemsize != 1)
-      {
-	grub_dprintf ("zfs", "Unexpected key element size %" PRIuGRUB_SIZE "\n",
-		      elemsize);
-	return 0;
-      }
-
-    subvol->keyring[keyn].txg = grub_be_to_cpu64 (*(grub_uint64_t *) name);
-    subvol->keyring[keyn].algo = grub_le_to_cpu64 (*(grub_uint64_t *) val_in);
-    subvol->keyring[keyn].cipher = grub_zfs_load_key (val_in, nelem, salt,
-						      subvol->keyring[keyn].algo);
-    keyn++;
-    return 0;
-  }
 
   ptr_at = grub_strchr (fullpath, '@');
   if (! ptr_at)
@@ -2953,6 +2979,10 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
   keychainobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->keychain, dn->endian);
   if (grub_zfs_load_key && keychainobj)
     {
+      struct dnode_get_fullpath_ctx ctx = {
+	.subvol = subvol,
+	.keyn = 0
+      };
       dnode_end_t keychain_dn, props_dn;
       grub_uint64_t propsobj;
       propsobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&dn->dn))->dd_props_zapobj, dn->endian);
@@ -2966,12 +2996,12 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 	  return err;
 	}
 
-      err = zap_lookup (&props_dn, "salt", &salt, data, 0);
+      err = zap_lookup (&props_dn, "salt", &ctx.salt, data, 0);
       if (err == GRUB_ERR_FILE_NOT_FOUND)
 	{
 	  err = 0;
 	  grub_errno = 0;
-	  salt = 0;
+	  ctx.salt = 0;
 	}
       if (err)
 	{
@@ -2988,7 +3018,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 	  return err;
 	}
       subvol->nkeys = 0;
-      zap_iterate (&keychain_dn, 8, count_zap_keys, data);
+      zap_iterate (&keychain_dn, 8, count_zap_keys, &ctx, data);
       subvol->keyring = grub_zalloc (subvol->nkeys * sizeof (subvol->keyring[0]));
       if (!subvol->keyring)
 	{
@@ -2996,7 +3026,7 @@ dnode_get_fullpath (const char *fullpath, struct subvolume *subvol,
 	  grub_free (snapname);
 	  return err;
 	}
-      zap_iterate (&keychain_dn, 8, load_zap_key, data);
+      zap_iterate (&keychain_dn, 8, load_zap_key, &ctx, data);
     }
 
   if (snapname)
@@ -3748,108 +3778,122 @@ fill_fs_info (struct grub_dirhook_info *info,
   return;
 }
 
-static grub_err_t
-grub_zfs_dir (grub_device_t device, const char *path,
-	      int (*hook) (const char *, const struct grub_dirhook_info *))
+/* Helper for grub_zfs_dir.  */
+static int
+iterate_zap (const char *name, grub_uint64_t val, struct grub_zfs_dir_ctx *ctx)
 {
-  struct grub_zfs_data *data;
   grub_err_t err;
-  int isfs;
-  auto int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val);
-  auto int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, 
-					    grub_uint64_t val);
-  auto int NESTED_FUNC_ATTR iterate_zap_snap (const char *name, 
-					      grub_uint64_t val);
+  struct grub_dirhook_info info;
 
-  int NESTED_FUNC_ATTR iterate_zap (const char *name, grub_uint64_t val)
-  {
-    struct grub_dirhook_info info;
-    dnode_end_t dn;
-    grub_memset (&info, 0, sizeof (info));
+  dnode_end_t dn;
+  grub_memset (&info, 0, sizeof (info));
 
-    dnode_get (&(data->subvol.mdn), val, 0, &dn, data);
+  dnode_get (&(ctx->data->subvol.mdn), val, 0, &dn, ctx->data);
 
-    if (dn.dn.dn_bonustype == DMU_OT_SA)
-      {
-	void *sahdrp;
-	int hdrsize;
+  if (dn.dn.dn_bonustype == DMU_OT_SA)
+    {
+      void *sahdrp;
+      int hdrsize;
 
-	if (dn.dn.dn_bonuslen != 0)
-	  {
-	    sahdrp = (sa_hdr_phys_t *) DN_BONUS (&data->dnode.dn);
-	  }
-	else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
-	  {
-	    blkptr_t *bp = &dn.dn.dn_spill;
+      if (dn.dn.dn_bonuslen != 0)
+	{
+	  sahdrp = (sa_hdr_phys_t *) DN_BONUS (&ctx->data->dnode.dn);
+	}
+      else if (dn.dn.dn_flags & DNODE_FLAG_SPILL_BLKPTR)
+	{
+	  blkptr_t *bp = &dn.dn.dn_spill;
 
-	    err = zio_read (bp, dn.endian, &sahdrp, NULL, data);
-	    if (err)
-	      {
-		grub_print_error ();
-		return 0;
-	      }
-	  }
-	else
-	  {
-	    grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
-	    grub_print_error ();
-	    return 0;
-	  }
+	  err = zio_read (bp, dn.endian, &sahdrp, NULL, ctx->data);
+	  if (err)
+	    {
+	      grub_print_error ();
+	      return 0;
+	    }
+	}
+      else
+	{
+	  grub_error (GRUB_ERR_BAD_FS, "filesystem is corrupt");
+	  grub_print_error ();
+	  return 0;
+	}
 
-	hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
-	info.mtimeset = 1;
-	info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
-	info.case_insensitive = data->subvol.case_insensitive;
-      }
-    
-    if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
-      {	
-	info.mtimeset = 1;
-	info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0],
-					dn.endian);
-      }
-	info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS);
-	grub_dprintf ("zfs", "type=%d, name=%s\n", 
-		  (int)dn.dn.dn_type, (char *)name);
-    return hook (name, &info);
-  }
+      hdrsize = SA_HDR_SIZE (((sa_hdr_phys_t *) sahdrp));
+      info.mtimeset = 1;
+      info.mtime = grub_zfs_to_cpu64 (grub_get_unaligned64 ((char *) sahdrp + hdrsize + SA_MTIME_OFFSET), dn.endian);
+      info.case_insensitive = ctx->data->subvol.case_insensitive;
+    }
+  
+  if (dn.dn.dn_bonustype == DMU_OT_ZNODE)
+    {	
+      info.mtimeset = 1;
+      info.mtime = grub_zfs_to_cpu64 (((znode_phys_t *) DN_BONUS (&dn.dn))->zp_mtime[0],
+				      dn.endian);
+    }
+      info.dir = (dn.dn.dn_type == DMU_OT_DIRECTORY_CONTENTS);
+      grub_dprintf ("zfs", "type=%d, name=%s\n", 
+		(int)dn.dn.dn_type, (char *)name);
+  return ctx->hook (name, &info, ctx->hook_data);
+}
 
-  int NESTED_FUNC_ATTR iterate_zap_fs (const char *name, grub_uint64_t val)
-  {
-    struct grub_dirhook_info info;
-    dnode_end_t mdn;
-    err = dnode_get (&(data->mos), val, 0, &mdn, data);
-    if (err)
-      return 0;
-    if (mdn.dn.dn_type != DMU_OT_DSL_DIR)
-      return 0;
+/* Helper for grub_zfs_dir.  */
+static int
+iterate_zap_fs (const char *name, grub_uint64_t val,
+		struct grub_zfs_dir_ctx *ctx)
+{
+  grub_err_t err;
+  struct grub_dirhook_info info;
 
-    fill_fs_info (&info, mdn, data);
-    return hook (name, &info);
-  }
-  int NESTED_FUNC_ATTR iterate_zap_snap (const char *name, grub_uint64_t val)
-  {
-    struct grub_dirhook_info info;
-    char *name2;
-    int ret;
-    dnode_end_t mdn;
+  dnode_end_t mdn;
+  err = dnode_get (&(ctx->data->mos), val, 0, &mdn, ctx->data);
+  if (err)
+    return 0;
+  if (mdn.dn.dn_type != DMU_OT_DSL_DIR)
+    return 0;
 
-    err = dnode_get (&(data->mos), val, 0, &mdn, data);
-    if (err)
-      return 0;
+  fill_fs_info (&info, mdn, ctx->data);
+  return ctx->hook (name, &info, ctx->hook_data);
+}
 
-    if (mdn.dn.dn_type != DMU_OT_DSL_DATASET)
-      return 0;
+/* Helper for grub_zfs_dir.  */
+static int
+iterate_zap_snap (const char *name, grub_uint64_t val,
+		  struct grub_zfs_dir_ctx *ctx)
+{
+  grub_err_t err;
+  struct grub_dirhook_info info;
+  char *name2;
+  int ret;
+
+  dnode_end_t mdn;
 
-    fill_fs_info (&info, mdn, data);
+  err = dnode_get (&(ctx->data->mos), val, 0, &mdn, ctx->data);
+  if (err)
+    return 0;
 
-    name2 = grub_malloc (grub_strlen (name) + 2);
-    name2[0] = '@';
-    grub_memcpy (name2 + 1, name, grub_strlen (name) + 1);
-    ret = hook (name2, &info);
-    grub_free (name2);
-    return ret;
-  }
+  if (mdn.dn.dn_type != DMU_OT_DSL_DATASET)
+    return 0;
+
+  fill_fs_info (&info, mdn, ctx->data);
+
+  name2 = grub_malloc (grub_strlen (name) + 2);
+  name2[0] = '@';
+  grub_memcpy (name2 + 1, name, grub_strlen (name) + 1);
+  ret = ctx->hook (name2, &info, ctx->hook_data);
+  grub_free (name2);
+  return ret;
+}
+
+static grub_err_t
+grub_zfs_dir (grub_device_t device, const char *path,
+	      grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_zfs_dir_ctx ctx = {
+    .hook = hook,
+    .hook_data = hook_data
+  };
+  struct grub_zfs_data *data;
+  grub_err_t err;
+  int isfs;
 
   data = zfs_mount (device);
   if (! data)
@@ -3860,6 +3904,8 @@ grub_zfs_dir (grub_device_t device, const char *path,
       zfs_unmount (data);
       return err;
     }
+  ctx.data = data;
+
   if (isfs)
     {
       grub_uint64_t childobj, headobj; 
@@ -3868,7 +3914,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
       struct grub_dirhook_info info;
 
       fill_fs_info (&info, data->dnode, data);
-      hook ("@", &info);
+      hook ("@", &info, hook_data);
       
       childobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_child_dir_zapobj, data->dnode.endian);
       headobj = grub_zfs_to_cpu64 (((dsl_dir_phys_t *) DN_BONUS (&data->dnode.dn))->dd_head_dataset_obj, data->dnode.endian);
@@ -3880,7 +3926,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
 	  return err;
 	}
 
-      zap_iterate_u64 (&dn, iterate_zap_fs, data);
+      zap_iterate_u64 (&dn, iterate_zap_fs, data, &ctx);
       
       err = dnode_get (&(data->mos), headobj, DMU_OT_DSL_DATASET, &dn, data);
       if (err)
@@ -3899,7 +3945,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
 	  return err;
 	}
 
-      zap_iterate_u64 (&dn, iterate_zap_snap, data);
+      zap_iterate_u64 (&dn, iterate_zap_snap, data, &ctx);
     }
   else
     {
@@ -3908,7 +3954,7 @@ grub_zfs_dir (grub_device_t device, const char *path,
 	  zfs_unmount (data);
 	  return grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
 	}
-      zap_iterate_u64 (&(data->dnode), iterate_zap, data);
+      zap_iterate_u64 (&(data->dnode), iterate_zap, data, &ctx);
     }
   zfs_unmount (data);
   return grub_errno;
diff --git a/grub-core/kern/corecmd.c b/grub-core/kern/corecmd.c
index 3441ccb..0dc4d00 100644
--- a/grub-core/kern/corecmd.c
+++ b/grub-core/kern/corecmd.c
@@ -105,7 +105,8 @@ grub_mini_print_devices (const char *name, void *data __attribute__ ((unused)))
 
 static int
 grub_mini_print_files (const char *filename,
-		       const struct grub_dirhook_info *info)
+		       const struct grub_dirhook_info *info,
+		       void *data __attribute__ ((unused)))
 {
   grub_printf ("%s%s ", filename, info->dir ? "/" : "");
 
@@ -160,7 +161,7 @@ grub_core_cmd_ls (struct grub_command *cmd __attribute__ ((unused)),
 	}
       else if (fs)
 	{
-	  (fs->dir) (dev, path, grub_mini_print_files);
+	  (fs->dir) (dev, path, grub_mini_print_files, NULL);
 	  grub_xputs ("\n");
 	  grub_refresh ();
 	}
diff --git a/grub-core/kern/emu/hostfs.c b/grub-core/kern/emu/hostfs.c
index 3cb089c..46bf5e8 100644
--- a/grub-core/kern/emu/hostfs.c
+++ b/grub-core/kern/emu/hostfs.c
@@ -65,8 +65,7 @@ struct grub_hostfs_data
 
 static grub_err_t
 grub_hostfs_dir (grub_device_t device, const char *path,
-		 int (*hook) (const char *filename,
-			      const struct grub_dirhook_info *info))
+		 grub_fs_dir_hook_t hook, void *hook_data)
 {
   DIR *dir;
 
@@ -91,7 +90,7 @@ grub_hostfs_dir (grub_device_t device, const char *path,
 	break;
 
       info.dir = !! is_dir (path, de->d_name);
-      hook (de->d_name, &info);
+      hook (de->d_name, &info, hook_data);
 
     }
 
diff --git a/grub-core/kern/fs.c b/grub-core/kern/fs.c
index 7e150f2..9085895 100644
--- a/grub-core/kern/fs.c
+++ b/grub-core/kern/fs.c
@@ -35,10 +35,11 @@ grub_fs_autoload_hook_t grub_fs_autoload_hook = 0;
 /* Helper for grub_fs_probe.  */
 static int
 probe_dummy_iter (const char *filename __attribute__ ((unused)),
-		  const struct grub_dirhook_info *info __attribute__ ((unused)))
-  {
-    return 1;
-  }
+		  const struct grub_dirhook_info *info __attribute__ ((unused)),
+		  void *data __attribute__ ((unused)))
+{
+  return 1;
+}
 
 grub_fs_t
 grub_fs_probe (grub_device_t device)
@@ -69,7 +70,7 @@ grub_fs_probe (grub_device_t device)
 	    }
 	  else
 #endif
-	    (p->dir) (device, "/", probe_dummy_iter);
+	    (p->dir) (device, "/", probe_dummy_iter, NULL);
 	  if (grub_errno == GRUB_ERR_NONE)
 	    return p;
 
@@ -93,7 +94,7 @@ grub_fs_probe (grub_device_t device)
 	    {
 	      p = grub_fs_list;
 
-	      (p->dir) (device, "/", probe_dummy_iter);
+	      (p->dir) (device, "/", probe_dummy_iter, NULL);
 	      if (grub_errno == GRUB_ERR_NONE)
 		{
 		  count--;
diff --git a/grub-core/loader/xnu.c b/grub-core/loader/xnu.c
index ed3fc72..8c522f5 100644
--- a/grub-core/loader/xnu.c
+++ b/grub-core/loader/xnu.c
@@ -1017,49 +1017,66 @@ grub_xnu_check_os_bundle_required (char *plistname,
   return ret;
 }
 
+/* Context for grub_xnu_scan_dir_for_kexts.  */
+struct grub_xnu_scan_dir_for_kexts_ctx
+{
+  char *dirname;
+  const char *osbundlerequired;
+  int maxrecursion;
+};
+
+/* Helper for grub_xnu_scan_dir_for_kexts.  */
+static int
+grub_xnu_scan_dir_for_kexts_load (const char *filename,
+				  const struct grub_dirhook_info *info,
+				  void *data)
+{
+  struct grub_xnu_scan_dir_for_kexts_ctx *ctx = data;
+  char *newdirname;
+
+  if (! info->dir)
+    return 0;
+  if (filename[0] == '.')
+    return 0;
+
+  if (grub_strlen (filename) < 5 ||
+      grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
+    return 0;
+
+  newdirname
+    = grub_malloc (grub_strlen (ctx->dirname) + grub_strlen (filename) + 2);
+
+  /* It's a .kext. Try to load it. */
+  if (newdirname)
+    {
+      grub_strcpy (newdirname, ctx->dirname);
+      newdirname[grub_strlen (newdirname) + 1] = 0;
+      newdirname[grub_strlen (newdirname)] = '/';
+      grub_strcpy (newdirname + grub_strlen (newdirname), filename);
+      grub_xnu_load_kext_from_dir (newdirname, ctx->osbundlerequired,
+				   ctx->maxrecursion);
+      if (grub_errno == GRUB_ERR_BAD_OS)
+	grub_errno = GRUB_ERR_NONE;
+      grub_free (newdirname);
+    }
+  return 0;
+}
+
 /* Load all loadable kexts placed under DIRNAME and matching OSBUNDLEREQUIRED */
 grub_err_t
 grub_xnu_scan_dir_for_kexts (char *dirname, const char *osbundlerequired,
 			     int maxrecursion)
 {
+  struct grub_xnu_scan_dir_for_kexts_ctx ctx = {
+    .dirname = dirname,
+    .osbundlerequired = osbundlerequired,
+    .maxrecursion = maxrecursion
+  };
   grub_device_t dev;
   char *device_name;
   grub_fs_t fs;
   const char *path;
 
-  auto int load_hook (const char *filename,
-		      const struct grub_dirhook_info *info);
-  int load_hook (const char *filename, const struct grub_dirhook_info *info)
-  {
-    char *newdirname;
-    if (! info->dir)
-      return 0;
-    if (filename[0] == '.')
-      return 0;
-
-    if (grub_strlen (filename) < 5 ||
-	grub_memcmp (filename + grub_strlen (filename) - 5, ".kext", 5) != 0)
-      return 0;
-
-    newdirname
-      = grub_malloc (grub_strlen (dirname) + grub_strlen (filename) + 2);
-
-    /* It's a .kext. Try to load it. */
-    if (newdirname)
-      {
-	grub_strcpy (newdirname, dirname);
-	newdirname[grub_strlen (newdirname) + 1] = 0;
-	newdirname[grub_strlen (newdirname)] = '/';
-	grub_strcpy (newdirname + grub_strlen (newdirname), filename);
-	grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
-				     maxrecursion);
-	if (grub_errno == GRUB_ERR_BAD_OS)
-	  grub_errno = GRUB_ERR_NONE;
-	grub_free (newdirname);
-      }
-    return 0;
-  }
-
   if (! grub_xnu_heap_size)
     return grub_error (GRUB_ERR_BAD_OS, N_("you need to load the kernel first"));
 
@@ -1075,7 +1092,7 @@ grub_xnu_scan_dir_for_kexts (char *dirname, const char *osbundlerequired,
 	path++;
 
       if (fs)
-	(fs->dir) (dev, path, load_hook);
+	(fs->dir) (dev, path, grub_xnu_scan_dir_for_kexts_load, &ctx);
       grub_device_close (dev);
     }
   grub_free (device_name);
@@ -1083,60 +1100,78 @@ grub_xnu_scan_dir_for_kexts (char *dirname, const char *osbundlerequired,
   return GRUB_ERR_NONE;
 }
 
+/* Context for grub_xnu_load_kext_from_dir.  */
+struct grub_xnu_load_kext_from_dir_ctx
+{
+  char *dirname;
+  const char *osbundlerequired;
+  int maxrecursion;
+  char *plistname;
+  char *newdirname;
+  int usemacos;
+};
+
+/* Helper for grub_xnu_load_kext_from_dir.  */
+static int
+grub_xnu_load_kext_from_dir_load (const char *filename,
+				  const struct grub_dirhook_info *info,
+				  void *data)
+{
+  struct grub_xnu_load_kext_from_dir_ctx *ctx = data;
+
+  if (grub_strlen (filename) > 15)
+    return 0;
+  grub_strcpy (ctx->newdirname + grub_strlen (ctx->dirname) + 1, filename);
+
+  /* If the kext contains directory "Contents" all real stuff is in
+     this directory. */
+  if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
+    grub_xnu_load_kext_from_dir (ctx->newdirname, ctx->osbundlerequired,
+				 ctx->maxrecursion - 1);
+
+  /* Directory "Plugins" contains nested kexts. */
+  if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
+    grub_xnu_scan_dir_for_kexts (ctx->newdirname, ctx->osbundlerequired,
+				 ctx->maxrecursion - 1);
+
+  /* Directory "MacOS" contains executable, otherwise executable is
+     on the top. */
+  if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
+    ctx->usemacos = 1;
+
+  /* Info.plist is the file which governs our future actions. */
+  if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
+      && ! ctx->plistname)
+    ctx->plistname = grub_strdup (ctx->newdirname);
+  return 0;
+}
+
 /* Load extension DIRNAME. (extensions are directories in xnu) */
 grub_err_t
 grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired,
 			     int maxrecursion)
 {
+  struct grub_xnu_load_kext_from_dir_ctx ctx = {
+    .dirname = dirname,
+    .osbundlerequired = osbundlerequired,
+    .maxrecursion = maxrecursion,
+    .plistname = 0,
+    .usemacos = 0
+  };
   grub_device_t dev;
-  char *plistname = 0;
-  char *newdirname;
   char *newpath;
   char *device_name;
   grub_fs_t fs;
   const char *path;
   char *binsuffix;
-  int usemacos = 0;
   grub_file_t binfile;
 
-  auto int load_hook (const char *filename,
-		      const struct grub_dirhook_info *info);
-
-  int load_hook (const char *filename, const struct grub_dirhook_info *info)
-  {
-    if (grub_strlen (filename) > 15)
-      return 0;
-    grub_strcpy (newdirname + grub_strlen (dirname) + 1, filename);
-
-    /* If the kext contains directory "Contents" all real stuff is in
-       this directory. */
-    if (info->dir && grub_strcasecmp (filename, "Contents") == 0)
-      grub_xnu_load_kext_from_dir (newdirname, osbundlerequired,
-				   maxrecursion - 1);
-
-    /* Directory "Plugins" contains nested kexts. */
-    if (info->dir && grub_strcasecmp (filename, "Plugins") == 0)
-      grub_xnu_scan_dir_for_kexts (newdirname, osbundlerequired,
-				   maxrecursion - 1);
-
-    /* Directory "MacOS" contains executable, otherwise executable is
-       on the top. */
-    if (info->dir && grub_strcasecmp (filename, "MacOS") == 0)
-      usemacos = 1;
-
-    /* Info.plist is the file which governs our future actions. */
-    if (! info->dir && grub_strcasecmp (filename, "Info.plist") == 0
-	&& ! plistname)
-      plistname = grub_strdup (newdirname);
-    return 0;
-  }
-
-  newdirname = grub_malloc (grub_strlen (dirname) + 20);
-  if (! newdirname)
+  ctx.newdirname = grub_malloc (grub_strlen (dirname) + 20);
+  if (! ctx.newdirname)
     return grub_errno;
-  grub_strcpy (newdirname, dirname);
-  newdirname[grub_strlen (dirname)] = '/';
-  newdirname[grub_strlen (dirname) + 1] = 0;
+  grub_strcpy (ctx.newdirname, dirname);
+  ctx.newdirname[grub_strlen (dirname)] = '/';
+  ctx.newdirname[grub_strlen (dirname) + 1] = 0;
   device_name = grub_file_get_device_name (dirname);
   dev = grub_device_open (device_name);
   if (dev)
@@ -1148,18 +1183,18 @@ grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired,
       else
 	path++;
 
-      newpath = grub_strchr (newdirname, ')');
+      newpath = grub_strchr (ctx.newdirname, ')');
       if (! newpath)
-	newpath = newdirname;
+	newpath = ctx.newdirname;
       else
 	newpath++;
 
       /* Look at the directory. */
       if (fs)
-	(fs->dir) (dev, path, load_hook);
+	(fs->dir) (dev, path, grub_xnu_load_kext_from_dir_load, &ctx);
 
-      if (plistname && grub_xnu_check_os_bundle_required
-	  (plistname, osbundlerequired, &binsuffix))
+      if (ctx.plistname && grub_xnu_check_os_bundle_required
+	  (ctx.plistname, osbundlerequired, &binsuffix))
 	{
 	  if (binsuffix)
 	    {
@@ -1168,29 +1203,29 @@ grub_xnu_load_kext_from_dir (char *dirname, const char *osbundlerequired,
 					   + grub_strlen (binsuffix)
 					   + sizeof ("/MacOS/"));
 	      grub_strcpy (binname, dirname);
-	      if (usemacos)
+	      if (ctx.usemacos)
 		grub_strcpy (binname + grub_strlen (binname), "/MacOS/");
 	      else
 		grub_strcpy (binname + grub_strlen (binname), "/");
 	      grub_strcpy (binname + grub_strlen (binname), binsuffix);
-	      grub_dprintf ("xnu", "%s:%s\n", plistname, binname);
+	      grub_dprintf ("xnu", "%s:%s\n", ctx.plistname, binname);
 	      binfile = grub_file_open (binname);
 	      if (! binfile)
 		grub_errno = GRUB_ERR_NONE;
 
 	      /* Load the extension. */
-	      grub_xnu_load_driver (plistname, binfile,
+	      grub_xnu_load_driver (ctx.plistname, binfile,
 				    binname);
 	      grub_free (binname);
 	      grub_free (binsuffix);
 	    }
 	  else
 	    {
-	      grub_dprintf ("xnu", "%s:0\n", plistname);
-	      grub_xnu_load_driver (plistname, 0, 0);
+	      grub_dprintf ("xnu", "%s:0\n", ctx.plistname);
+	      grub_xnu_load_driver (ctx.plistname, 0, 0);
 	    }
 	}
-      grub_free (plistname);
+      grub_free (ctx.plistname);
       grub_device_close (dev);
     }
   grub_free (device_name);
diff --git a/grub-core/net/net.c b/grub-core/net/net.c
index 01c5d32..aebbe4b 100644
--- a/grub-core/net/net.c
+++ b/grub-core/net/net.c
@@ -1253,8 +1253,8 @@ grub_net_open_real (const char *name)
 
 static grub_err_t
 grub_net_fs_dir (grub_device_t device, const char *path __attribute__ ((unused)),
-	       int (*hook) (const char *filename,
-			    const struct grub_dirhook_info *info) __attribute__ ((unused)))
+		 grub_fs_dir_hook_t hook __attribute__ ((unused)),
+		 void *hook_data __attribute__ ((unused)))
 {
   if (!device->net)
     return grub_error (GRUB_ERR_BUG, "invalid net device");
diff --git a/grub-core/normal/completion.c b/grub-core/normal/completion.c
index 367a2b7..71c083c 100644
--- a/grub-core/normal/completion.c
+++ b/grub-core/normal/completion.c
@@ -123,7 +123,8 @@ iterate_partition (grub_disk_t disk, const grub_partition_t p,
 }
 
 static int
-iterate_dir (const char *filename, const struct grub_dirhook_info *info)
+iterate_dir (const char *filename, const struct grub_dirhook_info *info,
+	     void *data __attribute__ ((unused)))
 {
   if (! info->dir)
     {
@@ -295,7 +296,7 @@ complete_file (void)
       dirfile[1] = '\0';
 
       /* Iterate the directory.  */
-      (fs->dir) (dev, dir, iterate_dir);
+      (fs->dir) (dev, dir, iterate_dir, NULL);
 
       grub_free (dir);
 
diff --git a/include/grub/fs.h b/include/grub/fs.h
index 503d1a9..e451797 100644
--- a/include/grub/fs.h
+++ b/include/grub/fs.h
@@ -41,6 +41,10 @@ struct grub_dirhook_info
   grub_int32_t mtime;
 };
 
+typedef int (*grub_fs_dir_hook_t) (const char *filename,
+				   const struct grub_dirhook_info *info,
+				   void *data);
+
 /* Filesystem descriptor.  */
 struct grub_fs
 {
@@ -53,8 +57,7 @@ struct grub_fs
 
   /* Call HOOK with each file under DIR.  */
   grub_err_t (*dir) (grub_device_t device, const char *path,
-		     int (*hook) (const char *filename,
-				  const struct grub_dirhook_info *info));
+		     grub_fs_dir_hook_t hook, void *hook_data);
 
   /* Open a file named NAME and initialize FILE.  */
   grub_err_t (*open) (struct grub_file *file, const char *name);
diff --git a/include/grub/fshelp.h b/include/grub/fshelp.h
index 4838fca..e437d4c 100644
--- a/include/grub/fshelp.h
+++ b/include/grub/fshelp.h
@@ -38,24 +38,25 @@ enum grub_fshelp_filetype
     GRUB_FSHELP_SYMLINK
   };
 
+typedef int (*grub_fshelp_iterate_dir_hook_t) (const char *filename,
+					       enum grub_fshelp_filetype filetype,
+					       grub_fshelp_node_t node,
+					       void *data);
+
 /* Lookup the node PATH.  The node ROOTNODE describes the root of the
    directory tree.  The node found is returned in FOUNDNODE, which is
    either a ROOTNODE or a new malloc'ed node.  ITERATE_DIR is used to
    iterate over all directory entries in the current node.
    READ_SYMLINK is used to read the symlink if a node is a symlink.
    EXPECTTYPE is the type node that is expected by the called, an
-   error is generated if the node is not of the expected type.  Make
-   sure you use the NESTED_FUNC_ATTR macro for HOOK, this is required
-   because GCC has a nasty bug when using regparm=3.  */
+   error is generated if the node is not of the expected type.  */
 grub_err_t
 EXPORT_FUNC(grub_fshelp_find_file) (const char *path,
 				    grub_fshelp_node_t rootnode,
 				    grub_fshelp_node_t *foundnode,
 				    int (*iterate_dir) (grub_fshelp_node_t dir,
-							int NESTED_FUNC_ATTR
-							(*hook) (const char *filename,
-								 enum grub_fshelp_filetype filetype,
-								 grub_fshelp_node_t node)),
+							grub_fshelp_iterate_dir_hook_t hook,
+							void *hook_data),
 				    char *(*read_symlink) (grub_fshelp_node_t node),
 				    enum grub_fshelp_filetype expect);
 
diff --git a/util/grub-mount.c b/util/grub-mount.c
index e3eb1d7..d0ab6a2 100644
--- a/util/grub-mount.c
+++ b/util/grub-mount.c
@@ -116,30 +116,38 @@ translate_error (void)
   return ret;
 }
 
+/* Context for fuse_getattr.  */
+struct fuse_getattr_ctx
+{
+  char *filename;
+  struct grub_dirhook_info file_info;
+  int file_exists;
+};
+
+/* A hook for iterating directories. */
+static int
+fuse_getattr_find_file (const char *cur_filename,
+			const struct grub_dirhook_info *info, void *data)
+{
+  struct fuse_getattr_ctx *ctx = data;
+
+  if ((info->case_insensitive ? grub_strcasecmp (cur_filename, ctx->filename)
+       : grub_strcmp (cur_filename, ctx->filename)) == 0)
+    {
+      ctx->file_info = *info;
+      ctx->file_exists = 1;
+      return 1;
+    }
+  return 0;
+}
+
 static int
 fuse_getattr (const char *path, struct stat *st)
 {
-  char *filename, *pathname, *path2;
+  struct fuse_getattr_ctx ctx;
+  char *pathname, *path2;
   const char *pathname_t;
-  struct grub_dirhook_info file_info;
-  int file_exists = 0;
   
-  /* A hook for iterating directories. */
-  auto int find_file (const char *cur_filename,
-		      const struct grub_dirhook_info *info);
-  int find_file (const char *cur_filename,
-		 const struct grub_dirhook_info *info)
-  {
-    if ((info->case_insensitive ? grub_strcasecmp (cur_filename, filename)
-	 : grub_strcmp (cur_filename, filename)) == 0)
-      {
-	file_info = *info;
-	file_exists = 1;
-	return 1;
-      }
-    return 0;
-  }
-
   if (path[0] == '/' && path[1] == 0)
     {
       st->st_dev = 0;
@@ -155,7 +163,7 @@ fuse_getattr (const char *path, struct stat *st)
       return 0;
     }
 
-  file_exists = 0;
+  ctx.file_exists = 0;
 
   pathname_t = grub_strchr (path, ')');
   if (! pathname_t)
@@ -169,35 +177,35 @@ fuse_getattr (const char *path, struct stat *st)
     pathname[grub_strlen (pathname) - 1] = 0;
 
   /* Split into path and filename. */
-  filename = grub_strrchr (pathname, '/');
-  if (! filename)
+  ctx.filename = grub_strrchr (pathname, '/');
+  if (! ctx.filename)
     {
       path2 = grub_strdup ("/");
-      filename = pathname;
+      ctx.filename = pathname;
     }
   else
     {
-      filename++;
+      ctx.filename++;
       path2 = grub_strdup (pathname);
-      path2[filename - pathname] = 0;
+      path2[ctx.filename - pathname] = 0;
     }
 
   /* It's the whole device. */
-  (fs->dir) (dev, path2, find_file);
+  (fs->dir) (dev, path2, fuse_getattr_find_file, &ctx);
 
   grub_free (path2);
-  if (!file_exists)
+  if (!ctx.file_exists)
     {
       grub_errno = GRUB_ERR_NONE;
       return -ENOENT;
     }
   st->st_dev = 0;
   st->st_ino = 0;
-  st->st_mode = file_info.dir ? (0555 | S_IFDIR) : (0444 | S_IFREG);
+  st->st_mode = ctx.file_info.dir ? (0555 | S_IFDIR) : (0444 | S_IFREG);
   st->st_uid = 0;
   st->st_gid = 0;
   st->st_rdev = 0;
-  if (!file_info.dir)
+  if (!ctx.file_info.dir)
     {
       grub_file_t file;
       file = grub_file_open (path);
@@ -210,8 +218,8 @@ fuse_getattr (const char *path, struct stat *st)
     st->st_size = 0;
   st->st_blksize = 512;
   st->st_blocks = (st->st_size + 511) >> 9;
-  st->st_atime = st->st_mtime = st->st_ctime = file_info.mtimeset
-    ? file_info.mtime : 0;
+  st->st_atime = st->st_mtime = st->st_ctime = ctx.file_info.mtimeset
+    ? ctx.file_info.mtime : 0;
   grub_errno = GRUB_ERR_NONE;
   return 0;
 }
@@ -271,39 +279,55 @@ fuse_release (const char *path, struct fuse_file_info *fi)
   return 0;
 }
 
+/* Context for fuse_readdir.  */
+struct fuse_readdir_ctx
+{
+  const char *path;
+  void *buf;
+  fuse_fill_dir_t fill;
+};
+
+/* Helper for fuse_readdir.  */
+static int
+fuse_readdir_call_fill (const char *filename,
+			const struct grub_dirhook_info *info, void *data)
+{
+  struct fuse_readdir_ctx *ctx = data;
+  struct stat st;
+
+  grub_memset (&st, 0, sizeof (st));
+  st.st_mode = info->dir ? (0555 | S_IFDIR) : (0444 | S_IFREG);
+  if (!info->dir)
+    {
+      grub_file_t file;
+      char *tmp;
+      tmp = xasprintf ("%s/%s", ctx->path, filename);
+      file = grub_file_open (tmp);
+      free (tmp);
+      if (! file)
+	return translate_error ();
+      st.st_size = file->size;
+      grub_file_close (file);
+    }
+  st.st_blksize = 512;
+  st.st_blocks = (st.st_size + 511) >> 9;
+  st.st_atime = st.st_mtime = st.st_ctime
+    = info->mtimeset ? info->mtime : 0;
+  ctx->fill (ctx->buf, filename, &st, 0);
+  return 0;
+}
+
 static int 
 fuse_readdir (const char *path, void *buf,
 	      fuse_fill_dir_t fill, off_t off, struct fuse_file_info *fi)
 {
+  struct fuse_readdir_ctx ctx = {
+    .path = path,
+    .buf = buf,
+    .fill = fill
+  };
   char *pathname;
 
-  auto int call_fill (const char *filename,
-		      const struct grub_dirhook_info *info);
-  int call_fill (const char *filename, const struct grub_dirhook_info *info)
-  {
-    struct stat st;
-    grub_memset (&st, 0, sizeof (st));
-    st.st_mode = info->dir ? (0555 | S_IFDIR) : (0444 | S_IFREG);
-    if (!info->dir)
-      {
-	grub_file_t file;
-	char *tmp;
-	tmp = xasprintf ("%s/%s", path, filename);
-	file = grub_file_open (tmp);
-	free (tmp);
-	if (! file)
-	  return translate_error ();
-	st.st_size = file->size;
-	grub_file_close (file);
-      }
-    st.st_blksize = 512;
-    st.st_blocks = (st.st_size + 511) >> 9;
-    st.st_atime = st.st_mtime = st.st_ctime
-      = info->mtimeset ? info->mtime : 0;
-    fill (buf, filename, &st, 0);
-    return 0;
-  }
-
   pathname = xstrdup (path);
   
   /* Remove trailing '/'. */
@@ -311,7 +335,7 @@ fuse_readdir (const char *path, void *buf,
 	 && pathname[grub_strlen (pathname) - 1] == '/')
     pathname[grub_strlen (pathname) - 1] = 0;
 
-  (fs->dir) (dev, pathname, call_fill);
+  (fs->dir) (dev, pathname, fuse_readdir_call_fill, &ctx);
   free (pathname);
   grub_errno = GRUB_ERR_NONE;
   return 0;
-- 
1.8.2.1