]> git.proxmox.com Git - grub2.git/blob - grub-core/fs/minix.c
minix3fs support
[grub2.git] / grub-core / fs / minix.c
1 /* minix.c - The minix filesystem, version 1 and 2. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004,2005,2006,2007,2008 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
20 #include <grub/err.h>
21 #include <grub/file.h>
22 #include <grub/mm.h>
23 #include <grub/misc.h>
24 #include <grub/disk.h>
25 #include <grub/dl.h>
26 #include <grub/types.h>
27
28 #ifdef MODE_MINIX3
29 #define GRUB_MINIX_MAGIC 0x4D5A
30 #elif defined(MODE_MINIX2)
31 #define GRUB_MINIX_MAGIC 0x2468
32 #define GRUB_MINIX_MAGIC_30 0x2478
33 #else
34 #define GRUB_MINIX_MAGIC 0x137F
35 #define GRUB_MINIX_MAGIC_30 0x138F
36 #endif
37
38 #define GRUB_MINIX_INODE_DIR_BLOCKS 7
39 #define GRUB_MINIX_LOG2_BSIZE 1
40 #define GRUB_MINIX_ROOT_INODE 1
41 #define GRUB_MINIX_MAX_SYMLNK_CNT 8
42 #define GRUB_MINIX_SBLOCK 2
43
44 #define GRUB_MINIX_IFDIR 0040000U
45 #define GRUB_MINIX_IFLNK 0120000U
46
47 #if defined(MODE_MINIX2) || defined(MODE_MINIX3)
48 typedef grub_uint32_t grub_minix_uintn_t;
49 #define grub_minix_le_to_cpu_n grub_le_to_cpu32
50 #else
51 typedef grub_uint16_t grub_minix_uintn_t;
52 #define grub_minix_le_to_cpu_n grub_le_to_cpu16
53 #endif
54
55 #define GRUB_MINIX_INODE_BLKSZ(data) sizeof (grub_minix_uintn_t)
56 #ifdef MODE_MINIX3
57 typedef grub_uint32_t grub_minix_ino_t;
58 #define grub_minix_le_to_cpu_ino grub_le_to_cpu32
59 #else
60 typedef grub_uint16_t grub_minix_ino_t;
61 #define grub_minix_le_to_cpu_ino grub_le_to_cpu16
62 #endif
63
64 #define GRUB_MINIX_INODE_SIZE(data) (grub_minix_le_to_cpu_n (data->inode.size))
65 #define GRUB_MINIX_INODE_MODE(data) (grub_le_to_cpu16 (data->inode.mode))
66 #define GRUB_MINIX_INODE_DIR_ZONES(data,blk) (grub_minix_le_to_cpu_n \
67 (data->inode.dir_zones[blk]))
68 #define GRUB_MINIX_INODE_INDIR_ZONE(data) (grub_minix_le_to_cpu_n \
69 (data->inode.indir_zone))
70 #define GRUB_MINIX_INODE_DINDIR_ZONE(data) (grub_minix_le_to_cpu_n \
71 (data->inode.double_indir_zone))
72
73 #define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE \
74 + grub_le_to_cpu16 (sblock->log2_zone_size))
75 #define GRUB_MINIX_ZONESZ (data->block_size \
76 << grub_le_to_cpu16 (sblock->log2_zone_size))
77
78 #ifdef MODE_MINIX3
79 struct grub_minix_sblock
80 {
81 grub_uint32_t inode_cnt;
82 grub_uint16_t zone_cnt;
83 grub_uint16_t inode_bmap_size;
84 grub_uint16_t zone_bmap_size;
85 grub_uint16_t first_data_zone;
86 grub_uint16_t log2_zone_size;
87 grub_uint16_t pad;
88 grub_uint32_t max_file_size;
89 grub_uint32_t zones;
90 grub_uint16_t magic;
91
92 grub_uint16_t pad2;
93 grub_uint16_t block_size;
94 grub_uint8_t disk_version;
95 };
96 #else
97 struct grub_minix_sblock
98 {
99 grub_uint16_t inode_cnt;
100 grub_uint16_t zone_cnt;
101 grub_uint16_t inode_bmap_size;
102 grub_uint16_t zone_bmap_size;
103 grub_uint16_t first_data_zone;
104 grub_uint16_t log2_zone_size;
105 grub_uint32_t max_file_size;
106 grub_uint16_t magic;
107 };
108 #endif
109
110 #if defined(MODE_MINIX3)
111
112 struct grub_minix_inode
113 {
114 grub_uint16_t mode;
115 grub_uint16_t nlinks;
116 grub_uint16_t uid;
117 grub_uint8_t gid;
118 grub_uint8_t pad;
119 grub_uint32_t size;
120 grub_uint32_t atime;
121 grub_uint32_t mtime;
122 grub_uint32_t ctime;
123 grub_uint32_t dir_zones[7];
124 grub_uint32_t indir_zone;
125 grub_uint32_t double_indir_zone;
126 grub_uint32_t unused;
127 };
128
129 #elif defined(MODE_MINIX2)
130 struct grub_minix_inode
131 {
132 grub_uint16_t mode;
133 grub_uint16_t uid;
134 grub_uint16_t size;
135 grub_uint32_t ctime;
136 grub_uint8_t gid;
137 grub_uint8_t nlinks;
138 grub_uint16_t dir_zones[7];
139 grub_uint16_t indir_zone;
140 grub_uint16_t double_indir_zone;
141 };
142
143 #else
144
145 struct grub_minix_inode
146 {
147 grub_uint16_t mode;
148 grub_uint16_t nlinks;
149 grub_uint16_t uid;
150 grub_uint16_t gid;
151 grub_uint32_t size;
152 grub_uint32_t atime;
153 grub_uint32_t mtime;
154 grub_uint32_t ctime;
155 grub_uint32_t dir_zones[7];
156 grub_uint32_t indir_zone;
157 grub_uint32_t double_indir_zone;
158 grub_uint32_t unused;
159
160 };
161
162 #endif
163
164 /* Information about a "mounted" minix filesystem. */
165 struct grub_minix_data
166 {
167 struct grub_minix_sblock sblock;
168 struct grub_minix_inode inode;
169 int ino;
170 int linknest;
171 grub_disk_t disk;
172 int filename_size;
173 grub_size_t block_size;
174 };
175
176 static grub_dl_t my_mod;
177 \f
178 static grub_err_t grub_minix_find_file (struct grub_minix_data *data,
179 const char *path);
180
181 static int
182 grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk)
183 {
184 struct grub_minix_sblock *sblock = &data->sblock;
185 int indir;
186
187 auto int grub_get_indir (int, int);
188
189 /* Read the block pointer in ZONE, on the offset NUM. */
190 int grub_get_indir (int zone, int num)
191 {
192 grub_minix_uintn_t indirn;
193 grub_disk_read (data->disk,
194 #ifdef MODE_MINIX3
195 zone * (data->block_size / GRUB_DISK_SECTOR_SIZE),
196 #else
197 zone << GRUB_MINIX_LOG2_ZONESZ,
198 #endif
199 sizeof (grub_minix_uintn_t) * num,
200 sizeof (grub_minix_uintn_t), (char *) &indirn);
201 return grub_minix_le_to_cpu_n (indirn);
202 }
203
204 /* Direct block. */
205 if (blk < GRUB_MINIX_INODE_DIR_BLOCKS)
206 return GRUB_MINIX_INODE_DIR_ZONES (data, blk);
207
208 /* Indirect block. */
209 blk -= GRUB_MINIX_INODE_DIR_BLOCKS;
210 if (blk < GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
211 {
212 indir = grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data), blk);
213 return indir;
214 }
215
216 /* Double indirect block. */
217 blk -= GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data);
218 if (blk < (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
219 * (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data)))
220 {
221 indir = grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data),
222 blk / GRUB_MINIX_ZONESZ);
223
224 indir = grub_get_indir (indir, blk % GRUB_MINIX_ZONESZ);
225
226 return indir;
227 }
228
229 /* This should never happen. */
230 grub_error (GRUB_ERR_OUT_OF_RANGE, "file bigger than maximum size");
231
232 return 0;
233 }
234
235
236 /* Read LEN bytes from the file described by DATA starting with byte
237 POS. Return the amount of read bytes in READ. */
238 static grub_ssize_t
239 grub_minix_read_file (struct grub_minix_data *data,
240 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
241 unsigned offset, unsigned length),
242 grub_off_t pos, grub_disk_addr_t len, char *buf)
243 {
244 struct grub_minix_sblock *sblock = &data->sblock;
245 grub_disk_addr_t i;
246 grub_disk_addr_t blockcnt;
247 grub_uint64_t posblock;
248 grub_uint32_t blockoff;
249
250 /* Adjust len so it we can't read past the end of the file. */
251 if (len + pos > GRUB_MINIX_INODE_SIZE (data))
252 len = GRUB_MINIX_INODE_SIZE (data) - pos;
253
254 blockcnt = grub_divmod64 ((len + pos + data->block_size - 1),
255 data->block_size, 0);
256 posblock = grub_divmod64 (pos, data->block_size, &blockoff);
257
258 for (i = posblock; i < blockcnt; i++)
259 {
260 grub_disk_addr_t blknr;
261 grub_uint32_t blockend = data->block_size;
262 grub_off_t skipfirst = 0;
263
264 blknr = grub_minix_get_file_block (data, i);
265 if (grub_errno)
266 return -1;
267
268 /* Last block. */
269 if (i == blockcnt - 1)
270 {
271 grub_divmod64 (len + pos, data->block_size, &blockend);
272
273 if (!blockend)
274 blockend = data->block_size;
275 }
276
277 /* First block. */
278 if (i == posblock)
279 {
280 skipfirst = blockoff;
281 blockend -= skipfirst;
282 }
283
284 data->disk->read_hook = read_hook;
285 grub_disk_read (data->disk,
286 #ifdef MODE_MINIX3
287 blknr * (sblock->block_size / GRUB_DISK_SECTOR_SIZE),
288 #else
289 blknr << GRUB_MINIX_LOG2_ZONESZ,
290 #endif
291 skipfirst, blockend, buf);
292 data->disk->read_hook = 0;
293 if (grub_errno)
294 return -1;
295
296 buf += data->block_size - skipfirst;
297 }
298
299 return len;
300 }
301
302
303 /* Read inode INO from the mounted filesystem described by DATA. This
304 inode is used by default now. */
305 static grub_err_t
306 grub_minix_read_inode (struct grub_minix_data *data, int ino)
307 {
308 struct grub_minix_sblock *sblock = &data->sblock;
309
310 /* Block in which the inode is stored. */
311 grub_disk_addr_t block;
312 data->ino = ino;
313
314 /* The first inode in minix is inode 1. */
315 ino--;
316 block = (2 + grub_le_to_cpu16 (sblock->inode_bmap_size)
317 + grub_le_to_cpu16 (sblock->zone_bmap_size));
318 #ifndef MODE_MINIX3
319 block <<= GRUB_MINIX_LOG2_BSIZE;
320 #else
321 block *= sblock->block_size / GRUB_DISK_SECTOR_SIZE;
322 #endif
323 block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode));
324 int offs = (ino % (GRUB_DISK_SECTOR_SIZE
325 / sizeof (struct grub_minix_inode))
326 * sizeof (struct grub_minix_inode));
327
328 grub_disk_read (data->disk, block, offs,
329 sizeof (struct grub_minix_inode), &data->inode);
330
331 return GRUB_ERR_NONE;
332 }
333
334
335 /* Lookup the symlink the current inode points to. INO is the inode
336 number of the directory the symlink is relative to. */
337 static grub_err_t
338 grub_minix_lookup_symlink (struct grub_minix_data *data, int ino)
339 {
340 char symlink[GRUB_MINIX_INODE_SIZE (data) + 1];
341
342 if (++data->linknest > GRUB_MINIX_MAX_SYMLNK_CNT)
343 return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
344
345 if (grub_minix_read_file (data, 0, 0,
346 GRUB_MINIX_INODE_SIZE (data), symlink) < 0)
347 return grub_errno;
348
349 symlink[GRUB_MINIX_INODE_SIZE (data)] = '\0';
350
351 /* The symlink is an absolute path, go back to the root inode. */
352 if (symlink[0] == '/')
353 ino = GRUB_MINIX_ROOT_INODE;
354
355 /* Now load in the old inode. */
356 if (grub_minix_read_inode (data, ino))
357 return grub_errno;
358
359 grub_minix_find_file (data, symlink);
360 if (grub_errno)
361 grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
362
363 return grub_errno;
364 }
365
366
367 /* Find the file with the pathname PATH on the filesystem described by
368 DATA. */
369 static grub_err_t
370 grub_minix_find_file (struct grub_minix_data *data, const char *path)
371 {
372 char fpath[grub_strlen (path) + 1];
373 char *name = fpath;
374 char *next;
375 unsigned int pos = 0;
376 int dirino;
377
378 grub_strcpy (fpath, path);
379
380 /* Skip the first slash. */
381 if (name[0] == '/')
382 {
383 name++;
384 if (!*name)
385 return 0;
386 }
387
388 /* Extract the actual part from the pathname. */
389 next = grub_strchr (name, '/');
390 if (next)
391 {
392 next[0] = '\0';
393 next++;
394 }
395
396 do
397 {
398 grub_minix_ino_t ino;
399 char filename[data->filename_size + 1];
400
401 if (grub_strlen (name) == 0)
402 return GRUB_ERR_NONE;
403
404 if (grub_minix_read_file (data, 0, pos, sizeof (ino),
405 (char *) &ino) < 0)
406 return grub_errno;
407 if (grub_minix_read_file (data, 0, pos + sizeof (ino),
408 data->filename_size, (char *) filename)< 0)
409 return grub_errno;
410
411 filename[data->filename_size] = '\0';
412
413 /* Check if the current direntry matches the current part of the
414 pathname. */
415 if (!grub_strcmp (name, filename))
416 {
417 dirino = data->ino;
418 grub_minix_read_inode (data, grub_minix_le_to_cpu_ino (ino));
419
420 /* Follow the symlink. */
421 if ((GRUB_MINIX_INODE_MODE (data)
422 & GRUB_MINIX_IFLNK) == GRUB_MINIX_IFLNK)
423 {
424 grub_minix_lookup_symlink (data, dirino);
425 if (grub_errno)
426 return grub_errno;
427 }
428
429 if (!next)
430 return 0;
431
432 pos = 0;
433
434 name = next;
435 next = grub_strchr (name, '/');
436 if (next)
437 {
438 next[0] = '\0';
439 next++;
440 }
441
442 if ((GRUB_MINIX_INODE_MODE (data)
443 & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR)
444 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
445
446 continue;
447 }
448
449 pos += sizeof (ino) + data->filename_size;
450 } while (pos < GRUB_MINIX_INODE_SIZE (data));
451
452 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
453 return grub_errno;
454 }
455
456
457 /* Mount the filesystem on the disk DISK. */
458 static struct grub_minix_data *
459 grub_minix_mount (grub_disk_t disk)
460 {
461 struct grub_minix_data *data;
462
463 data = grub_malloc (sizeof (struct grub_minix_data));
464 if (!data)
465 return 0;
466
467 /* Read the superblock. */
468 grub_disk_read (disk, GRUB_MINIX_SBLOCK, 0,
469 sizeof (struct grub_minix_sblock),&data->sblock);
470 if (grub_errno)
471 goto fail;
472
473 if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC)
474 {
475 #if !defined(MODE_MINIX3)
476 data->filename_size = 14;
477 #else
478 data->filename_size = 60;
479 #endif
480 }
481 #if !defined(MODE_MINIX3)
482 else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC_30)
483 data->filename_size = 30;
484 #endif
485 else
486 goto fail;
487
488 data->disk = disk;
489 data->linknest = 0;
490 #ifdef MODE_MINIX3
491 data->block_size = grub_le_to_cpu16 (data->sblock.block_size);
492 #else
493 data->block_size = 1024U;
494 #endif
495
496 return data;
497
498 fail:
499 grub_free (data);
500 #if defined(MODE_MINIX3)
501 grub_error (GRUB_ERR_BAD_FS, "not a minix3 filesystem");
502 #elif defined(MODE_MINIX2)
503 grub_error (GRUB_ERR_BAD_FS, "not a minix2 filesystem");
504 #else
505 grub_error (GRUB_ERR_BAD_FS, "not a minix filesystem");
506 #endif
507 return 0;
508 }
509 \f
510 static grub_err_t
511 grub_minix_dir (grub_device_t device, const char *path,
512 int (*hook) (const char *filename,
513 const struct grub_dirhook_info *info))
514 {
515 struct grub_minix_data *data = 0;
516 unsigned int pos = 0;
517
518 data = grub_minix_mount (device->disk);
519 if (!data)
520 return grub_errno;
521
522 grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
523 if (grub_errno)
524 goto fail;
525
526 grub_minix_find_file (data, path);
527 if (grub_errno)
528 goto fail;
529
530 if ((GRUB_MINIX_INODE_MODE (data) & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR)
531 {
532 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
533 goto fail;
534 }
535
536 while (pos < GRUB_MINIX_INODE_SIZE (data))
537 {
538 grub_minix_ino_t ino;
539 char filename[data->filename_size + 1];
540 int dirino = data->ino;
541 struct grub_dirhook_info info;
542 grub_memset (&info, 0, sizeof (info));
543
544
545 if (grub_minix_read_file (data, 0, pos, sizeof (ino),
546 (char *) &ino) < 0)
547 return grub_errno;
548
549 if (grub_minix_read_file (data, 0, pos + sizeof (ino),
550 data->filename_size,
551 (char *) filename) < 0)
552 return grub_errno;
553 filename[data->filename_size] = '\0';
554 if (!ino)
555 {
556 pos += sizeof (ino) + data->filename_size;
557 continue;
558 }
559
560 grub_minix_read_inode (data, grub_minix_le_to_cpu_ino (ino));
561 info.dir = ((GRUB_MINIX_INODE_MODE (data)
562 & GRUB_MINIX_IFDIR) == GRUB_MINIX_IFDIR);
563 if (hook (filename, &info) ? 1 : 0)
564 break;
565
566 /* Load the old inode back in. */
567 grub_minix_read_inode (data, dirino);
568
569 pos += sizeof (ino) + data->filename_size;
570 }
571
572 fail:
573 grub_free (data);
574 return grub_errno;
575 }
576
577
578 /* Open a file named NAME and initialize FILE. */
579 static grub_err_t
580 grub_minix_open (struct grub_file *file, const char *name)
581 {
582 struct grub_minix_data *data;
583 data = grub_minix_mount (file->device->disk);
584 if (!data)
585 return grub_errno;
586
587 /* Open the inode op the root directory. */
588 grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
589 if (grub_errno)
590 {
591 grub_free (data);
592 return grub_errno;
593 }
594
595 if (!name || name[0] != '/')
596 {
597 grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
598 return grub_errno;
599 }
600
601 /* Traverse the directory tree to the node that should be
602 opened. */
603 grub_minix_find_file (data, name);
604 if (grub_errno)
605 {
606 grub_free (data);
607 return grub_errno;
608 }
609
610 file->data = data;
611 file->size = GRUB_MINIX_INODE_SIZE (data);
612
613 return GRUB_ERR_NONE;
614 }
615
616
617 static grub_ssize_t
618 grub_minix_read (grub_file_t file, char *buf, grub_size_t len)
619 {
620 struct grub_minix_data *data =
621 (struct grub_minix_data *) file->data;
622
623 return grub_minix_read_file (data, file->read_hook, file->offset, len, buf);
624 }
625
626
627 static grub_err_t
628 grub_minix_close (grub_file_t file)
629 {
630 grub_free (file->data);
631
632 return GRUB_ERR_NONE;
633 }
634
635
636 \f
637 static struct grub_fs grub_minix_fs =
638 {
639 #if defined(MODE_MINIX3)
640 .name = "minix3",
641 #elif defined(MODE_MINIX2)
642 .name = "minix2",
643 #else
644 .name = "minix",
645 #endif
646 .dir = grub_minix_dir,
647 .open = grub_minix_open,
648 .read = grub_minix_read,
649 .close = grub_minix_close,
650 .next = 0
651 };
652
653 #if defined(MODE_MINIX3)
654 GRUB_MOD_INIT(minix3)
655 #elif defined(MODE_MINIX2)
656 GRUB_MOD_INIT(minix2)
657 #else
658 GRUB_MOD_INIT(minix)
659 #endif
660 {
661 grub_fs_register (&grub_minix_fs);
662 my_mod = mod;
663 }
664
665 #if defined(MODE_MINIX3)
666 GRUB_MOD_FINI(minix3)
667 #elif defined(MODE_MINIX2)
668 GRUB_MOD_FINI(minix2)
669 #else
670 GRUB_MOD_FINI(minix)
671 #endif
672 {
673 grub_fs_unregister (&grub_minix_fs);
674 }