1 /* minix.c - The minix filesystem, version 1 and 2. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
6 * This program 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 2 of the License, or
9 * (at your option) any later version.
11 * This program 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 this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 #include <grub/file.h>
24 #include <grub/misc.h>
25 #include <grub/disk.h>
27 #include <grub/types.h>
29 #define GRUB_MINIX_MAGIC 0x137F
30 #define GRUB_MINIX2_MAGIC 0x2468
31 #define GRUB_MINIX_MAGIC_30 0x138F
32 #define GRUB_MINIX2_MAGIC_30 0x2478
33 #define GRUB_MINIX_BSIZE 1024U
34 #define GRUB_MINIX_LOG2_BSIZE 1
35 #define GRUB_MINIX_ROOT_INODE 1
36 #define GRUB_MINIX_MAX_SYMLNK_CNT 8
37 #define GRUB_MINIX_SBLOCK 2
39 #define GRUB_MINIX_IFDIR 0040000U
40 #define GRUB_MINIX_IFLNK 0120000U
42 #define GRUB_MINIX_INODE(data,field) (data->version == 1 ? \
43 data->inode. field : data->inode2. field)
44 #define GRUB_MINIX_INODE_ENDIAN(data,field,bits1,bits2) (data->version == 1 ? \
45 grub_le_to_cpu##bits1 (data->inode.field) : \
46 grub_le_to_cpu##bits2 (data->inode2.field))
47 #define GRUB_MINIX_INODE_SIZE(data) GRUB_MINIX_INODE_ENDIAN (data,size,16,32)
48 #define GRUB_MINIX_INODE_MODE(data) GRUB_MINIX_INODE_ENDIAN (data,mode,16,16)
49 #define GRUB_MINIX_INODE_DIR_ZONES(data,blk) GRUB_MINIX_INODE_ENDIAN \
50 (data,dir_zones[blk],16,32)
51 #define GRUB_MINIX_INODE_INDIR_ZONE(data) \
52 GRUB_MINIX_INODE_ENDIAN (data,indir_zone,16,32)
53 #define GRUB_MINIX_INODE_DINDIR_ZONE(data) \
54 GRUB_MINIX_INODE_ENDIAN (data,double_indir_zone,16,32)
55 #define GRUB_MINIX_INODE_BLKSZ(data) (data->version == 1 ? 2 : 4)
56 #define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE \
57 + grub_le_to_cpu16 (sblock->log2_zone_size))
58 #define GRUB_MINIX_ZONESZ (GRUB_MINIX_BSIZE \
59 << grub_le_to_cpu16 (sblock->log2_zone_size))
61 struct grub_minix_sblock
63 grub_uint16_t inode_cnt
;
64 grub_uint16_t zone_cnt
;
65 grub_uint16_t inode_bmap_size
;
66 grub_uint16_t zone_bmap_size
;
67 grub_uint16_t first_data_zone
;
68 grub_uint16_t log2_zone_size
;
69 grub_uint32_t max_file_size
;
73 struct grub_minix_inode
81 grub_uint16_t dir_zones
[7];
82 grub_uint16_t indir_zone
;
83 grub_uint16_t double_indir_zone
;
86 struct grub_minix2_inode
96 grub_uint32_t dir_zones
[7];
97 grub_uint32_t indir_zone
;
98 grub_uint32_t double_indir_zone
;
103 /* Information about a "mounted" minix filesystem. */
104 struct grub_minix_data
106 struct grub_minix_sblock sblock
;
107 struct grub_minix_inode inode
;
108 struct grub_minix2_inode inode2
;
117 static grub_dl_t my_mod
;
120 static grub_err_t
grub_minix_find_file (struct grub_minix_data
*data
,
124 grub_minix_get_file_block (struct grub_minix_data
*data
, unsigned int blk
)
126 struct grub_minix_sblock
*sblock
= &data
->sblock
;
129 auto int grub_get_indir (int, int);
131 /* Read the block pointer in ZONE, on the offset NUM. */
132 int grub_get_indir (int zone
, int num
)
134 if (data
->version
== 1)
136 grub_uint16_t indir16
;
137 grub_disk_read (data
->disk
,
138 zone
<< GRUB_MINIX_LOG2_ZONESZ
,
139 sizeof (grub_uint16_t
) * num
,
140 sizeof (grub_uint16_t
), (char *) &indir16
);
141 return grub_le_to_cpu16 (indir16
);
145 grub_uint32_t indir32
;
146 grub_disk_read (data
->disk
,
147 zone
<< GRUB_MINIX_LOG2_ZONESZ
,
148 sizeof (grub_uint32_t
) * num
,
149 sizeof (grub_uint32_t
), (char *) &indir32
);
150 return grub_le_to_cpu32 (indir32
);
156 return GRUB_MINIX_INODE_DIR_ZONES (data
, blk
);
158 /* Indirect block. */
160 if (blk
< GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
))
162 indir
= grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data
), blk
);
166 /* Double indirect block. */
167 blk
-= GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
);
168 if (blk
< (GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
))
169 * (GRUB_MINIX_ZONESZ
/ GRUB_MINIX_INODE_BLKSZ (data
)))
171 indir
= grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data
),
172 blk
/ GRUB_MINIX_ZONESZ
);
174 indir
= grub_get_indir (indir
, blk
% GRUB_MINIX_ZONESZ
);
179 /* This should never happen. */
180 grub_error (GRUB_ERR_OUT_OF_RANGE
, "file bigger than maximum size");
186 /* Read LEN bytes from the file described by DATA starting with byte
187 POS. Return the amount of read bytes in READ. */
189 grub_minix_read_file (struct grub_minix_data
*data
,
190 void (*read_hook
) (unsigned long sector
,
191 unsigned offset
, unsigned length
),
192 int pos
, unsigned int len
, char *buf
)
194 struct grub_minix_sblock
*sblock
= &data
->sblock
;
198 /* Adjust len so it we can't read past the end of the file. */
199 if (len
> GRUB_MINIX_INODE_SIZE (data
))
200 len
= GRUB_MINIX_INODE_SIZE (data
);
202 blockcnt
= (len
+ pos
+ GRUB_MINIX_BSIZE
- 1) / GRUB_MINIX_BSIZE
;
204 for (i
= pos
/ GRUB_MINIX_BSIZE
; i
< blockcnt
; i
++)
207 int blockoff
= pos
% GRUB_MINIX_BSIZE
;
208 int blockend
= GRUB_MINIX_BSIZE
;
212 blknr
= grub_minix_get_file_block (data
, i
);
217 if (i
== blockcnt
- 1)
219 blockend
= (len
+ pos
) % GRUB_MINIX_BSIZE
;
222 blockend
= GRUB_MINIX_BSIZE
;
226 if (i
== (pos
/ (int) GRUB_MINIX_BSIZE
))
228 skipfirst
= blockoff
;
229 blockend
-= skipfirst
;
232 data
->disk
->read_hook
= read_hook
;
233 grub_disk_read (data
->disk
, blknr
<< GRUB_MINIX_LOG2_ZONESZ
,
234 skipfirst
, blockend
, buf
);
236 data
->disk
->read_hook
= 0;
240 buf
+= GRUB_MINIX_BSIZE
- skipfirst
;
247 /* Read inode INO from the mounted filesystem described by DATA. This
248 inode is used by default now. */
250 grub_minix_read_inode (struct grub_minix_data
*data
, int ino
)
252 struct grub_minix_sblock
*sblock
= &data
->sblock
;
254 /* Block in which the inode is stored. */
258 /* The first inode in minix is inode 1. */
261 block
= ((2 + grub_le_to_cpu16 (sblock
->inode_bmap_size
)
262 + grub_le_to_cpu16 (sblock
->zone_bmap_size
))
263 << GRUB_MINIX_LOG2_BSIZE
);
265 if (data
->version
== 1)
267 block
+= ino
/ (GRUB_DISK_SECTOR_SIZE
/ sizeof (struct grub_minix_inode
));
268 int offs
= (ino
% (GRUB_DISK_SECTOR_SIZE
269 / sizeof (struct grub_minix_inode
))
270 * sizeof (struct grub_minix_inode
));
272 grub_disk_read (data
->disk
, block
, offs
,
273 sizeof (struct grub_minix_inode
), (char *) &data
->inode
);
277 block
+= ino
/ (GRUB_DISK_SECTOR_SIZE
278 / sizeof (struct grub_minix2_inode
));
280 % (GRUB_DISK_SECTOR_SIZE
/ sizeof (struct grub_minix2_inode
))
281 * sizeof (struct grub_minix2_inode
));
283 grub_disk_read (data
->disk
, block
, offs
,
284 sizeof (struct grub_minix2_inode
),(char *) &data
->inode2
);
287 return GRUB_ERR_NONE
;
291 /* Lookup the symlink the current inode points to. INO is the inode
292 number of the directory the symlink is relative to. */
294 grub_minix_lookup_symlink (struct grub_minix_data
*data
, int ino
)
296 char symlink
[GRUB_MINIX_INODE_SIZE (data
) + 1];
298 if (++data
->linknest
> GRUB_MINIX_MAX_SYMLNK_CNT
)
299 return grub_error (GRUB_ERR_SYMLINK_LOOP
, "too deep nesting of symlinks");
301 if (grub_minix_read_file (data
, 0, 0,
302 GRUB_MINIX_INODE_SIZE (data
), symlink
) < 0)
305 symlink
[GRUB_MINIX_INODE_SIZE (data
)] = '\0';
307 /* The symlink is an absolute path, go back to the root inode. */
308 if (symlink
[0] == '/')
309 ino
= GRUB_MINIX_ROOT_INODE
;
311 /* Now load in the old inode. */
312 if (grub_minix_read_inode (data
, ino
))
315 grub_minix_find_file (data
, symlink
);
317 grub_error (grub_errno
, "Can not follow symlink `%s'.", symlink
);
323 /* Find the file with the pathname PATH on the filesystem described by
326 grub_minix_find_file (struct grub_minix_data
*data
, const char *path
)
328 char fpath
[grub_strlen (path
) + 1];
331 unsigned int pos
= 0;
334 grub_strcpy (fpath
, path
);
336 /* Skip the first slash. */
344 /* Extract the actual part from the pathname. */
345 next
= grub_strchr (name
, '/');
355 char filename
[data
->filename_size
+ 1];
357 if (grub_strlen (name
) == 0)
358 return GRUB_ERR_NONE
;
360 if (grub_minix_read_file (data
, 0, pos
, sizeof (ino
),
363 if (grub_minix_read_file (data
, 0, pos
+ sizeof (ino
),
364 data
->filename_size
, (char *) filename
)< 0)
367 filename
[data
->filename_size
] = '\0';
369 /* Check if the current direntry matches the current part of the
371 if (!grub_strcmp (name
, filename
))
374 grub_minix_read_inode (data
, grub_le_to_cpu16 (ino
));
376 /* Follow the symlink. */
377 if ((GRUB_MINIX_INODE_MODE (data
)
378 & GRUB_MINIX_IFLNK
) == GRUB_MINIX_IFLNK
)
380 grub_minix_lookup_symlink (data
, dirino
);
391 next
= grub_strchr (name
, '/');
398 if ((GRUB_MINIX_INODE_MODE (data
)
399 & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
400 return grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
405 pos
+= sizeof (ino
) + data
->filename_size
;
406 } while (pos
< GRUB_MINIX_INODE_SIZE (data
));
408 grub_error (GRUB_ERR_FILE_NOT_FOUND
, "file not found");
413 /* Mount the filesystem on the disk DISK. */
414 static struct grub_minix_data
*
415 grub_minix_mount (grub_disk_t disk
)
417 struct grub_minix_data
*data
;
419 data
= grub_malloc (sizeof (struct grub_minix_data
));
423 /* Read the superblock. */
424 grub_disk_read (disk
, GRUB_MINIX_SBLOCK
, 0,
425 sizeof (struct grub_minix_sblock
),(char *) &data
->sblock
);
427 if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX_MAGIC
)
430 data
->filename_size
= 14;
432 else if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX2_MAGIC
)
435 data
->filename_size
= 14;
437 else if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX_MAGIC_30
)
440 data
->filename_size
= 30;
442 else if (grub_le_to_cpu16 (data
->sblock
.magic
) == GRUB_MINIX2_MAGIC_30
)
445 data
->filename_size
= 30;
450 grub_error (GRUB_ERR_BAD_FS
, "not an minix filesystem");
461 grub_minix_dir (grub_device_t device
, const char *path
,
462 int (*hook
) (const char *filename
, int dir
))
464 struct grub_minix_data
*data
= 0;
465 struct grub_minix_sblock
*sblock
;
466 unsigned int pos
= 0;
468 data
= grub_minix_mount (device
->disk
);
472 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
476 sblock
= &data
->sblock
;
478 grub_minix_find_file (data
, path
);
482 if ((GRUB_MINIX_INODE_MODE (data
) & GRUB_MINIX_IFDIR
) != GRUB_MINIX_IFDIR
)
484 grub_error (GRUB_ERR_BAD_FILE_TYPE
, "not a directory");
488 while (pos
< GRUB_MINIX_INODE_SIZE (data
))
491 char filename
[data
->filename_size
+ 1];
492 int dirino
= data
->ino
;
494 if (grub_minix_read_file (data
, 0, pos
, sizeof (ino
),
498 if (grub_minix_read_file (data
, 0, pos
+ sizeof (ino
),
500 (char *) filename
) < 0)
502 filename
[data
->filename_size
] = '\0';
504 /* The filetype is not stored in the dirent. Read the inode to
505 find out the filetype. This *REALLY* sucks. */
506 grub_minix_read_inode (data
, grub_le_to_cpu16 (ino
));
507 if (hook (filename
, ((GRUB_MINIX_INODE_MODE (data
)
508 & GRUB_MINIX_IFDIR
) == GRUB_MINIX_IFDIR
)) ? 1 : 0)
511 /* Load the old inode back in. */
512 grub_minix_read_inode (data
, dirino
);
514 pos
+= sizeof (ino
) + data
->filename_size
;
523 /* Open a file named NAME and initialize FILE. */
525 grub_minix_open (struct grub_file
*file
, const char *name
)
527 struct grub_minix_data
*data
;
528 data
= grub_minix_mount (file
->device
->disk
);
532 /* Open the inode op the root directory. */
533 grub_minix_read_inode (data
, GRUB_MINIX_ROOT_INODE
);
540 if (!name
|| name
[0] != '/')
542 grub_error (GRUB_ERR_BAD_FILENAME
, "bad filename");
546 /* Traverse the directory tree to the node that should be
548 grub_minix_find_file (data
, name
);
556 file
->size
= GRUB_MINIX_INODE_SIZE (data
);
558 return GRUB_ERR_NONE
;
563 grub_minix_read (grub_file_t file
, char *buf
, grub_ssize_t len
)
565 struct grub_minix_data
*data
=
566 (struct grub_minix_data
*) file
->data
;
568 return grub_minix_read_file (data
, file
->read_hook
, file
->offset
, len
, buf
);
573 grub_minix_close (grub_file_t file
)
575 grub_free (file
->data
);
577 return GRUB_ERR_NONE
;
582 grub_minix_label (grub_device_t device
__attribute ((unused
)),
583 char **label
__attribute ((unused
)))
585 return GRUB_ERR_NONE
;
589 static struct grub_fs grub_minix_fs
=
592 .dir
= grub_minix_dir
,
593 .open
= grub_minix_open
,
594 .read
= grub_minix_read
,
595 .close
= grub_minix_close
,
596 .label
= grub_minix_label
,
602 grub_fs_register (&grub_minix_fs
);
610 grub_fs_unregister (&grub_minix_fs
);