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>
28 GRUB_MOD_LICENSE ("GPLv3+");
31 #define GRUB_MINIX_MAGIC 0x4D5A
32 #elif defined(MODE_MINIX2)
33 #define GRUB_MINIX_MAGIC 0x2468
34 #define GRUB_MINIX_MAGIC_30 0x2478
36 #define GRUB_MINIX_MAGIC 0x137F
37 #define GRUB_MINIX_MAGIC_30 0x138F
40 #define GRUB_MINIX_INODE_DIR_BLOCKS 7
41 #define GRUB_MINIX_LOG2_BSIZE 1
42 #define GRUB_MINIX_ROOT_INODE 1
43 #define GRUB_MINIX_MAX_SYMLNK_CNT 8
44 #define GRUB_MINIX_SBLOCK 2
46 #define GRUB_MINIX_IFDIR 0040000U
47 #define GRUB_MINIX_IFLNK 0120000U
49 #if defined(MODE_MINIX2) || defined(MODE_MINIX3)
50 typedef grub_uint32_t grub_minix_uintn_t
;
51 #define grub_minix_le_to_cpu_n grub_le_to_cpu32
53 typedef grub_uint16_t grub_minix_uintn_t
;
54 #define grub_minix_le_to_cpu_n grub_le_to_cpu16
57 #define GRUB_MINIX_INODE_BLKSZ(data) sizeof (grub_minix_uintn_t)
59 typedef grub_uint32_t grub_minix_ino_t
;
60 #define grub_minix_le_to_cpu_ino grub_le_to_cpu32
62 typedef grub_uint16_t grub_minix_ino_t
;
63 #define grub_minix_le_to_cpu_ino grub_le_to_cpu16
66 #define GRUB_MINIX_INODE_SIZE(data) (grub_minix_le_to_cpu_n (data->inode.size))
67 #define GRUB_MINIX_INODE_MODE(data) (grub_le_to_cpu16 (data->inode.mode))
68 #define GRUB_MINIX_INODE_DIR_ZONES(data,blk) (grub_minix_le_to_cpu_n \
69 (data->inode.dir_zones[blk]))
70 #define GRUB_MINIX_INODE_INDIR_ZONE(data) (grub_minix_le_to_cpu_n \
71 (data->inode.indir_zone))
72 #define GRUB_MINIX_INODE_DINDIR_ZONE(data) (grub_minix_le_to_cpu_n \
73 (data->inode.double_indir_zone))
76 #define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE \
77 + grub_le_to_cpu16 (data->sblock.log2_zone_size))
79 #define GRUB_MINIX_ZONESZ (data->block_size \
80 << grub_le_to_cpu16 (data->sblock.log2_zone_size))
83 #define GRUB_MINIX_ZONE2SECT(zone) ((zone) * (data->block_size / GRUB_DISK_SECTOR_SIZE))
85 #define GRUB_MINIX_ZONE2SECT(zone) ((zone) << GRUB_MINIX_LOG2_ZONESZ)
90 struct grub_minix_sblock
92 grub_uint32_t inode_cnt
;
93 grub_uint16_t zone_cnt
;
94 grub_uint16_t inode_bmap_size
;
95 grub_uint16_t zone_bmap_size
;
96 grub_uint16_t first_data_zone
;
97 grub_uint16_t log2_zone_size
;
99 grub_uint32_t max_file_size
;
104 grub_uint16_t block_size
;
105 grub_uint8_t disk_version
;
108 struct grub_minix_sblock
110 grub_uint16_t inode_cnt
;
111 grub_uint16_t zone_cnt
;
112 grub_uint16_t inode_bmap_size
;
113 grub_uint16_t zone_bmap_size
;
114 grub_uint16_t first_data_zone
;
115 grub_uint16_t log2_zone_size
;
116 grub_uint32_t max_file_size
;
121 #if defined(MODE_MINIX3) || defined(MODE_MINIX2)
122 struct grub_minix_inode
125 grub_uint16_t nlinks
;
132 grub_uint32_t dir_zones
[7];
133 grub_uint32_t indir_zone
;
134 grub_uint32_t double_indir_zone
;
135 grub_uint32_t unused
;
139 struct grub_minix_inode
147 grub_uint16_t dir_zones
[7];
148 grub_uint16_t indir_zone
;
149 grub_uint16_t double_indir_zone
;
154 /* Information about a "mounted" minix filesystem. */
155 struct grub_minix_data
157 struct grub_minix_sblock sblock
;
158 struct grub_minix_inode inode
;
163 grub_size_t block_size
;
166 static grub_dl_t my_mod
;
168 static grub_err_t
grub_minix_find_file (struct grub_minix_data
*data
,
172 grub_minix_get_file_block (struct grub_minix_data
*data
, unsigned int blk
)
176 auto int grub_get_indir (int, int);
178 /* Read the block pointer in ZONE, on the offset NUM. */
179 int grub_get_indir (int zone
, int num
)
181 grub_minix_uintn_t indirn
;
182 grub_disk_read (data
->disk
,
183 GRUB_MINIX_ZONE2SECT(zone
),
184 sizeof (grub_minix_uintn_t
) * num
,
185 sizeof (grub_minix_uintn_t
), (char *) &indirn
);
186 return grub_minix_le_to_cpu_n (indirn
);
190 if (blk
< GRUB_MINIX_INODE_DIR_BLOCKS
)
191 return GRUB_MINIX_INODE_DIR_ZONES (data
, blk
);
193 /* Indirect block. */
194 blk
-= GRUB_MINIX_INODE_DIR_BLOCKS
;
195 if (blk
< GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
))
197 indir
= grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data
), blk
);
201 /* Double indirect block. */
202 blk
-= GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
);
203 if (blk
< (GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
))
204 * (GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
)))
206 indir
= grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data
),
207 blk
/ GRUB_MINIX_ZONESZ
);
209 indir
= grub_get_indir (indir
, blk
% GRUB_MINIX_ZONESZ
);
214 /* This should never happen. */
215 grub_error (GRUB_ERR_OUT_OF_RANGE
, "file bigger than maximum size");
221 /* Read LEN bytes from the file described by DATA starting with byte
222 POS. Return the amount of read bytes in READ. */
224 grub_minix_read_file (struct grub_minix_data
*data
,
225 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
226 unsigned offset
, unsigned length
),
227 grub_off_t pos
, grub_disk_addr_t len
, char *buf
)
230 grub_disk_addr_t blockcnt
;
231 grub_uint64_t posblock
;
232 grub_uint32_t blockoff
;
234 /* Adjust len so it we can't read past the end of the file. */
235 if (len
+ pos
> GRUB_MINIX_INODE_SIZE (data
))
236 len
= GRUB_MINIX_INODE_SIZE (data
) - pos
;
238 blockcnt
= grub_divmod64 ((len
+ pos
+ data
->block_size
- 1),
239 data
->block_size
, 0);
240 posblock
= grub_divmod64 (pos
, data
->block_size
, &blockoff
);
242 for (i
= posblock
; i
< blockcnt
; i
++)
244 grub_disk_addr_t blknr
;
245 grub_uint32_t blockend
= data
->block_size
;
246 grub_off_t skipfirst
= 0;
248 blknr
= grub_minix_get_file_block (data
, i
);
253 if (i
== blockcnt
- 1)
255 grub_divmod64 (len
+ pos
, data
->block_size
, &blockend
);
258 blockend
= data
->block_size
;
264 skipfirst
= blockoff
;
265 blockend
-= skipfirst
;
268 data
->disk
->read_hook
= read_hook
;
269 grub_disk_read (data
->disk
,
270 GRUB_MINIX_ZONE2SECT(blknr
),
271 skipfirst
, blockend
, buf
);
272 data
->disk
->read_hook
= 0;
276 buf
+= data
->block_size
- skipfirst
;
283 /* Read inode INO from the mounted filesystem described by DATA. This
284 inode is used by default now. */
286 grub_minix_read_inode (struct grub_minix_data
*data
, int ino
)
288 struct grub_minix_sblock
*sblock
= &data
->sblock
;
290 /* Block in which the inode is stored. */
291 grub_disk_addr_t block
;
294 /* The first inode in minix is inode 1. */
296 block
= GRUB_MINIX_ZONE2SECT (2 + grub_le_to_cpu16 (sblock
->inode_bmap_size
)
297 + grub_le_to_cpu16 (sblock
->zone_bmap_size
));
298 block
+= ino
/ (GRUB_DISK_SECTOR_SIZE
/ sizeof (struct grub_minix_inode
));
299 int offs
= (ino
% (GRUB_DISK_SECTOR_SIZE
300 / sizeof (struct grub_minix_inode
))
301 * sizeof (struct grub_minix_inode
));
303 grub_disk_read (data
->disk
, block
, offs
,
304 sizeof (struct grub_minix_inode
), &data
->inode
);
306 return GRUB_ERR_NONE
;
310 /* Lookup the symlink the current inode points to. INO is the inode
311 number of the directory the symlink is relative to. */
313 grub_minix_lookup_symlink (struct grub_minix_data
*data
, int ino
)
315 char symlink
[GRUB_MINIX_INODE_SIZE (data
) + 1];
317 if (++data
->linknest
> GRUB_MINIX_MAX_SYMLNK_CNT
)
318 return grub_error (GRUB_ERR_SYMLINK_LOOP
, "too deep nesting of symlinks");
320 if (grub_minix_read_file (data
, 0, 0,
321 GRUB_MINIX_INODE_SIZE (data
), symlink
) < 0)
324 symlink
[GRUB_MINIX_INODE_SIZE (data
)] = '\0';
326 /* The symlink is an absolute path, go back to the root inode. */
327 if (symlink
[0] == '/')
328 ino
= GRUB_MINIX_ROOT_INODE
;
330 /* Now load in the old inode. */
331 if (grub_minix_read_inode (data
, ino
))
334 grub_minix_find_file (data
, symlink
);
336 grub_error (grub_errno
, "cannot follow symlink `%s'", symlink
);
342 /* Find the file with the pathname PATH on the filesystem described by
345 grub_minix_find_file (struct grub_minix_data
*data
, const char *path
)
347 char fpath
[grub_strlen (path
) + 1];
350 unsigned int pos
= 0;
353 grub_strcpy (fpath
, path
);
355 /* Skip the first slash. */
363 /* Extract the actual part from the pathname. */
364 next
= grub_strchr (name
, '/');
373 grub_minix_ino_t ino
;
374 char filename
[data
->filename_size
+ 1];
376 if (grub_strlen (name
) == 0)
377 return GRUB_ERR_NONE
;
379 if (grub_minix_read_file (data
, 0, pos
, sizeof (ino
),
382 if (grub_minix_read_file (data
, 0, pos
+ sizeof (ino
),
383 data
->filename_size
, (char *) filename
)< 0)
386 filename
[data
->filename_size
] = '\0';
388 /* Check if the current direntry matches the current part of the
390 if (!grub_strcmp (name
, filename
))
393 grub_minix_read_inode (data
, grub_minix_le_to_cpu_ino (ino
));
395 /* Follow the symlink. */
396 if ((GRUB_MINIX_INODE_MODE (data
)
397 & GRUB_MINIX_IFLNK
) == GRUB_MINIX_IFLNK
)
399 grub_minix_lookup_symlink (data
, dirino
);
410 next
= grub_strchr (name
, '/');
417 if ((GRUB_MINIX_INODE_MODE (data
)
418 & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
419 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
424 pos
+= sizeof (ino
) + data
->filename_size
;
425 } while (pos
< GRUB_MINIX_INODE_SIZE (data
));
427 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
432 /* Mount the filesystem on the disk DISK. */
433 static struct grub_minix_data
*
434 grub_minix_mount (grub_disk_t disk
)
436 struct grub_minix_data
*data
;
438 data
= grub_malloc (sizeof (struct grub_minix_data
));
442 /* Read the superblock. */
443 grub_disk_read (disk
, GRUB_MINIX_SBLOCK
, 0,
444 sizeof (struct grub_minix_sblock
),&data
->sblock
);
448 if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX_MAGIC
)
450 #if !defined(MODE_MINIX3)
451 data
->filename_size
= 14;
453 data
->filename_size
= 60;
456 #if !defined(MODE_MINIX3)
457 else if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX_MAGIC_30
)
458 data
->filename_size
= 30;
466 data
->block_size
= grub_le_to_cpu16 (data
->sblock
.block_size
);
468 data
->block_size
= 1024U;
475 #if defined(MODE_MINIX3)
476 grub_error (GRUB_ERR_BAD_FS
, "not a minix3 filesystem");
477 #elif defined(MODE_MINIX2)
478 grub_error (GRUB_ERR_BAD_FS
, "not a minix2 filesystem");
480 grub_error (GRUB_ERR_BAD_FS
, "not a minix filesystem");
486 grub_minix_dir (grub_device_t device
, const char *path
,
487 int (*hook
) (const char *filename
,
488 const struct grub_dirhook_info
*info
))
490 struct grub_minix_data
*data
= 0;
491 unsigned int pos
= 0;
493 data
= grub_minix_mount (device
->disk
);
497 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
501 grub_minix_find_file (data
, path
);
505 if ((GRUB_MINIX_INODE_MODE (data
) & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
507 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
511 while (pos
< GRUB_MINIX_INODE_SIZE (data
))
513 grub_minix_ino_t ino
;
514 char filename
[data
->filename_size
+ 1];
515 int dirino
= data
->ino
;
516 struct grub_dirhook_info info
;
517 grub_memset (&info
, 0, sizeof (info
));
520 if (grub_minix_read_file (data
, 0, pos
, sizeof (ino
),
524 if (grub_minix_read_file (data
, 0, pos
+ sizeof (ino
),
526 (char *) filename
) < 0)
528 filename
[data
->filename_size
] = '\0';
531 pos
+= sizeof (ino
) + data
->filename_size
;
535 grub_minix_read_inode (data
, grub_minix_le_to_cpu_ino (ino
));
536 info
.dir
= ((GRUB_MINIX_INODE_MODE (data
)
537 & GRUB_MINIX_IFDIR
) == GRUB_MINIX_IFDIR
);
538 if (hook (filename
, &info
) ? 1 : 0)
541 /* Load the old inode back in. */
542 grub_minix_read_inode (data
, dirino
);
544 pos
+= sizeof (ino
) + data
->filename_size
;
553 /* Open a file named NAME and initialize FILE. */
555 grub_minix_open (struct grub_file
*file
, const char *name
)
557 struct grub_minix_data
*data
;
558 data
= grub_minix_mount (file
->device
->disk
);
562 /* Open the inode op the root directory. */
563 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
570 if (!name
|| name
[0] != '/')
572 grub_error (GRUB_ERR_BAD_FILENAME
, "bad filename");
576 /* Traverse the directory tree to the node that should be
578 grub_minix_find_file (data
, name
);
586 file
->size
= GRUB_MINIX_INODE_SIZE (data
);
588 return GRUB_ERR_NONE
;
593 grub_minix_read (grub_file_t file
, char *buf
, grub_size_t len
)
595 struct grub_minix_data
*data
=
596 (struct grub_minix_data
*) file
->data
;
598 return grub_minix_read_file (data
, file
->read_hook
, file
->offset
, len
, buf
);
603 grub_minix_close (grub_file_t file
)
605 grub_free (file
->data
);
607 return GRUB_ERR_NONE
;
612 static struct grub_fs grub_minix_fs
=
614 #if defined(MODE_MINIX3)
616 #elif defined(MODE_MINIX2)
621 .dir
= grub_minix_dir
,
622 .open
= grub_minix_open
,
623 .read
= grub_minix_read
,
624 .close
= grub_minix_close
,
628 #if defined(MODE_MINIX3)
629 GRUB_MOD_INIT(minix3
)
630 #elif defined(MODE_MINIX2)
631 GRUB_MOD_INIT(minix2
)
636 grub_fs_register (&grub_minix_fs
);
640 #if defined(MODE_MINIX3)
641 GRUB_MOD_FINI(minix3
)
642 #elif defined(MODE_MINIX2)
643 GRUB_MOD_FINI(minix2
)
648 grub_fs_unregister (&grub_minix_fs
);