~darkmuggle-deactivatedaccount/ubuntu/quantal/grub2/fix-872244

« back to all changes in this revision

Viewing changes to fs/jfs.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mto: (17.3.1 squeeze) (1.9.1 upstream)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20060105152040-b72i5pq1a82z22yi
Tags: upstream-1.92
ImportĀ upstreamĀ versionĀ 1.92

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
 
 
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
 
 
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
 
 
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
}