2 * libhfsp - library for reading and writing Macintosh HFS+ volumes.
4 * a record contains a key and a folder or file and is part
7 * Copyright (C) 2000 Klaus Halfmann <khalfmann@libra.de>
8 * Original 1996-1998 Robert Leslie <rob@mars.org>
9 * Additional work by Brad Boyer (flar@pants.nu)
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston,
26 * $Id: record.c,v 1.24 2000/10/17 05:58:46 hasi Exp $
38 /* read a hfsp_cat_key from memory */
39 void* record_readkey(void* p
, void* buf
)
41 hfsp_cat_key
* key
= (hfsp_cat_key
*) buf
;
43 UInt16 key_length
, len
,i
;
46 key
->key_length
= key_length
= bswabU16_inc(p
);
48 key
->parent_cnid
= bswabU32_inc(p
);
49 key
->name
.strlen
= len
= bswabU16_inc(p
);
51 for (i
=0; i
< len
; i
++, cp
++)
52 *cp
= bswabU16_inc(p
);
53 /* check if keylenght was correct */
54 if (key_length
!= ((char*) p
) - ((char*) check
))
55 HFSP_ERROR(EINVAL
, "Invalid key length in record_readkey");
61 /* read a hfsp_extent_key from memory */
62 void* record_extent_readkey(void* p
, void* buf
)
64 hfsp_extent_key
* key
= (hfsp_extent_key
*) buf
;
67 key
->key_length
= key_length
= bswabU16_inc(p
);
68 key
->fork_type
= bswabU8_inc(p
);
69 key
->filler
= bswabU8_inc(p
);
71 HFSP_ERROR(-1, "Invalid key length in record_extent_readkey");
72 key
->file_id
= bswabU32_inc(p
);
73 key
->start_block
= bswabU32_inc(p
);
80 /* read posix permission from memory */
81 static inline void* record_readperm(void *p
, hfsp_perm
* perm
)
83 perm
->owner
= bswabU32_inc(p
);
84 perm
->group
= bswabU32_inc(p
);
85 perm
->mode
= bswabU32_inc(p
);
86 perm
->dev
= bswabU32_inc(p
);
90 /* read directory info */
91 static inline void* record_readDInfo(void *p
, DInfo
* info
)
93 info
->frRect
.top
= bswabU16_inc(p
);
94 info
->frRect
.left
= bswabU16_inc(p
);
95 info
->frRect
.bottom
= bswabU16_inc(p
);
96 info
->frRect
.right
= bswabU16_inc(p
);
97 info
->frFlags
= bswabU16_inc(p
);
98 info
->frLocation
.v
= bswabU16_inc(p
);
99 info
->frLocation
.h
= bswabU16_inc(p
);
100 info
->frView
= bswabU16_inc(p
);
104 /* read extra Directory info */
105 static inline void* record_readDXInfo(void *p
, DXInfo
* xinfo
)
107 xinfo
->frScroll
.v
= bswabU16_inc(p
);
108 xinfo
->frScroll
.h
= bswabU16_inc(p
);
109 xinfo
->frOpenChain
= bswabU32_inc(p
);
110 xinfo
->frUnused
= bswabU16_inc(p
);
111 xinfo
->frComment
= bswabU16_inc(p
);
112 xinfo
->frPutAway
= bswabU32_inc(p
);
116 /* read a hfsp_cat_folder from memory */
117 static void* record_readfolder(void *p
, hfsp_cat_folder
* folder
)
119 folder
->flags
= bswabU16_inc(p
);
120 folder
->valence
= bswabU32_inc(p
);
121 folder
->id
= bswabU32_inc(p
);
122 folder
->create_date
= bswabU32_inc(p
);
123 folder
->content_mod_date
= bswabU32_inc(p
);
124 folder
->attribute_mod_date
= bswabU32_inc(p
);
125 folder
->access_date
= bswabU32_inc(p
);
126 folder
->backup_date
= bswabU32_inc(p
);
127 p
= record_readperm (p
, &folder
->permissions
);
128 p
= record_readDInfo (p
, &folder
->user_info
);
129 p
= record_readDXInfo (p
, &folder
->finder_info
);
130 folder
->text_encoding
= bswabU32_inc(p
);
131 folder
->reserved
= bswabU32_inc(p
);
136 static inline void* record_readFInfo(void *p
, FInfo
* info
)
138 info
->fdType
= bswabU32_inc(p
);
139 info
->fdCreator
= bswabU32_inc(p
);
140 info
->fdFlags
= bswabU16_inc(p
);
141 info
->fdLocation
.v
= bswabU16_inc(p
);
142 info
->fdLocation
.h
= bswabU16_inc(p
);
143 info
->fdFldr
= bswabU16_inc(p
);
147 /* read extra File info */
148 static inline void* record_readFXInfo(void *p
, FXInfo
* xinfo
)
151 xinfo
->fdIconID
= bswabU16_inc(p
);
155 xinfo
->fdComment
= bswabU16_inc(p
);
156 xinfo
->fdPutAway
= bswabU32_inc(p
);
160 /* read a hfsp_cat_file from memory */
161 static void* record_readfile(void *p
, hfsp_cat_file
* file
)
163 file
->flags
= bswabU16_inc(p
);
164 file
->reserved1
= bswabU32_inc(p
);
165 file
->id
= bswabU32_inc(p
);
166 file
->create_date
= bswabU32_inc(p
);
167 file
->content_mod_date
= bswabU32_inc(p
);
168 file
->attribute_mod_date
= bswabU32_inc(p
);
169 file
->access_date
= bswabU32_inc(p
);
170 file
->backup_date
= bswabU32_inc(p
);
171 p
= record_readperm (p
, &file
->permissions
);
172 p
= record_readFInfo (p
, &file
->user_info
);
173 p
= record_readFXInfo (p
, &file
->finder_info
);
174 file
->text_encoding
= bswabU32_inc(p
);
175 file
->reserved2
= bswabU32_inc(p
);
176 p
= volume_readfork (p
, &file
->data_fork
);
177 return volume_readfork (p
, &file
->res_fork
);
180 /* read a hfsp_cat_thread from memory */
181 static void* record_readthread(void *p
, hfsp_cat_thread
* entry
)
187 entry
-> reserved
= bswabU16_inc(p
);
188 entry
-> parentID
= bswabU32_inc(p
);
189 entry
->nodeName
.strlen
= len
= bswabU16_inc(p
);
190 cp
= entry
->nodeName
.name
;
192 HFSP_ERROR(-1, "Invalid key length in record thread");
193 for (i
=0; i
< len
; i
++, cp
++)
194 *cp
= bswabU16_inc(p
);
200 /* read a hfsp_cat_entry from memory */
201 static void* record_readentry(void *p
, hfsp_cat_entry
* entry
)
203 UInt16 type
= bswabU16_inc(p
);
208 return record_readfolder(p
, &entry
->u
.folder
);
210 return record_readfile (p
, &entry
->u
.file
);
211 case HFSP_FOLDER_THREAD
:
212 case HFSP_FILE_THREAD
:
213 return record_readthread(p
, &entry
->u
.thread
);
215 HFSP_ERROR(-1, "Unexpected record type in record_readentry");
222 /* Most of the functions here will not change the node in the btree,
223 But this must be changed in the future ... */
226 /* intialize the record with the given index entry in the btree. */
227 static int record_init(record
* r
, btree
* bt
, node_buf
* buf
, UInt16 index
)
231 p
= btree_key_by_index(bt
,buf
,index
);
234 p
= record_readkey (p
, &r
->key
);
237 p
= record_readentry(p
, &r
->record
);
240 r
->node_index
= buf
->index
;
246 /* intialize the record with the given index entry in the btree. */
247 static int record_init_extent(extent_record
* r
, btree
* bt
, node_buf
* buf
, UInt16 index
)
251 p
= btree_key_by_index(bt
, buf
,index
);
254 p
= record_extent_readkey(p
, &r
->key
);
257 p
= volume_readextent(p
, r
->extent
);
260 r
->node_index
= buf
->index
;
266 /* intialize the record to the first record of the tree
267 * which is (per design) the root node.
269 int record_init_root(record
* r
, btree
* tree
)
271 // Position to first leaf node ...
272 UInt32 leaf_head
= tree
->head
.leaf_head
;
273 node_buf
* buf
= btree_node_by_index(tree
, leaf_head
);
276 return record_init(r
, tree
, buf
, 0);
279 /* Compare two cat_keys ... */
280 int record_key_compare(void* k1
, void* k2
)
282 hfsp_cat_key
* key1
= (hfsp_cat_key
*) k1
;
283 hfsp_cat_key
* key2
= (hfsp_cat_key
*) k2
;
284 int diff
= key2
->parent_cnid
- key1
->parent_cnid
;
285 if (!diff
) // same parent
286 diff
= fast_unicode_compare(&key1
->name
, &key2
->name
);
290 /* Compare two extent_keys ... */
291 int record_extent_key_compare(void* k1
, void* k2
)
293 hfsp_extent_key
* key1
= (hfsp_extent_key
*) k1
;
294 hfsp_extent_key
* key2
= (hfsp_extent_key
*) k2
;
295 int diff
= key2
->fork_type
- key1
->fork_type
;
296 if (!diff
) // same type
298 diff
= key2
->file_id
- key1
->file_id
;
299 if (!diff
) // same file
300 diff
= key2
->start_block
- key1
->start_block
;
305 /* Position node in btree so that key might be inside */
306 static node_buf
* record_find_node(btree
* tree
, void *key
)
308 int start
, end
, mid
, comp
; // components of a binary search
310 char curr_key
[tree
->head
.max_key_len
];
311 // The current key under examination
312 hfsp_key_read readkey
= tree
->kread
;
313 hfsp_key_compare key_compare
= tree
->kcomp
;
315 node_buf
* node
= btree_node_by_index(tree
, tree
->head
.root
);
317 HFSP_ERROR(-1, "record_find_node: Cant position to root node");
318 while (node
->desc
.kind
== HFSP_NODE_NDX
)
321 end
= node
->desc
.num_rec
;
325 mid
= (start
+ end
) >> 1;
326 p
= btree_key_by_index(tree
, node
, mid
);
328 HFSP_ERROR(-1, "record_find_node: unexpected error");
329 p
= readkey (p
, curr_key
);
331 HFSP_ERROR(-1, "record_find_node: unexpected error");
332 comp
= key_compare(curr_key
, key
);
340 if (!p
) // Empty tree, fascinating ...
341 HFSP_ERROR(-1, "record_find_node: unexpected empty node");
342 if (comp
< 0) // mmh interesting key is before this key ...
345 return NULL
; // nothing before this key ..
346 p
= btree_key_by_index(tree
, node
, mid
-1);
348 HFSP_ERROR(-1, "record_find_node: unexpected error");
349 p
= readkey (p
, curr_key
);
351 HFSP_ERROR(-1, "record_find_node: unexpected error");
354 index
= bswabU32_inc(p
);
355 node
= btree_node_by_index(tree
, index
);
357 return node
; // go on and use the found node
362 /* search for the given key in the btree.
364 * returns pointer to memory just after key or NULL
365 * In any case *keyind recives the index where the
366 * key was found (or could be inserted.)
369 record_find_key(btree
* tree
, void* key
, int* keyind
, UInt16
* node_index
)
371 node_buf
* buf
= record_find_node(tree
, key
);
375 int start
= 0; // components of a binary search
376 int end
= buf
->desc
.num_rec
;
379 char curr_key
[tree
->head
.max_key_len
];
380 hfsp_key_read readkey
= tree
->kread
;
381 hfsp_key_compare key_compare
= tree
->kcomp
;
384 mid
= (start
+ end
) >> 1;
385 p
= btree_key_by_index(tree
, buf
, mid
);
387 HFSP_ERROR(-1, "record_init_key: unexpected error");
388 p
= readkey (p
, curr_key
);
390 HFSP_ERROR(-1, "record_init_cat_key: unexpected error");
391 comp
= key_compare(curr_key
, key
);
399 if (!p
) // Empty tree, fascinating ...
400 HFSP_ERROR(ENOENT
, "record_init_key: unexpected empty node");
402 *node_index
= buf
->index
;
403 if (!comp
) // found something ...
406 HFSP_ERROR(ENOENT
, NULL
);
411 /* intialize the record by searching for the given key in the btree.
413 * r is umodified on error.
416 record_init_key(record
* r
, btree
* tree
, hfsp_cat_key
* key
)
420 void *p
= record_find_key(tree
, key
, &keyind
, &node_index
);
425 r
-> node_index
= node_index
;
426 r
-> keyind
= keyind
;
427 r
-> key
= *key
; // Better use a record_key_copy ...
428 p
= record_readentry(p
, &r
->record
);
430 HFSP_ERROR(-1, "record_init_key: unexpected error");
437 /* intialize the extent_record to the extent identified by the
438 * (first) blockindex.
440 * forktype: either HFSP_EXTEND_DATA or HFSP_EXTEND_RSRC
442 int record_init_file(extent_record
* r
, btree
* tree
,
443 UInt8 forktype
, UInt32 fileId
, UInt32 blockindex
)
447 hfsp_extent_key key
= { 10, forktype
, 0, fileId
, blockindex
};
448 void *p
= record_find_key(tree
, &key
, &keyind
, &node_index
);
453 r
-> node_index
= node_index
;
454 r
-> keyind
= keyind
;
455 r
-> key
= key
; // Better use a record_key_copy ...
456 p
= volume_readextent(p
, r
->extent
);
458 HFSP_ERROR(-1, "record_init_file: unexpected error");
465 /* intialize the record to the folder identified by cnid
467 int record_init_cnid(record
* r
, btree
* tree
, UInt32 cnid
)
469 hfsp_cat_key thread_key
; // the thread is the first record
471 thread_key
.key_length
= 6; // null name (like '.' in unix )
472 thread_key
.parent_cnid
= cnid
;
473 thread_key
.name
.strlen
= 0;
475 return record_init_key(r
, tree
, &thread_key
);
478 /* intialize the record to the first record of the parent.
480 int record_init_parent(record
* r
, record
* parent
)
482 if (parent
->record
.type
== HFSP_FOLDER
)
483 return record_init_cnid(r
, parent
->tree
, parent
->record
.u
.folder
.id
);
484 else if(parent
->record
.type
== HFSP_FOLDER_THREAD
)
487 *r
= *parent
; // The folder thread is in fact the first entry, like '.'
491 "record_init_parent: parent is neither folder nor folder thread.");
498 /* find correct node record for given node and *pindex.
500 * index of record in this (or next) node
502 static node_buf
* prepare_next(btree
* tree
, UInt16 node_index
, UInt16
* pindex
)
504 node_buf
* buf
= btree_node_by_index(tree
, node_index
);
505 btree_node_desc
* desc
= &buf
->desc
;
506 UInt32 numrec
= desc
->num_rec
;
507 if (*pindex
>= numrec
) // move on to next node
509 UInt16 next
= desc
->next
;
511 if (!next
/* is there a next node ? */
512 || !( buf
= btree_node_by_index(tree
, next
)))
517 /* move record foreward to next entry.
519 * In case of an error the value of *r is undefined !
521 int record_next(record
* r
)
523 btree
* tree
= r
->tree
;
524 UInt16 index
= r
->keyind
+1;
526 node_buf
* buf
= prepare_next(tree
, r
->node_index
, &index
);
529 return ENOENT
; // No (more) such file or directory
531 parent
= r
->key
.parent_cnid
;
533 if (record_init(r
, tree
, buf
, index
))
536 if (r
->key
.parent_cnid
!= parent
|| // end of current directory
537 index
!= r
->keyind
) // internal error ?
538 return ENOENT
; // No (more) such file or directory
543 /* move record foreward to next extent record.
545 * In case of an error the value of *r is undefined !
547 int record_next_extent(extent_record
* r
)
549 btree
* tree
= r
->tree
;
550 UInt16 index
= r
->keyind
+1;
553 node_buf
* buf
= prepare_next(tree
, r
->node_index
, &index
);
556 return ENOENT
; // No (more) such file or directory
558 file_id
= r
->key
.file_id
;
559 fork_type
= r
->key
.fork_type
;
561 if (record_init_extent(r
, tree
, buf
, index
))
564 if (r
->key
.file_id
!= file_id
|| // end of current file
565 r
->key
.fork_type
!= fork_type
|| // end of current fork
566 index
!= r
->keyind
) // internal error ?
567 return ENOENT
; // No (more) such file or directory
572 /* intialize the record by searching for the given string in the given folder.
574 * parent and r may be the same.
576 int record_init_string_parent(record
* r
, record
* parent
, char* name
)
580 if (parent
->record
.type
== HFSP_FOLDER
)
581 key
.parent_cnid
= parent
->record
.u
.folder
.id
;
582 else if(parent
->record
.type
== HFSP_FOLDER_THREAD
)
583 key
.parent_cnid
= parent
->key
.parent_cnid
;
585 HFSP_ERROR(-1, "record_init_string_parent: parent is not a folder.");
587 key
.key_length
= 6 + unicode_asc2uni(&key
.name
,name
); // 6 for minumum size
588 return record_init_key(r
, parent
->tree
, &key
);
594 /* move record up in folder hierarchy (if possible) */
595 int record_up(record
* r
)
597 if (r
->record
.type
== HFSP_FOLDER
)
599 // locate folder thread
600 if (record_init_cnid(r
, r
->tree
, r
->record
.u
.folder
.id
))
603 else if(r
->record
.type
== HFSP_FOLDER_THREAD
)
605 // do nothing were are already where we want to be
608 HFSP_ERROR(-1, "record_up: record is neither folder nor folder thread.");
610 if(r
->record
.type
!= HFSP_FOLDER_THREAD
)
611 HFSP_ERROR(-1, "record_up: unable to locate parent");
612 return record_init_cnid(r
, r
->tree
, r
->record
.u
.thread
.parentID
);
620 /* print Quickdraw Point */
621 static void record_print_Point(Point
* p
)
623 printf("[ v=%d, h=%d ]", p
->v
, p
->h
);
626 /* print Quickdraw Rect */
627 static void record_print_Rect(Rect
* r
)
629 printf("[ top=%d, left=%d, bottom=%d, right=%d ]",
630 r
->top
, r
->left
, r
->bottom
, r
->right
);
633 /* print the key of a record */
634 static void record_print_key(hfsp_cat_key
* key
)
636 char buf
[255]; // mh this _might_ overflow
637 unicode_uni2asc(buf
, &key
->name
, 255);
638 printf("parent cnid : %ld\n", key
->parent_cnid
);
639 printf("name : %s\n", buf
);
642 /* print permissions */
643 static void record_print_perm(hfsp_perm
* perm
)
645 printf("owner :\t%ld\n", perm
->owner
);
646 printf("group :\t%ld\n", perm
->group
);
647 printf("perm :\t0x%lX\n",perm
->mode
);
648 printf("dev :\t%ld\n", perm
->dev
);
651 /* print Directory info */
652 static void record_print_DInfo(DInfo
* dinfo
)
654 printf( "frRect :\t"); record_print_Rect(&dinfo
->frRect
);
655 printf("\nfrFlags :\t0X%X\n", dinfo
->frFlags
);
656 printf( "frLocation :\t"); record_print_Point(&dinfo
->frLocation
);
657 printf("\nfrView :\t0X%X\n", dinfo
->frView
);
660 /* print extended Directory info */
661 static void record_print_DXInfo(DXInfo
* xinfo
)
663 printf( "frScroll :\t"); record_print_Point(&xinfo
->frScroll
);
664 printf("\nfrOpenChain :\t%ld\n", xinfo
->frOpenChain
);
665 printf( "frUnused :\t%d\n", xinfo
->frUnused
);
666 printf( "frComment :\t%d\n", xinfo
->frComment
);
667 printf( "frPutAway :\t%ld\n", xinfo
->frPutAway
);
670 static void record_print_folder(hfsp_cat_folder
* folder
)
672 printf("flags :\t0x%X\n", folder
->flags
);
673 printf("valence :\t0x%lX\n", folder
->valence
);
674 printf("id :\t%ld\n", folder
->id
);
675 record_print_perm (&folder
->permissions
);
676 record_print_DInfo (&folder
->user_info
);
677 record_print_DXInfo (&folder
->finder_info
);
678 printf("text_encoding :\t0x%lX\n", folder
->text_encoding
);
679 printf("reserved :\t0x%lX\n", folder
->reserved
);
682 /* print File info */
683 static void record_print_FInfo(FInfo
* finfo
)
685 printf( "fdType :\t%4.4s\n", (char*) &finfo
->fdType
);
686 printf( "fdCreator :\t%4.4s\n", (char*) &finfo
->fdCreator
);
687 printf( "fdFlags :\t0X%X\n", finfo
->fdFlags
);
688 printf( "fdLocation :\t"); record_print_Point(&finfo
->fdLocation
);
689 printf("\nfdFldr :\t%d\n", finfo
->fdFldr
);
692 /* print extended File info */
693 static void record_print_FXInfo(FXInfo
* xinfo
)
695 printf( "fdIconID :\t%d\n", xinfo
->fdIconID
);
696 // xinfo -> fdUnused;
697 printf( "fdComment :\t%d\n", xinfo
->fdComment
);
698 printf( "fdPutAway :\t%ld\n", xinfo
->fdPutAway
);
701 /* print folder entry */
703 /* print file entry */
704 static void record_print_file(hfsp_cat_file
* file
)
706 printf("flags :\t0x%X\n", file
->flags
);
707 printf("reserved1 :\t0x%lX\n", file
->reserved1
);
708 printf("id :\t%ld\n", file
->id
);
709 record_print_perm (&file
->permissions
);
710 record_print_FInfo (&file
->user_info
);
711 record_print_FXInfo (&file
->finder_info
);
712 printf("text_encoding :\t0x%lX\n", file
->text_encoding
);
713 printf("reserved :\t0x%lX\n", file
->reserved2
);
714 printf("Datafork:\n");
715 volume_print_fork (&file
->data_fork
);
716 printf("Rsrcfork:\n");
717 volume_print_fork (&file
->res_fork
);
720 /* print info for a file or folder thread */
721 static void record_print_thread(hfsp_cat_thread
* entry
)
723 char buf
[255]; // mh this _might_ overflow
724 unicode_uni2asc(buf
, &entry
->nodeName
, 255);
725 printf("parent cnid :\t%ld\n", entry
->parentID
);
726 printf("name :\t%s\n" , buf
);
729 /* print the information for a record */
730 static void record_print_entry(hfsp_cat_entry
* entry
)
735 printf("=== Folder ===\n");
736 return record_print_folder(&entry
->u
.folder
);
738 printf("=== File ===\n");
739 return record_print_file (&entry
->u
.file
);
740 case HFSP_FOLDER_THREAD
:
741 printf("=== Folder Thread ===\n");
742 return record_print_thread(&entry
->u
.thread
);
743 case HFSP_FILE_THREAD
:
744 printf("=== File Thread ==\n");
745 return record_print_thread(&entry
->u
.thread
);
747 printf("=== Unknown Record Type ===\n");
751 /* Dump all the record information to stdout */
752 void record_print(record
* r
)
754 printf ("keyind : %u\n", r
->keyind
);
755 record_print_key (&r
->key
);
756 record_print_entry(&r
->record
);