]> git.ipfire.org Git - thirdparty/grub.git/commitdiff
FAT: Convert to fshelp.
authorVladimir Serbinenko <phcoder@gmail.com>
Mon, 27 Jul 2015 10:49:26 +0000 (12:49 +0200)
committerVladimir Serbinenko <phcoder@gmail.com>
Mon, 27 Jul 2015 10:49:26 +0000 (12:49 +0200)
exFAT doesn't handle "." and ".." correctly, convert it to fshelp to
reuse the same logic.

grub-core/fs/fat.c

index 827708c18149a612ba79cdfadb1b7a8bfa79fd9f..aa2145cf22d6f23d5ca8da2c41c73d72a339481b 100644 (file)
@@ -31,6 +31,7 @@
 #else
 #include <grub/exfat.h>
 #endif
+#include <grub/fshelp.h>
 #include <grub/i18n.h>
 
 GRUB_MOD_LICENSE ("GPLv3+");
@@ -173,8 +174,6 @@ struct grub_fat_data
 #ifndef MODE_EXFAT
   grub_uint32_t root_sector;
   grub_uint32_t num_root_sectors;
-#else
-  int is_contiguous;
 #endif
 
   int cluster_bits;
@@ -182,13 +181,22 @@ struct grub_fat_data
   grub_uint32_t cluster_sector;
   grub_uint32_t num_clusters;
 
+  grub_uint32_t uuid;
+};
+
+struct grub_fshelp_node {
+  grub_disk_t disk;
+  struct grub_fat_data *data;
+
   grub_uint8_t attr;
   grub_ssize_t file_size;
   grub_uint32_t file_cluster;
   grub_uint32_t cur_cluster_num;
   grub_uint32_t cur_cluster;
 
-  grub_uint32_t uuid;
+#ifdef MODE_EXFAT
+  int is_contiguous;
+#endif
 };
 
 static grub_dl_t my_mod;
@@ -427,13 +435,6 @@ grub_fat_mount (grub_disk_t disk)
   (void) magic;
 #endif
 
-  /* Start from the root directory.  */
-  data->file_cluster = data->root_cluster;
-  data->cur_cluster_num = ~0U;
-  data->attr = GRUB_FAT_ATTR_DIRECTORY;
-#ifdef MODE_EXFAT
-  data->is_contiguous = 0;
-#endif
   return data;
 
  fail:
@@ -444,7 +445,7 @@ grub_fat_mount (grub_disk_t disk)
 }
 
 static grub_ssize_t
-grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
+grub_fat_read_data (grub_disk_t disk, grub_fshelp_node_t node,
                    grub_disk_read_hook_t read_hook, void *read_hook_data,
                    grub_off_t offset, grub_size_t len, char *buf)
 {
@@ -457,13 +458,13 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
 #ifndef MODE_EXFAT
   /* This is a special case. FAT12 and FAT16 doesn't have the root directory
      in clusters.  */
-  if (data->file_cluster == ~0U)
+  if (node->file_cluster == ~0U)
     {
-      size = (data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset;
+      size = (node->data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset;
       if (size > len)
        size = len;
 
-      if (grub_disk_read (disk, data->root_sector, offset, size, buf))
+      if (grub_disk_read (disk, node->data->root_sector, offset, size, buf))
        return -1;
 
       return size;
@@ -471,12 +472,12 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
 #endif
 
 #ifdef MODE_EXFAT
-  if (data->is_contiguous)
+  if (node->is_contiguous)
     {
       /* Read the data here.  */
-      sector = (data->cluster_sector
-               + ((data->file_cluster - 2)
-                  << data->cluster_bits));
+      sector = (node->data->cluster_sector
+               + ((node->file_cluster - 2)
+                  << node->data->cluster_bits));
 
       disk->read_hook = read_hook;
       disk->read_hook_data = read_hook_data;
@@ -491,53 +492,53 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
 #endif
 
   /* Calculate the logical cluster number and offset.  */
-  logical_cluster_bits = (data->cluster_bits
+  logical_cluster_bits = (node->data->cluster_bits
                          + GRUB_DISK_SECTOR_BITS);
   logical_cluster = offset >> logical_cluster_bits;
   offset &= (1ULL << logical_cluster_bits) - 1;
 
-  if (logical_cluster < data->cur_cluster_num)
+  if (logical_cluster < node->cur_cluster_num)
     {
-      data->cur_cluster_num = 0;
-      data->cur_cluster = data->file_cluster;
+      node->cur_cluster_num = 0;
+      node->cur_cluster = node->file_cluster;
     }
 
   while (len)
     {
-      while (logical_cluster > data->cur_cluster_num)
+      while (logical_cluster > node->cur_cluster_num)
        {
          /* Find next cluster.  */
          grub_uint32_t next_cluster;
          grub_uint32_t fat_offset;
 
-         switch (data->fat_size)
+         switch (node->data->fat_size)
            {
            case 32:
-             fat_offset = data->cur_cluster << 2;
+             fat_offset = node->cur_cluster << 2;
              break;
            case 16:
-             fat_offset = data->cur_cluster << 1;
+             fat_offset = node->cur_cluster << 1;
              break;
            default:
              /* case 12: */
-             fat_offset = data->cur_cluster + (data->cur_cluster >> 1);
+             fat_offset = node->cur_cluster + (node->cur_cluster >> 1);
              break;
            }
 
          /* Read the FAT.  */
-         if (grub_disk_read (disk, data->fat_sector, fat_offset,
-                             (data->fat_size + 7) >> 3,
+         if (grub_disk_read (disk, node->data->fat_sector, fat_offset,
+                             (node->data->fat_size + 7) >> 3,
                              (char *) &next_cluster))
            return -1;
 
          next_cluster = grub_le_to_cpu32 (next_cluster);
-         switch (data->fat_size)
+         switch (node->data->fat_size)
            {
            case 16:
              next_cluster &= 0xFFFF;
              break;
            case 12:
-             if (data->cur_cluster & 1)
+             if (node->cur_cluster & 1)
                next_cluster >>= 4;
 
              next_cluster &= 0x0FFF;
@@ -545,27 +546,27 @@ grub_fat_read_data (grub_disk_t disk, struct grub_fat_data *data,
            }
 
          grub_dprintf ("fat", "fat_size=%d, next_cluster=%u\n",
-                       data->fat_size, next_cluster);
+                       node->data->fat_size, next_cluster);
 
          /* Check the end.  */
-         if (next_cluster >= data->cluster_eof_mark)
+         if (next_cluster >= node->data->cluster_eof_mark)
            return ret;
 
-         if (next_cluster < 2 || next_cluster >= data->num_clusters)
+         if (next_cluster < 2 || next_cluster >= node->data->num_clusters)
            {
              grub_error (GRUB_ERR_BAD_FS, "invalid cluster %u",
                          next_cluster);
              return -1;
            }
 
-         data->cur_cluster = next_cluster;
-         data->cur_cluster_num++;
+         node->cur_cluster = next_cluster;
+         node->cur_cluster_num++;
        }
 
       /* Read the data here.  */
-      sector = (data->cluster_sector
-               + ((data->cur_cluster - 2)
-                  << data->cluster_bits));
+      sector = (node->data->cluster_sector
+               + ((node->cur_cluster - 2)
+                  << node->data->cluster_bits));
       size = (1 << logical_cluster_bits) - offset;
       if (size > len)
        size = len;
@@ -631,7 +632,7 @@ grub_fat_iterate_fini (struct grub_fat_iterate_context *ctxt)
 
 #ifdef MODE_EXFAT
 static grub_err_t
-grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
+grub_fat_iterate_dir_next (grub_fshelp_node_t node,
                           struct grub_fat_iterate_context *ctxt)
 {
   grub_memset (&ctxt->dir, 0, sizeof (ctxt->dir));
@@ -641,7 +642,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
 
       ctxt->offset += sizeof (dir);
 
-      if (grub_fat_read_data (disk, data, 0, 0, ctxt->offset, sizeof (dir),
+      if (grub_fat_read_data (node->disk, node, 0, 0, ctxt->offset, sizeof (dir),
                              (char *) &dir)
           != sizeof (dir))
        break;
@@ -663,7 +664,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
            {
              struct grub_fat_dir_entry sec;
              ctxt->offset += sizeof (sec);
-             if (grub_fat_read_data (disk, data, 0, 0,
+             if (grub_fat_read_data (node->disk, node, 0, 0,
                                      ctxt->offset, sizeof (sec), (char *) &sec)
                  != sizeof (sec))
                break;
@@ -727,7 +728,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
 #else
 
 static grub_err_t
-grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
+grub_fat_iterate_dir_next (grub_fshelp_node_t node,
                           struct grub_fat_iterate_context *ctxt)
 {
   char *filep = 0;
@@ -742,7 +743,7 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
       ctxt->offset += sizeof (ctxt->dir);
 
       /* Read a directory entry.  */
-      if (grub_fat_read_data (disk, data, 0, 0,
+      if (grub_fat_read_data (node->disk, node, 0, 0,
                              ctxt->offset, sizeof (ctxt->dir),
                              (char *) &ctxt->dir)
           != sizeof (ctxt->dir) || ctxt->dir.name[0] == 0)
@@ -853,84 +854,20 @@ grub_fat_iterate_dir_next (grub_disk_t disk, struct grub_fat_data *data,
 
 #endif
 
-/* Find the underlying directory or file in PATH and return the
-   next path. If there is no next path or an error occurs, return NULL.
-   If HOOK is specified, call it with each file name.  */
-static char *
-grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
-                  const char *path, const char *origpath,
-                  grub_fs_dir_hook_t hook, void *hook_data)
+static grub_err_t lookup_file (grub_fshelp_node_t node,
+                              const char *name,
+                              grub_fshelp_node_t *foundnode,
+                              enum grub_fshelp_filetype *foundtype)
 {
-  char *dirname, *dirp;
-  int call_hook;
-  int found = 0;
-  struct grub_fat_iterate_context ctxt;
   grub_err_t err;
-
-  if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
-    {
-      grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
-      return 0;
-    }
-
-  do
-    {
-      /* Extract a directory name.  */
-      while (*path == '/')
-       path++;
-
-      /* Emulate special "." and ".." entries in root directory */
-      if (data->file_cluster == data->root_cluster)
-       {
-         if (*path != '.')
-           break;
-         if (!path[1] || path[1] == '/')
-           {
-             path++;
-             continue;
-           }
-         if (path[1] == '.' && (!path[2] || path[2] == '/'))
-           {
-             path += 2;
-             continue;
-           }
-       }
-      break;
-    }
-  while (1);
-
-  dirp = grub_strchr (path, '/');
-  if (dirp)
-    {
-      unsigned len = dirp - path;
-
-      dirname = grub_malloc (len + 1);
-      if (! dirname)
-       goto fail;
-
-      grub_memcpy (dirname, path, len);
-      dirname[len] = '\0';
-    }
-  else
-    /* This is actually a file.  */
-    dirname = grub_strdup (path);
-
-  call_hook = (! dirp && hook);
+  struct grub_fat_iterate_context ctxt;
 
   err = grub_fat_iterate_init (&ctxt);
   if (err)
-    {
-      grub_free (dirname);
-      return 0;
-    }
+    return err;
 
-  while (!(err = grub_fat_iterate_dir_next (disk, data, &ctxt)))
+  while (!(err = grub_fat_iterate_dir_next (node, &ctxt)))
     {
-      struct grub_dirhook_info info;
-      grub_memset (&info, 0, sizeof (info));
-
-      info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY);
-      info.case_insensitive = 1;
 
 #ifdef MODE_EXFAT
       if (!ctxt.dir.have_stream)
@@ -939,36 +876,33 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
       if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
        continue;
 #endif
-      if (*dirname == '\0' && call_hook)
-       {
-         if (hook (ctxt.filename, &info, hook_data))
-           break;
-         else
-           continue;
-       }
 
-      if (grub_strcasecmp (dirname, ctxt.filename) == 0)
+      if (grub_strcasecmp (name, ctxt.filename) == 0)
        {
-         found = 1;
-         data->attr = ctxt.dir.attr;
+         *foundnode = grub_malloc (sizeof (struct grub_fshelp_node));
+         if (!*foundnode)
+           return grub_errno;
+         (*foundnode)->attr = ctxt.dir.attr;
 #ifdef MODE_EXFAT
-         data->file_size = ctxt.dir.file_size;
-         data->file_cluster = ctxt.dir.first_cluster;
-         data->is_contiguous = ctxt.dir.is_contiguous;
+         (*foundnode)->file_size = ctxt.dir.file_size;
+         (*foundnode)->file_cluster = ctxt.dir.first_cluster;
+         (*foundnode)->is_contiguous = ctxt.dir.is_contiguous;
 #else
-         data->file_size = grub_le_to_cpu32 (ctxt.dir.file_size);
-         data->file_cluster = ((grub_le_to_cpu16 (ctxt.dir.first_cluster_high) << 16)
+         (*foundnode)->file_size = grub_le_to_cpu32 (ctxt.dir.file_size);
+         (*foundnode)->file_cluster = ((grub_le_to_cpu16 (ctxt.dir.first_cluster_high) << 16)
                                | grub_le_to_cpu16 (ctxt.dir.first_cluster_low));
          /* If directory points to root, starting cluster is 0 */
-         if (!data->file_cluster)
-           data->file_cluster = data->root_cluster;
+         if (!(*foundnode)->file_cluster)
+           (*foundnode)->file_cluster = node->data->root_cluster;
 #endif
-         data->cur_cluster_num = ~0U;
+         (*foundnode)->cur_cluster_num = ~0U;
+         (*foundnode)->data = node->data;
+         (*foundnode)->disk = node->disk;
 
-         if (call_hook)
-           hook (ctxt.filename, &info, hook_data);
+         *foundtype = ((*foundnode)->attr & GRUB_FAT_ATTR_DIRECTORY) ? GRUB_FSHELP_DIR : GRUB_FSHELP_REG;
 
-         break;
+         grub_fat_iterate_fini (&ctxt);
+         return GRUB_ERR_NONE;
        }
     }
 
@@ -976,13 +910,8 @@ grub_fat_find_dir (grub_disk_t disk, struct grub_fat_data *data,
   if (err == GRUB_ERR_EOF)
     err = 0;
 
-  if (grub_errno == GRUB_ERR_NONE && ! found && !call_hook)
-    grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("file `%s' not found"), origpath);
+  return err;
 
- fail:
-  grub_free (dirname);
-
-  return found ? dirp : 0;
 }
 
 static grub_err_t
@@ -991,9 +920,9 @@ grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
 {
   struct grub_fat_data *data = 0;
   grub_disk_t disk = device->disk;
-  grub_size_t len;
-  char *dirname = 0;
-  char *p;
+  grub_fshelp_node_t found = NULL;
+  grub_err_t err;
+  struct grub_fat_iterate_context ctxt;
 
   grub_dl_ref (my_mod);
 
@@ -1001,27 +930,53 @@ grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
   if (! data)
     goto fail;
 
-  /* Make sure that DIRNAME terminates with '/'.  */
-  len = grub_strlen (path);
-  dirname = grub_malloc (len + 1 + 1);
-  if (! dirname)
+  struct grub_fshelp_node root = {
+    .data = data,
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .file_cluster = data->root_cluster,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+#ifdef MODE_EXFAT
+    .is_contiguous = 0,
+#endif
+  };
+
+  err = grub_fshelp_find_file_lookup (path, &root, &found, lookup_file, NULL, GRUB_FSHELP_DIR);
+  if (err)
+    goto fail;
+
+  err = grub_fat_iterate_init (&ctxt);
+  if (err)
     goto fail;
-  grub_memcpy (dirname, path, len);
-  p = dirname + len;
-  if (path[len - 1] != '/')
-    *p++ = '/';
-  *p = '\0';
-  p = dirname;
-
-  do
+
+  while (!(err = grub_fat_iterate_dir_next (found, &ctxt)))
     {
-      p = grub_fat_find_dir (disk, data, p, path, hook, hook_data);
+      struct grub_dirhook_info info;
+      grub_memset (&info, 0, sizeof (info));
+
+      info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY);
+      info.case_insensitive = 1;
+#ifdef MODE_EXFAT
+      if (!ctxt.dir.have_stream)
+       continue;
+#else
+      if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
+       continue;
+#endif
+
+      if (hook (ctxt.filename, &info, hook_data))
+       break;
     }
-  while (p && grub_errno == GRUB_ERR_NONE);
+  grub_fat_iterate_fini (&ctxt);
+  if (err == GRUB_ERR_EOF)
+    err = 0;
 
  fail:
+  if (found != &root)
+    grub_free (found);
 
-  grub_free (dirname);
   grub_free (data);
 
   grub_dl_unref (my_mod);
@@ -1033,35 +988,43 @@ static grub_err_t
 grub_fat_open (grub_file_t file, const char *name)
 {
   struct grub_fat_data *data = 0;
-  char *p = (char *) name;
+  grub_fshelp_node_t found = NULL;
+  grub_err_t err;
+  grub_disk_t disk = file->device->disk;
 
   grub_dl_ref (my_mod);
 
-  data = grub_fat_mount (file->device->disk);
+  data = grub_fat_mount (disk);
   if (! data)
     goto fail;
 
-  do
-    {
-      p = grub_fat_find_dir (file->device->disk, data, p, name, 0, 0);
-      if (grub_errno != GRUB_ERR_NONE)
-       goto fail;
-    }
-  while (p);
+  struct grub_fshelp_node root = {
+    .data = data,
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .file_cluster = data->root_cluster,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+#ifdef MODE_EXFAT
+    .is_contiguous = 0,
+#endif
+  };
 
-  if (data->attr & GRUB_FAT_ATTR_DIRECTORY)
-    {
-      grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a regular file"));
-      goto fail;
-    }
+  err = grub_fshelp_find_file_lookup (name, &root, &found, lookup_file, NULL, GRUB_FSHELP_REG);
+  if (err)
+    goto fail;
 
-  file->data = data;
-  file->size = data->file_size;
+  file->data = found;
+  file->size = found->file_size;
 
   return GRUB_ERR_NONE;
 
  fail:
 
+  if (found != &root)
+    grub_free (found);
+
   grub_free (data);
 
   grub_dl_unref (my_mod);
@@ -1080,7 +1043,10 @@ grub_fat_read (grub_file_t file, char *buf, grub_size_t len)
 static grub_err_t
 grub_fat_close (grub_file_t file)
 {
-  grub_free (file->data);
+  grub_fshelp_node_t node = file->data;
+
+  grub_free (node->data);
+  grub_free (node);
 
   grub_dl_unref (my_mod);
 
@@ -1093,20 +1059,29 @@ grub_fat_label (grub_device_t device, char **label)
 {
   struct grub_fat_dir_entry dir;
   grub_ssize_t offset = -sizeof(dir);
-  struct grub_fat_data *data;
   grub_disk_t disk = device->disk;
+  struct grub_fshelp_node root = {
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+    .is_contiguous = 0,
+  };
 
-  data = grub_fat_mount (disk);
-  if (! data)
+  root.data = grub_fat_mount (disk);
+  if (! root.data)
     return grub_errno;
 
+  root.file_cluster = root.data->root_cluster;
+
   *label = NULL;
 
   while (1)
     {
       offset += sizeof (dir);
 
-      if (grub_fat_read_data (disk, data, 0, 0,
+      if (grub_fat_read_data (disk, &root, 0, 0,
                               offset, sizeof (dir), (char *) &dir)
           != sizeof (dir))
        break;
@@ -1126,7 +1101,7 @@ grub_fat_label (grub_device_t device, char **label)
                                * GRUB_MAX_UTF8_PER_UTF16 + 1);
          if (!*label)
            {
-             grub_free (data);
+             grub_free (root.data);
              return grub_errno;
            }
          chc = dir.type_specific.volume_label.character_count;
@@ -1138,7 +1113,7 @@ grub_fat_label (grub_device_t device, char **label)
        }
     }
 
-  grub_free (data);
+  grub_free (root.data);
   return grub_errno;
 }
 
@@ -1147,30 +1122,32 @@ grub_fat_label (grub_device_t device, char **label)
 static grub_err_t
 grub_fat_label (grub_device_t device, char **label)
 {
-  struct grub_fat_data *data;
   grub_disk_t disk = device->disk;
   grub_err_t err;
   struct grub_fat_iterate_context ctxt;
+  struct grub_fshelp_node root = {
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+  };
 
   *label = 0;
 
   grub_dl_ref (my_mod);
 
-  data = grub_fat_mount (disk);
-  if (! data)
+  root.data = grub_fat_mount (disk);
+  if (! root.data)
     goto fail;
 
-  if (! (data->attr & GRUB_FAT_ATTR_DIRECTORY))
-    {
-      grub_error (GRUB_ERR_BAD_FILE_TYPE, N_("not a directory"));
-      goto fail;
-    }
+  root.file_cluster = root.data->root_cluster;
 
   err = grub_fat_iterate_init (&ctxt);
   if (err)
     goto fail;
 
-  while (!(err = grub_fat_iterate_dir_next (disk, data, &ctxt)))
+  while (!(err = grub_fat_iterate_dir_next (&root, &ctxt)))
     if ((ctxt.dir.attr & ~GRUB_FAT_ATTR_ARCHIVE) == GRUB_FAT_ATTR_VOLUME_ID)
       {
        *label = grub_strdup (ctxt.filename);
@@ -1183,7 +1160,7 @@ grub_fat_label (grub_device_t device, char **label)
 
   grub_dl_unref (my_mod);
 
-  grub_free (data);
+  grub_free (root.data);
 
   return grub_errno;
 }