]> git.proxmox.com Git - grub2.git/blob - fs/minix.c
2005-11-13 Marco Gerards <mgerards@xs4all.nl>
[grub2.git] / fs / minix.c
1 /* minix.c - The minix filesystem, version 1 and 2. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2004, 2005 Free Software Foundation, Inc.
5 *
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.
10 *
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.
15 *
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.
19 */
20
21 #include <grub/err.h>
22 #include <grub/file.h>
23 #include <grub/mm.h>
24 #include <grub/misc.h>
25 #include <grub/disk.h>
26 #include <grub/dl.h>
27 #include <grub/types.h>
28
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
38
39 #define GRUB_MINIX_IFDIR 0040000U
40 #define GRUB_MINIX_IFLNK 0120000U
41
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))
60
61 struct grub_minix_sblock
62 {
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;
70 grub_uint16_t magic;
71 };
72
73 struct grub_minix_inode
74 {
75 grub_uint16_t mode;
76 grub_uint16_t uid;
77 grub_uint16_t size;
78 grub_uint32_t ctime;
79 grub_uint8_t gid;
80 grub_uint8_t nlinks;
81 grub_uint16_t dir_zones[7];
82 grub_uint16_t indir_zone;
83 grub_uint16_t double_indir_zone;
84 };
85
86 struct grub_minix2_inode
87 {
88 grub_uint16_t mode;
89 grub_uint16_t nlinks;
90 grub_uint16_t uid;
91 grub_uint16_t gid;
92 grub_uint32_t size;
93 grub_uint32_t atime;
94 grub_uint32_t mtime;
95 grub_uint32_t ctime;
96 grub_uint32_t dir_zones[7];
97 grub_uint32_t indir_zone;
98 grub_uint32_t double_indir_zone;
99 grub_uint32_t unused;
100
101 };
102
103 /* Information about a "mounted" minix filesystem. */
104 struct grub_minix_data
105 {
106 struct grub_minix_sblock sblock;
107 struct grub_minix_inode inode;
108 struct grub_minix2_inode inode2;
109 int ino;
110 int linknest;
111 grub_disk_t disk;
112 int version;
113 int filename_size;
114 };
115
116 #ifndef GRUB_UTIL
117 static grub_dl_t my_mod;
118 #endif
119 \f
120 static grub_err_t grub_minix_find_file (struct grub_minix_data *data,
121 const char *path);
122
123 static int
124 grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk)
125 {
126 struct grub_minix_sblock *sblock = &data->sblock;
127 int indir;
128
129 auto int grub_get_indir (int, int);
130
131 /* Read the block pointer in ZONE, on the offset NUM. */
132 int grub_get_indir (int zone, int num)
133 {
134 if (data->version == 1)
135 {
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);
142 }
143 else
144 {
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);
151 }
152 }
153
154 /* Direct block. */
155 if (blk < 7)
156 return GRUB_MINIX_INODE_DIR_ZONES (data, blk);
157
158 /* Indirect block. */
159 blk -= 7;
160 if (blk < GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
161 {
162 indir = grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data), blk);
163 return indir;
164 }
165
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)))
170 {
171 indir = grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data),
172 blk / GRUB_MINIX_ZONESZ);
173
174 indir = grub_get_indir (indir, blk % GRUB_MINIX_ZONESZ);
175
176 return indir;
177 }
178
179 /* This should never happen. */
180 grub_error (GRUB_ERR_OUT_OF_RANGE, "file bigger than maximum size");
181
182 return 0;
183 }
184
185
186 /* Read LEN bytes from the file described by DATA starting with byte
187 POS. Return the amount of read bytes in READ. */
188 static grub_ssize_t
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)
193 {
194 struct grub_minix_sblock *sblock = &data->sblock;
195 int i;
196 int blockcnt;
197
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);
201
202 blockcnt = (len + pos + GRUB_MINIX_BSIZE - 1) / GRUB_MINIX_BSIZE;
203
204 for (i = pos / GRUB_MINIX_BSIZE; i < blockcnt; i++)
205 {
206 int blknr;
207 int blockoff = pos % GRUB_MINIX_BSIZE;
208 int blockend = GRUB_MINIX_BSIZE;
209
210 int skipfirst = 0;
211
212 blknr = grub_minix_get_file_block (data, i);
213 if (grub_errno)
214 return -1;
215
216 /* Last block. */
217 if (i == blockcnt - 1)
218 {
219 blockend = (len + pos) % GRUB_MINIX_BSIZE;
220
221 if (!blockend)
222 blockend = GRUB_MINIX_BSIZE;
223 }
224
225 /* First block. */
226 if (i == (pos / (int) GRUB_MINIX_BSIZE))
227 {
228 skipfirst = blockoff;
229 blockend -= skipfirst;
230 }
231
232 data->disk->read_hook = read_hook;
233 grub_disk_read (data->disk, blknr << GRUB_MINIX_LOG2_ZONESZ,
234 skipfirst, blockend, buf);
235
236 data->disk->read_hook = 0;
237 if (grub_errno)
238 return -1;
239
240 buf += GRUB_MINIX_BSIZE - skipfirst;
241 }
242
243 return len;
244 }
245
246
247 /* Read inode INO from the mounted filesystem described by DATA. This
248 inode is used by default now. */
249 static grub_err_t
250 grub_minix_read_inode (struct grub_minix_data *data, int ino)
251 {
252 struct grub_minix_sblock *sblock = &data->sblock;
253
254 /* Block in which the inode is stored. */
255 int block;
256 data->ino = ino;
257
258 /* The first inode in minix is inode 1. */
259 ino--;
260
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);
264
265 if (data->version == 1)
266 {
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));
271
272 grub_disk_read (data->disk, block, offs,
273 sizeof (struct grub_minix_inode), (char *) &data->inode);
274 }
275 else
276 {
277 block += ino / (GRUB_DISK_SECTOR_SIZE
278 / sizeof (struct grub_minix2_inode));
279 int offs = (ino
280 % (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix2_inode))
281 * sizeof (struct grub_minix2_inode));
282
283 grub_disk_read (data->disk, block, offs,
284 sizeof (struct grub_minix2_inode),(char *) &data->inode2);
285 }
286
287 return GRUB_ERR_NONE;
288 }
289
290
291 /* Lookup the symlink the current inode points to. INO is the inode
292 number of the directory the symlink is relative to. */
293 static grub_err_t
294 grub_minix_lookup_symlink (struct grub_minix_data *data, int ino)
295 {
296 char symlink[GRUB_MINIX_INODE_SIZE (data) + 1];
297
298 if (++data->linknest > GRUB_MINIX_MAX_SYMLNK_CNT)
299 return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
300
301 if (grub_minix_read_file (data, 0, 0,
302 GRUB_MINIX_INODE_SIZE (data), symlink) < 0)
303 return grub_errno;
304
305 symlink[GRUB_MINIX_INODE_SIZE (data)] = '\0';
306
307 /* The symlink is an absolute path, go back to the root inode. */
308 if (symlink[0] == '/')
309 ino = GRUB_MINIX_ROOT_INODE;
310
311 /* Now load in the old inode. */
312 if (grub_minix_read_inode (data, ino))
313 return grub_errno;
314
315 grub_minix_find_file (data, symlink);
316 if (grub_errno)
317 grub_error (grub_errno, "Can not follow symlink `%s'.", symlink);
318
319 return grub_errno;
320 }
321
322
323 /* Find the file with the pathname PATH on the filesystem described by
324 DATA. */
325 static grub_err_t
326 grub_minix_find_file (struct grub_minix_data *data, const char *path)
327 {
328 char fpath[grub_strlen (path) + 1];
329 char *name = fpath;
330 char *next;
331 unsigned int pos = 0;
332 int dirino;
333
334 grub_strcpy (fpath, path);
335
336 /* Skip the first slash. */
337 if (name[0] == '/')
338 {
339 name++;
340 if (!*name)
341 return 0;
342 }
343
344 /* Extract the actual part from the pathname. */
345 next = grub_strchr (name, '/');
346 if (next)
347 {
348 next[0] = '\0';
349 next++;
350 }
351
352 do
353 {
354 grub_uint16_t ino;
355 char filename[data->filename_size + 1];
356
357 if (grub_strlen (name) == 0)
358 return GRUB_ERR_NONE;
359
360 if (grub_minix_read_file (data, 0, pos, sizeof (ino),
361 (char *) &ino) < 0)
362 return grub_errno;
363 if (grub_minix_read_file (data, 0, pos + sizeof (ino),
364 data->filename_size, (char *) filename)< 0)
365 return grub_errno;
366
367 filename[data->filename_size] = '\0';
368
369 /* Check if the current direntry matches the current part of the
370 pathname. */
371 if (!grub_strcmp (name, filename))
372 {
373 dirino = data->ino;
374 grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
375
376 /* Follow the symlink. */
377 if ((GRUB_MINIX_INODE_MODE (data)
378 & GRUB_MINIX_IFLNK) == GRUB_MINIX_IFLNK)
379 {
380 grub_minix_lookup_symlink (data, dirino);
381 if (grub_errno)
382 return grub_errno;
383 }
384
385 if (!next)
386 return 0;
387
388 pos = 0;
389
390 name = next;
391 next = grub_strchr (name, '/');
392 if (next)
393 {
394 next[0] = '\0';
395 next++;
396 }
397
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");
401
402 continue;
403 }
404
405 pos += sizeof (ino) + data->filename_size;
406 } while (pos < GRUB_MINIX_INODE_SIZE (data));
407
408 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
409 return grub_errno;
410 }
411
412
413 /* Mount the filesystem on the disk DISK. */
414 static struct grub_minix_data *
415 grub_minix_mount (grub_disk_t disk)
416 {
417 struct grub_minix_data *data;
418
419 data = grub_malloc (sizeof (struct grub_minix_data));
420 if (!data)
421 return 0;
422
423 /* Read the superblock. */
424 grub_disk_read (disk, GRUB_MINIX_SBLOCK, 0,
425 sizeof (struct grub_minix_sblock),(char *) &data->sblock);
426
427 if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC)
428 {
429 data->version = 1;
430 data->filename_size = 14;
431 }
432 else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC)
433 {
434 data->version = 2;
435 data->filename_size = 14;
436 }
437 else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC_30)
438 {
439 data->version = 1;
440 data->filename_size = 30;
441 }
442 else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC_30)
443 {
444 data->version = 2;
445 data->filename_size = 30;
446 }
447 else
448 {
449 grub_free (data);
450 grub_error (GRUB_ERR_BAD_FS, "not an minix filesystem");
451 return 0;
452 }
453
454 data->disk = disk;
455 data->linknest = 0;
456
457 return data;
458 }
459 \f
460 static grub_err_t
461 grub_minix_dir (grub_device_t device, const char *path,
462 int (*hook) (const char *filename, int dir))
463 {
464 struct grub_minix_data *data = 0;
465 struct grub_minix_sblock *sblock;
466 unsigned int pos = 0;
467
468 data = grub_minix_mount (device->disk);
469 if (!data)
470 return grub_errno;
471
472 grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
473 if (grub_errno)
474 goto fail;
475
476 sblock = &data->sblock;
477
478 grub_minix_find_file (data, path);
479 if (grub_errno)
480 goto fail;
481
482 if ((GRUB_MINIX_INODE_MODE (data) & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR)
483 {
484 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
485 goto fail;
486 }
487
488 while (pos < GRUB_MINIX_INODE_SIZE (data))
489 {
490 grub_uint16_t ino;
491 char filename[data->filename_size + 1];
492 int dirino = data->ino;
493
494 if (grub_minix_read_file (data, 0, pos, sizeof (ino),
495 (char *) &ino) < 0)
496 return grub_errno;
497
498 if (grub_minix_read_file (data, 0, pos + sizeof (ino),
499 data->filename_size,
500 (char *) filename) < 0)
501 return grub_errno;
502 filename[data->filename_size] = '\0';
503
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)
509 break;
510
511 /* Load the old inode back in. */
512 grub_minix_read_inode (data, dirino);
513
514 pos += sizeof (ino) + data->filename_size;
515 }
516
517 fail:
518 grub_free (data);
519 return grub_errno;
520 }
521
522
523 /* Open a file named NAME and initialize FILE. */
524 static grub_err_t
525 grub_minix_open (struct grub_file *file, const char *name)
526 {
527 struct grub_minix_data *data;
528 data = grub_minix_mount (file->device->disk);
529 if (!data)
530 return grub_errno;
531
532 /* Open the inode op the root directory. */
533 grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
534 if (grub_errno)
535 {
536 grub_free (data);
537 return grub_errno;
538 }
539
540 if (!name || name[0] != '/')
541 {
542 grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
543 return grub_errno;
544 }
545
546 /* Traverse the directory tree to the node that should be
547 opened. */
548 grub_minix_find_file (data, name);
549 if (grub_errno)
550 {
551 grub_free (data);
552 return grub_errno;
553 }
554
555 file->data = data;
556 file->size = GRUB_MINIX_INODE_SIZE (data);
557
558 return GRUB_ERR_NONE;
559 }
560
561
562 static grub_ssize_t
563 grub_minix_read (grub_file_t file, char *buf, grub_ssize_t len)
564 {
565 struct grub_minix_data *data =
566 (struct grub_minix_data *) file->data;
567
568 return grub_minix_read_file (data, file->read_hook, file->offset, len, buf);
569 }
570
571
572 static grub_err_t
573 grub_minix_close (grub_file_t file)
574 {
575 grub_free (file->data);
576
577 return GRUB_ERR_NONE;
578 }
579
580
581 static grub_err_t
582 grub_minix_label (grub_device_t device __attribute ((unused)),
583 char **label __attribute ((unused)))
584 {
585 return GRUB_ERR_NONE;
586 }
587
588 \f
589 static struct grub_fs grub_minix_fs =
590 {
591 .name = "minix",
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,
597 .next = 0
598 };
599
600 GRUB_MOD_INIT(minix)
601 {
602 grub_fs_register (&grub_minix_fs);
603 #ifndef GRUB_UTIL
604 my_mod = mod;
605 #endif
606 }
607
608 GRUB_MOD_FINI(minix)
609 {
610 grub_fs_unregister (&grub_minix_fs);
611 }