AmendHub

Download

jcs

/

amend

/

bile.c

 

(View History)

jcs   bile: Don't re-use deleted id Latest amendment: 167 on 2026-09-28

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