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