1 /* minix.c - The minix filesystem, version 1 and 2. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004,2005,2006,2007,2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
21 #include <grub/file.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
26 #include <grub/types.h>
27 #include <grub/i18n.h>
29 GRUB_MOD_LICENSE ("GPLv3+");
32 #define GRUB_MINIX_MAGIC 0x4D5A
33 #elif defined(MODE_MINIX2)
34 #define GRUB_MINIX_MAGIC 0x2468
35 #define GRUB_MINIX_MAGIC_30 0x2478
37 #define GRUB_MINIX_MAGIC 0x137F
38 #define GRUB_MINIX_MAGIC_30 0x138F
41 #define GRUB_MINIX_INODE_DIR_BLOCKS 7
42 #define GRUB_MINIX_LOG2_BSIZE 1
43 #define GRUB_MINIX_ROOT_INODE 1
44 #define GRUB_MINIX_MAX_SYMLNK_CNT 8
45 #define GRUB_MINIX_SBLOCK 2
47 #define GRUB_MINIX_IFDIR 0040000U
48 #define GRUB_MINIX_IFLNK 0120000U
51 #define grub_cpu_to_minix16_compile_time grub_cpu_to_be16_compile_time
52 #define grub_minix_to_cpu16 grub_be_to_cpu16
53 #define grub_minix_to_cpu32 grub_be_to_cpu32
55 #define grub_cpu_to_minix16_compile_time grub_cpu_to_le16_compile_time
56 #define grub_minix_to_cpu16 grub_le_to_cpu16
57 #define grub_minix_to_cpu32 grub_le_to_cpu32
60 #if defined(MODE_MINIX2) || defined(MODE_MINIX3)
61 typedef grub_uint32_t grub_minix_uintn_t
;
62 #define grub_minix_to_cpu_n grub_minix_to_cpu32
64 typedef grub_uint16_t grub_minix_uintn_t
;
65 #define grub_minix_to_cpu_n grub_minix_to_cpu16
69 typedef grub_uint32_t grub_minix_ino_t
;
70 #define grub_minix_to_cpu_ino grub_minix_to_cpu32
72 typedef grub_uint16_t grub_minix_ino_t
;
73 #define grub_minix_to_cpu_ino grub_minix_to_cpu16
76 #define GRUB_MINIX_INODE_SIZE(data) (grub_minix_to_cpu32 (data->inode.size))
77 #define GRUB_MINIX_INODE_MODE(data) (grub_minix_to_cpu16 (data->inode.mode))
78 #define GRUB_MINIX_INODE_DIR_ZONES(data,blk) (grub_minix_to_cpu_n \
79 (data->inode.dir_zones[blk]))
80 #define GRUB_MINIX_INODE_INDIR_ZONE(data) (grub_minix_to_cpu_n \
81 (data->inode.indir_zone))
82 #define GRUB_MINIX_INODE_DINDIR_ZONE(data) (grub_minix_to_cpu_n \
83 (data->inode.double_indir_zone))
87 struct grub_minix_sblock
89 grub_uint32_t inode_cnt
;
90 grub_uint16_t zone_cnt
;
91 grub_uint16_t inode_bmap_size
;
92 grub_uint16_t zone_bmap_size
;
93 grub_uint16_t first_data_zone
;
94 grub_uint16_t log2_zone_size
;
96 grub_uint32_t max_file_size
;
101 grub_uint16_t block_size
;
102 grub_uint8_t disk_version
;
105 struct grub_minix_sblock
107 grub_uint16_t inode_cnt
;
108 grub_uint16_t zone_cnt
;
109 grub_uint16_t inode_bmap_size
;
110 grub_uint16_t zone_bmap_size
;
111 grub_uint16_t first_data_zone
;
112 grub_uint16_t log2_zone_size
;
113 grub_uint32_t max_file_size
;
118 #if defined(MODE_MINIX3) || defined(MODE_MINIX2)
119 struct grub_minix_inode
122 grub_uint16_t nlinks
;
129 grub_uint32_t dir_zones
[7];
130 grub_uint32_t indir_zone
;
131 grub_uint32_t double_indir_zone
;
132 grub_uint32_t triple_indir_zone
;
135 struct grub_minix_inode
143 grub_uint16_t dir_zones
[7];
144 grub_uint16_t indir_zone
;
145 grub_uint16_t double_indir_zone
;
150 #if defined(MODE_MINIX3)
151 #define MAX_MINIX_FILENAME_SIZE 60
153 #define MAX_MINIX_FILENAME_SIZE 30
156 /* Information about a "mounted" minix filesystem. */
157 struct grub_minix_data
159 struct grub_minix_sblock sblock
;
160 struct grub_minix_inode inode
;
161 grub_uint32_t block_per_zone
;
162 grub_minix_ino_t ino
;
166 grub_size_t block_size
;
169 static grub_dl_t my_mod
;
171 static grub_err_t
grub_minix_find_file (struct grub_minix_data
*data
,
175 static inline grub_disk_addr_t
176 grub_minix_zone2sect (struct grub_minix_data
*data
, grub_minix_uintn_t zone
)
178 return ((grub_disk_addr_t
) zone
) * data
->block_size
;
181 static inline grub_disk_addr_t
182 grub_minix_zone2sect (struct grub_minix_data
*data
, grub_minix_uintn_t zone
)
184 int log2_zonesz
= (GRUB_MINIX_LOG2_BSIZE
185 + grub_minix_to_cpu16 (data
->sblock
.log2_zone_size
));
186 return (((grub_disk_addr_t
) zone
) << log2_zonesz
);
191 /* Read the block pointer in ZONE, on the offset NUM. */
192 static grub_minix_uintn_t
193 grub_get_indir (struct grub_minix_data
*data
,
194 grub_minix_uintn_t zone
,
195 grub_minix_uintn_t num
)
197 grub_minix_uintn_t indirn
;
198 grub_disk_read (data
->disk
,
199 grub_minix_zone2sect(data
, zone
),
200 sizeof (grub_minix_uintn_t
) * num
,
201 sizeof (grub_minix_uintn_t
), (char *) &indirn
);
202 return grub_minix_to_cpu_n (indirn
);
205 static grub_minix_uintn_t
206 grub_minix_get_file_block (struct grub_minix_data
*data
, unsigned int blk
)
208 grub_minix_uintn_t indir
;
211 if (blk
< GRUB_MINIX_INODE_DIR_BLOCKS
)
212 return GRUB_MINIX_INODE_DIR_ZONES (data
, blk
);
214 /* Indirect block. */
215 blk
-= GRUB_MINIX_INODE_DIR_BLOCKS
;
216 if (blk
< data
->block_per_zone
)
218 indir
= grub_get_indir (data
, GRUB_MINIX_INODE_INDIR_ZONE (data
), blk
);
222 /* Double indirect block. */
223 blk
-= data
->block_per_zone
;
224 if (blk
< (grub_uint64_t
) data
->block_per_zone
* (grub_uint64_t
) data
->block_per_zone
)
226 indir
= grub_get_indir (data
, GRUB_MINIX_INODE_DINDIR_ZONE (data
),
227 blk
/ data
->block_per_zone
);
229 indir
= grub_get_indir (data
, indir
, blk
% data
->block_per_zone
);
234 #if defined (MODE_MINIX3) || defined (MODE_MINIX2)
235 blk
-= data
->block_per_zone
* data
->block_per_zone
;
236 if (blk
< ((grub_uint64_t
) data
->block_per_zone
* (grub_uint64_t
) data
->block_per_zone
237 * (grub_uint64_t
) data
->block_per_zone
))
239 indir
= grub_get_indir (data
, grub_minix_to_cpu_n (data
->inode
.triple_indir_zone
),
240 (blk
/ data
->block_per_zone
) / data
->block_per_zone
);
241 indir
= grub_get_indir (data
, indir
, (blk
/ data
->block_per_zone
) % data
->block_per_zone
);
242 indir
= grub_get_indir (data
, indir
, blk
% data
->block_per_zone
);
248 /* This should never happen. */
249 grub_error (GRUB_ERR_OUT_OF_RANGE
, "file bigger than maximum size");
255 /* Read LEN bytes from the file described by DATA starting with byte
256 POS. Return the amount of read bytes in READ. */
258 grub_minix_read_file (struct grub_minix_data
*data
,
259 grub_disk_read_hook_t read_hook
, void *read_hook_data
,
260 grub_off_t pos
, grub_size_t len
, char *buf
)
263 grub_uint32_t blockcnt
;
264 grub_uint32_t posblock
;
265 grub_uint32_t blockoff
;
267 if (pos
> GRUB_MINIX_INODE_SIZE (data
))
269 grub_error (GRUB_ERR_OUT_OF_RANGE
,
270 N_("attempt to read past the end of file"));
274 /* Adjust len so it we can't read past the end of the file. */
275 if (len
+ pos
> GRUB_MINIX_INODE_SIZE (data
))
276 len
= GRUB_MINIX_INODE_SIZE (data
) - pos
;
280 /* Files are at most 2G/4G - 1 bytes on minixfs. Avoid 64-bit division. */
281 blockcnt
= ((grub_uint32_t
) ((len
+ pos
- 1)
282 >> GRUB_DISK_SECTOR_BITS
)) / data
->block_size
+ 1;
283 posblock
= (((grub_uint32_t
) pos
)
284 / (data
->block_size
<< GRUB_DISK_SECTOR_BITS
));
285 blockoff
= (((grub_uint32_t
) pos
)
286 % (data
->block_size
<< GRUB_DISK_SECTOR_BITS
));
288 for (i
= posblock
; i
< blockcnt
; i
++)
290 grub_minix_uintn_t blknr
;
291 grub_uint64_t blockend
= data
->block_size
<< GRUB_DISK_SECTOR_BITS
;
292 grub_off_t skipfirst
= 0;
294 blknr
= grub_minix_get_file_block (data
, i
);
299 if (i
== blockcnt
- 1)
301 /* len + pos < 4G (checked above), so it doesn't overflow. */
302 blockend
= (((grub_uint32_t
) (len
+ pos
))
303 % (data
->block_size
<< GRUB_DISK_SECTOR_BITS
));
306 blockend
= data
->block_size
<< GRUB_DISK_SECTOR_BITS
;
312 skipfirst
= blockoff
;
313 blockend
-= skipfirst
;
316 data
->disk
->read_hook
= read_hook
;
317 data
->disk
->read_hook_data
= read_hook_data
;
318 grub_disk_read (data
->disk
,
319 grub_minix_zone2sect(data
, blknr
),
320 skipfirst
, blockend
, buf
);
321 data
->disk
->read_hook
= 0;
325 buf
+= (data
->block_size
<< GRUB_DISK_SECTOR_BITS
) - skipfirst
;
332 /* Read inode INO from the mounted filesystem described by DATA. This
333 inode is used by default now. */
335 grub_minix_read_inode (struct grub_minix_data
*data
, grub_minix_ino_t ino
)
337 struct grub_minix_sblock
*sblock
= &data
->sblock
;
339 /* Block in which the inode is stored. */
340 grub_disk_addr_t block
;
343 /* The first inode in minix is inode 1. */
345 block
= grub_minix_zone2sect (data
,
346 2 + grub_minix_to_cpu16 (sblock
->inode_bmap_size
)
347 + grub_minix_to_cpu16 (sblock
->zone_bmap_size
));
348 block
+= ino
/ (GRUB_DISK_SECTOR_SIZE
/ sizeof (struct grub_minix_inode
));
349 int offs
= (ino
% (GRUB_DISK_SECTOR_SIZE
350 / sizeof (struct grub_minix_inode
))
351 * sizeof (struct grub_minix_inode
));
353 grub_disk_read (data
->disk
, block
, offs
,
354 sizeof (struct grub_minix_inode
), &data
->inode
);
356 return GRUB_ERR_NONE
;
360 /* Lookup the symlink the current inode points to. INO is the inode
361 number of the directory the symlink is relative to. */
363 grub_minix_lookup_symlink (struct grub_minix_data
*data
, grub_minix_ino_t ino
)
366 grub_size_t sz
= GRUB_MINIX_INODE_SIZE (data
);
368 if (++data
->linknest
> GRUB_MINIX_MAX_SYMLNK_CNT
)
369 return grub_error (GRUB_ERR_SYMLINK_LOOP
, N_("too deep nesting of symlinks"));
371 symlink
= grub_malloc (sz
+ 1);
374 if (grub_minix_read_file (data
, 0, 0, 0, sz
, symlink
) < 0)
379 /* The symlink is an absolute path, go back to the root inode. */
380 if (symlink
[0] == '/')
381 ino
= GRUB_MINIX_ROOT_INODE
;
383 /* Now load in the old inode. */
384 if (grub_minix_read_inode (data
, ino
))
387 grub_minix_find_file (data
, symlink
);
393 /* Find the file with the pathname PATH on the filesystem described by
396 grub_minix_find_file (struct grub_minix_data
*data
, const char *path
)
399 const char *next
= path
;
400 unsigned int pos
= 0;
401 grub_minix_ino_t dirino
;
406 /* Skip the first slash. */
410 return GRUB_ERR_NONE
;
412 if ((GRUB_MINIX_INODE_MODE (data
)
413 & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
414 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("not a directory"));
416 /* Extract the actual part from the pathname. */
417 for (next
= name
; *next
&& *next
!= '/'; next
++);
421 grub_minix_ino_t ino
;
422 char filename
[MAX_MINIX_FILENAME_SIZE
+ 1];
424 if (pos
>= GRUB_MINIX_INODE_SIZE (data
))
426 grub_error (GRUB_ERR_FILE_NOT_FOUND
, N_("file `%s' not found"), path
);
430 if (grub_minix_read_file (data
, 0, 0, pos
, sizeof (ino
),
433 if (grub_minix_read_file (data
, 0, 0, pos
+ sizeof (ino
),
434 data
->filename_size
, (char *) filename
)< 0)
437 pos
+= sizeof (ino
) + data
->filename_size
;
439 filename
[data
->filename_size
] = '\0';
441 /* Check if the current direntry matches the current part of the
443 if (grub_strncmp (name
, filename
, next
- name
) == 0
444 && filename
[next
- name
] == '\0')
447 grub_minix_read_inode (data
, grub_minix_to_cpu_ino (ino
));
449 /* Follow the symlink. */
450 if ((GRUB_MINIX_INODE_MODE (data
)
451 & GRUB_MINIX_IFLNK
) == GRUB_MINIX_IFLNK
)
453 grub_minix_lookup_symlink (data
, dirino
);
465 /* Mount the filesystem on the disk DISK. */
466 static struct grub_minix_data
*
467 grub_minix_mount (grub_disk_t disk
)
469 struct grub_minix_data
*data
;
471 data
= grub_malloc (sizeof (struct grub_minix_data
));
475 /* Read the superblock. */
476 grub_disk_read (disk
, GRUB_MINIX_SBLOCK
, 0,
477 sizeof (struct grub_minix_sblock
),&data
->sblock
);
481 if (data
->sblock
.magic
== grub_cpu_to_minix16_compile_time (GRUB_MINIX_MAGIC
))
483 #if !defined(MODE_MINIX3)
484 data
->filename_size
= 14;
486 data
->filename_size
= 60;
489 #if !defined(MODE_MINIX3)
490 else if (data
->sblock
.magic
491 == grub_cpu_to_minix16_compile_time (GRUB_MINIX_MAGIC_30
))
492 data
->filename_size
= 30;
497 /* 20 means 1G zones. We could support up to 31 but already 1G isn't
498 supported by anything else. */
499 if (grub_minix_to_cpu16 (data
->sblock
.log2_zone_size
) >= 20)
505 /* These tests are endian-independent. No need to byteswap. */
506 if (data
->sblock
.block_size
== 0xffff)
507 data
->block_size
= 2;
510 if ((data
->sblock
.block_size
== grub_cpu_to_minix16_compile_time (0x200))
511 || (data
->sblock
.block_size
== 0)
512 || (data
->sblock
.block_size
& grub_cpu_to_minix16_compile_time (0x1ff)))
514 data
->block_size
= grub_minix_to_cpu16 (data
->sblock
.block_size
)
515 >> GRUB_DISK_SECTOR_BITS
;
518 data
->block_size
= 2;
521 data
->block_per_zone
= (((grub_uint64_t
) data
->block_size
<< \
522 (GRUB_DISK_SECTOR_BITS
+ grub_minix_to_cpu16 (data
->sblock
.log2_zone_size
)))
523 / sizeof (grub_minix_uintn_t
));
524 if (!data
->block_per_zone
)
531 #if defined(MODE_MINIX3)
532 grub_error (GRUB_ERR_BAD_FS
, "not a minix3 filesystem");
533 #elif defined(MODE_MINIX2)
534 grub_error (GRUB_ERR_BAD_FS
, "not a minix2 filesystem");
536 grub_error (GRUB_ERR_BAD_FS
, "not a minix filesystem");
542 grub_minix_dir (grub_device_t device
, const char *path
,
543 grub_fs_dir_hook_t hook
, void *hook_data
)
545 struct grub_minix_data
*data
= 0;
546 unsigned int pos
= 0;
548 data
= grub_minix_mount (device
->disk
);
552 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
556 grub_minix_find_file (data
, path
);
560 if ((GRUB_MINIX_INODE_MODE (data
) & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
562 grub_error (GRUB_ERR_BAD_FILE_TYPE
, N_("not a directory"));
566 while (pos
< GRUB_MINIX_INODE_SIZE (data
))
568 grub_minix_ino_t ino
;
569 char filename
[MAX_MINIX_FILENAME_SIZE
+ 1];
570 grub_minix_ino_t dirino
= data
->ino
;
571 struct grub_dirhook_info info
;
572 grub_memset (&info
, 0, sizeof (info
));
575 if (grub_minix_read_file (data
, 0, 0, pos
, sizeof (ino
),
579 if (grub_minix_read_file (data
, 0, 0, pos
+ sizeof (ino
),
581 (char *) filename
) < 0)
583 filename
[data
->filename_size
] = '\0';
586 pos
+= sizeof (ino
) + data
->filename_size
;
590 grub_minix_read_inode (data
, grub_minix_to_cpu_ino (ino
));
591 info
.dir
= ((GRUB_MINIX_INODE_MODE (data
)
592 & GRUB_MINIX_IFDIR
) == GRUB_MINIX_IFDIR
);
594 info
.mtime
= grub_minix_to_cpu32 (data
->inode
.mtime
);
596 if (hook (filename
, &info
, hook_data
) ? 1 : 0)
599 /* Load the old inode back in. */
600 grub_minix_read_inode (data
, dirino
);
602 pos
+= sizeof (ino
) + data
->filename_size
;
611 /* Open a file named NAME and initialize FILE. */
613 grub_minix_open (struct grub_file
*file
, const char *name
)
615 struct grub_minix_data
*data
;
616 data
= grub_minix_mount (file
->device
->disk
);
620 /* Open the inode op the root directory. */
621 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
628 if (!name
|| name
[0] != '/')
630 grub_error (GRUB_ERR_BAD_FILENAME
, N_("invalid file name `%s'"), name
);
634 /* Traverse the directory tree to the node that should be
636 grub_minix_find_file (data
, name
);
644 file
->size
= GRUB_MINIX_INODE_SIZE (data
);
646 return GRUB_ERR_NONE
;
651 grub_minix_read (grub_file_t file
, char *buf
, grub_size_t len
)
653 struct grub_minix_data
*data
=
654 (struct grub_minix_data
*) file
->data
;
656 return grub_minix_read_file (data
, file
->read_hook
, file
->read_hook_data
,
657 file
->offset
, len
, buf
);
662 grub_minix_close (grub_file_t file
)
664 grub_free (file
->data
);
666 return GRUB_ERR_NONE
;
671 static struct grub_fs grub_minix_fs
=
673 #ifdef MODE_BIGENDIAN
674 #if defined(MODE_MINIX3)
676 #elif defined(MODE_MINIX2)
682 #if defined(MODE_MINIX3)
684 #elif defined(MODE_MINIX2)
690 .dir
= grub_minix_dir
,
691 .open
= grub_minix_open
,
692 .read
= grub_minix_read
,
693 .close
= grub_minix_close
,
695 .reserved_first_sector
= 1,
696 .blocklist_install
= 1,
701 #ifdef MODE_BIGENDIAN
702 #if defined(MODE_MINIX3)
703 GRUB_MOD_INIT(minix3_be
)
704 #elif defined(MODE_MINIX2)
705 GRUB_MOD_INIT(minix2_be
)
707 GRUB_MOD_INIT(minix_be
)
710 #if defined(MODE_MINIX3)
711 GRUB_MOD_INIT(minix3
)
712 #elif defined(MODE_MINIX2)
713 GRUB_MOD_INIT(minix2
)
719 grub_fs_register (&grub_minix_fs
);
723 #ifdef MODE_BIGENDIAN
724 #if defined(MODE_MINIX3)
725 GRUB_MOD_FINI(minix3_be
)
726 #elif defined(MODE_MINIX2)
727 GRUB_MOD_FINI(minix2_be
)
729 GRUB_MOD_FINI(minix_be
)
732 #if defined(MODE_MINIX3)
733 GRUB_MOD_FINI(minix3
)
734 #elif defined(MODE_MINIX2)
735 GRUB_MOD_FINI(minix2
)
741 grub_fs_unregister (&grub_minix_fs
);