Update svn:ignore property
[grub2/jjazz.git] / fs / minix.c
blobcc03d7d82795b0f3c5c45c6aee9d71b1cbc39ec8
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.
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.
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.
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/>.
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>
28 #define GRUB_MINIX_MAGIC 0x137F
29 #define GRUB_MINIX2_MAGIC 0x2468
30 #define GRUB_MINIX_MAGIC_30 0x138F
31 #define GRUB_MINIX2_MAGIC_30 0x2478
32 #define GRUB_MINIX_BSIZE 1024U
33 #define GRUB_MINIX_LOG2_BSIZE 1
34 #define GRUB_MINIX_ROOT_INODE 1
35 #define GRUB_MINIX_MAX_SYMLNK_CNT 8
36 #define GRUB_MINIX_SBLOCK 2
38 #define GRUB_MINIX_IFDIR 0040000U
39 #define GRUB_MINIX_IFLNK 0120000U
41 #define GRUB_MINIX_INODE(data,field) (data->version == 1 ? \
42 data->inode. field : data->inode2. field)
43 #define GRUB_MINIX_INODE_ENDIAN(data,field,bits1,bits2) (data->version == 1 ? \
44 grub_le_to_cpu##bits1 (data->inode.field) : \
45 grub_le_to_cpu##bits2 (data->inode2.field))
46 #define GRUB_MINIX_INODE_SIZE(data) GRUB_MINIX_INODE_ENDIAN (data,size,16,32)
47 #define GRUB_MINIX_INODE_MODE(data) GRUB_MINIX_INODE_ENDIAN (data,mode,16,16)
48 #define GRUB_MINIX_INODE_DIR_ZONES(data,blk) GRUB_MINIX_INODE_ENDIAN \
49 (data,dir_zones[blk],16,32)
50 #define GRUB_MINIX_INODE_INDIR_ZONE(data) \
51 GRUB_MINIX_INODE_ENDIAN (data,indir_zone,16,32)
52 #define GRUB_MINIX_INODE_DINDIR_ZONE(data) \
53 GRUB_MINIX_INODE_ENDIAN (data,double_indir_zone,16,32)
54 #define GRUB_MINIX_INODE_BLKSZ(data) (data->version == 1 ? 2 : 4)
55 #define GRUB_MINIX_LOG2_ZONESZ (GRUB_MINIX_LOG2_BSIZE \
56 + grub_le_to_cpu16 (sblock->log2_zone_size))
57 #define GRUB_MINIX_ZONESZ (GRUB_MINIX_BSIZE \
58 << grub_le_to_cpu16 (sblock->log2_zone_size))
60 struct grub_minix_sblock
62 grub_uint16_t inode_cnt;
63 grub_uint16_t zone_cnt;
64 grub_uint16_t inode_bmap_size;
65 grub_uint16_t zone_bmap_size;
66 grub_uint16_t first_data_zone;
67 grub_uint16_t log2_zone_size;
68 grub_uint32_t max_file_size;
69 grub_uint16_t magic;
72 struct grub_minix_inode
74 grub_uint16_t mode;
75 grub_uint16_t uid;
76 grub_uint16_t size;
77 grub_uint32_t ctime;
78 grub_uint8_t gid;
79 grub_uint8_t nlinks;
80 grub_uint16_t dir_zones[7];
81 grub_uint16_t indir_zone;
82 grub_uint16_t double_indir_zone;
85 struct grub_minix2_inode
87 grub_uint16_t mode;
88 grub_uint16_t nlinks;
89 grub_uint16_t uid;
90 grub_uint16_t gid;
91 grub_uint32_t size;
92 grub_uint32_t atime;
93 grub_uint32_t mtime;
94 grub_uint32_t ctime;
95 grub_uint32_t dir_zones[7];
96 grub_uint32_t indir_zone;
97 grub_uint32_t double_indir_zone;
98 grub_uint32_t unused;
102 /* Information about a "mounted" minix filesystem. */
103 struct grub_minix_data
105 struct grub_minix_sblock sblock;
106 struct grub_minix_inode inode;
107 struct grub_minix2_inode inode2;
108 int ino;
109 int linknest;
110 grub_disk_t disk;
111 int version;
112 int filename_size;
115 #ifndef GRUB_UTIL
116 static grub_dl_t my_mod;
117 #endif
119 static grub_err_t grub_minix_find_file (struct grub_minix_data *data,
120 const char *path);
122 static int
123 grub_minix_get_file_block (struct grub_minix_data *data, unsigned int blk)
125 struct grub_minix_sblock *sblock = &data->sblock;
126 int indir;
128 auto int grub_get_indir (int, int);
130 /* Read the block pointer in ZONE, on the offset NUM. */
131 int grub_get_indir (int zone, int num)
133 if (data->version == 1)
135 grub_uint16_t indir16;
136 grub_disk_read (data->disk,
137 zone << GRUB_MINIX_LOG2_ZONESZ,
138 sizeof (grub_uint16_t) * num,
139 sizeof (grub_uint16_t), (char *) &indir16);
140 return grub_le_to_cpu16 (indir16);
142 else
144 grub_uint32_t indir32;
145 grub_disk_read (data->disk,
146 zone << GRUB_MINIX_LOG2_ZONESZ,
147 sizeof (grub_uint32_t) * num,
148 sizeof (grub_uint32_t), (char *) &indir32);
149 return grub_le_to_cpu32 (indir32);
153 /* Direct block. */
154 if (blk < 7)
155 return GRUB_MINIX_INODE_DIR_ZONES (data, blk);
157 /* Indirect block. */
158 blk -= 7;
159 if (blk < GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
161 indir = grub_get_indir (GRUB_MINIX_INODE_INDIR_ZONE (data), blk);
162 return indir;
165 /* Double indirect block. */
166 blk -= GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data);
167 if (blk < (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data))
168 * (GRUB_MINIX_ZONESZ / GRUB_MINIX_INODE_BLKSZ (data)))
170 indir = grub_get_indir (GRUB_MINIX_INODE_DINDIR_ZONE (data),
171 blk / GRUB_MINIX_ZONESZ);
173 indir = grub_get_indir (indir, blk % GRUB_MINIX_ZONESZ);
175 return indir;
178 /* This should never happen. */
179 grub_error (GRUB_ERR_OUT_OF_RANGE, "file bigger than maximum size");
181 return 0;
185 /* Read LEN bytes from the file described by DATA starting with byte
186 POS. Return the amount of read bytes in READ. */
187 static grub_ssize_t
188 grub_minix_read_file (struct grub_minix_data *data,
189 void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
190 unsigned offset, unsigned length),
191 int pos, grub_disk_addr_t len, char *buf)
193 struct grub_minix_sblock *sblock = &data->sblock;
194 int i;
195 int blockcnt;
197 /* Adjust len so it we can't read past the end of the file. */
198 if (len > GRUB_MINIX_INODE_SIZE (data))
199 len = GRUB_MINIX_INODE_SIZE (data);
201 blockcnt = (len + pos + GRUB_MINIX_BSIZE - 1) / GRUB_MINIX_BSIZE;
203 for (i = pos / GRUB_MINIX_BSIZE; i < blockcnt; i++)
205 int blknr;
206 int blockoff = pos % GRUB_MINIX_BSIZE;
207 int blockend = GRUB_MINIX_BSIZE;
209 int skipfirst = 0;
211 blknr = grub_minix_get_file_block (data, i);
212 if (grub_errno)
213 return -1;
215 /* Last block. */
216 if (i == blockcnt - 1)
218 blockend = (len + pos) % GRUB_MINIX_BSIZE;
220 if (!blockend)
221 blockend = GRUB_MINIX_BSIZE;
224 /* First block. */
225 if (i == (pos / (int) GRUB_MINIX_BSIZE))
227 skipfirst = blockoff;
228 blockend -= skipfirst;
231 data->disk->read_hook = read_hook;
232 grub_disk_read (data->disk, blknr << GRUB_MINIX_LOG2_ZONESZ,
233 skipfirst, blockend, buf);
235 data->disk->read_hook = 0;
236 if (grub_errno)
237 return -1;
239 buf += GRUB_MINIX_BSIZE - skipfirst;
242 return len;
246 /* Read inode INO from the mounted filesystem described by DATA. This
247 inode is used by default now. */
248 static grub_err_t
249 grub_minix_read_inode (struct grub_minix_data *data, int ino)
251 struct grub_minix_sblock *sblock = &data->sblock;
253 /* Block in which the inode is stored. */
254 int block;
255 data->ino = ino;
257 /* The first inode in minix is inode 1. */
258 ino--;
260 block = ((2 + grub_le_to_cpu16 (sblock->inode_bmap_size)
261 + grub_le_to_cpu16 (sblock->zone_bmap_size))
262 << GRUB_MINIX_LOG2_BSIZE);
264 if (data->version == 1)
266 block += ino / (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix_inode));
267 int offs = (ino % (GRUB_DISK_SECTOR_SIZE
268 / sizeof (struct grub_minix_inode))
269 * sizeof (struct grub_minix_inode));
271 grub_disk_read (data->disk, block, offs,
272 sizeof (struct grub_minix_inode), (char *) &data->inode);
274 else
276 block += ino / (GRUB_DISK_SECTOR_SIZE
277 / sizeof (struct grub_minix2_inode));
278 int offs = (ino
279 % (GRUB_DISK_SECTOR_SIZE / sizeof (struct grub_minix2_inode))
280 * sizeof (struct grub_minix2_inode));
282 grub_disk_read (data->disk, block, offs,
283 sizeof (struct grub_minix2_inode),(char *) &data->inode2);
286 return GRUB_ERR_NONE;
290 /* Lookup the symlink the current inode points to. INO is the inode
291 number of the directory the symlink is relative to. */
292 static grub_err_t
293 grub_minix_lookup_symlink (struct grub_minix_data *data, int ino)
295 char symlink[GRUB_MINIX_INODE_SIZE (data) + 1];
297 if (++data->linknest > GRUB_MINIX_MAX_SYMLNK_CNT)
298 return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
300 if (grub_minix_read_file (data, 0, 0,
301 GRUB_MINIX_INODE_SIZE (data), symlink) < 0)
302 return grub_errno;
304 symlink[GRUB_MINIX_INODE_SIZE (data)] = '\0';
306 /* The symlink is an absolute path, go back to the root inode. */
307 if (symlink[0] == '/')
308 ino = GRUB_MINIX_ROOT_INODE;
310 /* Now load in the old inode. */
311 if (grub_minix_read_inode (data, ino))
312 return grub_errno;
314 grub_minix_find_file (data, symlink);
315 if (grub_errno)
316 grub_error (grub_errno, "Can not follow symlink `%s'.", symlink);
318 return grub_errno;
322 /* Find the file with the pathname PATH on the filesystem described by
323 DATA. */
324 static grub_err_t
325 grub_minix_find_file (struct grub_minix_data *data, const char *path)
327 char fpath[grub_strlen (path) + 1];
328 char *name = fpath;
329 char *next;
330 unsigned int pos = 0;
331 int dirino;
333 grub_strcpy (fpath, path);
335 /* Skip the first slash. */
336 if (name[0] == '/')
338 name++;
339 if (!*name)
340 return 0;
343 /* Extract the actual part from the pathname. */
344 next = grub_strchr (name, '/');
345 if (next)
347 next[0] = '\0';
348 next++;
353 grub_uint16_t ino;
354 char filename[data->filename_size + 1];
356 if (grub_strlen (name) == 0)
357 return GRUB_ERR_NONE;
359 if (grub_minix_read_file (data, 0, pos, sizeof (ino),
360 (char *) &ino) < 0)
361 return grub_errno;
362 if (grub_minix_read_file (data, 0, pos + sizeof (ino),
363 data->filename_size, (char *) filename)< 0)
364 return grub_errno;
366 filename[data->filename_size] = '\0';
368 /* Check if the current direntry matches the current part of the
369 pathname. */
370 if (!grub_strcmp (name, filename))
372 dirino = data->ino;
373 grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
375 /* Follow the symlink. */
376 if ((GRUB_MINIX_INODE_MODE (data)
377 & GRUB_MINIX_IFLNK) == GRUB_MINIX_IFLNK)
379 grub_minix_lookup_symlink (data, dirino);
380 if (grub_errno)
381 return grub_errno;
384 if (!next)
385 return 0;
387 pos = 0;
389 name = next;
390 next = grub_strchr (name, '/');
391 if (next)
393 next[0] = '\0';
394 next++;
397 if ((GRUB_MINIX_INODE_MODE (data)
398 & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR)
399 return grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
401 continue;
404 pos += sizeof (ino) + data->filename_size;
405 } while (pos < GRUB_MINIX_INODE_SIZE (data));
407 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
408 return grub_errno;
412 /* Mount the filesystem on the disk DISK. */
413 static struct grub_minix_data *
414 grub_minix_mount (grub_disk_t disk)
416 struct grub_minix_data *data;
418 data = grub_malloc (sizeof (struct grub_minix_data));
419 if (!data)
420 return 0;
422 /* Read the superblock. */
423 grub_disk_read (disk, GRUB_MINIX_SBLOCK, 0,
424 sizeof (struct grub_minix_sblock),(char *) &data->sblock);
425 if (grub_errno)
426 goto fail;
428 if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC)
430 data->version = 1;
431 data->filename_size = 14;
433 else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC)
435 data->version = 2;
436 data->filename_size = 14;
438 else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX_MAGIC_30)
440 data->version = 1;
441 data->filename_size = 30;
443 else if (grub_le_to_cpu16 (data->sblock.magic) == GRUB_MINIX2_MAGIC_30)
445 data->version = 2;
446 data->filename_size = 30;
448 else
449 goto fail;
451 data->disk = disk;
452 data->linknest = 0;
454 return data;
456 fail:
457 grub_free (data);
458 grub_error (GRUB_ERR_BAD_FS, "not a minix filesystem");
459 return 0;
462 static grub_err_t
463 grub_minix_dir (grub_device_t device, const char *path,
464 int (*hook) (const char *filename, int dir))
466 struct grub_minix_data *data = 0;
467 struct grub_minix_sblock *sblock;
468 unsigned int pos = 0;
470 data = grub_minix_mount (device->disk);
471 if (!data)
472 return grub_errno;
474 grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
475 if (grub_errno)
476 goto fail;
478 sblock = &data->sblock;
480 grub_minix_find_file (data, path);
481 if (grub_errno)
482 goto fail;
484 if ((GRUB_MINIX_INODE_MODE (data) & GRUB_MINIX_IFDIR) != GRUB_MINIX_IFDIR)
486 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
487 goto fail;
490 while (pos < GRUB_MINIX_INODE_SIZE (data))
492 grub_uint16_t ino;
493 char filename[data->filename_size + 1];
494 int dirino = data->ino;
496 if (grub_minix_read_file (data, 0, pos, sizeof (ino),
497 (char *) &ino) < 0)
498 return grub_errno;
500 if (grub_minix_read_file (data, 0, pos + sizeof (ino),
501 data->filename_size,
502 (char *) filename) < 0)
503 return grub_errno;
504 filename[data->filename_size] = '\0';
506 /* The filetype is not stored in the dirent. Read the inode to
507 find out the filetype. This *REALLY* sucks. */
508 grub_minix_read_inode (data, grub_le_to_cpu16 (ino));
509 if (hook (filename, ((GRUB_MINIX_INODE_MODE (data)
510 & GRUB_MINIX_IFDIR) == GRUB_MINIX_IFDIR)) ? 1 : 0)
511 break;
513 /* Load the old inode back in. */
514 grub_minix_read_inode (data, dirino);
516 pos += sizeof (ino) + data->filename_size;
519 fail:
520 grub_free (data);
521 return grub_errno;
525 /* Open a file named NAME and initialize FILE. */
526 static grub_err_t
527 grub_minix_open (struct grub_file *file, const char *name)
529 struct grub_minix_data *data;
530 data = grub_minix_mount (file->device->disk);
531 if (!data)
532 return grub_errno;
534 /* Open the inode op the root directory. */
535 grub_minix_read_inode (data, GRUB_MINIX_ROOT_INODE);
536 if (grub_errno)
538 grub_free (data);
539 return grub_errno;
542 if (!name || name[0] != '/')
544 grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
545 return grub_errno;
548 /* Traverse the directory tree to the node that should be
549 opened. */
550 grub_minix_find_file (data, name);
551 if (grub_errno)
553 grub_free (data);
554 return grub_errno;
557 file->data = data;
558 file->size = GRUB_MINIX_INODE_SIZE (data);
560 return GRUB_ERR_NONE;
564 static grub_ssize_t
565 grub_minix_read (grub_file_t file, char *buf, grub_size_t len)
567 struct grub_minix_data *data =
568 (struct grub_minix_data *) file->data;
570 return grub_minix_read_file (data, file->read_hook, file->offset, len, buf);
574 static grub_err_t
575 grub_minix_close (grub_file_t file)
577 grub_free (file->data);
579 return GRUB_ERR_NONE;
583 static grub_err_t
584 grub_minix_label (grub_device_t device __attribute ((unused)),
585 char **label __attribute ((unused)))
587 return GRUB_ERR_NONE;
591 static struct grub_fs grub_minix_fs =
593 .name = "minix",
594 .dir = grub_minix_dir,
595 .open = grub_minix_open,
596 .read = grub_minix_read,
597 .close = grub_minix_close,
598 .label = grub_minix_label,
599 .next = 0
602 GRUB_MOD_INIT(minix)
604 grub_fs_register (&grub_minix_fs);
605 #ifndef GRUB_UTIL
606 my_mod = mod;
607 #endif
610 GRUB_MOD_FINI(minix)
612 grub_fs_unregister (&grub_minix_fs);