Download
jcs
/amend
/bile.c
(View History)
jcs *: Bring in util from Subtext with malloc debugging, integrate it | Latest amendment: 92 on 2022-08-18 |
1 | /* |
2 | * Copyright (c) 2022 joshua stein <jcs@jcs.org> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include <string.h> |
18 | #include <stdio.h> |
19 | #include "bile.h" |
20 | #include "util.h" |
21 | |
22 | /* for errors not specific to a bile */ |
23 | static short _bile_error = 0; |
24 | |
25 | static short _bile_open_ignore_primary_map = 0; |
26 | |
27 | struct bile_object * bile_alloc(struct bile *bile, const OSType type, |
28 | const unsigned long id, const size_t size); |
29 | struct bile_object * bile_object_in_map(struct bile *bile, |
30 | const OSType type, const unsigned long id); |
31 | short bile_read_map(struct bile *bile, |
32 | struct bile_object *map_ptr); |
33 | short bile_write_map(struct bile *bile); |
34 | size_t bile_xwriteat(struct bile *bile, const size_t pos, |
35 | const void *data, const size_t len); |
36 | void bile_check_sanity(struct bile *bile); |
37 | |
38 | /* Public API */ |
39 | |
40 | short |
41 | bile_error(struct bile *bile) |
42 | { |
43 | if (bile == NULL) |
44 | return _bile_error; |
45 | |
46 | return bile->last_error; |
47 | } |
48 | |
49 | struct bile * |
50 | bile_create(const Str255 filename, short vrefnum, const OSType creator, |
51 | const OSType type) |
52 | { |
53 | struct bile *bile = NULL; |
54 | size_t len; |
55 | char *tmp; |
56 | short fh; |
57 | |
58 | _bile_error = 0; |
59 | |
60 | /* create file */ |
61 | _bile_error = Create(filename, vrefnum, creator, type); |
62 | if (_bile_error) |
63 | return NULL; |
64 | |
65 | _bile_error = FSOpen(filename, vrefnum, &fh); |
66 | if (_bile_error) |
67 | return NULL; |
68 | |
69 | _bile_error = SetEOF(fh, BILE_ALLOCATE_SIZE); |
70 | if (_bile_error) |
71 | return NULL; |
72 | SetFPos(fh, fsFromStart, 0); |
73 | |
74 | bile = xmalloczero(sizeof(struct bile), "bile_create"); |
75 | memcpy(bile->magic, BILE_MAGIC, sizeof(bile->magic)); |
76 | bile->vrefnum = vrefnum; |
77 | bile->frefnum = fh; |
78 | bile->map_ptr.type = BILE_TYPE_MAPPTR; |
79 | memcpy(bile->filename, filename, sizeof(bile->filename)); |
80 | |
81 | /* write magic */ |
82 | len = BILE_MAGIC_LEN; |
83 | tmp = xstrdup(BILE_MAGIC, "bile_create magic"); |
84 | _bile_error = FSWrite(bile->frefnum, &len, tmp); |
85 | xfree(&tmp); |
86 | if (_bile_error) |
87 | goto create_bail; |
88 | |
89 | /* write header pointing to blank map */ |
90 | len = sizeof(bile->map_ptr); |
91 | _bile_error = FSWrite(bile->frefnum, &len, &bile->map_ptr); |
92 | if (_bile_error) |
93 | goto create_bail; |
94 | |
95 | len = sizeof(bile->old_map_ptr); |
96 | _bile_error = FSWrite(bile->frefnum, &len, &bile->old_map_ptr); |
97 | if (_bile_error) |
98 | goto create_bail; |
99 | |
100 | /* padding */ |
101 | len = BILE_HEADER_LEN - BILE_MAGIC_LEN - BILE_OBJECT_SIZE - |
102 | BILE_OBJECT_SIZE; |
103 | tmp = xmalloczero(len, "bile_create padding"); |
104 | _bile_error = FSWrite(bile->frefnum, &len, tmp); |
105 | if (_bile_error) |
106 | goto create_bail; |
107 | xfree(&tmp); |
108 | |
109 | GetFPos(fh, &bile->file_size); |
110 | |
111 | if (bile->file_size != BILE_HEADER_LEN) |
112 | panic("bile_create: incorrect length after writing: %ld (!= %ld)", |
113 | bile->file_size, (long)BILE_HEADER_LEN); |
114 | |
115 | return bile; |
116 | |
117 | create_bail: |
118 | FSClose(bile->frefnum); |
119 | bile->frefnum = -1; |
120 | if (bile != NULL) |
121 | xfree(&bile); |
122 | return NULL; |
123 | } |
124 | |
125 | struct bile * |
126 | bile_open(const Str255 filename, short vrefnum) |
127 | { |
128 | struct bile *bile = NULL; |
129 | struct bile_object map_obj, *o; |
130 | char magic[BILE_MAGIC_LEN + 1]; |
131 | size_t file_size, map_size, size; |
132 | short fh, old_map_tried = 0; |
133 | |
134 | _bile_error = 0; |
135 | |
136 | /* open file */ |
137 | _bile_error = FSOpen(filename, vrefnum, &fh); |
138 | if (_bile_error) |
139 | return NULL; |
140 | |
141 | _bile_error = SetFPos(fh, fsFromLEOF, 0); |
142 | if (_bile_error) |
143 | goto open_bail; |
144 | GetFPos(fh, &file_size); |
145 | SetFPos(fh, fsFromStart, 0); |
146 | |
147 | bile = xmalloczero(sizeof(struct bile), "bile_open"); |
148 | memcpy(bile->magic, BILE_MAGIC, sizeof(bile->magic)); |
149 | bile->vrefnum = vrefnum; |
150 | bile->frefnum = fh; |
151 | memcpy(bile->filename, filename, sizeof(bile->filename)); |
152 | bile->file_size = file_size; |
153 | |
154 | /* verify magic */ |
155 | size = BILE_MAGIC_LEN; |
156 | _bile_error = FSRead(fh, &size, &magic); |
157 | if (_bile_error) |
158 | goto open_bail; |
159 | |
160 | if (strncmp(magic, BILE_MAGIC, BILE_MAGIC_LEN) != 0) { |
161 | if (strncmp(magic, BILE1_MAGIC, BILE1_MAGIC_LEN) == 0) |
162 | _bile_error = BILE_ERR_NEED_UPGRADE_1; |
163 | else |
164 | _bile_error = -1; |
165 | goto open_bail; |
166 | } |
167 | |
168 | /* load map pointer */ |
169 | size = sizeof(bile->map_ptr); |
170 | _bile_error = FSRead(bile->frefnum, &size, &bile->map_ptr); |
171 | if (_bile_error) |
172 | goto open_bail; |
173 | |
174 | /* old map pointer */ |
175 | size = sizeof(bile->old_map_ptr); |
176 | _bile_error = FSRead(bile->frefnum, &size, &bile->old_map_ptr); |
177 | if (_bile_error) |
178 | goto open_bail; |
179 | |
180 | if (_bile_open_ignore_primary_map) { |
181 | if (!bile->old_map_ptr.size) |
182 | goto open_bail; |
183 | bile->map_ptr = bile->old_map_ptr; |
184 | } |
185 | |
186 | if (bile->map_ptr.size) { |
187 | if (bile_read_map(bile, &bile->map_ptr) != 0) { |
188 | warn("bile_open: Failed reading map"); |
189 | goto open_bail; |
190 | } |
191 | } |
192 | |
193 | return bile; |
194 | |
195 | open_bail: |
196 | FSClose(bile->frefnum); |
197 | bile->frefnum = -1; |
198 | if (bile != NULL) |
199 | xfree(&bile); |
200 | return NULL; |
201 | } |
202 | |
203 | void |
204 | bile_check_sanity(struct bile *bile) |
205 | { |
206 | if (bile == NULL) |
207 | panic("bile_check_sanity: NULL bile"); |
208 | |
209 | if (memcmp(bile->magic, BILE_MAGIC, sizeof(bile->magic)) != 0) |
210 | panic("bile_check_sanity: bogus magic"); |
211 | } |
212 | |
213 | struct bile * |
214 | bile_open_recover_map(const Str255 filename, short vrefnum) |
215 | { |
216 | struct bile *bile; |
217 | |
218 | _bile_open_ignore_primary_map = 1; |
219 | bile = bile_open(filename, vrefnum); |
220 | _bile_open_ignore_primary_map = 0; |
221 | |
222 | if (bile) { |
223 | bile->map_ptr = bile->old_map_ptr; |
224 | bile_write_map(bile); |
225 | } |
226 | |
227 | return bile; |
228 | } |
229 | |
230 | short |
231 | bile_flush(struct bile *bile, short and_vol) |
232 | { |
233 | IOParam pb; |
234 | short ret; |
235 | |
236 | bile_check_sanity(bile); |
237 | |
238 | memset(&pb, 0, sizeof(pb)); |
239 | pb.ioRefNum = bile->frefnum; |
240 | /* if we're not flushing the volume, write async */ |
241 | ret = PBFlushFile(&pb, !and_vol); |
242 | if (ret != noErr) |
243 | return ret; |
244 | |
245 | if (and_vol) { |
246 | memset(&pb, 0, sizeof(pb)); |
247 | /* XXX: this flushes default volume, maybe flush specific volume */ |
248 | ret = PBFlushVol(&pb, false); |
249 | if (ret != noErr) |
250 | return ret; |
251 | } |
252 | |
253 | return 0; |
254 | } |
255 | |
256 | void |
257 | bile_close(struct bile *bile) |
258 | { |
259 | bile_check_sanity(bile); |
260 | |
261 | _bile_error = 0; |
262 | |
263 | FSClose(bile->frefnum); |
264 | bile->frefnum = -1; |
265 | if (bile->map != NULL) |
266 | xfree(&bile->map); |
267 | } |
268 | |
269 | struct bile_object * |
270 | bile_find(struct bile *bile, const OSType type, const unsigned long id) |
271 | { |
272 | struct bile_object *o, *ocopy; |
273 | unsigned long n; |
274 | char note[MALLOC_NOTE_SIZE]; |
275 | |
276 | bile_check_sanity(bile); |
277 | |
278 | o = bile_object_in_map(bile, type, id); |
279 | if (o == NULL) |
280 | return NULL; |
281 | |
282 | snprintf(note, sizeof(note), "bile_find %s %lu", OSTypeToString(type), |
283 | id); |
284 | ocopy = xmalloc(BILE_OBJECT_SIZE, note); |
285 | memcpy(ocopy, o, BILE_OBJECT_SIZE); |
286 | |
287 | return ocopy; |
288 | } |
289 | |
290 | size_t |
291 | bile_count_by_type(struct bile *bile, const OSType type) |
292 | { |
293 | struct bile_object *o; |
294 | size_t n, count = 0; |
295 | |
296 | bile_check_sanity(bile); |
297 | |
298 | _bile_error = bile->last_error = 0; |
299 | |
300 | for (n = 0; n < bile->nobjects; n++) { |
301 | o = &bile->map[n]; |
302 | if (o->type == type) |
303 | count++; |
304 | } |
305 | |
306 | return count; |
307 | } |
308 | |
309 | size_t |
310 | bile_sorted_ids_by_type(struct bile *bile, const OSType type, |
311 | unsigned long **ret) |
312 | { |
313 | struct bile_object *o; |
314 | size_t count, size = 0, n, j, t; |
315 | unsigned long *ids = NULL; |
316 | |
317 | count = 0; |
318 | *ret = NULL; |
319 | |
320 | bile_check_sanity(bile); |
321 | |
322 | for (n = 0; n < bile->nobjects; n++) { |
323 | o = &bile->map[n]; |
324 | if (o->type != type) |
325 | continue; |
326 | |
327 | EXPAND_TO_FIT(ids, size, count * sizeof(size_t), sizeof(size_t), |
328 | 10 * sizeof(size_t)); |
329 | ids[count++] = o->id; |
330 | } |
331 | |
332 | for (n = 0; n < count; n++) { |
333 | for (j = 0; j < count - n - 1; j++) { |
334 | if (ids[j] > ids[j + 1]) { |
335 | t = ids[j]; |
336 | ids[j] = ids[j + 1]; |
337 | ids[j + 1] = t; |
338 | } |
339 | } |
340 | } |
341 | |
342 | *ret = ids; |
343 | return count; |
344 | } |
345 | |
346 | struct bile_object * |
347 | bile_get_nth_of_type(struct bile *bile, const unsigned long index, |
348 | const OSType type) |
349 | { |
350 | struct bile_object *o, *ocopy; |
351 | size_t n, count = 0; |
352 | char note[MALLOC_NOTE_SIZE]; |
353 | |
354 | bile_check_sanity(bile); |
355 | |
356 | _bile_error = bile->last_error = 0; |
357 | |
358 | for (n = 0; n < bile->nobjects; n++) { |
359 | o = &bile->map[n]; |
360 | if (o->type != type) |
361 | continue; |
362 | |
363 | if (count == index) { |
364 | snprintf(note, sizeof(note), "bile_get_nth %s %lu", |
365 | OSTypeToString(type), index); |
366 | ocopy = xmalloc(BILE_OBJECT_SIZE, note); |
367 | memcpy(ocopy, o, BILE_OBJECT_SIZE); |
368 | return ocopy; |
369 | } |
370 | count++; |
371 | } |
372 | |
373 | return NULL; |
374 | } |
375 | |
376 | unsigned long |
377 | bile_next_id(struct bile *bile, const OSType type) |
378 | { |
379 | struct bile_object *o; |
380 | size_t n; |
381 | unsigned long id = 1; |
382 | unsigned long highest; |
383 | |
384 | bile_check_sanity(bile); |
385 | |
386 | _bile_error = bile->last_error = 0; |
387 | |
388 | for (n = 0; n < bile->nobjects; n++) { |
389 | o = &bile->map[n]; |
390 | if (o->type == type && o->id >= id) |
391 | id = o->id + 1; |
392 | } |
393 | |
394 | if (bile_read(bile, BILE_TYPE_HIGHESTID, type, &highest, |
395 | sizeof(unsigned long)) == sizeof(unsigned long)) { |
396 | if (highest > id) |
397 | id = highest + 1; |
398 | } |
399 | |
400 | return id; |
401 | } |
402 | |
403 | short |
404 | bile_delete(struct bile *bile, const OSType type, const unsigned long id) |
405 | { |
406 | static char zero[128] = { 0 }; |
407 | struct bile_object *o; |
408 | size_t pos, size, wsize, n; |
409 | unsigned long highest; |
410 | |
411 | bile_check_sanity(bile); |
412 | |
413 | _bile_error = bile->last_error = 0; |
414 | |
415 | o = bile_object_in_map(bile, type, id); |
416 | if (o == NULL) { |
417 | _bile_error = bile->last_error = -1; |
418 | return -1; |
419 | } |
420 | |
421 | o->type = BILE_TYPE_PURGE; |
422 | pos = o->pos; |
423 | size = o->size + BILE_OBJECT_SIZE; |
424 | |
425 | _bile_error = bile->last_error = SetFPos(bile->frefnum, fsFromStart, |
426 | pos); |
427 | if (_bile_error) |
428 | return -1; |
429 | |
430 | while (size > 0) { |
431 | wsize = MIN(128, size); |
432 | size -= wsize; |
433 | |
434 | _bile_error = bile->last_error = FSWrite(bile->frefnum, &wsize, |
435 | zero); |
436 | if (_bile_error) |
437 | return -1; |
438 | } |
439 | |
440 | /* |
441 | * If this is the highest id of this type, store it so it won't get |
442 | * handed out again from bile_next_id |
443 | */ |
444 | highest = id; |
445 | for (n = 0; n < bile->nobjects; n++) { |
446 | o = &bile->map[n]; |
447 | if (o->type == type && o->id > highest) { |
448 | highest = o->id; |
449 | break; |
450 | } |
451 | } |
452 | |
453 | if (highest == id) { |
454 | /* store the type as the id, and the highest id as the data */ |
455 | bile_write(bile, BILE_TYPE_HIGHESTID, type, &highest, |
456 | sizeof(unsigned long)); |
457 | |
458 | /* bile_write wrote a new map for us */ |
459 | } else { |
460 | bile_write_map(bile); |
461 | if (_bile_error) |
462 | return -1; |
463 | } |
464 | |
465 | return 0; |
466 | } |
467 | |
468 | size_t |
469 | bile_read_object(struct bile *bile, const struct bile_object *o, |
470 | void *data, const size_t len) |
471 | { |
472 | struct bile_object verify; |
473 | size_t rsize, wantlen, ret; |
474 | |
475 | bile_check_sanity(bile); |
476 | |
477 | if (o == NULL) |
478 | panic("bile_read_object: NULL object passed"); |
479 | if (data == NULL) |
480 | panic("bile_read_object: NULL data pointer passed"); |
481 | if (len == 0) |
482 | panic("bile_read_object: zero len"); |
483 | |
484 | _bile_error = bile->last_error = 0; |
485 | |
486 | if (o->pos + BILE_OBJECT_SIZE + o->size > bile->file_size) { |
487 | warn("bile_read_object: object %s:%ld pos %ld size %ld > " |
488 | "file size %ld", OSTypeToString(o->type), o->id, o->pos, |
489 | o->size, bile->file_size); |
490 | _bile_error = bile->last_error = BILE_ERR_BOGUS_OBJECT; |
491 | return 0; |
492 | } |
493 | |
494 | _bile_error = bile->last_error = SetFPos(bile->frefnum, fsFromStart, |
495 | o->pos); |
496 | if (_bile_error) { |
497 | warn("bile_read_object: object %s:%lu points to bogus position " |
498 | "%lu", OSTypeToString(o->type), o->id, o->pos); |
499 | _bile_error = bile->last_error = BILE_ERR_BOGUS_OBJECT; |
500 | return 0; |
501 | } |
502 | |
503 | rsize = BILE_OBJECT_SIZE; |
504 | _bile_error = bile->last_error = FSRead(bile->frefnum, &rsize, |
505 | &verify); |
506 | if (_bile_error) |
507 | return 0; |
508 | |
509 | if (verify.id != o->id) { |
510 | warn("bile_read_object: object %s:%ld pos %ld wrong id %ld, " |
511 | "expected %ld", OSTypeToString(o->type), o->id, o->pos, |
512 | verify.id, o->id); |
513 | _bile_error = bile->last_error = BILE_ERR_BOGUS_OBJECT; |
514 | return 0; |
515 | } |
516 | if (verify.type != o->type) { |
517 | warn("bile_read_object: object %s:%ld pos %ld wrong type %ld, " |
518 | "expected %ld", OSTypeToString(o->type), o->id, o->pos, |
519 | verify.type, o->type); |
520 | _bile_error = bile->last_error = BILE_ERR_BOGUS_OBJECT; |
521 | return 0; |
522 | } |
523 | if (verify.size != o->size) { |
524 | warn("bile_read_object: object %s:%ld pos %ld wrong size %ld, " |
525 | "expected %ld", OSTypeToString(o->type), o->id, o->pos, |
526 | verify.size, o->size); |
527 | _bile_error = bile->last_error = BILE_ERR_BOGUS_OBJECT; |
528 | return 0; |
529 | } |
530 | |
531 | wantlen = len; |
532 | if (wantlen > o->size) |
533 | wantlen = o->size; |
534 | |
535 | rsize = wantlen; |
536 | _bile_error = bile->last_error = FSRead(bile->frefnum, &rsize, data); |
537 | if (_bile_error) |
538 | return 0; |
539 | |
540 | if (rsize != wantlen) { |
541 | warn("bile_read_object: %s:%lu: needed to read %ld, read %ld", |
542 | OSTypeToString(o->type), o->id, wantlen, rsize); |
543 | _bile_error = bile->last_error = BILE_ERR_BOGUS_OBJECT; |
544 | return 0; |
545 | } |
546 | |
547 | return rsize; |
548 | } |
549 | |
550 | size_t |
551 | bile_read(struct bile *bile, const OSType type, const unsigned long id, |
552 | void *data, const size_t len) |
553 | { |
554 | struct bile_object *o; |
555 | size_t ret; |
556 | |
557 | bile_check_sanity(bile); |
558 | |
559 | if (data == NULL) |
560 | panic("bile_read: NULL data pointer passed"); |
561 | |
562 | _bile_error = bile->last_error = 0; |
563 | |
564 | o = bile_object_in_map(bile, type, id); |
565 | if (o == NULL) { |
566 | _bile_error = bile->last_error = -1; |
567 | return 0; |
568 | } |
569 | |
570 | return bile_read_object(bile, o, data, len); |
571 | } |
572 | |
573 | size_t |
574 | bile_read_alloc(struct bile *bile, const OSType type, |
575 | const unsigned long id, void *data_ptr) |
576 | { |
577 | struct bile_object *o; |
578 | size_t ret; |
579 | char **data; |
580 | char note[MALLOC_NOTE_SIZE]; |
581 | |
582 | bile_check_sanity(bile); |
583 | |
584 | if (data_ptr == NULL) |
585 | panic("bile_read: NULL data pointer passed"); |
586 | |
587 | data = (char **)data_ptr; |
588 | _bile_error = bile->last_error = 0; |
589 | |
590 | o = bile_object_in_map(bile, type, id); |
591 | if (o == NULL) { |
592 | _bile_error = bile->last_error = -1; |
593 | *data = NULL; |
594 | return 0; |
595 | } |
596 | |
597 | snprintf(note, sizeof(note), "bile_read_alloc %s %ld", |
598 | OSTypeToString(type), id); |
599 | *data = xmalloczero(o->size, note); |
600 | ret = bile_read_object(bile, o, *data, o->size); |
601 | |
602 | return ret; |
603 | } |
604 | |
605 | size_t |
606 | bile_write(struct bile *bile, const OSType type, const unsigned long id, |
607 | const void *data, const size_t len) |
608 | { |
609 | struct bile_object *old, *new_obj; |
610 | size_t wrote; |
611 | short error; |
612 | |
613 | bile_check_sanity(bile); |
614 | |
615 | if (len == 0) |
616 | panic("bile_write: zero len passed"); |
617 | if (data == NULL) |
618 | panic("bile_write: NULL data pointer passed"); |
619 | |
620 | _bile_error = bile->last_error = 0; |
621 | |
622 | if ((old = bile_object_in_map(bile, type, id)) != NULL) |
623 | old->type = BILE_TYPE_PURGE; |
624 | |
625 | new_obj = bile_alloc(bile, type, id, len); |
626 | |
627 | wrote = bile_xwriteat(bile, new_obj->pos, new_obj, BILE_OBJECT_SIZE); |
628 | if (wrote != BILE_OBJECT_SIZE || bile->last_error) |
629 | return 0; |
630 | wrote = bile_xwriteat(bile, new_obj->pos + BILE_OBJECT_SIZE, data, len); |
631 | if (wrote != len || bile->last_error) |
632 | return 0; |
633 | |
634 | SetFPos(bile->frefnum, fsFromLEOF, 0); |
635 | GetFPos(bile->frefnum, &bile->file_size); |
636 | |
637 | bile_write_map(bile); |
638 | if (bile->last_error) |
639 | return 0; |
640 | |
641 | return wrote; |
642 | } |
643 | |
644 | short |
645 | bile_marshall_object(struct bile *bile, |
646 | const struct bile_object_field *fields, const size_t nfields, |
647 | void *object, void *ret_ptr, size_t *ret_size, char *note) |
648 | { |
649 | char **ret; |
650 | char *data, *ptr; |
651 | size_t size = 0, fsize = 0, n; |
652 | bool write = false; |
653 | |
654 | bile_check_sanity(bile); |
655 | |
656 | if (ret_ptr == NULL) |
657 | panic("bile_pack_object invalid ret"); |
658 | |
659 | ret = (char **)ret_ptr; |
660 | *ret = NULL; |
661 | *ret_size = 0; |
662 | |
663 | iterate_fields: |
664 | for (n = 0; n < nfields; n++) { |
665 | if (fields[n].size < 0) { |
666 | /* |
667 | * Dynamically-sized field, get length from its length field |
668 | * and multiply by negative size, so -1 is the actual length but |
669 | * -4 could be used if length field is number of longs |
670 | */ |
671 | ptr = (char *)object + fields[n].object_len_off; |
672 | fsize = *(size_t *)ptr * -(fields[n].size); |
673 | } else |
674 | fsize = fields[n].size; |
675 | |
676 | if (write) { |
677 | if (fields[n].size < 0) { |
678 | /* field is dynamically allocated, first write size */ |
679 | memcpy(data + size, &fsize, sizeof(fsize)); |
680 | size += sizeof(fsize); |
681 | if (!fsize) |
682 | continue; |
683 | } |
684 | |
685 | ptr = (char *)object + fields[n].struct_off; |
686 | if (fields[n].size < 0) { |
687 | ptr = (char *)*(unsigned long *)ptr; |
688 | if (ptr == 0) |
689 | panic("bile_pack_object field[%lu] points to NULL", n); |
690 | } |
691 | |
692 | memcpy(data + size, ptr, fsize); |
693 | } else if (fields[n].size < 0) { |
694 | /* account for dynamic field length */ |
695 | size += sizeof(fsize); |
696 | } |
697 | |
698 | size += fsize; |
699 | } |
700 | |
701 | if (!write) { |
702 | data = xmalloc(size, note); |
703 | write = true; |
704 | size = 0; |
705 | goto iterate_fields; |
706 | } |
707 | |
708 | *ret = data; |
709 | *ret_size = size; |
710 | |
711 | return 0; |
712 | } |
713 | |
714 | short |
715 | bile_unmarshall_object(struct bile *bile, |
716 | const struct bile_object_field *fields, const size_t nfields, |
717 | const void *data, const size_t data_size, void *object, |
718 | const size_t object_size, bool deep, char *note) |
719 | { |
720 | size_t off, fsize = 0, n; |
721 | char *ptr, *dptr; |
722 | |
723 | bile_check_sanity(bile); |
724 | |
725 | for (off = 0, n = 0; n < nfields; n++) { |
726 | if (fields[n].size < 0) { |
727 | /* dynamically-sized field, read length */ |
728 | memcpy(&fsize, (char *)data + off, sizeof(fsize)); |
729 | off += sizeof(fsize); |
730 | } else |
731 | fsize = fields[n].size; |
732 | |
733 | if (off + fsize > data_size) |
734 | panic("bile_unmarshall_object: overflow at field %lu of %lu!", |
735 | n + 1, nfields); |
736 | |
737 | ptr = (char *)object + fields[n].struct_off; |
738 | |
739 | if (fields[n].size < 0 && deep) { |
740 | if (fsize == 0) { |
741 | memset(ptr, 0, sizeof(dptr)); |
742 | continue; |
743 | } |
744 | dptr = xmalloc(fsize, note); |
745 | memcpy(ptr, &dptr, sizeof(dptr)); |
746 | ptr = dptr; |
747 | } |
748 | |
749 | if (fields[n].size < 0 && !deep) |
750 | memset(ptr, 0, sizeof(dptr)); |
751 | else { |
752 | if (fields[n].size > 0 && |
753 | fields[n].struct_off + fsize > object_size) |
754 | panic("bile_unmarshall_object: overflow writing to object " |
755 | "at field %lu! (%lu > %lu)", n + 1, |
756 | fields[n].struct_off + fsize, object_size); |
757 | memcpy(ptr, (char *)data + off, fsize); |
758 | } |
759 | |
760 | off += fsize; |
761 | } |
762 | |
763 | return 0; |
764 | } |
765 | |
766 | |
767 | short |
768 | bile_verify(struct bile *bile) |
769 | { |
770 | struct bile_object o; |
771 | size_t n, size, pos; |
772 | char data; |
773 | |
774 | bile_check_sanity(bile); |
775 | |
776 | _bile_error = bile->last_error = 0; |
777 | |
778 | for (n = 0, pos = 0; n < bile->nobjects; n++) { |
779 | size = bile_read_object(bile, &bile->map[n], &data, 1); |
780 | if (bile_error(bile)) |
781 | return bile_error(bile); |
782 | else if (size == 0) |
783 | return -1; |
784 | |
785 | if (bile->map[n].pos <= pos) |
786 | return -1; |
787 | pos = bile->map[n].pos + bile->map[n].size; |
788 | } |
789 | |
790 | return 0; |
791 | } |
792 | |
793 | /* Private API */ |
794 | |
795 | struct bile_object * |
796 | bile_object_in_map(struct bile *bile, const OSType type, |
797 | const unsigned long id) |
798 | { |
799 | struct bile_object *o; |
800 | size_t n; |
801 | |
802 | bile_check_sanity(bile); |
803 | |
804 | _bile_error = bile->last_error = 0; |
805 | |
806 | /* look backwards, optimizing for newer data */ |
807 | for (n = bile->nobjects; n > 0; n--) { |
808 | o = &bile->map[n - 1]; |
809 | if (o->type == type && o->id == id) |
810 | return o; |
811 | } |
812 | |
813 | return NULL; |
814 | } |
815 | |
816 | struct bile_object * |
817 | bile_alloc(struct bile *bile, const OSType type, const unsigned long id, |
818 | const size_t size) |
819 | { |
820 | struct bile_object newo; |
821 | size_t last_pos = BILE_HEADER_LEN; |
822 | size_t n, map_pos; |
823 | |
824 | bile_check_sanity(bile); |
825 | |
826 | _bile_error = bile->last_error = 0; |
827 | |
828 | /* find a gap big enough to hold our object + its size */ |
829 | for (n = 0; n < bile->nobjects; n++) { |
830 | if (bile->map[n].pos - last_pos >= (size + BILE_OBJECT_SIZE)) |
831 | break; |
832 | last_pos = bile->map[n].pos + BILE_OBJECT_SIZE + bile->map[n].size; |
833 | } |
834 | |
835 | /* |
836 | * The map is always sorted, so walk the map to find out where to |
837 | * wedge a copy of this new object, then return a pointer to it in |
838 | * the map. |
839 | */ |
840 | |
841 | map_pos = bile->nobjects; |
842 | for (n = 0; n + 1 < bile->nobjects; n++) { |
843 | if (n == 0 && last_pos < bile->map[n].pos) { |
844 | map_pos = 0; |
845 | break; |
846 | } |
847 | if (last_pos > bile->map[n].pos && |
848 | last_pos < bile->map[n + 1].pos) { |
849 | map_pos = n + 1; |
850 | break; |
851 | } |
852 | } |
853 | |
854 | bile->nobjects++; |
855 | bile->map = xreallocarray(bile->map, bile->nobjects, BILE_OBJECT_SIZE); |
856 | |
857 | if (map_pos + 1 < bile->nobjects) { |
858 | /* shift remaining objects up */ |
859 | memmove(&bile->map[map_pos + 1], &bile->map[map_pos], |
860 | sizeof(struct bile_object) * (bile->nobjects - map_pos - 1)); |
861 | } |
862 | |
863 | bile->map[map_pos].type = type; |
864 | bile->map[map_pos].id = id; |
865 | bile->map[map_pos].size = size; |
866 | bile->map[map_pos].pos = last_pos; |
867 | |
868 | return &bile->map[map_pos]; |
869 | } |
870 | |
871 | short |
872 | bile_read_map(struct bile *bile, struct bile_object *map_ptr) |
873 | { |
874 | size_t size; |
875 | struct bile_object map_obj, *map; |
876 | |
877 | bile_check_sanity(bile); |
878 | |
879 | if (map_ptr->pos + map_ptr->size > bile->file_size) { |
880 | warn("bile_read_map: map points to %lu + %lu, but file is only %lu", |
881 | map_ptr->pos, map_ptr->size, bile->file_size); |
882 | return -1; |
883 | } |
884 | |
885 | if (map_ptr->size % BILE_OBJECT_SIZE != 0) { |
886 | warn("bile_read_map: map pointer size is not a multiple of object " |
887 | "size (%lu): %lu", BILE_OBJECT_SIZE, map_ptr->size); |
888 | return -1; |
889 | } |
890 | |
891 | /* read and verify map object header map_ptr points to */ |
892 | _bile_error = SetFPos(bile->frefnum, fsFromStart, map_ptr->pos); |
893 | if (_bile_error) |
894 | return -1; |
895 | |
896 | size = sizeof(struct bile_object); |
897 | _bile_error = FSRead(bile->frefnum, &size, &map_obj); |
898 | if (_bile_error) |
899 | return -1; |
900 | |
901 | if (map_obj.pos != map_ptr->pos) { |
902 | warn("bile_read_map: map pointer points to %lu but object " |
903 | "there has position %lu", map_ptr->pos, map_obj.pos); |
904 | return -1; |
905 | } |
906 | |
907 | if (map_obj.size != map_ptr->size) { |
908 | warn("bile_read_map: map is supposed to have size %lu but " |
909 | "object pointed to has size %lu", map_ptr->size, map_obj.size); |
910 | return -1; |
911 | } |
912 | |
913 | /* read entire map */ |
914 | size = map_obj.size; |
915 | map = xmalloczero(size, "bile_read_map"); |
916 | _bile_error = FSRead(bile->frefnum, &size, map); |
917 | if (_bile_error) { |
918 | xfree(&map); |
919 | return -1; |
920 | } |
921 | |
922 | bile->map = map; |
923 | bile->nobjects = map_obj.size / BILE_OBJECT_SIZE; |
924 | |
925 | return 0; |
926 | } |
927 | |
928 | short |
929 | bile_write_map(struct bile *bile) |
930 | { |
931 | struct bile_object *obj, *new_map_obj, *new_map, |
932 | *new_map_obj_in_new_map = NULL; |
933 | size_t new_map_size, new_nobjects, new_map_id; |
934 | size_t n; |
935 | short ret; |
936 | |
937 | bile_check_sanity(bile); |
938 | |
939 | _bile_error = bile->last_error = 0; |
940 | |
941 | /* allocate a new map slightly larger than we need */ |
942 | new_nobjects = bile->nobjects; |
943 | if (bile->map_ptr.pos) |
944 | new_map_id = bile->map_ptr.id + 1; |
945 | else { |
946 | /* new file, map never written, allocate another object for map */ |
947 | new_nobjects++; |
948 | new_map_id = 1; |
949 | } |
950 | new_map_size = BILE_OBJECT_SIZE * new_nobjects; |
951 | new_map_obj = bile_alloc(bile, BILE_TYPE_MAP, new_map_id, |
952 | new_map_size); |
953 | new_map = xcalloc(BILE_OBJECT_SIZE, new_nobjects, "bile_write_map"); |
954 | |
955 | for (n = 0, new_nobjects = 0; n < bile->nobjects; n++) { |
956 | obj = &bile->map[n]; |
957 | |
958 | if (obj->type == BILE_TYPE_MAP && obj->pos == bile->map_ptr.pos) |
959 | /* don't include old map in new one */ |
960 | continue; |
961 | if (obj->type == BILE_TYPE_PURGE) |
962 | continue; |
963 | |
964 | if (obj->type == BILE_TYPE_MAP && obj->pos == new_map_obj->pos) |
965 | new_map_obj_in_new_map = &new_map[new_nobjects]; |
966 | |
967 | new_map[new_nobjects++] = *obj; |
968 | } |
969 | |
970 | /* shrink to new object count */ |
971 | new_map_size = BILE_OBJECT_SIZE * new_nobjects; |
972 | new_map_obj->size = new_map_size; |
973 | new_map_obj_in_new_map->size = new_map_size; |
974 | |
975 | /* write object header */ |
976 | bile_xwriteat(bile, new_map_obj->pos, new_map_obj, BILE_OBJECT_SIZE); |
977 | if (bile->last_error) |
978 | return -1; |
979 | |
980 | /* and then the map contents */ |
981 | bile_xwriteat(bile, new_map_obj->pos + BILE_OBJECT_SIZE, new_map, |
982 | new_map_size); |
983 | if (bile->last_error) |
984 | return -1; |
985 | |
986 | SetFPos(bile->frefnum, fsFromLEOF, 0); |
987 | GetFPos(bile->frefnum, &bile->file_size); |
988 | |
989 | if ((ret = bile_flush(bile, false)) != noErr) { |
990 | warn("bile_write_map: flush failed: %d", ret); |
991 | return -1; |
992 | } |
993 | |
994 | /* successfully wrote new map, switch over */ |
995 | xfree(&bile->map); |
996 | bile->nobjects = new_nobjects; |
997 | bile->map = new_map; |
998 | bile->old_map_ptr.pos = bile->map_ptr.pos; |
999 | bile->old_map_ptr.size = bile->map_ptr.size; |
1000 | bile->old_map_ptr.id = bile->map_ptr.id; |
1001 | bile->map_ptr.pos = new_map_obj->pos; |
1002 | bile->map_ptr.size = new_map_obj->size; |
1003 | bile->map_ptr.id = new_map_obj->id; |
1004 | |
1005 | /* write new pointer to point at new map object */ |
1006 | bile_xwriteat(bile, BILE_MAGIC_LEN, &bile->map_ptr, |
1007 | sizeof(bile->map_ptr)); |
1008 | if (bile->last_error) |
1009 | return -1; |
1010 | bile_xwriteat(bile, BILE_MAGIC_LEN + sizeof(bile->map_ptr), |
1011 | &bile->old_map_ptr, sizeof(bile->old_map_ptr)); |
1012 | if (bile->last_error) |
1013 | return -1; |
1014 | |
1015 | if ((ret = bile_flush(bile, false)) != noErr) { |
1016 | warn("bile_write_map: final flush failed: %d", ret); |
1017 | return -1; |
1018 | } |
1019 | |
1020 | return 0; |
1021 | } |
1022 | |
1023 | size_t |
1024 | bile_xwriteat(struct bile *bile, const size_t pos, const void *data, |
1025 | const size_t len) |
1026 | { |
1027 | short error; |
1028 | size_t wsize, tsize; |
1029 | long asize; |
1030 | |
1031 | bile_check_sanity(bile); |
1032 | |
1033 | _bile_error = bile->last_error = 0; |
1034 | |
1035 | if (pos + len > bile->file_size) { |
1036 | /* add new space aligning to BILE_ALLOCATE_SIZE */ |
1037 | tsize = pos + len; |
1038 | tsize += BILE_ALLOCATE_SIZE - (tsize % BILE_ALLOCATE_SIZE); |
1039 | _bile_error = SetEOF(bile->frefnum, tsize); |
1040 | if (_bile_error) |
1041 | return 0; |
1042 | _bile_error = bile->last_error = bile_flush(bile, true); |
1043 | if (_bile_error) |
1044 | return 0; |
1045 | } |
1046 | |
1047 | _bile_error = bile->last_error = SetFPos(bile->frefnum, fsFromStart, |
1048 | pos); |
1049 | if (_bile_error) |
1050 | return 0; |
1051 | |
1052 | wsize = len; |
1053 | _bile_error = bile->last_error = FSWrite(bile->frefnum, &wsize, data); |
1054 | if (_bile_error) |
1055 | return 0; |
1056 | if (wsize != len) |
1057 | panic("bile_xwriteat: short write of %lu at %lu to %s: %lu", |
1058 | len, pos, PtoCstr(bile->filename), wsize); |
1059 | |
1060 | SetFPos(bile->frefnum, fsFromLEOF, 0); |
1061 | GetFPos(bile->frefnum, &bile->file_size); |
1062 | |
1063 | return wsize; |
1064 | } |