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