1 /* $Id: fsw_hfs.c 33540 2010-10-28 09:27:05Z vboxsync $ */
3 * fsw_hfs.c - HFS file system driver code, see
5 * http://developer.apple.com/technotes/tn/tn1150.html
8 * - Doesn't support permissions
9 * - Complete Unicode case-insensitiveness disabled (large tables)
11 * - Only supports pure HFS+ (i.e. no HFS, or HFS+ embedded to HFS)
15 * Copyright (C) 2010 Oracle Corporation
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.
29 #define DPRINT(x) printf(x)
30 #define DPRINT2(x,y) printf(x,y)
31 #define BP(msg) do { printf("ERROR: %s", msg); asm("int3"); } while (0)
33 #define CONCAT(x,y) x##y
34 #define DPRINT(x) Print(CONCAT(L,x))
35 #define DPRINT2(x,y) Print(CONCAT(L,x), y)
36 #define BP(msg) DPRINT(msg)
41 void dump_str(fsw_u16
* p
, fsw_u32 len
, int swap
)
47 fprintf(stderr
, "%c", swap
? be16_to_cpu(p
[i
]) : p
[i
]);
49 fprintf(stderr
, "\n");
53 static fsw_status_t
fsw_hfs_volume_mount(struct fsw_hfs_volume
*vol
);
54 static void fsw_hfs_volume_free(struct fsw_hfs_volume
*vol
);
55 static fsw_status_t
fsw_hfs_volume_stat(struct fsw_hfs_volume
*vol
, struct fsw_volume_stat
*sb
);
57 static fsw_status_t
fsw_hfs_dnode_fill(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
);
58 static void fsw_hfs_dnode_free(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
);
59 static fsw_status_t
fsw_hfs_dnode_stat(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
60 struct fsw_dnode_stat
*sb
);
61 static fsw_status_t
fsw_hfs_get_extent(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
62 struct fsw_extent
*extent
);
64 static fsw_status_t
fsw_hfs_dir_lookup(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
65 struct fsw_string
*lookup_name
, struct fsw_hfs_dnode
**child_dno
);
66 static fsw_status_t
fsw_hfs_dir_read(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
67 struct fsw_shandle
*shand
, struct fsw_hfs_dnode
**child_dno
);
69 static fsw_status_t
fsw_hfs_read_dirrec(struct fsw_shandle
*shand
, struct hfs_dirrec_buffer
*dirrec_buffer
);
72 static fsw_status_t
fsw_hfs_readlink(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
73 struct fsw_string
*link
);
79 struct fsw_fstype_table
FSW_FSTYPE_TABLE_NAME(hfs
) = {
80 { FSW_STRING_TYPE_ISO88591
, 4, 4, "hfs" },
81 sizeof(struct fsw_hfs_volume
),
82 sizeof(struct fsw_hfs_dnode
),
84 fsw_hfs_volume_mount
, // volume open
85 fsw_hfs_volume_free
, // volume close
86 fsw_hfs_volume_stat
, // volume info: total_bytes, free_bytes
87 fsw_hfs_dnode_fill
, //return FSW_SUCCESS;
88 fsw_hfs_dnode_free
, // empty
89 fsw_hfs_dnode_stat
, //size and times
90 fsw_hfs_get_extent
, // get the physical disk block number for the requested logical block number
91 fsw_hfs_dir_lookup
, //retrieve the directory entry with the given name
92 fsw_hfs_dir_read
, // next directory entry when reading a directory
93 fsw_hfs_readlink
, // return FSW_UNSUPPORTED;
96 static const fsw_u16 fsw_latin_case_fold
[] =
98 /* 0 */ 0xFFFF, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F,
99 /* 1 */ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C, 0x001D, 0x001E, 0x001F,
100 /* 2 */ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C, 0x002D, 0x002E, 0x002F,
101 /* 3 */ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x003D, 0x003E, 0x003F,
102 /* 4 */ 0x0040, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
103 /* 5 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x005B, 0x005C, 0x005D, 0x005E, 0x005F,
104 /* 6 */ 0x0060, 0x0061, 0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C, 0x006D, 0x006E, 0x006F,
105 /* 7 */ 0x0070, 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C, 0x007D, 0x007E, 0x007F,
106 /* 8 */ 0x0080, 0x0081, 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C, 0x008D, 0x008E, 0x008F,
107 /* 9 */ 0x0090, 0x0091, 0x0092, 0x0093, 0x0094, 0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C, 0x009D, 0x009E, 0x009F,
108 /* A */ 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4, 0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC, 0x00AD, 0x00AE, 0x00AF,
109 /* B */ 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4, 0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC, 0x00BD, 0x00BE, 0x00BF,
110 /* C */ 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4, 0x00C5, 0x00E6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC, 0x00CD, 0x00CE, 0x00CF,
111 /* D */ 0x00F0, 0x00D1, 0x00D2, 0x00D3, 0x00D4, 0x00D5, 0x00D6, 0x00D7, 0x00F8, 0x00D9, 0x00DA, 0x00DB, 0x00DC, 0x00DD, 0x00FE, 0x00DF,
112 /* E */ 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4, 0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC, 0x00ED, 0x00EE, 0x00EF,
113 /* F */ 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4, 0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC, 0x00FD, 0x00FE, 0x00FF,
116 static fsw_u16
fsw_to_lower(fsw_u16 ch
)
120 return fsw_latin_case_fold
[ch
];
126 fsw_hfs_read_block (struct fsw_hfs_dnode
* dno
,
133 struct fsw_extent extent
;
137 extent
.log_start
= log_bno
;
138 status
= fsw_hfs_get_extent(dno
->g
.vol
, dno
, &extent
);
142 phys_bno
= extent
.phys_start
;
143 //Slice - increase cache level from 0 to 3
144 status
= fsw_block_get(dno
->g
.vol
, phys_bno
, 3, (void **)&buffer
);
148 fsw_memcpy(buf
, buffer
+ off
, len
);
150 fsw_block_release(dno
->g
.vol
, phys_bno
, buffer
);
156 /* Read data from HFS file. */
158 fsw_hfs_read_file (struct fsw_hfs_dnode
* dno
,
166 fsw_u32 block_size_bits
= dno
->g
.vol
->block_size_shift
;
167 fsw_u32 block_size
= (1 << block_size_bits
);
168 fsw_u32 block_size_mask
= block_size
- 1;
173 fsw_u32 off
= (fsw_u32
)(pos
& block_size_mask
);
174 fsw_s32 next_len
= len
;
176 log_bno
= (fsw_u32
)RShiftU64(pos
, block_size_bits
);
179 && (fsw_u32
)next_len
> block_size
)
180 next_len
= block_size
;
181 status
= fsw_hfs_read_block(dno
, log_bno
, off
, next_len
, buf
);
195 fsw_hfs_compute_shift(fsw_u32 size
)
201 if ((size
>> i
) == 0)
210 * Mount an HFS+ volume. Reads the superblock and constructs the
211 * root directory dnode.
213 //algo from Chameleon
216 HFSGetDescription(CICell ih, char *str, long strMaxLen)
220 UInt32 firstLeafNode;
225 if (HFSInitPartition(ih) == -1) { return; }
227 // Fill some crucial data structures by side effect.
229 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
231 // Now we can loook up the volume name node.
232 nodeSize = be16_to_cpu(gBTHeaders[kBTreeCatalog]->nodeSize);
233 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
235 dirIndex = (long long) firstLeafNode * nodeSize;
237 GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
239 strncpy(str, name, strMaxLen);
240 str[strMaxLen] = '\0';
245 static fsw_status_t
fsw_hfs_volume_mount(struct fsw_hfs_volume
*vol
)
247 fsw_status_t status
, rv
;
249 HFSPlusVolumeHeader
*voldesc
;
252 HFSMasterDirectoryBlock
* mdb
;
253 fsw_u32 firstLeafNum
;
255 fsw_u8 cbuff
[sizeof (BTNodeDescriptor
) + sizeof (HFSPlusCatalogKey
)];
257 rv
= FSW_UNSUPPORTED
;
259 vol
->primary_voldesc
= NULL
;
260 fsw_set_blocksize(vol
, HFS_BLOCKSIZE
, HFS_BLOCKSIZE
);
261 blockno
= HFS_SUPERBLOCK_BLOCKNO
;
269 vol
->emb_block_off
= 0;
273 BTHeaderRec tree_header
;
277 status
= fsw_block_get(vol
, blockno
, 0, &buffer
);
279 voldesc
= (HFSPlusVolumeHeader
*)buffer
;
280 mdb
= (HFSMasterDirectoryBlock
*)buffer
;
281 signature
= be16_to_cpu(voldesc
->signature
);
283 if ((signature
== kHFSPlusSigWord
) || (signature
== kHFSXSigWord
)) //H+ or HX
285 if (vol
->hfs_kind
== 0)
287 // DPRINT("found HFS+\n");
288 vol
->hfs_kind
= FSW_HFS_PLUS
;
291 else if (signature
== kHFSSigWord
) // 'BD'
293 if (be16_to_cpu(mdb
->drEmbedSigWord
) == kHFSPlusSigWord
)
295 DPRINT("found HFS+ inside HFS, untested\n");
296 vol
->hfs_kind
= FSW_HFS_PLUS_EMB
;
297 vol
->emb_block_off
= be32_to_cpu(mdb
->drEmbedExtent
.startBlock
);
298 blockno
+= vol
->emb_block_off
;
304 DPRINT("found plain HFS, unsupported\n");
305 vol
->hfs_kind
= FSW_HFS_PLAIN
;
307 rv
= FSW_UNSUPPORTED
;
312 rv
= FSW_UNSUPPORTED
;
316 status
= fsw_memdup((void **)&vol
->primary_voldesc
, voldesc
,
321 block_size
= be32_to_cpu(voldesc
->blockSize
);
322 vol
->block_size_shift
= fsw_hfs_compute_shift(block_size
);
324 fsw_block_release(vol
, blockno
, buffer
);
327 fsw_set_blocksize(vol
, block_size
, block_size
);
329 /* set default/fallback volume name */
330 s
.type
= FSW_STRING_TYPE_ISO88591
;
331 s
.size
= s
.len
= kHFSMaxVolumeNameChars
;
332 s
.data
= "HFS+ volume";
333 status
= fsw_strdup_coerce(&vol
->g
.label
, vol
->g
.host_string_type
, &s
);
336 /* Setup catalog dnode */
337 status
= fsw_dnode_create_root(vol
, kHFSCatalogFileID
, &vol
->catalog_tree
.file
);
339 fsw_memcpy (vol
->catalog_tree
.file
->extents
,
340 vol
->primary_voldesc
->catalogFile
.extents
,
341 sizeof vol
->catalog_tree
.file
->extents
);
342 vol
->catalog_tree
.file
->g
.size
=
343 be64_to_cpu(vol
->primary_voldesc
->catalogFile
.logicalSize
);
345 /* Setup extents overflow file */
346 status
= fsw_dnode_create_root(vol
, kHFSExtentsFileID
, &vol
->extents_tree
.file
);
347 fsw_memcpy (vol
->extents_tree
.file
->extents
,
348 vol
->primary_voldesc
->extentsFile
.extents
,
349 sizeof vol
->extents_tree
.file
->extents
);
350 vol
->extents_tree
.file
->g
.size
=
351 be64_to_cpu(vol
->primary_voldesc
->extentsFile
.logicalSize
);
353 /* Setup the root dnode */
354 status
= fsw_dnode_create_root(vol
, kHFSRootFolderID
, &vol
->g
.root
);
358 * Read catalog file, we know that first record is in the first node, right after
359 * the node descriptor.
361 r
= fsw_hfs_read_file(vol
->catalog_tree
.file
,
362 sizeof (BTNodeDescriptor
),
363 sizeof (BTHeaderRec
), (fsw_u8
*) &tree_header
);
366 status
= FSW_VOLUME_CORRUPTED
;
369 vol
->case_sensitive
=
370 (signature
== kHFSXSigWord
) &&
371 (tree_header
.keyCompareType
== kHFSBinaryCompare
);
372 vol
->catalog_tree
.root_node
= be32_to_cpu (tree_header
.rootNode
);
373 vol
->catalog_tree
.node_size
= be16_to_cpu (tree_header
.nodeSize
);
376 /* Take Volume Name before tree_header overwritten */
377 firstLeafNum
= be32_to_cpu(tree_header
.firstLeafNode
);
378 catfOffset
= firstLeafNum
* vol
->catalog_tree
.node_size
;
380 r
= fsw_hfs_read_file(vol
->catalog_tree
.file
, catfOffset
, sizeof (cbuff
), cbuff
);
382 if (r
== sizeof (cbuff
))
384 BTNodeDescriptor
* btnd
;
385 HFSPlusCatalogKey
* ck
;
387 btnd
= (BTNodeDescriptor
*) cbuff
;
388 ck
= (HFSPlusCatalogKey
*) (cbuff
+ sizeof(BTNodeDescriptor
));
389 if (btnd
->kind
== kBTLeafNode
&& be32_to_cpu (ck
->parentID
) == kHFSRootParentID
)
391 struct fsw_string vn
;
393 vn
.type
= FSW_STRING_TYPE_UTF16_BE
;
394 vn
.len
= be16_to_cpu (ck
->nodeName
.length
);
395 vn
.size
= vn
.len
* sizeof (fsw_u16
);
396 vn
.data
= ck
->nodeName
.unicode
;
397 fsw_strfree (&vol
->g
.label
);
398 status
= fsw_strdup_coerce(&vol
->g
.label
, vol
->g
.host_string_type
, &vn
);
403 /* Read extents overflow file */
404 r
= fsw_hfs_read_file(vol
->extents_tree
.file
,
405 sizeof (BTNodeDescriptor
),
406 sizeof (BTHeaderRec
), (fsw_u8
*) &tree_header
);
409 status
= FSW_VOLUME_CORRUPTED
;
413 vol
->extents_tree
.root_node
= be32_to_cpu (tree_header
.rootNode
);
414 vol
->extents_tree
.node_size
= be16_to_cpu (tree_header
.nodeSize
);
423 fsw_block_release(vol
, blockno
, buffer
);
427 //Here is a method to obtain Volume label from Apple
428 //how to implement it?
431 UInt32 firstLeafNode;
435 char *nodeBuf, *testKey, *entry;
438 if (HFSInitPartition(ih) == -1) { return; }
440 // Fill some crucial data structures by side effect.
442 HFSGetDirEntry(ih, "/", &dirIndex, &name, &flags, &time, 0, 0);
444 // Now we can loook up the volume name node.
445 nodeSize = SWAP_BE16(gBTHeaders[kBTreeCatalog]->nodeSize);
446 firstLeafNode = SWAP_BE32(gBTHeaders[kBTreeCatalog]->firstLeafNode);
448 dirIndex = (long long) firstLeafNode * nodeSize;
449 index = (long) (*dirIndex % nodeSize); == 0
450 curNode = (long) (*dirIndex / nodeSize); == firstLeafNode
452 //GetCatalogEntry(&dirIndex, &name, &flags, &time, 0, 0);
453 // Read the BTree node and get the record for index.
454 ReadExtent(extent, extentSize, kHFSCatalogFileID,
455 (long long) curNode * nodeSize, nodeSize, nodeBuf, 1);
456 GetBTreeRecord(index, nodeBuf, nodeSize, &testKey, &entry);
458 utf_encodestr(((HFSPlusCatalogKey *)testKey)->nodeName.unicode,
459 SWAP_BE16(((HFSPlusCatalogKey *)testKey)->nodeName.length),
460 (u_int8_t *)gTempStr, 256, OSBigEndian);
464 strncpy(str, name, strMaxLen);
465 str[strMaxLen] = '\0';
469 * Free the volume data structure. Called by the core after an unmount or after
470 * an unsuccessful mount to release the memory used by the file system type specific
471 * part of the volume structure.
474 static void fsw_hfs_volume_free(struct fsw_hfs_volume
*vol
)
476 if (vol
->primary_voldesc
)
478 fsw_free(vol
->primary_voldesc
);
479 vol
->primary_voldesc
= NULL
;
484 * Get in-depth information on a volume.
487 static fsw_status_t
fsw_hfs_volume_stat(struct fsw_hfs_volume
*vol
, struct fsw_volume_stat
*sb
)
489 sb
->total_bytes
= be32_to_cpu(vol
->primary_voldesc
->totalBlocks
) << vol
->block_size_shift
;
490 sb
->free_bytes
= be32_to_cpu(vol
->primary_voldesc
->freeBlocks
) << vol
->block_size_shift
;
495 * Get full information on a dnode from disk. This function is called by the core
496 * whenever it needs to access fields in the dnode structure that may not
497 * be filled immediately upon creation of the dnode.
500 static fsw_status_t
fsw_hfs_dnode_fill(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
506 * Free the dnode data structure. Called by the core when deallocating a dnode
507 * structure to release the memory used by the file system type specific part
508 * of the dnode structure.
511 static void fsw_hfs_dnode_free(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
)
515 static fsw_u32
mac_to_posix(fsw_u32 mac_time
)
517 /* Mac time is 1904 year based */
518 return mac_time
? mac_time
- 2082844800 : 0;
522 * Get in-depth information on a dnode. The core makes sure that fsw_hfs_dnode_fill
523 * has been called on the dnode before this function is called. Note that some
524 * data is not directly stored into the structure, but passed to a host-specific
525 * callback that converts it to the host-specific format.
528 static fsw_status_t
fsw_hfs_dnode_stat(struct fsw_hfs_volume
*vol
,
529 struct fsw_hfs_dnode
*dno
,
530 struct fsw_dnode_stat
*sb
)
532 sb
->used_bytes
= dno
->used_bytes
;
533 fsw_store_time_posix(sb
, FSW_DNODE_STAT_CTIME
, mac_to_posix(dno
->ctime
));
534 fsw_store_time_posix(sb
, FSW_DNODE_STAT_MTIME
, mac_to_posix(dno
->mtime
));
535 fsw_store_time_posix(sb
, FSW_DNODE_STAT_ATIME
, 0);
536 fsw_store_attr_posix(sb
, 0700);
542 fsw_hfs_find_block(HFSPlusExtentRecord
* exts
,
547 fsw_u32 cur_lbno
= *lbno
;
549 for (i
= 0; i
< 8; i
++)
551 fsw_u32 start
= be32_to_cpu ((*exts
)[i
].startBlock
);
552 fsw_u32 count
= be32_to_cpu ((*exts
)[i
].blockCount
);
554 if (cur_lbno
< count
)
556 *pbno
= start
+ cur_lbno
;
568 /* Find record offset, numbering starts from the end */
570 fsw_hfs_btree_recoffset (struct fsw_hfs_btree
* btree
,
571 BTNodeDescriptor
* node
,
574 fsw_u8
*cnode
= (fsw_u8
*) node
;
576 recptr
= (fsw_u16
*) (cnode
+btree
->node_size
- index
* 2 - 2);
577 return be16_to_cpu(*recptr
);
580 /* Pointer to the key inside node */
582 fsw_hfs_btree_rec (struct fsw_hfs_btree
* btree
,
583 BTNodeDescriptor
* node
,
586 fsw_u8
*cnode
= (fsw_u8
*) node
;
588 offset
= fsw_hfs_btree_recoffset (btree
, node
, index
);
589 return (BTreeKey
*) (cnode
+ offset
);
594 fsw_hfs_btree_search (struct fsw_hfs_btree
* btree
,
596 int (*compare_keys
) (BTreeKey
* key1
, BTreeKey
* key2
),
597 BTNodeDescriptor
** result
,
598 fsw_u32
* key_offset
)
600 BTNodeDescriptor
* node
;
604 fsw_u8
* buffer
= NULL
;
606 currnode
= btree
->root_node
;
607 status
= fsw_alloc(btree
->node_size
, &buffer
);
610 node
= (BTNodeDescriptor
*)buffer
;
621 if (fsw_hfs_read_file (btree
->file
,
622 (fsw_u64
)currnode
* btree
->node_size
,
623 btree
->node_size
, buffer
) <= 0)
625 status
= FSW_VOLUME_CORRUPTED
;
629 if (be16_to_cpu(*(fsw_u16
*)(buffer
+ btree
->node_size
- 2)) != sizeof(BTNodeDescriptor
))
630 BP("corrupted node\n");
632 count
= be16_to_cpu (node
->numRecords
);
635 for (rec
= 0; rec
< count
; rec
++)
639 currkey
= fsw_hfs_btree_rec (btree
, node
, rec
);
640 cmp
= compare_keys (currkey
, key
);
641 //fprintf(stderr, "rec=%d cmp=%d kind=%d \n", rec, cmp, node->kind);
644 if (node
->kind
== kBTLeafNode
)
652 status
= FSW_SUCCESS
;
656 else if (node
->kind
== kBTIndexNode
)
663 pointer
= (fsw_u32
*) ((char *) currkey
664 + be16_to_cpu (currkey
->length16
)
666 currnode
= be32_to_cpu (*pointer
);
671 if (node
->kind
== kBTLeafNode
&& cmp
< 0 && node
->fLink
)
673 currnode
= be32_to_cpu(node
->fLink
);
678 status
= FSW_NOT_FOUND
;
682 /* Perform binary search */
684 fsw_u32 upper
= count
- 1;
686 BTreeKey
*currkey
= NULL
;
690 status
= FSW_NOT_FOUND
;
694 while (lower
<= upper
)
696 fsw_u32 index
= (lower
+ upper
) / 2;
698 currkey
= fsw_hfs_btree_rec (btree
, node
, index
);
700 cmp
= compare_keys (currkey
, key
);
701 if (cmp
< 0) upper
= index
- 1;
702 if (cmp
> 0) lower
= index
+ 1;
709 status
= FSW_SUCCESS
;
715 currkey
= fsw_hfs_btree_rec (btree
, node
, upper
);
717 if (node
->kind
== kBTIndexNode
&& currkey
)
721 pointer
= (fsw_u32
*) ((char *) currkey
722 + be16_to_cpu (currkey
->length16
)
724 currnode
= be32_to_cpu (*pointer
);
728 status
= FSW_NOT_FOUND
;
736 if (buffer
!= NULL
&& status
!= FSW_SUCCESS
)
745 struct fsw_string
* name
;
750 HFSPlusExtentRecord extents
;
755 fsw_u32 cur_pos
; /* current position */
757 struct fsw_hfs_volume
* vol
;
759 struct fsw_shandle
* shandle
; /* this one track iterator's state */
760 file_info_t file_info
;
761 } visitor_parameter_t
;
764 fsw_hfs_btree_visit_node(BTreeKey
*record
, void* param
)
766 visitor_parameter_t
* vp
= (visitor_parameter_t
*)param
;
767 fsw_u8
* base
= (fsw_u8
*)record
->rawData
+ be16_to_cpu(record
->length16
) + 2;
768 fsw_u16 rec_type
= be16_to_cpu(*(fsw_u16
*)base
);
769 struct HFSPlusCatalogKey
* cat_key
= (HFSPlusCatalogKey
*)record
;
773 struct fsw_string
* file_name
;
775 if (be32_to_cpu(cat_key
->parentID
) != vp
->parent
)
778 /* not smth we care about */
779 if (vp
->shandle
->pos
!= vp
->cur_pos
++)
784 case kHFSPlusFolderRecord
:
786 HFSPlusCatalogFolder
* folder_info
= (HFSPlusCatalogFolder
*)base
;
788 vp
->file_info
.id
= be32_to_cpu(folder_info
->folderID
);
789 vp
->file_info
.type
= FSW_DNODE_TYPE_DIR
;
790 vp
->file_info
.size
= be32_to_cpu(folder_info
->valence
);
791 vp
->file_info
.used
= be32_to_cpu(folder_info
->valence
);
792 vp
->file_info
.ctime
= be32_to_cpu(folder_info
->createDate
);
793 vp
->file_info
.mtime
= be32_to_cpu(folder_info
->contentModDate
);
796 case kHFSPlusFileRecord
:
798 HFSPlusCatalogFile
* file_info
= (HFSPlusCatalogFile
*)base
;
800 vp
->file_info
.id
= be32_to_cpu(file_info
->fileID
);
801 vp
->file_info
.type
= FSW_DNODE_TYPE_FILE
;
802 vp
->file_info
.size
= be64_to_cpu(file_info
->dataFork
.logicalSize
);
803 vp
->file_info
.used
= LShiftU64(be32_to_cpu(file_info
->dataFork
.totalBlocks
),
804 vp
->vol
->block_size_shift
);
805 vp
->file_info
.ctime
= be32_to_cpu(file_info
->createDate
);
806 vp
->file_info
.mtime
= be32_to_cpu(file_info
->contentModDate
);
807 fsw_memcpy(&vp
->file_info
.extents
, &file_info
->dataFork
.extents
,
808 sizeof vp
->file_info
.extents
);
811 case kHFSPlusFolderThreadRecord
:
812 case kHFSPlusFileThreadRecord
:
818 BP("unknown file type\n");
819 vp
->file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
823 name_len
= be16_to_cpu(cat_key
->nodeName
.length
);
825 file_name
= vp
->file_info
.name
;
826 file_name
->len
= name_len
;
827 fsw_memdup(&file_name
->data
, &cat_key
->nodeName
.unicode
[0], 2*name_len
);
828 file_name
->size
= 2*name_len
;
829 file_name
->type
= FSW_STRING_TYPE_UTF16
;
830 name_ptr
= (fsw_u16
*)file_name
->data
;
831 for (i
=0; i
<name_len
; i
++)
833 name_ptr
[i
] = be16_to_cpu(name_ptr
[i
]);
841 fsw_hfs_btree_iterate_node (struct fsw_hfs_btree
* btree
,
842 BTNodeDescriptor
* first_node
,
844 int (*callback
) (BTreeKey
*record
, void* param
),
848 /* We modify node, so make a copy */
849 BTNodeDescriptor
* node
= first_node
;
850 fsw_u8
* buffer
= NULL
;
852 status
= fsw_alloc(btree
->node_size
, &buffer
);
859 fsw_u32 count
= be16_to_cpu(node
->numRecords
);
862 /* Iterate over all records in this node. */
863 for (i
= first_rec
; i
< count
; i
++)
865 int rv
= callback(fsw_hfs_btree_rec (btree
, node
, i
), param
);
870 status
= FSW_SUCCESS
;
873 status
= FSW_NOT_FOUND
;
876 /* if callback returned 0 - continue */
879 next_node
= be32_to_cpu(node
->fLink
);
883 status
= FSW_NOT_FOUND
;
887 if (fsw_hfs_read_file (btree
->file
,
888 next_node
* btree
->node_size
,
889 btree
->node_size
, buffer
) <= 0)
891 status
= FSW_VOLUME_CORRUPTED
;
895 node
= (BTNodeDescriptor
*)buffer
;
906 void deb(fsw_u16
* p
, int len
, int swap
)
909 for (i
=0; i
<len
; i
++)
911 printf("%c", swap
? be16_to_cpu(p
[i
]) : p
[i
]);
918 fsw_hfs_cmp_extkey(BTreeKey
* key1
, BTreeKey
* key2
)
920 HFSPlusExtentKey
* ekey1
= (HFSPlusExtentKey
*)key1
;
921 HFSPlusExtentKey
* ekey2
= (HFSPlusExtentKey
*)key2
;
924 /* First key is read from the FS data, second is in-memory in CPU endianess */
925 result
= be32_to_cpu(ekey1
->fileID
) - ekey2
->fileID
;
930 result
= ekey1
->forkType
- ekey2
->forkType
;
935 result
= be32_to_cpu(ekey1
->startBlock
) - ekey2
->startBlock
;
940 fsw_hfs_cmp_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
942 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
943 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
952 parentId1
= be32_to_cpu(ckey1
->parentID
);
954 if (parentId1
> ckey2
->parentID
)
956 if (parentId1
< ckey2
->parentID
)
959 p1
= &ckey1
->nodeName
.unicode
[0];
960 p2
= &ckey2
->nodeName
.unicode
[0];
961 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
966 /* get next valid character from ckey1 */
967 for (lc
= 0; lc
== 0 && apos
< key1Len
; apos
++) {
968 ac
= be16_to_cpu(p1
[apos
]);
973 /* get next valid character from ckey2 */
974 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
980 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
986 fsw_hfs_cmpi_catkey (BTreeKey
*key1
, BTreeKey
*key2
)
988 HFSPlusCatalogKey
*ckey1
= (HFSPlusCatalogKey
*)key1
;
989 HFSPlusCatalogKey
*ckey2
= (HFSPlusCatalogKey
*)key2
;
998 parentId1
= be32_to_cpu(ckey1
->parentID
);
1000 if (parentId1
> ckey2
->parentID
)
1002 if (parentId1
< ckey2
->parentID
)
1005 key1Len
= be16_to_cpu (ckey1
->nodeName
.length
);
1007 if (key1Len
== 0 && ckey2
->nodeName
.length
== 0)
1010 p1
= &ckey1
->nodeName
.unicode
[0];
1011 p2
= &ckey2
->nodeName
.unicode
[0];
1017 /* get next valid character from ckey1 */
1018 for (lc
= 0; lc
== 0 && apos
< key1Len
; apos
++) {
1019 ac
= be16_to_cpu(p1
[apos
]);
1020 lc
= ac
? fsw_to_lower(ac
) : 0;
1024 /* get next valid character from ckey2 */
1025 for (lc
= 0; lc
== 0 && bpos
< ckey2
->nodeName
.length
; bpos
++) {
1027 lc
= bc
? fsw_to_lower(bc
) : 0;
1031 if (ac
!= bc
|| (ac
== 0 && bc
== 0))
1037 * Retrieve file data mapping information. This function is called by the core when
1038 * fsw_shandle_read needs to know where on the disk the required piece of the file's
1039 * data can be found. The core makes sure that fsw_hfs_dnode_fill has been called
1040 * on the dnode before. Our task here is to get the physical disk block number for
1041 * the requested logical block number.
1044 static fsw_status_t
fsw_hfs_get_extent(struct fsw_hfs_volume
* vol
,
1045 struct fsw_hfs_dnode
* dno
,
1046 struct fsw_extent
* extent
)
1048 fsw_status_t status
;
1050 HFSPlusExtentRecord
*exts
;
1051 BTNodeDescriptor
*node
= NULL
;
1053 extent
->type
= FSW_EXTENT_TYPE_PHYSBLOCK
;
1054 extent
->log_count
= 1;
1055 lbno
= extent
->log_start
;
1057 /* we only care about data forks atm, do we? */
1058 exts
= &dno
->extents
;
1062 struct HFSPlusExtentKey
* key
;
1063 struct HFSPlusExtentKey overflowkey
;
1067 if (fsw_hfs_find_block(exts
, &lbno
, &phys_bno
))
1069 extent
->phys_start
= phys_bno
+ vol
->emb_block_off
;
1070 status
= FSW_SUCCESS
;
1075 /* Find appropriate overflow record */
1076 overflowkey
.fileID
= dno
->g
.dnode_id
;
1077 overflowkey
.startBlock
= extent
->log_start
- lbno
;
1085 status
= fsw_hfs_btree_search (&vol
->extents_tree
,
1086 (BTreeKey
*)&overflowkey
,
1092 key
= (struct HFSPlusExtentKey
*)
1093 fsw_hfs_btree_rec (&vol
->extents_tree
, node
, ptr
);
1094 exts
= (HFSPlusExtentRecord
*) (key
+ 1);
1103 static const fsw_u16
* g_blacklist
[] =
1105 //L"AppleIntelCPUPowerManagement.kext",
1110 //#define HFS_FILE_INJECTION
1112 #ifdef HFS_FILE_INJECTION
1115 const fsw_u16
* path
;
1116 const fsw_u16
* name
;
1120 L
"/System/Library/Extensions",
1121 L
"ApplePS2Controller.kext"
1131 create_hfs_dnode(struct fsw_hfs_dnode
* dno
,
1132 file_info_t
* file_info
,
1133 struct fsw_hfs_dnode
** child_dno_out
)
1135 fsw_status_t status
;
1136 struct fsw_hfs_dnode
* baby
;
1138 status
= fsw_dnode_create(dno
, file_info
->id
, file_info
->type
,
1139 file_info
->name
, &baby
);
1143 baby
->g
.size
= file_info
->size
;
1144 baby
->used_bytes
= file_info
->used
;
1145 baby
->ctime
= file_info
->ctime
;
1146 baby
->mtime
= file_info
->mtime
;
1149 /* Fill-in extents info */
1150 if (file_info
->type
== FSW_DNODE_TYPE_FILE
)
1152 fsw_memcpy(baby
->extents
, &file_info
->extents
, sizeof file_info
->extents
);
1155 *child_dno_out
= baby
;
1162 * Lookup a directory's child dnode by name. This function is called on a directory
1163 * to retrieve the directory entry with the given name. A dnode is constructed for
1164 * this entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1165 * and the dnode is actually a directory.
1168 static fsw_status_t
fsw_hfs_dir_lookup(struct fsw_hfs_volume
* vol
,
1169 struct fsw_hfs_dnode
* dno
,
1170 struct fsw_string
* lookup_name
,
1171 struct fsw_hfs_dnode
** child_dno_out
)
1173 fsw_status_t status
;
1174 struct HFSPlusCatalogKey catkey
;
1177 BTNodeDescriptor
* node
= NULL
;
1178 struct fsw_string rec_name
;
1179 int free_data
= 0, i
;
1180 HFSPlusCatalogKey
* file_key
;
1181 file_info_t file_info
;
1185 fsw_memzero(&file_info
, sizeof file_info
);
1186 file_info
.name
= &rec_name
;
1188 catkey
.parentID
= dno
->g
.dnode_id
;
1189 catkey
.nodeName
.length
= (fsw_u16
)lookup_name
->len
;
1191 /* no need to allocate anything */
1192 if (lookup_name
->type
== FSW_STRING_TYPE_UTF16
)
1194 fsw_memcpy(catkey
.nodeName
.unicode
, lookup_name
->data
, lookup_name
->size
);
1195 rec_name
= *lookup_name
;
1198 status
= fsw_strdup_coerce(&rec_name
, FSW_STRING_TYPE_UTF16
, lookup_name
);
1199 /* nothing allocated so far */
1203 fsw_memcpy(catkey
.nodeName
.unicode
, rec_name
.data
, rec_name
.size
);
1206 /* Dirty hack: blacklisting of certain files on FS driver level */
1207 for (i
= 0; g_blacklist
[i
]; i
++)
1209 if (fsw_memeq(g_blacklist
[i
], catkey
.nodeName
.unicode
, catkey
.nodeName
.length
*2))
1211 DPRINT2("Blacklisted %s\n", g_blacklist
[i
]);
1212 status
= FSW_NOT_FOUND
;
1217 #ifdef HFS_FILE_INJECTION
1218 if (fsw_hfs_inject(vol
,
1220 catkey
.nodeName
.unicode
,
1221 catkey
.nodeName
.length
,
1224 status
= FSW_SUCCESS
;
1229 catkey
.keyLength
= (fsw_u16
)(5 + rec_name
.size
);
1231 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1233 vol
->case_sensitive
?
1234 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
1239 file_key
= (HFSPlusCatalogKey
*)fsw_hfs_btree_rec (&vol
->catalog_tree
, node
, ptr
);
1240 /* for plain HFS "-(keySize & 1)" would be needed */
1241 base
= (fsw_u8
*)file_key
+ be16_to_cpu(file_key
->keyLength
) + 2;
1242 rec_type
= be16_to_cpu(*(fsw_u16
*)base
);
1244 /** @todo: read additional info */
1247 case kHFSPlusFolderRecord
:
1249 HFSPlusCatalogFolder
* info
= (HFSPlusCatalogFolder
*)base
;
1251 file_info
.id
= be32_to_cpu(info
->folderID
);
1252 file_info
.type
= FSW_DNODE_TYPE_DIR
;
1253 /* @todo: return number of elements, maybe use smth else */
1254 file_info
.size
= be32_to_cpu(info
->valence
);
1255 file_info
.used
= be32_to_cpu(info
->valence
);
1256 file_info
.ctime
= be32_to_cpu(info
->createDate
);
1257 file_info
.mtime
= be32_to_cpu(info
->contentModDate
);
1260 case kHFSPlusFileRecord
:
1262 HFSPlusCatalogFile
* info
= (HFSPlusCatalogFile
*)base
;
1264 file_info
.id
= be32_to_cpu(info
->fileID
);
1265 file_info
.type
= FSW_DNODE_TYPE_FILE
;
1266 file_info
.size
= be64_to_cpu(info
->dataFork
.logicalSize
);
1267 file_info
.used
= LShiftU64(be32_to_cpu(info
->dataFork
.totalBlocks
), vol
->block_size_shift
);
1268 file_info
.ctime
= be32_to_cpu(info
->createDate
);
1269 file_info
.mtime
= be32_to_cpu(info
->contentModDate
);
1270 fsw_memcpy(&file_info
.extents
, &info
->dataFork
.extents
,
1271 sizeof file_info
.extents
);
1275 BP("unknown file type\n");
1276 file_info
.type
= FSW_DNODE_TYPE_UNKNOWN
;
1280 #ifdef HFS_FILE_INJECTION
1283 status
= create_hfs_dnode(dno
, &file_info
, child_dno_out
);
1293 fsw_strfree(&rec_name
);
1299 * Get the next directory entry when reading a directory. This function is called during
1300 * directory iteration to retrieve the next directory entry. A dnode is constructed for
1301 * the entry and returned. The core makes sure that fsw_hfs_dnode_fill has been called
1302 * and the dnode is actually a directory. The shandle provided by the caller is used to
1303 * record the position in the directory between calls.
1306 static fsw_status_t
fsw_hfs_dir_read(struct fsw_hfs_volume
*vol
,
1307 struct fsw_hfs_dnode
*dno
,
1308 struct fsw_shandle
*shand
,
1309 struct fsw_hfs_dnode
**child_dno_out
)
1311 fsw_status_t status
;
1312 struct HFSPlusCatalogKey catkey
;
1314 BTNodeDescriptor
* node
= NULL
;
1316 visitor_parameter_t param
;
1317 struct fsw_string rec_name
;
1319 catkey
.parentID
= dno
->g
.dnode_id
;
1320 catkey
.nodeName
.length
= 0;
1322 fsw_memzero(¶m
, sizeof(param
));
1324 rec_name
.type
= FSW_STRING_TYPE_EMPTY
;
1325 param
.file_info
.name
= &rec_name
;
1327 status
= fsw_hfs_btree_search (&vol
->catalog_tree
,
1329 vol
->case_sensitive
?
1330 fsw_hfs_cmp_catkey
: fsw_hfs_cmpi_catkey
,
1335 /* Iterator updates shand state */
1337 param
.shandle
= shand
;
1338 param
.parent
= dno
->g
.dnode_id
;
1340 status
= fsw_hfs_btree_iterate_node (&vol
->catalog_tree
,
1343 fsw_hfs_btree_visit_node
,
1348 status
= create_hfs_dnode(dno
, ¶m
.file_info
, child_dno_out
);
1354 fsw_strfree(&rec_name
);
1360 * Get the target path of a symbolic link. This function is called when a symbolic
1361 * link needs to be resolved. The core makes sure that the fsw_hfs_dnode_fill has been
1362 * called on the dnode and that it really is a symlink.
1365 static fsw_status_t
fsw_hfs_readlink(struct fsw_hfs_volume
*vol
, struct fsw_hfs_dnode
*dno
,
1366 struct fsw_string
*link_target
)
1368 return FSW_UNSUPPORTED
;