diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp index 5c487598..c6f4feaf 100644 --- a/src/raspberrypi/devices/device_factory.cpp +++ b/src/raspberrypi/devices/device_factory.cpp @@ -17,6 +17,7 @@ #include "exceptions.h" #include "device_factory.h" #include +#include using namespace std; using namespace rascsi_interface; @@ -30,6 +31,15 @@ DeviceFactory::DeviceFactory() sector_sizes_scsi.insert(1024); sector_sizes_scsi.insert(2048); sector_sizes_scsi.insert(4096); + + // 128 MB, 512 bytes per block, 248826 blocks + geometries_mo[0x797f400] = make_pair(512, 248826); + // 230 MB, 512 bytes per sector, 446325 blocks + geometries_mo[0xd9eea00] = make_pair(512, 446325); + // 540 MB, 512 bytes per sector, 1041500 blocks + geometries_mo[0x1fc8b800] = make_pair(512, 1041500); + // 640 MB, 20248 bytes per sector, 310352 blocks + geometries_mo[0x25e28000] = make_pair(2048, 310352); } DeviceFactory::~DeviceFactory() @@ -104,6 +114,7 @@ Device *DeviceFactory::CreateDevice(PbDeviceType& type, const string& filename, device->SetRemovable(true); device->SetLockable(true); device->SetProduct("SCSI MO"); + ((Disk *)device)->SetGeometries(geometries_mo); break; case SCCD: @@ -138,3 +149,14 @@ Device *DeviceFactory::CreateDevice(PbDeviceType& type, const string& filename, return device; } + +const set DeviceFactory::GetMoCapacities() const +{ + set keys; + + for (const auto& geometry : geometries_mo) { + keys.insert(geometry.first); + } + + return keys; +} diff --git a/src/raspberrypi/devices/device_factory.h b/src/raspberrypi/devices/device_factory.h index 6ab1827a..b4fd77ac 100644 --- a/src/raspberrypi/devices/device_factory.h +++ b/src/raspberrypi/devices/device_factory.h @@ -12,27 +12,37 @@ #pragma once #include +#include #include #include "rascsi_interface.pb.h" +using namespace std; + class Device; class DeviceFactory { public: + typedef pair Geometry; + DeviceFactory(); ~DeviceFactory(); static DeviceFactory& instance(); - const std::set& GetSasiSectorSizes() const { return sector_sizes_sasi; }; - const std::set& GetScsiSectorSizes() const { return sector_sizes_scsi; }; + const set& GetSasiSectorSizes() const { return sector_sizes_sasi; }; + const set& GetScsiSectorSizes() const { return sector_sizes_scsi; }; - Device *CreateDevice(rascsi_interface::PbDeviceType& type, const std::string& filename, const std::string& ext); + const set GetMoCapacities() const; + + Device *CreateDevice(rascsi_interface::PbDeviceType& type, const string& filename, const string& ext); private: - std::set sector_sizes_sasi; - std::set sector_sizes_scsi; + set sector_sizes_sasi; + set sector_sizes_scsi; + + // Mapping of supported MO capacities in bytes to the respective block sizes and block counts + map geometries_mo; }; diff --git a/src/raspberrypi/devices/disk.cpp b/src/raspberrypi/devices/disk.cpp index f60fcd2e..e003f9d8 100644 --- a/src/raspberrypi/devices/disk.cpp +++ b/src/raspberrypi/devices/disk.cpp @@ -1746,14 +1746,14 @@ bool Disk::GetStartAndCount(SASIDEV *controller, uint64_t& start, uint32_t& coun return true; } -int Disk::GetSectorSizeInBytes() const +uint32_t Disk::GetSectorSizeInBytes() const { return disk.size ? 1 << disk.size : 0; } -void Disk::SetSectorSizeInBytes(int size, bool sasi) +void Disk::SetSectorSizeInBytes(uint32_t size, bool sasi) { - set sector_sizes = sasi ? DeviceFactory::instance().GetSasiSectorSizes() : DeviceFactory::instance().GetScsiSectorSizes(); + set sector_sizes = sasi ? DeviceFactory::instance().GetSasiSectorSizes() : DeviceFactory::instance().GetScsiSectorSizes(); if (sector_sizes.find(size) == sector_sizes.end()) { stringstream error; error << "Invalid sector size of " << size << " bytes"; @@ -1787,7 +1787,7 @@ void Disk::SetSectorSizeInBytes(int size, bool sasi) } } -int Disk::GetSectorSize() const +uint32_t Disk::GetSectorSize() const { return disk.size; } @@ -1797,17 +1797,17 @@ bool Disk::IsSectorSizeConfigurable() const return !sector_sizes.empty(); } -void Disk::SetSectorSizes(const set& sector_sizes) +void Disk::SetSectorSizes(const set& sector_sizes) { this->sector_sizes = sector_sizes; } -int Disk::GetConfiguredSectorSize() const +uint32_t Disk::GetConfiguredSectorSize() const { return configured_sector_size; } -bool Disk::SetConfiguredSectorSize(int configured_sector_size) +bool Disk::SetConfiguredSectorSize(uint32_t configured_sector_size) { if (configured_sector_size != 512 && configured_sector_size != 1024 && configured_sector_size != 2048 && configured_sector_size != 4096) { @@ -1819,12 +1819,45 @@ bool Disk::SetConfiguredSectorSize(int configured_sector_size) return true; } +bool Disk::SetGeometries(const map& geometries) +{ + if (!IsMo()) { + return false; + } + + this->geometries = geometries; + + return true; +} + +void Disk::SetGeometryForCapacity(uint64_t capacity) { + const auto& geometry = geometries.find(capacity); + + if (geometry == geometries.end()) { + ostringstream error; + error << "Invalid file size of " << capacity << " bytes. Supported file sizes are "; + bool isFirst = true; + for (const auto& g : geometries) { + if (!isFirst) { + error << ", "; + } + error << g.first << " bytes"; + isFirst = false; + } + error << "."; + throw io_exception(error.str()); + } + + SetSectorSizeInBytes(geometry->second.first, false); + SetBlockCount(geometry->second.second); +} + uint32_t Disk::GetBlockCount() const { return disk.blocks; } -void Disk::SetBlockCount(DWORD blocks) +void Disk::SetBlockCount(uint32_t blocks) { disk.blocks = blocks; } diff --git a/src/raspberrypi/devices/disk.h b/src/raspberrypi/devices/disk.h index 37750029..3fe7eacb 100644 --- a/src/raspberrypi/devices/disk.h +++ b/src/raspberrypi/devices/disk.h @@ -21,6 +21,7 @@ #include "scsi.h" #include "controllers/scsidev_ctrl.h" #include "device.h" +#include "device_factory.h" #include "disk_track_cache.h" #include "file_support.h" #include "filepath.h" @@ -37,8 +38,12 @@ class Disk : public Device, public ScsiPrimaryCommands, public ScsiBlockCommands private: enum access_mode { RW6, RW10, RW16 }; - set sector_sizes; - int configured_sector_size; + // The supported configurable block sizes, empty if not configurable + set sector_sizes; + uint32_t configured_sector_size; + + // The mapping of supported capacities to block sizes and block counts, empty if there is no capacity restriction + map geometries; SASIDEV::ctrl_t *ctrl; @@ -46,7 +51,7 @@ protected: // Internal data structure typedef struct { int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096) - DWORD blocks; // Total number of sectors + uint32_t blocks; // Total number of sectors DiskCache *dcache; // Disk cache off_t imgoffset; // Offset to actual data } disk_t; @@ -124,16 +129,18 @@ public: int SelectCheck(const DWORD *cdb); // SELECT check int SelectCheck10(const DWORD *cdb); // SELECT(10) check - int GetSectorSizeInBytes() const; - void SetSectorSizeInBytes(int, bool); - int GetSectorSize() const; + uint32_t GetSectorSizeInBytes() const; + void SetSectorSizeInBytes(uint32_t, bool); + uint32_t GetSectorSize() const; bool IsSectorSizeConfigurable() const; - set GetSectorSizes() const; - void SetSectorSizes(const set&); - int GetConfiguredSectorSize() const; - bool SetConfiguredSectorSize(int); + set GetSectorSizes() const; + void SetSectorSizes(const set&); + uint32_t GetConfiguredSectorSize() const; + bool SetConfiguredSectorSize(uint32_t); + bool SetGeometries(const map&); + void SetGeometryForCapacity(uint64_t); uint32_t GetBlockCount() const; - void SetBlockCount(DWORD); + void SetBlockCount(uint32_t); bool GetStartAndCount(SASIDEV *, uint64_t&, uint32_t&, access_mode); // TODO Try to get rid of this method, which is called by SASIDEV (but must not) diff --git a/src/raspberrypi/devices/scsicd.cpp b/src/raspberrypi/devices/scsicd.cpp index 3aff736f..31112140 100644 --- a/src/raspberrypi/devices/scsicd.cpp +++ b/src/raspberrypi/devices/scsicd.cpp @@ -304,7 +304,7 @@ void SCSICD::Open(const Filepath& path) // Open as read-only Fileio fio; if (!fio.Open(path, Fileio::ReadOnly)) { - throw io_exception("Can't open CD-ROM file read-only"); + throw file_not_found_exception("Can't open CD-ROM file read-only"); } // Close and transfer for physical CD access diff --git a/src/raspberrypi/devices/scsihd.cpp b/src/raspberrypi/devices/scsihd.cpp index 72f089f8..f2a79d2d 100644 --- a/src/raspberrypi/devices/scsihd.cpp +++ b/src/raspberrypi/devices/scsihd.cpp @@ -64,7 +64,7 @@ void SCSIHD::Open(const Filepath& path) // Open as read-only Fileio fio; if (!fio.Open(path, Fileio::ReadOnly)) { - throw io_exception("Can't open hard disk file read-only"); + throw file_not_found_exception("Can't open hard disk file read-only"); } // Get file size diff --git a/src/raspberrypi/devices/scsimo.cpp b/src/raspberrypi/devices/scsimo.cpp index 4aa2c8a9..de6bec82 100644 --- a/src/raspberrypi/devices/scsimo.cpp +++ b/src/raspberrypi/devices/scsimo.cpp @@ -46,48 +46,16 @@ void SCSIMO::Open(const Filepath& path) // Open as read-only Fileio fio; + if (!fio.Open(path, Fileio::ReadOnly)) { - throw io_exception("Can't open MO file read-only"); + throw file_not_found_exception("Can't open MO file read-only"); } // Get file size off_t size = fio.GetFileSize(); fio.Close(); - switch (size) { - // 128MB - case 0x797f400: - // 512 bytes per sector - SetSectorSizeInBytes(512, false); - SetBlockCount(248826); - break; - - // 230MB - case 0xd9eea00: - // 512 bytes per sector - SetSectorSizeInBytes(512, false); - SetBlockCount(446325); - break; - - // 540MB - case 0x1fc8b800: - // 512 bytes per sector - SetSectorSizeInBytes(512, false); - SetBlockCount(1041500); - break; - - // 640MB - case 0x25e28000: - // 2048 bytes per sector - SetSectorSizeInBytes(2048, false); - SetBlockCount(310352); - break; - - // Other (this is an error) - default: - throw io_exception("Invalid MO file size, supported sizes are 127398912 bytes (128 MB, 512 BPS), " - "228518400 bytes (230 MB, 512 BPS), 533248000 bytes (540 MB, 512 BPS), 635600896 bytes (640 MB, 2048 BPS)"); - } + SetGeometryForCapacity(size); Disk::Open(path); FileSupport::SetPath(path); diff --git a/src/raspberrypi/exceptions.h b/src/raspberrypi/exceptions.h index a078a45d..e9c7e431 100644 --- a/src/raspberrypi/exceptions.h +++ b/src/raspberrypi/exceptions.h @@ -35,9 +35,15 @@ private: public: io_exception(const string& _msg) : msg(_msg) {} - ~io_exception() {} + virtual ~io_exception() {} const string& getmsg() const { return msg; } }; + +class file_not_found_exception : public io_exception { +public: + file_not_found_exception(const string& msg) : io_exception(msg) {} + ~file_not_found_exception() {} +}; diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 02097ac0..472807ba 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -448,7 +448,7 @@ bool MapController(Device **map) bool ReturnStatus(int fd, bool status = true, const string msg = "") { if (!status && !msg.empty()) { - LOGWARN("%s", msg.c_str()); + LOGERROR("%s", msg.c_str()); } if (fd == -1) { @@ -535,19 +535,18 @@ void GetDeviceTypeFeatures(PbServerInfo& serverInfo) PbDeviceProperties *properties = new PbDeviceProperties(); types_properties->set_allocated_properties(properties); properties->set_supports_file(true); - set block_sizes = device_factory.GetSasiSectorSizes(); + auto block_sizes = device_factory.GetSasiSectorSizes(); for (const auto& block_size : block_sizes) { properties->add_block_sizes(block_size); } - block_sizes = device_factory.GetScsiSectorSizes(); - types_properties = serverInfo.add_types_properties(); types_properties->set_type(SCHD); properties = new PbDeviceProperties(); types_properties->set_allocated_properties(properties); properties->set_protectable(true); properties->set_supports_file(true); + block_sizes = device_factory.GetScsiSectorSizes(); for (const auto& block_size : block_sizes) { properties->add_block_sizes(block_size); } @@ -572,6 +571,10 @@ void GetDeviceTypeFeatures(PbServerInfo& serverInfo) properties->set_removable(true); properties->set_lockable(true); properties->set_supports_file(true); + auto capacities = device_factory.GetMoCapacities(); + for (const auto& capacity : capacities) { + properties->add_capacities(capacity); + } types_properties = serverInfo.add_types_properties(); types_properties->set_type(SCCD); @@ -755,21 +758,20 @@ bool ProcessCmd(int fd, const PbDeviceDefinition& pbDevice, const PbOperation op filepath.SetPath(filename.c_str()); try { - fileSupport->Open(filepath); - } - catch(const io_exception& e) { - // If the file does not exist search for it in the default image folder - string default_file = default_image_folder + "/" + filename; - filepath.SetPath(default_file.c_str()); try { fileSupport->Open(filepath); } - catch(const io_exception&) { - delete device; - - return ReturnStatus(fd, false, "Tried to open an invalid file '" + filename + "': " + e.getmsg()); + catch(const file_not_found_exception&) { + // If the file does not exist search for it in the default image folder + filepath.SetPath(string(default_image_folder + "/" + filename).c_str()); + fileSupport->Open(filepath); } } + catch(const io_exception& e) { + delete device; + + return ReturnStatus(fd, false, "Tried to open an invalid file '" + string(filepath.GetPath()) + "': " + e.getmsg()); + } const string path = filepath.GetPath(); if (files_in_use.find(path) != files_in_use.end()) { @@ -897,31 +899,44 @@ bool ProcessCmd(int fd, const PbDeviceDefinition& pbDevice, const PbOperation op LOGINFO("Insert file '%s' requested into %s ID %d, unit %d", filename.c_str(), device->GetType().c_str(), id, unit); try { - fileSupport->Open(filepath); - } - catch(const io_exception& e) { - // If the file does not exist search for it in the default image folder - string default_file = default_image_folder + "/" + filename; - filepath.SetPath(default_file.c_str()); try { fileSupport->Open(filepath); } - catch(const io_exception&) { - return ReturnStatus(fd, false, "Tried to open an invalid file '" + filename + "': " + e.getmsg()); + catch(const file_not_found_exception&) { + // If the file does not exist search for it in the default image folder + filepath.SetPath(string(default_image_folder + "/" + filename).c_str()); + fileSupport->Open(filepath); } } + catch(const io_exception& e) { + return ReturnStatus(fd, false, "Tried to open an invalid file '" + string(filepath.GetPath()) + "': " + e.getmsg()); + } + + const string path = filepath.GetPath(); + if (files_in_use.find(path) != files_in_use.end()) { + const auto& full_id = files_in_use[path]; + error << "Image file '" << filename << "' is already used by ID " << full_id.first << ", unit " << full_id.second; + return ReturnStatus(fd, false, error); + } + + files_in_use[path] = make_pair(device->GetId(), device->GetLun()); } break; - case EJECT: - if (dryRun) { - return true; + case EJECT: { + if (dryRun) { + return true; + } + + LOGINFO("Eject requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); + + // EJECT is idempotent + if (device->Eject(true) && fileSupport) { + Filepath filepath; + fileSupport->GetPath(filepath); + files_in_use.erase(filepath.GetPath()); + } } - - LOGINFO("Eject requested for %s ID %d, unit %d", device->GetType().c_str(), id, unit); - - // EJECT is idempotent - device->Eject(true); break; case PROTECT: diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 2a4cb221..70f4f971 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -63,8 +63,10 @@ message PbDeviceProperties { bool supports_file = 5; // Device supports parameters other than a filename bool supports_params = 6; - // Unordered list of supported block sizes, empty if the block size is not configurable + // Unordered list of supported block sizes in bytes, empty if the block size is not configurable repeated uint32 block_sizes = 7; + // Unordered list of supported capacities in bytes, empty if there is no capacity restriction + repeated uint64 capacities = 8; } // The status of a device diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 99b6beff..3fc43649 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -219,8 +219,6 @@ void CommandServerInfo(const string& hostname, int port) PbDeviceTypeProperties types_properties = serverInfo.types_properties(i); cout << " " << PbDeviceType_Name(types_properties.type()); - list block_sizes; - cout << " Properties: "; bool has_property = false; const PbDeviceProperties& properties = types_properties.properties(); @@ -254,15 +252,15 @@ void CommandServerInfo(const string& hostname, int port) } cout << endl; - for (int k = 0 ; k < properties.block_sizes_size(); k++) - { + list block_sizes; + for (int k = 0 ; k < properties.block_sizes_size(); k++) { block_sizes.push_back(properties.block_sizes(k)); } if (!block_sizes.empty()) { - block_sizes.sort([](const int& a, const int& b) { return a < b; }); + block_sizes.sort([](const auto& a, const auto& b) { return a < b; }); - cout << " Configurable block sizes: "; + cout << " Configurable block sizes in bytes: "; bool isFirst = true; for (const auto& block_size : block_sizes) { @@ -275,6 +273,28 @@ void CommandServerInfo(const string& hostname, int port) } cout << endl; } + + list capacities; + for (int k = 0; k < properties.capacities_size(); k++) { + capacities.push_back(properties.capacities(k)); + } + + if (!capacities.empty()) { + capacities.sort([](const auto& a, const auto& b) { return a < b; }); + + cout << " Supported capacities in bytes: "; + + bool isFirst = true; + for (const auto& capacity : capacities) { + if (!isFirst) { + cout << ", "; + } + cout << capacity; + + isFirst = false; + } + cout << endl; + } } cout << "Attached devices:" << endl;