~hamo/ubuntu/precise/grub2/grub2.hi_res

« back to all changes in this revision

Viewing changes to fs/hfsplus.c

  • Committer: Bazaar Package Importer
  • Author(s): Colin Watson, Colin Watson, Robert Millan, Updated translations
  • Date: 2010-11-22 12:24:56 UTC
  • mfrom: (1.26.4 upstream) (17.3.36 sid)
  • mto: (17.3.43 sid)
  • mto: This revision was merged to the branch mainline in revision 89.
  • Revision ID: james.westby@ubuntu.com-20101122122456-y82z3sfb7k4zfdcc
Tags: 1.99~20101122-1
[ Colin Watson ]
* New Bazaar snapshot.  Too many changes to list in full, but some of the
  more user-visible ones are as follows:
  - GRUB script:
    + Function parameters, "break", "continue", "shift", "setparams",
      "return", and "!".
    + "export" command supports multiple variable names.
    + Multi-line quoted strings support.
    + Wildcard expansion.
  - sendkey support.
  - USB hotunplugging and USB serial support.
  - Rename CD-ROM to cd on BIOS.
  - Add new --boot-directory option to grub-install, grub-reboot, and
    grub-set-default; the old --root-directory option is still accepted
    but was often confusing.
  - Basic btrfs detection/UUID support (but no file reading yet).
  - bash-completion for utilities.
  - If a device is listed in device.map, always assume that it is
    BIOS-visible rather than using extra layers such as LVM or RAID.
  - Add grub-mknetdir script (closes: #550658).
  - Remove deprecated "root" command.
  - Handle RAID devices containing virtio components.
  - GRUB Legacy configuration file support (via grub-menulst2cfg).
  - Keyboard layout support (via grub-mklayout and grub-kbdcomp).
  - Check generated grub.cfg for syntax errors before saving.
  - Pause execution for at most ten seconds if any errors are displayed,
    so that the user has a chance to see them.
  - Support submenus.
  - Write embedding zone using Reed-Solomon, so that it's robust against
    being partially overwritten (closes: #550702, #591416, #593347).
  - GRUB_DISABLE_LINUX_RECOVERY and GRUB_DISABLE_NETBSD_RECOVERY merged
    into a single GRUB_DISABLE_RECOVERY variable.
  - Fix loader memory allocation failure (closes: #551627).
  - Don't call savedefault on recovery entries (closes: #589325).
  - Support triple-indirect blocks on ext2 (closes: #543924).
  - Recognise DDF1 fake RAID (closes: #603354).

[ Robert Millan ]
* Use dpkg architecture wildcards.

[ Updated translations ]
* Slovenian (Vanja Cvelbar).  Closes: #604003
* Dzongkha (dawa pemo via Tenzin Dendup).  Closes: #604102

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* hfsplus.c - HFS+ Filesystem.  */
2
 
/*
3
 
 *  GRUB  --  GRand Unified Bootloader
4
 
 *  Copyright (C) 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
 
/* HFS+ is documented at http://developer.apple.com/technotes/tn/tn1150.html */
21
 
 
22
 
#include <grub/err.h>
23
 
#include <grub/file.h>
24
 
#include <grub/mm.h>
25
 
#include <grub/misc.h>
26
 
#include <grub/disk.h>
27
 
#include <grub/dl.h>
28
 
#include <grub/types.h>
29
 
#include <grub/fshelp.h>
30
 
#include <grub/hfs.h>
31
 
#include <grub/charset.h>
32
 
 
33
 
#define GRUB_HFSPLUS_MAGIC 0x482B
34
 
#define GRUB_HFSPLUSX_MAGIC 0x4858
35
 
#define GRUB_HFSPLUS_SBLOCK 2
36
 
 
37
 
/* A HFS+ extent.  */
38
 
struct grub_hfsplus_extent
39
 
{
40
 
  /* The first block of a file on disk.  */
41
 
  grub_uint32_t start;
42
 
  /* The amount of blocks described by this extent.  */
43
 
  grub_uint32_t count;
44
 
} __attribute__ ((packed));
45
 
 
46
 
/* The descriptor of a fork.  */
47
 
struct grub_hfsplus_forkdata
48
 
{
49
 
  grub_uint64_t size;
50
 
  grub_uint32_t clumpsize;
51
 
  grub_uint32_t blocks;
52
 
  struct grub_hfsplus_extent extents[8];
53
 
} __attribute__ ((packed));
54
 
 
55
 
/* The HFS+ Volume Header.  */
56
 
struct grub_hfsplus_volheader
57
 
{
58
 
  grub_uint16_t magic;
59
 
  grub_uint16_t version;
60
 
  grub_uint32_t attributes;
61
 
  grub_uint8_t unused1[12];
62
 
  grub_uint32_t utime;
63
 
  grub_uint8_t unused2[16];
64
 
  grub_uint32_t blksize;
65
 
  grub_uint8_t unused3[60];
66
 
  grub_uint64_t num_serial;
67
 
  struct grub_hfsplus_forkdata allocations_file;
68
 
  struct grub_hfsplus_forkdata extents_file;
69
 
  struct grub_hfsplus_forkdata catalog_file;
70
 
  struct grub_hfsplus_forkdata attrib_file;
71
 
  struct grub_hfsplus_forkdata startup_file;
72
 
} __attribute__ ((packed));
73
 
 
74
 
/* The type of node.  */
75
 
enum grub_hfsplus_btnode_type
76
 
  {
77
 
    GRUB_HFSPLUS_BTNODE_TYPE_LEAF = -1,
78
 
    GRUB_HFSPLUS_BTNODE_TYPE_INDEX = 0,
79
 
    GRUB_HFSPLUS_BTNODE_TYPE_HEADER = 1,
80
 
    GRUB_HFSPLUS_BTNODE_TYPE_MAP = 2,
81
 
  };
82
 
 
83
 
struct grub_hfsplus_btnode
84
 
{
85
 
  grub_uint32_t next;
86
 
  grub_uint32_t prev;
87
 
  grub_int8_t type;
88
 
  grub_uint8_t height;
89
 
  grub_uint16_t count;
90
 
  grub_uint16_t unused;
91
 
} __attribute__ ((packed));
92
 
 
93
 
/* The header of a HFS+ B+ Tree.  */
94
 
struct grub_hfsplus_btheader
95
 
{
96
 
  grub_uint16_t depth;
97
 
  grub_uint32_t root;
98
 
  grub_uint32_t leaf_records;
99
 
  grub_uint32_t first_leaf_node;
100
 
  grub_uint32_t last_leaf_node;
101
 
  grub_uint16_t nodesize;
102
 
  grub_uint16_t keysize;
103
 
  grub_uint32_t total_nodes;
104
 
  grub_uint32_t free_nodes;
105
 
  grub_uint16_t reserved1;
106
 
  grub_uint32_t clump_size;  /* ignored */
107
 
  grub_uint8_t btree_type;
108
 
  grub_uint8_t key_compare;
109
 
  grub_uint32_t attributes;
110
 
} __attribute__ ((packed));
111
 
 
112
 
/* The on disk layout of a catalog key.  */
113
 
struct grub_hfsplus_catkey
114
 
{
115
 
  grub_uint16_t keylen;
116
 
  grub_uint32_t parent;
117
 
  grub_uint16_t namelen;
118
 
  grub_uint16_t name[30];
119
 
} __attribute__ ((packed));
120
 
 
121
 
/* The on disk layout of an extent overflow file key.  */
122
 
struct grub_hfsplus_extkey
123
 
{
124
 
  grub_uint16_t keylen;
125
 
  grub_uint8_t type;
126
 
  grub_uint8_t unused;
127
 
  grub_uint32_t fileid;
128
 
  grub_uint32_t start;
129
 
} __attribute__ ((packed));
130
 
 
131
 
struct grub_hfsplus_key
132
 
{
133
 
  union
134
 
  {
135
 
    struct grub_hfsplus_extkey extkey;
136
 
    struct grub_hfsplus_catkey catkey;
137
 
    grub_uint16_t keylen;
138
 
  };
139
 
} __attribute__ ((packed));
140
 
 
141
 
struct grub_hfsplus_catfile
142
 
{
143
 
  grub_uint16_t type;
144
 
  grub_uint16_t flags;
145
 
  grub_uint32_t reserved;
146
 
  grub_uint32_t fileid;
147
 
  grub_uint8_t unused1[4];
148
 
  grub_uint32_t mtime;
149
 
  grub_uint8_t unused2[22];
150
 
  grub_uint16_t mode;
151
 
  grub_uint8_t unused3[44];
152
 
  struct grub_hfsplus_forkdata data;
153
 
  struct grub_hfsplus_forkdata resource;
154
 
} __attribute__ ((packed));
155
 
 
156
 
/* Filetype information as used in inodes.  */
157
 
#define GRUB_HFSPLUS_FILEMODE_MASK      0170000
158
 
#define GRUB_HFSPLUS_FILEMODE_REG       0100000
159
 
#define GRUB_HFSPLUS_FILEMODE_DIRECTORY 0040000
160
 
#define GRUB_HFSPLUS_FILEMODE_SYMLINK   0120000
161
 
 
162
 
/* Some pre-defined file IDs.  */
163
 
#define GRUB_HFSPLUS_FILEID_ROOTDIR     2
164
 
#define GRUB_HFSPLUS_FILEID_OVERFLOW    3
165
 
#define GRUB_HFSPLUS_FILEID_CATALOG     4
166
 
 
167
 
enum grub_hfsplus_filetype
168
 
  {
169
 
    GRUB_HFSPLUS_FILETYPE_DIR = 1,
170
 
    GRUB_HFSPLUS_FILETYPE_REG = 2,
171
 
    GRUB_HFSPLUS_FILETYPE_DIR_THREAD = 3,
172
 
    GRUB_HFSPLUS_FILETYPE_REG_THREAD = 4
173
 
  };
174
 
 
175
 
#define GRUB_HFSPLUSX_BINARYCOMPARE     0xBC
176
 
#define GRUB_HFSPLUSX_CASEFOLDING       0xCF
177
 
 
178
 
/* Internal representation of a catalog key.  */
179
 
struct grub_hfsplus_catkey_internal
180
 
{
181
 
  int parent;
182
 
  char *name;
183
 
};
184
 
 
185
 
/* Internal representation of an extent overflow key.  */
186
 
struct grub_hfsplus_extkey_internal
187
 
{
188
 
  grub_uint32_t fileid;
189
 
  grub_uint32_t start;
190
 
};
191
 
 
192
 
struct grub_hfsplus_key_internal
193
 
{
194
 
  union
195
 
  {
196
 
    struct grub_hfsplus_extkey_internal extkey;
197
 
    struct grub_hfsplus_catkey_internal catkey;
198
 
  };
199
 
};
200
 
 
201
 
 
202
 
 
203
 
struct grub_fshelp_node
204
 
{
205
 
  struct grub_hfsplus_data *data;
206
 
  struct grub_hfsplus_extent extents[8];
207
 
  grub_uint64_t size;
208
 
  grub_uint32_t fileid;
209
 
  grub_int32_t mtime;
210
 
};
211
 
 
212
 
struct grub_hfsplus_btree
213
 
{
214
 
  grub_uint32_t root;
215
 
  int nodesize;
216
 
 
217
 
  /* Catalog file node.  */
218
 
  struct grub_fshelp_node file;
219
 
};
220
 
 
221
 
/* Information about a "mounted" HFS+ filesystem.  */
222
 
struct grub_hfsplus_data
223
 
{
224
 
  struct grub_hfsplus_volheader volheader;
225
 
  grub_disk_t disk;
226
 
 
227
 
  unsigned int log2blksize;
228
 
 
229
 
  struct grub_hfsplus_btree catalog_tree;
230
 
  struct grub_hfsplus_btree extoverflow_tree;
231
 
 
232
 
  struct grub_fshelp_node dirroot;
233
 
  struct grub_fshelp_node opened_file;
234
 
 
235
 
  /* This is the offset into the physical disk for an embedded HFS+
236
 
     filesystem (one inside a plain HFS wrapper).  */
237
 
  int embedded_offset;
238
 
  int case_sensitive;
239
 
};
240
 
 
241
 
static grub_dl_t my_mod;
242
 
 
243
 
 
244
 
/* Return the offset of the record with the index INDEX, in the node
245
 
   NODE which is part of the B+ tree BTREE.  */
246
 
static inline unsigned int
247
 
grub_hfsplus_btree_recoffset (struct grub_hfsplus_btree *btree,
248
 
                           struct grub_hfsplus_btnode *node, int index)
249
 
{
250
 
  char *cnode = (char *) node;
251
 
  grub_uint16_t *recptr;
252
 
  recptr = (grub_uint16_t *) (&cnode[btree->nodesize
253
 
                                     - index * sizeof (grub_uint16_t) - 2]);
254
 
  return grub_be_to_cpu16 (*recptr);
255
 
}
256
 
 
257
 
/* Return a pointer to the record with the index INDEX, in the node
258
 
   NODE which is part of the B+ tree BTREE.  */
259
 
static inline struct grub_hfsplus_key *
260
 
grub_hfsplus_btree_recptr (struct grub_hfsplus_btree *btree,
261
 
                           struct grub_hfsplus_btnode *node, int index)
262
 
{
263
 
  char *cnode = (char *) node;
264
 
  unsigned int offset;
265
 
  offset = grub_hfsplus_btree_recoffset (btree, node, index);
266
 
  return (struct grub_hfsplus_key *) &cnode[offset];
267
 
}
268
 
 
269
 
 
270
 
/* Find the extent that points to FILEBLOCK.  If it is not in one of
271
 
   the 8 extents described by EXTENT, return -1.  In that case set
272
 
   FILEBLOCK to the next block.  */
273
 
static int
274
 
grub_hfsplus_find_block (struct grub_hfsplus_extent *extent,
275
 
                         int *fileblock)
276
 
{
277
 
  int i;
278
 
  grub_size_t blksleft = *fileblock;
279
 
 
280
 
  /* First lookup the file in the given extents.  */
281
 
  for (i = 0; i < 8; i++)
282
 
    {
283
 
      if (blksleft < grub_be_to_cpu32 (extent[i].count))
284
 
        return grub_be_to_cpu32 (extent[i].start) + blksleft;
285
 
      blksleft -= grub_be_to_cpu32 (extent[i].count);
286
 
    }
287
 
 
288
 
  *fileblock = blksleft;
289
 
  return -1;
290
 
}
291
 
 
292
 
static grub_err_t
293
 
grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
294
 
                           struct grub_hfsplus_key_internal *key,
295
 
                           int (*compare_keys) (struct grub_hfsplus_key *keya,
296
 
                                                struct grub_hfsplus_key_internal *keyb),
297
 
                           struct grub_hfsplus_btnode **matchnode, int *keyoffset);
298
 
 
299
 
static int grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya,
300
 
                                    struct grub_hfsplus_key_internal *keyb);
301
 
 
302
 
/* Search for the block FILEBLOCK inside the file NODE.  Return the
303
 
   blocknumber of this block on disk.  */
304
 
static grub_disk_addr_t
305
 
grub_hfsplus_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
306
 
{
307
 
  struct grub_hfsplus_btnode *nnode = 0;
308
 
  int blksleft = fileblock;
309
 
  struct grub_hfsplus_extent *extents = &node->extents[0];
310
 
 
311
 
  while (1)
312
 
    {
313
 
      struct grub_hfsplus_extkey *key;
314
 
      struct grub_hfsplus_extkey_internal extoverflow;
315
 
      int blk;
316
 
      int ptr;
317
 
 
318
 
      /* Try to find this block in the current set of extents.  */
319
 
      blk = grub_hfsplus_find_block (extents, &blksleft);
320
 
 
321
 
      /* The previous iteration of this loop allocated memory.  The
322
 
         code above used this memory, it can be freed now.  */
323
 
      grub_free (nnode);
324
 
      nnode = 0;
325
 
 
326
 
      if (blk != -1)
327
 
        return (blk
328
 
                + (node->data->embedded_offset >> (node->data->log2blksize
329
 
                                                   - GRUB_DISK_SECTOR_BITS)));
330
 
 
331
 
      /* For the extent overflow file, extra extents can't be found in
332
 
         the extent overflow file.  If this happens, you found a
333
 
         bug...  */
334
 
      if (node->fileid == GRUB_HFSPLUS_FILEID_OVERFLOW)
335
 
        {
336
 
          grub_error (GRUB_ERR_READ_ERROR,
337
 
                      "extra extents found in an extend overflow file");
338
 
          break;
339
 
        }
340
 
 
341
 
      /* Set up the key to look for in the extent overflow file.  */
342
 
      extoverflow.fileid = node->fileid;
343
 
      extoverflow.start = fileblock - blksleft;
344
 
 
345
 
      if (grub_hfsplus_btree_search (&node->data->extoverflow_tree,
346
 
                                     (struct grub_hfsplus_key_internal *) &extoverflow,
347
 
                                     grub_hfsplus_cmp_extkey, &nnode, &ptr))
348
 
        {
349
 
          grub_error (GRUB_ERR_READ_ERROR,
350
 
                      "no block found for the file id 0x%x and the block offset 0x%x",
351
 
                      node->fileid, fileblock);
352
 
          break;
353
 
        }
354
 
 
355
 
      /* The extent overflow file has 8 extents right after the key.  */
356
 
      key = (struct grub_hfsplus_extkey *)
357
 
        grub_hfsplus_btree_recptr (&node->data->extoverflow_tree, nnode, ptr);
358
 
      extents = (struct grub_hfsplus_extent *) (key + 1);
359
 
 
360
 
      /* The block wasn't found.  Perhaps the next iteration will find
361
 
         it.  The last block we found is stored in BLKSLEFT now.  */
362
 
    }
363
 
 
364
 
  grub_free (nnode);
365
 
 
366
 
  /* Too bad, you lose.  */
367
 
  return -1;
368
 
}
369
 
 
370
 
 
371
 
/* Read LEN bytes from the file described by DATA starting with byte
372
 
   POS.  Return the amount of read bytes in READ.  */
373
 
static grub_ssize_t
374
 
grub_hfsplus_read_file (grub_fshelp_node_t node,
375
 
                        void NESTED_FUNC_ATTR (*read_hook) (grub_disk_addr_t sector,
376
 
                                           unsigned offset, unsigned length),
377
 
                        int pos, grub_size_t len, char *buf)
378
 
{
379
 
  return grub_fshelp_read_file (node->data->disk, node, read_hook,
380
 
                                pos, len, buf, grub_hfsplus_read_block,
381
 
                                node->size,
382
 
                                node->data->log2blksize - GRUB_DISK_SECTOR_BITS);
383
 
}
384
 
 
385
 
static struct grub_hfsplus_data *
386
 
grub_hfsplus_mount (grub_disk_t disk)
387
 
{
388
 
  struct grub_hfsplus_data *data;
389
 
  struct grub_hfsplus_btheader header;
390
 
  struct grub_hfsplus_btnode node;
391
 
  grub_uint16_t magic;
392
 
  union {
393
 
    struct grub_hfs_sblock hfs;
394
 
    struct grub_hfsplus_volheader hfsplus;
395
 
  } volheader;
396
 
 
397
 
  data = grub_malloc (sizeof (*data));
398
 
  if (!data)
399
 
    return 0;
400
 
 
401
 
  data->disk = disk;
402
 
 
403
 
  /* Read the bootblock.  */
404
 
  grub_disk_read (disk, GRUB_HFSPLUS_SBLOCK, 0, sizeof (volheader),
405
 
                  &volheader);
406
 
  if (grub_errno)
407
 
    goto fail;
408
 
 
409
 
  data->embedded_offset = 0;
410
 
  if (grub_be_to_cpu16 (volheader.hfs.magic) == GRUB_HFS_MAGIC)
411
 
    {
412
 
      int extent_start;
413
 
      int ablk_size;
414
 
      int ablk_start;
415
 
 
416
 
      /* See if there's an embedded HFS+ filesystem.  */
417
 
      if (grub_be_to_cpu16 (volheader.hfs.embed_sig) != GRUB_HFSPLUS_MAGIC)
418
 
        {
419
 
          grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem");
420
 
          goto fail;
421
 
        }
422
 
 
423
 
      /* Calculate the offset needed to translate HFS+ sector numbers.  */
424
 
      extent_start = grub_be_to_cpu16 (volheader.hfs.embed_extent.first_block);
425
 
      ablk_size = grub_be_to_cpu32 (volheader.hfs.blksz);
426
 
      ablk_start = grub_be_to_cpu16 (volheader.hfs.first_block);
427
 
      data->embedded_offset = (ablk_start
428
 
                               + extent_start
429
 
                               * (ablk_size >> GRUB_DISK_SECTOR_BITS));
430
 
 
431
 
      grub_disk_read (disk, data->embedded_offset + GRUB_HFSPLUS_SBLOCK, 0,
432
 
                      sizeof (volheader), &volheader);
433
 
      if (grub_errno)
434
 
        goto fail;
435
 
    }
436
 
 
437
 
  /* Make sure this is an HFS+ filesystem.  XXX: Do we really support
438
 
     HFX?  */
439
 
  magic = grub_be_to_cpu16 (volheader.hfsplus.magic);
440
 
  if ((magic != GRUB_HFSPLUS_MAGIC) && (magic != GRUB_HFSPLUSX_MAGIC))
441
 
    {
442
 
      grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem");
443
 
      goto fail;
444
 
    }
445
 
 
446
 
  grub_memcpy (&data->volheader, &volheader.hfsplus,
447
 
      sizeof (volheader.hfsplus));
448
 
 
449
 
  if (grub_fshelp_log2blksize (grub_be_to_cpu32 (data->volheader.blksize),
450
 
                               &data->log2blksize))
451
 
    goto fail;
452
 
 
453
 
  /* Make a new node for the catalog tree.  */
454
 
  data->catalog_tree.file.data = data;
455
 
  data->catalog_tree.file.fileid = GRUB_HFSPLUS_FILEID_CATALOG;
456
 
  grub_memcpy (&data->catalog_tree.file.extents,
457
 
               data->volheader.catalog_file.extents,
458
 
               sizeof data->volheader.catalog_file.extents);
459
 
  data->catalog_tree.file.size =
460
 
    grub_be_to_cpu64 (data->volheader.catalog_file.size);
461
 
 
462
 
  /* Make a new node for the extent overflow file.  */
463
 
  data->extoverflow_tree.file.data = data;
464
 
  data->extoverflow_tree.file.fileid = GRUB_HFSPLUS_FILEID_OVERFLOW;
465
 
  grub_memcpy (&data->extoverflow_tree.file.extents,
466
 
               data->volheader.extents_file.extents,
467
 
               sizeof data->volheader.catalog_file.extents);
468
 
 
469
 
  data->extoverflow_tree.file.size =
470
 
    grub_be_to_cpu64 (data->volheader.extents_file.size);
471
 
 
472
 
  /* Read the essential information about the trees.  */
473
 
  if (grub_hfsplus_read_file (&data->catalog_tree.file, 0,
474
 
                              sizeof (struct grub_hfsplus_btnode),
475
 
                              sizeof (header), (char *) &header) <= 0)
476
 
    goto fail;
477
 
 
478
 
  data->catalog_tree.root = grub_be_to_cpu32 (header.root);
479
 
  data->catalog_tree.nodesize = grub_be_to_cpu16 (header.nodesize);
480
 
  data->case_sensitive = ((magic == GRUB_HFSPLUSX_MAGIC) &&
481
 
                          (header.key_compare == GRUB_HFSPLUSX_BINARYCOMPARE));
482
 
 
483
 
  if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0,
484
 
                              sizeof (struct grub_hfsplus_btnode),
485
 
                              sizeof (header), (char *) &header) <= 0)
486
 
    goto fail;
487
 
 
488
 
  data->extoverflow_tree.root = grub_be_to_cpu32 (header.root);
489
 
 
490
 
  if (grub_hfsplus_read_file (&data->extoverflow_tree.file, 0, 0,
491
 
                              sizeof (node), (char *) &node) <= 0)
492
 
    goto fail;
493
 
 
494
 
  data->extoverflow_tree.root = grub_be_to_cpu32 (header.root);
495
 
  data->extoverflow_tree.nodesize = grub_be_to_cpu16 (header.nodesize);
496
 
 
497
 
  data->dirroot.data = data;
498
 
  data->dirroot.fileid = GRUB_HFSPLUS_FILEID_ROOTDIR;
499
 
 
500
 
  return data;
501
 
 
502
 
 fail:
503
 
 
504
 
  if (grub_errno == GRUB_ERR_OUT_OF_RANGE)
505
 
    grub_error (GRUB_ERR_BAD_FS, "not a HFS+ filesystem");
506
 
 
507
 
  grub_free (data);
508
 
  return 0;
509
 
}
510
 
 
511
 
/* Compare the on disk catalog key KEYA with the catalog key we are
512
 
   looking for (KEYB).  */
513
 
static int
514
 
grub_hfsplus_cmp_catkey (struct grub_hfsplus_key *keya,
515
 
                         struct grub_hfsplus_key_internal *keyb)
516
 
{
517
 
  struct grub_hfsplus_catkey *catkey_a = &keya->catkey;
518
 
  struct grub_hfsplus_catkey_internal *catkey_b = &keyb->catkey;
519
 
  char *filename;
520
 
  int i;
521
 
  int diff;
522
 
 
523
 
  diff = grub_be_to_cpu32 (catkey_a->parent) - catkey_b->parent;
524
 
  if (diff)
525
 
    return diff;
526
 
 
527
 
  /* Change the filename in keya so the endianness is correct.  */
528
 
  for (i = 0; i < grub_be_to_cpu16 (catkey_a->namelen); i++)
529
 
    catkey_a->name[i] = grub_be_to_cpu16 (catkey_a->name[i]);
530
 
 
531
 
  filename = grub_malloc (grub_be_to_cpu16 (catkey_a->namelen) + 1);
532
 
 
533
 
  if (! grub_utf16_to_utf8 ((grub_uint8_t *) filename, catkey_a->name,
534
 
                            grub_be_to_cpu16 (catkey_a->namelen)))
535
 
    return -1; /* XXX: This error never occurs, but in case it happens
536
 
                  just skip this entry.  */
537
 
 
538
 
  diff = grub_strncmp (filename, catkey_b->name,
539
 
                       grub_be_to_cpu16 (catkey_a->namelen));
540
 
 
541
 
  grub_free (filename);
542
 
 
543
 
  /* The endianness was changed to host format, change it back to
544
 
     whatever it was.  */
545
 
  for (i = 0; i < grub_be_to_cpu16 (catkey_a->namelen); i++)
546
 
    catkey_a->name[i] = grub_cpu_to_be16 (catkey_a->name[i]);
547
 
  return diff;
548
 
}
549
 
 
550
 
/* Compare the on disk extent overflow key KEYA with the extent
551
 
   overflow key we are looking for (KEYB).  */
552
 
static int
553
 
grub_hfsplus_cmp_extkey (struct grub_hfsplus_key *keya,
554
 
                         struct grub_hfsplus_key_internal *keyb)
555
 
{
556
 
  struct grub_hfsplus_extkey *extkey_a = &keya->extkey;
557
 
  struct grub_hfsplus_extkey_internal *extkey_b = &keyb->extkey;
558
 
  int diff;
559
 
 
560
 
  diff = grub_be_to_cpu32 (extkey_a->fileid) - extkey_b->fileid;
561
 
 
562
 
  if (diff)
563
 
    return diff;
564
 
 
565
 
  diff = grub_be_to_cpu32 (extkey_a->start) - extkey_b->start;
566
 
  return diff;
567
 
}
568
 
 
569
 
static char *
570
 
grub_hfsplus_read_symlink (grub_fshelp_node_t node)
571
 
{
572
 
  char *symlink;
573
 
  grub_ssize_t numread;
574
 
 
575
 
  symlink = grub_malloc (node->size + 1);
576
 
  if (!symlink)
577
 
    return 0;
578
 
 
579
 
  numread = grub_hfsplus_read_file (node, 0, 0, node->size, symlink);
580
 
  if (numread != (grub_ssize_t) node->size)
581
 
    {
582
 
      grub_free (symlink);
583
 
      return 0;
584
 
    }
585
 
  symlink[node->size] = '\0';
586
 
 
587
 
  return symlink;
588
 
}
589
 
 
590
 
static int
591
 
grub_hfsplus_btree_iterate_node (struct grub_hfsplus_btree *btree,
592
 
                                 struct grub_hfsplus_btnode *first_node,
593
 
                                 int first_rec,
594
 
                                 int (*hook) (void *record))
595
 
{
596
 
  int rec;
597
 
 
598
 
  for (;;)
599
 
    {
600
 
      char *cnode = (char *) first_node;
601
 
 
602
 
      /* Iterate over all records in this node.  */
603
 
      for (rec = first_rec; rec < grub_be_to_cpu16 (first_node->count); rec++)
604
 
        {
605
 
          if (hook (grub_hfsplus_btree_recptr (btree, first_node, rec)))
606
 
            return 1;
607
 
        }
608
 
 
609
 
      if (! first_node->next)
610
 
        break;
611
 
 
612
 
      if (grub_hfsplus_read_file (&btree->file, 0,
613
 
                                  (grub_be_to_cpu32 (first_node->next)
614
 
                                   * btree->nodesize),
615
 
                                  btree->nodesize, cnode) <= 0)
616
 
        return 1;
617
 
 
618
 
      /* Don't skip any record in the next iteration.  */
619
 
      first_rec = 0;
620
 
    }
621
 
 
622
 
  return 0;
623
 
}
624
 
 
625
 
/* Lookup the node described by KEY in the B+ Tree BTREE.  Compare
626
 
   keys using the function COMPARE_KEYS.  When a match is found,
627
 
   return the node in MATCHNODE and a pointer to the data in this node
628
 
   in KEYOFFSET.  MATCHNODE should be freed by the caller.  */
629
 
static grub_err_t
630
 
grub_hfsplus_btree_search (struct grub_hfsplus_btree *btree,
631
 
                           struct grub_hfsplus_key_internal *key,
632
 
                           int (*compare_keys) (struct grub_hfsplus_key *keya,
633
 
                                                struct grub_hfsplus_key_internal *keyb),
634
 
                           struct grub_hfsplus_btnode **matchnode, int *keyoffset)
635
 
{
636
 
  grub_uint64_t currnode;
637
 
  char *node;
638
 
  struct grub_hfsplus_btnode *nodedesc;
639
 
  int rec;
640
 
 
641
 
  node = grub_malloc (btree->nodesize);
642
 
  if (! node)
643
 
    return grub_errno;
644
 
 
645
 
  currnode = btree->root;
646
 
  while (1)
647
 
    {
648
 
      int match = 0;
649
 
 
650
 
      /* Read a node.  */
651
 
      if (grub_hfsplus_read_file (&btree->file, 0,
652
 
                                  (long)currnode * (long)btree->nodesize,
653
 
                                  btree->nodesize, (char *) node) <= 0)
654
 
        {
655
 
          grub_free (node);
656
 
          return grub_error (GRUB_ERR_BAD_FS, "couldn't read i-node");
657
 
        }
658
 
 
659
 
      nodedesc = (struct grub_hfsplus_btnode *) node;
660
 
 
661
 
      /* Find the record in this tree.  */
662
 
      for (rec = 0; rec < grub_be_to_cpu16 (nodedesc->count); rec++)
663
 
        {
664
 
          struct grub_hfsplus_key *currkey;
665
 
          currkey = grub_hfsplus_btree_recptr (btree, nodedesc, rec);
666
 
 
667
 
          /* The action that has to be taken depend on the type of
668
 
             record.  */
669
 
          if (nodedesc->type == GRUB_HFSPLUS_BTNODE_TYPE_LEAF
670
 
              && compare_keys (currkey, key) == 0)
671
 
            {
672
 
              /* An exact match was found!  */
673
 
 
674
 
              *matchnode = nodedesc;
675
 
              *keyoffset = rec;
676
 
 
677
 
              return 0;
678
 
            }
679
 
          else if (nodedesc->type == GRUB_HFSPLUS_BTNODE_TYPE_INDEX)
680
 
            {
681
 
              grub_uint32_t *pointer;
682
 
 
683
 
              /* The place where the key could have been found didn't
684
 
                 contain the key.  This means that the previous match
685
 
                 is the one that should be followed.  */
686
 
              if (compare_keys (currkey, key) > 0)
687
 
                break;
688
 
 
689
 
              /* Mark the last key which is lower or equal to the key
690
 
                 that we are looking for.  The last match that is
691
 
                 found will be used to locate the child which can
692
 
                 contain the record.  */
693
 
              pointer = (grub_uint32_t *) ((char *) currkey
694
 
                                           + grub_be_to_cpu16 (currkey->keylen)
695
 
                                           + 2);
696
 
              currnode = grub_be_to_cpu32 (*pointer);
697
 
              match = 1;
698
 
            }
699
 
        }
700
 
 
701
 
      /* No match is found, no record with this key exists in the
702
 
         tree.  */
703
 
      if (! match)
704
 
        {
705
 
          *matchnode = 0;
706
 
          grub_free (node);
707
 
          return 1;
708
 
        }
709
 
    }
710
 
}
711
 
 
712
 
static int
713
 
grub_hfsplus_iterate_dir (grub_fshelp_node_t dir,
714
 
                          int NESTED_FUNC_ATTR
715
 
                          (*hook) (const char *filename,
716
 
                                   enum grub_fshelp_filetype filetype,
717
 
                                   grub_fshelp_node_t node))
718
 
{
719
 
  int ret = 0;
720
 
 
721
 
  auto int list_nodes (void *record);
722
 
  int list_nodes (void *record)
723
 
    {
724
 
      struct grub_hfsplus_catkey *catkey;
725
 
      char *filename;
726
 
      int i;
727
 
      struct grub_fshelp_node *node;
728
 
      struct grub_hfsplus_catfile *fileinfo;
729
 
      enum grub_fshelp_filetype type = GRUB_FSHELP_UNKNOWN;
730
 
 
731
 
      catkey = (struct grub_hfsplus_catkey *) record;
732
 
 
733
 
      fileinfo =
734
 
        (struct grub_hfsplus_catfile *) ((char *) record
735
 
                                         + grub_be_to_cpu16 (catkey->keylen)
736
 
                                         + 2 + (grub_be_to_cpu16(catkey->keylen)
737
 
                                                % 2));
738
 
 
739
 
      /* Stop iterating when the last directory entry is found.  */
740
 
      if (grub_be_to_cpu32 (catkey->parent) != dir->fileid)
741
 
        return 1;
742
 
 
743
 
      /* Determine the type of the node that is found.  */
744
 
      if (grub_be_to_cpu16 (fileinfo->type) == GRUB_HFSPLUS_FILETYPE_REG)
745
 
        {
746
 
          int mode = (grub_be_to_cpu16 (fileinfo->mode)
747
 
                      & GRUB_HFSPLUS_FILEMODE_MASK);
748
 
 
749
 
          if (mode == GRUB_HFSPLUS_FILEMODE_REG)
750
 
            type = GRUB_FSHELP_REG;
751
 
          else if (mode == GRUB_HFSPLUS_FILEMODE_SYMLINK)
752
 
            type = GRUB_FSHELP_SYMLINK;
753
 
          else
754
 
            type = GRUB_FSHELP_UNKNOWN;
755
 
        }
756
 
      else if (grub_be_to_cpu16 (fileinfo->type) == GRUB_HFSPLUS_FILETYPE_DIR)
757
 
        type = GRUB_FSHELP_DIR;
758
 
 
759
 
      if (type == GRUB_FSHELP_UNKNOWN)
760
 
        return 0;
761
 
 
762
 
      /* Make sure the byte order of the UTF16 string is correct.  */
763
 
      for (i = 0; i < grub_be_to_cpu16 (catkey->namelen); i++)
764
 
        {
765
 
          catkey->name[i] = grub_be_to_cpu16 (catkey->name[i]);
766
 
 
767
 
          /* If the name is obviously invalid, skip this node.  */
768
 
          if (catkey->name[i] == 0)
769
 
            return 0;
770
 
        }
771
 
 
772
 
      filename = grub_malloc (grub_be_to_cpu16 (catkey->namelen) + 1);
773
 
      if (! filename)
774
 
        return 0;
775
 
 
776
 
      if (! grub_utf16_to_utf8 ((grub_uint8_t *) filename, catkey->name,
777
 
                                grub_be_to_cpu16 (catkey->namelen)))
778
 
        {
779
 
          grub_free (filename);
780
 
          return 0;
781
 
        }
782
 
 
783
 
      filename[grub_be_to_cpu16 (catkey->namelen)] = '\0';
784
 
 
785
 
      /* Restore the byte order to what it was previously.  */
786
 
      for (i = 0; i < grub_be_to_cpu16 (catkey->namelen); i++)
787
 
        catkey->name[i] = grub_be_to_cpu16 (catkey->name[i]);
788
 
 
789
 
      /* hfs+ is case insensitive.  */
790
 
      if (! dir->data->case_sensitive)
791
 
        type |= GRUB_FSHELP_CASE_INSENSITIVE;
792
 
 
793
 
      /* Only accept valid nodes.  */
794
 
      if (grub_strlen (filename) == grub_be_to_cpu16 (catkey->namelen))
795
 
        {
796
 
          /* A valid node is found; setup the node and call the
797
 
             callback function.  */
798
 
          node = grub_malloc (sizeof (*node));
799
 
          node->data = dir->data;
800
 
 
801
 
          grub_memcpy (node->extents, fileinfo->data.extents,
802
 
                       sizeof (node->extents));
803
 
          node->mtime = grub_be_to_cpu32 (fileinfo->mtime) - 2082844800;
804
 
          node->size = grub_be_to_cpu64 (fileinfo->data.size);
805
 
          node->fileid = grub_be_to_cpu32 (fileinfo->fileid);
806
 
 
807
 
          ret = hook (filename, type, node);
808
 
        }
809
 
 
810
 
      grub_free (filename);
811
 
 
812
 
      return ret;
813
 
    }
814
 
 
815
 
  struct grub_hfsplus_key_internal intern;
816
 
  struct grub_hfsplus_btnode *node;
817
 
  int ptr;
818
 
 
819
 
  /* Create a key that points to the first entry in the directory.  */
820
 
  intern.catkey.parent = dir->fileid;
821
 
  intern.catkey.name = "";
822
 
 
823
 
  /* First lookup the first entry.  */
824
 
  if (grub_hfsplus_btree_search (&dir->data->catalog_tree, &intern,
825
 
                                 grub_hfsplus_cmp_catkey, &node, &ptr))
826
 
    return 0;
827
 
 
828
 
  /* Iterate over all entries in this directory.  */
829
 
  grub_hfsplus_btree_iterate_node (&dir->data->catalog_tree, node, ptr,
830
 
                                   list_nodes);
831
 
 
832
 
  grub_free (node);
833
 
 
834
 
  return ret;
835
 
}
836
 
 
837
 
/* Open a file named NAME and initialize FILE.  */
838
 
static grub_err_t
839
 
grub_hfsplus_open (struct grub_file *file, const char *name)
840
 
{
841
 
  struct grub_hfsplus_data *data;
842
 
  struct grub_fshelp_node *fdiro = 0;
843
 
 
844
 
  grub_dl_ref (my_mod);
845
 
 
846
 
  data = grub_hfsplus_mount (file->device->disk);
847
 
  if (!data)
848
 
    goto fail;
849
 
 
850
 
  grub_fshelp_find_file (name, &data->dirroot, &fdiro,
851
 
                         grub_hfsplus_iterate_dir,
852
 
                         grub_hfsplus_read_symlink, GRUB_FSHELP_REG);
853
 
  if (grub_errno)
854
 
    goto fail;
855
 
 
856
 
  file->size = fdiro->size;
857
 
  data->opened_file = *fdiro;
858
 
  grub_free (fdiro);
859
 
 
860
 
  file->data = data;
861
 
  file->offset = 0;
862
 
 
863
 
  return 0;
864
 
 
865
 
 fail:
866
 
  if (data && fdiro != &data->dirroot)
867
 
    grub_free (fdiro);
868
 
  grub_free (data);
869
 
 
870
 
  grub_dl_unref (my_mod);
871
 
 
872
 
  return grub_errno;
873
 
}
874
 
 
875
 
 
876
 
static grub_err_t
877
 
grub_hfsplus_close (grub_file_t file)
878
 
{
879
 
  grub_free (file->data);
880
 
 
881
 
  grub_dl_unref (my_mod);
882
 
 
883
 
  return GRUB_ERR_NONE;
884
 
}
885
 
 
886
 
 
887
 
/* Read LEN bytes data from FILE into BUF.  */
888
 
static grub_ssize_t
889
 
grub_hfsplus_read (grub_file_t file, char *buf, grub_size_t len)
890
 
{
891
 
  struct grub_hfsplus_data *data =
892
 
    (struct grub_hfsplus_data *) file->data;
893
 
 
894
 
  int size = grub_hfsplus_read_file (&data->opened_file, file->read_hook,
895
 
                                     file->offset, len, buf);
896
 
 
897
 
  return size;
898
 
}
899
 
 
900
 
 
901
 
static grub_err_t
902
 
grub_hfsplus_dir (grub_device_t device, const char *path,
903
 
                  int (*hook) (const char *filename,
904
 
                               const struct grub_dirhook_info *info))
905
 
{
906
 
  struct grub_hfsplus_data *data = 0;
907
 
  struct grub_fshelp_node *fdiro = 0;
908
 
 
909
 
  auto int NESTED_FUNC_ATTR iterate (const char *filename,
910
 
                                     enum grub_fshelp_filetype filetype,
911
 
                                     grub_fshelp_node_t node);
912
 
 
913
 
  int NESTED_FUNC_ATTR iterate (const char *filename,
914
 
                                enum grub_fshelp_filetype filetype,
915
 
                                grub_fshelp_node_t node)
916
 
    {
917
 
      struct grub_dirhook_info info;
918
 
      grub_memset (&info, 0, sizeof (info));
919
 
      info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
920
 
      info.mtimeset = 1;
921
 
      info.mtime = node->mtime;
922
 
      info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE);
923
 
      grub_free (node);
924
 
      return hook (filename, &info);
925
 
    }
926
 
 
927
 
  grub_dl_ref (my_mod);
928
 
 
929
 
  data = grub_hfsplus_mount (device->disk);
930
 
  if (!data)
931
 
    goto fail;
932
 
 
933
 
  /* Find the directory that should be opened.  */
934
 
  grub_fshelp_find_file (path, &data->dirroot, &fdiro,
935
 
                         grub_hfsplus_iterate_dir,
936
 
                         grub_hfsplus_read_symlink, GRUB_FSHELP_DIR);
937
 
  if (grub_errno)
938
 
    goto fail;
939
 
 
940
 
  /* Iterate over all entries in this directory.  */
941
 
  grub_hfsplus_iterate_dir (fdiro, iterate);
942
 
 
943
 
 fail:
944
 
  if (data && fdiro != &data->dirroot)
945
 
    grub_free (fdiro);
946
 
  grub_free (data);
947
 
 
948
 
  grub_dl_unref (my_mod);
949
 
 
950
 
  return grub_errno;
951
 
}
952
 
 
953
 
 
954
 
static grub_err_t
955
 
grub_hfsplus_label (grub_device_t device __attribute__((unused))
956
 
                    , char **label __attribute__((unused)))
957
 
{
958
 
  /* XXX: It's not documented how to read a label.  */
959
 
  return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET,
960
 
                     "reading the label of a HFS+ "
961
 
                     "partition is not implemented");
962
 
}
963
 
 
964
 
/* Get mtime.  */
965
 
static grub_err_t
966
 
grub_hfsplus_mtime (grub_device_t device, grub_int32_t *tm)
967
 
{
968
 
  struct grub_hfsplus_data *data;
969
 
  grub_disk_t disk = device->disk;
970
 
 
971
 
  grub_dl_ref (my_mod);
972
 
 
973
 
  data = grub_hfsplus_mount (disk);
974
 
  if (!data)
975
 
    *tm = 0;
976
 
  else
977
 
    *tm = grub_be_to_cpu32 (data->volheader.utime) - 2082844800;
978
 
 
979
 
  grub_dl_unref (my_mod);
980
 
 
981
 
  grub_free (data);
982
 
 
983
 
  return grub_errno;
984
 
 
985
 
}
986
 
 
987
 
static grub_err_t
988
 
grub_hfsplus_uuid (grub_device_t device, char **uuid)
989
 
{
990
 
  struct grub_hfsplus_data *data;
991
 
  grub_disk_t disk = device->disk;
992
 
 
993
 
  grub_dl_ref (my_mod);
994
 
 
995
 
  data = grub_hfsplus_mount (disk);
996
 
  if (data)
997
 
    {
998
 
      *uuid = grub_xasprintf ("%016llx",
999
 
                             (unsigned long long)
1000
 
                             grub_be_to_cpu64 (data->volheader.num_serial));
1001
 
    }
1002
 
  else
1003
 
    *uuid = NULL;
1004
 
 
1005
 
  grub_dl_unref (my_mod);
1006
 
 
1007
 
  grub_free (data);
1008
 
 
1009
 
  return grub_errno;
1010
 
}
1011
 
 
1012
 
 
1013
 
 
1014
 
static struct grub_fs grub_hfsplus_fs =
1015
 
  {
1016
 
    .name = "hfsplus",
1017
 
    .dir = grub_hfsplus_dir,
1018
 
    .open = grub_hfsplus_open,
1019
 
    .read = grub_hfsplus_read,
1020
 
    .close = grub_hfsplus_close,
1021
 
    .label = grub_hfsplus_label,
1022
 
    .mtime = grub_hfsplus_mtime,
1023
 
    .uuid = grub_hfsplus_uuid,
1024
 
#ifdef GRUB_UTIL
1025
 
    .reserved_first_sector = 1,
1026
 
#endif
1027
 
    .next = 0
1028
 
  };
1029
 
 
1030
 
GRUB_MOD_INIT(hfsplus)
1031
 
{
1032
 
  grub_fs_register (&grub_hfsplus_fs);
1033
 
  my_mod = mod;
1034
 
}
1035
 
 
1036
 
GRUB_MOD_FINI(hfsplus)
1037
 
{
1038
 
  grub_fs_unregister (&grub_hfsplus_fs);
1039
 
}