From b7f65d33e237c4aaf9272c2af7447612a8494ff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaus=20K=C3=A4mpf?= Date: Tue, 9 Jan 2024 22:36:24 +0100 Subject: [PATCH] Honor sector size change via ModeSelect6 in scsicd (#1406) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Make ModeSelect() non-const Signed-off-by: Klaus Kämpf * Implement ModeSelect for scsicd and honor sector size setting. DEC's VMS can't handle 2k sector sizes and uses ModeSelect6 to set the sector size to 512 bytes. Fixes #1397 Signed-off-by: Klaus Kämpf * Test sector size setting via ModeSelect in SCSICD Signed-off-by: Klaus Kämpf * Re-calculate total blocks when sector size changes Signed-off-by: Klaus Kämpf * Reset CD data tracks after sector size change The track calculation is based on sector size and must be reset after change of sector size. Signed-off-by: Klaus Kämpf * resize cache after change of sector size The disk cache is based on sector size and must be resized when the sector size changes. Disk::ResizeCache needs a `raw` parameter, make this value accessible from the current cache. Signed-off-by: Klaus Kämpf * Make GetRawMode const Signed-off-by: Klaus Kämpf --------- Signed-off-by: Klaus Kämpf --- cpp/devices/disk.cpp | 10 ++++++++-- cpp/devices/disk.h | 2 +- cpp/devices/disk_cache.h | 1 + cpp/devices/mode_page_device.cpp | 2 +- cpp/devices/mode_page_device.h | 2 +- cpp/devices/scsicd.cpp | 23 +++++++++++++++++++++++ cpp/devices/scsicd.h | 1 + cpp/devices/scsihd.cpp | 2 +- cpp/devices/scsihd.h | 2 +- cpp/devices/scsimo.cpp | 2 +- cpp/devices/scsimo.h | 2 +- cpp/test/mocks.h | 1 + cpp/test/scsicd_test.cpp | 23 +++++++++++++++++++++++ 13 files changed, 64 insertions(+), 9 deletions(-) diff --git a/cpp/devices/disk.cpp b/cpp/devices/disk.cpp index 7ae8f275..b771b881 100644 --- a/cpp/devices/disk.cpp +++ b/cpp/devices/disk.cpp @@ -696,11 +696,17 @@ uint32_t Disk::GetSectorSizeInBytes() const void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes) { if (!GetSupportedSectorSizes().contains(size_in_bytes)) { - throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)"); + throw io_exception("Invalid sector size of " + to_string(size_in_bytes) + " byte(s)"); } + uint64_t current_blocks = GetBlockCount(); + uint32_t current_size_shift_count = size_shift_count; + uint64_t current_size = current_blocks << current_size_shift_count; size_shift_count = CalculateShiftCount(size_in_bytes); assert(size_shift_count); + if ((current_blocks > 0) && (current_size_shift_count > 0)) { + SetBlockCount(current_size >> size_shift_count); + } } uint32_t Disk::GetConfiguredSectorSize() const @@ -714,7 +720,7 @@ bool Disk::SetConfiguredSectorSize(uint32_t configured_size) return false; } - configured_sector_size = configured_size; + configured_sector_size = configured_size; return true; } diff --git a/cpp/devices/disk.h b/cpp/devices/disk.h index 6bb54d66..a69a4662 100644 --- a/cpp/devices/disk.h +++ b/cpp/devices/disk.h @@ -112,7 +112,7 @@ protected: void SetUpCache(off_t, bool = false); void ResizeCache(const string&, bool); - + bool GetRawMode() const { return (cache?cache->GetRawMode():false); } void SetUpModePages(map>&, int, bool) const override; void AddErrorPage(map>&, bool) const; virtual void AddFormatPage(map>&, bool) const; diff --git a/cpp/devices/disk_cache.h b/cpp/devices/disk_cache.h index ec486edd..d25eb2f0 100644 --- a/cpp/devices/disk_cache.h +++ b/cpp/devices/disk_cache.h @@ -51,6 +51,7 @@ public: ~DiskCache() = default; void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting + bool GetRawMode() const { return cd_raw; } bool Save(); // Save and release all bool ReadSector(span, uint32_t); // Sector Read diff --git a/cpp/devices/mode_page_device.cpp b/cpp/devices/mode_page_device.cpp index 7335d5a3..bb64cc37 100644 --- a/cpp/devices/mode_page_device.cpp +++ b/cpp/devices/mode_page_device.cpp @@ -114,7 +114,7 @@ void ModePageDevice::ModeSense10() const EnterDataInPhase(); } -void ModePageDevice::ModeSelect(scsi_command, cdb_t, span, int) const +void ModePageDevice::ModeSelect(scsi_command, cdb_t, span, int) { // There is no default implementation of MODE SELECT throw scsi_exception(sense_key::illegal_request, asc::invalid_command_operation_code); diff --git a/cpp/devices/mode_page_device.h b/cpp/devices/mode_page_device.h index 4a487fb2..4b657ebe 100644 --- a/cpp/devices/mode_page_device.h +++ b/cpp/devices/mode_page_device.h @@ -23,7 +23,7 @@ public: bool Init(const param_map&) override; - virtual void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int) const; + virtual void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int); protected: diff --git a/cpp/devices/scsicd.cpp b/cpp/devices/scsicd.cpp index 27ab23a3..e1d356bd 100644 --- a/cpp/devices/scsicd.cpp +++ b/cpp/devices/scsicd.cpp @@ -165,6 +165,29 @@ vector SCSICD::InquiryInternal() const return HandleInquiry(device_type::cd_rom, scsi_level, true); } +void SCSICD::ModeSelect(scsi_command cmd, cdb_t cdb, span buf, int length) +{ + int sector_size = 1 << GetSectorSizeShiftCount(); + int wanted_sector_size; + // skip Block Descriptor + int offset = 4; + // evaluate Mode Parameter Block Descriptor, sector size + wanted_sector_size = scsi_command_util::GetInt16(buf, offset + 6); + if (wanted_sector_size != sector_size) { + LogDebug("Changing sector size from " + to_string(sector_size) + " to " + to_string(wanted_sector_size)); + SetSectorSizeInBytes(wanted_sector_size); + ClearTrack(); + CreateDataTrack(); + FlushCache(); + ResizeCache(GetFilename(), GetRawMode()); + } + + if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, sector_size); + !result.empty()) { + LogWarn(result); + } +} + void SCSICD::SetUpModePages(map>& pages, int page, bool changeable) const { Disk::SetUpModePages(pages, page, changeable); diff --git a/cpp/devices/scsicd.h b/cpp/devices/scsicd.h index c2aaf4e3..cb1158dd 100644 --- a/cpp/devices/scsicd.h +++ b/cpp/devices/scsicd.h @@ -34,6 +34,7 @@ public: vector InquiryInternal() const override; int Read(span, uint64_t) override; + void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int) override; protected: diff --git a/cpp/devices/scsihd.cpp b/cpp/devices/scsihd.cpp index 09c6645a..51384c6a 100644 --- a/cpp/devices/scsihd.cpp +++ b/cpp/devices/scsihd.cpp @@ -82,7 +82,7 @@ vector SCSIHD::InquiryInternal() const return HandleInquiry(device_type::direct_access, scsi_level, IsRemovable()); } -void SCSIHD::ModeSelect(scsi_command cmd, cdb_t cdb, span buf, int length) const +void SCSIHD::ModeSelect(scsi_command cmd, cdb_t cdb, span buf, int length) { if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount()); !result.empty()) { diff --git a/cpp/devices/scsihd.h b/cpp/devices/scsihd.h index 28a6119d..95ded11a 100644 --- a/cpp/devices/scsihd.h +++ b/cpp/devices/scsihd.h @@ -37,7 +37,7 @@ public: // Commands vector InquiryInternal() const override; - void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int) const override; + void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int) override; void AddFormatPage(map>&, bool) const override; void AddVendorPage(map>&, int, bool) const override; diff --git a/cpp/devices/scsimo.cpp b/cpp/devices/scsimo.cpp index 57311be8..ab2af6c4 100644 --- a/cpp/devices/scsimo.cpp +++ b/cpp/devices/scsimo.cpp @@ -88,7 +88,7 @@ void SCSIMO::AddOptionPage(map>& pages, bool) const // Do not report update blocks } -void SCSIMO::ModeSelect(scsi_command cmd, cdb_t cdb, span buf, int length) const +void SCSIMO::ModeSelect(scsi_command cmd, cdb_t cdb, span buf, int length) { if (const string result = scsi_command_util::ModeSelect(cmd, cdb, buf, length, 1 << GetSectorSizeShiftCount()); !result.empty()) { diff --git a/cpp/devices/scsimo.h b/cpp/devices/scsimo.h index 1ef651b8..b41c193c 100644 --- a/cpp/devices/scsimo.h +++ b/cpp/devices/scsimo.h @@ -32,7 +32,7 @@ public: void Open() override; vector InquiryInternal() const override; - void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int) const override; + void ModeSelect(scsi_defs::scsi_command, cdb_t, span, int) override; protected: diff --git a/cpp/test/mocks.h b/cpp/test/mocks.h index 3fefdb40..919afd8d 100644 --- a/cpp/test/mocks.h +++ b/cpp/test/mocks.h @@ -387,6 +387,7 @@ class MockSCSICD : public SCSICD //NOSONAR Ignore inheritance hierarchy depth in FRIEND_TEST(ScsiCdTest, GetSectorSizes); FRIEND_TEST(ScsiCdTest, SetUpModePages); FRIEND_TEST(ScsiCdTest, ReadToc); + FRIEND_TEST(ScsiCdTest, ModeSelect); using SCSICD::SCSICD; }; diff --git a/cpp/test/scsicd_test.cpp b/cpp/test/scsicd_test.cpp index b4c9154f..4ebe3ce2 100644 --- a/cpp/test/scsicd_test.cpp +++ b/cpp/test/scsicd_test.cpp @@ -133,3 +133,26 @@ TEST(ScsiCdTest, ReadToc) // Further testing requires filesystem access } + +TEST(ScsiCdTest, ModeSelect) +{ + MockSCSICD cd(0); + vector cmd(6); + vector buf(255); + + cd.SetSectorSizeInBytes(2048); + + // PF + cmd[1] = 0x10; + // Length + buf[3] = 0x08; + // 2048 bytes per sector + buf[10] = 0x08; + // Page 3 (Device Format Page) + buf[12] = 0x01; + EXPECT_NO_THROW(cd.ModeSelect(scsi_command::eCmdModeSelect6, cmd, buf, 255)) << "MODE SELECT(6) with sector size 2048 is supported"; + + // 512 bytes per sector + buf[10] = 0x02; + EXPECT_NO_THROW(cd.ModeSelect(scsi_command::eCmdModeSelect6, cmd, buf, 255)) << "MODE SELECT(6) with sector size 512 is supported"; +}