AmendHub

Download

jcs

/

subtext

/

bile.c

 

(View History)

jcs   bile: Sync with upstream Latest amendment: 524 on 2023-09-20

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