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>
29 #define GRUB_MINIX_MAGIC 0x2468
30 #define GRUB_MINIX_MAGIC_30 0x2478
32 #define GRUB_MINIX_MAGIC 0x137F
33 #define GRUB_MINIX_MAGIC_30 0x138F
35 #define GRUB_MINIX_BSIZE 1024U
36 #define GRUB_MINIX_LOG2_BSIZE 1
37 #define GRUB_MINIX_ROOT_INODE 1
38 #define GRUB_MINIX_MAX_SYMLNK_CNT 8
39 #define GRUB_MINIX_SBLOCK 2
41 #define GRUB_MINIX_IFDIR 0040000U
42 #define GRUB_MINIX_IFLNK 0120000U
45 typedef grub_uint32_t grub_minix_uintn_t
;
46 #define grub_minix_le_to_cpu_n grub_le_to_cpu32
48 typedef grub_uint16_t grub_minix_uintn_t
;
49 #define grub_minix_le_to_cpu_n grub_le_to_cpu16
52 #define GRUB_MINIX_INODE_BLKSZ(data) sizeof (grub_minix_uintn_t)
54 #define GRUB_MINIX_INODE_SIZE(data) (grub_minix_le_to_cpu_n (data->inode.size))
55 #define GRUB_MINIX_INODE_MODE(data) (grub_le_to_cpu16 (data->inode.mode))
56 #define GRUB_MINIX_INODE_DIR_ZONES(data,blk) (grub_minix_le_to_cpu_n \
57 (data->inode.dir_zones[blk]))
58 #define GRUB_MINIX_INODE_INDIR_ZONE(data) (grub_minix_le_to_cpu_n \
59 (data->inode.indir_zone))
60 #define GRUB_MINIX_INODE_DINDIR_ZONE(data) (grub_minix_le_to_cpu_n \
61 (data->inode.double_indir_zone))
63 #define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE \
64 + grub_le_to_cpu16 (sblock->log2_zone_size))
65 #define GRUB_MINIX_ZONESZ (GRUB_MINIX_BSIZE \
66 << grub_le_to_cpu16 (sblock->log2_zone_size))
68 struct grub_minix_sblock
70 grub_uint16_t inode_cnt
;
71 grub_uint16_t zone_cnt
;
72 grub_uint16_t inode_bmap_size
;
73 grub_uint16_t zone_bmap_size
;
74 grub_uint16_t first_data_zone
;
75 grub_uint16_t log2_zone_size
;
76 grub_uint32_t max_file_size
;
81 struct grub_minix_inode
89 grub_uint16_t dir_zones
[7];
90 grub_uint16_t indir_zone
;
91 grub_uint16_t double_indir_zone
;
96 struct grub_minix_inode
106 grub_uint32_t dir_zones
[7];
107 grub_uint32_t indir_zone
;
108 grub_uint32_t double_indir_zone
;
109 grub_uint32_t unused
;
115 /* Information about a "mounted" minix filesystem. */
116 struct grub_minix_data
118 struct grub_minix_sblock sblock
;
119 struct grub_minix_inode inode
;
126 static grub_dl_t my_mod
;
128 static grub_err_t
grub_minix_find_file (struct grub_minix_data
*data
,
132 grub_minix_get_file_block (struct grub_minix_data
*data
, unsigned int blk
)
134 struct grub_minix_sblock
*sblock
= &data
->sblock
;
137 auto int grub_get_indir (int, int);
139 /* Read the block pointer in ZONE, on the offset NUM. */
140 int grub_get_indir (int zone
, int num
)
142 grub_minix_uintn_t indirn
;
143 grub_disk_read (data
->disk
,
144 zone
<< GRUB_MINIX_LOG2_ZONESZ
,
145 sizeof (grub_minix_uintn_t
) * num
,
146 sizeof (grub_minix_uintn_t
), (char *) &indirn
);
147 return grub_minix_le_to_cpu_n (indirn
);
152 return GRUB_MINIX_INODE_DIR_ZONES (data
, blk
);
154 /* Indirect block. */
156 if (blk
< GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
))
158 indir
= grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data
), blk
);
162 /* Double indirect block. */
163 blk
-= GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
);
164 if (blk
< (GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
))
165 * (GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
)))
167 indir
= grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data
),
168 blk
/ GRUB_MINIX_ZONESZ
);
170 indir
= grub_get_indir (indir
, blk
% GRUB_MINIX_ZONESZ
);
175 /* This should never happen. */
176 grub_error (GRUB_ERR_OUT_OF_RANGE
, "file bigger than maximum size");
182 /* Read LEN bytes from the file described by DATA starting with byte
183 POS. Return the amount of read bytes in READ. */
185 grub_minix_read_file (struct grub_minix_data
*data
,
186 void NESTED_FUNC_ATTR (*read_hook
) (grub_disk_addr_t sector
,
187 unsigned offset
, unsigned length
),
188 int pos
, grub_disk_addr_t len
, char *buf
)
190 struct grub_minix_sblock
*sblock
= &data
->sblock
;
194 /* Adjust len so it we can't read past the end of the file. */
195 if (len
+ pos
> GRUB_MINIX_INODE_SIZE (data
))
196 len
= GRUB_MINIX_INODE_SIZE (data
) - pos
;
198 blockcnt
= (len
+ pos
+ GRUB_MINIX_BSIZE
- 1) / GRUB_MINIX_BSIZE
;
200 for (i
= pos
/ GRUB_MINIX_BSIZE
; i
< blockcnt
; i
++)
203 int blockoff
= pos
% GRUB_MINIX_BSIZE
;
204 int blockend
= GRUB_MINIX_BSIZE
;
208 blknr
= grub_minix_get_file_block (data
, i
);
213 if (i
== blockcnt
- 1)
215 blockend
= (len
+ pos
) % GRUB_MINIX_BSIZE
;
218 blockend
= GRUB_MINIX_BSIZE
;
222 if (i
== (pos
/ (int) GRUB_MINIX_BSIZE
))
224 skipfirst
= blockoff
;
225 blockend
-= skipfirst
;
228 data
->disk
->read_hook
= read_hook
;
229 grub_disk_read (data
->disk
, blknr
<< GRUB_MINIX_LOG2_ZONESZ
,
230 skipfirst
, blockend
, buf
);
232 data
->disk
->read_hook
= 0;
236 buf
+= GRUB_MINIX_BSIZE
- skipfirst
;
243 /* Read inode INO from the mounted filesystem described by DATA. This
244 inode is used by default now. */
246 grub_minix_read_inode (struct grub_minix_data
*data
, int ino
)
248 struct grub_minix_sblock
*sblock
= &data
->sblock
;
250 /* Block in which the inode is stored. */
254 /* The first inode in minix is inode 1. */
257 block
= ((2 + grub_le_to_cpu16 (sblock
->inode_bmap_size
)
258 + grub_le_to_cpu16 (sblock
->zone_bmap_size
))
259 << GRUB_MINIX_LOG2_BSIZE
);
261 block
+= ino
/ (GRUB_DISK_SECTOR_SIZE
/ sizeof (struct grub_minix_inode
));
262 int offs
= (ino
% (GRUB_DISK_SECTOR_SIZE
263 / sizeof (struct grub_minix_inode
))
264 * sizeof (struct grub_minix_inode
));
266 grub_disk_read (data
->disk
, block
, offs
,
267 sizeof (struct grub_minix_inode
), &data
->inode
);
269 return GRUB_ERR_NONE
;
273 /* Lookup the symlink the current inode points to. INO is the inode
274 number of the directory the symlink is relative to. */
276 grub_minix_lookup_symlink (struct grub_minix_data
*data
, int ino
)
278 char symlink
[GRUB_MINIX_INODE_SIZE (data
) + 1];
280 if (++data
->linknest
> GRUB_MINIX_MAX_SYMLNK_CNT
)
281 return grub_error (GRUB_ERR_SYMLINK_LOOP
, "too deep nesting of symlinks");
283 if (grub_minix_read_file (data
, 0, 0,
284 GRUB_MINIX_INODE_SIZE (data
), symlink
) < 0)
287 symlink
[GRUB_MINIX_INODE_SIZE (data
)] = '\0';
289 /* The symlink is an absolute path, go back to the root inode. */
290 if (symlink
[0] == '/')
291 ino
= GRUB_MINIX_ROOT_INODE
;
293 /* Now load in the old inode. */
294 if (grub_minix_read_inode (data
, ino
))
297 grub_minix_find_file (data
, symlink
);
299 grub_error (grub_errno
, "cannot follow symlink `%s'", symlink
);
305 /* Find the file with the pathname PATH on the filesystem described by
308 grub_minix_find_file (struct grub_minix_data
*data
, const char *path
)
310 char fpath
[grub_strlen (path
) + 1];
313 unsigned int pos
= 0;
316 grub_strcpy (fpath
, path
);
318 /* Skip the first slash. */
326 /* Extract the actual part from the pathname. */
327 next
= grub_strchr (name
, '/');
337 char filename
[data
->filename_size
+ 1];
339 if (grub_strlen (name
) == 0)
340 return GRUB_ERR_NONE
;
342 if (grub_minix_read_file (data
, 0, pos
, sizeof (ino
),
345 if (grub_minix_read_file (data
, 0, pos
+ sizeof (ino
),
346 data
->filename_size
, (char *) filename
)< 0)
349 filename
[data
->filename_size
] = '\0';
351 /* Check if the current direntry matches the current part of the
353 if (!grub_strcmp (name
, filename
))
356 grub_minix_read_inode (data
, grub_le_to_cpu16 (ino
));
358 /* Follow the symlink. */
359 if ((GRUB_MINIX_INODE_MODE (data
)
360 & GRUB_MINIX_IFLNK
) == GRUB_MINIX_IFLNK
)
362 grub_minix_lookup_symlink (data
, dirino
);
373 next
= grub_strchr (name
, '/');
380 if ((GRUB_MINIX_INODE_MODE (data
)
381 & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
382 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
387 pos
+= sizeof (ino
) + data
->filename_size
;
388 } while (pos
< GRUB_MINIX_INODE_SIZE (data
));
390 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
395 /* Mount the filesystem on the disk DISK. */
396 static struct grub_minix_data
*
397 grub_minix_mount (grub_disk_t disk
)
399 struct grub_minix_data
*data
;
401 data
= grub_malloc (sizeof (struct grub_minix_data
));
405 /* Read the superblock. */
406 grub_disk_read (disk
, GRUB_MINIX_SBLOCK
, 0,
407 sizeof (struct grub_minix_sblock
),&data
->sblock
);
411 if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX_MAGIC
)
412 data
->filename_size
= 14;
413 else if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX_MAGIC_30
)
414 data
->filename_size
= 30;
426 grub_error (GRUB_ERR_BAD_FS
, "not a minix2 filesystem");
428 grub_error (GRUB_ERR_BAD_FS
, "not a minix filesystem");
434 grub_minix_dir (grub_device_t device
, const char *path
,
435 int (*hook
) (const char *filename
,
436 const struct grub_dirhook_info
*info
))
438 struct grub_minix_data
*data
= 0;
439 struct grub_minix_sblock
*sblock
;
440 unsigned int pos
= 0;
442 data
= grub_minix_mount (device
->disk
);
446 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
450 sblock
= &data
->sblock
;
452 grub_minix_find_file (data
, path
);
456 if ((GRUB_MINIX_INODE_MODE (data
) & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
458 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
462 while (pos
< GRUB_MINIX_INODE_SIZE (data
))
465 char filename
[data
->filename_size
+ 1];
466 int dirino
= data
->ino
;
467 struct grub_dirhook_info info
;
468 grub_memset (&info
, 0, sizeof (info
));
471 if (grub_minix_read_file (data
, 0, pos
, sizeof (ino
),
475 if (grub_minix_read_file (data
, 0, pos
+ sizeof (ino
),
477 (char *) filename
) < 0)
479 filename
[data
->filename_size
] = '\0';
481 /* The filetype is not stored in the dirent. Read the inode to
482 find out the filetype. This *REALLY* sucks. */
483 grub_minix_read_inode (data
, grub_le_to_cpu16 (ino
));
484 info
.dir
= ((GRUB_MINIX_INODE_MODE (data
)
485 & GRUB_MINIX_IFDIR
) == GRUB_MINIX_IFDIR
);
486 if (hook (filename
, &info
) ? 1 : 0)
489 /* Load the old inode back in. */
490 grub_minix_read_inode (data
, dirino
);
492 pos
+= sizeof (ino
) + data
->filename_size
;
501 /* Open a file named NAME and initialize FILE. */
503 grub_minix_open (struct grub_file
*file
, const char *name
)
505 struct grub_minix_data
*data
;
506 data
= grub_minix_mount (file
->device
->disk
);
510 /* Open the inode op the root directory. */
511 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
518 if (!name
|| name
[0] != '/')
520 grub_error (GRUB_ERR_BAD_FILENAME
, "bad filename");
524 /* Traverse the directory tree to the node that should be
526 grub_minix_find_file (data
, name
);
534 file
->size
= GRUB_MINIX_INODE_SIZE (data
);
536 return GRUB_ERR_NONE
;
541 grub_minix_read (grub_file_t file
, char *buf
, grub_size_t len
)
543 struct grub_minix_data
*data
=
544 (struct grub_minix_data
*) file
->data
;
546 return grub_minix_read_file (data
, file
->read_hook
, file
->offset
, len
, buf
);
551 grub_minix_close (grub_file_t file
)
553 grub_free (file
->data
);
555 return GRUB_ERR_NONE
;
560 static struct grub_fs grub_minix_fs
=
567 .dir
= grub_minix_dir
,
568 .open
= grub_minix_open
,
569 .read
= grub_minix_read
,
570 .close
= grub_minix_close
,
575 GRUB_MOD_INIT(minix2
)
580 grub_fs_register (&grub_minix_fs
);
585 GRUB_MOD_FINI(minix2
)
590 grub_fs_unregister (&grub_minix_fs
);