1 |
/* |
2 |
* libhfsp - library for reading and writing Macintosh HFS+ volumes. |
3 |
* |
4 |
* a record contains a key and a folder or file and is part |
5 |
* of a btree. This file conatins various methods to read and |
6 |
* write the record related HFS+ structures from/to memory. |
7 |
* |
8 |
* Copyright (C) 2000-2001 Klaus Halfmann <klaus.halfmann@t-online.de> |
9 |
* Original 1996-1998 Robert Leslie <rob@mars.org> |
10 |
* Additional work by Brad Boyer (flar@pants.nu) |
11 |
* Additional work in 2004 by Stefan Weyergraf (stefan@weyergraf.de) for use in PearPC |
12 |
* |
13 |
* This program is free software; you can redistribute it and/or modify |
14 |
* it under the terms of the GNU General Public License as published by |
15 |
* the Free Software Foundation; either version 2 of the License, or |
16 |
* (at your option) any later version. |
17 |
* |
18 |
* This program is distributed in the hope that it will be useful, |
19 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 |
* GNU General Public License for more details. |
22 |
* |
23 |
* You should have received a copy of the GNU General Public License |
24 |
* along with this program; if not, write to the Free Software |
25 |
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
26 |
* |
27 |
*/ |
28 |
|
29 |
#ifdef HAVE_CONFIG_H |
30 |
# include "config.h" |
31 |
# endif |
32 |
|
33 |
#include <errno.h> |
34 |
#include <stdio.h> |
35 |
#include <string.h> |
36 |
|
37 |
#include "libhfsp.h" |
38 |
#include "hfstime.h" |
39 |
#include "record.h" |
40 |
#include "volume.h" |
41 |
#include "btree.h" |
42 |
#include "unicode.h" |
43 |
#include "swab.h" |
44 |
|
45 |
/* read a hfsp_cat_key from memory, check for correct length |
46 |
* |
47 |
* @param p buffer in memory to read from |
48 |
* @param buf buffer containing the correctly swapped structure |
49 |
* |
50 |
* @return pointer to next byte after structure, NULL on failure |
51 |
*/ |
52 |
char* record_readkey(char* p, void* buf) |
53 |
{ |
54 |
hfsp_cat_key* key = (hfsp_cat_key*) buf; |
55 |
const char* check; |
56 |
UInt16 key_length, len,i; |
57 |
UInt16* cp; |
58 |
|
59 |
key->key_length = key_length = bswabU16_inc(&p); |
60 |
check = p; |
61 |
key->parent_cnid = bswabU32_inc(&p); |
62 |
key->name.strlen = len = bswabU16_inc(&p); |
63 |
cp = key->name.name; |
64 |
for (i=0; i < len; i++, cp++) |
65 |
*cp = bswabU16_inc(&p); |
66 |
/* check if keylenght was correct */ |
67 |
if (key_length != p - check) |
68 |
HFSP_ERROR(EINVAL, "Invalid key length in record_readkey"); |
69 |
return p; |
70 |
fail: |
71 |
return NULL; |
72 |
} |
73 |
|
74 |
/* write a hfsp_cat_key back to memory, check for correct length. |
75 |
* |
76 |
* @param p buffer in memory to write to |
77 |
* @param buf memory containing the (swapped) key. |
78 |
* |
79 |
* @return pointer to byte after the structure or NULL on failure. |
80 |
* |
81 |
*/ |
82 |
char* record_writekey(char* p, void* buf) |
83 |
{ |
84 |
hfsp_cat_key* key = (hfsp_cat_key*) buf; |
85 |
UInt16 key_length, len,i; |
86 |
UInt16* cp; |
87 |
|
88 |
key_length = key->key_length; |
89 |
len = key->name.strlen; |
90 |
cp = key->name.name; |
91 |
if (key_length != (6 + len * 2)) |
92 |
HFSP_ERROR(EINVAL, "Invalid key length in record_writekey"); |
93 |
|
94 |
bstoreU16_inc(&p, key_length); |
95 |
bstoreU32_inc(&p, key->parent_cnid); |
96 |
bstoreU16_inc(&p, len); |
97 |
for (i=0; i < len; i++, cp++) |
98 |
bstoreU16_inc(&p, *cp); |
99 |
return p; |
100 |
fail: |
101 |
return NULL; |
102 |
} |
103 |
|
104 |
/* read a hfsp_extent_key from memory */ |
105 |
char* record_extent_readkey(char* p, void* buf) |
106 |
{ |
107 |
hfsp_extent_key* key = (hfsp_extent_key*) buf; |
108 |
UInt16 key_length; |
109 |
|
110 |
key->key_length = key_length = bswabU16_inc(&p); |
111 |
key->fork_type = bswabU8_inc(&p); |
112 |
key->filler = bswabU8_inc(&p); |
113 |
if (key_length != 10) |
114 |
HFSP_ERROR(-1, "Invalid key length in record_extent_readkey"); |
115 |
key->file_id = bswabU32_inc(&p); |
116 |
key->start_block = bswabU32_inc(&p); |
117 |
return p; |
118 |
fail: |
119 |
return NULL; |
120 |
} |
121 |
|
122 |
/* write a hfsp_extent_key to memory */ |
123 |
char* record_extent_writekey(char* p, void* buf) |
124 |
{ |
125 |
hfsp_extent_key* key = (hfsp_extent_key*) buf; |
126 |
UInt16 key_length = key->key_length; |
127 |
if (key_length != 10) |
128 |
HFSP_ERROR(-1, "Invalid key length in record_extent_writekey"); |
129 |
|
130 |
bstoreU16_inc (&p, key_length); |
131 |
bstoreU8_inc (&p, key->fork_type); |
132 |
bstoreU8_inc (&p, key->filler); |
133 |
bstoreU32_inc (&p, key->file_id); |
134 |
bstoreU32_inc (&p, key->start_block); |
135 |
return p; |
136 |
fail: |
137 |
return NULL; |
138 |
} |
139 |
|
140 |
/* read posix permission from memory */ |
141 |
static inline char* record_readperm(char *p, hfsp_perm* perm) |
142 |
{ |
143 |
perm->owner= bswabU32_inc(&p); |
144 |
perm->group= bswabU32_inc(&p); |
145 |
perm->mode = bswabU32_inc(&p); |
146 |
perm->dev = bswabU32_inc(&p); |
147 |
return p; |
148 |
} |
149 |
|
150 |
/* write posix permission to memory */ |
151 |
static inline char* record_writeperm(char *p, hfsp_perm* perm) |
152 |
{ |
153 |
bstoreU32_inc (&p, perm->owner); |
154 |
bstoreU32_inc (&p, perm->group); |
155 |
bstoreU32_inc (&p, perm->mode ); |
156 |
bstoreU32_inc (&p, perm->dev ); |
157 |
return p; |
158 |
} |
159 |
|
160 |
/* intialize posix permission from memory. |
161 |
* |
162 |
* TODO use current umask, user, group, etc. |
163 |
*/ |
164 |
static inline void record_initperm(hfsp_perm* perm) |
165 |
{ |
166 |
perm->owner= 0; |
167 |
perm->group= 0; |
168 |
perm->mode = 0; |
169 |
perm->dev = 0; |
170 |
} |
171 |
|
172 |
|
173 |
/* read directory info */ |
174 |
static inline char* record_readDInfo(char *p, DInfo* info) |
175 |
{ |
176 |
info->frRect.top = bswabU16_inc(&p); |
177 |
info->frRect.left = bswabU16_inc(&p); |
178 |
info->frRect.bottom = bswabU16_inc(&p); |
179 |
info->frRect.right = bswabU16_inc(&p); |
180 |
info->frFlags = bswabU16_inc(&p); |
181 |
info->frLocation.v = bswabU16_inc(&p); |
182 |
info->frLocation.h = bswabU16_inc(&p); |
183 |
info->frView = bswabU16_inc(&p); |
184 |
return p; |
185 |
} |
186 |
|
187 |
/* write directory info */ |
188 |
static inline char* record_writeDInfo(char *p, DInfo* info) |
189 |
{ |
190 |
bstoreU16_inc (&p, info->frRect.top ); |
191 |
bstoreU16_inc (&p, info->frRect.left ); |
192 |
bstoreU16_inc (&p, info->frRect.bottom); |
193 |
bstoreU16_inc (&p, info->frRect.right); |
194 |
bstoreU16_inc (&p, info->frFlags ); |
195 |
bstoreU16_inc (&p, info->frLocation.v); |
196 |
bstoreU16_inc (&p, info->frLocation.h); |
197 |
bstoreU16_inc (&p, info->frView ); |
198 |
return p; |
199 |
} |
200 |
|
201 |
/* initialize directory info */ |
202 |
static inline void record_initDInfo(DInfo* info) |
203 |
{ |
204 |
// Hope the finder will not choke on these values |
205 |
memset(info, 0, sizeof(DInfo)); |
206 |
/* |
207 |
info->frRect.top = 0; |
208 |
info->frRect.left = 0; |
209 |
info->frRect.bottom = 0; |
210 |
info->frRect.right = 0; |
211 |
info->frFlags = 0; |
212 |
info->frLocation.v = 0; |
213 |
info->frLocation.h = 0; |
214 |
info->frView = 0; |
215 |
*/ |
216 |
} |
217 |
|
218 |
/* read extra Directory info */ |
219 |
static inline char* record_readDXInfo(char *p, DXInfo* xinfo) |
220 |
{ |
221 |
xinfo->frScroll.v = bswabU16_inc(&p); |
222 |
xinfo->frScroll.h = bswabU16_inc(&p); |
223 |
xinfo->frOpenChain = bswabU32_inc(&p); |
224 |
xinfo->frUnused = bswabU16_inc(&p); |
225 |
xinfo->frComment = bswabU16_inc(&p); |
226 |
xinfo->frPutAway = bswabU32_inc(&p); |
227 |
return p; |
228 |
} |
229 |
|
230 |
/* write extra Directory info */ |
231 |
static inline char* record_writeDXInfo(char *p, DXInfo* xinfo) |
232 |
{ |
233 |
bstoreU16_inc (&p, xinfo->frScroll.v ); |
234 |
bstoreU16_inc (&p, xinfo->frScroll.h ); |
235 |
bstoreU32_inc (&p, xinfo->frOpenChain); |
236 |
bstoreU16_inc (&p, xinfo->frUnused ); |
237 |
bstoreU16_inc (&p, xinfo->frComment ); |
238 |
bstoreU32_inc (&p, xinfo->frPutAway ); |
239 |
return p; |
240 |
} |
241 |
|
242 |
/* initialize extra Directory info */ |
243 |
static inline void record_initDXInfo(DXInfo* xinfo) |
244 |
{ |
245 |
// Hope the finder will not choke on these values |
246 |
memset(xinfo, 0, sizeof(DXInfo)); |
247 |
/* |
248 |
xinfo->frScroll.v = 0; |
249 |
xinfo->frScroll.h = 0; |
250 |
xinfo->frOpenChain= 0; |
251 |
xinfo->frUnused = 0; |
252 |
xinfo->frComment = 0; |
253 |
xinfo->frPutAway = 0; |
254 |
*/ |
255 |
} |
256 |
|
257 |
|
258 |
/* read a hfsp_cat_folder from memory */ |
259 |
static char* record_readfolder(char *p, hfsp_cat_folder* folder) |
260 |
{ |
261 |
folder->flags = bswabU16_inc(&p); |
262 |
folder->valence = bswabU32_inc(&p); |
263 |
folder->id = bswabU32_inc(&p); |
264 |
folder->create_date = bswabU32_inc(&p); |
265 |
folder->content_mod_date = bswabU32_inc(&p); |
266 |
folder->attribute_mod_date = bswabU32_inc(&p); |
267 |
folder->access_date = bswabU32_inc(&p); |
268 |
folder->backup_date = bswabU32_inc(&p); |
269 |
p = record_readperm (p, &folder->permissions); |
270 |
p = record_readDInfo (p, &folder->user_info); |
271 |
p = record_readDXInfo (p, &folder->finder_info); |
272 |
folder->text_encoding = bswabU32_inc(&p); |
273 |
folder->reserved = bswabU32_inc(&p); |
274 |
return p; |
275 |
} |
276 |
|
277 |
/* write a hfsp_cat_folder to memory */ |
278 |
static char* record_writefolder(char *p, hfsp_cat_folder* folder) |
279 |
{ |
280 |
bstoreU16_inc (&p, folder->flags ); |
281 |
bstoreU32_inc (&p, folder->valence ); |
282 |
bstoreU32_inc (&p, folder->id ); |
283 |
bstoreU32_inc (&p, folder->create_date ); |
284 |
bstoreU32_inc (&p, folder->content_mod_date ); |
285 |
bstoreU32_inc (&p, folder->attribute_mod_date); |
286 |
bstoreU32_inc (&p, folder->access_date ); |
287 |
bstoreU32_inc (&p, folder->backup_date ); |
288 |
p = record_writeperm (p, &folder->permissions); |
289 |
p = record_writeDInfo (p, &folder->user_info); |
290 |
p = record_writeDXInfo (p, &folder->finder_info); |
291 |
bstoreU32_inc (&p, folder->text_encoding ); |
292 |
bstoreU32_inc (&p, folder->reserved ); |
293 |
return p; |
294 |
} |
295 |
|
296 |
/* initialize a hfsp_cat_folder with given values. |
297 |
* |
298 |
* @vol is needed to create a new record Id. |
299 |
* @return 0 on sucess anything else on error. |
300 |
*/ |
301 |
static int record_initfolder(volume* vol, hfsp_cat_folder* folder) |
302 |
{ |
303 |
UInt32 macNow = HFSPTIMEDIFF + time(NULL); |
304 |
|
305 |
folder->flags = 0; |
306 |
folder->valence = 0; // no subfiles/folders yet |
307 |
if (! (folder->id = volume_get_nextid(vol))) // oops possible wrap around overflow |
308 |
return -1; |
309 |
folder->create_date = macNow; |
310 |
folder->content_mod_date = macNow; |
311 |
folder->attribute_mod_date = macNow; |
312 |
folder->access_date = macNow; |
313 |
folder->backup_date = 0; |
314 |
record_initperm (&folder->permissions); |
315 |
record_initDInfo (&folder->user_info); |
316 |
record_initDXInfo (&folder->finder_info); |
317 |
folder->text_encoding = 0; // Not supported, sorry |
318 |
folder->reserved = 0; |
319 |
return 0; |
320 |
} |
321 |
|
322 |
/* read file info */ |
323 |
static inline char* record_readFInfo(char *p, FInfo* info) |
324 |
{ |
325 |
info->fdType = bswabU32_inc(&p); |
326 |
info->fdCreator = bswabU32_inc(&p); |
327 |
info->fdFlags = bswabU16_inc(&p); |
328 |
info->fdLocation.v = bswabU16_inc(&p); |
329 |
info->fdLocation.h = bswabU16_inc(&p); |
330 |
info->fdFldr = bswabU16_inc(&p); |
331 |
return p; |
332 |
} |
333 |
|
334 |
/* write file info */ |
335 |
static inline char* record_writeFInfo(char *p, FInfo* info) |
336 |
{ |
337 |
bstoreU32_inc (&p, info->fdType ); |
338 |
bstoreU32_inc (&p, info->fdCreator ); |
339 |
bstoreU16_inc (&p, info->fdFlags ); |
340 |
bstoreU16_inc (&p, info->fdLocation.v); |
341 |
bstoreU16_inc (&p, info->fdLocation.h); |
342 |
bstoreU16_inc (&p, info->fdFldr ); |
343 |
return p; |
344 |
} |
345 |
|
346 |
/* initialize file info */ |
347 |
static inline void record_initFInfo(FInfo* info) |
348 |
{ |
349 |
// should use something better somehow |
350 |
info->fdType = sig('T','E','X','T'); |
351 |
info->fdCreator = HPLS_SIGNATURE; |
352 |
info->fdFlags = 0; // dunno any finder flags |
353 |
info->fdLocation.v = 0; |
354 |
info->fdLocation.h = 0; |
355 |
info->fdFldr = 0; |
356 |
} |
357 |
|
358 |
/* read extra File info */ |
359 |
static inline char* record_readFXInfo(char *p, FXInfo* xinfo) |
360 |
{ |
361 |
xinfo->fdIconID = bswabU16_inc(&p); |
362 |
xinfo->fdUnused[0] = bswabU16_inc(&p); |
363 |
xinfo->fdUnused[1] = bswabU16_inc(&p); |
364 |
xinfo->fdUnused[2] = bswabU16_inc(&p); |
365 |
xinfo->fdUnused[3] = bswabU16_inc(&p); |
366 |
xinfo->fdComment = bswabU16_inc(&p); |
367 |
xinfo->fdPutAway = bswabU32_inc(&p); |
368 |
return p; |
369 |
} |
370 |
|
371 |
/* write extra File info */ |
372 |
static inline char* record_writeFXInfo(char *p, FXInfo* xinfo) |
373 |
{ |
374 |
bstoreU16_inc (&p, xinfo->fdIconID); |
375 |
bstoreU16_inc (&p, xinfo->fdUnused[0]); |
376 |
bstoreU16_inc (&p, xinfo->fdUnused[1]); |
377 |
bstoreU16_inc (&p, xinfo->fdUnused[2]); |
378 |
bstoreU16_inc (&p, xinfo->fdUnused[3]); |
379 |
bstoreU16_inc (&p, xinfo->fdComment); |
380 |
bstoreU16_inc (&p, xinfo->fdPutAway); |
381 |
return p; |
382 |
} |
383 |
|
384 |
/* initialize extra File info */ |
385 |
static inline void record_initFXInfo(FXInfo* xinfo) |
386 |
{ |
387 |
// Hope the finder will not choke on these values |
388 |
memset(xinfo, 0, sizeof(FXInfo)); |
389 |
/* |
390 |
xinfo->fdIconID = 0; |
391 |
xinfo->fdUnused[0] = 0; |
392 |
xinfo->fdUnused[1] = 0; |
393 |
xinfo->fdUnused[2] = 0; |
394 |
xinfo->fdUnused[3] = 0; |
395 |
xinfo->fdComment = 0; |
396 |
xinfo->fdPutAway = 0; |
397 |
*/ |
398 |
} |
399 |
|
400 |
/* read a hfsp_cat_file from memory */ |
401 |
static char* record_readfile(char *p, hfsp_cat_file* file) |
402 |
{ |
403 |
file->flags = bswabU16_inc(&p); |
404 |
file->reserved1 = bswabU32_inc(&p); |
405 |
file->id = bswabU32_inc(&p); |
406 |
file->create_date = bswabU32_inc(&p); |
407 |
file->content_mod_date = bswabU32_inc(&p); |
408 |
file->attribute_mod_date = bswabU32_inc(&p); |
409 |
file->access_date = bswabU32_inc(&p); |
410 |
file->backup_date = bswabU32_inc(&p); |
411 |
p = record_readperm (p, &file->permissions); |
412 |
p = record_readFInfo (p, &file->user_info); |
413 |
p = record_readFXInfo (p, &file->finder_info); |
414 |
file->text_encoding = bswabU32_inc(&p); |
415 |
file->reserved2 = bswabU32_inc(&p); |
416 |
p = volume_readfork (p, &file->data_fork); |
417 |
return volume_readfork (p, &file->res_fork); |
418 |
} |
419 |
|
420 |
/* write a hfsp_cat_file to memory */ |
421 |
static char* record_writefile(char *p, hfsp_cat_file* file) |
422 |
{ |
423 |
bstoreU16_inc(&p, file->flags); |
424 |
bstoreU16_inc(&p, file->reserved1); |
425 |
bstoreU16_inc(&p, file->id); |
426 |
bstoreU16_inc(&p, file->create_date); |
427 |
bstoreU16_inc(&p, file->content_mod_date); |
428 |
bstoreU16_inc(&p, file->attribute_mod_date); |
429 |
bstoreU16_inc(&p, file->access_date); |
430 |
bstoreU16_inc(&p, file->backup_date); |
431 |
p = record_writeperm (p, &file->permissions); |
432 |
p = record_writeFInfo (p, &file->user_info); |
433 |
p = record_writeFXInfo (p, &file->finder_info); |
434 |
bstoreU16_inc(&p, file->text_encoding); |
435 |
bstoreU16_inc(&p, file->reserved2 ); |
436 |
p = volume_writefork (p, &file->data_fork); |
437 |
return volume_writefork (p, &file->res_fork); |
438 |
} |
439 |
|
440 |
/* initialize a hfsp_cat_file with given values. |
441 |
* |
442 |
* vol needed to create new Id |
443 |
*/ |
444 |
static int record_initfile(volume* vol, hfsp_cat_file* file) |
445 |
{ |
446 |
UInt32 macNow = HFSPTIMEDIFF + time(NULL); |
447 |
|
448 |
file->flags = 0; |
449 |
file->reserved1 = 0; // no subfiles/folders yet |
450 |
if (! (file->id = volume_get_nextid(vol))) // oops possible wrap around overflow |
451 |
return -1; |
452 |
file->create_date = macNow; |
453 |
file->content_mod_date = macNow; |
454 |
file->attribute_mod_date = macNow; |
455 |
file->access_date = macNow; |
456 |
file->backup_date = 0; |
457 |
record_initperm (&file->permissions); |
458 |
record_initFInfo (&file->user_info); |
459 |
record_initFXInfo (&file->finder_info); |
460 |
file->text_encoding = 0; // Not supported, sorry |
461 |
file->reserved2 = 0; |
462 |
volume_initfork(vol,&file->data_fork, HFSP_EXTENT_DATA); |
463 |
volume_initfork(vol,&file->res_fork, HFSP_EXTENT_RSRC); |
464 |
return 0; |
465 |
} |
466 |
|
467 |
|
468 |
/* read a hfsp_cat_thread from memory */ |
469 |
static char* record_readthread(char *p, hfsp_cat_thread* entry) |
470 |
{ |
471 |
int i; |
472 |
UInt16 len; |
473 |
UInt16* cp; |
474 |
|
475 |
entry-> reserved = bswabU16_inc(&p); |
476 |
entry-> parentID = bswabU32_inc(&p); |
477 |
entry->nodeName.strlen = len= bswabU16_inc(&p); |
478 |
cp = entry->nodeName.name; |
479 |
if (len > 255) |
480 |
HFSP_ERROR(-1, "Invalid key length in record_thread"); |
481 |
for (i=0; i < len; i++, cp++) |
482 |
*cp = bswabU16_inc(&p); |
483 |
return p; |
484 |
fail: |
485 |
return NULL; |
486 |
} |
487 |
|
488 |
/* write a hfsp_cat_thread to memory */ |
489 |
static char* record_writethread(char *p, hfsp_cat_thread* entry) |
490 |
{ |
491 |
int i; |
492 |
UInt16 len; |
493 |
UInt16* cp; |
494 |
|
495 |
bstoreU16_inc(&p, entry->reserved); |
496 |
bstoreU32_inc(&p, entry->parentID); |
497 |
/* this is bad style, friends... (SW) */ |
498 |
/* bstoreU16_inc(&p, len = entry->nodeName.strlen);*/ |
499 |
len = entry->nodeName.strlen; |
500 |
bstoreU16_inc(&p, len); |
501 |
cp = entry->nodeName.name; |
502 |
if (len > 255) |
503 |
HFSP_ERROR(-1, "Invalid key length in record_thread"); |
504 |
for (i=0; i < len; i++, cp++) |
505 |
bstoreU16_inc(&p, *cp); |
506 |
return p; |
507 |
fail: |
508 |
return NULL; |
509 |
} |
510 |
|
511 |
|
512 |
/* read a hfsp_cat_entry from memory */ |
513 |
char* record_readentry(char *p, void* entry) |
514 |
{ |
515 |
UInt16 type = bswabU16_inc(&p); |
516 |
hfsp_cat_entry* e = (hfsp_cat_entry*) entry; |
517 |
e->type = type; |
518 |
switch (type) |
519 |
{ |
520 |
case HFSP_FOLDER: |
521 |
return record_readfolder(p, &e->u.folder); |
522 |
case HFSP_FILE: |
523 |
return record_readfile (p, &e->u.file); |
524 |
case HFSP_FOLDER_THREAD: |
525 |
case HFSP_FILE_THREAD: |
526 |
return record_readthread(p, &e->u.thread); |
527 |
default: |
528 |
HFSP_ERROR(-1, "Unexpected record type in record_readentry"); |
529 |
} ; |
530 |
fail: |
531 |
return NULL; |
532 |
} |
533 |
|
534 |
/* write a hfsp_cat_entry to memory */ |
535 |
char* record_writeentry(char *p, hfsp_cat_entry* entry) |
536 |
{ |
537 |
UInt16 type = entry->type; |
538 |
bstoreU16_inc(&p, type); |
539 |
switch (type) |
540 |
{ |
541 |
case HFSP_FOLDER: |
542 |
return record_writefolder(p, &entry->u.folder); |
543 |
case HFSP_FILE: |
544 |
return record_writefile (p, &entry->u.file); |
545 |
case HFSP_FOLDER_THREAD: |
546 |
case HFSP_FILE_THREAD: |
547 |
return record_writethread(p, &entry->u.thread); |
548 |
default: |
549 |
HFSP_ERROR(-1, "Unexpected record type in record_writeentry"); |
550 |
} ; |
551 |
fail: |
552 |
return NULL; |
553 |
} |
554 |
|
555 |
/* read an extent record from memory */ |
556 |
// For dependency reasons this actually is found in volume.h |
557 |
char* record_extent_readrecord(char *p, void* entry) |
558 |
{ |
559 |
return volume_readextent(p, (hfsp_extent*) entry); |
560 |
} |
561 |
|
562 |
|
563 |
/* intialize the record with the given index entry in the btree. */ |
564 |
int record_init(record* r, btree* bt, node_buf* buf, UInt16 index) |
565 |
{ |
566 |
void *p; |
567 |
r-> tree = bt; |
568 |
p = btree_key_by_index(bt,buf,index); |
569 |
if (!p) |
570 |
return -1; |
571 |
p = record_readkey (p, &r->key); |
572 |
if (!p) |
573 |
return -1; |
574 |
/* void *help = p; // se comment below */ |
575 |
p = record_readentry(p, &r->record); |
576 |
/* This was for testing write cache only |
577 |
void * help; |
578 |
help = record_writeentry(help, &r->record); |
579 |
if (p != help) |
580 |
HFSP_ERROR(-1, "Error in write entry"); |
581 |
*/ |
582 |
if (!p) |
583 |
return -1; |
584 |
r->node_index = buf->index; |
585 |
r-> keyind = index; |
586 |
|
587 |
return 0; |
588 |
/* |
589 |
fail: |
590 |
return -1; |
591 |
*/ |
592 |
} |
593 |
|
594 |
/* Update the record using its node- and key-index. |
595 |
* |
596 |
* Only use this function with a write lock, directly |
597 |
* after reading the record, otherwise use update_bykey(). |
598 |
*/ |
599 |
int record_update(record* r) |
600 |
{ |
601 |
btree *tree= r->tree; |
602 |
node_buf *buf = btree_node_by_index(tree, r->node_index, NODE_DIRTY); |
603 |
void *p = btree_key_by_index (tree, buf, r->keyind); |
604 |
if (!p) |
605 |
return -1; |
606 |
p = record_writekey (p, &r->key); |
607 |
if (!p) |
608 |
return -1; |
609 |
p = record_writeentry(p, &r->record); |
610 |
if (!p) |
611 |
return -1; |
612 |
|
613 |
return 0; |
614 |
} |
615 |
|
616 |
|
617 |
/* intialize the record with the given index entry in the btree. */ |
618 |
static int record_init_extent(extent_record* r, btree* bt, node_buf* buf, UInt16 index) |
619 |
{ |
620 |
char *p; |
621 |
r-> tree = bt; |
622 |
p = btree_key_by_index(bt, buf,index); |
623 |
if (!p) |
624 |
return -1; |
625 |
p = record_extent_readkey(p, &r->key); |
626 |
if (!p) |
627 |
return -1; |
628 |
p = volume_readextent(p, r->extent); |
629 |
if (!p) |
630 |
return -1; |
631 |
r->node_index = buf->index; |
632 |
r-> keyind = index; |
633 |
|
634 |
return 0; |
635 |
} |
636 |
|
637 |
/* intialize the record to the first record of the tree |
638 |
* which is (per design) the root node. |
639 |
*/ |
640 |
int record_init_root(record* r, btree* tree) |
641 |
{ |
642 |
// Position to first leaf node ... |
643 |
UInt32 leaf_head = tree->head.leaf_head; |
644 |
node_buf* buf = btree_node_by_index(tree, leaf_head, NODE_CLEAN); |
645 |
if (!buf) |
646 |
return -1; |
647 |
return record_init(r, tree, buf, 0); |
648 |
} |
649 |
|
650 |
/* initialize a (catalog) record with given type and (ascii) name. |
651 |
* parent must be a HFSP_FOLDER or FOLDER_THREAD |
652 |
* You should normally call record_insert afterwards. |
653 |
*/ |
654 |
int record_init_string(record* r, UInt16 type, char* name, record* parent) |
655 |
{ |
656 |
int result = 0; |
657 |
hfsp_cat_key* key = &r->key; |
658 |
hfsp_cat_entry* entry = &r->record; |
659 |
UInt16 ptype = parent->record.type; |
660 |
|
661 |
memset(r, 0, sizeof *r); // **** Debugging only |
662 |
|
663 |
r->tree = parent->tree; |
664 |
r->node_index = 0; |
665 |
r->keyind = 0; |
666 |
key->key_length = 6 + 2 * unicode_asc2uni(&key->name,name); |
667 |
// 6 for minumum size |
668 |
if (ptype == HFSP_FOLDER) |
669 |
key->parent_cnid= parent->record.u.folder.id; |
670 |
else if (ptype == HFSP_FOLDER_THREAD) |
671 |
key->parent_cnid= parent->key.parent_cnid; |
672 |
else // no kind of folder ?? |
673 |
{ |
674 |
hfsp_error = "parent for record_init_string is not a folder."; |
675 |
return EINVAL; |
676 |
} |
677 |
|
678 |
switch(type) |
679 |
{ |
680 |
case HFSP_FOLDER : |
681 |
entry->type = type; |
682 |
record_initfolder(parent->tree->vol, &entry->u.folder); |
683 |
break; |
684 |
case HFSP_FILE : |
685 |
entry->type = type; |
686 |
record_initfile(parent->tree->vol, &entry->u.file); |
687 |
break; |
688 |
// Those are unsupported use the types above instead |
689 |
// record_init will care about the threads |
690 |
case HFSP_FOLDER_THREAD : |
691 |
case HFSP_FILE_THREAD : |
692 |
default: |
693 |
hfsp_error = "Unsupported type for record_init_string()"; |
694 |
result = -1; |
695 |
} |
696 |
|
697 |
return result; |
698 |
} |
699 |
|
700 |
/* initialize a (catalog) record thread by its original record |
701 |
* used internally by record_insert. |
702 |
*/ |
703 |
static int record_init_thread(record* r, record* template) |
704 |
{ |
705 |
int result = 0; |
706 |
hfsp_cat_key* key = &r->key; |
707 |
hfsp_cat_entry* entry = &r->record; |
708 |
UInt16 type = template->record.type; |
709 |
hfsp_cat_thread* thread; |
710 |
|
711 |
r->tree = template->tree; |
712 |
r->node_index = 0; |
713 |
r->keyind = 0; |
714 |
key->key_length = 6; // empty name is ok for a thread |
715 |
key->parent_cnid= template->record.u.folder.id; |
716 |
|
717 |
thread = &entry->u.thread; |
718 |
switch(type) |
719 |
{ |
720 |
case HFSP_FOLDER : |
721 |
case HFSP_FILE : |
722 |
entry->type = type + HFSP_THREAD_OFFSET; |
723 |
thread->reserved = 0; |
724 |
thread->parentID = template->key.parent_cnid; |
725 |
thread->nodeName = template->key.name; |
726 |
break; |
727 |
case HFSP_FOLDER_THREAD : |
728 |
case HFSP_FILE_THREAD : |
729 |
default: |
730 |
hfsp_error = "Cannot create a thread for a thread"; |
731 |
result = -1; |
732 |
} |
733 |
|
734 |
return result; |
735 |
} |
736 |
|
737 |
/* Compare two cat_keys ... */ |
738 |
int record_key_compare(void* k1, void* k2) |
739 |
{ |
740 |
hfsp_cat_key* key1 = (hfsp_cat_key*) k1; |
741 |
hfsp_cat_key* key2 = (hfsp_cat_key*) k2; |
742 |
int diff = key2->parent_cnid - key1->parent_cnid; |
743 |
if (!diff) // same parent |
744 |
diff = fast_unicode_compare(&key1->name, &key2->name); |
745 |
return diff; |
746 |
} |
747 |
|
748 |
/* Compare two extent_keys ... */ |
749 |
int record_extent_key_compare(void* k1, void* k2) |
750 |
{ |
751 |
hfsp_extent_key* key1 = (hfsp_extent_key*) k1; |
752 |
hfsp_extent_key* key2 = (hfsp_extent_key*) k2; |
753 |
int diff = key2->fork_type - key1->fork_type; |
754 |
if (!diff) // same type |
755 |
{ |
756 |
diff = key2->file_id - key1->file_id; |
757 |
if (!diff) // same file |
758 |
diff = key2->start_block - key1->start_block; |
759 |
} |
760 |
return diff; |
761 |
} |
762 |
|
763 |
/* Position node in btree so that key might be inside */ |
764 |
static node_buf* record_find_node(btree* tree, void *key) |
765 |
{ |
766 |
int start, end, mid, comp; // components of a binary search |
767 |
char *p = NULL; |
768 |
char curr_key[tree->head.max_key_len]; |
769 |
// The current key under examination |
770 |
hfsp_key_read readkey = tree->kread; |
771 |
hfsp_key_compare key_compare = tree->kcomp; |
772 |
UInt32 index; |
773 |
node_buf* node = |
774 |
btree_node_by_index(tree, tree->head.root, NODE_CLEAN); |
775 |
HFSP_SYNC_START(HFSP_READLOCK, node); |
776 |
if (!node) |
777 |
HFSP_ERROR(-1, "record_find_node: Cant position to root node"); |
778 |
while (node->desc.kind == HFSP_NODE_NDX) |
779 |
{ |
780 |
mid = start = 0; |
781 |
end = node->desc.num_rec; |
782 |
comp = -1; |
783 |
while (start < end) |
784 |
{ |
785 |
mid = (start + end) >> 1; |
786 |
p = btree_key_by_index(tree, node, mid); |
787 |
if (!p) |
788 |
HFSP_ERROR(-1, "record_find_node: unexpected error"); |
789 |
p = readkey (p, curr_key); |
790 |
if (!p) |
791 |
HFSP_ERROR(-1, "record_find_node: unexpected error"); |
792 |
comp = key_compare(curr_key, key); |
793 |
if (comp > 0) |
794 |
start = mid + 1; |
795 |
else if (comp < 0) |
796 |
end = mid; |
797 |
else |
798 |
break; |
799 |
} |
800 |
if (!p) // Empty tree, fascinating ... |
801 |
HFSP_ERROR(-1, "record_find_node: unexpected empty node"); |
802 |
if (comp < 0) // mmh interesting key is before this key ... |
803 |
{ |
804 |
if (mid == 0) |
805 |
return NULL; // nothing before this key .. |
806 |
p = btree_key_by_index(tree, node, mid-1); |
807 |
if (!p) |
808 |
HFSP_ERROR(-1, "record_find_node: unexpected error"); |
809 |
p = readkey (p, curr_key); |
810 |
if (!p) |
811 |
HFSP_ERROR(-1, "record_find_node: unexpected error"); |
812 |
} |
813 |
|
814 |
index = bswabU32_inc(&p); |
815 |
node = btree_node_by_index(tree, index, NODE_CLEAN); |
816 |
} |
817 |
HFSP_SYNC_END(HFSP_READLOCK, node); |
818 |
return node; // go on and use the found node |
819 |
fail: |
820 |
HFSP_SYNC_END(HFSP_READLOCK, node); |
821 |
return NULL; |
822 |
} |
823 |
|
824 |
/* search for the given key in the btree. |
825 |
* |
826 |
* returns pointer to memory just after key or NULL. |
827 |
* |
828 |
* *keyind recives the index where the key was found |
829 |
* (or could be inserted.) |
830 |
* *node_index is the index of the node where key was found/might |
831 |
* be inserted before |
832 |
*/ |
833 |
char* record_find_key(btree* tree, void* key, int* keyind, UInt16* node_index) |
834 |
{ |
835 |
node_buf* buf = record_find_node(tree, key); |
836 |
if (buf) |
837 |
{ |
838 |
int comp = -1; |
839 |
int start = 0; // components of a binary search |
840 |
int end = buf->desc.num_rec; |
841 |
int mid = -1; |
842 |
char *p = NULL; |
843 |
char curr_key[tree->head.max_key_len]; |
844 |
hfsp_key_read readkey = tree->kread; |
845 |
hfsp_key_compare key_compare = tree->kcomp; |
846 |
HFSP_SYNC_START(HFSP_READLOCK, node); |
847 |
while (start < end) |
848 |
{ |
849 |
mid = (start + end) >> 1; |
850 |
p = btree_key_by_index(tree, buf, mid); |
851 |
if (!p) |
852 |
HFSP_ERROR(-1, "record_find_key: unexpected error"); |
853 |
p = readkey (p, curr_key); |
854 |
if (!p) |
855 |
goto fail; |
856 |
comp = key_compare(curr_key, key); |
857 |
if (comp > 0) |
858 |
start = mid + 1; |
859 |
else if (comp < 0) |
860 |
end = mid; |
861 |
else |
862 |
break; |
863 |
} |
864 |
if (!p) // Empty tree, fascinating ... |
865 |
HFSP_ERROR(ENOENT, "record_find_key: unexpected empty node"); |
866 |
*node_index = buf->index; |
867 |
if (!comp) // found something ... |
868 |
{ |
869 |
*keyind = mid; // Thats where we found it |
870 |
HFSP_SYNC_END(HFSP_READLOCK, node); |
871 |
return p; |
872 |
} |
873 |
*keyind = end; // Here we can insert a new key |
874 |
} |
875 |
HFSP_ERROR(ENOENT, NULL); |
876 |
fail: |
877 |
HFSP_SYNC_END(HFSP_READLOCK, node); |
878 |
return NULL; |
879 |
} |
880 |
|
881 |
/* intialize the record by searching for the given key in the btree. |
882 |
* |
883 |
* r is umodified on error. |
884 |
*/ |
885 |
int record_init_key(record* r, btree* tree, hfsp_cat_key* key) |
886 |
{ |
887 |
int keyind; |
888 |
UInt16 node_index; |
889 |
char *p = record_find_key(tree, key, &keyind, &node_index); |
890 |
|
891 |
if (p) |
892 |
{ |
893 |
r -> tree = tree; |
894 |
r -> node_index= node_index; |
895 |
r -> keyind = keyind; |
896 |
r -> key = *key; // Better use a record_key_copy ... |
897 |
p = record_readentry(p, &r->record); |
898 |
if (!p) |
899 |
HFSP_ERROR(-1, "record_init_key: unexpected error"); |
900 |
return 0; |
901 |
} |
902 |
fail: |
903 |
return -1; |
904 |
} |
905 |
|
906 |
/* intialize the extent_record to the extent identified by the |
907 |
* (first) blockindex. |
908 |
* |
909 |
* forktype: either HFSP_EXTEND_DATA or HFSP_EXTEND_RSRC |
910 |
*/ |
911 |
int record_init_file(extent_record* r, btree* tree, |
912 |
UInt8 forktype, UInt32 fileId, UInt32 blockindex) |
913 |
{ |
914 |
int keyind; |
915 |
UInt16 node_index; |
916 |
hfsp_extent_key key = { 10, forktype, 0, fileId, blockindex }; |
917 |
char *p = record_find_key(tree, &key, &keyind, &node_index); |
918 |
|
919 |
if (p) |
920 |
{ |
921 |
r -> tree = tree; |
922 |
r -> node_index= node_index; |
923 |
r -> keyind = keyind; |
924 |
r -> key = key; // Better use a record_key_copy ... |
925 |
p = volume_readextent(p, r->extent); |
926 |
if (!p) |
927 |
HFSP_ERROR(-1, "record_init_file: unexpected error"); |
928 |
return 0; |
929 |
} |
930 |
fail: |
931 |
return -1; |
932 |
} |
933 |
|
934 |
/* intialize the record to the folder identified by cnid |
935 |
*/ |
936 |
int record_init_cnid(record* r, btree* tree, UInt32 cnid) |
937 |
{ |
938 |
hfsp_cat_key thread_key; // the thread is the first record |
939 |
|
940 |
thread_key.key_length = 6; // null name (like '.' in unix ) |
941 |
thread_key.parent_cnid = cnid; |
942 |
thread_key.name.strlen = 0; |
943 |
|
944 |
return record_init_key(r, tree, &thread_key); |
945 |
} |
946 |
|
947 |
/* intialize the record to the first record of the parent. |
948 |
*/ |
949 |
int record_init_parent(record* r, record* parent) |
950 |
{ |
951 |
if (parent->record.type == HFSP_FOLDER) |
952 |
return record_init_cnid(r, parent->tree, parent->record.u.folder.id); |
953 |
else if(parent->record.type == HFSP_FOLDER_THREAD) |
954 |
{ |
955 |
if (r != parent) |
956 |
*r = *parent; // The folder thread is in fact the first entry, like '.' |
957 |
return 0; |
958 |
} |
959 |
HFSP_ERROR(EINVAL, |
960 |
"record_init_parent: parent is neither folder nor folder thread."); |
961 |
|
962 |
fail: |
963 |
return EINVAL; |
964 |
} |
965 |
|
966 |
/* intialize the record to the parent directory of the given record. |
967 |
*/ |
968 |
int record_find_parent(record* r, record* from) |
969 |
{ |
970 |
UInt16 type = from->record.type; |
971 |
btree* bt = from->tree; |
972 |
hfsp_cat_key parentKey; |
973 |
if (type == HFSP_FOLDER || type == HFSP_FILE) |
974 |
if (record_init_cnid(r, bt, from->key.parent_cnid)) |
975 |
goto fail; |
976 |
// r now is the folder thread, position to the real folder |
977 |
parentKey.key_length = 6 + r->record.u.thread.nodeName.strlen * 2; |
978 |
parentKey.parent_cnid = r->record.u.thread.parentID; |
979 |
parentKey.name = r->record.u.thread.nodeName; |
980 |
if (record_init_key(r, bt, &parentKey)) |
981 |
goto fail; |
982 |
|
983 |
return 0; |
984 |
fail: |
985 |
return -1; |
986 |
|
987 |
} |
988 |
|
989 |
/* find correct node record for given node and *pindex. |
990 |
* |
991 |
* index of record in this (or next) node |
992 |
*/ |
993 |
static node_buf* prepare_next(btree* tree, UInt16 node_index, UInt16* pindex) |
994 |
{ |
995 |
node_buf* buf = btree_node_by_index(tree, node_index, NODE_CLEAN); |
996 |
btree_node_desc* desc = &buf->desc; |
997 |
UInt32 numrec = desc->num_rec; |
998 |
if (*pindex >= numrec) // move on to next node |
999 |
{ |
1000 |
UInt16 next = desc->next; |
1001 |
*pindex = 0; |
1002 |
if (!next /* is there a next node ? */ |
1003 |
|| !( buf = btree_node_by_index(tree, next, NODE_CLEAN))) |
1004 |
return NULL; |
1005 |
} |
1006 |
return buf; |
1007 |
} |
1008 |
/* move record foreward to next entry. |
1009 |
* |
1010 |
* In case of an error the value of *r is undefined ! |
1011 |
*/ |
1012 |
int record_next(record* r) |
1013 |
{ |
1014 |
btree* tree = r->tree; |
1015 |
UInt16 index = r->keyind +1; |
1016 |
UInt32 parent; |
1017 |
node_buf* buf = prepare_next(tree, r->node_index, &index); |
1018 |
|
1019 |
if (!buf) |
1020 |
return ENOENT; // No (more) such file or directory |
1021 |
|
1022 |
parent = r->key.parent_cnid; |
1023 |
|
1024 |
if (record_init(r, tree, buf, index)) |
1025 |
return -1; |
1026 |
|
1027 |
if (r->key.parent_cnid != parent || // end of current directory |
1028 |
index != r->keyind) // internal error ? |
1029 |
return ENOENT; // No (more) such file or directory |
1030 |
|
1031 |
return 0; |
1032 |
} |
1033 |
|
1034 |
/* move record foreward to next extent record. |
1035 |
* |
1036 |
* In case of an error the value of *r is undefined ! |
1037 |
*/ |
1038 |
int record_next_extent(extent_record* r) |
1039 |
{ |
1040 |
btree* tree = r->tree; |
1041 |
UInt16 index = r->keyind +1; |
1042 |
UInt32 file_id; |
1043 |
UInt8 fork_type; |
1044 |
node_buf* buf = prepare_next(tree, r->node_index, &index); |
1045 |
|
1046 |
if (!buf) |
1047 |
return ENOENT; // No (more) such file or directory |
1048 |
|
1049 |
file_id = r->key.file_id; |
1050 |
fork_type = r->key.fork_type; |
1051 |
|
1052 |
if (record_init_extent(r, tree, buf, index)) |
1053 |
return -1; |
1054 |
|
1055 |
if (r->key.file_id != file_id || // end of current file |
1056 |
r->key.fork_type != fork_type || // end of current fork |
1057 |
index != r->keyind) // internal error ? |
1058 |
return ENOENT; // No (more) such file or directory |
1059 |
|
1060 |
return 0; |
1061 |
} |
1062 |
|
1063 |
/* intialize the record by searching for the given string in the given folder. |
1064 |
* |
1065 |
* parent and r may be the same. |
1066 |
*/ |
1067 |
int record_init_string_parent(record* r, record* parent, char* name) |
1068 |
{ |
1069 |
hfsp_cat_key key; |
1070 |
|
1071 |
if (parent->record.type == HFSP_FOLDER) |
1072 |
key.parent_cnid = parent->record.u.folder.id; |
1073 |
else if(parent->record.type == HFSP_FOLDER_THREAD) |
1074 |
key.parent_cnid = parent->key.parent_cnid; |
1075 |
else |
1076 |
HFSP_ERROR(-1, "record_init_string_parent: parent is not a folder."); |
1077 |
|
1078 |
key.key_length = 6 + unicode_asc2uni(&key.name,name); // 6 for minumum size |
1079 |
return record_init_key(r, parent->tree, &key); |
1080 |
|
1081 |
fail: |
1082 |
return -1; |
1083 |
} |
1084 |
|
1085 |
/* move record up in folder hierarchy (if possible) */ |
1086 |
int record_up(record* r) |
1087 |
{ |
1088 |
if (r->record.type == HFSP_FOLDER) |
1089 |
{ |
1090 |
// locate folder thread |
1091 |
if (record_init_cnid(r, r->tree, r->record.u.folder.id)) |
1092 |
return -1; |
1093 |
} |
1094 |
else if(r->record.type == HFSP_FOLDER_THREAD) |
1095 |
{ |
1096 |
// do nothing were are already where we want to be |
1097 |
} |
1098 |
else |
1099 |
HFSP_ERROR(-1, "record_up: record is neither folder nor folder thread."); |
1100 |
|
1101 |
if(r->record.type != HFSP_FOLDER_THREAD) |
1102 |
HFSP_ERROR(-1, "record_up: unable to locate parent"); |
1103 |
return record_init_cnid(r, r->tree, r->record.u.thread.parentID); |
1104 |
|
1105 |
fail: |
1106 |
return -1; |
1107 |
} |
1108 |
|
1109 |
/* Delete the record from the tree but dont care about its type. |
1110 |
* helper function for record_delete */ |
1111 |
|
1112 |
static int record_delete_direct(record *r) |
1113 |
{ |
1114 |
btree *bt = r->tree; |
1115 |
record parent; // Parent folder of the deleted record |
1116 |
UInt16 type = r->record.type; |
1117 |
hfsp_vh *volheader; |
1118 |
|
1119 |
HFSP_SYNC_START(HFSP_WRITELOCK, bt); |
1120 |
|
1121 |
// Must reload record it may bave become invalid.. |
1122 |
if (record_init_key(r, bt, &r->key)) |
1123 |
goto fail; |
1124 |
|
1125 |
btree_remove_record(bt, r->node_index, r->keyind); |
1126 |
|
1127 |
// Care about valence only when not using threads ... |
1128 |
if (type <= HFSP_THREAD_OFFSET) |
1129 |
{ |
1130 |
if (record_find_parent(&parent, r)) |
1131 |
goto fail; |
1132 |
if (parent.record.u.folder.valence == 0) { |
1133 |
// fprintf(stderr, "Deleting item from folder with 0 items !?\n"); |
1134 |
} else { |
1135 |
parent.record.u.folder.valence --; |
1136 |
parent.record.u.folder.content_mod_date = HFSPTIMEDIFF + time(NULL); |
1137 |
// write back that folder ... |
1138 |
record_update(&parent); |
1139 |
} |
1140 |
} |
1141 |
|
1142 |
volheader = &bt->vol->vol; |
1143 |
HFSP_SYNC_END(HFSP_WRITELOCK, bt); |
1144 |
|
1145 |
// Update header depending on type |
1146 |
if (type == HFSP_FOLDER_THREAD) |
1147 |
volheader->folder_count--; |
1148 |
else if (type == HFSP_FILE) |
1149 |
volheader->file_count--; |
1150 |
|
1151 |
return 0; |
1152 |
fail: |
1153 |
HFSP_SYNC_END(HFSP_WRITELOCK, bt); |
1154 |
return -1; |
1155 |
} |
1156 |
|
1157 |
/* recursivly remove contents of folder from btree |
1158 |
* |
1159 |
* r must be a folder thread. |
1160 |
*/ |
1161 |
static int record_delete_recurse(record* r, int flags) |
1162 |
{ |
1163 |
record iter = *r; // iterator for entries |
1164 |
int result = 0; |
1165 |
if (r->record.type != HFSP_FOLDER_THREAD) |
1166 |
return -1; // should not happen ! |
1167 |
|
1168 |
while (!result && !record_next(&iter)) |
1169 |
{ |
1170 |
if (flags & RECORD_DELETE_RECURSE) |
1171 |
result = record_delete(&iter, flags); |
1172 |
// Mmh, this will fail as soon as the b*tree is reorganized :( |
1173 |
else |
1174 |
return ENOTEMPTY; // must not delete non-empty directory |
1175 |
iter = *r; // reset iterator |
1176 |
} |
1177 |
|
1178 |
return 0; |
1179 |
} |
1180 |
|
1181 |
/* remove record from btree, It does not care about any |
1182 |
* forks associated with a file (yet) */ |
1183 |
int record_delete(record* r, int flags) |
1184 |
{ |
1185 |
btree *bt = r->tree; |
1186 |
record parent; // Parent folder of the deleted record |
1187 |
hfsp_cat_key parentKey; |
1188 |
UInt16 type = r->record.type; |
1189 |
int result = 0; |
1190 |
|
1191 |
if (type == HFSP_FOLDER && !(flags & RECORD_DELETE_DIRECT)) |
1192 |
{ |
1193 |
record thread; |
1194 |
// locate folder thread and delete it |
1195 |
result = record_init_cnid(&thread, bt, r->record.u.folder.id); |
1196 |
if (!result) |
1197 |
result = record_delete(&thread, flags | RECORD_DELETE_DIRECT); |
1198 |
// failing to delete the folder now will leave the |
1199 |
// btree inconsistant, but this should not happen any more |
1200 |
} |
1201 |
|
1202 |
if (type == HFSP_FOLDER_THREAD) |
1203 |
{ |
1204 |
// will result in error in case folder is not empty |
1205 |
result = record_delete_recurse(r, flags & ~RECORD_DELETE_DIRECT); |
1206 |
|
1207 |
if (!result && !(flags & RECORD_DELETE_DIRECT)) |
1208 |
{ |
1209 |
record folder; |
1210 |
hfsp_cat_key folderKey; |
1211 |
// locate folder for thread |
1212 |
folderKey.key_length = 6 + r->record.u.thread.nodeName.strlen * 2; |
1213 |
folderKey.parent_cnid = r->record.u.thread.parentID; |
1214 |
folderKey.name = r->record.u.thread.nodeName; |
1215 |
result = record_init_key(&parent, bt, &parentKey); |
1216 |
if (!result) // shortcut recursive call |
1217 |
result = record_delete_direct(&folder); |
1218 |
// failing to delete the folder thread now will leave the |
1219 |
// btree inconsistant, but this should not happen any more |
1220 |
} |
1221 |
} |
1222 |
|
1223 |
if (!result) |
1224 |
result = record_delete_direct(r); |
1225 |
|
1226 |
return result; |
1227 |
} |
1228 |
|
1229 |
/* insert record into btree, It does not care about any |
1230 |
* forks associated with a file (yet) |
1231 |
* ToDo: Care about parent and header counts |
1232 |
* |
1233 |
*/ |
1234 |
int record_insert(record* r) |
1235 |
{ |
1236 |
btree *bt = r->tree; |
1237 |
record parent; // Parent folder of the new record |
1238 |
UInt16 type = r->record.type; |
1239 |
int result = 0; |
1240 |
char buf[sizeof(record)]; // a bit too long, well |
1241 |
char* p = buf; |
1242 |
int len; // actual len of buffer used |
1243 |
int keyind; // index where key should be inserted |
1244 |
UInt16 nodeind; // node where record should be inserted |
1245 |
hfsp_vh *volheader; |
1246 |
|
1247 |
// now insert thread for file/folder |
1248 |
if (type == HFSP_FOLDER || type == HFSP_FILE) |
1249 |
{ |
1250 |
record thread; |
1251 |
// create folder thread and insert it |
1252 |
result = record_init_thread(&thread, r); |
1253 |
if (!result) |
1254 |
result = record_insert(&thread); |
1255 |
} |
1256 |
|
1257 |
HFSP_SYNC_START(HFSP_WRITELOCK, bt); |
1258 |
|
1259 |
// Find out where to insert the record |
1260 |
if (record_find_key(bt, &r->key, &keyind, &nodeind)) { |
1261 |
hfsp_error = "File/Folder already exists"; |
1262 |
HFSP_ERROR(EEXIST, hfsp_error); |
1263 |
} |
1264 |
|
1265 |
// Create memory image |
1266 |
p = record_writekey (p, &r->key); |
1267 |
if (!p) |
1268 |
return -1; // ???? |
1269 |
p = record_writeentry(p, &r->record); |
1270 |
if (!p) |
1271 |
return -1; // ???? |
1272 |
// Insert the buffer |
1273 |
len = p - buf; |
1274 |
if (len > bt->max_rec_size) // Emergency bail out, sorry |
1275 |
{ |
1276 |
/* fprintf(stderr,"Unexpected Buffer overflow in record_insert %d > %d", |
1277 |
len, sizeof(bt->max_rec_size));*/ |
1278 |
// exit(-1); |
1279 |
goto fail; |
1280 |
} |
1281 |
if (btree_insert_record(bt,nodeind,keyind,buf,len)) |
1282 |
HFSP_ERROR(ENOSPC, "Unable to insert record into tree (volume full ?)"); |
1283 |
|
1284 |
// Ignore threads for valence and file/folder counts |
1285 |
if (type == HFSP_FOLDER || type == HFSP_FILE) |
1286 |
{ |
1287 |
// Update parent valence |
1288 |
if (record_find_parent(&parent, r)) |
1289 |
goto fail; |
1290 |
|
1291 |
parent.record.u.folder.valence ++; |
1292 |
parent.record.u.folder.content_mod_date = HFSPTIMEDIFF + time(NULL); |
1293 |
// write back that folder ... |
1294 |
record_update(&parent); |
1295 |
|
1296 |
volheader = &bt->vol->vol; |
1297 |
|
1298 |
// Update header depending on type |
1299 |
if (type == HFSP_FOLDER) |
1300 |
volheader->folder_count++; |
1301 |
else if (type == HFSP_FILE) |
1302 |
volheader->file_count++; |
1303 |
} |
1304 |
HFSP_SYNC_END(HFSP_WRITELOCK, bt); |
1305 |
return result; |
1306 |
fail: |
1307 |
HFSP_SYNC_END(HFSP_WRITELOCK, bt); |
1308 |
return -1; |
1309 |
} |