VirtualBox

source: vbox/trunk/src/VBox/Devices/EFI/Firmware/VBoxPkg/VBoxFsDxe/fsw_hfs.c@ 59146

Last change on this file since 59146 was 59146, checked in by vboxsync, 9 years ago

EFI: Updated HFS+ driver to support hard links.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.4 KB
Line 
1/* $Id: fsw_hfs.c 59146 2015-12-16 08:53:19Z vboxsync $ */
2/** @file
3 * fsw_hfs.c - HFS file system driver code, see
4 *
5 * http://developer.apple.com/technotes/tn/tn1150.html
6 *
7 * Current limitations:
8 * - Doesn't support permissions
9 * - Complete Unicode case-insensitiveness disabled (large tables)
10 * - No links
11 * - Only supports pure HFS+ (i.e. no HFS, or HFS+ embedded to HFS)
12 */
13
14/*
15 * Copyright (C) 2010-2015 Oracle Corporation
16 *
17 * This file is part of VirtualBox Open Source Edition (OSE), as
18 * available from http://www.virtualbox.org. This file is free software;
19 * you can redistribute it and/or modify it under the terms of the GNU
20 * General Public License (GPL) as published by the Free Software
21 * Foundation, in version 2 as it comes in the "COPYING" file of the
22 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
23 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
28 * VirtualBox OSE distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 */
34
35#include "fsw_hfs.h"
36
37#ifdef HOST_POSIX
38#define DPRINT(x) printf(x)
39#define DPRINT2(x,y) printf(x,y)
40#define BP(msg) do { printf("ERROR: %s", msg); asm("int3"); } while (0)
41#elif defined DEBUG_LEVEL
42#define CONCAT(x,y) x##y
43#define DPRINT(x) Print(CONCAT(L,x))
44#define DPRINT2(x,y) Print(CONCAT(L,x), y)
45#define BP(msg) DPRINT(msg)
46#else
47#include <Library/PrintLib.h>
48#define DPRINT(x) do { } while (0)
49#define DPRINT2(x,y) do { } while (0)
50#define BP(msg) do { } while (0)
51#endif
52
53// functions
54#if 0
55void dump_str(fsw_u16* p, fsw_u32 len, int swap)
56{
57 int i;
58
59 for (i=0; i<len; i++)
60 {
61 fprintf(stderr, "%c", swap ? be16_to_cpu(p[i]) : p[i]);
62 }
63 fprintf(stderr, "\n");
64}
65#endif
66
67static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol);
68static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol);
69static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb);
70
71static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno);
72static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno);
73static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
74 struct fsw_dnode_stat *sb);
75static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
76 struct fsw_extent *extent);
77
78static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
79 struct fsw_string *lookup_name, struct fsw_hfs_dnode **child_dno);
80static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
81 struct fsw_shandle *shand, struct fsw_hfs_dnode **child_dno);
82#if 0
83static fsw_status_t fsw_hfs_read_dirrec(struct fsw_shandle *shand, struct hfs_dirrec_buffer *dirrec_buffer);
84#endif
85
86static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
87 struct fsw_string *link);
88
89//
90// Dispatch Table
91//
92
93struct fsw_fstype_table FSW_FSTYPE_TABLE_NAME(hfs) = {
94 { FSW_STRING_TYPE_ISO88591, 4, 4, "hfs" },
95 sizeof(struct fsw_hfs_volume),
96 sizeof(struct fsw_hfs_dnode),
97
98 fsw_hfs_volume_mount,
99 fsw_hfs_volume_free,
100 fsw_hfs_volume_stat,
101 fsw_hfs_dnode_fill,
102 fsw_hfs_dnode_free,
103 fsw_hfs_dnode_stat,
104 fsw_hfs_get_extent,
105 fsw_hfs_dir_lookup,
106 fsw_hfs_dir_read,
107 fsw_hfs_readlink,
108};
109
110static fsw_s32
111fsw_hfs_read_block (struct fsw_hfs_dnode * dno,
112 fsw_u32 log_bno,
113 fsw_u32 off,
114 fsw_s32 len,
115 fsw_u8 * buf)
116{
117 fsw_status_t status;
118 struct fsw_extent extent;
119 fsw_u32 phys_bno;
120 fsw_u8* buffer;
121
122 extent.log_start = log_bno;
123 status = fsw_hfs_get_extent(dno->g.vol, dno, &extent);
124 if (status)
125 return status;
126
127 phys_bno = extent.phys_start;
128 status = fsw_block_get(dno->g.vol, phys_bno, 0, (void **)&buffer);
129 if (status)
130 return status;
131
132 fsw_memcpy(buf, buffer + off, len);
133
134 fsw_block_release(dno->g.vol, phys_bno, buffer);
135
136 return FSW_SUCCESS;
137
138}
139
140/* Read data from HFS file. */
141static fsw_s32
142fsw_hfs_read_file (struct fsw_hfs_dnode * dno,
143 fsw_u64 pos,
144 fsw_s32 len,
145 fsw_u8 * buf)
146{
147
148 fsw_status_t status;
149 fsw_u32 log_bno;
150 fsw_u32 block_size_bits = dno->g.vol->block_size_shift;
151 fsw_u32 block_size = (1 << block_size_bits);
152 fsw_u32 block_size_mask = block_size - 1;
153 fsw_s32 read = 0;
154
155 while (len > 0)
156 {
157 fsw_u32 off = (fsw_u32)(pos & block_size_mask);
158 fsw_s32 next_len = len;
159
160 log_bno = (fsw_u32)RShiftU64(pos, block_size_bits);
161
162 if ( next_len >= 0
163 && (fsw_u32)next_len > block_size)
164 next_len = block_size;
165 status = fsw_hfs_read_block(dno, log_bno, off, next_len, buf);
166 if (status)
167 return -1;
168 buf += next_len;
169 pos += next_len;
170 len -= next_len;
171 read += next_len;
172 }
173
174 return read;
175}
176
177
178static fsw_s32
179fsw_hfs_compute_shift(fsw_u32 size)
180{
181 fsw_s32 i;
182
183 for (i=0; i<32; i++)
184 {
185 if ((size >> i) == 0)
186 return i - 1;
187 }
188
189 BP("BUG\n");
190 return 0;
191}
192
193/**
194 * Mount an HFS+ volume. Reads the superblock and constructs the
195 * root directory dnode.
196 */
197
198static fsw_status_t fsw_hfs_volume_mount(struct fsw_hfs_volume *vol)
199{
200 fsw_status_t status, rv;
201 void *buffer = NULL;
202 HFSPlusVolumeHeader *voldesc;
203 fsw_u32 blockno;
204 struct fsw_string s;
205
206 rv = FSW_UNSUPPORTED;
207
208 vol->primary_voldesc = NULL;
209 fsw_set_blocksize(vol, HFS_BLOCKSIZE, HFS_BLOCKSIZE);
210 blockno = HFS_SUPERBLOCK_BLOCKNO;
211
212#define CHECK(s) \
213 if (status) { \
214 rv = status; \
215 break; \
216 }
217
218 vol->emb_block_off = 0;
219 vol->hfs_kind = 0;
220 do {
221 fsw_u16 signature;
222 BTHeaderRec tree_header;
223 fsw_s32 r;
224 fsw_u32 block_size;
225
226 status = fsw_block_get(vol, blockno, 0, &buffer);
227 CHECK(status);
228 voldesc = (HFSPlusVolumeHeader *)buffer;
229 signature = be16_to_cpu(voldesc->signature);
230
231 if ((signature == kHFSPlusSigWord) || (signature == kHFSXSigWord))
232 {
233 if (vol->hfs_kind == 0)
234 {
235 DPRINT("found HFS+\n");
236 vol->hfs_kind = FSW_HFS_PLUS;
237 }
238 }
239 else if (signature == kHFSSigWord)
240 {
241 HFSMasterDirectoryBlock* mdb = (HFSMasterDirectoryBlock*)buffer;
242
243 if (be16_to_cpu(mdb->drEmbedSigWord) == kHFSPlusSigWord)
244 {
245 DPRINT("found HFS+ inside HFS, untested\n");
246 vol->hfs_kind = FSW_HFS_PLUS_EMB;
247 vol->emb_block_off = be32_to_cpu(mdb->drEmbedExtent.startBlock);
248 blockno += vol->emb_block_off;
249 /* retry */
250 continue;
251 }
252 else
253 {
254 DPRINT("found plain HFS, unsupported\n");
255 vol->hfs_kind = FSW_HFS_PLAIN;
256 }
257 rv = FSW_UNSUPPORTED;
258 break;
259 }
260 else
261 {
262 rv = FSW_UNSUPPORTED;
263 break;
264 }
265
266 status = fsw_memdup((void **)&vol->primary_voldesc, voldesc,
267 sizeof(*voldesc));
268 CHECK(status);
269
270
271 block_size = be32_to_cpu(voldesc->blockSize);
272 vol->block_size_shift = fsw_hfs_compute_shift(block_size);
273
274 fsw_block_release(vol, blockno, buffer);
275 buffer = NULL;
276 voldesc = NULL;
277 fsw_set_blocksize(vol, block_size, block_size);
278
279 /* get volume name */
280 s.type = FSW_STRING_TYPE_ISO88591;
281 s.size = s.len = kHFSMaxVolumeNameChars;
282 s.data = "HFS+ volume";
283 status = fsw_strdup_coerce(&vol->g.label, vol->g.host_string_type, &s);
284 CHECK(status);
285
286 /* Setup catalog dnode */
287 status = fsw_dnode_create_root(vol, kHFSCatalogFileID, &vol->catalog_tree.file);
288 CHECK(status);
289 fsw_memcpy (vol->catalog_tree.file->extents,
290 vol->primary_voldesc->catalogFile.extents,
291 sizeof vol->catalog_tree.file->extents);
292 vol->catalog_tree.file->g.size =
293 be64_to_cpu(vol->primary_voldesc->catalogFile.logicalSize);
294
295 /* Setup extents overflow file */
296 status = fsw_dnode_create_root(vol, kHFSExtentsFileID, &vol->extents_tree.file);
297 fsw_memcpy (vol->extents_tree.file->extents,
298 vol->primary_voldesc->extentsFile.extents,
299 sizeof vol->extents_tree.file->extents);
300 vol->extents_tree.file->g.size =
301 be64_to_cpu(vol->primary_voldesc->extentsFile.logicalSize);
302
303 /* Setup the root dnode */
304 status = fsw_dnode_create_root(vol, kHFSRootFolderID, &vol->g.root);
305 CHECK(status);
306
307 /*
308 * Read catalog file, we know that first record is in the first node, right after
309 * the node descriptor.
310 */
311 r = fsw_hfs_read_file(vol->catalog_tree.file,
312 sizeof (BTNodeDescriptor),
313 sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
314 if (r <= 0)
315 {
316 status = FSW_VOLUME_CORRUPTED;
317 break;
318 }
319 vol->case_sensitive =
320 (signature == kHFSXSigWord) &&
321 (tree_header.keyCompareType == kHFSBinaryCompare);
322 vol->catalog_tree.root_node = be32_to_cpu (tree_header.rootNode);
323 vol->catalog_tree.node_size = be16_to_cpu (tree_header.nodeSize);
324
325 /* Read extents overflow file */
326 r = fsw_hfs_read_file(vol->extents_tree.file,
327 sizeof (BTNodeDescriptor),
328 sizeof (BTHeaderRec), (fsw_u8 *) &tree_header);
329 if (r <= 0)
330 {
331 status = FSW_VOLUME_CORRUPTED;
332 break;
333 }
334
335 vol->extents_tree.root_node = be32_to_cpu (tree_header.rootNode);
336 vol->extents_tree.node_size = be16_to_cpu (tree_header.nodeSize);
337
338 rv = FSW_SUCCESS;
339 } while (0);
340
341#undef CHECK
342
343
344 if (buffer != NULL)
345 fsw_block_release(vol, blockno, buffer);
346
347 return rv;
348}
349
350/**
351 * Free the volume data structure. Called by the core after an unmount or after
352 * an unsuccessful mount to release the memory used by the file system type specific
353 * part of the volume structure.
354 */
355
356static void fsw_hfs_volume_free(struct fsw_hfs_volume *vol)
357{
358 if (vol->primary_voldesc)
359 {
360 fsw_free(vol->primary_voldesc);
361 vol->primary_voldesc = NULL;
362 }
363}
364
365/**
366 * Get in-depth information on a volume.
367 */
368
369static fsw_status_t fsw_hfs_volume_stat(struct fsw_hfs_volume *vol, struct fsw_volume_stat *sb)
370{
371 sb->total_bytes = be32_to_cpu(vol->primary_voldesc->totalBlocks) << vol->block_size_shift;
372 sb->free_bytes = be32_to_cpu(vol->primary_voldesc->freeBlocks) << vol->block_size_shift;
373 return FSW_SUCCESS;
374}
375
376/**
377 * Get full information on a dnode from disk. This function is called by the core
378 * whenever it needs to access fields in the dnode structure that may not
379 * be filled immediately upon creation of the dnode.
380 */
381
382static fsw_status_t fsw_hfs_dnode_fill(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
383{
384 return FSW_SUCCESS;
385}
386
387/**
388 * Free the dnode data structure. Called by the core when deallocating a dnode
389 * structure to release the memory used by the file system type specific part
390 * of the dnode structure.
391 */
392
393static void fsw_hfs_dnode_free(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno)
394{
395}
396
397static fsw_u32 mac_to_posix(fsw_u32 mac_time)
398{
399 /* Mac time is 1904 year based */
400 return mac_time ? mac_time - 2082844800 : 0;
401}
402
403/**
404 * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill
405 * has been called on the dnode before this function is called. Note that some
406 * data is not directly stored into the structure, but passed to a host-specific
407 * callback that converts it to the host-specific format.
408 */
409
410static fsw_status_t fsw_hfs_dnode_stat(struct fsw_hfs_volume *vol,
411 struct fsw_hfs_dnode *dno,
412 struct fsw_dnode_stat *sb)
413{
414 sb->used_bytes = dno->used_bytes;
415 sb->store_time_posix(sb, FSW_DNODE_STAT_CTIME, mac_to_posix(dno->ctime));
416 sb->store_time_posix(sb, FSW_DNODE_STAT_MTIME, mac_to_posix(dno->mtime));
417 sb->store_time_posix(sb, FSW_DNODE_STAT_ATIME, 0);
418 sb->store_attr_posix(sb, 0700);
419
420 return FSW_SUCCESS;
421}
422
423static int
424fsw_hfs_find_block(HFSPlusExtentRecord * exts,
425 fsw_u32 * lbno,
426 fsw_u32 * pbno)
427{
428 int i;
429 fsw_u32 cur_lbno = *lbno;
430
431 for (i = 0; i < 8; i++)
432 {
433 fsw_u32 start = be32_to_cpu ((*exts)[i].startBlock);
434 fsw_u32 count = be32_to_cpu ((*exts)[i].blockCount);
435
436 if (cur_lbno < count)
437 {
438 *pbno = start + cur_lbno;
439 return 1;
440 }
441
442 cur_lbno -= count;
443 }
444
445 *lbno = cur_lbno;
446
447 return 0;
448}
449
450/* Find record offset, numbering starts from the end */
451static fsw_u32
452fsw_hfs_btree_recoffset (struct fsw_hfs_btree * btree,
453 BTNodeDescriptor * node,
454 fsw_u32 index)
455{
456 fsw_u8 *cnode = (fsw_u8 *) node;
457 fsw_u16 *recptr;
458 recptr = (fsw_u16 *) (cnode+btree->node_size - index * 2 - 2);
459 return be16_to_cpu(*recptr);
460}
461
462/* Pointer to the key inside node */
463static BTreeKey *
464fsw_hfs_btree_rec (struct fsw_hfs_btree * btree,
465 BTNodeDescriptor * node,
466 fsw_u32 index)
467{
468 fsw_u8 *cnode = (fsw_u8 *) node;
469 fsw_u32 offset;
470 offset = fsw_hfs_btree_recoffset (btree, node, index);
471 return (BTreeKey *) (cnode + offset);
472}
473
474
475static fsw_status_t
476fsw_hfs_btree_search (struct fsw_hfs_btree * btree,
477 BTreeKey * key,
478 int (*compare_keys) (BTreeKey* key1, BTreeKey* key2),
479 BTNodeDescriptor ** result,
480 fsw_u32 * key_offset)
481{
482 BTNodeDescriptor* node;
483 fsw_u32 currnode;
484 fsw_u32 rec;
485 fsw_status_t status;
486 fsw_u8* buffer = NULL;
487
488 currnode = btree->root_node;
489 status = fsw_alloc(btree->node_size, &buffer);
490 if (status)
491 return status;
492 node = (BTNodeDescriptor*)buffer;
493
494 while (1)
495 {
496 int cmp = 0;
497 int match;
498 fsw_u32 count;
499
500 readnode:
501 match = 0;
502 /* Read a node. */
503 if (fsw_hfs_read_file (btree->file,
504 (fsw_u64)currnode * btree->node_size,
505 btree->node_size, buffer) <= 0)
506 {
507 status = FSW_VOLUME_CORRUPTED;
508 break;
509 }
510
511 if (be16_to_cpu(*(fsw_u16*)(buffer + btree->node_size - 2)) != sizeof(BTNodeDescriptor))
512 BP("corrupted node\n");
513
514 count = be16_to_cpu (node->numRecords);
515
516#if 1
517 for (rec = 0; rec < count; rec++)
518 {
519 BTreeKey *currkey;
520
521 currkey = fsw_hfs_btree_rec (btree, node, rec);
522 cmp = compare_keys (currkey, key);
523 //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
524
525 /* Leaf node. */
526 if (node->kind == kBTLeafNode)
527 {
528 if (cmp == 0)
529 {
530 /* Found! */
531 *result = node;
532 *key_offset = rec;
533
534 status = FSW_SUCCESS;
535 goto done;
536 }
537 }
538 else if (node->kind == kBTIndexNode)
539 {
540 fsw_u32 *pointer;
541
542 if (cmp > 0)
543 break;
544
545 pointer = (fsw_u32 *) ((char *) currkey
546 + be16_to_cpu (currkey->length16)
547 + 2);
548 currnode = be32_to_cpu (*pointer);
549 match = 1;
550 }
551 }
552
553 if (node->kind == kBTLeafNode && cmp < 0 && node->fLink)
554 {
555 currnode = be32_to_cpu(node->fLink);
556 goto readnode;
557 }
558 else if (!match)
559 {
560 status = FSW_NOT_FOUND;
561 break;
562 }
563#else
564 /* Perform binary search */
565 fsw_u32 lower = 0;
566 fsw_u32 upper = count - 1;
567 fsw_s32 cmp = -1;
568 BTreeKey *currkey = NULL;
569
570 if (count == 0)
571 {
572 status = FSW_NOT_FOUND;
573 goto done;
574 }
575
576 while (lower <= upper)
577 {
578 fsw_u32 index = (lower + upper) / 2;
579
580 currkey = fsw_hfs_btree_rec (btree, node, index);
581
582 cmp = compare_keys (currkey, key);
583 if (cmp < 0) upper = index - 1;
584 if (cmp > 0) lower = index + 1;
585 if (cmp == 0)
586 {
587 /* Found! */
588 *result = node;
589 *key_offset = rec;
590
591 status = FSW_SUCCESS;
592 goto done;
593 }
594 }
595
596 if (cmp < 0)
597 currkey = fsw_hfs_btree_rec (btree, node, upper);
598
599 if (node->kind == kBTIndexNode && currkey)
600 {
601 fsw_u32 *pointer;
602
603 pointer = (fsw_u32 *) ((char *) currkey
604 + be16_to_cpu (currkey->length16)
605 + 2);
606 currnode = be32_to_cpu (*pointer);
607 }
608 else
609 {
610 status = FSW_NOT_FOUND;
611 break;
612 }
613#endif
614 }
615
616
617 done:
618 if (buffer != NULL && status != FSW_SUCCESS)
619 fsw_free(buffer);
620
621 return status;
622}
623
624typedef struct
625{
626 fsw_u32 id;
627 fsw_u32 type;
628 struct fsw_string * name;
629 fsw_u64 size;
630 fsw_u64 used;
631 fsw_u32 ctime;
632 fsw_u32 mtime;
633 fsw_u32 node_num;
634 HFSPlusExtentRecord extents;
635} file_info_t;
636
637typedef struct
638{
639 fsw_u32 cur_pos; /* current position */
640 fsw_u32 parent;
641 struct fsw_hfs_volume * vol;
642
643 struct fsw_shandle * shandle; /* this one track iterator's state */
644 file_info_t file_info;
645} visitor_parameter_t;
646
647static void hfs_fill_info(struct fsw_hfs_volume *vol, HFSPlusCatalogKey *file_key, file_info_t *file_info)
648{
649 fsw_u8 * base;
650 fsw_u16 rec_type;
651
652 /* for plain HFS "-(keySize & 1)" would be needed */
653 base = (fsw_u8*)file_key + be16_to_cpu(file_key->keyLength) + 2;
654 rec_type = be16_to_cpu(*(fsw_u16*)base);
655
656 /** @todo: read additional info */
657 switch (rec_type)
658 {
659 case kHFSPlusFolderRecord:
660 {
661 HFSPlusCatalogFolder* info = (HFSPlusCatalogFolder*)base;
662
663 file_info->id = be32_to_cpu(info->folderID);
664 file_info->type = FSW_DNODE_TYPE_DIR;
665 /* @todo: return number of elements, maybe use smth else */
666 file_info->size = be32_to_cpu(info->valence);
667 file_info->used = be32_to_cpu(info->valence);
668 file_info->ctime = be32_to_cpu(info->createDate);
669 file_info->mtime = be32_to_cpu(info->contentModDate);
670 break;
671 }
672 case kHFSPlusFileRecord:
673 {
674 HFSPlusCatalogFile* info = (HFSPlusCatalogFile*)base;
675 uint32_t creator = be32_to_cpu(info->userInfo.fdCreator);
676 uint32_t crtype = be32_to_cpu(info->userInfo.fdType);
677
678 file_info->id = be32_to_cpu(info->fileID);
679 file_info->type = FSW_DNODE_TYPE_FILE;
680 file_info->size = be64_to_cpu(info->dataFork.logicalSize);
681 file_info->used = LShiftU64(be32_to_cpu(info->dataFork.totalBlocks), vol->block_size_shift);
682 file_info->ctime = be32_to_cpu(info->createDate);
683 file_info->mtime = be32_to_cpu(info->contentModDate);
684 fsw_memcpy(&file_info->extents, &info->dataFork.extents,
685 sizeof file_info->extents);
686 if (creator == kHFSPlusCreator && crtype == kHardLinkFileType)
687 {
688 /* Only hard links currently supported. */
689 file_info->type = FSW_DNODE_TYPE_SYMLINK;
690 file_info->node_num = be32_to_cpu(info->bsdInfo.special.iNodeNum);
691 }
692 break;
693 }
694 case kHFSPlusFolderThreadRecord:
695 case kHFSPlusFileThreadRecord:
696 {
697 /* Do nothing. */
698 break;
699 }
700 default:
701 BP("unknown file type\n");
702 file_info->type = FSW_DNODE_TYPE_UNKNOWN;
703
704 break;
705 }
706}
707
708static int
709fsw_hfs_btree_visit_node(BTreeKey *record, void* param)
710{
711 visitor_parameter_t* vp = (visitor_parameter_t*)param;
712 fsw_u8* base = (fsw_u8*)record->rawData + be16_to_cpu(record->length16) + 2;
713 fsw_u16 rec_type = be16_to_cpu(*(fsw_u16*)base);
714 struct HFSPlusCatalogKey* cat_key = (HFSPlusCatalogKey*)record;
715 fsw_u16 name_len;
716 fsw_u16 *name_ptr;
717 fsw_u32 i;
718 struct fsw_string * file_name;
719
720 if (be32_to_cpu(cat_key->parentID) != vp->parent)
721 return -1;
722
723 /* not smth we care about */
724 if (vp->shandle->pos != vp->cur_pos++)
725 return 0;
726
727 if (rec_type == kHFSPlusFolderThreadRecord || rec_type == kHFSPlusFileThreadRecord)
728 {
729 vp->shandle->pos++;
730 return 0;
731 }
732
733 hfs_fill_info(vp->vol, cat_key, &vp->file_info);
734
735 name_len = be16_to_cpu(cat_key->nodeName.length);
736
737 file_name = vp->file_info.name;
738 file_name->len = name_len;
739 fsw_memdup(&file_name->data, &cat_key->nodeName.unicode[0], 2*name_len);
740 file_name->size = 2*name_len;
741 file_name->type = FSW_STRING_TYPE_UTF16;
742 name_ptr = (fsw_u16*)file_name->data;
743 for (i=0; i<name_len; i++)
744 {
745 name_ptr[i] = be16_to_cpu(name_ptr[i]);
746 }
747 vp->shandle->pos++;
748
749 return 1;
750}
751
752static fsw_status_t
753fsw_hfs_btree_iterate_node (struct fsw_hfs_btree * btree,
754 BTNodeDescriptor * first_node,
755 fsw_u32 first_rec,
756 int (*callback) (BTreeKey *record, void* param),
757 void * param)
758{
759 fsw_status_t status;
760 /* We modify node, so make a copy */
761 BTNodeDescriptor* node = first_node;
762 fsw_u8* buffer = NULL;
763
764 status = fsw_alloc(btree->node_size, &buffer);
765 if (status)
766 return status;
767
768 while (1)
769 {
770 fsw_u32 i;
771 fsw_u32 count = be16_to_cpu(node->numRecords);
772 fsw_u32 next_node;
773
774 /* Iterate over all records in this node. */
775 for (i = first_rec; i < count; i++)
776 {
777 int rv = callback(fsw_hfs_btree_rec (btree, node, i), param);
778
779 switch (rv)
780 {
781 case 1:
782 status = FSW_SUCCESS;
783 goto done;
784 case -1:
785 status = FSW_NOT_FOUND;
786 goto done;
787 }
788 /* if callback returned 0 - continue */
789 }
790
791 next_node = be32_to_cpu(node->fLink);
792
793 if (!next_node)
794 {
795 status = FSW_NOT_FOUND;
796 break;
797 }
798
799 if (fsw_hfs_read_file (btree->file,
800 next_node * btree->node_size,
801 btree->node_size, buffer) <= 0)
802 {
803 status = FSW_VOLUME_CORRUPTED;
804 return 1;
805 }
806
807 node = (BTNodeDescriptor*)buffer;
808 first_rec = 0;
809 }
810 done:
811 if (buffer)
812 fsw_free(buffer);
813
814 return status;
815}
816
817#if 0
818void deb(fsw_u16* p, int len, int swap)
819{
820 int i;
821 for (i=0; i<len; i++)
822 {
823 printf("%c", swap ? be16_to_cpu(p[i]) : p[i]);
824 }
825 printf("\n");
826}
827#endif
828
829static int
830fsw_hfs_cmp_extkey(BTreeKey* key1, BTreeKey* key2)
831{
832 HFSPlusExtentKey* ekey1 = (HFSPlusExtentKey*)key1;
833 HFSPlusExtentKey* ekey2 = (HFSPlusExtentKey*)key2;
834 int result;
835
836 /* First key is read from the FS data, second is in-memory in CPU endianess */
837 result = be32_to_cpu(ekey1->fileID) - ekey2->fileID;
838
839 if (result)
840 return result;
841
842 result = ekey1->forkType - ekey2->forkType;
843
844 if (result)
845 return result;
846
847 result = be32_to_cpu(ekey1->startBlock) - ekey2->startBlock;
848 return result;
849}
850
851static int
852fsw_hfs_cmp_catkey (BTreeKey *key1, BTreeKey *key2)
853{
854 HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
855 HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
856
857 int apos, bpos, lc;
858 fsw_u16 ac, bc;
859 fsw_u32 parentId1;
860 int key1Len;
861 fsw_u16 *p1;
862 fsw_u16 *p2;
863
864 parentId1 = be32_to_cpu(ckey1->parentID);
865
866 if (parentId1 > ckey2->parentID)
867 return 1;
868 if (parentId1 < ckey2->parentID)
869 return -1;
870
871 p1 = &ckey1->nodeName.unicode[0];
872 p2 = &ckey2->nodeName.unicode[0];
873 key1Len = be16_to_cpu (ckey1->nodeName.length);
874 apos = bpos = 0;
875
876 while(1)
877 {
878 /* get next valid character from ckey1 */
879 for (lc = 0; lc == 0 && apos < key1Len; apos++) {
880 ac = be16_to_cpu(p1[apos]);
881 lc = ac;
882 };
883 ac = (fsw_u16)lc;
884
885 /* get next valid character from ckey2 */
886 for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
887 bc = p2[bpos];
888 lc = bc;
889 };
890 bc = (fsw_u16)lc;
891
892 if (ac != bc || (ac == 0 && bc == 0))
893 return ac - bc;
894 }
895}
896
897static int
898fsw_hfs_cmpi_catkey (BTreeKey *key1, BTreeKey *key2)
899{
900 HFSPlusCatalogKey *ckey1 = (HFSPlusCatalogKey*)key1;
901 HFSPlusCatalogKey *ckey2 = (HFSPlusCatalogKey*)key2;
902
903 int apos, bpos, lc;
904 fsw_u16 ac, bc;
905 fsw_u32 parentId1;
906 int key1Len;
907 fsw_u16 *p1;
908 fsw_u16 *p2;
909
910 parentId1 = be32_to_cpu(ckey1->parentID);
911
912 if (parentId1 > ckey2->parentID)
913 return 1;
914 if (parentId1 < ckey2->parentID)
915 return -1;
916
917 key1Len = be16_to_cpu (ckey1->nodeName.length);
918
919 if (key1Len == 0 && ckey2->nodeName.length == 0)
920 return 0;
921
922 p1 = &ckey1->nodeName.unicode[0];
923 p2 = &ckey2->nodeName.unicode[0];
924
925 apos = bpos = 0;
926
927 while(1)
928 {
929 /* get next valid character from ckey1 */
930 for (lc = 0; lc == 0 && apos < key1Len; apos++) {
931 ac = be16_to_cpu(p1[apos]);
932 lc = ac ? fsw_to_lower(ac) : 0;
933 };
934 ac = (fsw_u16)lc;
935
936 /* get next valid character from ckey2 */
937 for (lc = 0; lc == 0 && bpos < ckey2->nodeName.length; bpos++) {
938 bc = p2[bpos];
939 lc = bc ? fsw_to_lower(bc) : 0;
940 };
941 bc = (fsw_u16)lc;
942
943 if (ac != bc || (ac == 0 && bc == 0))
944 return ac - bc;
945 }
946}
947
948/**
949 * Retrieve file data mapping information. This function is called by the core when
950 * fsw_shandle_read needs to know where on the disk the required piece of the file's
951 * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called
952 * on the dnode before. Our task here is to get the physical disk block number for
953 * the requested logical block number.
954 */
955
956static fsw_status_t fsw_hfs_get_extent(struct fsw_hfs_volume * vol,
957 struct fsw_hfs_dnode * dno,
958 struct fsw_extent * extent)
959{
960 fsw_status_t status;
961 fsw_u32 lbno;
962 HFSPlusExtentRecord *exts;
963 BTNodeDescriptor *node = NULL;
964
965 extent->type = FSW_EXTENT_TYPE_PHYSBLOCK;
966 extent->log_count = 1;
967 lbno = extent->log_start;
968
969 /* we only care about data forks atm, do we? */
970 exts = &dno->extents;
971
972 while (1)
973 {
974 struct HFSPlusExtentKey* key;
975 struct HFSPlusExtentKey overflowkey;
976 fsw_u32 ptr;
977 fsw_u32 phys_bno;
978
979 if (fsw_hfs_find_block(exts, &lbno, &phys_bno))
980 {
981 extent->phys_start = phys_bno + vol->emb_block_off;
982 status = FSW_SUCCESS;
983 break;
984 }
985
986
987 /* Find appropriate overflow record */
988 overflowkey.fileID = dno->g.dnode_id;
989 overflowkey.startBlock = extent->log_start - lbno;
990
991 if (node != NULL)
992 {
993 fsw_free(node);
994 node = NULL;
995 }
996
997 status = fsw_hfs_btree_search (&vol->extents_tree,
998 (BTreeKey*)&overflowkey,
999 fsw_hfs_cmp_extkey,
1000 &node, &ptr);
1001 if (status)
1002 break;
1003
1004 key = (struct HFSPlusExtentKey *)
1005 fsw_hfs_btree_rec (&vol->extents_tree, node, ptr);
1006 exts = (HFSPlusExtentRecord*) (key + 1);
1007 }
1008
1009 if (node != NULL)
1010 fsw_free(node);
1011
1012 return status;
1013}
1014
1015static const fsw_u16* g_blacklist[] =
1016{
1017 //L"AppleIntelCPUPowerManagement.kext",
1018 NULL
1019};
1020
1021
1022//#define HFS_FILE_INJECTION
1023
1024#ifdef HFS_FILE_INJECTION
1025static struct
1026{
1027 const fsw_u16* path;
1028 const fsw_u16* name;
1029} g_injectList[] =
1030{
1031 {
1032 L"/System/Library/Extensions",
1033 L"ApplePS2Controller.kext"
1034 },
1035 {
1036 NULL,
1037 NULL
1038 }
1039};
1040#endif
1041
1042static fsw_status_t
1043create_hfs_dnode(struct fsw_hfs_dnode * dno,
1044 file_info_t * file_info,
1045 struct fsw_hfs_dnode ** child_dno_out)
1046{
1047 fsw_status_t status;
1048 struct fsw_hfs_dnode * baby;
1049
1050 status = fsw_dnode_create(dno, file_info->id, file_info->type,
1051 file_info->name, &baby);
1052 if (status)
1053 return status;
1054
1055 baby->g.size = file_info->size;
1056 baby->used_bytes = file_info->used;
1057 baby->ctime = file_info->ctime;
1058 baby->mtime = file_info->mtime;
1059 baby->node_num = file_info->node_num;
1060
1061
1062 /* Fill-in extents info */
1063 if (file_info->type == FSW_DNODE_TYPE_FILE)
1064 {
1065 fsw_memcpy(baby->extents, &file_info->extents, sizeof file_info->extents);
1066 }
1067
1068 *child_dno_out = baby;
1069
1070 return FSW_SUCCESS;
1071}
1072
1073
1074/**
1075 * Lookup a directory's child dnode by name. This function is called on a directory
1076 * to retrieve the directory entry with the given name. A dnode is constructed for
1077 * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1078 * and the dnode is actually a directory.
1079 */
1080
1081static fsw_status_t fsw_hfs_dir_lookup(struct fsw_hfs_volume * vol,
1082 struct fsw_hfs_dnode * dno,
1083 struct fsw_string * lookup_name,
1084 struct fsw_hfs_dnode ** child_dno_out)
1085{
1086 fsw_status_t status;
1087 struct HFSPlusCatalogKey catkey;
1088 fsw_u32 ptr;
1089 BTNodeDescriptor * node = NULL;
1090 struct fsw_string rec_name;
1091 int free_data = 0, i;
1092 HFSPlusCatalogKey* file_key;
1093 file_info_t file_info;
1094
1095
1096 fsw_memzero(&file_info, sizeof file_info);
1097 file_info.name = &rec_name;
1098
1099 catkey.parentID = dno->g.dnode_id;
1100 catkey.nodeName.length = (fsw_u16)lookup_name->len;
1101
1102 /* no need to allocate anything */
1103 if (lookup_name->type == FSW_STRING_TYPE_UTF16)
1104 {
1105 fsw_memcpy(catkey.nodeName.unicode, lookup_name->data, lookup_name->size);
1106 rec_name = *lookup_name;
1107 } else
1108 {
1109 status = fsw_strdup_coerce(&rec_name, FSW_STRING_TYPE_UTF16, lookup_name);
1110 /* nothing allocated so far */
1111 if (status)
1112 goto done;
1113 free_data = 1;
1114 fsw_memcpy(catkey.nodeName.unicode, rec_name.data, rec_name.size);
1115 }
1116
1117 /* Dirty hack: blacklisting of certain files on FS driver level */
1118 for (i = 0; g_blacklist[i]; i++)
1119 {
1120 if (fsw_memeq(g_blacklist[i], catkey.nodeName.unicode, catkey.nodeName.length*2))
1121 {
1122 DPRINT2("Blacklisted %s\n", g_blacklist[i]);
1123 status = FSW_NOT_FOUND;
1124 goto done;
1125 }
1126 }
1127
1128#ifdef HFS_FILE_INJECTION
1129 if (fsw_hfs_inject(vol,
1130 dno,
1131 catkey.nodeName.unicode,
1132 catkey.nodeName.length,
1133 &file_info))
1134 {
1135 status = FSW_SUCCESS;
1136 goto create;
1137 }
1138#endif
1139
1140 catkey.keyLength = (fsw_u16)(6 + rec_name.len);
1141
1142 status = fsw_hfs_btree_search (&vol->catalog_tree,
1143 (BTreeKey*)&catkey,
1144 vol->case_sensitive ?
1145 fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
1146 &node, &ptr);
1147 if (status)
1148 goto done;
1149
1150 file_key = (HFSPlusCatalogKey *)fsw_hfs_btree_rec (&vol->catalog_tree, node, ptr);
1151 hfs_fill_info(vol, file_key, &file_info);
1152
1153#ifdef HFS_FILE_INJECTION
1154create:
1155#endif
1156 status = create_hfs_dnode(dno, &file_info, child_dno_out);
1157 if (status)
1158 goto done;
1159
1160done:
1161
1162 if (node != NULL)
1163 fsw_free(node);
1164
1165 if (free_data)
1166 fsw_strfree(&rec_name);
1167
1168 return status;
1169}
1170
1171/**
1172 * Get the next directory entry when reading a directory. This function is called during
1173 * directory iteration to retrieve the next directory entry. A dnode is constructed for
1174 * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1175 * and the dnode is actually a directory. The shandle provided by the caller is used to
1176 * record the position in the directory between calls.
1177 */
1178
1179static fsw_status_t fsw_hfs_dir_read(struct fsw_hfs_volume *vol,
1180 struct fsw_hfs_dnode *dno,
1181 struct fsw_shandle *shand,
1182 struct fsw_hfs_dnode **child_dno_out)
1183{
1184 fsw_status_t status;
1185 struct HFSPlusCatalogKey catkey;
1186 fsw_u32 ptr;
1187 BTNodeDescriptor * node = NULL;
1188
1189 visitor_parameter_t param;
1190 struct fsw_string rec_name;
1191
1192 catkey.parentID = dno->g.dnode_id;
1193 catkey.nodeName.length = 0;
1194
1195 fsw_memzero(&param, sizeof(param));
1196
1197 rec_name.type = FSW_STRING_TYPE_EMPTY;
1198 param.file_info.name = &rec_name;
1199
1200 status = fsw_hfs_btree_search (&vol->catalog_tree,
1201 (BTreeKey*)&catkey,
1202 vol->case_sensitive ?
1203 fsw_hfs_cmp_catkey : fsw_hfs_cmpi_catkey,
1204 &node, &ptr);
1205 if (status)
1206 goto done;
1207
1208 /* Iterator updates shand state */
1209 param.vol = vol;
1210 param.shandle = shand;
1211 param.parent = dno->g.dnode_id;
1212 param.cur_pos = 0;
1213 status = fsw_hfs_btree_iterate_node (&vol->catalog_tree,
1214 node,
1215 ptr,
1216 fsw_hfs_btree_visit_node,
1217 &param);
1218 if (status)
1219 goto done;
1220
1221 status = create_hfs_dnode(dno, &param.file_info, child_dno_out);
1222
1223 if (status)
1224 goto done;
1225
1226 done:
1227 fsw_strfree(&rec_name);
1228
1229 return status;
1230}
1231
1232static const char hfs_priv_prefix[] = "/\0\0\0\0HFS+ Private Data/" HFS_INODE_PREFIX;
1233
1234/**
1235 * Get the target path of a symbolic link. This function is called when a symbolic
1236 * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
1237 * called on the dnode and that it really is a symlink.
1238 *
1239 */
1240static fsw_status_t fsw_hfs_readlink(struct fsw_hfs_volume *vol, struct fsw_hfs_dnode *dno,
1241 struct fsw_string *link_target)
1242{
1243 fsw_status_t status;
1244
1245 if (dno->node_num)
1246 {
1247 struct fsw_string tgt;
1248
1249 DPRINT2("hfs_readlink: %d\n", dno->node_num);
1250 tgt.type = FSW_STRING_TYPE_ISO88591;
1251 tgt.size = sizeof(hfs_priv_prefix) + 10;
1252 tgt.len = tgt.size - 1;
1253 status = fsw_alloc(tgt.size, &tgt.data);
1254 if (!status)
1255 {
1256 char *str = tgt.data;
1257 fsw_memcpy(tgt.data, hfs_priv_prefix, sizeof(hfs_priv_prefix)); // null chars here!
1258#ifdef HOST_POSIX
1259 tgt.len = sprintf(&str[sizeof(hfs_priv_prefix) - 1], "%d", dno->node_num);
1260#else
1261 tgt.len = (int)AsciiSPrint(&str[sizeof(hfs_priv_prefix) - 1], tgt.len, "%d", dno->node_num);
1262#endif
1263 tgt.len += sizeof(hfs_priv_prefix) - 1;
1264 status = fsw_strdup_coerce(link_target, vol->g.host_string_type, &tgt);
1265 fsw_strfree(&tgt);
1266 }
1267 return status;
1268 }
1269
1270 return FSW_UNSUPPORTED;
1271}
1272
1273// EOF
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette