// license:BSD-3-Clause // copyright-holders:Nathan Woods /**************************************************************************** os9.cpp CoCo OS-9 disk images ****************************************************************************/ #include #include "imgtool.h" #include "formats/imageutl.h" #include "formats/coco_dsk.h" #include "iflopimg.h" enum creation_policy_t { CREATE_NONE, CREATE_FILE, CREATE_DIR }; struct os9_diskinfo { uint32_t total_sectors; uint32_t sectors_per_track; uint32_t allocation_bitmap_bytes; uint32_t cluster_size; uint32_t root_dir_lsn; uint32_t owner_id; uint32_t attributes; uint32_t disk_id; uint32_t format_flags; uint32_t bootstrap_lsn; uint32_t bootstrap_size; uint32_t sector_size; unsigned int sides : 2; unsigned int double_density : 1; unsigned int double_track : 1; unsigned int quad_track_density : 1; unsigned int octal_track_density : 1; char name[33]; uint8_t *allocation_bitmap; }; struct os9_fileinfo { unsigned int directory : 1; unsigned int non_sharable : 1; unsigned int public_execute : 1; unsigned int public_write : 1; unsigned int public_read : 1; unsigned int user_execute : 1; unsigned int user_write : 1; unsigned int user_read : 1; uint32_t lsn; uint32_t owner_id; uint32_t link_count; uint32_t file_size; struct { uint32_t lsn; uint32_t count; } sector_map[48]; }; struct os9_direnum { struct os9_fileinfo dir_info; uint32_t index; }; static void pick_string(const void *ptr, size_t offset, size_t length, char *dest) { uint8_t b; while(length--) { b = ((uint8_t *) ptr)[offset++]; *(dest++) = b & 0x7F; if (b & 0x80) length = 0; } *dest = '\0'; } static void place_string(void *ptr, size_t offset, size_t length, const char *s) { size_t i; uint8_t b; uint8_t *bptr; bptr = (uint8_t *) ptr; bptr += offset; bptr[0] = 0x80; for (i = 0; s[i] && (i < length); i++) { b = ((uint8_t) s[i]) & 0x7F; if (s[i+1] == '\0') b |= 0x80; bptr[i] = b; } } static os9_diskinfo *os9_get_diskinfo(imgtool::image &image) { return (os9_diskinfo *) imgtool_floppy_extrabytes(image); } static struct os9_direnum *os9_get_dirinfo(imgtool::directory &directory) { return (struct os9_direnum *) directory.extra_bytes(); } static imgtoolerr_t os9_locate_lsn(imgtool::image &image, uint32_t lsn, uint32_t *head, uint32_t *track, uint32_t *sector) { const os9_diskinfo *disk_info; disk_info = os9_get_diskinfo(image); if (lsn > disk_info->total_sectors) return IMGTOOLERR_CORRUPTIMAGE; *track = lsn / (disk_info->sectors_per_track * disk_info->sides); *head = (lsn / disk_info->sectors_per_track) % disk_info->sides; *sector = (lsn % disk_info->sectors_per_track) + 1; return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_read_lsn(imgtool::image &img, uint32_t lsn, int offset, void *buffer, size_t buffer_len) { imgtoolerr_t err; floperr_t ferr; uint32_t head, track, sector; err = os9_locate_lsn(img, lsn, &head, &track, §or); if (err) return err; ferr = floppy_read_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len); if (ferr) return imgtool_floppy_error(ferr); return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_write_lsn(imgtool::image &img, uint32_t lsn, int offset, const void *buffer, size_t buffer_len) { imgtoolerr_t err; floperr_t ferr; uint32_t head, track, sector; err = os9_locate_lsn(img, lsn, &head, &track, §or); if (err) return err; ferr = floppy_write_sector(imgtool_floppy(img), head, track, sector, offset, buffer, buffer_len, 0); /* TODO: pass ddam argument from imgtool */ if (ferr) return imgtool_floppy_error(ferr); return IMGTOOLERR_SUCCESS; } static uint32_t os9_lookup_lsn(imgtool::image &img, const struct os9_fileinfo *file_info, uint32_t *index) { int i; uint32_t lsn; const os9_diskinfo *disk_info; disk_info = os9_get_diskinfo(img); lsn = *index / disk_info->sector_size; i = 0; while(lsn >= file_info->sector_map[i].count) lsn -= file_info->sector_map[i++].count; lsn = file_info->sector_map[i].lsn + lsn; *index %= disk_info->sector_size; return lsn; } static int os9_interpret_dirent(void *entry, char **filename, uint32_t *lsn, int *corrupt) { int i; char *entry_b = (char *) entry; *filename = NULL; *lsn = 0; if (corrupt) *corrupt = false; if (entry_b[28] != '\0') { if (corrupt) *corrupt = true; } for (i = 0; (i < 28) && !(entry_b[i] & 0x80); i++) ; entry_b[i] &= 0x7F; entry_b[i+1] = '\0'; *lsn = pick_integer_be(entry, 29, 3); if (strcmp(entry_b, ".") && strcmp(entry_b, "..")) *filename = entry_b; return *filename != NULL; } /*------------------------------------------------- os9_decode_file_header - reads a file/directory entry from an LSN, and decodes it -------------------------------------------------*/ static imgtoolerr_t os9_decode_file_header(imgtool::image &image, int lsn, struct os9_fileinfo *info) { imgtoolerr_t err; uint32_t attributes, count; int max_entries, i; const os9_diskinfo *disk_info; uint8_t header[256]; disk_info = os9_get_diskinfo(image); err = os9_read_lsn(image, lsn, 0, header, sizeof(header)); if (err) return err; info->lsn = lsn; attributes = pick_integer_be(header, 0, 1); info->owner_id = pick_integer_be(header, 1, 2); info->link_count = pick_integer_be(header, 8, 1); info->file_size = pick_integer_be(header, 9, 4); info->directory = (attributes & 0x80) ? 1 : 0; info->non_sharable = (attributes & 0x40) ? 1 : 0; info->public_execute = (attributes & 0x20) ? 1 : 0; info->public_write = (attributes & 0x10) ? 1 : 0; info->public_read = (attributes & 0x08) ? 1 : 0; info->user_execute = (attributes & 0x04) ? 1 : 0; info->user_write = (attributes & 0x02) ? 1 : 0; info->user_read = (attributes & 0x01) ? 1 : 0; if (info->directory && (info->file_size % 32 != 0)) return IMGTOOLERR_CORRUPTIMAGE; /* read all sector map entries */ max_entries = (disk_info->sector_size - 16) / 5; max_entries = (std::min)(max_entries, ARRAY_LENGTH(info->sector_map) - 1); for (i = 0; i < max_entries; i++) { lsn = pick_integer_be(header, 16 + (i * 5) + 0, 3); count = pick_integer_be(header, 16 + (i * 5) + 3, 2); if (count <= 0) break; info->sector_map[i].lsn = lsn; info->sector_map[i].count = count; } info->sector_map[i].lsn = 0; info->sector_map[i].count = 0; return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_allocate_lsn(imgtool::image &image, uint32_t *lsn) { uint32_t i; os9_diskinfo *disk_info; uint8_t b, mask; disk_info = os9_get_diskinfo(image); for (i = 0; i < disk_info->total_sectors; i++) { b = disk_info->allocation_bitmap[i / 8]; mask = 1 << (7 - (i % 8)); if ((b & mask) == 0) { disk_info->allocation_bitmap[i / 8] |= mask; *lsn = i; return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes); } } return IMGTOOLERR_NOSPACE; } static imgtoolerr_t os9_deallocate_lsn(imgtool::image &image, uint32_t lsn) { uint8_t mask; os9_diskinfo *disk_info; disk_info = os9_get_diskinfo(image); mask = 1 << (7 - (lsn % 8)); disk_info->allocation_bitmap[lsn / 8] &= ~mask; return os9_write_lsn(image, 1, 0, disk_info->allocation_bitmap, disk_info->allocation_bitmap_bytes); } static uint32_t os9_get_free_lsns(imgtool::image &image) { const os9_diskinfo *disk_info; uint32_t i, free_lsns; uint8_t b; disk_info = os9_get_diskinfo(image); free_lsns = 0; for (i = 0; i < disk_info->total_sectors; i++) { b = disk_info->allocation_bitmap[i / 8]; b >>= (7 - (i % 8)); free_lsns += ~b & 1; } return free_lsns; } static imgtoolerr_t os9_corrupt_file_error(const struct os9_fileinfo *file_info) { imgtoolerr_t err; if (file_info->directory) err = IMGTOOLERR_CORRUPTDIR; else err = IMGTOOLERR_CORRUPTFILE; return err; } static imgtoolerr_t os9_set_file_size(imgtool::image &image, struct os9_fileinfo *file_info, uint32_t new_size) { imgtoolerr_t err; const os9_diskinfo *disk_info; uint32_t new_lsn_count, current_lsn_count; uint32_t free_lsns, lsn, i; int sector_map_length = -1; uint8_t header[256]; /* easy way out? */ if (file_info->file_size == new_size) return IMGTOOLERR_SUCCESS; disk_info = os9_get_diskinfo(image); free_lsns = os9_get_free_lsns(image); current_lsn_count = (file_info->file_size + disk_info->sector_size - 1) / disk_info->sector_size; new_lsn_count = (new_size + disk_info->sector_size - 1) / disk_info->sector_size; /* check to see if the file is growing and we do not have enough space */ if ((new_lsn_count > current_lsn_count) && (new_lsn_count - current_lsn_count > free_lsns)) return IMGTOOLERR_NOSPACE; if (current_lsn_count != new_lsn_count) { /* first find out the size of our sector map */ sector_map_length = 0; lsn = 0; while((lsn < current_lsn_count) && (sector_map_length < ARRAY_LENGTH(file_info->sector_map))) { if (file_info->sector_map[sector_map_length].count == 0) return os9_corrupt_file_error(file_info); lsn += file_info->sector_map[sector_map_length].count; sector_map_length++; } /* keep in mind that the sector_map might not parallel our expected * current LSN count; we should tolerate this abnormality */ current_lsn_count = lsn; while(current_lsn_count > new_lsn_count) { /* shrink this file */ lsn = file_info->sector_map[sector_map_length - 1].lsn + file_info->sector_map[sector_map_length - 1].count - 1; err = os9_deallocate_lsn(image, lsn); if (err) return err; file_info->sector_map[sector_map_length - 1].count--; while(sector_map_length > 0 && (file_info->sector_map[sector_map_length - 1].count == 0)) sector_map_length--; current_lsn_count--; } while(current_lsn_count < new_lsn_count) { /* grow this file */ err = os9_allocate_lsn(image, &lsn); if (err) return err; if ((sector_map_length > 0) && ((file_info->sector_map[sector_map_length - 1].lsn + file_info->sector_map[sector_map_length - 1].count) == lsn)) { file_info->sector_map[sector_map_length - 1].count++; } else if (sector_map_length >= ARRAY_LENGTH(file_info->sector_map)) { return IMGTOOLERR_NOSPACE; } else { file_info->sector_map[sector_map_length].lsn = lsn; file_info->sector_map[sector_map_length].count = 1; sector_map_length++; file_info->sector_map[sector_map_length].lsn = 0; file_info->sector_map[sector_map_length].count = 0; } current_lsn_count++; } } /* now lets write back the sector */ err = os9_read_lsn(image, file_info->lsn, 0, header, sizeof(header)); if (err) return err; file_info->file_size = new_size; place_integer_be(header, 9, 4, file_info->file_size); /* do we have to write the sector map? */ if (sector_map_length >= 0) { for (i = 0; i < (std::min)(sector_map_length + 1, ARRAY_LENGTH(file_info->sector_map)); i++) { place_integer_be(header, 16 + (i * 5) + 0, 3, file_info->sector_map[i].lsn); place_integer_be(header, 16 + (i * 5) + 3, 2, file_info->sector_map[i].count); } } err = os9_write_lsn(image, file_info->lsn, 0, header, disk_info->sector_size); if (err) return err; return IMGTOOLERR_SUCCESS; } /*------------------------------------------------- os9_lookup_path - walks an OS-9 directory structure to find a file, or optionally, create a file -------------------------------------------------*/ static imgtoolerr_t 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) { imgtoolerr_t err = IMGTOOLERR_SUCCESS; struct os9_fileinfo dir_info; uint32_t index, current_lsn, dir_size; uint32_t entry_index = 0; uint32_t free_entry_index = 0xffffffff; uint32_t entry_lsn = 0; uint32_t allocated_lsn = 0; uint8_t entry[32]; uint8_t block[64]; char *filename; const os9_diskinfo *disk_info; disk_info = os9_get_diskinfo(img); current_lsn = disk_info->root_dir_lsn; if (parent_lsn) *parent_lsn = 0; /* we have to transverse each path element */ while(*path) { if (parent_lsn) *parent_lsn = current_lsn; /* decode the directory header of this directory */ err = os9_decode_file_header(img, current_lsn, &dir_info); if (err) goto done; dir_size = dir_info.file_size; /* sanity check directory */ if (!dir_info.directory) { err = (current_lsn == disk_info->root_dir_lsn) ? IMGTOOLERR_CORRUPTIMAGE : IMGTOOLERR_INVALIDPATH; goto done; } /* walk the directory */ for (index = 0; index < dir_size; index += 32) { entry_index = index; entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index); err = os9_read_lsn(img, entry_lsn, entry_index, entry, sizeof(entry)); if (err) goto done; /* remember first free entry found */ if( free_entry_index == 0xffffffff ) { if( entry[0] == 0 ) free_entry_index = index; } if (os9_interpret_dirent(entry, &filename, ¤t_lsn, NULL)) { if (!strcmp(path, filename)) break; } } /* at the end of the file? */ if (index >= dir_size) { /* if we're not creating, or we are creating but we have not fully * transversed the directory, error out */ if (!create || path[strlen(path) + 1]) { err = IMGTOOLERR_PATHNOTFOUND; goto done; } /* allocate a new LSN */ err = os9_allocate_lsn(img, &allocated_lsn); if (err) goto done; /* write the file */ memset(block, 0, sizeof(block)); place_integer_be(block, 0, 1, 0x1B | ((create == CREATE_DIR) ? 0x80 : 0x00)); err = os9_write_lsn(img, allocated_lsn, 0, block, sizeof(block)); if (err) goto done; if( free_entry_index == 0xffffffff ) { /* expand the directory to hold the new entry */ err = os9_set_file_size(img, &dir_info, dir_size + 32); if (err) goto done; } else /* use first unused entry in directory */ dir_size = free_entry_index; /* now prepare the directory entry */ memset(entry, 0, sizeof(entry)); place_string(entry, 0, 28, path); place_integer_be(entry, 29, 3, allocated_lsn); /* now write the directory entry */ entry_index = dir_size; entry_lsn = os9_lookup_lsn(img, &dir_info, &entry_index); err = os9_write_lsn(img, entry_lsn, entry_index, entry, 32); if (err) goto done; /* directory entry append complete; no need to hold this lsn */ current_lsn = allocated_lsn; allocated_lsn = 0; } path += strlen(path) + 1; } if (file_info) { err = os9_decode_file_header(img, current_lsn, file_info); if (err) goto done; } if (dirent_lsn) *dirent_lsn = entry_lsn; if (dirent_index) *dirent_index = entry_index; done: if (allocated_lsn != 0) os9_deallocate_lsn(img, allocated_lsn); return err; } static imgtoolerr_t os9_diskimage_open(imgtool::image &image, imgtool::stream::ptr &&dummy) { imgtoolerr_t err; floperr_t ferr; os9_diskinfo *info; uint32_t track_size_in_sectors, i; //, attributes; uint8_t header[256]; uint32_t allocation_bitmap_lsns; uint8_t b, mask; info = os9_get_diskinfo(image); ferr = floppy_read_sector(imgtool_floppy(image), 0, 0, 1, 0, header, sizeof(header)); if (ferr) return imgtool_floppy_error(ferr); info->total_sectors = pick_integer_be(header, 0, 3); track_size_in_sectors = pick_integer_be(header, 3, 1); info->allocation_bitmap_bytes = pick_integer_be(header, 4, 2); info->cluster_size = pick_integer_be(header, 6, 2); info->root_dir_lsn = pick_integer_be(header, 8, 3); info->owner_id = pick_integer_be(header, 11, 2); // attributes = pick_integer_be(header, 13, 1); info->disk_id = pick_integer_be(header, 14, 2); info->format_flags = pick_integer_be(header, 16, 1); info->sectors_per_track = pick_integer_be(header, 17, 2); info->bootstrap_lsn = pick_integer_be(header, 21, 3); info->bootstrap_size = pick_integer_be(header, 24, 2); info->sector_size = pick_integer_be(header, 104, 2); info->sides = (info->format_flags & 0x01) ? 2 : 1; info->double_density = (info->format_flags & 0x02) ? 1 : 0; info->double_track = (info->format_flags & 0x04) ? 1 : 0; info->quad_track_density = (info->format_flags & 0x08) ? 1 : 0; info->octal_track_density = (info->format_flags & 0x10) ? 1 : 0; pick_string(header, 31, 32, info->name); if (info->sector_size == 0) info->sector_size = 256; /* does the root directory and allocation bitmap collide? */ allocation_bitmap_lsns = (info->allocation_bitmap_bytes + info->sector_size - 1) / info->sector_size; if (1 + allocation_bitmap_lsns > info->root_dir_lsn) return IMGTOOLERR_CORRUPTIMAGE; /* is the allocation bitmap too big? */ info->allocation_bitmap = (uint8_t*)image.malloc(info->allocation_bitmap_bytes); if (!info->allocation_bitmap) return IMGTOOLERR_OUTOFMEMORY; memset(info->allocation_bitmap, 0, info->allocation_bitmap_bytes); /* sectors per track and track size don't jive? */ if (info->sectors_per_track != track_size_in_sectors) return IMGTOOLERR_CORRUPTIMAGE; /* zero sectors per track? */ if (info->sectors_per_track == 0) return IMGTOOLERR_CORRUPTIMAGE; /* do we have an odd number of sectors? */ if (info->total_sectors % info->sectors_per_track) return IMGTOOLERR_CORRUPTIMAGE; /* read the allocation bitmap */ for (i = 0; i < allocation_bitmap_lsns; i++) { err = os9_read_lsn(image, 1 + i, 0, &info->allocation_bitmap[i * info->sector_size], std::min(info->allocation_bitmap_bytes - (i * info->sector_size), info->sector_size)); if (err) return err; } /* check to make sure that the allocation bitmap and root sector are reserved */ for (i = 0; i <= allocation_bitmap_lsns; i++) { b = info->allocation_bitmap[i / 8]; mask = 1 << (7 - (i % 8)); if ((b & mask) == 0) return IMGTOOLERR_CORRUPTIMAGE; } return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_diskimage_create(imgtool::image &img, imgtool::stream::ptr &&stream, util::option_resolution *opts) { imgtoolerr_t err; std::vector header; uint32_t heads, tracks, sectors, sector_bytes, first_sector_id; uint32_t cluster_size, owner_id; uint32_t allocation_bitmap_bits, allocation_bitmap_lsns; uint32_t attributes, format_flags, disk_id; uint32_t i; int32_t total_allocated_sectors; const char *title; time_t t; struct tm *ltime; time(&t); ltime = localtime(&t); heads = opts->lookup_int('H'); tracks = opts->lookup_int('T'); sectors = opts->lookup_int('S'); sector_bytes = opts->lookup_int('L'); first_sector_id = opts->lookup_int('F'); title = ""; header.resize(sector_bytes); if (sector_bytes > 256) sector_bytes = 256; cluster_size = 1; owner_id = 1; disk_id = 1; attributes = 0; allocation_bitmap_bits = heads * tracks * sectors / cluster_size; allocation_bitmap_lsns = (allocation_bitmap_bits / 8 + sector_bytes - 1) / sector_bytes; format_flags = ((heads > 1) ? 0x01 : 0x00) | ((tracks > 40) ? 0x02 : 0x00); memset(&header[0], 0, sector_bytes); place_integer_be(&header[0], 0, 3, heads * tracks * sectors); place_integer_be(&header[0], 3, 1, sectors); place_integer_be(&header[0], 4, 2, (allocation_bitmap_bits + 7) / 8); place_integer_be(&header[0], 6, 2, cluster_size); place_integer_be(&header[0], 8, 3, 1 + allocation_bitmap_lsns); place_integer_be(&header[0], 11, 2, owner_id); place_integer_be(&header[0], 13, 1, attributes); place_integer_be(&header[0], 14, 2, disk_id); place_integer_be(&header[0], 16, 1, format_flags); place_integer_be(&header[0], 17, 2, sectors); place_string(&header[0], 31, 32, title); place_integer_be(&header[0], 103, 2, sector_bytes / 256); /* path descriptor options */ place_integer_be(&header[0], 0x3f+0x00, 1, 1); /* device class */ place_integer_be(&header[0], 0x3f+0x01, 1, 1); /* drive number */ place_integer_be(&header[0], 0x3f+0x03, 1, 0x20); /* device type */ place_integer_be(&header[0], 0x3f+0x04, 1, 1); /* density capability */ place_integer_be(&header[0], 0x3f+0x05, 2, tracks); /* number of tracks */ place_integer_be(&header[0], 0x3f+0x07, 1, heads); /* number of sides */ place_integer_be(&header[0], 0x3f+0x09, 2, sectors); /* sectors per track */ place_integer_be(&header[0], 0x3f+0x0b, 2, sectors); /* sectors on track zero */ place_integer_be(&header[0], 0x3f+0x0d, 1, 3); /* sector interleave factor */ place_integer_be(&header[0], 0x3f+0x0e, 1, 8); /* default sectors per allocation */ 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 */ if (err) goto done; total_allocated_sectors = 1 + allocation_bitmap_lsns + 1 + 7; for (i = 0; i < allocation_bitmap_lsns; i++) { memset(&header[0], 0x00, sector_bytes); if (total_allocated_sectors > (8 * 256)) { memset(&header[0], 0xff, sector_bytes); total_allocated_sectors -= (8 * 256); } else if (total_allocated_sectors > 1 ) { int offset; uint8_t mask; while( total_allocated_sectors >= 0 ) { offset = total_allocated_sectors / 8; mask = 1 << (7 - ( total_allocated_sectors % 8 ) ); header[offset] |= mask; total_allocated_sectors--; } } 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 */ if (err) goto done; } memset(&header[0], 0, sector_bytes); header[0x00] = 0xBF; header[0x01] = 0x00; header[0x02] = 0x00; header[0x03] = (uint8_t) ltime->tm_year; header[0x04] = (uint8_t) ltime->tm_mon + 1; header[0x05] = (uint8_t) ltime->tm_mday; header[0x06] = (uint8_t) ltime->tm_hour; header[0x07] = (uint8_t) ltime->tm_min; header[0x08] = 0x02; header[0x09] = 0x00; header[0x0A] = 0x00; header[0x0B] = 0x00; header[0x0C] = 0x40; header[0x0D] = (uint8_t) (ltime->tm_year % 100); header[0x0E] = (uint8_t) ltime->tm_mon; header[0x0F] = (uint8_t) ltime->tm_mday; place_integer_be(&header[0], 0x10, 3, 1 + allocation_bitmap_lsns + 1); place_integer_be(&header[0], 0x13, 2, 8); 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 */ if (err) goto done; memset(&header[0], 0, sector_bytes); header[0x00] = 0x2E; header[0x01] = 0xAE; header[0x1F] = 1 + allocation_bitmap_lsns; header[0x20] = 0xAE; header[0x3F] = 1 + allocation_bitmap_lsns; 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 */ if (err) goto done; done: return err; } static imgtoolerr_t os9_diskimage_beginenum(imgtool::directory &enumeration, const char *path) { imgtoolerr_t err = IMGTOOLERR_SUCCESS; struct os9_direnum *os9enum; imgtool::image &image(enumeration.image()); os9enum = os9_get_dirinfo(enumeration); err = os9_lookup_path(image, path, CREATE_NONE, &os9enum->dir_info, NULL, NULL, NULL); if (err) goto done; /* this had better be a directory */ if (!os9enum->dir_info.directory) { err = IMGTOOLERR_CORRUPTIMAGE; goto done; } done: return err; } static imgtoolerr_t os9_diskimage_nextenum(imgtool::directory &enumeration, imgtool_dirent &ent) { struct os9_direnum *os9enum; uint32_t lsn, index; imgtoolerr_t err; uint8_t dir_entry[32]; char filename[29]; struct os9_fileinfo file_info; imgtool::image &image(enumeration.image()); os9enum = os9_get_dirinfo(enumeration); do { /* check for EOF */ if (os9enum->index >= os9enum->dir_info.file_size) { ent.eof = 1; return IMGTOOLERR_SUCCESS; } /* locate the LSN and offset for this directory entry */ index = os9enum->index; lsn = os9_lookup_lsn(image, &os9enum->dir_info, &index); /* read the directory entry out of the lSN */ err = os9_read_lsn(image, lsn, index, dir_entry, sizeof(dir_entry)); if (err) return err; if (dir_entry[0]) { /* read the file or directory name */ pick_string(dir_entry, 0, 28, filename); /* we have certain expectations of the directory contents; the * first directory entry should be "..", the second ".", and * subsequent entries should never be "." or ".." */ switch(os9enum->index) { case 0: if (strcmp(filename, "..")) imgtool_warn("First entry in directory should be \"..\" and not \"%s\"", filename); break; case 32: if (strcmp(filename, ".")) imgtool_warn("Second entry in directory should be \".\" and not \"%s\"", filename); break; default: if (!strcmp(filename, ".") || !strcmp(filename, "..")) imgtool_warn("Directory entry %d should not be \"%s\"", index / 32, filename); break; } /* if the filename is ".", the file should point to the current directory */ if (!strcmp(filename, ".") && (pick_integer_be(dir_entry, 29, 3) != os9enum->dir_info.lsn)) { imgtool_warn("Directory \".\" does not point back to same directory"); } } else { /* no more directory entries */ filename[0] = '\0'; } /* move on to the next directory entry */ os9enum->index += 32; } while(!filename[0] || !strcmp(filename, ".") || !strcmp(filename, "..")); /* read file attributes */ lsn = pick_integer_be(dir_entry, 29, 3); err = os9_decode_file_header(enumeration.image(), lsn, &file_info); if (err) return err; /* fill out imgtool_dirent structure */ snprintf(ent.filename, ARRAY_LENGTH(ent.filename), "%s", filename); snprintf(ent.attr, ARRAY_LENGTH(ent.attr), "%c%c%c%c%c%c%c%c", file_info.directory ? 'd' : '-', file_info.non_sharable ? 's' : '-', file_info.public_execute ? 'x' : '-', file_info.public_write ? 'w' : '-', file_info.public_read ? 'r' : '-', file_info.user_execute ? 'x' : '-', file_info.user_write ? 'w' : '-', file_info.user_read ? 'r' : '-'); ent.directory = file_info.directory; ent.corrupt = (dir_entry[28] != 0); ent.filesize = file_info.file_size; return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_diskimage_freespace(imgtool::partition &partition, uint64_t *size) { imgtool::image &image(partition.image()); const os9_diskinfo *disk_info; uint32_t free_lsns; disk_info = os9_get_diskinfo(image); free_lsns = os9_get_free_lsns(image); *size = free_lsns * disk_info->sector_size; return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_diskimage_readfile(imgtool::partition &partition, const char *filename, const char *fork, imgtool::stream &destf) { imgtoolerr_t err; imgtool::image &img(partition.image()); const os9_diskinfo *disk_info; struct os9_fileinfo file_info; uint8_t buffer[256]; int i, j; uint32_t file_size; uint32_t used_size; disk_info = os9_get_diskinfo(img); err = os9_lookup_path(img, filename, CREATE_NONE, &file_info, NULL, NULL, NULL); if (err) return err; if (file_info.directory) return IMGTOOLERR_FILENOTFOUND; file_size = file_info.file_size; for (i = 0; file_info.sector_map[i].count > 0; i++) { for (j = 0; j < file_info.sector_map[i].count; j++) { used_size = std::min(file_size, disk_info->sector_size); err = os9_read_lsn(img, file_info.sector_map[i].lsn + j, 0, buffer, used_size); if (err) return err; destf.write(buffer, used_size); file_size -= used_size; } } return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_diskimage_writefile(imgtool::partition &partition, const char *path, const char *fork, imgtool::stream &sourcef, util::option_resolution *opts) { imgtoolerr_t err; imgtool::image &image(partition.image()); struct os9_fileinfo file_info; size_t write_size; std::vector buf; int i = -1; uint32_t lsn = 0; uint32_t count = 0; uint32_t sz; const os9_diskinfo *disk_info; disk_info = os9_get_diskinfo(image); buf.resize(disk_info->sector_size); err = os9_lookup_path(image, path, CREATE_FILE, &file_info, NULL, NULL, NULL); if (err) goto done; sz = (uint32_t) sourcef.size(); err = os9_set_file_size(image, &file_info, sz); if (err) goto done; while(sz > 0) { write_size = (std::min)(sz, disk_info->sector_size); sourcef.read(&buf[0], write_size); while(count == 0) { i++; lsn = file_info.sector_map[i].lsn; count = file_info.sector_map[i].count; } err = os9_write_lsn(image, lsn, 0, &buf[0], write_size); if (err) goto done; lsn++; count--; sz -= write_size; } done: return err; } static imgtoolerr_t os9_diskimage_delete(imgtool::partition &partition, const char *path, unsigned int delete_directory) { imgtoolerr_t err; imgtool::image &image(partition.image()); struct os9_fileinfo file_info; uint32_t dirent_lsn, dirent_index; uint32_t entry_lsn, entry_index; uint32_t i, j, lsn; uint8_t b; //disk_info = os9_get_diskinfo(image); err = os9_lookup_path(image, path, CREATE_NONE, &file_info, NULL, &dirent_lsn, &dirent_index); if (err) return err; if (file_info.directory != delete_directory) return IMGTOOLERR_FILENOTFOUND; /* make sure that if we are deleting a directory, it is empty */ if (delete_directory) { for (i = 64; i < file_info.file_size; i += 32) { entry_index = i; entry_lsn = os9_lookup_lsn(image, &file_info, &entry_index); err = os9_read_lsn(image, entry_lsn, entry_index, &b, 1); if (err) return err; /* this had better be a deleted file, if not we can't delete */ if (b != 0) return IMGTOOLERR_DIRNOTEMPTY; } } /* zero out the file entry */ b = '\0'; err = os9_write_lsn(image, dirent_lsn, dirent_index, &b, 1); if (err) return err; /* get the link count */ err = os9_read_lsn(image, file_info.lsn, 8, &b, 1); if (err) return err; if (b > 0) b--; if (b > 0) { /* link count is greater than zero */ err = os9_write_lsn(image, file_info.lsn, 8, &b, 1); if (err) return err; } else { /* no more links; outright delete the file */ err = os9_deallocate_lsn(image, file_info.lsn); if (err) return err; for (i = 0; (i < ARRAY_LENGTH(file_info.sector_map)) && file_info.sector_map[i].count; i++) { lsn = file_info.sector_map[i].lsn; for (j = 0; j < file_info.sector_map[i].count; j++) { err = os9_deallocate_lsn(image, lsn + j); if (err) return err; } } } return IMGTOOLERR_SUCCESS; } static imgtoolerr_t os9_diskimage_deletefile(imgtool::partition &partition, const char *path) { return os9_diskimage_delete(partition, path, 0); } static imgtoolerr_t os9_diskimage_createdir(imgtool::partition &partition, const char *path) { imgtoolerr_t err; imgtool::image &image(partition.image()); struct os9_fileinfo file_info; uint8_t dir_data[64]; uint32_t parent_lsn; err = os9_lookup_path(image, path, CREATE_DIR, &file_info, &parent_lsn, NULL, NULL); if (err) goto done; err = os9_set_file_size(image, &file_info, 64); if (err) goto done; /* create intial directories */ memset(dir_data, 0, sizeof(dir_data)); place_string(dir_data, 0, 32, ".."); place_integer_be(dir_data, 29, 3, parent_lsn); place_string(dir_data, 32, 32, "."); place_integer_be(dir_data, 61, 3, file_info.lsn); err = os9_write_lsn(image, file_info.sector_map[0].lsn, 0, dir_data, sizeof(dir_data)); if (err) goto done; done: return err; } static imgtoolerr_t os9_diskimage_deletedir(imgtool::partition &partition, const char *path) { return os9_diskimage_delete(partition, path, 1); } void os9_get_info(const imgtool_class *imgclass, uint32_t state, union imgtoolinfo *info) { switch(state) { /* --- the following bits of info are returned as 64-bit signed integers --- */ case IMGTOOLINFO_INT_INITIAL_PATH_SEPARATOR: info->i = 1; break; case IMGTOOLINFO_INT_OPEN_IS_STRICT: info->i = 1; break; case IMGTOOLINFO_INT_IMAGE_EXTRA_BYTES: info->i = sizeof(os9_diskinfo); break; case IMGTOOLINFO_INT_DIRECTORY_EXTRA_BYTES: info->i = sizeof(struct os9_direnum); break; case IMGTOOLINFO_INT_PATH_SEPARATOR: info->i = '/'; break; /* --- the following bits of info are returned as NULL-terminated strings --- */ case IMGTOOLINFO_STR_NAME: strcpy(info->s = imgtool_temp_str(), "os9"); break; case IMGTOOLINFO_STR_DESCRIPTION: strcpy(info->s = imgtool_temp_str(), "OS-9 format"); break; case IMGTOOLINFO_STR_FILE: strcpy(info->s = imgtool_temp_str(), __FILE__); break; case IMGTOOLINFO_STR_EOLN: strcpy(info->s = imgtool_temp_str(), "\r"); break; /* --- the following bits of info are returned as pointers to data or functions --- */ case IMGTOOLINFO_PTR_MAKE_CLASS: info->make_class = imgtool_floppy_make_class; break; case IMGTOOLINFO_PTR_FLOPPY_CREATE: info->create = os9_diskimage_create; break; case IMGTOOLINFO_PTR_FLOPPY_OPEN: info->open = os9_diskimage_open; break; case IMGTOOLINFO_PTR_BEGIN_ENUM: info->begin_enum = os9_diskimage_beginenum; break; case IMGTOOLINFO_PTR_NEXT_ENUM: info->next_enum = os9_diskimage_nextenum; break; case IMGTOOLINFO_PTR_FREE_SPACE: info->free_space = os9_diskimage_freespace; break; case IMGTOOLINFO_PTR_READ_FILE: info->read_file = os9_diskimage_readfile; break; case IMGTOOLINFO_PTR_WRITE_FILE: info->write_file = os9_diskimage_writefile; break; case IMGTOOLINFO_PTR_DELETE_FILE: info->delete_file = os9_diskimage_deletefile; break; case IMGTOOLINFO_PTR_CREATE_DIR: info->create_dir = os9_diskimage_createdir; break; case IMGTOOLINFO_PTR_DELETE_DIR: info->delete_dir = os9_diskimage_deletedir; break; case IMGTOOLINFO_PTR_FLOPPY_FORMAT: info->p = (void *) floppyoptions_coco; break; } }