/** * ZuluSCSI™ - Copyright (c) 2022 Rabbit Hole Computing™ * * This file is licensed under the GPL version 3 or any later version.  * It is derived from disk.c in SCSI2SD V6 * * https://www.gnu.org/licenses/gpl-3.0.html * ---- * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version.  * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details.  * * You should have received a copy of the GNU General Public License * along with this program.  If not, see . **/ #include "ImageBackingStore.h" #include #include #include "BlueSCSI_log.h" #include "BlueSCSI_config.h" #include #include #include #include ImageBackingStore::ImageBackingStore() { m_israw = false; m_isrom = false; m_isreadonly_attr = false; m_blockdev = nullptr; m_bgnsector = m_endsector = m_cursector = 0; } ImageBackingStore::ImageBackingStore(const char *filename, uint32_t scsi_block_size): ImageBackingStore() { if (strncasecmp(filename, "RAW:", 4) == 0) { char *endptr, *endptr2; m_bgnsector = strtoul(filename + 4, &endptr, 0); m_endsector = strtoul(endptr + 1, &endptr2, 0); if (*endptr != ':' || *endptr2 != '\0') { log("Invalid format for raw filename: ", filename); return; } if ((scsi_block_size % SD_SECTOR_SIZE) != 0) { log("SCSI block size ", (int)scsi_block_size, " is not supported for RAW partitions (must be divisible by 512 bytes)"); return; } m_israw = true; m_blockdev = SD.card(); uint32_t sectorCount = SD.card()->sectorCount(); if (m_endsector >= sectorCount) { log("Limiting RAW image mapping to SD card sector count: ", (int)sectorCount); m_endsector = sectorCount - 1; } } else if (strncasecmp(filename, "ROM:", 4) == 0) { if (!romDriveCheckPresent(&m_romhdr)) { m_romhdr.imagesize = 0; } else { m_isrom = true; } } else { m_isreadonly_attr = !!(FS_ATTRIB_READ_ONLY & SD.attrib(filename)); if (m_isreadonly_attr) { m_fsfile = SD.open(filename, O_RDONLY); log("---- Image file is read-only, writes disabled"); } else { m_fsfile = SD.open(filename, O_RDWR); } uint32_t sectorcount = m_fsfile.size() / SD_SECTOR_SIZE; uint32_t begin = 0, end = 0; if (m_fsfile.contiguousRange(&begin, &end) && end >= begin + sectorcount && (scsi_block_size % SD_SECTOR_SIZE) == 0) { // Convert to raw mapping, this avoids some unnecessary // access overhead in SdFat library. // If non-aligned offsets are later requested, it automatically falls // back to SdFat access mode. m_israw = true; m_blockdev = SD.card(); m_bgnsector = begin; m_endsector = begin + sectorcount - 1; m_fsfile.flush(); // Note: m_fsfile is also kept open as a fallback. } } } bool ImageBackingStore::isOpen() { if (m_israw) return (m_blockdev != NULL); else if (m_isrom) return (m_romhdr.imagesize > 0); else return m_fsfile.isOpen(); } bool ImageBackingStore::isWritable() { return !(m_isrom && m_isreadonly_attr); } bool ImageBackingStore::isRom() { return m_isrom; } bool ImageBackingStore::isRaw() { return m_israw; } bool ImageBackingStore::close() { if (m_israw) { m_blockdev = nullptr; return true; } else if (m_isrom) { m_romhdr.imagesize = 0; return true; } else { return m_fsfile.close(); } } uint64_t ImageBackingStore::size() { if (m_israw && m_blockdev) { return (uint64_t)(m_endsector - m_bgnsector + 1) * SD_SECTOR_SIZE; } else if (m_isrom) { return m_romhdr.imagesize; } else { return m_fsfile.size(); } } bool ImageBackingStore::contiguousRange(uint32_t* bgnSector, uint32_t* endSector) { if (m_israw && m_blockdev) { *bgnSector = m_bgnsector; *endSector = m_endsector; return true; } else if (m_isrom) { *bgnSector = 0; *endSector = 0; return true; } else { return m_fsfile.contiguousRange(bgnSector, endSector); } } bool ImageBackingStore::seek(uint64_t pos) { uint32_t sectornum = pos / SD_SECTOR_SIZE; if (m_israw && (uint64_t)sectornum * SD_SECTOR_SIZE != pos) { debuglog("---- Unaligned access to image, falling back to SdFat access mode"); m_israw = false; } if (m_israw) { m_cursector = m_bgnsector + sectornum; return (m_cursector <= m_endsector); } else if (m_isrom) { uint32_t sectornum = pos / SD_SECTOR_SIZE; assert((uint64_t)sectornum * SD_SECTOR_SIZE == pos); m_cursector = sectornum; return m_cursector * SD_SECTOR_SIZE < m_romhdr.imagesize; } else { return m_fsfile.seek(pos); } } ssize_t ImageBackingStore::read(void* buf, size_t count) { uint32_t sectorcount = count / SD_SECTOR_SIZE; if (m_israw && (uint64_t)sectorcount * SD_SECTOR_SIZE != count) { debuglog("---- Unaligned access to image, falling back to SdFat access mode"); m_israw = false; } if (m_israw && m_blockdev) { if (m_blockdev->readSectors(m_cursector, (uint8_t*)buf, sectorcount)) { m_cursector += sectorcount; return count; } else { return -1; } } else if (m_isrom) { assert((uint64_t)sectorcount * SD_SECTOR_SIZE == count); uint32_t start = m_cursector * SD_SECTOR_SIZE; if (romDriveRead((uint8_t*)buf, start, count)) { m_cursector += sectorcount; return count; } else { return -1; } } else { return m_fsfile.read(buf, count); } } ssize_t ImageBackingStore::write(const void* buf, size_t count) { uint32_t sectorcount = count / SD_SECTOR_SIZE; if (m_israw && (uint64_t)sectorcount * SD_SECTOR_SIZE != count) { debuglog("---- Unaligned access to image, falling back to SdFat access mode"); m_israw = false; } if (m_israw && m_blockdev) { if (m_blockdev->writeSectors(m_cursector, (const uint8_t*)buf, sectorcount)) { m_cursector += sectorcount; return count; } else { return 0; } } else if (m_isrom) { log("ERROR: attempted to write to ROM drive"); return 0; } else if (m_isreadonly_attr) { log("ERROR: attempted to write to a read only image"); return 0; } else { return m_fsfile.write(buf, count); } } void ImageBackingStore::flush() { if (!m_israw && !m_isrom && !m_isreadonly_attr) { m_fsfile.flush(); } } void ImageBackingStore::getName(char * name, size_t len) { if(m_isrom) name = (char*)"ROM:"; else if(m_israw) name = (char*)"RAW:"; else m_fsfile.getName(name, len); } uint64_t ImageBackingStore::position() { if (!m_israw && !m_isrom) { return m_fsfile.curPosition(); } else { return 0; } }