AmendHub

Download

jcs

/

amend

/

bile.c

 

(View History)

jcs   bile: Sync with other projects: bug fixes and high id persistence Latest amendment: 71 on 2022-06-14

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