]> git.proxmox.com Git - grub2.git/blob - fs/jfs.c
2005-11-13 Marco Gerards <mgerards@xs4all.nl>
[grub2.git] / fs / jfs.c
1 /* jfs.c - JFS. */
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_JFS_MAX_SYMLNK_CNT 8
30 #define GRUB_JFS_FILETYPE_MASK 0170000
31 #define GRUB_JFS_FILETYPE_REG 0100000
32 #define GRUB_JFS_FILETYPE_LNK 0120000
33 #define GRUB_JFS_FILETYPE_DIR 0040000
34
35 #define GRUB_JFS_SBLOCK 64
36 #define GRUB_JFS_AGGR_INODE 2
37 #define GRUB_JFS_FS1_INODE_BLK 104
38
39 #define GRUB_JFS_TREE_LEAF 2
40
41 struct grub_jfs_sblock
42 {
43 /* The magic for JFS. It should contain the string "JFS1". */
44 grub_uint8_t magic[4];
45 grub_uint32_t version;
46 grub_uint64_t ag_size;
47
48 /* The size of a filesystem block in bytes. XXX: currently only
49 4096 was tested. */
50 grub_uint32_t blksz;
51 grub_uint16_t log2_blksz;
52
53 grub_uint8_t unused[71];
54 grub_uint8_t volname[11];
55 };
56
57 struct grub_jfs_extent
58 {
59 /* The length of the extent in filesystem blocks. */
60 grub_uint16_t length;
61 grub_uint8_t length2;
62
63 /* The physical offset of the first block on the disk. */
64 grub_uint8_t blk1;
65 grub_uint32_t blk2;
66 } __attribute__ ((packed));
67
68 struct grub_jfs_iag
69 {
70 grub_uint8_t unused[3072];
71 struct grub_jfs_extent inodes[128];
72 } __attribute__ ((packed));
73
74
75 /* The head of the tree used to find extents. */
76 struct grub_jfs_treehead
77 {
78 grub_uint64_t next;
79 grub_uint64_t prev;
80
81 grub_uint8_t flags;
82 grub_uint8_t unused;
83
84 grub_uint16_t count;
85 grub_uint16_t max;
86 grub_uint8_t unused2[10];
87 } __attribute__ ((packed));
88
89 /* A node in the extent tree. */
90 struct grub_jfs_tree_extent
91 {
92 grub_uint8_t flags;
93 grub_uint16_t unused;
94
95 /* The offset is the key used to lookup an extent. */
96 grub_uint8_t offset1;
97 grub_uint32_t offset2;
98
99 struct grub_jfs_extent extent;
100 } __attribute__ ((packed));
101
102 /* The tree of directory entries. */
103 struct grub_jfs_tree_dir
104 {
105 /* Pointers to the previous and next tree headers of other nodes on
106 this level. */
107 grub_uint64_t nextb;
108 grub_uint64_t prevb;
109
110 grub_uint8_t flags;
111
112 /* The amount of dirents in this node. */
113 grub_uint8_t count;
114 grub_uint8_t freecnt;
115 grub_uint8_t freelist;
116 grub_uint8_t maxslot;
117
118 /* The location of the sorted array of pointers to dirents. */
119 grub_uint8_t sindex;
120 grub_uint8_t unused[10];
121 } __attribute__ ((packed));
122
123 /* An internal node in the dirents tree. */
124 struct grub_jfs_internal_dirent
125 {
126 struct grub_jfs_extent ex;
127 grub_uint8_t next;
128 grub_uint8_t len;
129 grub_uint16_t namepart[11];
130 } __attribute__ ((packed));
131
132 /* A leaf node in the dirents tree. */
133 struct grub_jfs_leaf_dirent
134 {
135 /* The inode for this dirent. */
136 grub_uint32_t inode;
137 grub_uint8_t next;
138
139 /* The size of the name. */
140 grub_uint8_t len;
141 grub_uint16_t namepart[11];
142 grub_uint32_t index;
143 } __attribute__ ((packed));
144
145 /* A leaf in the dirents tree. This one is used if the previously
146 dirent was not big enough to store the name. */
147 struct grub_jfs_leaf_next_dirent
148 {
149 grub_uint8_t next;
150 grub_uint8_t len;
151 grub_uint16_t namepart[15];
152 } __attribute__ ((packed));
153
154 struct grub_jfs_inode
155 {
156 grub_uint32_t stamp;
157 grub_uint32_t fileset;
158 grub_uint32_t inode;
159 grub_uint8_t unused[12];
160 grub_uint64_t size;
161 grub_uint8_t unused2[20];
162 grub_uint32_t mode;
163 grub_uint8_t unused3[72];
164 grub_uint8_t unused4[96];
165
166 union
167 {
168 /* The tree describing the extents of the file. */
169 struct
170 {
171 struct grub_jfs_treehead tree;
172 struct grub_jfs_tree_extent extents[16];
173 } file __attribute__ ((packed));
174 union
175 {
176 /* The tree describing the dirents. */
177 struct
178 {
179 grub_uint8_t unused[16];
180 grub_uint8_t flags;
181
182 /* Amount of dirents in this node. */
183 grub_uint8_t count;
184 grub_uint8_t freecnt;
185 grub_uint8_t freelist;
186 grub_uint32_t idotdot;
187 grub_uint8_t sorted[8];
188 } header;
189 struct grub_jfs_leaf_dirent dirents[8];
190 } dir __attribute__ ((packed));
191 /* Fast symlink. */
192 struct
193 {
194 grub_uint8_t unused[32];
195 grub_uint8_t path[128];
196 } symlink;
197 } __attribute__ ((packed));
198 } __attribute__ ((packed));
199
200 struct grub_jfs_data
201 {
202 struct grub_jfs_sblock sblock;
203 grub_disk_t disk;
204 struct grub_jfs_inode fileset;
205 struct grub_jfs_inode currinode;
206 int pos;
207 int linknest;
208 } __attribute__ ((packed));
209
210 struct grub_jfs_diropen
211 {
212 int index;
213 union
214 {
215 struct grub_jfs_tree_dir header;
216 struct grub_jfs_leaf_dirent dirent[0];
217 struct grub_jfs_leaf_next_dirent next_dirent[0];
218 char sorted[0];
219 } *dirpage __attribute__ ((packed));
220 struct grub_jfs_data *data;
221 struct grub_jfs_inode *inode;
222 int count;
223 char *sorted;
224 struct grub_jfs_leaf_dirent *leaf;
225 struct grub_jfs_leaf_next_dirent *next_leaf;
226
227 /* The filename and inode of the last read dirent. */
228 char name[255];
229 grub_uint32_t ino;
230 } __attribute__ ((packed));
231
232
233 #ifndef GRUB_UTIL
234 static grub_dl_t my_mod;
235 #endif
236 \f
237 static grub_err_t grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino);
238
239 /* Get the block number for the block BLK in the node INODE in the
240 mounted filesystem DATA. */
241 static int
242 grub_jfs_blkno (struct grub_jfs_data *data, struct grub_jfs_inode *inode,
243 unsigned int blk)
244 {
245 auto int getblk (struct grub_jfs_treehead *treehead,
246 struct grub_jfs_tree_extent *extents);
247
248 int getblk (struct grub_jfs_treehead *treehead,
249 struct grub_jfs_tree_extent *extents)
250 {
251 int found = -1;
252 int i;
253
254 for (i = 0; i < grub_le_to_cpu16 (treehead->count) - 2; i++)
255 {
256 if (treehead->flags & GRUB_JFS_TREE_LEAF)
257 {
258 /* Read the leafnode. */
259 if (grub_le_to_cpu32 (extents[i].offset2) <= blk
260 && ((grub_le_to_cpu16 (extents[i].extent.length))
261 + (extents[i].extent.length2 << 8)
262 + grub_le_to_cpu32 (extents[i].offset2)) > blk)
263 return (blk - grub_le_to_cpu32 (extents[i].offset2)
264 + grub_le_to_cpu32 (extents[i].extent.blk2));
265 }
266 else
267 if (blk >= grub_le_to_cpu32 (extents[i].offset2))
268 found = i;
269 }
270
271 if (found != -1)
272 {
273 struct
274 {
275 struct grub_jfs_treehead treehead;
276 struct grub_jfs_tree_extent extents[254];
277 } tree;
278
279 if (grub_disk_read (data->disk,
280 grub_le_to_cpu32 (extents[found].extent.blk2)
281 << (grub_le_to_cpu16 (data->sblock.log2_blksz)
282 - GRUB_DISK_SECTOR_BITS), 0,
283 sizeof (tree), (char *) &tree))
284 return -1;
285
286 return getblk (&tree.treehead, &tree.extents[0]);
287 }
288
289 return -1;
290 }
291
292 return getblk (&inode->file.tree, &inode->file.extents[0]);
293 }
294
295
296 static grub_err_t
297 grub_jfs_read_inode (struct grub_jfs_data *data, int ino,
298 struct grub_jfs_inode *inode)
299 {
300 struct grub_jfs_iag iag;
301 int iagnum = ino / 4096;
302 int inoext = (ino % 4096) / 32;
303 int inonum = (ino % 4096) % 32;
304 grub_uint32_t iagblk;
305 grub_uint32_t inoblk;
306
307 iagblk = grub_jfs_blkno (data, &data->fileset, iagnum + 1);
308 if (grub_errno)
309 return grub_errno;
310
311 /* Read in the IAG. */
312 if (grub_disk_read (data->disk,
313 iagblk << (grub_le_to_cpu16 (data->sblock.log2_blksz)
314 - GRUB_DISK_SECTOR_BITS), 0,
315 sizeof (struct grub_jfs_iag), (char *) &iag))
316 return grub_errno;
317
318 inoblk = grub_le_to_cpu32 (iag.inodes[inoext].blk2);
319 inoblk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz)
320 - GRUB_DISK_SECTOR_BITS);
321 inoblk += inonum;
322
323 if (grub_disk_read (data->disk, inoblk, 0,
324 sizeof (struct grub_jfs_inode), (char *) inode))
325 return grub_errno;
326
327 return 0;
328 }
329
330
331 static struct grub_jfs_data *
332 grub_jfs_mount (grub_disk_t disk)
333 {
334 struct grub_jfs_data *data = 0;
335
336 data = grub_malloc (sizeof (struct grub_jfs_data));
337 if (!data)
338 return 0;
339
340 /* Read the superblock. */
341 if (grub_disk_read (disk, GRUB_JFS_SBLOCK, 0,
342 sizeof (struct grub_jfs_sblock), (char *) &data->sblock))
343 goto fail;
344
345 if (grub_strncmp (data->sblock.magic, "JFS1", 4))
346 {
347 grub_error (GRUB_ERR_BAD_FS, "not a jfs filesystem");
348 goto fail;
349 }
350
351 data->disk = disk;
352 data->pos = 0;
353 data->linknest = 0;
354
355 /* Read the inode of the first fileset. */
356 if (grub_disk_read (data->disk, GRUB_JFS_FS1_INODE_BLK, 0,
357 sizeof (struct grub_jfs_inode), (char *) &data->fileset))
358 goto fail;
359
360 return data;
361
362 fail:
363 grub_free (data);
364
365 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
366 grub_error (GRUB_ERR_BAD_FS, "not a jfs filesystem");
367
368 return 0;
369 }
370
371
372 static struct grub_jfs_diropen *
373 grub_jfs_opendir (struct grub_jfs_data *data, struct grub_jfs_inode *inode)
374 {
375 struct grub_jfs_internal_dirent *de;
376 struct grub_jfs_diropen *diro;
377 int blk;
378
379 de = (struct grub_jfs_internal_dirent *) inode->dir.dirents;
380
381 if (!((grub_le_to_cpu32 (inode->mode)
382 & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR))
383 {
384 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
385 return 0;
386 }
387
388 diro = grub_malloc (sizeof (struct grub_jfs_diropen));
389 if (!diro)
390 return 0;
391
392 diro->index = 0;
393 diro->data = data;
394 diro->inode = inode;
395
396 /* Check if the entire tree is contained within the inode. */
397 if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
398 {
399 diro->leaf = inode->dir.dirents;
400 diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de;
401 diro->sorted = inode->dir.header.sorted;
402 diro->count = inode->dir.header.count;
403 diro->dirpage = 0;
404
405 return diro;
406 }
407
408 diro->dirpage = grub_malloc (grub_le_to_cpu32 (data->sblock.blksz));
409 if (!diro->dirpage)
410 {
411 grub_free (diro);
412 return 0;
413 }
414
415 blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2);
416 blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS);
417
418 /* Read in the nodes until we are on the leaf node level. */
419 do
420 {
421 int index;
422 if (grub_disk_read (data->disk, blk, 0,
423 grub_le_to_cpu32 (data->sblock.blksz),
424 diro->dirpage->sorted))
425 {
426 grub_free (diro->dirpage);
427 grub_free (diro);
428 return 0;
429 }
430
431 de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent;
432 index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
433 blk = (grub_le_to_cpu32 (de[index].ex.blk2)
434 << (grub_le_to_cpu16 (data->sblock.log2_blksz)
435 - GRUB_DISK_SECTOR_BITS));
436 } while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF));
437
438 diro->leaf = diro->dirpage->dirent;
439 diro->next_leaf = diro->dirpage->next_dirent;
440 diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
441 diro->count = diro->dirpage->header.count;
442
443 return diro;
444 }
445
446
447 static void
448 grub_jfs_closedir (struct grub_jfs_diropen *diro)
449 {
450 if (!diro)
451 return;
452 grub_free (diro->dirpage);
453 grub_free (diro);
454 }
455
456
457 /* Read in the next dirent from the directory described by DIRO. */
458 static grub_err_t
459 grub_jfs_getent (struct grub_jfs_diropen *diro)
460 {
461 int strpos = 0;
462 struct grub_jfs_leaf_dirent *leaf;
463 struct grub_jfs_leaf_next_dirent *next_leaf;
464 int len;
465 int nextent;
466 grub_uint16_t filename[255];
467
468 auto void addstr (grub_uint16_t *uname, int ulen);
469
470 /* Add the unicode string to the utf16 filename buffer. */
471 void addstr (grub_uint16_t *name, int ulen)
472 {
473 while (ulen--)
474 filename[strpos++] = *(name++);
475 }
476
477 /* The last node, read in more. */
478 if (diro->index == diro->count)
479 {
480 unsigned int next;
481
482 /* If the inode contains the entrie tree or if this was the last
483 node, there is nothing to read. */
484 if ((diro->inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
485 || !grub_le_to_cpu64 (diro->dirpage->header.nextb))
486 return GRUB_ERR_OUT_OF_RANGE;
487
488 next = grub_le_to_cpu64 (diro->dirpage->header.nextb);
489 next <<= (grub_le_to_cpu16 (diro->data->sblock.log2_blksz)
490 - GRUB_DISK_SECTOR_BITS);
491
492 if (grub_disk_read (diro->data->disk, next, 0,
493 grub_le_to_cpu32 (diro->data->sblock.blksz),
494 diro->dirpage->sorted))
495 return grub_errno;
496
497 diro->leaf = diro->dirpage->dirent;
498 diro->next_leaf = diro->dirpage->next_dirent;
499 diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
500 diro->count = diro->dirpage->header.count;
501 diro->index = 0;
502 }
503
504 leaf = &diro->leaf[(int) diro->sorted[diro->index]];
505 next_leaf = &diro->next_leaf[diro->index];
506
507 len = leaf->len;
508 if (!len)
509 {
510 diro->index++;
511 return grub_jfs_getent (diro);
512 }
513
514 addstr (leaf->namepart, len < 11 ? len : 11);
515 diro->ino = grub_le_to_cpu32 (leaf->inode);
516 len -= 11;
517
518 /* Move down to the leaf level. */
519 nextent = leaf->next;
520 if (leaf->next != 255)
521 do
522 {
523 next_leaf = &diro->next_leaf[nextent];
524 addstr (next_leaf->namepart, len < 15 ? len : 15 );
525
526 len -= 15;
527 nextent = next_leaf->next;
528 } while (next_leaf->next != 255 && len > 0);
529
530 diro->index++;
531
532 /* Convert the temporary UTF16 filename to UTF8. */
533 *grub_utf16_to_utf8 (diro->name, filename, strpos) = '\0';
534
535 return 0;
536 }
537
538
539 /* Read LEN bytes from the file described by DATA starting with byte
540 POS. Return the amount of read bytes in READ. */
541 static grub_ssize_t
542 grub_jfs_read_file (struct grub_jfs_data *data,
543 void (*read_hook) (unsigned long sector,
544 unsigned offset, unsigned length),
545 int pos, unsigned int len, char *buf)
546 {
547 int i;
548 int blockcnt;
549
550 /* Adjust len so it we can't read past the end of the file. */
551 if (len > data->currinode.size)
552 len = data->currinode.size;
553
554 blockcnt = ((len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1)
555 / grub_le_to_cpu32 (data->sblock.blksz));
556
557 for (i = pos / grub_le_to_cpu32 (data->sblock.blksz); i < blockcnt; i++)
558 {
559 int blknr;
560 int blockoff = pos % grub_le_to_cpu32 (data->sblock.blksz);
561 int blockend = grub_le_to_cpu32 (data->sblock.blksz);
562
563 int skipfirst = 0;
564
565 blknr = grub_jfs_blkno (data, &data->currinode, i);
566 if (grub_errno)
567 return -1;
568
569 /* Last block. */
570 if (i == blockcnt - 1)
571 {
572 blockend = (len + pos) % grub_le_to_cpu32 (data->sblock.blksz);
573
574 if (!blockend)
575 blockend = grub_le_to_cpu32 (data->sblock.blksz);
576 }
577
578 /* First block. */
579 if (i == (pos / (int) grub_le_to_cpu32 (data->sblock.blksz)))
580 {
581 skipfirst = blockoff;
582 blockend -= skipfirst;
583 }
584
585 data->disk->read_hook = read_hook;
586 grub_disk_read (data->disk,
587 blknr << (grub_le_to_cpu16 (data->sblock.log2_blksz)
588 - GRUB_DISK_SECTOR_BITS),
589 skipfirst, blockend, buf);
590
591 data->disk->read_hook = 0;
592 if (grub_errno)
593 return -1;
594
595 buf += grub_le_to_cpu32 (data->sblock.blksz) - skipfirst;
596 }
597
598 return len;
599 }
600
601
602 /* Find the file with the pathname PATH on the filesystem described by
603 DATA. */
604 static grub_err_t
605 grub_jfs_find_file (struct grub_jfs_data *data, const char *path)
606 {
607 char fpath[grub_strlen (path)];
608 char *name = fpath;
609 char *next;
610 unsigned int pos = 0;
611 struct grub_jfs_diropen *diro;
612
613 grub_strncpy (fpath, path, grub_strlen (path) + 1);
614
615 if (grub_jfs_read_inode (data, GRUB_JFS_AGGR_INODE, &data->currinode))
616 return grub_errno;
617
618 /* Skip the first slash. */
619 if (name[0] == '/')
620 {
621 name++;
622 if (!*name)
623 return 0;
624 }
625
626 /* Extract the actual part from the pathname. */
627 next = grub_strchr (name, '/');
628 if (next)
629 {
630 next[0] = '\0';
631 next++;
632 }
633
634 diro = grub_jfs_opendir (data, &data->currinode);
635 if (!diro)
636 return grub_errno;
637
638 for (;;)
639 {
640 if (grub_strlen (name) == 0)
641 return GRUB_ERR_NONE;
642
643 if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE)
644 break;
645
646 /* Check if the current direntry matches the current part of the
647 pathname. */
648 if (!grub_strcmp (name, diro->name))
649 {
650 int ino = diro->ino;
651 int dirino = grub_le_to_cpu32 (data->currinode.inode);
652
653 grub_jfs_closedir (diro);
654 diro = 0;
655
656 if (grub_jfs_read_inode (data, ino, &data->currinode))
657 break;
658
659 /* Check if this is a symlink. */
660 if ((grub_le_to_cpu32 (data->currinode.mode)
661 & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK)
662 {
663 grub_jfs_lookup_symlink (data, dirino);
664 if (grub_errno)
665 return grub_errno;
666 }
667
668 if (!next)
669 return 0;
670
671 pos = 0;
672
673 name = next;
674 next = grub_strchr (name, '/');
675 if (next)
676 {
677 next[0] = '\0';
678 next++;
679 }
680
681 /* Open this directory for reading dirents. */
682 diro = grub_jfs_opendir (data, &data->currinode);
683 if (!diro)
684 return grub_errno;
685
686 continue;
687 }
688 }
689
690 grub_jfs_closedir (diro);
691 grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
692 return grub_errno;
693 }
694
695
696 static grub_err_t
697 grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino)
698 {
699 int size = grub_le_to_cpu64 (data->currinode.size);
700 char symlink[size + 1];
701
702 if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT)
703 return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
704
705 if (size <= 128)
706 grub_strncpy (symlink, data->currinode.symlink.path, 128);
707 else if (grub_jfs_read_file (data, 0, 0, size, symlink) < 0)
708 return grub_errno;
709
710 symlink[size] = '\0';
711
712 /* The symlink is an absolute path, go back to the root inode. */
713 if (symlink[0] == '/')
714 ino = 2;
715
716 /* Now load in the old inode. */
717 if (grub_jfs_read_inode (data, ino, &data->currinode))
718 return grub_errno;
719
720 grub_jfs_find_file (data, symlink);
721 if (grub_errno)
722 grub_error (grub_errno, "Can not follow symlink `%s'.", symlink);
723
724 return grub_errno;
725 }
726 \f
727
728 static grub_err_t
729 grub_jfs_dir (grub_device_t device, const char *path,
730 int (*hook) (const char *filename, int dir))
731 {
732 struct grub_jfs_data *data = 0;
733 struct grub_jfs_diropen *diro = 0;
734
735 #ifndef GRUB_UTIL
736 grub_dl_ref (my_mod);
737 #endif
738
739 data = grub_jfs_mount (device->disk);
740 if (!data)
741 goto fail;
742
743 if (grub_jfs_find_file (data, path))
744 goto fail;
745
746 diro = grub_jfs_opendir (data, &data->currinode);
747 if (!diro)
748 goto fail;
749
750 /* Iterate over the dirents in the directory that was found. */
751 while (grub_jfs_getent (diro) != GRUB_ERR_OUT_OF_RANGE)
752 {
753 struct grub_jfs_inode inode;
754 int isdir;
755
756 if (grub_jfs_read_inode (data, diro->ino, &inode))
757 goto fail;
758
759 isdir = (grub_le_to_cpu32 (inode.mode)
760 & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR;
761 if (hook (diro->name, isdir))
762 goto fail;
763 }
764
765 /* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent. */
766 if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
767 grub_errno = 0;
768
769 fail:
770 grub_jfs_closedir (diro);
771 grub_free (data);
772
773 #ifndef GRUB_UTIL
774 grub_dl_unref (my_mod);
775 #endif
776
777 return grub_errno;
778 }
779
780
781 /* Open a file named NAME and initialize FILE. */
782 static grub_err_t
783 grub_jfs_open (struct grub_file *file, const char *name)
784 {
785 struct grub_jfs_data *data;
786
787 #ifndef GRUB_UTIL
788 grub_dl_ref (my_mod);
789 #endif
790
791 data = grub_jfs_mount (file->device->disk);
792 if (!data)
793 goto fail;
794
795 grub_jfs_find_file (data, name);
796 if (grub_errno)
797 goto fail;
798
799 /* It is only possible for open regular files. */
800 if (! ((grub_le_to_cpu32 (data->currinode.mode)
801 & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_REG))
802 {
803 grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
804 goto fail;
805 }
806
807 file->data = data;
808 file->size = grub_le_to_cpu64 (data->currinode.size);
809
810 return 0;
811
812 fail:
813
814 #ifndef GRUB_UTIL
815 grub_dl_unref (my_mod);
816 #endif
817
818 grub_free (data);
819
820 return grub_errno;;
821 }
822
823
824 static grub_ssize_t
825 grub_jfs_read (grub_file_t file, char *buf, grub_ssize_t len)
826 {
827 struct grub_jfs_data *data =
828 (struct grub_jfs_data *) file->data;
829
830 return grub_jfs_read_file (data, file->read_hook, file->offset, len, buf);
831 }
832
833
834 static grub_err_t
835 grub_jfs_close (grub_file_t file)
836 {
837 grub_free (file->data);
838
839 #ifndef GRUB_UTIL
840 grub_dl_unref (my_mod);
841 #endif
842
843 return GRUB_ERR_NONE;
844 }
845
846
847 static grub_err_t
848 grub_jfs_label (grub_device_t device, char **label)
849 {
850 struct grub_jfs_data *data;
851 data = grub_jfs_mount (device->disk);
852
853 if (data)
854 *label = grub_strndup (data->sblock.volname, 11);
855 else
856 *label = 0;
857
858 return grub_errno;
859 }
860 \f
861
862 static struct grub_fs grub_jfs_fs =
863 {
864 .name = "jfs",
865 .dir = grub_jfs_dir,
866 .open = grub_jfs_open,
867 .read = grub_jfs_read,
868 .close = grub_jfs_close,
869 .label = grub_jfs_label,
870 .next = 0
871 };
872
873 GRUB_MOD_INIT(jfs)
874 {
875 grub_fs_register (&grub_jfs_fs);
876 #ifndef GRUB_UTIL
877 my_mod = mod;
878 #endif
879 }
880
881 GRUB_MOD_FINI(jfs)
882 {
883 grub_fs_unregister (&grub_jfs_fs);
884 }