~ubuntu-branches/ubuntu/lucid/grub2/lucid

« back to all changes in this revision

Viewing changes to fs/hfs.c

  • Committer: Bazaar Package Importer
  • Author(s): Otavio Salvador
  • Date: 2006-01-05 15:20:40 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20060105152040-1ab076d4n3y2o5yf
Tags: 1.92-1
* New upstream release.
  - Add support for GPT partition table format.
  - Add a new command "play" to play an audio file on PC.
  - Add support for Linux/ADFS partition table format.
  - Add support for BASH-like scripting.
  - Add support for Apple HFS+ filesystems.
* 01_fix_grub-install.patch: Added. Fix grub-install to use
  /bin/grub-mkimage instead of /sbin/grub-mkimage. Closes: #338824
* Do not use CDBS tarball mode anymore. Closes: #344272  

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* hfs.c - HFS.  */
 
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_HFS_SBLOCK         2
 
30
#define GRUB_HFS_MAGIC          0x4244
 
31
 
 
32
#define GRUB_HFS_BLKS           (data->blksz >> 9)
 
33
 
 
34
#define GRUB_HFS_NODE_LEAF      0xFF
 
35
 
 
36
/* The two supported filesystems a record can have.  */
 
37
enum
 
38
  {
 
39
    GRUB_HFS_FILETYPE_DIR = 1,
 
40
    GRUB_HFS_FILETYPE_FILE = 2
 
41
  };
 
42
 
 
43
/* A single extent.  A file consists of suchs extents.  */
 
44
struct grub_hfs_extent
 
45
{
 
46
  /* The first physical block.  */
 
47
  grub_uint16_t first_block;
 
48
  grub_uint16_t count;
 
49
};
 
50
 
 
51
/* HFS stores extents in groups of 3.  */
 
52
typedef struct grub_hfs_extent grub_hfs_datarecord_t[3];
 
53
 
 
54
/* The HFS superblock (The official name is `Master Directory
 
55
   Block').  */
 
56
struct grub_hfs_sblock
 
57
{
 
58
  grub_uint16_t magic;
 
59
  grub_uint8_t unused[18];
 
60
  grub_uint32_t blksz;
 
61
  grub_uint8_t unused2[4];
 
62
  grub_uint16_t first_block;
 
63
  grub_uint8_t unused4[6];
 
64
 
 
65
  /* A pascal style string that holds the volumename.  */
 
66
  grub_uint8_t volname[28];
 
67
  
 
68
  grub_uint8_t unused5[70];
 
69
  grub_hfs_datarecord_t extent_recs;
 
70
  grub_uint32_t catalog_size;
 
71
  grub_hfs_datarecord_t catalog_recs;
 
72
} __attribute__ ((packed));
 
73
 
 
74
/* A node desciptor.  This is the header of every node.  */
 
75
struct grub_hfs_node
 
76
{
 
77
  grub_uint32_t next;
 
78
  grub_uint32_t prev;
 
79
  grub_uint8_t type;
 
80
  grub_uint8_t level;
 
81
  grub_uint16_t reccnt;
 
82
  grub_uint16_t unused;
 
83
} __attribute__ ((packed));
 
84
 
 
85
/* The head of the B*-Tree.  */
 
86
struct grub_hfs_treeheader
 
87
{
 
88
  grub_uint16_t tree_depth;
 
89
  /* The number of the first node.  */
 
90
  grub_uint32_t root_node;
 
91
  grub_uint32_t leaves;
 
92
  grub_uint32_t first_leaf;
 
93
  grub_uint32_t last_leaf;
 
94
  grub_uint16_t node_size;
 
95
  grub_uint16_t key_size;
 
96
  grub_uint32_t nodes;
 
97
  grub_uint32_t free_nodes;
 
98
  grub_uint8_t unused[76];
 
99
} __attribute__ ((packed));
 
100
 
 
101
/* The state of a mounted HFS filesystem.  */
 
102
struct grub_hfs_data
 
103
{
 
104
  struct grub_hfs_sblock sblock;
 
105
  grub_disk_t disk;
 
106
  grub_hfs_datarecord_t extents;
 
107
  int fileid;
 
108
  int size;
 
109
  int ext_root;
 
110
  int ext_size;
 
111
  int cat_root;
 
112
  int cat_size;
 
113
  int blksz;
 
114
  int log2_blksz;
 
115
  int rootdir;
 
116
};
 
117
 
 
118
/* The key as used on disk in a catalog tree.  This is used to lookup
 
119
   file/directory nodes by parent directory ID and filename.  */
 
120
struct grub_hfs_catalog_key
 
121
{
 
122
  grub_uint8_t unused;
 
123
  grub_uint32_t parent_dir;
 
124
  
 
125
  /* Filename length.  */
 
126
  grub_uint8_t strlen;
 
127
 
 
128
  /* Filename.  */
 
129
  grub_uint8_t str[31];
 
130
} __attribute__ ((packed));
 
131
 
 
132
/* The key as used on disk in a extent overflow tree.  Using this key
 
133
   the extents can be looked up using a fileid and logical start block
 
134
   as index.  */
 
135
struct grub_hfs_extent_key
 
136
{
 
137
  /* The kind of fork.  This is used to store meta information like
 
138
     icons, attributes, etc.  We will only use the datafork, which is
 
139
     0.  */
 
140
  grub_uint8_t forktype;
 
141
  grub_uint32_t fileid;
 
142
  grub_uint16_t first_block;
 
143
} __attribute__ ((packed));
 
144
 
 
145
/* A dirrect record.  This is used to find out the directory ID.  */
 
146
struct grub_hfs_dirrec
 
147
{
 
148
  /* For a directory, type == 1.  */
 
149
  grub_uint8_t type;
 
150
  grub_uint8_t unused[5];
 
151
  grub_uint32_t dirid;
 
152
} __attribute__ ((packed));
 
153
 
 
154
/* Information about a file.  */
 
155
struct grub_hfs_filerec
 
156
{
 
157
  /* For a file, type == 2.  */
 
158
  grub_uint8_t type;
 
159
  grub_uint8_t unused[19];
 
160
  grub_uint32_t fileid;
 
161
  grub_uint8_t unused2[2];
 
162
  grub_uint32_t size;
 
163
  grub_uint8_t unused3[44];
 
164
 
 
165
  /* The first 3 extents of the file.  The other extents can be found
 
166
     in the extent overflow file.  */
 
167
  grub_hfs_datarecord_t extents;
 
168
} __attribute__ ((packed));
 
169
 
 
170
/* A record descriptor, both key and data, used to pass to call back
 
171
   functions.  */
 
172
struct grub_hfs_record
 
173
{
 
174
  void *key;
 
175
  int keylen;
 
176
  void *data;
 
177
  int datalen;
 
178
};
 
179
 
 
180
#ifndef GRUB_UTIL
 
181
static grub_dl_t my_mod;
 
182
#endif
 
183
 
 
184
static int grub_hfs_find_node (struct grub_hfs_data *, char *,
 
185
                               grub_uint32_t, int, char *, int);
 
186
 
 
187
/* Find block BLOCK of the file FILE in the mounted UFS filesystem
 
188
   DATA.  The first 3 extents are described by DAT.  If cache is set,
 
189
   using caching to improve non-random reads.  */
 
190
static unsigned int
 
191
grub_hfs_block (struct grub_hfs_data *data, grub_hfs_datarecord_t dat,
 
192
                int file, int block, int cache)
 
193
{
 
194
  grub_hfs_datarecord_t dr;
 
195
  int pos = 0;
 
196
  struct grub_hfs_extent_key key;
 
197
  
 
198
  int tree = 0;
 
199
  static int cache_file = 0;
 
200
  static int cache_pos = 0;  
 
201
  static grub_hfs_datarecord_t cache_dr;
 
202
  
 
203
  grub_memcpy (dr, dat, sizeof (dr));
 
204
  
 
205
  key.forktype = 0;
 
206
  key.fileid = grub_cpu_to_be32 (file);
 
207
  
 
208
  if (cache && cache_file == file  && block > cache_pos)
 
209
    {
 
210
      pos = cache_pos;
 
211
      key.first_block = grub_cpu_to_be16 (pos);
 
212
      grub_memcpy (dr, cache_dr, sizeof (cache_dr));
 
213
    }
 
214
  
 
215
  for (;;)
 
216
    {
 
217
      int i;
 
218
      
 
219
      /* Try all 3 extents.  */
 
220
      for (i = 0; i < 3; i++)
 
221
        {
 
222
          /* Check if the block is stored in this extent.  */
 
223
          if (grub_be_to_cpu16 (dr[i].count) + pos > block)
 
224
            {
 
225
              int first = grub_be_to_cpu16 (dr[i].first_block);
 
226
              
 
227
              /* If the cache is enabled, store the current position
 
228
                 in the tree.  */
 
229
              if (tree && cache)
 
230
                {
 
231
                  cache_file = file;
 
232
                  cache_pos = pos;
 
233
                  grub_memcpy (cache_dr, dr, sizeof (cache_dr));
 
234
                }
 
235
              
 
236
              return (grub_be_to_cpu16 (data->sblock.first_block)
 
237
                      + (first + block - pos) * GRUB_HFS_BLKS);
 
238
            }
 
239
          
 
240
          /* Try the next extent.  */
 
241
          pos += grub_be_to_cpu16 (dr[i].count);
 
242
        }
 
243
      
 
244
      /* Lookup the block in the extent overflow file.  */
 
245
      key.first_block = grub_cpu_to_be16 (pos);
 
246
      tree = 1;
 
247
      grub_hfs_find_node (data, (char *) &key, data->ext_root,
 
248
                          1, (char *) &dr, sizeof (dr));
 
249
      if (grub_errno)
 
250
        return 0;
 
251
    }
 
252
}
 
253
 
 
254
 
 
255
/* Read LEN bytes from the file described by DATA starting with byte
 
256
   POS.  Return the amount of read bytes in READ.  */
 
257
static grub_ssize_t
 
258
grub_hfs_read_file (struct grub_hfs_data *data,
 
259
                    void (*read_hook) (unsigned long sector,
 
260
                                       unsigned offset, unsigned length),
 
261
                     int pos, unsigned int len, char *buf)
 
262
{
 
263
  int i;
 
264
  int blockcnt;
 
265
 
 
266
  /* Adjust len so it we can't read past the end of the file.  */
 
267
  if (len > grub_le_to_cpu32 (data->size))
 
268
    len = grub_le_to_cpu32 (data->size);
 
269
 
 
270
  blockcnt = ((len + pos) 
 
271
              + data->blksz - 1) / data->blksz;
 
272
 
 
273
  for (i = pos / data->blksz; i < blockcnt; i++)
 
274
    {
 
275
      int blknr;
 
276
      int blockoff = pos % data->blksz;
 
277
      int blockend = data->blksz;
 
278
 
 
279
      int skipfirst = 0;
 
280
      
 
281
      blknr = grub_hfs_block (data, data->extents, data->fileid, i, 1);
 
282
      if (grub_errno)
 
283
        return -1;
 
284
      
 
285
      /* Last block.  */
 
286
      if (i == blockcnt - 1)
 
287
        {
 
288
          blockend = (len + pos) % data->blksz;
 
289
          
 
290
          /* The last portion is exactly EXT2_BLOCK_SIZE (data).  */
 
291
          if (!blockend)
 
292
            blockend = data->blksz;
 
293
        }
 
294
 
 
295
      /* First block.  */
 
296
      if (i == pos / data->blksz)
 
297
        {
 
298
          skipfirst = blockoff;
 
299
          blockend -= skipfirst;
 
300
        }
 
301
 
 
302
      /* If the block number is 0 this block is not stored on disk but
 
303
         is zero filled instead.  */
 
304
      if (blknr)
 
305
        {
 
306
          data->disk->read_hook = read_hook;      
 
307
          grub_disk_read (data->disk, blknr, skipfirst,
 
308
                          blockend, buf);
 
309
          data->disk->read_hook = 0;
 
310
          if (grub_errno)
 
311
            return -1;
 
312
        }
 
313
      
 
314
      buf += data->blksz - skipfirst;
 
315
    }
 
316
  
 
317
  return len;
 
318
}
 
319
 
 
320
 
 
321
/* Mount the filesystem on the disk DISK.  */
 
322
static struct grub_hfs_data *
 
323
grub_hfs_mount (grub_disk_t disk)
 
324
{
 
325
  struct grub_hfs_data *data;
 
326
  struct grub_hfs_catalog_key key;
 
327
  struct grub_hfs_dirrec dir;
 
328
  int first_block;
 
329
  
 
330
  struct
 
331
  {
 
332
    struct grub_hfs_node node;
 
333
    struct grub_hfs_treeheader head;
 
334
  } treehead;
 
335
  
 
336
  data = grub_malloc (sizeof (struct grub_hfs_data));
 
337
  if (!data)
 
338
    return 0;
 
339
 
 
340
  /* Read the superblock.  */
 
341
  if (grub_disk_read (disk, GRUB_HFS_SBLOCK, 0,
 
342
                      sizeof (struct grub_hfs_sblock), (char *) &data->sblock))
 
343
    goto fail;
 
344
  
 
345
  /* Check if this is a HFS filesystem.  */
 
346
  if (grub_be_to_cpu16 (data->sblock.magic) != GRUB_HFS_MAGIC)
 
347
    {
 
348
      grub_error (GRUB_ERR_BAD_FS, "not a hfs filesystem");
 
349
      goto fail;
 
350
    }
 
351
  
 
352
  data->blksz = grub_be_to_cpu32 (data->sblock.blksz);
 
353
  data->disk = disk;
 
354
  
 
355
  /* Lookup the root node of the extent overflow tree.  */
 
356
  first_block = ((grub_be_to_cpu16 (data->sblock.extent_recs[0].first_block) 
 
357
                  * GRUB_HFS_BLKS)
 
358
                 + grub_be_to_cpu16 (data->sblock.first_block));
 
359
  
 
360
  if (grub_disk_read (data->disk, first_block, 0,
 
361
                      sizeof (treehead), (char *)  &treehead))
 
362
    goto fail;
 
363
  data->ext_root = grub_be_to_cpu32 (treehead.head.root_node);
 
364
  data->ext_size = grub_be_to_cpu16 (treehead.head.node_size);
 
365
  
 
366
  /* Lookup the root node of the catalog tree.  */
 
367
  first_block = ((grub_be_to_cpu16 (data->sblock.catalog_recs[0].first_block) 
 
368
                  * GRUB_HFS_BLKS)
 
369
                 + grub_be_to_cpu16 (data->sblock.first_block));
 
370
  if (grub_disk_read (data->disk, first_block, 0,
 
371
                      sizeof (treehead), (char *)  &treehead))
 
372
    goto fail;
 
373
  data->cat_root = grub_be_to_cpu32 (treehead.head.root_node);
 
374
  data->cat_size = grub_be_to_cpu16 (treehead.head.node_size);
 
375
  
 
376
  /* Lookup the root directory node in the catalog tree using the
 
377
     volume name.  */
 
378
  key.parent_dir = grub_cpu_to_be32 (1);
 
379
  key.strlen = data->sblock.volname[0];
 
380
  grub_strcpy (key.str, data->sblock.volname + 1);
 
381
  
 
382
  if (grub_hfs_find_node (data, (char *) &key, data->cat_root,
 
383
                          0, (char *) &dir, sizeof (dir)) == 0)
 
384
    {
 
385
      grub_error (GRUB_ERR_BAD_FS, "can not find the hfs root directory");
 
386
      goto fail;
 
387
    }
 
388
    
 
389
  if (grub_errno)
 
390
    goto fail;
 
391
 
 
392
  data->rootdir = grub_be_to_cpu32 (dir.dirid);
 
393
  
 
394
  return data;
 
395
 fail:
 
396
  grub_free (data);
 
397
  
 
398
  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
 
399
    grub_error (GRUB_ERR_BAD_FS, "not a hfs filesystem");
 
400
  
 
401
  return 0;
 
402
}
 
403
 
 
404
 
 
405
/* Compare the K1 and K2 catalog file keys.  */
 
406
static int
 
407
grub_hfs_cmp_catkeys (struct grub_hfs_catalog_key *k1,
 
408
                      struct grub_hfs_catalog_key *k2)
 
409
{
 
410
  int cmp = (grub_be_to_cpu32 (k1->parent_dir)
 
411
             - grub_be_to_cpu32 (k2->parent_dir));
 
412
  
 
413
  if (cmp != 0)
 
414
    return cmp;
 
415
  
 
416
  cmp = grub_strncasecmp (k1->str, k2->str, k1->strlen);
 
417
  
 
418
  /* This is required because the compared strings are not of equal
 
419
     length.  */
 
420
  if (cmp == 0 && k1->strlen < k2->strlen)
 
421
    return -1;
 
422
  return cmp;
 
423
}
 
424
 
 
425
 
 
426
/* Compare the K1 and K2 extent overflow file keys.  */
 
427
static int
 
428
grub_hfs_cmp_extkeys (struct grub_hfs_extent_key *k1,
 
429
                      struct grub_hfs_extent_key *k2)
 
430
{
 
431
  int cmp = k1->forktype - k2->forktype;
 
432
  if (cmp == 0)
 
433
    cmp = grub_be_to_cpu32 (k1->fileid) - grub_be_to_cpu32 (k2->fileid);
 
434
  if (cmp == 0)
 
435
    cmp = (grub_be_to_cpu16 (k1->first_block) 
 
436
           - grub_be_to_cpu16 (k2->first_block));
 
437
  return cmp;
 
438
}
 
439
 
 
440
 
 
441
/* Iterate the records in the node with index IDX in the mounted HFS
 
442
   filesystem DATA.  This node holds data of the type TYPE (0 =
 
443
   catalog node, 1 = extent overflow node).  If this is set, continue
 
444
   iterating to the next node.  For every records, call NODE_HOOK.  */
 
445
static grub_err_t
 
446
grub_hfs_iterate_records (struct grub_hfs_data *data, int type, int idx,
 
447
                          int this, int (*node_hook) (struct grub_hfs_node *hnd,
 
448
                                                      struct grub_hfs_record *))
 
449
{
 
450
  int nodesize = type == 0 ? data->cat_size : data->ext_size;
 
451
  
 
452
  union
 
453
  {
 
454
    struct grub_hfs_node node;
 
455
    char rawnode[nodesize];
 
456
    grub_uint16_t offsets[nodesize / 2];
 
457
  } node;
 
458
  
 
459
  do
 
460
    {
 
461
      int i;
 
462
      struct grub_hfs_extent *dat;
 
463
      int blk;
 
464
      
 
465
      dat = (struct grub_hfs_extent *) (type == 0 
 
466
                                        ? (&data->sblock.catalog_recs)
 
467
                                        : (&data->sblock.extent_recs));
 
468
      
 
469
      /* Read the node into memory.  */
 
470
      blk = grub_hfs_block (data, dat,
 
471
                            0, idx / (data->blksz / nodesize), 0);
 
472
      blk += (idx % (data->blksz / nodesize));
 
473
      if (grub_errno)
 
474
        return grub_errno;
 
475
      
 
476
      if (grub_disk_read (data->disk, blk, 0,
 
477
                          sizeof (node), (char *)  &node))
 
478
        return grub_errno;
 
479
      
 
480
      /* Iterate over all records in this node.  */
 
481
      for (i = 0; i < grub_be_to_cpu16 (node.node.reccnt); i++)
 
482
        {
 
483
          int pos = (nodesize >> 1) - 1 - i;
 
484
          struct pointer
 
485
          {
 
486
            grub_uint8_t keylen;
 
487
            grub_uint8_t key;
 
488
          } __attribute__ ((packed)) *pnt;
 
489
          pnt = (struct pointer *) (grub_be_to_cpu16 (node.offsets[pos])
 
490
                                    + node.rawnode);
 
491
          
 
492
          struct grub_hfs_record rec = 
 
493
            {
 
494
              &pnt->key,
 
495
              pnt->keylen,
 
496
              &pnt->key + pnt->keylen +(pnt->keylen + 1) % 2,
 
497
              nodesize - grub_be_to_cpu16 (node.offsets[pos]) 
 
498
              - pnt->keylen - 1
 
499
            };
 
500
          
 
501
          if (node_hook (&node.node, &rec))
 
502
            return 0;
 
503
        }
 
504
      
 
505
      if (idx % (data->blksz / nodesize) == 0)
 
506
        idx = grub_be_to_cpu32 (node.node.next);
 
507
      else
 
508
        idx++;
 
509
    } while (idx && this);
 
510
  
 
511
  return 0;
 
512
}
 
513
 
 
514
 
 
515
/* Lookup a record in the mounted filesystem DATA using the key KEY.
 
516
   The index of the node on top of the tree is IDX.  The tree is of
 
517
   the type TYPE (0 = catalog node, 1 = extent overflow node).  Return
 
518
   the data in DATAR with a maximum length of DATALEN.  */
 
519
static int
 
520
grub_hfs_find_node (struct grub_hfs_data *data, char *key,
 
521
                    grub_uint32_t idx, int type, char *datar, int datalen)
 
522
{
 
523
  int found = -1;
 
524
  int isleaf = 0;
 
525
  
 
526
  auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
 
527
    
 
528
  int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
 
529
    {
 
530
      int cmp = 1;
 
531
      
 
532
      if (type == 0)
 
533
        cmp = grub_hfs_cmp_catkeys (rec->key, (void *) key);
 
534
      else
 
535
        cmp = grub_hfs_cmp_extkeys (rec->key, (void *) key);
 
536
      
 
537
      /* If the key is smaller or equal to the currect node, mark the
 
538
         entry.  In case of a non-leaf mode it will be used to lookup
 
539
         the rest of the tree.  */
 
540
      if (cmp <= 0)
 
541
        {
 
542
          grub_uint32_t *node = (grub_uint32_t *) rec->data;
 
543
          found = grub_be_to_cpu32 (*node);
 
544
        }
 
545
      else /* The key can not be found in the tree. */
 
546
        return 1;
 
547
      
 
548
      /* Check if this node is a leaf node.  */
 
549
      if (hnd->type == GRUB_HFS_NODE_LEAF)
 
550
        {
 
551
          isleaf = 1;
 
552
          
 
553
          /* Found it!!!!  */
 
554
          if (cmp == 0)
 
555
            {
 
556
              grub_memcpy (datar, rec->data,
 
557
                           rec->datalen < datalen ? rec->datalen : datalen);
 
558
              return 1;
 
559
            }
 
560
        }
 
561
      
 
562
      return 0;
 
563
    }
 
564
  
 
565
  if (grub_hfs_iterate_records (data, type, idx, 0, node_found))
 
566
    return 0;
 
567
  
 
568
  if (found == -1)
 
569
    return 0;
 
570
 
 
571
  if (isleaf)
 
572
    return 1;
 
573
  
 
574
  return grub_hfs_find_node (data, key, found, type, datar, datalen);
 
575
}
 
576
 
 
577
 
 
578
/* Iterate over the directory with the id DIR.  The tree is searched
 
579
   starting with the node ROOT_IDX.  For every entry in this directory
 
580
   call HOOK.  */
 
581
static grub_err_t
 
582
grub_hfs_iterate_dir (struct grub_hfs_data *data, grub_uint32_t root_idx,
 
583
                      unsigned int dir, int (*hook) (struct grub_hfs_record *))
 
584
{
 
585
  int found = -1;
 
586
  int isleaf = 0;
 
587
  int next = 0;
 
588
  
 
589
  /* The lowest key possible with DIR as root directory.  */
 
590
  struct grub_hfs_catalog_key key = {0, grub_cpu_to_be32 (dir), 0, ""};
 
591
 
 
592
  auto int node_found (struct grub_hfs_node *, struct grub_hfs_record *);
 
593
  auto int it_dir (struct grub_hfs_node * __attribute ((unused)),
 
594
                   struct grub_hfs_record *);
 
595
 
 
596
  
 
597
  int node_found (struct grub_hfs_node *hnd, struct grub_hfs_record *rec)
 
598
    {
 
599
      struct grub_hfs_catalog_key *ckey = rec->key;
 
600
      
 
601
      if (grub_hfs_cmp_catkeys (rec->key, (void *) &key) <= 0)
 
602
        found = grub_be_to_cpu32 (*(grub_uint32_t *) rec->data);
 
603
      
 
604
      if (hnd->type == 0xFF && ckey->strlen > 0)
 
605
        {
 
606
          isleaf = 1;
 
607
          next = grub_be_to_cpu32 (hnd->next);
 
608
          
 
609
          /* An entry was found.  */
 
610
          if (grub_be_to_cpu32 (ckey->parent_dir) == dir)
 
611
            return hook (rec);
 
612
        }
 
613
      
 
614
      return 0;
 
615
    }
 
616
  
 
617
  int it_dir (struct grub_hfs_node *hnd __attribute ((unused)),
 
618
              struct grub_hfs_record *rec)
 
619
    {
 
620
      struct grub_hfs_catalog_key *ckey = rec->key;
 
621
      struct grub_hfs_catalog_key *origkey = &key;
 
622
      
 
623
      /* Stop when the entries do not match anymore.  */
 
624
      if (grub_be_to_cpu32 (ckey->parent_dir) 
 
625
          != grub_be_to_cpu32 ((origkey)->parent_dir))
 
626
        return 1;
 
627
      
 
628
      return hook (rec);
 
629
    }
 
630
  
 
631
  if (grub_hfs_iterate_records (data, 0, root_idx, 0, node_found))
 
632
    return grub_errno;
 
633
  
 
634
  if (found == -1)
 
635
    return 0;
 
636
  
 
637
  /* If there was a matching record in this leaf node, continue the
 
638
     iteration until the last record was found.  */
 
639
  if (isleaf)
 
640
    {
 
641
      grub_hfs_iterate_records (data, 0, next, 1, it_dir);
 
642
      return grub_errno;
 
643
    }
 
644
  
 
645
  return grub_hfs_iterate_dir (data, found, dir, hook);
 
646
}
 
647
 
 
648
 
 
649
/* Find a file or directory with the pathname PATH in the filesystem
 
650
   DATA.  Return the file record in RETDATA when it is non-zero.
 
651
   Return the directory number in RETINODE when it is non-zero.  */
 
652
static grub_err_t
 
653
grub_hfs_find_dir (struct grub_hfs_data *data, const char *path,
 
654
                   struct grub_hfs_filerec *retdata, int *retinode)
 
655
{
 
656
  int inode = data->rootdir;
 
657
  char *next;
 
658
  char *origpath;
 
659
  struct grub_hfs_filerec frec;
 
660
  struct grub_hfs_dirrec *dir = (struct grub_hfs_dirrec *) &frec;
 
661
  frec.type = GRUB_HFS_FILETYPE_DIR;
 
662
  
 
663
  if (path[0] != '/')
 
664
    {
 
665
      grub_error (GRUB_ERR_BAD_FILENAME, "bad filename");
 
666
      return 0;
 
667
    }
 
668
  
 
669
  origpath = grub_strdup (path);
 
670
  if (!origpath)
 
671
    return grub_errno;
 
672
  
 
673
  path = origpath;
 
674
  path++;
 
675
  
 
676
  while (path && grub_strlen (path))
 
677
    {
 
678
      if (frec.type != GRUB_HFS_FILETYPE_DIR)
 
679
        {
 
680
          grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
 
681
          goto fail;
 
682
        }
 
683
      
 
684
      /* Isolate a part of the path.  */
 
685
      next = grub_strchr (path, '/');
 
686
      if (next)
 
687
        {
 
688
          next[0] = '\0';
 
689
          next++;
 
690
        }
 
691
      
 
692
      struct grub_hfs_catalog_key key;
 
693
      
 
694
      key.parent_dir = grub_cpu_to_be32 (inode);
 
695
      key.strlen = grub_strlen (path);
 
696
      grub_strcpy (key.str, path);
 
697
      
 
698
      /* Lookup this node.  */
 
699
      if (!grub_hfs_find_node (data, (char *) &key, data->cat_root,
 
700
                               0, (char *) &frec, sizeof (frec)))
 
701
        {
 
702
          grub_error (GRUB_ERR_FILE_NOT_FOUND, "file not found");
 
703
          goto fail;
 
704
        }
 
705
 
 
706
      if (grub_errno)
 
707
        goto fail;
 
708
      
 
709
      inode = grub_be_to_cpu32 (dir->dirid);
 
710
      path = next;
 
711
    }
 
712
 
 
713
  if (retdata)
 
714
    grub_memcpy (retdata, &frec, sizeof (frec));
 
715
  
 
716
  if (retinode)
 
717
    *retinode = inode;
 
718
  
 
719
 fail:
 
720
  grub_free (origpath);
 
721
  return grub_errno;
 
722
}
 
723
 
 
724
 
 
725
 
 
726
static grub_err_t
 
727
grub_hfs_dir (grub_device_t device, const char *path, 
 
728
                  int (*hook) (const char *filename, int dir))
 
729
{
 
730
  int inode;
 
731
 
 
732
  auto int dir_hook (struct grub_hfs_record *rec);
 
733
 
 
734
  int dir_hook (struct grub_hfs_record *rec)
 
735
    {
 
736
      char fname[32] = { 0 };
 
737
      char *filetype = rec->data;
 
738
      struct grub_hfs_catalog_key *ckey = rec->key;
 
739
      
 
740
      grub_strncpy (fname, ckey->str, ckey->strlen);
 
741
      
 
742
      if (*filetype == GRUB_HFS_FILETYPE_DIR)
 
743
        return hook (fname, 1);
 
744
      else if (*filetype == GRUB_HFS_FILETYPE_FILE)
 
745
        return hook (fname, 0);
 
746
      return 0;
 
747
    }
 
748
  
 
749
  struct grub_hfs_data *data;
 
750
  struct grub_hfs_filerec frec;
 
751
 
 
752
#ifndef GRUB_UTIL
 
753
  grub_dl_ref (my_mod);
 
754
#endif
 
755
  
 
756
  data = grub_hfs_mount (device->disk);
 
757
  if (!data)
 
758
    goto fail;
 
759
  
 
760
  /* First the directory ID for the directory.  */
 
761
  if (grub_hfs_find_dir (data, path, &frec, &inode))
 
762
    goto fail;
 
763
 
 
764
  if (frec.type != GRUB_HFS_FILETYPE_DIR)
 
765
    {
 
766
      grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a directory");
 
767
      goto fail;
 
768
    }
 
769
  
 
770
  grub_hfs_iterate_dir (data, data->cat_root, inode, dir_hook);
 
771
  
 
772
 fail:
 
773
  grub_free (data);
 
774
 
 
775
#ifndef GRUB_UTIL
 
776
  grub_dl_unref (my_mod);
 
777
#endif
 
778
  
 
779
  return grub_errno;
 
780
}
 
781
 
 
782
 
 
783
/* Open a file named NAME and initialize FILE.  */
 
784
static grub_err_t
 
785
grub_hfs_open (struct grub_file *file, const char *name)
 
786
{
 
787
  struct grub_hfs_data *data;
 
788
  struct grub_hfs_filerec frec;
 
789
  
 
790
#ifndef GRUB_UTIL
 
791
  grub_dl_ref (my_mod);
 
792
#endif
 
793
 
 
794
  data = grub_hfs_mount (file->device->disk);
 
795
  
 
796
  if (grub_hfs_find_dir (data, name, &frec, 0))
 
797
    {
 
798
      grub_free (data);
 
799
#ifndef GRUB_UTIL
 
800
  grub_dl_unref (my_mod);
 
801
#endif
 
802
      return grub_errno;
 
803
    }
 
804
  
 
805
  if (frec.type != GRUB_HFS_FILETYPE_FILE)
 
806
    {
 
807
      grub_free (data);
 
808
      grub_error (GRUB_ERR_BAD_FILE_TYPE, "not a file");
 
809
#ifndef GRUB_UTIL
 
810
      grub_dl_unref (my_mod);
 
811
#endif
 
812
      return grub_errno;
 
813
    }
 
814
  
 
815
  grub_memcpy (data->extents, frec.extents, sizeof (grub_hfs_datarecord_t));
 
816
  file->size = grub_be_to_cpu32 (frec.size);
 
817
  data->size = grub_be_to_cpu32 (frec.size);
 
818
  data->fileid = grub_be_to_cpu32 (frec.fileid);
 
819
  file->offset = 0;
 
820
 
 
821
  file->data = data;
 
822
  
 
823
  return 0;
 
824
}
 
825
 
 
826
static grub_ssize_t
 
827
grub_hfs_read (grub_file_t file, char *buf, grub_ssize_t len)
 
828
{
 
829
  struct grub_hfs_data *data = 
 
830
    (struct grub_hfs_data *) file->data;
 
831
  
 
832
  return grub_hfs_read_file (data, file->read_hook, file->offset, len, buf);
 
833
}
 
834
 
 
835
 
 
836
static grub_err_t
 
837
grub_hfs_close (grub_file_t file)
 
838
{
 
839
  grub_free (file->data);
 
840
 
 
841
#ifndef GRUB_UTIL
 
842
  grub_dl_unref (my_mod);
 
843
#endif
 
844
 
 
845
  return 0;
 
846
}
 
847
 
 
848
 
 
849
static grub_err_t
 
850
grub_hfs_label (grub_device_t device, char **label)
 
851
{
 
852
  struct grub_hfs_data *data;
 
853
 
 
854
  data = grub_hfs_mount (device->disk);
 
855
  
 
856
  if (data)
 
857
    *label = grub_strndup (data->sblock.volname + 1, *data->sblock.volname);
 
858
  else
 
859
    *label = 0;
 
860
 
 
861
  grub_free (data);
 
862
  return grub_errno;
 
863
}
 
864
 
 
865
 
 
866
 
 
867
static struct grub_fs grub_hfs_fs =
 
868
  {
 
869
    .name = "hfs",
 
870
    .dir = grub_hfs_dir,
 
871
    .open = grub_hfs_open,
 
872
    .read = grub_hfs_read,
 
873
    .close = grub_hfs_close,
 
874
    .label = grub_hfs_label,
 
875
    .next = 0
 
876
  };
 
877
 
 
878
GRUB_MOD_INIT(hfs)
 
879
{
 
880
  grub_fs_register (&grub_hfs_fs);
 
881
#ifndef GRUB_UTIL
 
882
  my_mod = mod;
 
883
#endif
 
884
}
 
885
 
 
886
GRUB_MOD_FINI(hfs)
 
887
{
 
888
  grub_fs_unregister (&grub_hfs_fs);
 
889
}