AmendHub

Download

jcs

/

amend

/

bile.c

 

(View History)

jcs   bile+repo: Faster sorts Latest amendment: 251 on 2023-09-19

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