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