1 // license:BSD-3-Clause
2 // copyright-holders:Nathan Woods
3 /****************************************************************************
4
5 os9.cpp
6
7 CoCo OS-9 disk images
8
9 ****************************************************************************/
10
11 #include <ctime>
12
13 #include "imgtool.h"
14 #include "formats/imageutl.h"
15 #include "formats/coco_dsk.h"
16 #include "iflopimg.h"
17
18 enum creation_policy_t
19 {
20 CREATE_NONE,
21 CREATE_FILE,
22 CREATE_DIR
23 };
24
25 struct os9_diskinfo
26 {
27 uint32_t total_sectors;
28 uint32_t sectors_per_track;
29 uint32_t allocation_bitmap_bytes;
30 uint32_t cluster_size;
31 uint32_t root_dir_lsn;
32 uint32_t owner_id;
33 uint32_t attributes;
34 uint32_t disk_id;
35 uint32_t format_flags;
36 uint32_t bootstrap_lsn;
37 uint32_t bootstrap_size;
38 uint32_t sector_size;
39
40 unsigned int sides : 2;
41 unsigned int double_density : 1;
42 unsigned int double_track : 1;
43 unsigned int quad_track_density : 1;
44 unsigned int octal_track_density : 1;
45
46 char name[33];
47 uint8_t *allocation_bitmap;
48 };
49
50
51 struct os9_fileinfo
52 {
53 unsigned int directory : 1;
54 unsigned int non_sharable : 1;
55 unsigned int public_execute : 1;
56 unsigned int public_write : 1;
57 unsigned int public_read : 1;
58 unsigned int user_execute : 1;
59 unsigned int user_write : 1;
60 unsigned int user_read : 1;
61
62 uint32_t lsn;
63 uint32_t owner_id;
64 uint32_t link_count;
65 uint32_t file_size;
66
67 struct
68 {
69 uint32_t lsn;
70 uint32_t count;
71 } sector_map[48];
72 };
73
74
75 struct os9_direnum
76 {
77 struct os9_fileinfo dir_info;
78 uint32_t index;
79 };
80
81
82
pick_string(const void * ptr,size_t offset,size_t length,char * dest)83 static void pick_string(const void *ptr, size_t offset, size_t length, char *dest)
84 {
85 uint8_t b;
86
87 while(length--)
88 {
89 b = ((uint8_t *) ptr)[offset++];
90 *(dest++) = b & 0x7F;
91 if (b & 0x80)
92 length = 0;
93 }
94 *dest = '\0';
95 }
96
97
98
place_string(void * ptr,size_t offset,size_t length,const char * s)99 static void place_string(void *ptr, size_t offset, size_t length, const char *s)
100 {
101 size_t i;
102 uint8_t b;
103 uint8_t *bptr;
104
105 bptr = (uint8_t *) ptr;
106 bptr += offset;
107
108 bptr[0] = 0x80;
109
110 for (i = 0; s[i] && (i < length); i++)
111 {
112 b = ((uint8_t) s[i]) & 0x7F;
113 if (s[i+1] == '\0')
114 b |= 0x80;
115 bptr[i] = b;
116 }
117 }
118
119
120
os9_get_diskinfo(imgtool::image & image)121 static os9_diskinfo *os9_get_diskinfo(imgtool::image &image)
122 {
123 return (os9_diskinfo *) imgtool_floppy_extrabytes(image);
124 }
125
126
127
os9_get_dirinfo(imgtool::directory & directory)128 static struct os9_direnum *os9_get_dirinfo(imgtool::directory &directory)
129 {
130 return (struct os9_direnum *) directory.extra_bytes();
131 }
132
133
134
os9_locate_lsn(imgtool::image & image,uint32_t lsn,uint32_t * head,uint32_t * track,uint32_t * sector)135 static imgtoolerr_t os9_locate_lsn(imgtool::image &image, uint32_t lsn, uint32_t *head, uint32_t *track, uint32_t *sector)
136 {
137 const os9_diskinfo *disk_info;
138
139 disk_info = os9_get_diskinfo(image);
140 if (lsn > disk_info->total_sectors)
141 return IMGTOOLERR_CORRUPTIMAGE;
142
143 *track = lsn / (disk_info->sectors_per_track * disk_info->sides);
144 *head = (lsn / disk_info->sectors_per_track) % disk_info->sides;
145 *sector = (lsn % disk_info->sectors_per_track) + 1;
146 return IMGTOOLERR_SUCCESS;
147 }
148
149
150
os9_read_lsn(imgtool::image & img,uint32_t lsn,int offset,void * buffer,size_t buffer_len)151 static imgtoolerr_t os9_read_lsn(imgtool::image &img, uint32_t lsn, int offset, void *buffer, size_t buffer_len)
152 {
153 imgtoolerr_t err;
154 floperr_t ferr;
155 uint32_t head, track, sector;
156
157 err = os9_locate_lsn(img, lsn, &head, &track, §or);
158 if (err)
159 return err;
160
161 ferr = floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len);
162 if (ferr)
163 return imgtool_floppy_error(ferr);
164
165 return IMGTOOLERR_SUCCESS;
166 }
167
168
169
os9_write_lsn(imgtool::image & img,uint32_t lsn,int offset,const void * buffer,size_t buffer_len)170 static imgtoolerr_t os9_write_lsn(imgtool::image &img, uint32_t lsn, int offset, const void *buffer, size_t buffer_len)
171 {
172 imgtoolerr_t err;
173 floperr_t ferr;
174 uint32_t head, track, sector;
175
176 err = os9_locate_lsn(img, lsn, &head, &track, §or);
177 if (err)
178 return err;
179
180 ferr = floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len, 0); /* TODO: pass ddam argument from imgtool */
181 if (ferr)
182 return imgtool_floppy_error(ferr);
183
184 return IMGTOOLERR_SUCCESS;
185 }
186
187
188
os9_lookup_lsn(imgtool::image & img,const struct os9_fileinfo * file_info,uint32_t * index)189 static uint32_t os9_lookup_lsn(imgtool::image &img,
190 const struct os9_fileinfo *file_info, uint32_t *index)
191 {
192 int i;
193 uint32_t lsn;
194 const os9_diskinfo *disk_info;
195
196 disk_info = os9_get_diskinfo(img);
197 lsn = *index / disk_info->sector_size;
198
199 i = 0;
200 while(lsn >= file_info->sector_map[i].count)
201 lsn -= file_info->sector_map[i++].count;
202
203 lsn = file_info->sector_map[i].lsn + lsn;
204 *index %= disk_info->sector_size;
205 return lsn;
206 }
207
208
209
os9_interpret_dirent(void * entry,char ** filename,uint32_t * lsn,int * corrupt)210 static int os9_interpret_dirent(void *entry, char **filename, uint32_t *lsn, int *corrupt)
211 {
212 int i;
213 char *entry_b = (char *) entry;
214
215 *filename = NULL;
216 *lsn = 0;
217 if (corrupt)
218 *corrupt = false;
219
220 if (entry_b[28] != '\0')
221 {
222 if (corrupt)
223 *corrupt = true;
224 }
225
226 for (i = 0; (i < 28) && !(entry_b[i] & 0x80); i++)
227 ;
228 entry_b[i] &= 0x7F;
229 entry_b[i+1] = '\0';
230
231 *lsn = pick_integer_be(entry, 29, 3);
232 if (strcmp(entry_b, ".") && strcmp(entry_b, ".."))
233 *filename = entry_b;
234 return *filename != NULL;
235 }
236
237
238
239 /*-------------------------------------------------
240 os9_decode_file_header - reads a file/directory
241 entry from an LSN, and decodes it
242 -------------------------------------------------*/
243
os9_decode_file_header(imgtool::image & image,int lsn,struct os9_fileinfo * info)244 static imgtoolerr_t os9_decode_file_header(imgtool::image &image,
245 int lsn, struct os9_fileinfo *info)
246 {
247 imgtoolerr_t err;
248 uint32_t attributes, count;
249 int max_entries, i;
250 const os9_diskinfo *disk_info;
251 uint8_t header[256];
252
253 disk_info = os9_get_diskinfo(image);
254
255 err = os9_read_lsn(image, lsn, 0, header, sizeof(header));
256 if (err)
257 return err;
258
259 info->lsn = lsn;
260 attributes = pick_integer_be(header, 0, 1);
261 info->owner_id = pick_integer_be(header, 1, 2);
262 info->link_count = pick_integer_be(header, 8, 1);
263 info->file_size = pick_integer_be(header, 9, 4);
264 info->directory = (attributes & 0x80) ? 1 : 0;
265 info->non_sharable = (attributes & 0x40) ? 1 : 0;
266 info->public_execute = (attributes & 0x20) ? 1 : 0;
267 info->public_write = (attributes & 0x10) ? 1 : 0;
268 info->public_read = (attributes & 0x08) ? 1 : 0;
269 info->user_execute = (attributes & 0x04) ? 1 : 0;
270 info->user_write = (attributes & 0x02) ? 1 : 0;
271 info->user_read = (attributes & 0x01) ? 1 : 0;
272
273 if (info->directory && (info->file_size % 32 != 0))
274 return IMGTOOLERR_CORRUPTIMAGE;
275
276 /* read all sector map entries */
277 max_entries = (disk_info->sector_size - 16) / 5;
278 max_entries = (std::min<std::size_t>)(max_entries, ARRAY_LENGTH(info->sector_map) - 1);
279 for (i = 0; i < max_entries; i++)
280 {
281 lsn = pick_integer_be(header, 16 + (i * 5) + 0, 3);
282 count = pick_integer_be(header, 16 + (i * 5) + 3, 2);
283 if (count <= 0)
284 break;
285
286 info->sector_map[i].lsn = lsn;
287 info->sector_map[i].count = count;
288 }
289 info->sector_map[i].lsn = 0;
290 info->sector_map[i].count = 0;
291 return IMGTOOLERR_SUCCESS;
292 }
293
294
295
os9_allocate_lsn(imgtool::image & image,uint32_t * lsn)296 static imgtoolerr_t os9_allocate_lsn(imgtool::image &image, uint32_t *lsn)
297 {
298 uint32_t i;
299 os9_diskinfo *disk_info;
300 uint8_t b, mask;
301
302 disk_info = os9_get_diskinfo(image);
303
304 for (i = 0; i < disk_info->total_sectors; i++)
305 {
306 b = disk_info->allocation_bitmap[i / 8];
307 mask = 1 << (7 - (i % 8));
308
309 if ((b & mask) == 0)
310 {
311 disk_info->allocation_bitmap[i / 8] |= mask;
312 *lsn = i;
313 return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes);
314 }
315 }
316 return IMGTOOLERR_NOSPACE;
317 }
318
319
320
os9_deallocate_lsn(imgtool::image & image,uint32_t lsn)321 static imgtoolerr_t os9_deallocate_lsn(imgtool::image &image, uint32_t lsn)
322 {
323 uint8_t mask;
324 os9_diskinfo *disk_info;
325
326 disk_info = os9_get_diskinfo(image);
327 mask = 1 << (7 - (lsn % 8));
328 disk_info->allocation_bitmap[lsn / 8] &= ~mask;
329 return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes);
330 }
331
332
333
os9_get_free_lsns(imgtool::image & image)334 static uint32_t os9_get_free_lsns(imgtool::image &image)
335 {
336 const os9_diskinfo *disk_info;
337 uint32_t i, free_lsns;
338 uint8_t b;
339
340 disk_info = os9_get_diskinfo(image);
341 free_lsns = 0;
342
343 for (i = 0; i < disk_info->total_sectors; i++)
344 {
345 b = disk_info->allocation_bitmap[i / 8];
346 b >>= (7 - (i % 8));
347 free_lsns += ~b & 1;
348 }
349
350 return free_lsns;
351 }
352
353
354
os9_corrupt_file_error(const struct os9_fileinfo * file_info)355 static imgtoolerr_t os9_corrupt_file_error(const struct os9_fileinfo *file_info)
356 {
357 imgtoolerr_t err;
358 if (file_info->directory)
359 err = IMGTOOLERR_CORRUPTDIR;
360 else
361 err = IMGTOOLERR_CORRUPTFILE;
362 return err;
363 }
364
365
366
os9_set_file_size(imgtool::image & image,struct os9_fileinfo * file_info,uint32_t new_size)367 static imgtoolerr_t os9_set_file_size(imgtool::image &image,
368 struct os9_fileinfo *file_info, uint32_t new_size)
369 {
370 imgtoolerr_t err;
371 const os9_diskinfo *disk_info;
372 uint32_t new_lsn_count, current_lsn_count;
373 uint32_t free_lsns, lsn, i;
374 int sector_map_length = -1;
375 uint8_t header[256];
376
377 /* easy way out? */
378 if (file_info->file_size == new_size)
379 return IMGTOOLERR_SUCCESS;
380
381 disk_info = os9_get_diskinfo(image);
382
383 free_lsns = os9_get_free_lsns(image);
384 current_lsn_count = (file_info->file_size + disk_info->sector_size - 1) / disk_info->sector_size;
385 new_lsn_count = (new_size + disk_info->sector_size - 1) / disk_info->sector_size;
386
387 /* check to see if the file is growing and we do not have enough space */
388 if ((new_lsn_count > current_lsn_count) && (new_lsn_count - current_lsn_count > free_lsns))
389 return IMGTOOLERR_NOSPACE;
390
391 if (current_lsn_count != new_lsn_count)
392 {
393 /* first find out the size of our sector map */
394 sector_map_length = 0;
395 lsn = 0;
396 while((lsn < current_lsn_count) && (sector_map_length < ARRAY_LENGTH(file_info->sector_map)))
397 {
398 if (file_info->sector_map[sector_map_length].count == 0)
399 return os9_corrupt_file_error(file_info);
400
401 lsn += file_info->sector_map[sector_map_length].count;
402 sector_map_length++;
403 }
404
405 /* keep in mind that the sector_map might not parallel our expected
406 * current LSN count; we should tolerate this abnormality */
407 current_lsn_count = lsn;
408
409 while(current_lsn_count > new_lsn_count)
410 {
411 /* shrink this file */
412 lsn = file_info->sector_map[sector_map_length - 1].lsn +
413 file_info->sector_map[sector_map_length - 1].count - 1;
414
415 err = os9_deallocate_lsn(image, lsn);
416 if (err)
417 return err;
418
419 file_info->sector_map[sector_map_length - 1].count--;
420 while(sector_map_length > 0 && (file_info->sector_map[sector_map_length - 1].count == 0))
421 sector_map_length--;
422 current_lsn_count--;
423 }
424
425 while(current_lsn_count < new_lsn_count)
426 {
427 /* grow this file */
428 err = os9_allocate_lsn(image, &lsn);
429 if (err)
430 return err;
431
432 if ((sector_map_length > 0) && ((file_info->sector_map[sector_map_length - 1].lsn +
433 file_info->sector_map[sector_map_length - 1].count) == lsn))
434 {
435 file_info->sector_map[sector_map_length - 1].count++;
436 }
437 else if (sector_map_length >= ARRAY_LENGTH(file_info->sector_map))
438 {
439 return IMGTOOLERR_NOSPACE;
440 }
441 else
442 {
443 file_info->sector_map[sector_map_length].lsn = lsn;
444 file_info->sector_map[sector_map_length].count = 1;
445 sector_map_length++;
446 file_info->sector_map[sector_map_length].lsn = 0;
447 file_info->sector_map[sector_map_length].count = 0;
448 }
449 current_lsn_count++;
450 }
451 }
452
453 /* now lets write back the sector */
454 err = os9_read_lsn(image, file_info->lsn, 0, header, sizeof(header));
455 if (err)
456 return err;
457
458 file_info->file_size = new_size;
459 place_integer_be(header, 9, 4, file_info->file_size);
460
461 /* do we have to write the sector map? */
462 if (sector_map_length >= 0)
463 {
464 for (i = 0; i < (std::min<std::size_t>)(sector_map_length + 1, ARRAY_LENGTH(file_info->sector_map)); i++)
465 {
466 place_integer_be(header, 16 + (i * 5) + 0, 3, file_info->sector_map[i].lsn);
467 place_integer_be(header, 16 + (i * 5) + 3, 2, file_info->sector_map[i].count);
468 }
469 }
470
471 err = os9_write_lsn(image, file_info->lsn, 0, header, disk_info->sector_size);
472 if (err)
473 return err;
474
475 return IMGTOOLERR_SUCCESS;
476 }
477
478
479
480 /*-------------------------------------------------
481 os9_lookup_path - walks an OS-9 directory
482 structure to find a file, or optionally, create
483 a file
484 -------------------------------------------------*/
485
os9_lookup_path(imgtool::image & img,const char * path,creation_policy_t create,struct os9_fileinfo * file_info,uint32_t * parent_lsn,uint32_t * dirent_lsn,uint32_t * dirent_index)486 static imgtoolerr_t os9_lookup_path(imgtool::image &img, const char *path,
487 creation_policy_t create, struct os9_fileinfo *file_info,
488 uint32_t *parent_lsn, uint32_t *dirent_lsn, uint32_t *dirent_index)
489 {
490 imgtoolerr_t err = IMGTOOLERR_SUCCESS;
491 struct os9_fileinfo dir_info;
492 uint32_t index, current_lsn, dir_size;
493 uint32_t entry_index = 0;
494 uint32_t free_entry_index = 0xffffffff;
495 uint32_t entry_lsn = 0;
496 uint32_t allocated_lsn = 0;
497 uint8_t entry[32];
498 uint8_t block[64];
499 char *filename;
500 const os9_diskinfo *disk_info;
501
502 disk_info = os9_get_diskinfo(img);
503 current_lsn = disk_info->root_dir_lsn;
504
505 if (parent_lsn)
506 *parent_lsn = 0;
507
508 /* we have to transverse each path element */
509 while(*path)
510 {
511 if (parent_lsn)
512 *parent_lsn = current_lsn;
513
514 /* decode the directory header of this directory */
515 err = os9_decode_file_header(img, current_lsn, &dir_info);
516 if (err)
517 goto done;
518 dir_size = dir_info.file_size;
519
520 /* sanity check directory */
521 if (!dir_info.directory)
522 {
523 err = (current_lsn == disk_info->root_dir_lsn) ? IMGTOOLERR_CORRUPTIMAGE : IMGTOOLERR_INVALIDPATH;
524 goto done;
525 }
526
527 /* walk the directory */
528 for (index = 0; index < dir_size; index += 32)
529 {
530 entry_index = index;
531 entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index);
532
533 err = os9_read_lsn(img, entry_lsn, entry_index, entry, sizeof(entry));
534 if (err)
535 goto done;
536
537 /* remember first free entry found */
538 if( free_entry_index == 0xffffffff )
539 {
540 if( entry[0] == 0 )
541 free_entry_index = index;
542 }
543
544 if (os9_interpret_dirent(entry, &filename, ¤t_lsn, NULL))
545 {
546 if (!strcmp(path, filename))
547 break;
548 }
549
550 }
551
552 /* at the end of the file? */
553 if (index >= dir_size)
554 {
555 /* if we're not creating, or we are creating but we have not fully
556 * transversed the directory, error out */
557 if (!create || path[strlen(path) + 1])
558 {
559 err = IMGTOOLERR_PATHNOTFOUND;
560 goto done;
561 }
562
563 /* allocate a new LSN */
564 err = os9_allocate_lsn(img, &allocated_lsn);
565 if (err)
566 goto done;
567
568 /* write the file */
569 memset(block, 0, sizeof(block));
570 place_integer_be(block, 0, 1, 0x1B | ((create == CREATE_DIR) ? 0x80 : 0x00));
571 err = os9_write_lsn(img, allocated_lsn, 0, block, sizeof(block));
572 if (err)
573 goto done;
574
575 if( free_entry_index == 0xffffffff )
576 {
577 /* expand the directory to hold the new entry */
578 err = os9_set_file_size(img, &dir_info, dir_size + 32);
579 if (err)
580 goto done;
581 }
582 else
583 /* use first unused entry in directory */
584 dir_size = free_entry_index;
585
586 /* now prepare the directory entry */
587 memset(entry, 0, sizeof(entry));
588 place_string(entry, 0, 28, path);
589 place_integer_be(entry, 29, 3, allocated_lsn);
590
591 /* now write the directory entry */
592 entry_index = dir_size;
593 entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index);
594 err = os9_write_lsn(img, entry_lsn, entry_index, entry, 32);
595 if (err)
596 goto done;
597
598 /* directory entry append complete; no need to hold this lsn */
599 current_lsn = allocated_lsn;
600 allocated_lsn = 0;
601 }
602 path += strlen(path) + 1;
603 }
604
605 if (file_info)
606 {
607 err = os9_decode_file_header(img, current_lsn, file_info);
608 if (err)
609 goto done;
610 }
611
612 if (dirent_lsn)
613 *dirent_lsn = entry_lsn;
614 if (dirent_index)
615 *dirent_index = entry_index;
616
617 done:
618 if (allocated_lsn != 0)
619 os9_deallocate_lsn(img, allocated_lsn);
620 return err;
621 }
622
623
624
os9_diskimage_open(imgtool::image & image,imgtool::stream::ptr && dummy)625 static imgtoolerr_t os9_diskimage_open(imgtool::image &image, imgtool::stream::ptr &&dummy)
626 {
627 imgtoolerr_t err;
628 floperr_t ferr;
629 os9_diskinfo *info;
630 uint32_t track_size_in_sectors, i; //, attributes;
631 uint8_t header[256];
632 uint32_t allocation_bitmap_lsns;
633 uint8_t b, mask;
634
635 info = os9_get_diskinfo(image);
636
637 ferr = floppy_read_sector(imgtool_floppy(image), 0, 0, 1, 0, header, sizeof(header));
638 if (ferr)
639 return imgtool_floppy_error(ferr);
640
641 info->total_sectors = pick_integer_be(header, 0, 3);
642 track_size_in_sectors = pick_integer_be(header, 3, 1);
643 info->allocation_bitmap_bytes = pick_integer_be(header, 4, 2);
644 info->cluster_size = pick_integer_be(header, 6, 2);
645 info->root_dir_lsn = pick_integer_be(header, 8, 3);
646 info->owner_id = pick_integer_be(header, 11, 2);
647 // attributes =
648 pick_integer_be(header, 13, 1);
649 info->disk_id = pick_integer_be(header, 14, 2);
650 info->format_flags = pick_integer_be(header, 16, 1);
651 info->sectors_per_track = pick_integer_be(header, 17, 2);
652 info->bootstrap_lsn = pick_integer_be(header, 21, 3);
653 info->bootstrap_size = pick_integer_be(header, 24, 2);
654 info->sector_size = pick_integer_be(header, 104, 2);
655
656 info->sides = (info->format_flags & 0x01) ? 2 : 1;
657 info->double_density = (info->format_flags & 0x02) ? 1 : 0;
658 info->double_track = (info->format_flags & 0x04) ? 1 : 0;
659 info->quad_track_density = (info->format_flags & 0x08) ? 1 : 0;
660 info->octal_track_density = (info->format_flags & 0x10) ? 1 : 0;
661
662 pick_string(header, 31, 32, info->name);
663
664 if (info->sector_size == 0)
665 info->sector_size = 256;
666
667 /* does the root directory and allocation bitmap collide? */
668 allocation_bitmap_lsns = (info->allocation_bitmap_bytes + info->sector_size - 1) / info->sector_size;
669 if (1 + allocation_bitmap_lsns > info->root_dir_lsn)
670 return IMGTOOLERR_CORRUPTIMAGE;
671
672 /* is the allocation bitmap too big? */
673 info->allocation_bitmap = (uint8_t*)image.malloc(info->allocation_bitmap_bytes);
674 if (!info->allocation_bitmap)
675 return IMGTOOLERR_OUTOFMEMORY;
676 memset(info->allocation_bitmap, 0, info->allocation_bitmap_bytes);
677
678 /* sectors per track and track size don't jive? */
679 if (info->sectors_per_track != track_size_in_sectors)
680 return IMGTOOLERR_CORRUPTIMAGE;
681
682 /* zero sectors per track? */
683 if (info->sectors_per_track == 0)
684 return IMGTOOLERR_CORRUPTIMAGE;
685
686 /* do we have an odd number of sectors? */
687 if (info->total_sectors % info->sectors_per_track)
688 return IMGTOOLERR_CORRUPTIMAGE;
689
690 /* read the allocation bitmap */
691 for (i = 0; i < allocation_bitmap_lsns; i++)
692 {
693 err = os9_read_lsn(image, 1 + i, 0, &info->allocation_bitmap[i * info->sector_size],
694 std::min(info->allocation_bitmap_bytes - (i * info->sector_size), info->sector_size));
695 if (err)
696 return err;
697 }
698
699 /* check to make sure that the allocation bitmap and root sector are reserved */
700 for (i = 0; i <= allocation_bitmap_lsns; i++)
701 {
702 b = info->allocation_bitmap[i / 8];
703 mask = 1 << (7 - (i % 8));
704 if ((b & mask) == 0)
705 return IMGTOOLERR_CORRUPTIMAGE;
706 }
707
708 return IMGTOOLERR_SUCCESS;
709 }
710
711
712
os9_diskimage_create(imgtool::image & img,imgtool::stream::ptr && stream,util::option_resolution * opts)713 static imgtoolerr_t os9_diskimage_create(imgtool::image &img, imgtool::stream::ptr &&stream, util::option_resolution *opts)
714 {
715 imgtoolerr_t err;
716 std::vector<uint8_t> header;
717 uint32_t heads, tracks, sectors, sector_bytes, first_sector_id;
718 uint32_t cluster_size, owner_id;
719 uint32_t allocation_bitmap_bits, allocation_bitmap_lsns;
720 uint32_t attributes, format_flags, disk_id;
721 uint32_t i;
722 int32_t total_allocated_sectors;
723 const char *title;
724 time_t t;
725 struct tm *ltime;
726
727 time(&t);
728 ltime = localtime(&t);
729
730 heads = opts->lookup_int('H');
731 tracks = opts->lookup_int('T');
732 sectors = opts->lookup_int('S');
733 sector_bytes = opts->lookup_int('L');
734 first_sector_id = opts->lookup_int('F');
735 title = "";
736
737 header.resize(sector_bytes);
738
739 if (sector_bytes > 256)
740 sector_bytes = 256;
741 cluster_size = 1;
742 owner_id = 1;
743 disk_id = 1;
744 attributes = 0;
745 allocation_bitmap_bits = heads * tracks * sectors / cluster_size;
746 allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes;
747 format_flags = ((heads > 1) ? 0x01 : 0x00) | ((tracks > 40) ? 0x02 : 0x00);
748
749 memset(&header[0], 0, sector_bytes);
750 place_integer_be(&header[0], 0, 3, heads * tracks * sectors);
751 place_integer_be(&header[0], 3, 1, sectors);
752 place_integer_be(&header[0], 4, 2, (allocation_bitmap_bits + 7) / 8);
753 place_integer_be(&header[0], 6, 2, cluster_size);
754 place_integer_be(&header[0], 8, 3, 1 + allocation_bitmap_lsns);
755 place_integer_be(&header[0], 11, 2, owner_id);
756 place_integer_be(&header[0], 13, 1, attributes);
757 place_integer_be(&header[0], 14, 2, disk_id);
758 place_integer_be(&header[0], 16, 1, format_flags);
759 place_integer_be(&header[0], 17, 2, sectors);
760 place_string(&header[0], 31, 32, title);
761 place_integer_be(&header[0], 103, 2, sector_bytes / 256);
762
763 /* path descriptor options */
764 place_integer_be(&header[0], 0x3f+0x00, 1, 1); /* device class */
765 place_integer_be(&header[0], 0x3f+0x01, 1, 1); /* drive number */
766 place_integer_be(&header[0], 0x3f+0x03, 1, 0x20); /* device type */
767 place_integer_be(&header[0], 0x3f+0x04, 1, 1); /* density capability */
768 place_integer_be(&header[0], 0x3f+0x05, 2, tracks); /* number of tracks */
769 place_integer_be(&header[0], 0x3f+0x07, 1, heads); /* number of sides */
770 place_integer_be(&header[0], 0x3f+0x09, 2, sectors); /* sectors per track */
771 place_integer_be(&header[0], 0x3f+0x0b, 2, sectors); /* sectors on track zero */
772 place_integer_be(&header[0], 0x3f+0x0d, 1, 3); /* sector interleave factor */
773 place_integer_be(&header[0], 0x3f+0x0e, 1, 8); /* default sectors per allocation */
774
775 err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id, 0, &header[0], sector_bytes, 0); /* TODO: pass ddam argument from imgtool */
776 if (err)
777 goto done;
778
779 total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 7;
780
781 for (i = 0; i < allocation_bitmap_lsns; i++)
782 {
783 memset(&header[0], 0x00, sector_bytes);
784
785 if (total_allocated_sectors > (8 * 256))
786 {
787 memset(&header[0], 0xff, sector_bytes);
788 total_allocated_sectors -= (8 * 256);
789 }
790 else if (total_allocated_sectors > 1 )
791 {
792 int offset;
793 uint8_t mask;
794
795 while( total_allocated_sectors >= 0 )
796 {
797 offset = total_allocated_sectors / 8;
798 mask = 1 << (7 - ( total_allocated_sectors % 8 ) );
799
800 header[offset] |= mask;
801 total_allocated_sectors--;
802 }
803 }
804
805 err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + i, 0, &header[0], sector_bytes, 0); /* TODO: pass ddam argument from imgtool */
806 if (err)
807 goto done;
808 }
809
810 memset(&header[0], 0, sector_bytes);
811 header[0x00] = 0xBF;
812 header[0x01] = 0x00;
813 header[0x02] = 0x00;
814 header[0x03] = (uint8_t) ltime->tm_year;
815 header[0x04] = (uint8_t) ltime->tm_mon + 1;
816 header[0x05] = (uint8_t) ltime->tm_mday;
817 header[0x06] = (uint8_t) ltime->tm_hour;
818 header[0x07] = (uint8_t) ltime->tm_min;
819 header[0x08] = 0x02;
820 header[0x09] = 0x00;
821 header[0x0A] = 0x00;
822 header[0x0B] = 0x00;
823 header[0x0C] = 0x40;
824 header[0x0D] = (uint8_t) (ltime->tm_year % 100);
825 header[0x0E] = (uint8_t) ltime->tm_mon;
826 header[0x0F] = (uint8_t) ltime->tm_mday;
827 place_integer_be(&header[0], 0x10, 3, 1 + allocation_bitmap_lsns + 1);
828 place_integer_be(&header[0], 0x13, 2, 8);
829
830 err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 1 + allocation_bitmap_lsns, 0, &header[0], sector_bytes, 0); /* TODO: pass ddam argument from imgtool */
831 if (err)
832 goto done;
833
834 memset(&header[0], 0, sector_bytes);
835 header[0x00] = 0x2E;
836 header[0x01] = 0xAE;
837 header[0x1F] = 1 + allocation_bitmap_lsns;
838 header[0x20] = 0xAE;
839 header[0x3F] = 1 + allocation_bitmap_lsns;
840 err = (imgtoolerr_t)floppy_write_sector(imgtool_floppy(img), 0, 0, first_sector_id + 2 + allocation_bitmap_lsns, 0, &header[0], sector_bytes, 0); /* TOOD: pass ddam argument from imgtool */
841 if (err)
842 goto done;
843
844 done:
845 return err;
846 }
847
848
849
os9_diskimage_beginenum(imgtool::directory & enumeration,const char * path)850 static imgtoolerr_t os9_diskimage_beginenum(imgtool::directory &enumeration, const char *path)
851 {
852 imgtoolerr_t err = IMGTOOLERR_SUCCESS;
853 struct os9_direnum *os9enum;
854 imgtool::image &image(enumeration.image());
855
856 os9enum = os9_get_dirinfo(enumeration);
857
858 err = os9_lookup_path(image, path, CREATE_NONE, &os9enum->dir_info, NULL, NULL, NULL);
859 if (err)
860 goto done;
861
862 /* this had better be a directory */
863 if (!os9enum->dir_info.directory)
864 {
865 err = IMGTOOLERR_CORRUPTIMAGE;
866 goto done;
867 }
868
869 done:
870 return err;
871 }
872
873
874
os9_diskimage_nextenum(imgtool::directory & enumeration,imgtool_dirent & ent)875 static imgtoolerr_t os9_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent)
876 {
877 struct os9_direnum *os9enum;
878 uint32_t lsn, index;
879 imgtoolerr_t err;
880 uint8_t dir_entry[32];
881 char filename[29];
882 struct os9_fileinfo file_info;
883 imgtool::image &image(enumeration.image());
884
885 os9enum = os9_get_dirinfo(enumeration);
886
887 do
888 {
889 /* check for EOF */
890 if (os9enum->index >= os9enum->dir_info.file_size)
891 {
892 ent.eof = 1;
893 return IMGTOOLERR_SUCCESS;
894 }
895
896 /* locate the LSN and offset for this directory entry */
897 index = os9enum->index;
898 lsn = os9_lookup_lsn(image, &os9enum->dir_info, &index);
899
900 /* read the directory entry out of the lSN */
901 err = os9_read_lsn(image, lsn, index, dir_entry, sizeof(dir_entry));
902 if (err)
903 return err;
904
905 if (dir_entry[0])
906 {
907 /* read the file or directory name */
908 pick_string(dir_entry, 0, 28, filename);
909
910 /* we have certain expectations of the directory contents; the
911 * first directory entry should be "..", the second ".", and
912 * subsequent entries should never be "." or ".." */
913 switch(os9enum->index)
914 {
915 case 0:
916 if (strcmp(filename, ".."))
917 imgtool_warn("First entry in directory should be \"..\" and not \"%s\"", filename);
918 break;
919
920 case 32:
921 if (strcmp(filename, "."))
922 imgtool_warn("Second entry in directory should be \".\" and not \"%s\"", filename);
923 break;
924
925 default:
926 if (!strcmp(filename, ".") || !strcmp(filename, ".."))
927 imgtool_warn("Directory entry %d should not be \"%s\"", index / 32, filename);
928 break;
929 }
930
931 /* if the filename is ".", the file should point to the current directory */
932 if (!strcmp(filename, ".") && (pick_integer_be(dir_entry, 29, 3) != os9enum->dir_info.lsn))
933 {
934 imgtool_warn("Directory \".\" does not point back to same directory");
935 }
936 }
937 else
938 {
939 /* no more directory entries */
940 filename[0] = '\0';
941 }
942
943 /* move on to the next directory entry */
944 os9enum->index += 32;
945 }
946 while(!filename[0] || !strcmp(filename, ".") || !strcmp(filename, ".."));
947
948 /* read file attributes */
949 lsn = pick_integer_be(dir_entry, 29, 3);
950 err = os9_decode_file_header(enumeration.image(), lsn, &file_info);
951 if (err)
952 return err;
953
954 /* fill out imgtool_dirent structure */
955 snprintf(ent.filename, ARRAY_LENGTH(ent.filename), "%s", filename);
956 snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "%c%c%c%c%c%c%c%c",
957 file_info.directory ? 'd' : '-',
958 file_info.non_sharable ? 's' : '-',
959 file_info.public_execute ? 'x' : '-',
960 file_info.public_write ? 'w' : '-',
961 file_info.public_read ? 'r' : '-',
962 file_info.user_execute ? 'x' : '-',
963 file_info.user_write ? 'w' : '-',
964 file_info.user_read ? 'r' : '-');
965
966 ent.directory = file_info.directory;
967 ent.corrupt = (dir_entry[28] != 0);
968 ent.filesize = file_info.file_size;
969 return IMGTOOLERR_SUCCESS;
970 }
971
972
973
os9_diskimage_freespace(imgtool::partition & partition,uint64_t * size)974 static imgtoolerr_t os9_diskimage_freespace(imgtool::partition &partition, uint64_t *size)
975 {
976 imgtool::image &image(partition.image());
977 const os9_diskinfo *disk_info;
978 uint32_t free_lsns;
979
980 disk_info = os9_get_diskinfo(image);
981 free_lsns = os9_get_free_lsns(image);
982
983 *size = free_lsns * disk_info->sector_size;
984 return IMGTOOLERR_SUCCESS;
985 }
986
987
988
os9_diskimage_readfile(imgtool::partition & partition,const char * filename,const char * fork,imgtool::stream & destf)989 static imgtoolerr_t os9_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf)
990 {
991 imgtoolerr_t err;
992 imgtool::image &img(partition.image());
993 const os9_diskinfo *disk_info;
994 struct os9_fileinfo file_info;
995 uint8_t buffer[256];
996 int i, j;
997 uint32_t file_size;
998 uint32_t used_size;
999
1000 disk_info = os9_get_diskinfo(img);
1001
1002 err = os9_lookup_path(img, filename, CREATE_NONE, &file_info, NULL, NULL, NULL);
1003 if (err)
1004 return err;
1005 if (file_info.directory)
1006 return IMGTOOLERR_FILENOTFOUND;
1007 file_size = file_info.file_size;
1008
1009 for (i = 0; file_info.sector_map[i].count > 0; i++)
1010 {
1011 for (j = 0; j < file_info.sector_map[i].count; j++)
1012 {
1013 used_size = std::min(file_size, disk_info->sector_size);
1014 err = os9_read_lsn(img, file_info.sector_map[i].lsn + j, 0,
1015 buffer, used_size);
1016 if (err)
1017 return err;
1018 destf.write(buffer, used_size);
1019 file_size -= used_size;
1020 }
1021 }
1022 return IMGTOOLERR_SUCCESS;
1023 }
1024
1025
1026
os9_diskimage_writefile(imgtool::partition & partition,const char * path,const char * fork,imgtool::stream & sourcef,util::option_resolution * opts)1027 static imgtoolerr_t os9_diskimage_writefile(imgtool::partition &partition, const char *path, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts)
1028 {
1029 imgtoolerr_t err;
1030 imgtool::image &image(partition.image());
1031 struct os9_fileinfo file_info;
1032 size_t write_size;
1033 std::vector<uint8_t> buf;
1034 int i = -1;
1035 uint32_t lsn = 0;
1036 uint32_t count = 0;
1037 uint32_t sz;
1038 const os9_diskinfo *disk_info;
1039
1040 disk_info = os9_get_diskinfo(image);
1041
1042 buf.resize(disk_info->sector_size);
1043
1044 err = os9_lookup_path(image, path, CREATE_FILE, &file_info, NULL, NULL, NULL);
1045 if (err)
1046 goto done;
1047
1048 sz = (uint32_t) sourcef.size();
1049
1050 err = os9_set_file_size(image, &file_info, sz);
1051 if (err)
1052 goto done;
1053
1054 while(sz > 0)
1055 {
1056 write_size = (std::min<uint64_t>)(sz, disk_info->sector_size);
1057
1058 sourcef.read(&buf[0], write_size);
1059
1060 while(count == 0)
1061 {
1062 i++;
1063 lsn = file_info.sector_map[i].lsn;
1064 count = file_info.sector_map[i].count;
1065 }
1066
1067 err = os9_write_lsn(image, lsn, 0, &buf[0], write_size);
1068 if (err)
1069 goto done;
1070
1071 lsn++;
1072 count--;
1073 sz -= write_size;
1074 }
1075
1076 done:
1077 return err;
1078 }
1079
1080
1081
os9_diskimage_delete(imgtool::partition & partition,const char * path,unsigned int delete_directory)1082 static imgtoolerr_t os9_diskimage_delete(imgtool::partition &partition, const char *path,
1083 unsigned int delete_directory)
1084 {
1085 imgtoolerr_t err;
1086 imgtool::image &image(partition.image());
1087 struct os9_fileinfo file_info;
1088 uint32_t dirent_lsn, dirent_index;
1089 uint32_t entry_lsn, entry_index;
1090 uint32_t i, j, lsn;
1091 uint8_t b;
1092
1093 //disk_info = os9_get_diskinfo(image);
1094
1095 err = os9_lookup_path(image, path, CREATE_NONE, &file_info, NULL, &dirent_lsn, &dirent_index);
1096 if (err)
1097 return err;
1098 if (file_info.directory != delete_directory)
1099 return IMGTOOLERR_FILENOTFOUND;
1100
1101 /* make sure that if we are deleting a directory, it is empty */
1102 if (delete_directory)
1103 {
1104 for (i = 64; i < file_info.file_size; i += 32)
1105 {
1106 entry_index = i;
1107 entry_lsn = os9_lookup_lsn(image, &file_info, &entry_index);
1108
1109 err = os9_read_lsn(image, entry_lsn, entry_index, &b, 1);
1110 if (err)
1111 return err;
1112
1113 /* this had better be a deleted file, if not we can't delete */
1114 if (b != 0)
1115 return IMGTOOLERR_DIRNOTEMPTY;
1116 }
1117 }
1118
1119 /* zero out the file entry */
1120 b = '\0';
1121 err = os9_write_lsn(image, dirent_lsn, dirent_index, &b, 1);
1122 if (err)
1123 return err;
1124
1125 /* get the link count */
1126 err = os9_read_lsn(image, file_info.lsn, 8, &b, 1);
1127 if (err)
1128 return err;
1129
1130 if (b > 0)
1131 b--;
1132 if (b > 0)
1133 {
1134 /* link count is greater than zero */
1135 err = os9_write_lsn(image, file_info.lsn, 8, &b, 1);
1136 if (err)
1137 return err;
1138 }
1139 else
1140 {
1141 /* no more links; outright delete the file */
1142 err = os9_deallocate_lsn(image, file_info.lsn);
1143 if (err)
1144 return err;
1145
1146 for (i = 0; (i < ARRAY_LENGTH(file_info.sector_map)) && file_info.sector_map[i].count; i++)
1147 {
1148 lsn = file_info.sector_map[i].lsn;
1149 for (j = 0; j < file_info.sector_map[i].count; j++)
1150 {
1151 err = os9_deallocate_lsn(image, lsn + j);
1152 if (err)
1153 return err;
1154 }
1155 }
1156 }
1157
1158 return IMGTOOLERR_SUCCESS;
1159 }
1160
1161
1162
os9_diskimage_deletefile(imgtool::partition & partition,const char * path)1163 static imgtoolerr_t os9_diskimage_deletefile(imgtool::partition &partition, const char *path)
1164 {
1165 return os9_diskimage_delete(partition, path, 0);
1166 }
1167
1168
1169
os9_diskimage_createdir(imgtool::partition & partition,const char * path)1170 static imgtoolerr_t os9_diskimage_createdir(imgtool::partition &partition, const char *path)
1171 {
1172 imgtoolerr_t err;
1173 imgtool::image &image(partition.image());
1174 struct os9_fileinfo file_info;
1175 uint8_t dir_data[64];
1176 uint32_t parent_lsn;
1177
1178 err = os9_lookup_path(image, path, CREATE_DIR, &file_info, &parent_lsn, NULL, NULL);
1179 if (err)
1180 goto done;
1181
1182 err = os9_set_file_size(image, &file_info, 64);
1183 if (err)
1184 goto done;
1185
1186 /* create intial directories */
1187 memset(dir_data, 0, sizeof(dir_data));
1188 place_string(dir_data, 0, 32, "..");
1189 place_integer_be(dir_data, 29, 3, parent_lsn);
1190 place_string(dir_data, 32, 32, ".");
1191 place_integer_be(dir_data, 61, 3, file_info.lsn);
1192
1193 err = os9_write_lsn(image, file_info.sector_map[0].lsn, 0, dir_data, sizeof(dir_data));
1194 if (err)
1195 goto done;
1196
1197 done:
1198 return err;
1199 }
1200
1201
1202
os9_diskimage_deletedir(imgtool::partition & partition,const char * path)1203 static imgtoolerr_t os9_diskimage_deletedir(imgtool::partition &partition, const char *path)
1204 {
1205 return os9_diskimage_delete(partition, path, 1);
1206 }
1207
1208
1209
os9_get_info(const imgtool_class * imgclass,uint32_t state,union imgtoolinfo * info)1210 void os9_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info)
1211 {
1212 switch(state)
1213 {
1214 /* --- the following bits of info are returned as 64-bit signed integers --- */
1215 case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR: info->i = 1; break;
1216 case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break;
1217 case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(os9_diskinfo); break;
1218 case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct os9_direnum); break;
1219 case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break;
1220
1221 /* --- the following bits of info are returned as NULL-terminated strings --- */
1222 case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "os9"); break;
1223 case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "OS-9 format"); break;
1224 case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break;
1225 case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break;
1226
1227 /* --- the following bits of info are returned as pointers to data or functions --- */
1228 case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break;
1229 case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = os9_diskimage_create; break;
1230 case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = os9_diskimage_open; break;
1231 case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = os9_diskimage_beginenum; break;
1232 case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = os9_diskimage_nextenum; break;
1233 case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = os9_diskimage_freespace; break;
1234 case IMGTOOLINFO_PTR_READ_FILE: info->read_file = os9_diskimage_readfile; break;
1235 case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = os9_diskimage_writefile; break;
1236 case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = os9_diskimage_deletefile; break;
1237 case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = os9_diskimage_createdir; break;
1238 case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = os9_diskimage_deletedir; break;
1239 case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_coco; break;
1240 }
1241 }
1242