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

« back to all changes in this revision

Viewing changes to grub-core/fs/jfs.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Evan Broder, Mario Limonciello
  • Date: 2010-11-24 13:59:55 UTC
  • mfrom: (1.17.6 upstream) (17.6.15 experimental)
  • Revision ID: james.westby@ubuntu.com-20101124135955-r6ii5sepayr7jt53
Tags: 1.99~20101124-1ubuntu1
[ Colin Watson ]
* Resynchronise with Debian experimental.  Remaining changes:
  - Adjust for default Ubuntu boot options ("quiet splash").
  - Default to hiding the menu; holding down Shift at boot will show it.
  - Set a monochromatic theme for Ubuntu.
  - Apply Ubuntu GRUB Legacy changes to legacy update-grub script: title,
    recovery mode, quiet option, tweak how memtest86+ is displayed, and
    use UUIDs where appropriate.
  - Fix backslash-escaping in merge_debconf_into_conf.
  - Remove "GNU/Linux" from default distributor string.
  - Add crashkernel= options if kdump and makedumpfile are available.
  - If other operating systems are installed, then automatically unhide
    the menu.  Otherwise, if GRUB_HIDDEN_TIMEOUT is 0, then use keystatus
    if available to check whether Shift is pressed.  If it is, show the
    menu, otherwise boot immediately.  If keystatus is not available, then
    fall back to a short delay interruptible with Escape.
  - Allow Shift to interrupt 'sleep --interruptible'.
  - Don't display introductory message about line editing unless we're
    actually offering a shell prompt.  Don't clear the screen just before
    booting if we never drew the menu in the first place.
  - Remove some verbose messages printed before reading the configuration
    file.
  - Suppress progress messages as the kernel and initrd load for
    non-recovery kernel menu entries.
  - Change prepare_grub_to_access_device to handle filesystems
    loop-mounted on file images.
  - Ignore devices loop-mounted from files in 10_linux.
  - Show the boot menu if the previous boot failed, that is if it failed
    to get to the end of one of the normal runlevels.
  - Don't generate /boot/grub/device.map during grub-install or
    grub-mkconfig by default.
  - Adjust upgrade version checks for Ubuntu.
  - Don't display "GRUB loading" unless Shift is held down.
  - Adjust versions of grub-doc and grub-legacy-doc conflicts to tolerate
    our backport of the grub-doc split.
  - Fix LVM/RAID probing in the absence of /boot/grub/device.map.
  - Look for .mo files in /usr/share/locale-langpack as well, in
    preference.
  - Make sure GRUB_TIMEOUT isn't quoted unnecessarily.
  - Probe all devices in 'grub-probe --target=drive' if
    /boot/grub/device.map is missing.
  - Build-depend on qemu-kvm rather than qemu-system for grub-pc tests.
  - Use qemu rather than qemu-system-i386.
  - Program vesafb on BIOS systems rather than efifb.
  - Add a grub-rescue-efi-amd64 package containing a rescue CD-ROM image
    for EFI-AMD64.
  - On Wubi, don't ask for an install device, but just update wubildr
    using the diverted grub-install.
  - When embedding the core image in a post-MBR gap, check for and avoid
    sectors matching any of a list of known signatures.
  - Disable video_bochs and video_cirrus on PC BIOS systems, as probing
    PCI space seems to break on some systems.
* Downgrade "ACPI shutdown failed" error to a debug message, since it can
  cause spurious test failures.

[ Evan Broder ]
* Enable lua from grub-extras.
* Incorporate the bitop library into lua.
* Add enum_pci function to grub module in lua.
* Switch back to gfxpayload=keep by default, unless the video hardware
  is known to not support it.

[ Mario Limonciello ]
* Built part_msdos and vfat into bootx64.efi (LP: #677758)

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,2006,2007,2008,2009  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
#include <grub/charset.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
  grub_uint8_t unused2[32];
 
56
  grub_uint8_t uuid[16];
 
57
};
 
58
 
 
59
struct grub_jfs_extent
 
60
{
 
61
  /* The length of the extent in filesystem blocks.  */
 
62
  grub_uint16_t length;
 
63
  grub_uint8_t length2;
 
64
 
 
65
  /* The physical offset of the first block on the disk.  */
 
66
  grub_uint8_t blk1;
 
67
  grub_uint32_t blk2;
 
68
} __attribute__ ((packed));
 
69
 
 
70
struct grub_jfs_iag
 
71
{
 
72
  grub_uint8_t unused[3072];
 
73
  struct grub_jfs_extent inodes[128];
 
74
} __attribute__ ((packed));
 
75
 
 
76
 
 
77
/* The head of the tree used to find extents.  */
 
78
struct grub_jfs_treehead
 
79
{
 
80
  grub_uint64_t next;
 
81
  grub_uint64_t prev;
 
82
 
 
83
  grub_uint8_t flags;
 
84
  grub_uint8_t unused;
 
85
 
 
86
  grub_uint16_t count;
 
87
  grub_uint16_t max;
 
88
  grub_uint8_t unused2[10];
 
89
} __attribute__ ((packed));
 
90
 
 
91
/* A node in the extent tree.  */
 
92
struct grub_jfs_tree_extent
 
93
{
 
94
  grub_uint8_t flags;
 
95
  grub_uint16_t unused;
 
96
 
 
97
  /* The offset is the key used to lookup an extent.  */
 
98
  grub_uint8_t offset1;
 
99
  grub_uint32_t offset2;
 
100
 
 
101
  struct grub_jfs_extent extent;
 
102
} __attribute__ ((packed));
 
103
 
 
104
/* The tree of directory entries.  */
 
105
struct grub_jfs_tree_dir
 
106
{
 
107
  /* Pointers to the previous and next tree headers of other nodes on
 
108
     this level.  */
 
109
  grub_uint64_t nextb;
 
110
  grub_uint64_t prevb;
 
111
 
 
112
  grub_uint8_t flags;
 
113
 
 
114
  /* The amount of dirents in this node.  */
 
115
  grub_uint8_t count;
 
116
  grub_uint8_t freecnt;
 
117
  grub_uint8_t freelist;
 
118
  grub_uint8_t maxslot;
 
119
 
 
120
  /* The location of the sorted array of pointers to dirents.  */
 
121
  grub_uint8_t sindex;
 
122
  grub_uint8_t unused[10];
 
123
} __attribute__ ((packed));
 
124
 
 
125
/* An internal node in the dirents tree.  */
 
126
struct grub_jfs_internal_dirent
 
127
{
 
128
  struct grub_jfs_extent ex;
 
129
  grub_uint8_t next;
 
130
  grub_uint8_t len;
 
131
  grub_uint16_t namepart[11];
 
132
} __attribute__ ((packed));
 
133
 
 
134
/* A leaf node in the dirents tree.  */
 
135
struct grub_jfs_leaf_dirent
 
136
{
 
137
  /* The inode for this dirent.  */
 
138
  grub_uint32_t inode;
 
139
  grub_uint8_t next;
 
140
 
 
141
  /* The size of the name.  */
 
142
  grub_uint8_t len;
 
143
  grub_uint16_t namepart[11];
 
144
  grub_uint32_t index;
 
145
} __attribute__ ((packed));
 
146
 
 
147
/* A leaf in the dirents tree.  This one is used if the previously
 
148
   dirent was not big enough to store the name.  */
 
149
struct grub_jfs_leaf_next_dirent
 
150
{
 
151
  grub_uint8_t next;
 
152
  grub_uint8_t len;
 
153
  grub_uint16_t namepart[15];
 
154
} __attribute__ ((packed));
 
155
 
 
156
struct grub_jfs_inode
 
157
{
 
158
  grub_uint32_t stamp;
 
159
  grub_uint32_t fileset;
 
160
  grub_uint32_t inode;
 
161
  grub_uint8_t unused[12];
 
162
  grub_uint64_t size;
 
163
  grub_uint8_t unused2[20];
 
164
  grub_uint32_t mode;
 
165
  grub_uint8_t unused3[72];
 
166
  grub_uint8_t unused4[96];
 
167
 
 
168
  union
 
169
  {
 
170
    /* The tree describing the extents of the file.  */
 
171
    struct __attribute__ ((packed))
 
172
    {
 
173
      struct grub_jfs_treehead tree;
 
174
      struct grub_jfs_tree_extent extents[16];
 
175
    } file;
 
176
    union
 
177
    {
 
178
      /* The tree describing the dirents.  */
 
179
      struct
 
180
      {
 
181
        grub_uint8_t unused[16];
 
182
        grub_uint8_t flags;
 
183
 
 
184
        /* Amount of dirents in this node.  */
 
185
        grub_uint8_t count;
 
186
        grub_uint8_t freecnt;
 
187
        grub_uint8_t freelist;
 
188
        grub_uint32_t idotdot;
 
189
        grub_uint8_t sorted[8];
 
190
      } header;
 
191
      struct grub_jfs_leaf_dirent dirents[8];
 
192
    } dir __attribute__ ((packed));
 
193
    /* Fast symlink.  */
 
194
    struct
 
195
    {
 
196
      grub_uint8_t unused[32];
 
197
      grub_uint8_t path[128];
 
198
    } symlink;
 
199
  } __attribute__ ((packed));
 
200
} __attribute__ ((packed));
 
201
 
 
202
struct grub_jfs_data
 
203
{
 
204
  struct grub_jfs_sblock sblock;
 
205
  grub_disk_t disk;
 
206
  struct grub_jfs_inode fileset;
 
207
  struct grub_jfs_inode currinode;
 
208
  int pos;
 
209
  int linknest;
 
210
} __attribute__ ((packed));
 
211
 
 
212
struct grub_jfs_diropen
 
213
{
 
214
  int index;
 
215
  union
 
216
  {
 
217
    struct grub_jfs_tree_dir header;
 
218
    struct grub_jfs_leaf_dirent dirent[0];
 
219
    struct grub_jfs_leaf_next_dirent next_dirent[0];
 
220
    char sorted[0];
 
221
  } *dirpage __attribute__ ((packed));
 
222
  struct grub_jfs_data *data;
 
223
  struct grub_jfs_inode *inode;
 
224
  int count;
 
225
  char *sorted;
 
226
  struct grub_jfs_leaf_dirent *leaf;
 
227
  struct grub_jfs_leaf_next_dirent *next_leaf;
 
228
 
 
229
  /* The filename and inode of the last read dirent.  */
 
230
  char name[255];
 
231
  grub_uint32_t ino;
 
232
} __attribute__ ((packed));
 
233
 
 
234
 
 
235
static grub_dl_t my_mod;
 
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), &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), 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), &data->sblock))
 
343
    goto fail;
 
344
 
 
345
  if (grub_strncmp ((char *) (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), &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_zalloc (sizeof (struct grub_jfs_diropen));
 
389
  if (!diro)
 
390
    return 0;
 
391
 
 
392
  diro->data = data;
 
393
  diro->inode = inode;
 
394
 
 
395
  /* Check if the entire tree is contained within the inode.  */
 
396
  if (inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
 
397
    {
 
398
      diro->leaf = inode->dir.dirents;
 
399
      diro->next_leaf = (struct grub_jfs_leaf_next_dirent *) de;
 
400
      diro->sorted = (char *) (inode->dir.header.sorted);
 
401
      diro->count = inode->dir.header.count;
 
402
 
 
403
      return diro;
 
404
    }
 
405
 
 
406
  diro->dirpage = grub_malloc (grub_le_to_cpu32 (data->sblock.blksz));
 
407
  if (!diro->dirpage)
 
408
    {
 
409
      grub_free (diro);
 
410
      return 0;
 
411
    }
 
412
 
 
413
  blk = grub_le_to_cpu32 (de[inode->dir.header.sorted[0]].ex.blk2);
 
414
  blk <<= (grub_le_to_cpu16 (data->sblock.log2_blksz) - GRUB_DISK_SECTOR_BITS);
 
415
 
 
416
  /* Read in the nodes until we are on the leaf node level.  */
 
417
  do
 
418
    {
 
419
      int index;
 
420
      if (grub_disk_read (data->disk, blk, 0,
 
421
                          grub_le_to_cpu32 (data->sblock.blksz),
 
422
                          diro->dirpage->sorted))
 
423
        {
 
424
          grub_free (diro->dirpage);
 
425
          grub_free (diro);
 
426
          return 0;
 
427
        }
 
428
 
 
429
      de = (struct grub_jfs_internal_dirent *) diro->dirpage->dirent;
 
430
      index = diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
 
431
      blk = (grub_le_to_cpu32 (de[index].ex.blk2)
 
432
             << (grub_le_to_cpu16 (data->sblock.log2_blksz)
 
433
                 - GRUB_DISK_SECTOR_BITS));
 
434
    } while (!(diro->dirpage->header.flags & GRUB_JFS_TREE_LEAF));
 
435
 
 
436
  diro->leaf = diro->dirpage->dirent;
 
437
  diro->next_leaf = diro->dirpage->next_dirent;
 
438
  diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
 
439
  diro->count = diro->dirpage->header.count;
 
440
 
 
441
  return diro;
 
442
}
 
443
 
 
444
 
 
445
static void
 
446
grub_jfs_closedir (struct grub_jfs_diropen *diro)
 
447
{
 
448
  if (!diro)
 
449
    return;
 
450
  grub_free (diro->dirpage);
 
451
  grub_free (diro);
 
452
}
 
453
 
 
454
 
 
455
/* Read in the next dirent from the directory described by DIRO.  */
 
456
static grub_err_t
 
457
grub_jfs_getent (struct grub_jfs_diropen *diro)
 
458
{
 
459
  int strpos = 0;
 
460
  struct grub_jfs_leaf_dirent *leaf;
 
461
  struct grub_jfs_leaf_next_dirent *next_leaf;
 
462
  int len;
 
463
  int nextent;
 
464
  grub_uint16_t filename[255];
 
465
 
 
466
  auto void addstr (grub_uint16_t *uname, int ulen);
 
467
 
 
468
  /* Add the unicode string to the utf16 filename buffer.  */
 
469
  void addstr (grub_uint16_t *name, int ulen)
 
470
    {
 
471
      while (ulen--)
 
472
        filename[strpos++] = *(name++);
 
473
    }
 
474
 
 
475
  /* The last node, read in more.  */
 
476
  if (diro->index == diro->count)
 
477
    {
 
478
      unsigned int next;
 
479
 
 
480
      /* If the inode contains the entry tree or if this was the last
 
481
         node, there is nothing to read.  */
 
482
      if ((diro->inode->file.tree.flags & GRUB_JFS_TREE_LEAF)
 
483
          || !grub_le_to_cpu64 (diro->dirpage->header.nextb))
 
484
        return GRUB_ERR_OUT_OF_RANGE;
 
485
 
 
486
      next = grub_le_to_cpu64 (diro->dirpage->header.nextb);
 
487
      next <<= (grub_le_to_cpu16 (diro->data->sblock.log2_blksz)
 
488
                - GRUB_DISK_SECTOR_BITS);
 
489
 
 
490
      if (grub_disk_read (diro->data->disk, next, 0,
 
491
                          grub_le_to_cpu32 (diro->data->sblock.blksz),
 
492
                          diro->dirpage->sorted))
 
493
        return grub_errno;
 
494
 
 
495
      diro->leaf = diro->dirpage->dirent;
 
496
      diro->next_leaf = diro->dirpage->next_dirent;
 
497
      diro->sorted = &diro->dirpage->sorted[diro->dirpage->header.sindex * 32];
 
498
      diro->count = diro->dirpage->header.count;
 
499
      diro->index = 0;
 
500
    }
 
501
 
 
502
  leaf = &diro->leaf[(int) diro->sorted[diro->index]];
 
503
  next_leaf = &diro->next_leaf[diro->index];
 
504
 
 
505
  len = leaf->len;
 
506
  if (!len)
 
507
    {
 
508
      diro->index++;
 
509
      return grub_jfs_getent (diro);
 
510
    }
 
511
 
 
512
  addstr (leaf->namepart, len < 11 ? len : 11);
 
513
  diro->ino = grub_le_to_cpu32 (leaf->inode);
 
514
  len -= 11;
 
515
 
 
516
  /* Move down to the leaf level.  */
 
517
  nextent = leaf->next;
 
518
  if (leaf->next != 255)
 
519
    do
 
520
      {
 
521
        next_leaf = &diro->next_leaf[nextent];
 
522
        addstr (next_leaf->namepart, len < 15 ? len : 15 );
 
523
 
 
524
        len -= 15;
 
525
        nextent = next_leaf->next;
 
526
      } while (next_leaf->next != 255 && len > 0);
 
527
 
 
528
  diro->index++;
 
529
 
 
530
  /* Convert the temporary UTF16 filename to UTF8.  */
 
531
  *grub_utf16_to_utf8 ((grub_uint8_t *) (diro->name), filename, strpos) = '\0';
 
532
 
 
533
  return 0;
 
534
}
 
535
 
 
536
 
 
537
/* Read LEN bytes from the file described by DATA starting with byte
 
538
   POS.  Return the amount of read bytes in READ.  */
 
539
static grub_ssize_t
 
540
grub_jfs_read_file (struct grub_jfs_data *data,
 
541
                    void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
 
542
                                       unsigned offset, unsigned length),
 
543
                    int pos, grub_size_t len, char *buf)
 
544
{
 
545
  int i;
 
546
  int blockcnt;
 
547
 
 
548
  blockcnt = ((len + pos + grub_le_to_cpu32 (data->sblock.blksz) - 1)
 
549
              / grub_le_to_cpu32 (data->sblock.blksz));
 
550
 
 
551
  for (i = pos / grub_le_to_cpu32 (data->sblock.blksz); i < blockcnt; i++)
 
552
    {
 
553
      int blknr;
 
554
      int blockoff = pos % grub_le_to_cpu32 (data->sblock.blksz);
 
555
      int blockend = grub_le_to_cpu32 (data->sblock.blksz);
 
556
 
 
557
      int skipfirst = 0;
 
558
 
 
559
      blknr = grub_jfs_blkno (data, &data->currinode, i);
 
560
      if (grub_errno)
 
561
        return -1;
 
562
 
 
563
      /* Last block.  */
 
564
      if (i == blockcnt - 1)
 
565
        {
 
566
          blockend = (len + pos) % grub_le_to_cpu32 (data->sblock.blksz);
 
567
 
 
568
          if (!blockend)
 
569
            blockend = grub_le_to_cpu32 (data->sblock.blksz);
 
570
        }
 
571
 
 
572
      /* First block.  */
 
573
      if (i == (pos / (int) grub_le_to_cpu32 (data->sblock.blksz)))
 
574
        {
 
575
          skipfirst = blockoff;
 
576
          blockend -= skipfirst;
 
577
        }
 
578
 
 
579
      data->disk->read_hook = read_hook;
 
580
      grub_disk_read (data->disk,
 
581
                      blknr << (grub_le_to_cpu16 (data->sblock.log2_blksz)
 
582
                                - GRUB_DISK_SECTOR_BITS),
 
583
                      skipfirst, blockend, buf);
 
584
 
 
585
      data->disk->read_hook = 0;
 
586
      if (grub_errno)
 
587
        return -1;
 
588
 
 
589
      buf += grub_le_to_cpu32 (data->sblock.blksz) - skipfirst;
 
590
    }
 
591
 
 
592
  return len;
 
593
}
 
594
 
 
595
 
 
596
/* Find the file with the pathname PATH on the filesystem described by
 
597
   DATA.  */
 
598
static grub_err_t
 
599
grub_jfs_find_file (struct grub_jfs_data *data, const char *path)
 
600
{
 
601
  char fpath[grub_strlen (path)];
 
602
  char *name = fpath;
 
603
  char *next;
 
604
  struct grub_jfs_diropen *diro;
 
605
 
 
606
  grub_strncpy (fpath, path, grub_strlen (path) + 1);
 
607
 
 
608
  if (grub_jfs_read_inode (data, GRUB_JFS_AGGR_INODE, &data->currinode))
 
609
    return grub_errno;
 
610
 
 
611
  /* Skip the first slashes.  */
 
612
  while (*name == '/')
 
613
    {
 
614
      name++;
 
615
      if (!*name)
 
616
        return 0;
 
617
    }
 
618
 
 
619
  /* Extract the actual part from the pathname.  */
 
620
  next = grub_strchr (name, '/');
 
621
  if (next)
 
622
    {
 
623
      while (*next == '/')
 
624
        {
 
625
          next[0] = '\0';
 
626
          next++;
 
627
        }
 
628
    }
 
629
  diro = grub_jfs_opendir (data, &data->currinode);
 
630
  if (!diro)
 
631
    return grub_errno;
 
632
 
 
633
  for (;;)
 
634
    {
 
635
      if (grub_strlen (name) == 0)
 
636
        return GRUB_ERR_NONE;
 
637
 
 
638
      if (grub_jfs_getent (diro) == GRUB_ERR_OUT_OF_RANGE)
 
639
        break;
 
640
 
 
641
      /* Check if the current direntry matches the current part of the
 
642
         pathname.  */
 
643
      if (!grub_strcmp (name, diro->name))
 
644
        {
 
645
          int ino = diro->ino;
 
646
          int dirino = grub_le_to_cpu32 (data->currinode.inode);
 
647
 
 
648
          grub_jfs_closedir (diro);
 
649
          diro = 0;
 
650
 
 
651
          if (grub_jfs_read_inode (data, ino, &data->currinode))
 
652
            break;
 
653
 
 
654
          /* Check if this is a symlink.  */
 
655
          if ((grub_le_to_cpu32 (data->currinode.mode)
 
656
               & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_LNK)
 
657
            {
 
658
              grub_jfs_lookup_symlink (data, dirino);
 
659
              if (grub_errno)
 
660
                return grub_errno;
 
661
            }
 
662
 
 
663
          if (!next)
 
664
            return 0;
 
665
 
 
666
          name = next;
 
667
          next = grub_strchr (name, '/');
 
668
          if (next)
 
669
            {
 
670
              next[0] = '\0';
 
671
              next++;
 
672
            }
 
673
 
 
674
          /* Open this directory for reading dirents.  */
 
675
          diro = grub_jfs_opendir (data, &data->currinode);
 
676
          if (!diro)
 
677
            return grub_errno;
 
678
 
 
679
          continue;
 
680
        }
 
681
    }
 
682
 
 
683
  grub_jfs_closedir (diro);
 
684
  grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
 
685
  return grub_errno;
 
686
}
 
687
 
 
688
 
 
689
static grub_err_t
 
690
grub_jfs_lookup_symlink (struct grub_jfs_data *data, int ino)
 
691
{
 
692
  int size = grub_le_to_cpu64 (data->currinode.size);
 
693
  char symlink[size + 1];
 
694
 
 
695
  if (++data->linknest > GRUB_JFS_MAX_SYMLNK_CNT)
 
696
    return grub_error (GRUB_ERR_SYMLINK_LOOP, "too deep nesting of symlinks");
 
697
 
 
698
  if (size <= 128)
 
699
    grub_strncpy (symlink, (char *) (data->currinode.symlink.path), 128);
 
700
  else if (grub_jfs_read_file (data, 0, 0, size, symlink) < 0)
 
701
    return grub_errno;
 
702
 
 
703
  symlink[size] = '\0';
 
704
 
 
705
  /* The symlink is an absolute path, go back to the root inode.  */
 
706
  if (symlink[0] == '/')
 
707
    ino = 2;
 
708
 
 
709
  /* Now load in the old inode.  */
 
710
  if (grub_jfs_read_inode (data, ino, &data->currinode))
 
711
    return grub_errno;
 
712
 
 
713
  grub_jfs_find_file (data, symlink);
 
714
  if (grub_errno)
 
715
    grub_error (grub_errno, "cannot follow symlink `%s'", symlink);
 
716
 
 
717
  return grub_errno;
 
718
}
 
719
 
 
720
 
 
721
static grub_err_t
 
722
grub_jfs_dir (grub_device_t device, const char *path,
 
723
              int (*hook) (const char *filename,
 
724
                           const struct grub_dirhook_info *info))
 
725
{
 
726
  struct grub_jfs_data *data = 0;
 
727
  struct grub_jfs_diropen *diro = 0;
 
728
 
 
729
  grub_dl_ref (my_mod);
 
730
 
 
731
  data = grub_jfs_mount (device->disk);
 
732
  if (!data)
 
733
    goto fail;
 
734
 
 
735
  if (grub_jfs_find_file (data, path))
 
736
    goto fail;
 
737
 
 
738
  diro = grub_jfs_opendir (data, &data->currinode);
 
739
  if (!diro)
 
740
    goto fail;
 
741
 
 
742
  /* Iterate over the dirents in the directory that was found.  */
 
743
  while (grub_jfs_getent (diro) != GRUB_ERR_OUT_OF_RANGE)
 
744
    {
 
745
      struct grub_jfs_inode inode;
 
746
      struct grub_dirhook_info info;
 
747
      grub_memset (&info, 0, sizeof (info));
 
748
 
 
749
      if (grub_jfs_read_inode (data, diro->ino, &inode))
 
750
        goto fail;
 
751
 
 
752
      info.dir = (grub_le_to_cpu32 (inode.mode)
 
753
                  & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_DIR;
 
754
      if (hook (diro->name, &info))
 
755
        goto fail;
 
756
    }
 
757
 
 
758
  /* XXX: GRUB_ERR_OUT_OF_RANGE is used for the last dirent.  */
 
759
  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
 
760
    grub_errno = 0;
 
761
 
 
762
 fail:
 
763
  grub_jfs_closedir (diro);
 
764
  grub_free (data);
 
765
 
 
766
  grub_dl_unref (my_mod);
 
767
 
 
768
  return grub_errno;
 
769
}
 
770
 
 
771
 
 
772
/* Open a file named NAME and initialize FILE.  */
 
773
static grub_err_t
 
774
grub_jfs_open (struct grub_file *file, const char *name)
 
775
{
 
776
  struct grub_jfs_data *data;
 
777
 
 
778
  grub_dl_ref (my_mod);
 
779
 
 
780
  data = grub_jfs_mount (file->device->disk);
 
781
  if (!data)
 
782
    goto fail;
 
783
 
 
784
  grub_jfs_find_file (data, name);
 
785
  if (grub_errno)
 
786
    goto fail;
 
787
 
 
788
  /* It is only possible for open regular files.  */
 
789
  if (! ((grub_le_to_cpu32 (data->currinode.mode)
 
790
          & GRUB_JFS_FILETYPE_MASK) == GRUB_JFS_FILETYPE_REG))
 
791
    {
 
792
      grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a regular file");
 
793
      goto fail;
 
794
    }
 
795
 
 
796
  file->data = data;
 
797
  file->size = grub_le_to_cpu64 (data->currinode.size);
 
798
 
 
799
  return 0;
 
800
 
 
801
 fail:
 
802
 
 
803
  grub_dl_unref (my_mod);
 
804
 
 
805
  grub_free (data);
 
806
 
 
807
  return grub_errno;
 
808
}
 
809
 
 
810
 
 
811
static grub_ssize_t
 
812
grub_jfs_read (grub_file_t file, char *buf, grub_size_t len)
 
813
{
 
814
  struct grub_jfs_data *data =
 
815
    (struct grub_jfs_data *) file->data;
 
816
 
 
817
  return grub_jfs_read_file (data, file->read_hook, file->offset, len, buf);
 
818
}
 
819
 
 
820
 
 
821
static grub_err_t
 
822
grub_jfs_close (grub_file_t file)
 
823
{
 
824
  grub_free (file->data);
 
825
 
 
826
  grub_dl_unref (my_mod);
 
827
 
 
828
  return GRUB_ERR_NONE;
 
829
}
 
830
 
 
831
static grub_err_t
 
832
grub_jfs_uuid (grub_device_t device, char **uuid)
 
833
{
 
834
  struct grub_jfs_data *data;
 
835
  grub_disk_t disk = device->disk;
 
836
 
 
837
  grub_dl_ref (my_mod);
 
838
 
 
839
  data = grub_jfs_mount (disk);
 
840
  if (data)
 
841
    {
 
842
      *uuid = grub_xasprintf ("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-"
 
843
                             "%02x%02x%02x%02x%02x%02x",
 
844
                             data->sblock.uuid[0], data->sblock.uuid[1],
 
845
                             data->sblock.uuid[2], data->sblock.uuid[3],
 
846
                             data->sblock.uuid[4], data->sblock.uuid[5],
 
847
                             data->sblock.uuid[6], data->sblock.uuid[7],
 
848
                             data->sblock.uuid[8], data->sblock.uuid[9],
 
849
                             data->sblock.uuid[10], data->sblock.uuid[11],
 
850
                             data->sblock.uuid[12], data->sblock.uuid[13],
 
851
                             data->sblock.uuid[14], data->sblock.uuid[15]);
 
852
    }
 
853
  else
 
854
    *uuid = NULL;
 
855
 
 
856
  grub_dl_unref (my_mod);
 
857
 
 
858
  grub_free (data);
 
859
 
 
860
  return grub_errno;
 
861
}
 
862
 
 
863
static grub_err_t
 
864
grub_jfs_label (grub_device_t device, char **label)
 
865
{
 
866
  struct grub_jfs_data *data;
 
867
  data = grub_jfs_mount (device->disk);
 
868
 
 
869
  if (data)
 
870
    *label = grub_strndup ((char *) (data->sblock.volname), 11);
 
871
  else
 
872
    *label = 0;
 
873
 
 
874
  return grub_errno;
 
875
}
 
876
 
 
877
 
 
878
static struct grub_fs grub_jfs_fs =
 
879
  {
 
880
    .name = "jfs",
 
881
    .dir = grub_jfs_dir,
 
882
    .open = grub_jfs_open,
 
883
    .read = grub_jfs_read,
 
884
    .close = grub_jfs_close,
 
885
    .label = grub_jfs_label,
 
886
    .uuid = grub_jfs_uuid,
 
887
    .next = 0
 
888
  };
 
889
 
 
890
GRUB_MOD_INIT(jfs)
 
891
{
 
892
  grub_fs_register (&grub_jfs_fs);
 
893
  my_mod = mod;
 
894
}
 
895
 
 
896
GRUB_MOD_FINI(jfs)
 
897
{
 
898
  grub_fs_unregister (&grub_jfs_fs);
 
899
}