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