AmendHub

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 }