]> git.proxmox.com Git - grub2.git/blobdiff - grub-core/fs/minix.c
Minix FS fixes.
[grub2.git] / grub-core / fs / minix.c
index a856e38c4bc436d5529243adbb2f93f02a6c09b7..1db2883cde0481803f9d35f02175d5940ffefecb 100644 (file)
 #include <grub/dl.h>
 #include <grub/types.h>
 
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#ifdef MODE_MINIX3
+#define GRUB_MINIX_MAGIC       0x4D5A
+#elif defined(MODE_MINIX2)
+#define GRUB_MINIX_MAGIC       0x2468
+#define GRUB_MINIX_MAGIC_30    0x2478
+#else
 #define GRUB_MINIX_MAGIC       0x137F
-#define GRUB_MINIX2_MAGIC      0x2468
 #define GRUB_MINIX_MAGIC_30    0x138F
-#define GRUB_MINIX2_MAGIC_30   0x2478
-#define GRUB_MINIX_BSIZE       1024U
+#endif
+
+#define GRUB_MINIX_INODE_DIR_BLOCKS    7
 #define GRUB_MINIX_LOG2_BSIZE  1
 #define GRUB_MINIX_ROOT_INODE  1
 #define GRUB_MINIX_MAX_SYMLNK_CNT      8
 #define GRUB_MINIX_IFDIR       0040000U
 #define GRUB_MINIX_IFLNK       0120000U
 
-#define GRUB_MINIX_INODE(data,field) (data->version == 1 ? \
-                           data->inode.  field : data->inode2.  field)
-#define GRUB_MINIX_INODE_ENDIAN(data,field,bits1,bits2) (data->version == 1 ?  \
-                        grub_le_to_cpu##bits1 (data->inode.field) :            \
-                        grub_le_to_cpu##bits2 (data->inode2.field))
-#define GRUB_MINIX_INODE_SIZE(data) GRUB_MINIX_INODE_ENDIAN (data,size,16,32)
-#define GRUB_MINIX_INODE_MODE(data) GRUB_MINIX_INODE_ENDIAN (data,mode,16,16)
-#define GRUB_MINIX_INODE_DIR_ZONES(data,blk) GRUB_MINIX_INODE_ENDIAN           \
-                                               (data,dir_zones[blk],16,32)
-#define GRUB_MINIX_INODE_INDIR_ZONE(data)                              \
-                        GRUB_MINIX_INODE_ENDIAN (data,indir_zone,16,32)
-#define GRUB_MINIX_INODE_DINDIR_ZONE(data)                                     \
-                        GRUB_MINIX_INODE_ENDIAN (data,double_indir_zone,16,32)
-#define GRUB_MINIX_INODE_BLKSZ(data) (data->version == 1 ? 2 : 4)
+#if defined(MODE_MINIX2) || defined(MODE_MINIX3)
+typedef grub_uint32_t grub_minix_uintn_t;
+#define grub_minix_le_to_cpu_n grub_le_to_cpu32
+#else
+typedef grub_uint16_t grub_minix_uintn_t;
+#define grub_minix_le_to_cpu_n grub_le_to_cpu16
+#endif
+
+#define GRUB_MINIX_INODE_BLKSZ(data) sizeof (grub_minix_uintn_t)
+#ifdef MODE_MINIX3
+typedef grub_uint32_t grub_minix_ino_t;
+#define grub_minix_le_to_cpu_ino grub_le_to_cpu32
+#else
+typedef grub_uint16_t grub_minix_ino_t;
+#define grub_minix_le_to_cpu_ino grub_le_to_cpu16
+#endif
+
+#define GRUB_MINIX_INODE_SIZE(data) (grub_le_to_cpu32 (data->inode.size))
+#define GRUB_MINIX_INODE_MODE(data) (grub_le_to_cpu16 (data->inode.mode))
+#define GRUB_MINIX_INODE_DIR_ZONES(data,blk) (grub_minix_le_to_cpu_n   \
+                                             (data->inode.dir_zones[blk]))
+#define GRUB_MINIX_INODE_INDIR_ZONE(data)  (grub_minix_le_to_cpu_n \
+                                           (data->inode.indir_zone))
+#define GRUB_MINIX_INODE_DINDIR_ZONE(data) (grub_minix_le_to_cpu_n \
+                                           (data->inode.double_indir_zone))
+
+#ifndef MODE_MINIX3
 #define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE                          \
-                                + grub_le_to_cpu16 (sblock->log2_zone_size))
-#define GRUB_MINIX_ZONESZ      (GRUB_MINIX_BSIZE                               \
-                                << grub_le_to_cpu16 (sblock->log2_zone_size))
+                                + grub_le_to_cpu16 (data->sblock.log2_zone_size))
+#endif
+#define GRUB_MINIX_ZONESZ      (data->block_size                               \
+                                << grub_le_to_cpu16 (data->sblock.log2_zone_size))
+
+#ifdef MODE_MINIX3
+#define GRUB_MINIX_ZONE2SECT(zone) ((zone) * (data->block_size / GRUB_DISK_SECTOR_SIZE))
+#else
+#define GRUB_MINIX_ZONE2SECT(zone) ((zone) << GRUB_MINIX_LOG2_ZONESZ)
+#endif
+
 
+#ifdef MODE_MINIX3
 struct grub_minix_sblock
 {
-  grub_uint16_t inode_cnt;
+  grub_uint32_t inode_cnt;
   grub_uint16_t zone_cnt;
   grub_uint16_t inode_bmap_size;
   grub_uint16_t zone_bmap_size;
   grub_uint16_t first_data_zone;
   grub_uint16_t log2_zone_size;
+  grub_uint16_t pad;
   grub_uint32_t max_file_size;
+  grub_uint32_t zones;
   grub_uint16_t magic;
+  
+  grub_uint16_t pad2;
+  grub_uint16_t block_size;
+  grub_uint8_t disk_version; 
 };
-
-struct grub_minix_inode
+#else
+struct grub_minix_sblock
 {
-  grub_uint16_t mode;
-  grub_uint16_t uid;
-  grub_uint16_t size;
-  grub_uint32_t ctime;
-  grub_uint8_t gid;
-  grub_uint8_t nlinks;
-  grub_uint16_t dir_zones[7];
-  grub_uint16_t indir_zone;
-  grub_uint16_t double_indir_zone;
+  grub_uint16_t inode_cnt;
+  grub_uint16_t zone_cnt;
+  grub_uint16_t inode_bmap_size;
+  grub_uint16_t zone_bmap_size;
+  grub_uint16_t first_data_zone;
+  grub_uint16_t log2_zone_size;
+  grub_uint32_t max_file_size;
+  grub_uint16_t magic;
 };
+#endif
 
-struct grub_minix2_inode
+#if defined(MODE_MINIX3) || defined(MODE_MINIX2)
+struct grub_minix_inode
 {
   grub_uint16_t mode;
   grub_uint16_t nlinks;
@@ -96,20 +133,33 @@ struct grub_minix2_inode
   grub_uint32_t indir_zone;
   grub_uint32_t double_indir_zone;
   grub_uint32_t unused;
-
 };
+#else
+struct grub_minix_inode
+{
+  grub_uint16_t mode;
+  grub_uint16_t uid;
+  grub_uint32_t size;
+  grub_uint32_t mtime;
+  grub_uint8_t gid;
+  grub_uint8_t nlinks;
+  grub_uint16_t dir_zones[7];
+  grub_uint16_t indir_zone;
+  grub_uint16_t double_indir_zone;
+};
+
+#endif
 
 /* Information about a "mounted" minix filesystem.  */
 struct grub_minix_data
 {
   struct grub_minix_sblock sblock;
   struct grub_minix_inode inode;
-  struct grub_minix2_inode inode2;
   int ino;
   int linknest;
   grub_disk_t disk;
-  int version;
   int filename_size;
+  grub_size_t block_size;
 };
 
 static grub_dl_t my_mod;
@@ -117,58 +167,48 @@ static grub_dl_t my_mod;
 static grub_err_t grub_minix_find_file (struct grub_minix_data *data,
                                        const char *path);
 
-static int
+static grub_minix_uintn_t
 grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk)
 {
-  struct grub_minix_sblock *sblock = &data->sblock;
-  int indir;
+  grub_minix_uintn_t indir;
+  const grub_uint32_t block_per_zone = (GRUB_MINIX_ZONESZ
+                                       / GRUB_MINIX_INODE_BLKSZ (data));
 
-  auto int grub_get_indir (int, int);
+  auto grub_minix_uintn_t grub_get_indir (grub_minix_uintn_t,
+                                         grub_minix_uintn_t);
 
   /* Read the block pointer in ZONE, on the offset NUM.  */
-  int grub_get_indir (int zone, int num)
+  grub_minix_uintn_t grub_get_indir (grub_minix_uintn_t zone,
+                                    grub_minix_uintn_t num)
     {
-      if (data->version == 1)
-       {
-         grub_uint16_t indir16;
-         grub_disk_read (data->disk,
-                         zone << GRUB_MINIX_LOG2_ZONESZ,
-                         sizeof (grub_uint16_t) * num,
-                         sizeof (grub_uint16_t), (char *) &indir16);
-         return grub_le_to_cpu16 (indir16);
-       }
-      else
-       {
-         grub_uint32_t indir32;
-         grub_disk_read (data->disk,
-                         zone << GRUB_MINIX_LOG2_ZONESZ,
-                         sizeof (grub_uint32_t) * num,
-                         sizeof (grub_uint32_t), (char *) &indir32);
-         return grub_le_to_cpu32 (indir32);
-       }
+      grub_minix_uintn_t indirn;
+      grub_disk_read (data->disk,
+                     GRUB_MINIX_ZONE2SECT(zone),
+                     sizeof (grub_minix_uintn_t) * num,
+                     sizeof (grub_minix_uintn_t), (char *) &indirn);
+      return grub_minix_le_to_cpu_n (indirn);
     }
 
   /* Direct block.  */
-  if (blk < 7)
+  if (blk < GRUB_MINIX_INODE_DIR_BLOCKS)
     return GRUB_MINIX_INODE_DIR_ZONES (data, blk);
 
   /* Indirect block.  */
-  blk -= 7;
-  if (blk < GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
+  blk -= GRUB_MINIX_INODE_DIR_BLOCKS;
+  if (blk < block_per_zone)
     {
       indir = grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data), blk);
       return indir;
     }
 
   /* Double indirect block.  */
-  blk -= GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data);
-  if (blk < (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
-      * (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data)))
+  blk -= block_per_zone;
+  if (blk < block_per_zone * block_per_zone)
     {
       indir = grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data),
-                             blk / GRUB_MINIX_ZONESZ);
+                             blk / block_per_zone);
 
-      indir = grub_get_indir (indir, blk % GRUB_MINIX_ZONESZ);
+      indir = grub_get_indir (indir, blk % block_per_zone);
 
       return indir;
     }
@@ -186,25 +226,26 @@ static grub_ssize_t
 grub_minix_read_file (struct grub_minix_data *data,
                      void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
                                         unsigned offset, unsigned length),
-                     int pos, grub_disk_addr_t len, char *buf)
+                     grub_off_t pos, grub_disk_addr_t len, char *buf)
 {
-  struct grub_minix_sblock *sblock = &data->sblock;
-  int i;
-  int blockcnt;
+  grub_disk_addr_t i;
+  grub_disk_addr_t blockcnt;
+  grub_uint64_t posblock;
+  grub_uint64_t blockoff;
 
   /* Adjust len so it we can't read past the end of the file.  */
   if (len + pos > GRUB_MINIX_INODE_SIZE (data))
     len = GRUB_MINIX_INODE_SIZE (data) - pos;
 
-  blockcnt = (len + pos + GRUB_MINIX_BSIZE - 1) / GRUB_MINIX_BSIZE;
+  blockcnt = grub_divmod64 ((len + pos + data->block_size - 1),
+                           data->block_size, 0);
+  posblock = grub_divmod64 (pos, data->block_size, &blockoff);
 
-  for (i = pos / GRUB_MINIX_BSIZE; i < blockcnt; i++)
+  for (i = posblock; i < blockcnt; i++)
     {
-      int blknr;
-      int blockoff = pos % GRUB_MINIX_BSIZE;
-      int blockend = GRUB_MINIX_BSIZE;
-
-      int skipfirst = 0;
+      grub_disk_addr_t blknr;
+      grub_uint64_t blockend = data->block_size;
+      grub_off_t skipfirst = 0;
 
       blknr = grub_minix_get_file_block (data, i);
       if (grub_errno)
@@ -213,28 +254,28 @@ grub_minix_read_file (struct grub_minix_data *data,
       /* Last block.  */
       if (i == blockcnt - 1)
        {
-         blockend = (len + pos) % GRUB_MINIX_BSIZE;
+         grub_divmod64 (len + pos, data->block_size, &blockend);
 
          if (!blockend)
-           blockend = GRUB_MINIX_BSIZE;
+           blockend = data->block_size;
        }
 
       /* First block.  */
-      if (i == (pos / (int) GRUB_MINIX_BSIZE))
+      if (i == posblock)
        {
          skipfirst = blockoff;
          blockend -= skipfirst;
        }
 
       data->disk->read_hook = read_hook;
-      grub_disk_read (data->disk, blknr << GRUB_MINIX_LOG2_ZONESZ,
+      grub_disk_read (data->disk,
+                     GRUB_MINIX_ZONE2SECT(blknr),
                      skipfirst, blockend, buf);
-
       data->disk->read_hook = 0;
       if (grub_errno)
        return -1;
 
-      buf += GRUB_MINIX_BSIZE - skipfirst;
+      buf += data->block_size - skipfirst;
     }
 
   return len;
@@ -249,37 +290,20 @@ grub_minix_read_inode (struct grub_minix_data *data, int ino)
   struct grub_minix_sblock *sblock = &data->sblock;
 
   /* Block in which the inode is stored.  */
-  int block;
+  grub_disk_addr_t block;
   data->ino = ino;
 
   /* The first inode in minix is inode 1.  */
   ino--;
-
-  block = ((2 + grub_le_to_cpu16 (sblock->inode_bmap_size)
-           + grub_le_to_cpu16 (sblock->zone_bmap_size))
-          << GRUB_MINIX_LOG2_BSIZE);
-
-  if (data->version == 1)
-    {
-      block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode));
-      int offs = (ino % (GRUB_DISK_SECTOR_SIZE
-                        / sizeof (struct grub_minix_inode))
-                 * sizeof (struct grub_minix_inode));
-
-      grub_disk_read (data->disk, block, offs,
-                     sizeof (struct grub_minix_inode), &data->inode);
-    }
-  else
-    {
-      block += ino / (GRUB_DISK_SECTOR_SIZE
-                     / sizeof (struct grub_minix2_inode));
-      int offs = (ino
-                 % (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix2_inode))
-                 * sizeof (struct grub_minix2_inode));
-
-      grub_disk_read (data->disk, block, offs,
-                     sizeof (struct grub_minix2_inode),&data->inode2);
-    }
+  block = GRUB_MINIX_ZONE2SECT (2 + grub_le_to_cpu16 (sblock->inode_bmap_size)
+                               + grub_le_to_cpu16 (sblock->zone_bmap_size));
+  block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode));
+  int offs = (ino % (GRUB_DISK_SECTOR_SIZE
+                    / sizeof (struct grub_minix_inode))
+             * sizeof (struct grub_minix_inode));
+  
+  grub_disk_read (data->disk, block, offs,
+                 sizeof (struct grub_minix_inode), &data->inode);
 
   return GRUB_ERR_NONE;
 }
@@ -348,7 +372,7 @@ grub_minix_find_file (struct grub_minix_data *data, const char *path)
 
   do
     {
-      grub_uint16_t ino;
+      grub_minix_ino_t ino;
       char filename[data->filename_size + 1];
 
       if (grub_strlen (name) == 0)
@@ -368,7 +392,7 @@ grub_minix_find_file (struct grub_minix_data *data, const char *path)
       if (!grub_strcmp (name, filename))
        {
          dirino = data->ino;
-         grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
+         grub_minix_read_inode (data, grub_minix_le_to_cpu_ino (ino));
 
          /* Follow the symlink.  */
          if ((GRUB_MINIX_INODE_MODE (data)
@@ -402,7 +426,7 @@ grub_minix_find_file (struct grub_minix_data *data, const char *path)
       pos += sizeof (ino) + data->filename_size;
     } while (pos < GRUB_MINIX_INODE_SIZE (data));
 
-  grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
+  grub_error (GRUB_ERR_FILE_NOT_FOUND, "file `%s' not found", path);
   return grub_errno;
 }
 
@@ -425,35 +449,38 @@ grub_minix_mount (grub_disk_t disk)
 
   if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC)
     {
-      data->version = 1;
-      data->filename_size = 14;
-    }
-  else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC)
-    {
-      data->version = 2;
-      data->filename_size = 14;
+#if !defined(MODE_MINIX3)
+    data->filename_size = 14;
+#else
+    data->filename_size = 60;
+#endif
     }
+#if !defined(MODE_MINIX3)
   else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC_30)
-    {
-      data->version = 1;
-      data->filename_size = 30;
-    }
-  else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC_30)
-    {
-      data->version = 2;
-      data->filename_size = 30;
-    }
+    data->filename_size = 30;
+#endif
   else
     goto fail;
 
   data->disk = disk;
   data->linknest = 0;
+#ifdef MODE_MINIX3
+  data->block_size = grub_le_to_cpu16 (data->sblock.block_size);
+#else
+  data->block_size = 1024U;
+#endif
 
   return data;
 
  fail:
   grub_free (data);
+#if defined(MODE_MINIX3)
+  grub_error (GRUB_ERR_BAD_FS, "not a minix3 filesystem");
+#elif defined(MODE_MINIX2)
+  grub_error (GRUB_ERR_BAD_FS, "not a minix2 filesystem");
+#else
   grub_error (GRUB_ERR_BAD_FS, "not a minix filesystem");
+#endif
   return 0;
 }
 \f
@@ -463,7 +490,6 @@ grub_minix_dir (grub_device_t device, const char *path,
                               const struct grub_dirhook_info *info))
 {
   struct grub_minix_data *data = 0;
-  struct grub_minix_sblock *sblock;
   unsigned int pos = 0;
 
   data = grub_minix_mount (device->disk);
@@ -474,8 +500,6 @@ grub_minix_dir (grub_device_t device, const char *path,
   if (grub_errno)
     goto fail;
 
-  sblock = &data->sblock;
-
   grub_minix_find_file (data, path);
   if (grub_errno)
     goto fail;
@@ -488,7 +512,7 @@ grub_minix_dir (grub_device_t device, const char *path,
 
   while (pos < GRUB_MINIX_INODE_SIZE (data))
     {
-      grub_uint16_t ino;
+      grub_minix_ino_t ino;
       char filename[data->filename_size + 1];
       int dirino = data->ino;
       struct grub_dirhook_info info;
@@ -504,12 +528,18 @@ grub_minix_dir (grub_device_t device, const char *path,
                                (char *) filename) < 0)
        return grub_errno;
       filename[data->filename_size] = '\0';
+      if (!ino)
+       {
+         pos += sizeof (ino) + data->filename_size;
+         continue;
+       }
 
-      /* The filetype is not stored in the dirent.  Read the inode to
-        find out the filetype.  This *REALLY* sucks.  */
-      grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
+      grub_minix_read_inode (data, grub_minix_le_to_cpu_ino (ino));
       info.dir = ((GRUB_MINIX_INODE_MODE (data)
                   & GRUB_MINIX_IFDIR) == GRUB_MINIX_IFDIR);
+      info.mtimeset = 1;
+      info.mtime = grub_le_to_cpu32 (data->inode.mtime);
+
       if (hook (filename, &info) ? 1 : 0)
        break;
 
@@ -583,32 +613,42 @@ grub_minix_close (grub_file_t file)
 }
 
 
-static grub_err_t
-grub_minix_label (grub_device_t device __attribute ((unused)),
-               char **label __attribute ((unused)))
-{
-  return GRUB_ERR_NONE;
-}
-
 \f
 static struct grub_fs grub_minix_fs =
   {
+#if defined(MODE_MINIX3)
+    .name = "minix3",
+#elif defined(MODE_MINIX2)
+    .name = "minix2",
+#else
     .name = "minix",
+#endif
     .dir = grub_minix_dir,
     .open = grub_minix_open,
     .read = grub_minix_read,
     .close = grub_minix_close,
-    .label = grub_minix_label,
     .next = 0
   };
 
+#if defined(MODE_MINIX3)
+GRUB_MOD_INIT(minix3)
+#elif defined(MODE_MINIX2)
+GRUB_MOD_INIT(minix2)
+#else
 GRUB_MOD_INIT(minix)
+#endif
 {
   grub_fs_register (&grub_minix_fs);
   my_mod = mod;
 }
 
+#if defined(MODE_MINIX3)
+GRUB_MOD_FINI(minix3)
+#elif defined(MODE_MINIX2)
+GRUB_MOD_FINI(minix2)
+#else
 GRUB_MOD_FINI(minix)
+#endif
 {
   grub_fs_unregister (&grub_minix_fs);
 }