More unit tests, replacement of raw pointers and C arrays, separation of concerns (#878)

* Added unit tests

* Fixed SonarCloud issues

* Updated error handling

* Updated deletion of controllers

* Image folder handling cleanup

* Fixed clang warning

* Removed duplicate code

* Reduced code complexity

* Updated array handling

* Initialize device with ID and LUN

* Use smart pointers

* Updated memory management

* Logging updates

* Extracted methods

* Split scsi.h
This commit is contained in:
Uwe Seimet 2022-10-04 17:23:42 +02:00 committed by GitHub
parent 0e4d42f04c
commit efbfb54d26
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
100 changed files with 3501 additions and 2430 deletions

View File

@ -56,5 +56,5 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
run: | run: |
cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.cfamily.cache.enabled=false --define sonar.coverage.exclusions="tests/*" --define sonar.python.version=3 cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.cfamily.cache.enabled=false --define sonar.coverage.exclusions="**/test/**" --define sonar.cpd.exclusions="**test/**" --define sonar.python.version=3

View File

@ -92,16 +92,18 @@ SRC_RASCSI_CORE = \
rascsi_version.cpp \ rascsi_version.cpp \
rascsi_image.cpp \ rascsi_image.cpp \
rascsi_response.cpp \ rascsi_response.cpp \
rascsi_executor.cpp \
rasutil.cpp \ rasutil.cpp \
command_util.cpp \ command_util.cpp \
socket_connector.cpp \ protobuf_serializer.cpp \
localizer.cpp localizer.cpp
SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp') SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp') SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./hal -name '*.cpp') SRC_RASCSI_CORE += $(shell find ./hal -name '*.cpp')
SRC_RASCSI_CORE += $(SRC_PROTOBUF) SRC_RASCSI_CORE += $(SRC_PROTOBUF)
SRC_RASCSI = rascsi.cpp SRC_RASCSI = rascsi.cpp \
rascsi_service.cpp
SRC_SCSIMON = \ SRC_SCSIMON = \
scsimon.cpp \ scsimon.cpp \
@ -117,7 +119,7 @@ SRC_RASCTL = \
rascsi_version.cpp \ rascsi_version.cpp \
rasutil.cpp \ rasutil.cpp \
command_util.cpp \ command_util.cpp \
socket_connector.cpp \ protobuf_serializer.cpp \
localizer.cpp localizer.cpp
SRC_RASCTL += $(SRC_PROTOBUF) SRC_RASCTL += $(SRC_PROTOBUF)

113
src/raspberrypi/bus.h Normal file
View File

@ -0,0 +1,113 @@
//---------------------------------------------------------------------------
//
// X68000 EMULATOR "XM6"
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
//
//---------------------------------------------------------------------------
#pragma once
#include "os.h"
#include "scsi.h"
#include <array>
#include <unordered_map>
using namespace std;
class BUS
{
public:
// Operation modes definition
enum class mode_e {
TARGET = 0,
INITIATOR = 1,
MONITOR = 2,
};
// Phase definitions
enum class phase_t : int {
busfree,
arbitration,
selection,
reselection,
command,
datain,
dataout,
status,
msgin,
msgout,
reserved
};
BUS() = default;
virtual ~BUS() = default;
// Basic Functions
virtual bool Init(mode_e mode) = 0;
virtual void Reset() = 0;
virtual void Cleanup() = 0;
phase_t GetPhase();
static phase_t GetPhase(int mci)
{
return phase_table[mci];
}
// Get the string phase name, based upon the raw data
static const char* GetPhaseStrRaw(phase_t current_phase);
// Extract as specific pin field from a raw data capture
static inline DWORD GetPinRaw(DWORD raw_data, DWORD pin_num)
{
return ((raw_data >> pin_num) & 1);
}
virtual bool GetBSY() const = 0;
virtual void SetBSY(bool ast) = 0;
virtual bool GetSEL() const = 0;
virtual void SetSEL(bool ast) = 0;
virtual bool GetATN() const = 0;
virtual void SetATN(bool ast) = 0;
virtual bool GetACK() const = 0;
virtual void SetACK(bool ast) = 0;
virtual bool GetRST() const = 0;
virtual void SetRST(bool ast) = 0;
virtual bool GetMSG() const = 0;
virtual void SetMSG(bool ast) = 0;
virtual bool GetCD() const = 0;
virtual void SetCD(bool ast) = 0;
virtual bool GetIO() = 0;
virtual void SetIO(bool ast) = 0;
virtual bool GetREQ() const = 0;
virtual void SetREQ(bool ast) = 0;
virtual BYTE GetDAT() = 0;
virtual void SetDAT(BYTE dat) = 0;
virtual bool GetDP() const = 0; // Get parity signal
virtual uint32_t Acquire() = 0;
virtual int CommandHandShake(BYTE *buf) = 0;
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
virtual bool GetSignal(int pin) const = 0;
// Get SCSI input signal value
virtual void SetSignal(int pin, bool ast) = 0;
// Set SCSI output signal value
static const int SEND_NO_DELAY = -1;
// Passed into SendHandShake when we don't want to delay
private:
static const array<phase_t, 8> phase_table;
static const unordered_map<phase_t, const char *> phase_str_mapping;
};

View File

@ -3,7 +3,7 @@
// SCSI Target Emulator RaSCSI Reloaded // SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi // for Raspberry Pi
// //
// Copyright (C) 2021 Uwe Seimet // Copyright (C) 2021-2022 Uwe Seimet
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -11,16 +11,18 @@
#include <string> #include <string>
class SocketConnector; class ProtobufSerializer;
class Localizer; class Localizer;
struct CommandContext class CommandContext
{ {
CommandContext(const SocketConnector& c, const Localizer& l, int f, const std::string& s) public:
: connector(c), localizer(l), fd(f), locale(s) {}
CommandContext(const ProtobufSerializer& c, const Localizer& l, int f, const std::string& s)
: serializer(c), localizer(l), fd(f), locale(s) {}
~CommandContext() = default; ~CommandContext() = default;
const SocketConnector& connector; const ProtobufSerializer& serializer;
const Localizer& localizer; const Localizer& localizer;
int fd; int fd;
std::string locale; std::string locale;

View File

@ -10,7 +10,7 @@
#include "log.h" #include "log.h"
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include "localizer.h" #include "localizer.h"
#include "socket_connector.h" #include "protobuf_serializer.h"
#include "command_util.h" #include "command_util.h"
#include <sstream> #include <sstream>
@ -129,7 +129,7 @@ bool command_util::ReturnStatus(const CommandContext& context, bool status, cons
result.set_status(status); result.set_status(status);
result.set_error_code(error_code); result.set_error_code(error_code);
result.set_msg(msg); result.set_msg(msg);
context.connector.SerializeMessage(context.fd, result); context.serializer.SerializeMessage(context.fd, result);
} }
return status; return status;

View File

@ -17,7 +17,7 @@
#include "localizer.h" #include "localizer.h"
#include <string> #include <string>
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi using namespace rascsi_interface;
namespace command_util namespace command_util
{ {

View File

@ -20,6 +20,6 @@
#define USE_SEL_EVENT_ENABLE // Check SEL signal by event #define USE_SEL_EVENT_ENABLE // Check SEL signal by event
// This avoids an indefinite loop with warnings if there is no RaSCSI hardware // This avoids an indefinite loop with warnings if there is no RaSCSI hardware
// and thus helps with running rasctl and unit test on x86 hardware. // and thus helps with running rasctl and unit test on x86 hardware.
#if defined(__x86_64__) || defined(__X86__) || !defined(__linux) #if defined(__x86_64__) || defined(__X86__) || !defined(__linux__)
#undef USE_SEL_EVENT_ENABLE #undef USE_SEL_EVENT_ENABLE
#endif #endif

View File

@ -7,8 +7,9 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "abstract_controller.h" #include "rascsi_exceptions.h"
#include "devices/primary_device.h" #include "devices/primary_device.h"
#include "abstract_controller.h"
void AbstractController::AllocateBuffer(size_t size) void AbstractController::AllocateBuffer(size_t size)
{ {
@ -17,7 +18,18 @@ void AbstractController::AllocateBuffer(size_t size)
} }
} }
PrimaryDevice *AbstractController::GetDeviceForLun(int lun) const { unordered_set<shared_ptr<PrimaryDevice>> AbstractController::GetDevices() const
{
unordered_set<shared_ptr<PrimaryDevice>> devices;
for (const auto& [id, lun] : luns) {
devices.insert(lun);
}
return devices;
}
shared_ptr<PrimaryDevice> AbstractController::GetDeviceForLun(int lun) const {
const auto& it = luns.find(lun); const auto& it = luns.find(lun);
return it == luns.end() ? nullptr : it->second; return it == luns.end() ? nullptr : it->second;
} }
@ -26,7 +38,7 @@ void AbstractController::Reset()
{ {
SetPhase(BUS::phase_t::busfree); SetPhase(BUS::phase_t::busfree);
ctrl.status = 0x00; ctrl.status = status::GOOD;
ctrl.message = 0x00; ctrl.message = 0x00;
ctrl.blocks = 0; ctrl.blocks = 0;
ctrl.next = 0; ctrl.next = 0;
@ -75,18 +87,15 @@ void AbstractController::ProcessPhase()
break; break;
default: default:
assert(false); LOGERROR("Cannot process phase %s", BUS::GetPhaseStrRaw(GetPhase()))
throw scsi_error_exception();
break; break;
} }
} }
bool AbstractController::AddDevice(PrimaryDevice *device) bool AbstractController::AddDevice(shared_ptr<PrimaryDevice> device)
{ {
if (device->GetLun() >= GetMaxLuns()) { if (device->GetLun() < 0 || device->GetLun() >= GetMaxLuns() || HasDeviceForLun(device->GetLun())) {
return false;
}
if (HasDeviceForLun(device->GetLun())) {
return false; return false;
} }
@ -96,7 +105,7 @@ bool AbstractController::AddDevice(PrimaryDevice *device)
return true; return true;
} }
bool AbstractController::DeleteDevice(const PrimaryDevice *device) bool AbstractController::DeleteDevice(const shared_ptr<PrimaryDevice> device)
{ {
return luns.erase(device->GetLun()) == 1; return luns.erase(device->GetLun()) == 1;
} }

View File

@ -11,24 +11,24 @@
#pragma once #pragma once
#include "scsi.h" #include "bus.h"
#include "phase_handler.h"
#include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <memory> #include <memory>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class PrimaryDevice; class PrimaryDevice;
class AbstractController class AbstractController : public PhaseHandler
{ {
friend class PrimaryDevice; friend class PrimaryDevice;
friend class ScsiController; friend class ScsiController;
BUS::phase_t phase = BUS::phase_t::busfree; // Logical units of this controller mapped to their LUN numbers
unordered_map<int, shared_ptr<PrimaryDevice>> luns;
// Logical units of this device controller mapped to their LUN numbers
unordered_map<int, PrimaryDevice *> luns;
public: public:
@ -41,7 +41,7 @@ public:
using ctrl_t = struct _ctrl_t { using ctrl_t = struct _ctrl_t {
vector<int> cmd; // Command data, dynamically allocated per received command vector<int> cmd; // Command data, dynamically allocated per received command
uint32_t status; // Status data scsi_defs::status status; // Status data
int message; // Message data int message; // Message data
// Transfer // Transfer
@ -52,21 +52,8 @@ public:
uint32_t length; // Transfer remaining length uint32_t length; // Transfer remaining length
}; };
AbstractController(shared_ptr<BUS> bus, int target_id, int luns) : target_id(target_id), bus(bus), max_luns(luns) {} AbstractController(BUS& bus, int target_id, int max_luns) : target_id(target_id), bus(bus), max_luns(max_luns) {}
virtual ~AbstractController() = default; ~AbstractController() override = default;
AbstractController(AbstractController&) = delete;
AbstractController& operator=(const AbstractController&) = delete;
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
virtual void Status() = 0;
virtual void DataIn() = 0;
virtual void DataOut() = 0;
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
virtual BUS::phase_t Process(int) = 0;
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION, virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0; scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0;
@ -81,19 +68,19 @@ public:
int GetTargetId() const { return target_id; } int GetTargetId() const { return target_id; }
int GetMaxLuns() const { return max_luns; } int GetMaxLuns() const { return max_luns; }
bool HasLuns() const { return !luns.empty(); } int GetLunCount() const { return (int)luns.size(); }
PrimaryDevice *GetDeviceForLun(int) const; unordered_set<shared_ptr<PrimaryDevice>> GetDevices() const;
bool AddDevice(PrimaryDevice *); shared_ptr<PrimaryDevice> GetDeviceForLun(int) const;
bool DeleteDevice(const PrimaryDevice *); bool AddDevice(shared_ptr<PrimaryDevice>);
bool DeleteDevice(const shared_ptr<PrimaryDevice>);
bool HasDeviceForLun(int) const; bool HasDeviceForLun(int) const;
int ExtractInitiatorId(int id_data) const; int ExtractInitiatorId(int) const;
void AllocateBuffer(size_t); void AllocateBuffer(size_t);
vector<BYTE>& GetBuffer() { return ctrl.buffer; } vector<BYTE>& GetBuffer() { return ctrl.buffer; }
size_t GetBufferSize() const { return ctrl.buffer.size(); } scsi_defs::status GetStatus() const { return ctrl.status; }
uint32_t GetStatus() const { return ctrl.status; } void SetStatus(scsi_defs::status s) { ctrl.status = s; }
void SetStatus(uint32_t s) { ctrl.status = s; }
uint32_t GetLength() const { return ctrl.length; } uint32_t GetLength() const { return ctrl.length; }
protected: protected:
@ -106,25 +93,15 @@ protected:
vector<int>& InitCmd(int size) { ctrl.cmd.resize(size); return ctrl.cmd; } vector<int>& InitCmd(int size) { ctrl.cmd.resize(size); return ctrl.cmd; }
bool HasValidLength() const { return ctrl.length != 0; } bool HasValidLength() const { return ctrl.length != 0; }
int GetOffset() const { return ctrl.offset; }
void ResetOffset() { ctrl.offset = 0; } void ResetOffset() { ctrl.offset = 0; }
void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; } void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; }
BUS::phase_t GetPhase() const { return phase; }
void SetPhase(BUS::phase_t p) { phase = p; }
bool IsSelection() const { return phase == BUS::phase_t::selection; }
bool IsBusFree() const { return phase == BUS::phase_t::busfree; }
bool IsCommand() const { return phase == BUS::phase_t::command; }
bool IsStatus() const { return phase == BUS::phase_t::status; }
bool IsDataIn() const { return phase == BUS::phase_t::datain; }
bool IsDataOut() const { return phase == BUS::phase_t::dataout; }
bool IsMsgIn() const { return phase == BUS::phase_t::msgin; }
bool IsMsgOut() const { return phase == BUS::phase_t::msgout; }
private: private:
int target_id; int target_id;
shared_ptr<BUS> bus; BUS& bus;
int max_luns; int max_luns;

View File

@ -15,17 +15,22 @@
using namespace std; using namespace std;
bool ControllerManager::CreateScsiController(shared_ptr<BUS> bus, PrimaryDevice *device) bool ControllerManager::AttachToScsiController(int id, shared_ptr<PrimaryDevice> device)
{ {
shared_ptr<AbstractController> controller = FindController(device->GetId()); auto controller = FindController(id);
if (controller == nullptr) { if (controller == nullptr) {
controller = make_shared<ScsiController>(bus, device->GetId()); controller = make_shared<ScsiController>(bus, id);
controllers[device->GetId()] = controller; controllers[id] = controller;
} }
return controller->AddDevice(device); return controller->AddDevice(device);
} }
void ControllerManager::DeleteController(shared_ptr<AbstractController> controller)
{
controllers.erase(controller->GetTargetId());
}
shared_ptr<AbstractController> ControllerManager::IdentifyController(int data) const shared_ptr<AbstractController> ControllerManager::IdentifyController(int data) const
{ {
for (const auto& [id, controller] : controllers) { for (const auto& [id, controller] : controllers) {
@ -43,6 +48,18 @@ shared_ptr<AbstractController> ControllerManager::FindController(int target_id)
return it == controllers.end() ? nullptr : it->second; return it == controllers.end() ? nullptr : it->second;
} }
unordered_set<shared_ptr<PrimaryDevice>> ControllerManager::GetAllDevices() const
{
unordered_set<shared_ptr<PrimaryDevice>> devices;
for (const auto& [id, controller] : controllers) {
auto d = controller->GetDevices();
devices.insert(d.begin(), d.end());
}
return devices;
}
void ControllerManager::DeleteAllControllers() void ControllerManager::DeleteAllControllers()
{ {
controllers.clear(); controllers.clear();
@ -55,7 +72,7 @@ void ControllerManager::ResetAllControllers() const
} }
} }
PrimaryDevice *ControllerManager::GetDeviceByIdAndLun(int id, int lun) const shared_ptr<PrimaryDevice> ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
{ {
if (const auto controller = FindController(id); controller != nullptr) { if (const auto controller = FindController(id); controller != nullptr) {
return controller->GetDeviceForLun(lun); return controller->GetDeviceForLun(lun);

View File

@ -12,9 +12,10 @@
#pragma once #pragma once
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
#include <memory> #include <memory>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class BUS; class BUS;
class AbstractController; class AbstractController;
@ -22,23 +23,24 @@ class PrimaryDevice;
class ControllerManager class ControllerManager
{ {
BUS& bus;
unordered_map<int, shared_ptr<AbstractController>> controllers;
public: public:
ControllerManager() = default; explicit ControllerManager(BUS& bus) : bus(bus) {}
~ControllerManager() = default; ~ControllerManager() = default;
ControllerManager(ControllerManager&) = delete;
ControllerManager& operator=(const ControllerManager&) = delete;
// Maximum number of controller devices // Maximum number of controller devices
static const int DEVICE_MAX = 8; static const int DEVICE_MAX = 8;
bool CreateScsiController(shared_ptr<BUS>, PrimaryDevice *); bool AttachToScsiController(int, shared_ptr<PrimaryDevice>);
void DeleteController(shared_ptr<AbstractController>);
shared_ptr<AbstractController> IdentifyController(int) const; shared_ptr<AbstractController> IdentifyController(int) const;
shared_ptr<AbstractController> FindController(int) const; shared_ptr<AbstractController> FindController(int) const;
unordered_set<shared_ptr<PrimaryDevice>> GetAllDevices() const;
void DeleteAllControllers(); void DeleteAllControllers();
void ResetAllControllers() const; void ResetAllControllers() const;
PrimaryDevice *GetDeviceByIdAndLun(int, int) const; shared_ptr<PrimaryDevice> GetDeviceByIdAndLun(int, int) const;
unordered_map<int, shared_ptr<AbstractController>> controllers;
}; };

View File

@ -0,0 +1,46 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
class PhaseHandler
{
BUS::phase_t phase = BUS::phase_t::busfree;
public:
PhaseHandler() = default;
virtual ~PhaseHandler() = default;
virtual void BusFree() = 0;
virtual void Selection() = 0;
virtual void Command() = 0;
virtual void Status() = 0;
virtual void DataIn() = 0;
virtual void DataOut() = 0;
virtual void MsgIn() = 0;
virtual void MsgOut() = 0;
virtual BUS::phase_t Process(int) = 0;
protected:
BUS::phase_t GetPhase() const { return phase; }
void SetPhase(BUS::phase_t p) { phase = p; }
bool IsSelection() const { return phase == BUS::phase_t::selection; }
bool IsBusFree() const { return phase == BUS::phase_t::busfree; }
bool IsCommand() const { return phase == BUS::phase_t::command; }
bool IsStatus() const { return phase == BUS::phase_t::status; }
bool IsDataIn() const { return phase == BUS::phase_t::datain; }
bool IsDataOut() const { return phase == BUS::phase_t::dataout; }
bool IsMsgIn() const { return phase == BUS::phase_t::msgin; }
bool IsMsgOut() const { return phase == BUS::phase_t::msgout; }
};

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -21,13 +21,13 @@
#include "scsi_controller.h" #include "scsi_controller.h"
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
#ifdef __linux #ifdef __linux__
#include <linux/if_tun.h> #include <linux/if_tun.h>
#endif #endif
using namespace scsi_defs; using namespace scsi_defs;
ScsiController::ScsiController(shared_ptr<BUS> bus, int target_id) : AbstractController(bus, target_id, LUN_MAX) ScsiController::ScsiController(BUS& bus, int target_id) : AbstractController(bus, target_id, LUN_MAX)
{ {
// The initial buffer size will default to either the default buffer size OR // The initial buffer size will default to either the default buffer size OR
// the size of an Ethernet message, whichever is larger. // the size of an Ethernet message, whichever is larger.
@ -52,17 +52,17 @@ void ScsiController::Reset()
BUS::phase_t ScsiController::Process(int id) BUS::phase_t ScsiController::Process(int id)
{ {
// Get bus information // Get bus information
bus->Acquire(); bus.Acquire();
// Check to see if the reset signal was asserted // Check to see if the reset signal was asserted
if (bus->GetRST()) { if (bus.GetRST()) {
LOGWARN("RESET signal received!") LOGWARN("RESET signal received!")
// Reset the controller // Reset the controller
Reset(); Reset();
// Reset the bus // Reset the bus
bus->Reset(); bus.Reset();
return GetPhase(); return GetPhase();
} }
@ -86,7 +86,7 @@ BUS::phase_t ScsiController::Process(int id)
LOGERROR("%s Unhandled SCSI error, resetting controller and bus and entering bus free phase", __PRETTY_FUNCTION__) LOGERROR("%s Unhandled SCSI error, resetting controller and bus and entering bus free phase", __PRETTY_FUNCTION__)
Reset(); Reset();
bus->Reset(); bus.Reset();
BusFree(); BusFree();
} }
@ -101,14 +101,14 @@ void ScsiController::BusFree()
SetPhase(BUS::phase_t::busfree); SetPhase(BUS::phase_t::busfree);
bus->SetREQ(false); bus.SetREQ(false);
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(false); bus.SetCD(false);
bus->SetIO(false); bus.SetIO(false);
bus->SetBSY(false); bus.SetBSY(false);
// Initialize status and message // Initialize status and message
SetStatus(0); SetStatus(status::GOOD);
ctrl.message = 0x00; ctrl.message = 0x00;
// Initialize ATN message reception status // Initialize ATN message reception status
@ -120,21 +120,21 @@ void ScsiController::BusFree()
bytes_to_transfer = 0; bytes_to_transfer = 0;
// When the bus is free RaSCSI or the Pi may be shut down. // When the bus is free RaSCSI or the Pi may be shut down.
// TODO Try to find a better place for this code without breaking encapsulation // This code has to be executed in the bus free phase and thus has to be located in the controller.
switch(shutdown_mode) { switch(shutdown_mode) {
case AbstractController::rascsi_shutdown_mode::STOP_RASCSI: case rascsi_shutdown_mode::STOP_RASCSI:
LOGINFO("RaSCSI shutdown requested") LOGINFO("RaSCSI shutdown requested")
exit(0); exit(0);
break; break;
case AbstractController::rascsi_shutdown_mode::STOP_PI: case rascsi_shutdown_mode::STOP_PI:
LOGINFO("Raspberry Pi shutdown requested") LOGINFO("Raspberry Pi shutdown requested")
if (system("init 0") == -1) { if (system("init 0") == -1) {
LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno)) LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno))
} }
break; break;
case AbstractController::rascsi_shutdown_mode::RESTART_PI: case rascsi_shutdown_mode::RESTART_PI:
LOGINFO("Raspberry Pi restart requested") LOGINFO("Raspberry Pi restart requested")
if (system("init 6") == -1) { if (system("init 6") == -1) {
LOGERROR("Raspberry Pi restart failed: %s", strerror(errno)) LOGERROR("Raspberry Pi restart failed: %s", strerror(errno))
@ -149,7 +149,7 @@ void ScsiController::BusFree()
} }
// Move to selection phase // Move to selection phase
if (bus->GetSEL() && !bus->GetBSY()) { if (bus.GetSEL() && !bus.GetBSY()) {
Selection(); Selection();
} }
} }
@ -158,12 +158,12 @@ void ScsiController::Selection()
{ {
if (!IsSelection()) { if (!IsSelection()) {
// A different device controller was selected // A different device controller was selected
if (int id = 1 << GetTargetId(); ((int)bus->GetDAT() & id) == 0) { if (int id = 1 << GetTargetId(); ((int)bus.GetDAT() & id) == 0) {
return; return;
} }
// Abort if there is no LUN for this controller // Abort if there is no LUN for this controller
if (!HasLuns()) { if (!GetLunCount()) {
return; return;
} }
@ -172,14 +172,14 @@ void ScsiController::Selection()
SetPhase(BUS::phase_t::selection); SetPhase(BUS::phase_t::selection);
// Raise BSY and respond // Raise BSY and respond
bus->SetBSY(true); bus.SetBSY(true);
return; return;
} }
// Selection completed // Selection completed
if (!bus->GetSEL() && bus->GetBSY()) { if (!bus.GetSEL() && bus.GetBSY()) {
// Message out phase if ATN=1, otherwise command phase // Message out phase if ATN=1, otherwise command phase
if (bus->GetATN()) { if (bus.GetATN()) {
MsgOut(); MsgOut();
} else { } else {
Command(); Command();
@ -194,11 +194,11 @@ void ScsiController::Command()
SetPhase(BUS::phase_t::command); SetPhase(BUS::phase_t::command);
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(false); bus.SetIO(false);
int actual_count = bus->CommandHandShake(GetBuffer().data()); int actual_count = bus.CommandHandShake(GetBuffer().data());
int command_byte_count = GPIOBUS::GetCommandByteCount(GetBuffer()[0]); int command_byte_count = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
// If not able to receive all, move to the status phase // If not able to receive all, move to the status phase
@ -229,8 +229,6 @@ void ScsiController::Execute()
{ {
LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (int)GetOpcode()) LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (int)GetOpcode())
SetPhase(BUS::phase_t::execute);
// Initialization for data transfer // Initialization for data transfer
ResetOffset(); ResetOffset();
ctrl.blocks = 1; ctrl.blocks = 1;
@ -238,7 +236,7 @@ void ScsiController::Execute()
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE // Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if (GetOpcode() != scsi_command::eCmdRequestSense) { if (GetOpcode() != scsi_command::eCmdRequestSense) {
SetStatus(0); SetStatus(status::GOOD);
} }
int lun = GetEffectiveLun(); int lun = GetEffectiveLun();
@ -258,13 +256,13 @@ void ScsiController::Execute()
} }
} }
PrimaryDevice *device = GetDeviceForLun(lun); auto device = GetDeviceForLun(lun);
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE // Discard pending sense data from the previous command if the current command is not REQUEST SENSE
if (GetOpcode() != scsi_command::eCmdRequestSense) { if (GetOpcode() != scsi_command::eCmdRequestSense) {
device->SetStatusCode(0); device->SetStatusCode(0);
} }
try { try {
if (!device->Dispatch(GetOpcode())) { if (!device->Dispatch(GetOpcode())) {
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (int)GetOpcode()) LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (int)GetOpcode())
@ -298,14 +296,14 @@ void ScsiController::Status()
SysTimer::SleepUsec(5); SysTimer::SleepUsec(5);
} }
LOGTRACE("%s Status Phase $%02X",__PRETTY_FUNCTION__, GetStatus()) LOGTRACE("%s Status Phase, status is $%02X",__PRETTY_FUNCTION__, (int)GetStatus())
SetPhase(BUS::phase_t::status); SetPhase(BUS::phase_t::status);
// Signal line operated by the target // Signal line operated by the target
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(true); bus.SetIO(true);
// Data transfer is 1 byte x 1 block // Data transfer is 1 byte x 1 block
ResetOffset(); ResetOffset();
@ -326,9 +324,9 @@ void ScsiController::MsgIn()
SetPhase(BUS::phase_t::msgin); SetPhase(BUS::phase_t::msgin);
bus->SetMSG(true); bus.SetMSG(true);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(true); bus.SetIO(true);
// length, blocks are already set // length, blocks are already set
assert(HasValidLength()); assert(HasValidLength());
@ -357,9 +355,9 @@ void ScsiController::MsgOut()
SetPhase(BUS::phase_t::msgout); SetPhase(BUS::phase_t::msgout);
bus->SetMSG(true); bus.SetMSG(true);
bus->SetCD(true); bus.SetCD(true);
bus->SetIO(false); bus.SetIO(false);
// Data transfer is 1 byte x 1 block // Data transfer is 1 byte x 1 block
ResetOffset(); ResetOffset();
@ -390,9 +388,9 @@ void ScsiController::DataIn()
SetPhase(BUS::phase_t::datain); SetPhase(BUS::phase_t::datain);
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(false); bus.SetCD(false);
bus->SetIO(true); bus.SetIO(true);
// length, blocks are already set // length, blocks are already set
assert(ctrl.blocks > 0); assert(ctrl.blocks > 0);
@ -423,9 +421,9 @@ void ScsiController::DataOut()
SetPhase(BUS::phase_t::dataout); SetPhase(BUS::phase_t::dataout);
// Signal line operated by the target // Signal line operated by the target
bus->SetMSG(false); bus.SetMSG(false);
bus->SetCD(false); bus.SetCD(false);
bus->SetIO(false); bus.SetIO(false);
ResetOffset(); ResetOffset();
return; return;
@ -437,12 +435,12 @@ void ScsiController::DataOut()
void ScsiController::Error(sense_key sense_key, asc asc, status status) void ScsiController::Error(sense_key sense_key, asc asc, status status)
{ {
// Get bus information // Get bus information
bus->Acquire(); bus.Acquire();
// Reset check // Reset check
if (bus->GetRST()) { if (bus.GetRST()) {
Reset(); Reset();
bus->Reset(); bus.Reset();
return; return;
} }
@ -461,11 +459,14 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
} }
if (sense_key != sense_key::NO_SENSE || asc != asc::NO_ADDITIONAL_SENSE_INFORMATION) { if (sense_key != sense_key::NO_SENSE || asc != asc::NO_ADDITIONAL_SENSE_INFORMATION) {
// Set Sense Key and ASC for a subsequent REQUEST SENSE LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X, ASCQ $%02X",
(int)sense_key << 16, (int)asc << 8, (int)asc & 0xff)
// Set Sense Key and ASC for a subsequent REQUEST SENSE
GetDeviceForLun(lun)->SetStatusCode(((int)sense_key << 16) | ((int)asc << 8)); GetDeviceForLun(lun)->SetStatusCode(((int)sense_key << 16) | ((int)asc << 8));
} }
SetStatus((uint32_t)status); SetStatus(status);
ctrl.message = 0x00; ctrl.message = 0x00;
LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__) LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__)
@ -475,16 +476,16 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
void ScsiController::Send() void ScsiController::Send()
{ {
assert(!bus->GetREQ()); assert(!bus.GetREQ());
assert(bus->GetIO()); assert(bus.GetIO());
if (HasValidLength()) { if (HasValidLength()) {
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(ctrl.offset) + ", length " LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(GetOffset()) + ", length "
+ to_string(ctrl.length)).c_str()) + to_string(ctrl.length)).c_str())
// TODO The delay has to be taken from ctrl.unit[lun], but as there are currently no Daynaport drivers for // TODO The delay has to be taken from ctrl.unit[lun], but as there are currently no Daynaport drivers for
// LUNs other than 0 this work-around works. // LUNs other than 0 this work-around works.
if (int len = bus->SendHandShake(GetBuffer().data() + ctrl.offset, ctrl.length, if (int len = bus.SendHandShake(GetBuffer().data() + ctrl.offset, ctrl.length,
HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0); HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0);
len != (int)ctrl.length) { len != (int)ctrl.length) {
// If you cannot send all, move to status phase // If you cannot send all, move to status phase
@ -571,15 +572,15 @@ void ScsiController::Receive()
LOGTRACE("%s",__PRETTY_FUNCTION__) LOGTRACE("%s",__PRETTY_FUNCTION__)
// REQ is low // REQ is low
assert(!bus->GetREQ()); assert(!bus.GetREQ());
assert(!bus->GetIO()); assert(!bus.GetIO());
// Length != 0 if received // Length != 0 if received
if (HasValidLength()) { if (HasValidLength()) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length) LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
// If not able to receive all, move to status phase // If not able to receive all, move to status phase
if (int len = bus->ReceiveHandShake(GetBuffer().data() + ctrl.offset, ctrl.length); if (int len = bus.ReceiveHandShake(GetBuffer().data() + GetOffset(), ctrl.length);
len != (int)ctrl.length) { len != (int)ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d",__PRETTY_FUNCTION__, ctrl.length, len) LOGERROR("%s Not able to receive %d bytes of data, only received %d",__PRETTY_FUNCTION__, ctrl.length, len)
Error(sense_key::ABORTED_COMMAND); Error(sense_key::ABORTED_COMMAND);
@ -675,14 +676,14 @@ bool ScsiController::XferMsg(int msg)
void ScsiController::ReceiveBytes() void ScsiController::ReceiveBytes()
{ {
assert(!bus->GetREQ()); assert(!bus.GetREQ());
assert(!bus->GetIO()); assert(!bus.GetIO());
if (HasValidLength()) { if (HasValidLength()) {
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length) LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
// If not able to receive all, move to status phase // If not able to receive all, move to status phase
if (uint32_t len = bus->ReceiveHandShake(GetBuffer().data() + ctrl.offset, ctrl.length); if (uint32_t len = bus.ReceiveHandShake(GetBuffer().data() + GetOffset(), ctrl.length);
len != ctrl.length) { len != ctrl.length) {
LOGERROR("%s Not able to receive %d bytes of data, only received %d", LOGERROR("%s Not able to receive %d bytes of data, only received %d",
__PRETTY_FUNCTION__, ctrl.length, len) __PRETTY_FUNCTION__, ctrl.length, len)
@ -773,7 +774,7 @@ void ScsiController::FlushUnit()
{ {
assert(IsDataOut()); assert(IsDataOut());
auto disk = dynamic_cast<Disk *>(GetDeviceForLun(GetEffectiveLun())); auto disk = dynamic_pointer_cast<Disk>(GetDeviceForLun(GetEffectiveLun()));
if (disk == nullptr) { if (disk == nullptr) {
return; return;
} }
@ -795,7 +796,7 @@ void ScsiController::FlushUnit()
// Without it we would not need this method at all. // Without it we would not need this method at all.
// ModeSelect is already handled in XferOutBlockOriented(). Why would it have to be handled once more? // ModeSelect is already handled in XferOutBlockOriented(). Why would it have to be handled once more?
try { try {
disk->ModeSelect(ctrl.cmd, GetBuffer(), ctrl.offset); disk->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
} }
catch(const scsi_error_exception& e) { catch(const scsi_error_exception& e) {
LOGWARN("Error occured while processing Mode Select command %02X\n", (int)GetOpcode()) LOGWARN("Error occured while processing Mode Select command %02X\n", (int)GetOpcode())
@ -838,7 +839,7 @@ bool ScsiController::XferIn(vector<BYTE>& buf)
case scsi_command::eCmdRead16: case scsi_command::eCmdRead16:
// Read from disk // Read from disk
try { try {
ctrl.length = (static_cast<Disk *>(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next); ctrl.length = (dynamic_pointer_cast<Disk>(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next);
} }
catch(const scsi_error_exception&) { catch(const scsi_error_exception&) {
// If there is an error, go to the status phase // If there is an error, go to the status phase
@ -868,7 +869,7 @@ bool ScsiController::XferIn(vector<BYTE>& buf)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
bool ScsiController::XferOutBlockOriented(bool cont) bool ScsiController::XferOutBlockOriented(bool cont)
{ {
auto disk = dynamic_cast<Disk *>(GetDeviceForLun(GetEffectiveLun())); auto disk = dynamic_pointer_cast<Disk>(GetDeviceForLun(GetEffectiveLun()));
if (disk == nullptr) { if (disk == nullptr) {
return false; return false;
} }
@ -878,7 +879,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
case scsi_command::eCmdModeSelect6: case scsi_command::eCmdModeSelect6:
case scsi_command::eCmdModeSelect10: case scsi_command::eCmdModeSelect10:
try { try {
disk->ModeSelect(ctrl.cmd, GetBuffer(), ctrl.offset); disk->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
} }
catch(const scsi_error_exception& e) { catch(const scsi_error_exception& e) {
Error(e.get_sense_key(), e.get_asc(), e.get_status()); Error(e.get_sense_key(), e.get_asc(), e.get_status());
@ -895,7 +896,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
{ {
// Special case Write function for brige // Special case Write function for brige
// TODO This class must not know about SCSIBR // TODO This class must not know about SCSIBR
if (auto bridge = dynamic_cast<SCSIBR *>(disk); bridge) { if (auto bridge = dynamic_pointer_cast<SCSIBR>(disk); bridge) {
if (!bridge->WriteBytes(ctrl.cmd, GetBuffer(), ctrl.length)) { if (!bridge->WriteBytes(ctrl.cmd, GetBuffer(), ctrl.length)) {
// Write failed // Write failed
return false; return false;
@ -907,7 +908,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
// Special case Write function for DaynaPort // Special case Write function for DaynaPort
// TODO This class must not know about DaynaPort // TODO This class must not know about DaynaPort
if (auto daynaport = dynamic_cast<SCSIDaynaPort *>(disk); daynaport) { if (auto daynaport = dynamic_pointer_cast<SCSIDaynaPort>(disk); daynaport) {
daynaport->WriteBytes(ctrl.cmd, GetBuffer(), 0); daynaport->WriteBytes(ctrl.cmd, GetBuffer(), 0);
ResetOffset(); ResetOffset();
@ -961,9 +962,10 @@ void ScsiController::ProcessCommand()
uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]); uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
stringstream s; stringstream s;
s << setfill('0') << setw(2) << hex;
for (uint32_t i = 0; i < len; i++) { for (uint32_t i = 0; i < len; i++) {
ctrl.cmd[i] = GetBuffer()[i]; ctrl.cmd[i] = GetBuffer()[i];
s << setfill('0') << setw(2) << hex << ctrl.cmd[i]; s << ctrl.cmd[i];
} }
LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str()) LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str())
@ -1036,7 +1038,7 @@ void ScsiController::ParseMessage()
void ScsiController::ProcessMessage() void ScsiController::ProcessMessage()
{ {
// Continue message out phase as long as ATN keeps asserting // Continue message out phase as long as ATN keeps asserting
if (bus->GetATN()) { if (bus.GetATN()) {
// Data transfer is 1 byte x 1 block // Data transfer is 1 byte x 1 block
ResetOffset(); ResetOffset();
ctrl.length = 1; ctrl.length = 1;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -54,10 +54,8 @@ public:
// Maximum number of logical units // Maximum number of logical units
static const int LUN_MAX = 32; static const int LUN_MAX = 32;
ScsiController(shared_ptr<BUS>, int); ScsiController(BUS&, int);
~ScsiController() override = default; ~ScsiController() override = default;
ScsiController(ScsiController&) = delete;
ScsiController& operator=(const ScsiController&) = delete;
void Reset() override; void Reset() override;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -22,8 +22,6 @@ public:
CDTrack() = default; CDTrack() = default;
~CDTrack() = default; ~CDTrack() = default;
CDTrack(CDTrack&) = delete;
CDTrack& operator=(const CDTrack&) = delete;
void Init(int track, DWORD first, DWORD last); void Init(int track, DWORD first, DWORD last);

View File

@ -2381,18 +2381,16 @@ bool CHostFcb::Truncate() const
/// Return -1 if error is thrown. /// Return -1 if error is thrown.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek) DWORD CHostFcb::Seek(DWORD nOffset, Human68k::seek_t nHumanSeek)
{ {
assert(nHumanSeek == Human68k::SK_BEGIN ||
nHumanSeek == Human68k::SK_CURRENT || nHumanSeek == Human68k::SK_END);
assert(m_pFile); assert(m_pFile);
int nSeek; int nSeek;
switch (nHumanSeek) { switch (nHumanSeek) {
case Human68k::SK_BEGIN: case Human68k::seek_t::SK_BEGIN:
nSeek = SEEK_SET; nSeek = SEEK_SET;
break; break;
case Human68k::SK_CURRENT: case Human68k::seek_t::SK_CURRENT:
nSeek = SEEK_CUR; nSeek = SEEK_CUR;
break; break;
// case SK_END: // case SK_END:
@ -3356,13 +3354,13 @@ int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset)
return FS_NOTOPENED; return FS_NOTOPENED;
// Parameter check // Parameter check
if (nSeek > Human68k::SK_END) { if (nSeek > (DWORD)Human68k::seek_t::SK_END) {
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_INVALIDPRM; return FS_INVALIDPRM;
} }
// File seek // File seek
DWORD nResult = pHostFcb->Seek(nOffset, nSeek); DWORD nResult = pHostFcb->Seek(nOffset, (Human68k::seek_t)nSeek);
if (nResult == (DWORD)-1) { if (nResult == (DWORD)-1) {
m_cFcb.Free(pHostFcb); m_cFcb.Free(pHostFcb);
return FS_CANTSEEK; return FS_CANTSEEK;

View File

@ -92,39 +92,14 @@ namespace Human68k {
}; };
/// Seek types /// Seek types
enum seek_t { enum class seek_t {
SK_BEGIN = 0, ///< From the beginning of a file SK_BEGIN = 0, ///< From the beginning of a file
SK_CURRENT = 1, ///< From the current location SK_CURRENT = 1, ///< From the current location
SK_END = 2, ///< From the end of the file SK_END = 2, ///< From the end of the file
}; };
/// Media byte // Media byte
enum media_t { const static int MEDIA_REMOTE = 0xF3; ///< Remote drive
MEDIA_2DD_10 = 0xE0, ///< 2DD/10 sector
MEDIA_1D_9 = 0xE5, ///< 1D/9 sector
MEDIA_2D_9 = 0xE6, ///< 2D/9 sector
MEDIA_1D_8 = 0xE7, ///< 1D/8 sector
MEDIA_2D_8 = 0xE8, ///< 2D/8 sector
MEDIA_2HT = 0xEA, ///< 2HT
MEDIA_2HS = 0xEB, ///< 2HS
MEDIA_2HDE = 0xEC, ///< 2DDE
MEDIA_1DD_9 = 0xEE, ///< 1DD/9 sector
MEDIA_1DD_8 = 0xEF, ///< 1DD/8 sector
MEDIA_MANUAL = 0xF1, ///< Remote drive (manual eject)
MEDIA_REMOVABLE = 0xF2, ///< Remote drive (removable)
MEDIA_REMOTE = 0xF3, ///< Remote drive
MEDIA_DAT = 0xF4, ///< SCSI-DAT
MEDIA_CDROM = 0xF5, ///< SCSI-CDROM
MEDIA_MO = 0xF6, ///< SCSI-MO
MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD
MEDIA_SASI_HD = 0xF8, ///< SASI-HD
MEDIA_RAMDISK = 0xF9, ///< RAM disk
MEDIA_2HQ = 0xFA, ///< 2HQ
MEDIA_2DD_8 = 0xFB, ///< 2DD/8 sector
MEDIA_2DD_9 = 0xFC, ///< 2DD/9 sector
MEDIA_2HC = 0xFD, ///< 2HC
MEDIA_2HD = 0xFE, ///< 2HD
};
struct namests_t { struct namests_t {
BYTE wildcard; ///< Wildcard character length BYTE wildcard; ///< Wildcard character length
@ -220,7 +195,7 @@ namespace Human68k {
/// Command line parameter struct /// Command line parameter struct
/** /**
The driver itself is included in the beginning of the argument, The driver itself is included in the beginning of the argument,
so setting to a length longer than HUMAN68K_PATH_MAX so setting to a length longer than HUMAN68K_PATH_MAX
*/ */
struct argument_t { struct argument_t {
@ -257,9 +232,9 @@ static const int XM6_HOST_PSEUDO_CLUSTER_MAX = 10;
/// Number of caches for directory entries /// Number of caches for directory entries
/** /**
Human68k carries out a large number of checks of directory entries when doing an operation Human68k carries out a large number of checks of directory entries when doing an operation
inside a subdirectory. This specifies the number of caches used to speed up this operation. inside a subdirectory. This specifies the number of caches used to speed up this operation.
Cache is allocated per drive. The more you add the faster it gets, but use too many Cache is allocated per drive. The more you add the faster it gets, but use too many
and the host OS gets under a heavy load, so be careful. and the host OS gets under a heavy load, so be careful.
Default is 16. Default is 16.
@ -268,10 +243,10 @@ static const int XM6_HOST_DIRENTRY_CACHE_MAX = 16;
/// Max number of entries that can be stored per directory /// Max number of entries that can be stored per directory
/** /**
When a large number of files are stored in a directory, a larger amount of data than When a large number of files are stored in a directory, a larger amount of data than
contemporanous applications can handle will be returned. This may lead to errors such as contemporanous applications can handle will be returned. This may lead to errors such as
partial data being recognized, performance dropping significantly, or OOM crashes. partial data being recognized, performance dropping significantly, or OOM crashes.
To guard against this, an upper limit is defined here. In the case of a particular To guard against this, an upper limit is defined here. In the case of a particular
file manager, the upper limit is 2560 files. This is one good example to use as reference. file manager, the upper limit is 2560 files. This is one good example to use as reference.
Default is around 60000 entries. (Upper limit of the FAT root directory) Default is around 60000 entries. (Upper limit of the FAT root directory)
@ -280,17 +255,17 @@ static const int XM6_HOST_DIRENTRY_FILE_MAX = 65535;
/// Max number of patterns for file name deduplication /// Max number of patterns for file name deduplication
/** /**
The file names on the Human68k side are automatically created based on the file system on The file names on the Human68k side are automatically created based on the file system on
the host side. However, Human68k have stricter file name length restrictions than the host has. the host side. However, Human68k have stricter file name length restrictions than the host has.
Because of this, there is a risk that file name duplication will occur. When this happens, Because of this, there is a risk that file name duplication will occur. When this happens,
WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve
the duplication. Theoretically, there are over 60 million (36^5) unique file names that the duplication. Theoretically, there are over 60 million (36^5) unique file names that
can be generated by this method. However, in reality any more than a few hundred can be generated by this method. However, in reality any more than a few hundred
deduplications will take excessive processing time. So here an upper limit to deduplication deduplications will take excessive processing time. So here an upper limit to deduplication
is set in order to maintain system performance. If a system is operated with common sense, is set in order to maintain system performance. If a system is operated with common sense,
you should only need a few dozen deduplication patterns, so this value can be kept low you should only need a few dozen deduplication patterns, so this value can be kept low
to further improve performance. In the case deduplication is not carried out, multiple files to further improve performance. In the case deduplication is not carried out, multiple files
with the same name will be created. When trying to access such files, with the same name will be created. When trying to access such files,
only the first entry will ever be accessed. only the first entry will ever be accessed.
Default is 36 patterns. Default is 36 patterns.
@ -338,11 +313,7 @@ Normal is 0. Becomes 1 if attempting to mount in read-only mode.
Reserving the other values for future use. Reserving the other values for future use.
Insurance against hard-to-detect devices such as homemade USB storage. Insurance against hard-to-detect devices such as homemade USB storage.
*/ */
enum { static const DWORD FSFLAG_WRITE_PROTECT = 0x00000001; ///< Bit0: Force write protect
FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: Force write protect
FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: Force removable media
FSFLAG_MANUAL = 0x00000004, ///< Bit2: Force manual eject
};
//=========================================================================== //===========================================================================
// //
@ -357,8 +328,8 @@ class CRing {
public: public:
CRing() { Init(); } CRing() { Init(); }
~CRing() { Remove(); } ~CRing() { Remove(); }
CRing(CRing&) = delete; CRing(CRing&) = default;
CRing& operator=(const CRing&) = delete; CRing& operator=(const CRing&) = default;
void Init() { next = prev = this; } void Init() { next = prev = this; }
@ -442,8 +413,6 @@ class CHostFilename {
public: public:
CHostFilename() = default; CHostFilename() = default;
~CHostFilename() = default; ~CHostFilename() = default;
CHostFilename(CHostFilename&) = delete;
CHostFilename& operator=(const CHostFilename&) = delete;
static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< Get offset location static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< Get offset location
@ -468,7 +437,7 @@ public:
{ m_dirHuman.time = nHumanTime; } ///< Set Human68k directory entry { m_dirHuman.time = nHumanTime; } ///< Set Human68k directory entry
void SetEntryCluster(WORD nHumanCluster) void SetEntryCluster(WORD nHumanCluster)
{ m_dirHuman.cluster = nHumanCluster; } ///< Set Human68k directory entry { m_dirHuman.cluster = nHumanCluster; } ///< Set Human68k directory entry
const Human68k::dirent_t* GetEntry() const const Human68k::dirent_t* GetEntry() const
{ return &m_dirHuman; } ///< Get Human68k directory entry { return &m_dirHuman; } ///< Get Human68k directory entry
int CheckAttribute(DWORD nHumanAttribute) const; ///< Determine Human68k directory entry attributes int CheckAttribute(DWORD nHumanAttribute) const; ///< Determine Human68k directory entry attributes
bool isSameEntry(const Human68k::dirent_t* pdirHuman) const bool isSameEntry(const Human68k::dirent_t* pdirHuman) const
@ -505,7 +474,7 @@ get updated for new directories created as a result of file operations, which
triggers updates to directory entires. triggers updates to directory entires.
However, on the host file system, new directories do typically get an updated time stamp. However, on the host file system, new directories do typically get an updated time stamp.
The unfortunate outcome is that when copying a directory for instance, the time stamp The unfortunate outcome is that when copying a directory for instance, the time stamp
will get overwritten even if the application did not intend for the time stamp to get updated. will get overwritten even if the application did not intend for the time stamp to get updated.
Here follows an implementation of a directory cache FAT time stamp emulation feature. Here follows an implementation of a directory cache FAT time stamp emulation feature.
@ -532,8 +501,8 @@ public:
CHostPath() = default; CHostPath() = default;
~CHostPath(); ~CHostPath();
CHostPath(CHostPath&) = delete; CHostPath(CHostPath&) = default;
CHostPath& operator=(const CHostPath&) = delete; CHostPath& operator=(const CHostPath&) = default;
void Clean(); ///< Initialialize for reuse void Clean(); ///< Initialialize for reuse
@ -575,9 +544,9 @@ private:
// //
/// File search processing /// File search processing
/// ///
/// It's pretty much impossible to process Human68k file names as Unicode internally. /// It's pretty much impossible to process Human68k file names as Unicode internally.
/// So, we carry out binary conversion for processing. We leave it up to the /// So, we carry out binary conversion for processing. We leave it up to the
/// directory entry cache to handle the conversion, which allows WINDRV to read /// directory entry cache to handle the conversion, which allows WINDRV to read
/// everything as Shift-JIS. Additionally, it allows Human68k names to be /// everything as Shift-JIS. Additionally, it allows Human68k names to be
/// fully independent of base path assignments. /// fully independent of base path assignments.
/// ///
@ -596,8 +565,6 @@ class CHostFiles {
public: public:
CHostFiles() = default; CHostFiles() = default;
~CHostFiles() = default; ~CHostFiles() = default;
CHostFiles(CHostFiles&) = delete;
CHostFiles& operator=(const CHostFiles&) = delete;
void Init(); void Init();
@ -675,8 +642,8 @@ class CHostFcb {
public: public:
CHostFcb() = default; CHostFcb() = default;
~CHostFcb() { Close(); } ~CHostFcb() { Close(); }
CHostFcb(CHostFcb&) = delete; CHostFcb(CHostFcb&) = default;
CHostFcb& operator=(const CHostFcb&) = delete; CHostFcb& operator=(const CHostFcb&) = default;
void Init(); void Init();
@ -694,7 +661,7 @@ public:
DWORD Read(BYTE* pBuffer, DWORD nSize); ///< Read file DWORD Read(BYTE* pBuffer, DWORD nSize); ///< Read file
DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< Write file DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< Write file
bool Truncate() const; ///< Truncate file bool Truncate() const; ///< Truncate file
DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< Seek file DWORD Seek(DWORD nOffset, Human68k::seek_t nHumanSeek); ///< Seek file
bool TimeStamp(DWORD nHumanTime) const; ///< Set file time stamp bool TimeStamp(DWORD nHumanTime) const; ///< Set file time stamp
void Close(); ///< Close file void Close(); ///< Close file
@ -747,8 +714,8 @@ class CHostDrv
public: public:
CHostDrv() = default; CHostDrv() = default;
~CHostDrv(); ~CHostDrv();
CHostDrv(CHostDrv&) = delete; CHostDrv(CHostDrv&) = default;
CHostDrv& operator=(const CHostDrv&) = delete; CHostDrv& operator=(const CHostDrv&) = default;
void Init(const TCHAR* szBase, DWORD nFlag); ///< Initialization (device startup and load) void Init(const TCHAR* szBase, DWORD nFlag); ///< Initialization (device startup and load)
@ -811,8 +778,8 @@ public:
CHostEntry() = default; CHostEntry() = default;
~CHostEntry(); ~CHostEntry();
CHostEntry(CHostEntry&) = delete; CHostEntry(CHostEntry&) = default;
CHostEntry& operator=(const CHostEntry&) = delete; CHostEntry& operator=(const CHostEntry&) = default;
void Init() const; ///< Initialization (when the driver is installed) void Init() const; ///< Initialization (when the driver is installed)
void Clean(); ///< Release (when starting up or resetting) void Clean(); ///< Release (when starting up or resetting)
@ -854,13 +821,13 @@ private:
/** @note /** @note
Current state of affairs: Current state of affairs:
While it violates the design philosophy of XM6, we should find a way for While it violates the design philosophy of XM6, we should find a way for
'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'. 'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'.
This way, we get the following benefits. This way, we get the following benefits.
Benefit no. 1 Benefit no. 1
Makes it possible to manage a large number of command handler methods in one place. Makes it possible to manage a large number of command handler methods in one place.
There is a high chance the command handlers will change drastically because of There is a high chance the command handlers will change drastically because of
host system architectural changes, so we will save a huge amount of maintenance work host system architectural changes, so we will save a huge amount of maintenance work
in the long run. in the long run.
@ -870,7 +837,7 @@ It is not feasible to implement code in XM6 for simultaneous use of file system
Therefore file system object polymorphism is a waste of CPU cycles. Therefore file system object polymorphism is a waste of CPU cycles.
I made the change as an experiment. Performance did improve. I made the change as an experiment. Performance did improve.
The improvement was obvious from looking at the source the compiler spit out The improvement was obvious from looking at the source the compiler spit out
after changing the FILESYS_FAST_STRUCTURE value in windrv.h. after changing the FILESYS_FAST_STRUCTURE value in windrv.h.
You may understand now why I decided to rant here. You may understand now why I decided to rant here.

View File

@ -37,7 +37,7 @@ using namespace ras_util;
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) { static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) {
#ifndef __linux #ifndef __linux__
return false; return false;
#else #else
ifreq ifr; ifreq ifr;
@ -83,7 +83,7 @@ CTapDriver::~CTapDriver()
} }
static bool ip_link(int fd, const char* ifname, bool up) { static bool ip_link(int fd, const char* ifname, bool up) {
#ifndef __linux #ifndef __linux__
return false; return false;
#else #else
ifreq ifr; ifreq ifr;
@ -126,7 +126,7 @@ static bool is_interface_up(string_view interface) {
bool CTapDriver::Init(const unordered_map<string, string>& const_params) bool CTapDriver::Init(const unordered_map<string, string>& const_params)
{ {
#ifndef __linux #ifndef __linux__
return false; return false;
#else #else
unordered_map<string, string> params = const_params; unordered_map<string, string> params = const_params;
@ -158,7 +158,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
} }
LOGTRACE("Opened tap device %d", m_hTAP) LOGTRACE("Opened tap device %d", m_hTAP)
// IFF_NO_PI for no extra packet information // IFF_NO_PI for no extra packet information
ifreq ifr = {}; ifreq ifr = {};
ifr.ifr_flags = IFF_TAP | IFF_NO_PI; ifr.ifr_flags = IFF_TAP | IFF_NO_PI;

View File

@ -19,7 +19,7 @@
#include <string> #include <string>
#include <array> #include <array>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class CTapDriver class CTapDriver
{ {
@ -30,8 +30,8 @@ class CTapDriver
CTapDriver() = default; CTapDriver() = default;
~CTapDriver(); ~CTapDriver();
CTapDriver(CTapDriver&) = delete; CTapDriver(CTapDriver&) = default;
CTapDriver& operator=(const CTapDriver&) = delete; CTapDriver& operator=(const CTapDriver&) = default;
bool Init(const unordered_map<string, string>&); bool Init(const unordered_map<string, string>&);

View File

@ -7,17 +7,16 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <cassert>
#include "rascsi_version.h" #include "rascsi_version.h"
#include "log.h" #include "log.h"
#include "rascsi_exceptions.h"
#include "device.h" #include "device.h"
#include <cassert>
#include <sstream> #include <sstream>
#include <iomanip> #include <iomanip>
using namespace std; using namespace std;
Device::Device(const string& t) : type(t) Device::Device(const string& type, int lun) : type(type), lun(lun)
{ {
assert(type.length() == 4); assert(type.length() == 4);
@ -43,7 +42,7 @@ void Device::SetProtected(bool b)
void Device::SetVendor(const string& v) void Device::SetVendor(const string& v)
{ {
if (v.empty() || v.length() > 8) { if (v.empty() || v.length() > 8) {
throw illegal_argument_exception("Vendor '" + v + "' must be between 1 and 8 characters"); throw invalid_argument("Vendor '" + v + "' must be between 1 and 8 characters");
} }
vendor = v; vendor = v;
@ -52,10 +51,10 @@ void Device::SetVendor(const string& v)
void Device::SetProduct(const string& p, bool force) void Device::SetProduct(const string& p, bool force)
{ {
if (p.empty() || p.length() > 16) { if (p.empty() || p.length() > 16) {
throw illegal_argument_exception("Product '" + p + "' must be between 1 and 16 characters"); throw invalid_argument("Product '" + p + "' must be between 1 and 16 characters");
} }
// Changing the device name is not SCSI compliant // Changing vital product data is not SCSI compliant
if (!product.empty() && !force) { if (!product.empty() && !force) {
return; return;
} }
@ -66,7 +65,7 @@ void Device::SetProduct(const string& p, bool force)
void Device::SetRevision(const string& r) void Device::SetRevision(const string& r)
{ {
if (r.empty() || r.length() > 4) { if (r.empty() || r.length() > 4) {
throw illegal_argument_exception("Revision '" + r + "' must be between 1 and 4 characters"); throw invalid_argument("Revision '" + r + "' must be between 1 and 4 characters");
} }
revision = r; revision = r;
@ -74,13 +73,10 @@ void Device::SetRevision(const string& r)
string Device::GetPaddedName() const string Device::GetPaddedName() const
{ {
string name = vendor; ostringstream os;
name.append(8 - vendor.length(), ' '); os << left << setfill(' ') << setw(8) << vendor << setw(16) << product << setw(4) << revision;
name += product;
name.append(16 - product.length(), ' ');
name += revision;
name.append(4 - revision.length(), ' ');
string name = os.str();
assert(name.length() == 28); assert(name.length() == 28);
return name; return name;
@ -107,15 +103,6 @@ void Device::SetParams(const unordered_map<string, string>& set_params)
} }
} }
void Device::SetStatusCode(int s)
{
if (s) {
LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X, ASCQ $%02X", s >> 16, (s >> 8 &0xff), s & 0xff)
}
status_code = s;
}
bool Device::Start() bool Device::Start()
{ {
if (!ready) { if (!ready) {

View File

@ -9,16 +9,13 @@
#pragma once #pragma once
#include "scsi.h"
#include <unordered_map> #include <unordered_map>
#include <string> #include <string>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class Device class Device //NOSONAR The number of fields and methods is justified, the complexity is low
{ {
friend class DeviceFactory;
const string DEFAULT_VENDOR = "RaSCSI"; const string DEFAULT_VENDOR = "RaSCSI";
string type; string type;
@ -45,15 +42,11 @@ class Device
bool lockable = false; bool lockable = false;
bool locked = false; bool locked = false;
// The block size is configurable
bool block_size_configurable = false;
// Device can be created with parameters // Device can be created with parameters
bool supports_params = false; bool supports_params = false;
// Device ID and LUN // Immutable LUN
int32_t id = 0; int lun;
int32_t lun = 0;
// Device identifier (for INQUIRY) // Device identifier (for INQUIRY)
string vendor = DEFAULT_VENDOR; string vendor = DEFAULT_VENDOR;
@ -86,21 +79,16 @@ protected:
string GetParam(const string&) const; string GetParam(const string&) const;
void SetParams(const unordered_map<string, string>&); void SetParams(const unordered_map<string, string>&);
explicit Device(const string&); Device(const string&, int);
public: public:
virtual ~Device() = default; virtual ~Device() = default;
Device(Device&) = delete;
Device& operator=(const Device&) = delete;
// Override for device specific initializations, to be called after all device properties have been set
virtual bool Init(const unordered_map<string, string>&) { return true; };
const string& GetType() const { return type; } const string& GetType() const { return type; }
bool IsReady() const { return ready; } bool IsReady() const { return ready; }
void Reset(); virtual void Reset();
bool IsProtectable() const { return protectable; } bool IsProtectable() const { return protectable; }
void SetProtectable(bool b) { protectable = b; } void SetProtectable(bool b) { protectable = b; }
@ -123,10 +111,8 @@ public:
bool IsLocked() const { return locked; } bool IsLocked() const { return locked; }
void SetLocked(bool b) { locked = b; } void SetLocked(bool b) { locked = b; }
int32_t GetId() const { return id; } virtual int GetId() const = 0;
void SetId(int32_t i) { id = i; } int GetLun() const { return lun; }
int32_t GetLun() const { return lun; }
void SetLun(int32_t l) { lun = l; }
string GetVendor() const { return vendor; } string GetVendor() const { return vendor; }
void SetVendor(const string&); void SetVendor(const string&);
@ -142,12 +128,9 @@ public:
unordered_map<string, string> GetParams() const { return params; } unordered_map<string, string> GetParams() const { return params; }
void SetDefaultParams(const unordered_map<string, string>& p) { default_params = p; } void SetDefaultParams(const unordered_map<string, string>& p) { default_params = p; }
void SetStatusCode(int); void SetStatusCode(int s) { status_code = s; }
bool Start(); bool Start();
void Stop(); void Stop();
virtual bool Eject(bool); virtual bool Eject(bool);
virtual void FlushCache() {
// Devices with a cache have to implement this method
}
}; };

View File

@ -25,8 +25,6 @@
using namespace std; using namespace std;
using namespace rascsi_interface; using namespace rascsi_interface;
multimap<int, unique_ptr<PrimaryDevice>> DeviceFactory::devices;
DeviceFactory::DeviceFactory() DeviceFactory::DeviceFactory()
{ {
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 }; sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
@ -60,45 +58,6 @@ DeviceFactory::DeviceFactory()
extension_mapping["iso"] = SCCD; extension_mapping["iso"] = SCCD;
} }
void DeviceFactory::DeleteDevice(const PrimaryDevice& device) const
{
auto [begin, end] = devices.equal_range(device.GetId());
for (auto& it = begin; it != end; ++it) {
if (it->second->GetLun() == device.GetLun()) {
devices.erase(it);
break;
}
}
}
void DeviceFactory::DeleteAllDevices() const
{
devices.clear();
}
const PrimaryDevice *DeviceFactory::GetDeviceByIdAndLun(int i, int lun) const
{
for (const auto& [id, device] : devices) {
if (device->GetId() == i && device->GetLun() == lun) {
return device.get();
}
}
return nullptr;
}
list<PrimaryDevice *> DeviceFactory::GetAllDevices() const
{
list<PrimaryDevice *> result;
for (const auto& [id, device] : devices) {
result.push_back(device.get());
}
return result;
}
string DeviceFactory::GetExtension(const string& filename) const string DeviceFactory::GetExtension(const string& filename) const
{ {
string ext; string ext;
@ -132,7 +91,8 @@ PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
} }
// ID -1 is used by rascsi to create a temporary device // ID -1 is used by rascsi to create a temporary device
PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, int id) shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& controller_manager, PbDeviceType type,
int lun, const string& filename)
{ {
// If no type was specified try to derive the device type from the filename // If no type was specified try to derive the device type from the filename
if (type == UNDEFINED) { if (type == UNDEFINED) {
@ -142,13 +102,14 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
} }
} }
unique_ptr<PrimaryDevice> device; shared_ptr<PrimaryDevice> device;
switch (type) { switch (type) {
case SCHD: { case SCHD: {
if (string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") { if (string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
device = make_unique<SCSIHD_NEC>(); device = make_shared<SCSIHD_NEC>(lun);
} else { } else {
device = make_unique<SCSIHD>(sector_sizes[SCHD], false, ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2); device = make_shared<SCSIHD>(lun, sector_sizes[SCHD], false,
ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
// Some Apple tools require a particular drive identification // Some Apple tools require a particular drive identification
if (ext == "hda") { if (ext == "hda") {
@ -162,7 +123,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
} }
case SCRM: case SCRM:
device = make_unique<SCSIHD>(sector_sizes[SCRM], true); device = make_shared<SCSIHD>(lun, sector_sizes[SCRM], true);
device->SetProtectable(true); device->SetProtectable(true);
device->SetStoppable(true); device->SetStoppable(true);
device->SetRemovable(true); device->SetRemovable(true);
@ -171,7 +132,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCMO: case SCMO:
device = make_unique<SCSIMO>(sector_sizes[SCMO]); device = make_shared<SCSIMO>(lun, sector_sizes[SCMO]);
device->SetProtectable(true); device->SetProtectable(true);
device->SetStoppable(true); device->SetStoppable(true);
device->SetRemovable(true); device->SetRemovable(true);
@ -180,7 +141,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCCD: case SCCD:
device = make_unique<SCSICD>(sector_sizes[SCCD]); device = make_shared<SCSICD>(lun, sector_sizes[SCCD]);
device->SetReadOnly(true); device->SetReadOnly(true);
device->SetStoppable(true); device->SetStoppable(true);
device->SetRemovable(true); device->SetRemovable(true);
@ -189,14 +150,15 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCBR: case SCBR:
device = make_unique<SCSIBR>(); device = make_shared<SCSIBR>(lun);
// Since this is an emulation for a specific driver the product name has to be set accordingly
device->SetProduct("RASCSI BRIDGE"); device->SetProduct("RASCSI BRIDGE");
device->SupportsParams(true); device->SupportsParams(true);
device->SetDefaultParams(default_params[SCBR]); device->SetDefaultParams(default_params[SCBR]);
break; break;
case SCDP: case SCDP:
device = make_unique<SCSIDaynaPort>(); device = make_shared<SCSIDaynaPort>(lun);
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly // Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("Dayna"); device->SetVendor("Dayna");
device->SetProduct("SCSI/Link"); device->SetProduct("SCSI/Link");
@ -206,14 +168,14 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
case SCHS: case SCHS:
device = make_unique<HostServices>(*this); device = make_shared<HostServices>(lun, controller_manager);
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly // Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
device->SetVendor("RaSCSI"); device->SetVendor("RaSCSI");
device->SetProduct("Host Services"); device->SetProduct("Host Services");
break; break;
case SCLP: case SCLP:
device = make_unique<SCSIPrinter>(); device = make_shared<SCSIPrinter>(lun);
device->SetProduct("SCSI PRINTER"); device->SetProduct("SCSI PRINTER");
device->SupportsParams(true); device->SupportsParams(true);
device->SetDefaultParams(default_params[SCLP]); device->SetDefaultParams(default_params[SCLP]);
@ -223,17 +185,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
break; break;
} }
if (device != nullptr) { return device;
PrimaryDevice *d = device.release();
d->SetId(id);
devices.emplace(id, d);
return d;
}
return nullptr;
} }
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type) const const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type) const
@ -260,7 +212,7 @@ list<string> DeviceFactory::GetNetworkInterfaces() const
{ {
list<string> network_interfaces; list<string> network_interfaces;
#ifdef __linux #ifdef __linux__
ifaddrs *addrs; ifaddrs *addrs;
getifaddrs(&addrs); getifaddrs(&addrs);
ifaddrs *tmp = addrs; ifaddrs *tmp = addrs;

View File

@ -18,9 +18,10 @@
#include <string> #include <string>
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi using namespace rascsi_interface;
class ControllerManager;
class PrimaryDevice; class PrimaryDevice;
class DeviceFactory class DeviceFactory
@ -29,14 +30,8 @@ public:
DeviceFactory(); DeviceFactory();
~DeviceFactory() = default; ~DeviceFactory() = default;
DeviceFactory(DeviceFactory&) = delete;
DeviceFactory& operator=(const DeviceFactory&) = delete;
PrimaryDevice *CreateDevice(PbDeviceType, const string&, int); shared_ptr<PrimaryDevice> CreateDevice(const ControllerManager&, PbDeviceType, int, const string&);
void DeleteDevice(const PrimaryDevice&) const;
void DeleteAllDevices() const;
const PrimaryDevice *GetDeviceByIdAndLun(int, int) const;
list<PrimaryDevice *> GetAllDevices() const;
PbDeviceType GetTypeForFile(const string&) const; PbDeviceType GetTypeForFile(const string&) const;
const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) const; const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) const;
const unordered_set<uint32_t>& GetSectorSizes(const string&) const; const unordered_set<uint32_t>& GetSectorSizes(const string&) const;
@ -54,8 +49,6 @@ private:
string GetExtension(const string&) const; string GetExtension(const string&) const;
static std::multimap<int, unique_ptr<PrimaryDevice>> devices;
unordered_set<uint32_t> empty_set; unordered_set<uint32_t> empty_set;
unordered_map<string, string> empty_map; unordered_map<string, string> empty_map;
}; };

View File

@ -25,7 +25,7 @@
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
Disk::Disk(const string& id) : ModePageDevice(id) Disk::Disk(const string& type, int lun) : ModePageDevice(type, lun)
{ {
dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero); dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero);
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit); dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
@ -88,7 +88,9 @@ bool Disk::Dispatch(scsi_command cmd)
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void Disk::Open(const Filepath& path) void Disk::Open(const Filepath& path)
{ {
assert(blocks > 0); if (blocks == 0) {
throw io_exception("Disk has 0 blocks");
}
SetReady(true); SetReady(true);
@ -108,10 +110,17 @@ void Disk::Open(const Filepath& path)
SetLocked(false); SetLocked(false);
} }
void Disk::SetUpCache(const Filepath& path, off_t image_offset) void Disk::SetUpCache(const Filepath& path, off_t image_offset, bool raw)
{ {
assert(cache == nullptr); assert(cache == nullptr);
cache = make_unique<DiskCache>(path, size_shift_count, (uint32_t)blocks, image_offset); cache = make_unique<DiskCache>(path, size_shift_count, (uint32_t)blocks, image_offset);
cache->SetRawMode(raw);
}
void Disk::ResizeCache(const Filepath& path, bool raw)
{
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)blocks));
cache->SetRawMode(raw);
} }
void Disk::FlushCache() void Disk::FlushCache()
@ -266,10 +275,10 @@ void Disk::StartStopUnit()
bool load = ctrl->cmd[4] & 0x02; bool load = ctrl->cmd[4] & 0x02;
if (load) { if (load) {
LOGTRACE("%s", start ? "Loading medium" : "Ejecting medium") LOGTRACE(start ? "Loading medium" : "Ejecting medium")
} }
else { else {
LOGTRACE("%s", start ? "Starting unit" : "Stopping unit") LOGTRACE(start ? "Starting unit" : "Stopping unit")
SetStopped(!start); SetStopped(!start);
} }
@ -315,7 +324,7 @@ void Disk::PreventAllowMediumRemoval()
bool lock = ctrl->cmd[4] & 0x01; bool lock = ctrl->cmd[4] & 0x01;
LOGTRACE("%s", lock ? "Locking medium" : "Unlocking medium") LOGTRACE(lock ? "Locking medium" : "Unlocking medium")
SetLocked(lock); SetLocked(lock);
@ -346,7 +355,7 @@ void Disk::MediumChanged()
is_medium_changed = true; is_medium_changed = true;
} }
else { else {
LOGWARN("%s Medium change requested for non-reomvable medium", __PRETTY_FUNCTION__) LOGERROR("Medium change requested for non-removable medium")
} }
} }
@ -366,10 +375,10 @@ bool Disk::Eject(bool force)
return status; return status;
} }
int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Get length, clear buffer // Get length, clear buffer
auto length = (int)min((size_t)max_length, (size_t)cdb[4]); auto length = (int)min(buf.size(), (size_t)cdb[4]);
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER // DEVICE SPECIFIC PARAMETER
@ -378,7 +387,7 @@ int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
} }
// Basic information // Basic information
int info_size = 4; int size = 4;
// Add block descriptor if DBD is 0 // Add block descriptor if DBD is 0
if ((cdb[1] & 0x08) == 0) { if ((cdb[1] & 0x08) == 0) {
@ -388,33 +397,33 @@ int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
// Only if ready // Only if ready
if (IsReady()) { if (IsReady()) {
// Short LBA mode parameter block descriptor (number of blocks and block length) // Short LBA mode parameter block descriptor (number of blocks and block length)
SetInt32(buf, 4, (uint32_t)GetBlockCount()); SetInt32(buf, 4, (uint32_t)blocks);
SetInt32(buf, 8, GetSectorSizeInBytes()); SetInt32(buf, 8, GetSectorSizeInBytes());
} }
info_size = 12; size = 12;
} }
info_size += super::AddModePages(cdb, buf, info_size, length - info_size); size += super::AddModePages(cdb, buf, size, length - size);
if (info_size > 255) { if (size > 255) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Do not return more than ALLOCATION LENGTH bytes // Do not return more than ALLOCATION LENGTH bytes
if (info_size > length) { if (size > length) {
info_size = length; size = length;
} }
// Final setting of mode data length // Final setting of mode data length
buf[0] = (BYTE)info_size; buf[0] = (BYTE)size;
return info_size; return size;
} }
int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Get length, clear buffer // Get length, clear buffer
auto length = (int)min((size_t)max_length, (size_t)GetInt16(cdb, 7)); auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// DEVICE SPECIFIC PARAMETER // DEVICE SPECIFIC PARAMETER
@ -423,11 +432,11 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
} }
// Basic Information // Basic Information
int info_size = 8; int size = 8;
// Add block descriptor if DBD is 0, only if ready // Add block descriptor if DBD is 0, only if ready
if ((cdb[1] & 0x08) == 0 && IsReady()) { if ((cdb[1] & 0x08) == 0 && IsReady()) {
uint64_t disk_blocks = GetBlockCount(); uint64_t disk_blocks = blocks;
uint32_t disk_size = GetSectorSizeInBytes(); uint32_t disk_size = GetSectorSizeInBytes();
// Check LLBAA for short or long block descriptor // Check LLBAA for short or long block descriptor
@ -439,7 +448,7 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
SetInt32(buf, 8, (uint32_t)disk_blocks); SetInt32(buf, 8, (uint32_t)disk_blocks);
SetInt32(buf, 12, disk_size); SetInt32(buf, 12, disk_size);
info_size = 16; size = 16;
} }
else { else {
// Mode parameter header, LONGLBA // Mode parameter header, LONGLBA
@ -452,24 +461,24 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
SetInt64(buf, 8, disk_blocks); SetInt64(buf, 8, disk_blocks);
SetInt32(buf, 20, disk_size); SetInt32(buf, 20, disk_size);
info_size = 24; size = 24;
} }
} }
info_size += super::AddModePages(cdb, buf, info_size, length - info_size); size += super::AddModePages(cdb, buf, size, length - size);
if (info_size > 65535) { if (size > 65535) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
// Do not return more than ALLOCATION LENGTH bytes // Do not return more than ALLOCATION LENGTH bytes
if (info_size > length) { if (size > length) {
info_size = length; size = length;
} }
// Final setting of mode data length // Final setting of mode data length
SetInt16(buf, 0, info_size); SetInt16(buf, 0, size);
return info_size; return size;
} }
void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
@ -798,7 +807,7 @@ void Disk::ValidateBlockAddress(access_mode mode) const
{ {
uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2); uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
uint64_t capacity = GetBlockCount(); uint64_t capacity = blocks;
if (block > capacity) { if (block > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block " LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
@ -834,7 +843,7 @@ bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mod
LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count) LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count)
// Check capacity // Check capacity
if (uint64_t capacity = GetBlockCount(); start > capacity || start + count > capacity) { if (uint64_t capacity = blocks; start > capacity || start + count > capacity) {
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block " LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
+ to_string(start) + ", block count " + to_string(count)).c_str()) + to_string(start) + ", block count " + to_string(count)).c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE); throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
@ -879,7 +888,7 @@ void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
break; break;
default: default:
assert(false); throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
break; break;
} }
} }

View File

@ -31,6 +31,8 @@ class Disk : public ModePageDevice, public ScsiBlockCommands
Dispatcher<Disk> dispatcher; Dispatcher<Disk> dispatcher;
unique_ptr<DiskCache> cache;
// The supported configurable sector sizes, empty if not configurable // The supported configurable sector sizes, empty if not configurable
unordered_set<uint32_t> sector_sizes; unordered_set<uint32_t> sector_sizes;
uint32_t configured_sector_size = 0; uint32_t configured_sector_size = 0;
@ -45,10 +47,8 @@ class Disk : public ModePageDevice, public ScsiBlockCommands
public: public:
explicit Disk(const string&); Disk(const string&, int);
~Disk() override; ~Disk() override;
Disk(Disk&) = delete;
Disk& operator=(const Disk&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
@ -105,13 +105,14 @@ private:
void ValidateBlockAddress(access_mode) const; void ValidateBlockAddress(access_mode) const;
bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode) const; bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode) const;
int ModeSense6(const vector<int>&, vector<BYTE>&, int) const override; int ModeSense6(const vector<int>&, vector<BYTE>&) const override;
int ModeSense10(const vector<int>&, vector<BYTE>&, int) const override; int ModeSense10(const vector<int>&, vector<BYTE>&) const override;
protected: protected:
virtual void Open(const Filepath&); virtual void Open(const Filepath&);
void SetUpCache(const Filepath&, off_t = 0); void SetUpCache(const Filepath&, off_t, bool = false);
void ResizeCache(const Filepath&, bool);
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override; void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
virtual void AddErrorPage(map<int, vector<byte>>&, bool) const; virtual void AddErrorPage(map<int, vector<byte>>&, bool) const;
@ -126,6 +127,4 @@ protected:
void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; } void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; }
uint32_t GetConfiguredSectorSize() const; uint32_t GetConfiguredSectorSize() const;
void SetBlockCount(uint64_t b) { blocks = b; } void SetBlockCount(uint64_t b) { blocks = b; }
unique_ptr<DiskCache> cache;
}; };

View File

@ -19,7 +19,7 @@
#include <array> #include <array>
#include <memory> #include <memory>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class DiskCache class DiskCache
{ {
@ -36,8 +36,6 @@ public:
DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0); DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0);
~DiskCache() = default; ~DiskCache() = default;
DiskCache(DiskCache&) = delete;
DiskCache& operator=(const DiskCache&) = delete;
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting

View File

@ -21,9 +21,7 @@
DiskTrack::~DiskTrack() DiskTrack::~DiskTrack()
{ {
// Release memory, but do not save automatically // Release memory, but do not save automatically
if (dt.buffer) { free(dt.buffer);
free(dt.buffer);
}
} }
void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff) void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff)

View File

@ -18,7 +18,7 @@
#include "filepath.h" #include "filepath.h"
#include <vector> #include <vector>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class DiskTrack class DiskTrack
{ {

View File

@ -14,8 +14,8 @@
#include "log.h" #include "log.h"
#include <unordered_map> #include <unordered_map>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
using namespace scsi_defs; //NOSONAR Not relevant for rascsi using namespace scsi_defs;
template<class T> template<class T>
class Dispatcher class Dispatcher
@ -24,8 +24,6 @@ public:
Dispatcher() = default; Dispatcher() = default;
~Dispatcher() = default; ~Dispatcher() = default;
Dispatcher(Dispatcher&) = delete;
Dispatcher& operator=(const Dispatcher&) = delete;
using operation = void (T::*)(); using operation = void (T::*)();
using command_t = struct _command_t { using command_t = struct _command_t {

View File

@ -7,6 +7,7 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "rascsi_exceptions.h"
#include "file_support.h" #include "file_support.h"
using namespace std; using namespace std;
@ -39,3 +40,16 @@ void FileSupport::UnreserveAll()
{ {
reserved_files.clear(); reserved_files.clear();
} }
bool FileSupport::FileExists(const Filepath& filepath)
{
try {
// Disk::Open closes the file in case it exists
Open(filepath);
}
catch(const file_not_found_exception&) {
return false;
}
return true;
}

View File

@ -15,7 +15,7 @@
#include <string> #include <string>
#include "filepath.h" #include "filepath.h"
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
using id_set = pair<int, int>; using id_set = pair<int, int>;
@ -30,8 +30,6 @@ public:
FileSupport() = default; FileSupport() = default;
virtual ~FileSupport() = default; virtual ~FileSupport() = default;
FileSupport(FileSupport&) = delete;
FileSupport& operator=(const FileSupport&) = delete;
void GetPath(Filepath& path) const { path = diskpath; } void GetPath(Filepath& path) const { path = diskpath; }
void SetPath(const Filepath& path) { diskpath = path; } void SetPath(const Filepath& path) { diskpath = path; }
@ -39,6 +37,7 @@ public:
void ReserveFile(const Filepath&, int, int) const; void ReserveFile(const Filepath&, int, int) const;
void UnreserveFile() const; void UnreserveFile() const;
static void UnreserveAll(); static void UnreserveAll();
bool FileExists(const Filepath&);
static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; } static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; }
static void SetReservedFiles(const unordered_map<string, id_set>& files_in_use) static void SetReservedFiles(const unordered_map<string, id_set>& files_in_use)

View File

@ -20,8 +20,9 @@
// c) start && load (LOAD): Reboot the Raspberry Pi // c) start && load (LOAD): Reboot the Raspberry Pi
// //
#include "controllers/controller_manager.h"
#include "controllers/scsi_controller.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "device_factory.h"
#include "scsi_command_util.h" #include "scsi_command_util.h"
#include "dispatcher.h" #include "dispatcher.h"
#include "host_services.h" #include "host_services.h"
@ -30,7 +31,8 @@
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
HostServices::HostServices(const DeviceFactory& factory) : ModePageDevice("SCHS"), device_factory(factory) HostServices::HostServices(int lun, const ControllerManager& manager)
: ModePageDevice("SCHS", lun), controller_manager(manager)
{ {
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady);
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit); dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
@ -60,40 +62,35 @@ void HostServices::StartStopUnit()
if (!start) { if (!start) {
// Flush any caches // Flush any caches
for (PrimaryDevice *device : device_factory.GetAllDevices()) { for (const auto& device : controller_manager.GetAllDevices()) {
device->FlushCache(); device->FlushCache();
} }
if (load) { if (load) {
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_PI); controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_PI);
} }
else { else {
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_RASCSI); controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_RASCSI);
} }
}
EnterStatusPhase(); else if (load) {
return; controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::RESTART_PI);
} }
else { else {
if (load) { throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::RESTART_PI);
EnterStatusPhase();
return;
}
} }
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); EnterStatusPhase();
} }
int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Block descriptors cannot be returned // Block descriptors cannot be returned
if (!(cdb[1] & 0x08)) { if (!(cdb[1] & 0x08)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
auto length = (int)min((size_t)max_length, (size_t)cdb[4]); auto length = (int)min(buf.size(), (size_t)cdb[4]);
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// Basic Information // Basic Information
@ -114,14 +111,14 @@ int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_
return size; return size;
} }
int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
{ {
// Block descriptors cannot be returned // Block descriptors cannot be returned
if (!(cdb[1] & 0x08)) { if (!(cdb[1] & 0x08)) {
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
} }
auto length = (int)min((size_t)max_length, (size_t)GetInt16(cdb, 7)); auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
fill_n(buf.begin(), length, 0); fill_n(buf.begin(), length, 0);
// Basic Information // Basic Information

View File

@ -15,17 +15,15 @@
#include <vector> #include <vector>
#include <map> #include <map>
class DeviceFactory; class ControllerManager;
class HostServices: public ModePageDevice class HostServices: public ModePageDevice
{ {
public: public:
explicit HostServices(const DeviceFactory&); HostServices(int, const ControllerManager&);
~HostServices() override = default; ~HostServices() override = default;
HostServices(HostServices&) = delete;
HostServices& operator=(const HostServices&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
@ -58,10 +56,10 @@ private:
Dispatcher<HostServices> dispatcher; Dispatcher<HostServices> dispatcher;
int ModeSense6(const vector<int>&, vector<BYTE>&, int) const override; const ControllerManager& controller_manager;
int ModeSense10(const vector<int>&, vector<BYTE>&, int) const override;
int ModeSense6(const vector<int>&, vector<BYTE>&) const override;
int ModeSense10(const vector<int>&, vector<BYTE>&) const override;
void AddRealtimeClockPage(map<int, vector<byte>>&, bool) const; void AddRealtimeClockPage(map<int, vector<byte>>&, bool) const;
const DeviceFactory& device_factory;
}; };

View File

@ -20,7 +20,7 @@ using namespace std;
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id) ModePageDevice::ModePageDevice(const string& type, int lun) : PrimaryDevice(type, lun)
{ {
dispatcher.Add(scsi_command::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6); dispatcher.Add(scsi_command::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
dispatcher.Add(scsi_command::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10); dispatcher.Add(scsi_command::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
@ -63,14 +63,14 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
for (auto const& [index, data] : pages) { for (auto const& [index, data] : pages) {
// The specification mandates that page 0 must be returned after all others // The specification mandates that page 0 must be returned after all others
if (index) { if (index) {
size_t offset = result.size(); size_t off = result.size();
// Page data // Page data
result.insert(result.end(), data.begin(), data.end()); result.insert(result.end(), data.begin(), data.end());
// Page code, PS bit may already have been set // Page code, PS bit may already have been set
result[offset] |= (byte)index; result[off] |= (byte)index;
// Page payload size // Page payload size
result[offset + 1] = (byte)(data.size() - 2); result[off + 1] = (byte)(data.size() - 2);
} }
else { else {
page0 = data; page0 = data;
@ -79,10 +79,12 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
// Page 0 must be last // Page 0 must be last
if (!page0.empty()) { if (!page0.empty()) {
size_t off = result.size();
// Page data // Page data
result.insert(result.end(), page0.begin(), page0.end()); result.insert(result.end(), page0.begin(), page0.end());
// Page payload size // Page payload size
result[result.size() + 1] = (byte)(page0.size() - 2); result[off + 1] = (byte)(page0.size() - 2);
} }
// Do not return more than the requested number of bytes // Do not return more than the requested number of bytes
@ -94,14 +96,14 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
void ModePageDevice::ModeSense6() void ModePageDevice::ModeSense6()
{ {
ctrl->length = ModeSense6(ctrl->cmd, controller->GetBuffer(), (int)controller->GetBufferSize()); ctrl->length = ModeSense6(ctrl->cmd, controller->GetBuffer());
EnterDataInPhase(); EnterDataInPhase();
} }
void ModePageDevice::ModeSense10() void ModePageDevice::ModeSense10()
{ {
ctrl->length = ModeSense10(ctrl->cmd, controller->GetBuffer(), (int)controller->GetBufferSize()); ctrl->length = ModeSense10(ctrl->cmd, controller->GetBuffer());
EnterDataInPhase(); EnterDataInPhase();
} }
@ -145,7 +147,7 @@ int ModePageDevice::ModeSelectCheck6() const
int ModePageDevice::ModeSelectCheck10() const int ModePageDevice::ModeSelectCheck10() const
{ {
// Receive the data specified by the parameter length // Receive the data specified by the parameter length
size_t length = min(controller->GetBufferSize(), (size_t)GetInt16(ctrl->cmd, 7)); size_t length = min(controller->GetBuffer().size(), (size_t)GetInt16(ctrl->cmd, 7));
return ModeSelectCheck((int)length); return ModeSelectCheck((int)length);
} }

View File

@ -18,10 +18,8 @@ class ModePageDevice: public PrimaryDevice
{ {
public: public:
explicit ModePageDevice(const string&); ModePageDevice(const string&, int);
~ModePageDevice()override = default; ~ModePageDevice()override = default;
ModePageDevice(ModePageDevice&) = delete;
ModePageDevice& operator=(const ModePageDevice&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;
@ -38,8 +36,8 @@ private:
Dispatcher<ModePageDevice> dispatcher; Dispatcher<ModePageDevice> dispatcher;
virtual int ModeSense6(const vector<int>&, vector<BYTE>&, int) const = 0; virtual int ModeSense6(const vector<int>&, vector<BYTE>&) const = 0;
virtual int ModeSense10(const vector<int>&, vector<BYTE>&, int) const = 0; virtual int ModeSense10(const vector<int>&, vector<BYTE>&) const = 0;
void ModeSense6(); void ModeSense6();
void ModeSense10(); void ModeSense10();

View File

@ -17,7 +17,7 @@ using namespace std;
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
PrimaryDevice::PrimaryDevice(const string& id) : Device(id) PrimaryDevice::PrimaryDevice(const string& type, int lun) : Device(type, lun)
{ {
// Mandatory SCSI primary commands // Mandatory SCSI primary commands
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
@ -33,6 +33,15 @@ bool PrimaryDevice::Dispatch(scsi_command cmd)
return dispatcher.Dispatch(this, cmd); return dispatcher.Dispatch(this, cmd);
} }
int PrimaryDevice::GetId() const
{
if (controller == nullptr) {
LOGERROR("Device is missing its controller")
}
return controller != nullptr ? controller->GetTargetId() : -1;
}
void PrimaryDevice::SetController(AbstractController *c) void PrimaryDevice::SetController(AbstractController *c)
{ {
controller = c; controller = c;
@ -65,7 +74,7 @@ void PrimaryDevice::Inquiry()
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId()) LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId())
// Signal that the requested LUN does not exist // Signal that the requested LUN does not exist
controller->GetBuffer()[0] |= 0x7f; controller->GetBuffer().data()[0] = 0x7f;
} }
EnterDataInPhase(); EnterDataInPhase();
@ -81,7 +90,7 @@ void PrimaryDevice::ReportLuns()
uint32_t allocation_length = GetInt32(ctrl->cmd, 6); uint32_t allocation_length = GetInt32(ctrl->cmd, 6);
vector<BYTE>& buf = controller->GetBuffer(); vector<BYTE>& buf = controller->GetBuffer();
fill_n(buf.begin(), min(controller->GetBufferSize(), (size_t)allocation_length), 0); fill_n(buf.begin(), min(buf.size(), (size_t)allocation_length), 0);
uint32_t size = 0; uint32_t size = 0;
for (int lun = 0; lun < controller->GetMaxLuns(); lun++) { for (int lun = 0; lun < controller->GetMaxLuns(); lun++) {
@ -115,7 +124,7 @@ void PrimaryDevice::RequestSense()
// Do not raise an exception here because the rest of the code must be executed // Do not raise an exception here because the rest of the code must be executed
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN); controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
controller->SetStatus(0); controller->SetStatus(status::GOOD);
} }
vector<byte> buf = controller->GetDeviceForLun(lun)->HandleRequestSense(); vector<byte> buf = controller->GetDeviceForLun(lun)->HandleRequestSense();
@ -195,7 +204,7 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
buf[12] = (byte)(GetStatusCode() >> 8); buf[12] = (byte)(GetStatusCode() >> 8);
buf[13] = (byte)GetStatusCode(); buf[13] = (byte)GetStatusCode();
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, controller->GetStatus(), LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, (int)controller->GetStatus(),
(int)buf[2], (int)buf[12]) (int)buf[2], (int)buf[12])
return buf; return buf;

View File

@ -12,27 +12,33 @@
#pragma once #pragma once
#include "interfaces/scsi_primary_commands.h" #include "interfaces/scsi_primary_commands.h"
#include "controllers/scsi_controller.h" #include "controllers/abstract_controller.h"
#include "device.h" #include "device.h"
#include "dispatcher.h" #include "dispatcher.h"
#include <string> #include <string>
class PrimaryDevice: public ScsiPrimaryCommands, public Device class PrimaryDevice: public ScsiPrimaryCommands, public Device
{ {
public: public:
explicit PrimaryDevice(const string&); PrimaryDevice(const string&, int);
~PrimaryDevice() override = default; ~PrimaryDevice() override = default;
PrimaryDevice(PrimaryDevice&) = delete;
PrimaryDevice& operator=(const PrimaryDevice&) = delete;
virtual bool Dispatch(scsi_command); virtual bool Dispatch(scsi_command);
int GetId() const override;
void SetController(AbstractController *); void SetController(AbstractController *);
virtual bool WriteByteSequence(vector<BYTE>&, uint32_t); virtual bool WriteByteSequence(vector<BYTE>&, uint32_t);
virtual int GetSendDelay() const { return BUS::SEND_NO_DELAY; } virtual int GetSendDelay() const { return BUS::SEND_NO_DELAY; }
// Override for device specific initializations, to be called after all device properties have been set
virtual bool Init(const unordered_map<string, string>&) { return true; };
virtual void FlushCache() {
// Devices with a cache have to implement this method
}
protected: protected:
vector<byte> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const; vector<byte> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;

View File

@ -14,7 +14,7 @@
#include <vector> #include <vector>
#include <map> #include <map>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
namespace scsi_command_util namespace scsi_command_util
{ {

View File

@ -7,17 +7,17 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp) // Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ] // [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
// //
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's // This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
// Tiny SCSI Emulator // Tiny SCSI Emulator
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT // - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator // - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
// //
// Additional documentation and clarification is available at the // Additional documentation and clarification is available at the
// following link: // following link:
// - https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link // - https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link
// //
@ -36,7 +36,7 @@ using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
// TODO Disk must not be the superclass // TODO Disk must not be the superclass
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP") SCSIDaynaPort::SCSIDaynaPort(int lun) : Disk("SCDP", lun)
{ {
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &SCSIDaynaPort::Read6); dispatcher.Add(scsi_command::eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
@ -139,7 +139,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length) LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length)
// At host startup, it will send a READ(6) command with a length of 1. We should // At host startup, it will send a READ(6) command with a length of 1. We should
// respond by going into the status mode with a code of 0x02 // respond by going into the status mode with a code of 0x02
if (requested_length == 1) { if (requested_length == 1) {
return 0; return 0;
@ -169,7 +169,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
LOGTRACE("%s Packet Sz %d (%08X) read: %d", __PRETTY_FUNCTION__, (unsigned int) rx_packet_size, (unsigned int) rx_packet_size, read_count) LOGTRACE("%s Packet Sz %d (%08X) read: %d", __PRETTY_FUNCTION__, (unsigned int) rx_packet_size, (unsigned int) rx_packet_size, read_count)
// This is a very basic filter to prevent unnecessary packets from // This is a very basic filter to prevent unnecessary packets from
// being sent to the SCSI initiator. // being sent to the SCSI initiator.
send_message_to_host = false; send_message_to_host = false;
// The following doesn't seem to work with unicast messages. Temporarily removing the filtering // The following doesn't seem to work with unicast messages. Temporarily removing the filtering
@ -192,7 +192,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
/////// } /////// }
send_message_to_host = true; send_message_to_host = true;
// TODO: We should check to see if this message is in the multicast // TODO: We should check to see if this message is in the multicast
// configuration from SCSI command 0x0D // configuration from SCSI command 0x0D
if (!send_message_to_host) { if (!send_message_to_host) {
@ -239,7 +239,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
// The CRC was already appended by the ctapdriver // The CRC was already appended by the ctapdriver
return size + DAYNAPORT_READ_HEADER_SZ; return size + DAYNAPORT_READ_HEADER_SZ;
} }
// If we got to this point, there are still messages in the queue, so // If we got to this point, there are still messages in the queue, so
// we should loop back and get the next one. // we should loop back and get the next one.
} // end while } // end while
@ -300,7 +300,7 @@ bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, const vector<BYTE>& buf,
return true; return true;
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// RetrieveStats // RetrieveStats
@ -406,9 +406,9 @@ void SCSIDaynaPort::Write6()
ctrl->length = GetInt16(ctrl->cmd, 3 + 8); ctrl->length = GetInt16(ctrl->cmd, 3 + 8);
} }
else { else {
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, data_format) LOGWARN("%s Unknown data format $%02X", __PRETTY_FUNCTION__, data_format)
} }
LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, ctrl->length, ctrl->length, data_format) LOGTRACE("%s length: $%04X (%d) format: $%02X", __PRETTY_FUNCTION__, ctrl->length, ctrl->length, data_format)
if (ctrl->length <= 0) { if (ctrl->length <= 0) {
throw scsi_error_exception(); throw scsi_error_exception();

View File

@ -7,12 +7,12 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp) // Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ] // [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
// //
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's // This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
// Tiny SCSI Emulator // Tiny SCSI Emulator
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT // - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator // - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
@ -45,10 +45,8 @@ class SCSIDaynaPort final : public Disk
{ {
public: public:
SCSIDaynaPort(); explicit SCSIDaynaPort(int);
~SCSIDaynaPort() override = default; ~SCSIDaynaPort() override = default;
SCSIDaynaPort(SCSIDaynaPort&) = delete;
SCSIDaynaPort& operator=(const SCSIDaynaPort&) = delete;
bool Init(const unordered_map<string, string>&) override; bool Init(const unordered_map<string, string>&) override;
void Open(const Filepath& path) override; void Open(const Filepath& path) override;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI Host Bridge for the Sharp X68000 ] // [ SCSI Host Bridge for the Sharp X68000 ]
@ -27,7 +27,7 @@ using namespace std;
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIBR::SCSIBR() : Disk("SCBR") SCSIBR::SCSIBR(int lun) : Disk("SCBR", lun)
{ {
// Create host file system // Create host file system
fs.Reset(); fs.Reset();
@ -41,7 +41,7 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
{ {
SetParams(params); SetParams(params);
#ifdef __linux #ifdef __linux__
// TAP Driver Generation // TAP Driver Generation
m_bTapEnable = tap.Init(GetParams()); m_bTapEnable = tap.Init(GetParams());
if (!m_bTapEnable){ if (!m_bTapEnable){

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI Host Bridge for the Sharp X68000 ] // [ SCSI Host Bridge for the Sharp X68000 ]
@ -24,7 +24,7 @@
#include <string> #include <string>
#include <array> #include <array>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
class SCSIBR final : public Disk class SCSIBR final : public Disk
{ {
@ -32,10 +32,8 @@ class SCSIBR final : public Disk
public: public:
SCSIBR(); explicit SCSIBR(int);
~SCSIBR() override = default; ~SCSIBR() override = default;
SCSIBR(SCSIBR&) = delete;
SCSIBR& operator=(const SCSIBR&) = delete;
bool Init(const unordered_map<string, string>&) override; bool Init(const unordered_map<string, string>&) override;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;

View File

@ -51,7 +51,7 @@ using namespace scsi_defs;
using namespace ras_util; using namespace ras_util;
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP") SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice("SCLP", lun)
{ {
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady); dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit); dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);
@ -149,8 +149,8 @@ void SCSIPrinter::Print()
LOGTRACE("Receiving %d byte(s) to be printed", length) LOGTRACE("Receiving %d byte(s) to be printed", length)
if (length > controller->GetBufferSize()) { if (length > controller->GetBuffer().size()) {
LOGERROR("%s", string("Transfer buffer overflow: Buffer size is " + to_string(controller->GetBufferSize()) + LOGERROR("%s", ("Transfer buffer overflow: Buffer size is " + to_string(controller->GetBuffer().size()) +
" bytes, " + to_string(length) + " bytes expected").c_str()) " bytes, " + to_string(length) + " bytes expected").c_str())
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB); throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);

View File

@ -24,10 +24,8 @@ class SCSIPrinter final : public PrimaryDevice, public ScsiPrinterCommands //NOS
public: public:
SCSIPrinter(); explicit SCSIPrinter(int);
~SCSIPrinter() override; ~SCSIPrinter() override;
SCSIPrinter(SCSIPrinter&) = delete;
SCSIPrinter& operator=(const SCSIPrinter&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI CD-ROM ] // [ SCSI CD-ROM ]
@ -24,7 +24,7 @@
using namespace scsi_defs; using namespace scsi_defs;
using namespace scsi_command_util; using namespace scsi_command_util;
SCSICD::SCSICD(const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD") SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD", lun)
{ {
SetSectorSizes(sector_sizes); SetSectorSizes(sector_sizes);
@ -92,10 +92,7 @@ void SCSICD::Open(const Filepath& path)
super::Open(path); super::Open(path);
FileSupport::SetPath(path); FileSupport::SetPath(path);
SetUpCache(path); SetUpCache(path, 0, rawfile);
// Set RAW flag
cache->SetRawMode(rawfile);
// Attention if ready // Attention if ready
if (IsReady()) { if (IsReady()) {
@ -117,8 +114,8 @@ void SCSICD::OpenIso(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); off_t size = fio.GetFileSize();
if (file_size < 0x800) { if (size < 0x800) {
fio.Close(); fio.Close();
throw io_exception("ISO CD-ROM file size must be at least 2048 bytes"); throw io_exception("ISO CD-ROM file size must be at least 2048 bytes");
} }
@ -157,16 +154,16 @@ void SCSICD::OpenIso(const Filepath& path)
if (rawfile) { if (rawfile) {
// Size must be a multiple of 2536 // Size must be a multiple of 2536
if (file_size % 2536) { if (size % 2536) {
throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is " throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is "
+ to_string(file_size) + " bytes"); + to_string(size) + " bytes");
} }
// Set the number of blocks // Set the number of blocks
SetBlockCount((DWORD)(file_size / 0x930)); SetBlockCount((DWORD)(size / 0x930));
} else { } else {
// Set the number of blocks // Set the number of blocks
SetBlockCount((DWORD)(file_size >> GetSectorSizeShiftCount())); SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount()));
} }
// Create only one data track // Create only one data track
@ -296,9 +293,9 @@ int SCSICD::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t block)
// Recreate the disk cache // Recreate the disk cache
Filepath path; Filepath path;
tracks[index]->GetPath(path); tracks[index]->GetPath(path);
// Re-assign disk cache (no need to save) // Re-assign disk cache (no need to save)
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)GetBlockCount())); ResizeCache(path, rawfile);
cache->SetRawMode(rawfile);
// Reset data index // Reset data index
dataindex = index; dataindex = index;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI CD-ROM ] // [ SCSI CD-ROM ]
@ -27,10 +27,8 @@ class SCSICD : public Disk, public ScsiMmcCommands, public FileSupport
{ {
public: public:
explicit SCSICD(const unordered_set<uint32_t>&); SCSICD(int, const unordered_set<uint32_t>&);
~SCSICD() override = default; ~SCSICD() override = default;
SCSICD(SCSICD&) = delete;
SCSICD& operator=(const SCSICD&) = delete;
bool Dispatch(scsi_command) override; bool Dispatch(scsi_command) override;

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI hard disk ] // [ SCSI hard disk ]
@ -22,8 +22,8 @@
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIHD::SCSIHD(const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level) SCSIHD::SCSIHD(int lun, const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level)
: Disk(removable ? "SCRM" : "SCHD") : Disk(removable ? "SCRM" : "SCHD", lun)
{ {
scsi_level = level; scsi_level = level;
@ -41,7 +41,13 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size, off_t image_offset)
if (!IsRemovable()) { if (!IsRemovable()) {
uint64_t capacity = GetBlockCount() * GetSectorSizeInBytes(); uint64_t capacity = GetBlockCount() * GetSectorSizeInBytes();
string unit; string unit;
if (capacity >= 1048576) { // 10 GiB and more
if (capacity >= 1099511627776) {
capacity /= 1099511627776;
unit = "GiB";
}
// 1 MiB and more
else if (capacity >= 1048576) {
capacity /= 1048576; capacity /= 1048576;
unit = "MiB"; unit = "MiB";
} }
@ -75,17 +81,17 @@ void SCSIHD::Open(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); off_t size = fio.GetFileSize();
fio.Close(); fio.Close();
// Sector size (default 512 bytes) and number of blocks // Sector size (default 512 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512); SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount((DWORD)(file_size >> GetSectorSizeShiftCount())); SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount()));
// Effective size must be a multiple of the sector size // Effective size must be a multiple of the sector size
file_size = (file_size / GetSectorSizeInBytes()) * GetSectorSizeInBytes(); size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes();
FinalizeSetup(path, file_size); FinalizeSetup(path, size);
} }
vector<byte> SCSIHD::InquiryInternal() const vector<byte> SCSIHD::InquiryInternal() const

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI hard disk ] // [ SCSI hard disk ]
@ -26,10 +26,8 @@ class SCSIHD : public Disk, public FileSupport
public: public:
SCSIHD(const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2); SCSIHD(int, const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2);
~SCSIHD() override = default; ~SCSIHD() override = default;
SCSIHD(SCSIHD&) = delete;
SCSIHD& operator=(const SCSIHD&) = delete;
void FinalizeSetup(const Filepath&, off_t, off_t = 0); void FinalizeSetup(const Filepath&, off_t, off_t = 0);

View File

@ -54,18 +54,18 @@ void SCSIHD_NEC::Open(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); off_t size = fio.GetFileSize();
// NEC root sector // NEC root sector
array<BYTE, 512> root_sector; array<BYTE, 512> root_sector;
if (file_size >= (off_t)root_sector.size() && !fio.Read(root_sector.data(), root_sector.size())) { if (size >= (off_t)root_sector.size() && !fio.Read(root_sector.data(), root_sector.size())) {
fio.Close(); fio.Close();
throw io_exception("Can't read NEC hard disk file root sector"); throw io_exception("Can't read NEC hard disk file root sector");
} }
fio.Close(); fio.Close();
// Effective size must be a multiple of 512 // Effective size must be a multiple of 512
file_size = (file_size / 512) * 512; size = (size / 512) * 512;
int image_size = 0; int image_size = 0;
int sector_size = 0; int sector_size = 0;
@ -76,11 +76,11 @@ void SCSIHD_NEC::Open(const Filepath& path)
if (const char *ext = path.GetFileExt(); !strcasecmp(ext, ".hdn")) { if (const char *ext = path.GetFileExt(); !strcasecmp(ext, ".hdn")) {
// Assuming sector size 512, number of sectors 25, number of heads 8 as default settings // Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
image_offset = 0; image_offset = 0;
image_size = (int)file_size; image_size = (int)size;
sector_size = 512; sector_size = 512;
sectors = 25; sectors = 25;
heads = 8; heads = 8;
cylinders = (int)(file_size >> 9); cylinders = (int)(size >> 9);
cylinders >>= 3; cylinders >>= 3;
cylinders /= 25; cylinders /= 25;
} }
@ -113,23 +113,23 @@ void SCSIHD_NEC::Open(const Filepath& path)
} }
// Image size consistency check // Image size consistency check
if (image_offset + image_size > file_size || image_size % sector_size != 0) { if (image_offset + image_size > size || image_size % sector_size != 0) {
throw io_exception("Image size consistency check failed"); throw io_exception("Image size consistency check failed");
} }
// Calculate sector size // Calculate sector size
for (file_size = 16; file_size > 0; --file_size) { for (size = 16; size > 0; --size) {
if ((1 << file_size) == sector_size) if ((1 << size) == sector_size)
break; break;
} }
if (file_size <= 0 || file_size > 16) { if (size <= 0 || size > 16) {
throw io_exception("Invalid NEC disk size"); throw io_exception("Invalid NEC disk size");
} }
SetSectorSizeShiftCount((uint32_t)file_size); SetSectorSizeShiftCount((uint32_t)size);
SetBlockCount(image_size >> GetSectorSizeShiftCount()); SetBlockCount(image_size >> GetSectorSizeShiftCount());
FinalizeSetup(path, file_size, image_offset); FinalizeSetup(path, size, image_offset);
} }
vector<byte> SCSIHD_NEC::InquiryInternal() const vector<byte> SCSIHD_NEC::InquiryInternal() const

View File

@ -7,7 +7,7 @@
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// Copyright (C) akuker // Copyright (C) akuker
// //
// Licensed under the BSD 3-Clause License. // Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder. // See LICENSE file in the project root folder.
// //
// [ SCSI NEC "Genuine" Hard Disk] // [ SCSI NEC "Genuine" Hard Disk]
@ -20,7 +20,7 @@
#include <unordered_set> #include <unordered_set>
#include <map> #include <map>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
//=========================================================================== //===========================================================================
// //
@ -31,10 +31,8 @@ class SCSIHD_NEC : public SCSIHD
{ {
public: public:
SCSIHD_NEC() : SCSIHD(sector_sizes, false) {} explicit SCSIHD_NEC(int lun) : SCSIHD(lun, sector_sizes, false) {}
~SCSIHD_NEC() override = default; ~SCSIHD_NEC() override = default;
SCSIHD_NEC(SCSIHD_NEC&) = delete;
SCSIHD_NEC& operator=(const SCSIHD_NEC&) = delete;
void Open(const Filepath&) override; void Open(const Filepath&) override;

View File

@ -19,7 +19,7 @@
using namespace scsi_command_util; using namespace scsi_command_util;
SCSIMO::SCSIMO(const unordered_set<uint32_t>& sector_sizes) : Disk("SCMO") SCSIMO::SCSIMO(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk("SCMO", lun)
{ {
SetSectorSizes(sector_sizes); SetSectorSizes(sector_sizes);
@ -45,14 +45,14 @@ void SCSIMO::Open(const Filepath& path)
} }
// Get file size // Get file size
off_t file_size = fio.GetFileSize(); off_t size = fio.GetFileSize();
fio.Close(); fio.Close();
// For some capacities there are hard-coded, well-defined sector sizes and block counts // For some capacities there are hard-coded, well-defined sector sizes and block counts
if (!SetGeometryForCapacity(file_size)) { if (!SetGeometryForCapacity(size)) {
// Sector size (default 512 bytes) and number of blocks // Sector size (default 512 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512); SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount(file_size >> GetSectorSizeShiftCount()); SetBlockCount(size >> GetSectorSizeShiftCount());
} }
SetReadOnly(false); SetReadOnly(false);
@ -62,7 +62,7 @@ void SCSIMO::Open(const Filepath& path)
Disk::Open(path); Disk::Open(path);
FileSupport::SetPath(path); FileSupport::SetPath(path);
SetUpCache(path); SetUpCache(path, 0);
// Attention if ready // Attention if ready
if (IsReady()) { if (IsReady()) {

View File

@ -24,10 +24,8 @@ class SCSIMO : public Disk, public FileSupport
{ {
public: public:
explicit SCSIMO(const unordered_set<uint32_t>&); SCSIMO(int, const unordered_set<uint32_t>&);
~SCSIMO() override = default; ~SCSIMO() override = default;
SCSIMO(SCSIMO&) = delete;
SCSIMO& operator=(const SCSIMO&) = delete;
void Open(const Filepath&) override; void Open(const Filepath&) override;

View File

@ -25,8 +25,8 @@ public:
Fileio() = default; Fileio() = default;
virtual ~Fileio(); virtual ~Fileio();
Fileio(Fileio&) = delete; Fileio(Fileio&) = default;
Fileio& operator=(const Fileio&) = delete; Fileio& operator=(const Fileio&) = default;
bool Open(const char *fname, OpenMode mode); bool Open(const char *fname, OpenMode mode);
bool Open(const Filepath& path, OpenMode mode); bool Open(const Filepath& path, OpenMode mode);

View File

@ -35,7 +35,7 @@ public:
Filepath(); Filepath();
virtual ~Filepath() = default; virtual ~Filepath() = default;
Filepath(Filepath&) = delete; Filepath(Filepath&) = default;
Filepath& operator=(const Filepath&); Filepath& operator=(const Filepath&);
void Clear(); void Clear();

View File

@ -19,13 +19,13 @@
#include "config.h" #include "config.h"
#include "log.h" #include "log.h"
#include <array> #include <array>
#ifdef __linux #ifdef __linux__
#include <sys/epoll.h> #include <sys/epoll.h>
#endif #endif
using namespace std; using namespace std;
#ifdef __linux #ifdef __linux__
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// imported from bcm_host.c // imported from bcm_host.c
@ -1258,7 +1258,7 @@ bool GPIOBUS::WaitSignal(int pin, int ast)
void GPIOBUS::DisableIRQ() void GPIOBUS::DisableIRQ()
{ {
#ifdef __linux #ifdef __linux__
if (rpitype == 4) { if (rpitype == 4) {
// RPI4 is disabled by GICC // RPI4 is disabled by GICC
giccpmr = gicc[GICC_PMR]; giccpmr = gicc[GICC_PMR];

View File

@ -15,7 +15,7 @@
#include "scsi.h" #include "scsi.h"
#include <array> #include <array>
#ifdef __linux #ifdef __linux__
#include <linux/gpio.h> #include <linux/gpio.h>
#endif #endif

View File

@ -195,13 +195,11 @@ string Localizer::Localize(LocalizationKey key, const string& locale, const stri
} }
} }
assert(it != localized_messages.end()); if (it == localized_messages.end()) {
return "Missing default localization for enum value " + to_string((int)key);
auto messages = it->second;
if (messages.empty()) {
return "Missing localization for enum value " + to_string((int)key);
} }
auto messages = it->second;
string message = messages[key]; string message = messages[key];
message = regex_replace(message, regex("%1"), arg1); message = regex_replace(message, regex("%1"), arg1);

View File

@ -15,7 +15,7 @@
#include <unordered_set> #include <unordered_set>
#include <unordered_map> #include <unordered_map>
using namespace std; //NOSONAR Not relevant for rascsi using namespace std;
enum class LocalizationKey { enum class LocalizationKey {
ERROR_AUTHENTICATION, ERROR_AUTHENTICATION,

View File

@ -9,48 +9,21 @@
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "socket_connector.h" #include "protobuf_serializer.h"
#include <unistd.h> #include <unistd.h>
#include <netinet/in.h>
#include <sstream> #include <sstream>
using namespace std; using namespace std;
using namespace rascsi_interface; using namespace rascsi_interface;
int SocketConnector::ReadCommand(PbCommand& command, int socket) const
{
// Wait for connection
sockaddr client = {};
socklen_t socklen = sizeof(client);
int fd = accept(socket, &client, &socklen);
if (fd < 0) {
throw io_exception("accept() failed");
}
// Read magic string
vector<byte> magic(6);
size_t bytes_read = ReadBytes(fd, magic);
if (!bytes_read) {
return -1;
}
if (bytes_read != magic.size() || memcmp(magic.data(), "RASCSI", magic.size())) {
throw io_exception("Invalid magic");
}
// Fetch the command
DeserializeMessage(fd, command);
return fd;
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Serialize/Deserialize protobuf message: Length followed by the actual data. // Serialize/Deserialize protobuf message: Length followed by the actual data.
// Little endian is assumed. // A little endian platform is assumed.
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void SocketConnector::SerializeMessage(int fd, const google::protobuf::Message& message) const void ProtobufSerializer::SerializeMessage(int fd, const google::protobuf::Message& message) const
{ {
string data; string data;
message.SerializeToString(&data); message.SerializeToString(&data);
@ -67,7 +40,7 @@ void SocketConnector::SerializeMessage(int fd, const google::protobuf::Message&
} }
} }
void SocketConnector::DeserializeMessage(int fd, google::protobuf::Message& message) const void ProtobufSerializer::DeserializeMessage(int fd, google::protobuf::Message& message) const
{ {
// Read the header with the size of the protobuf data // Read the header with the size of the protobuf data
vector<byte> header_buf(4); vector<byte> header_buf(4);
@ -91,7 +64,7 @@ void SocketConnector::DeserializeMessage(int fd, google::protobuf::Message& mess
message.ParseFromString(data); message.ParseFromString(data);
} }
size_t SocketConnector::ReadBytes(int fd, vector<byte>& buf) const size_t ProtobufSerializer::ReadBytes(int fd, vector<byte>& buf) const
{ {
size_t offset = 0; size_t offset = 0;
while (offset < buf.size()) { while (offset < buf.size()) {

View File

@ -5,28 +5,22 @@
// //
// Copyright (C) 2022 Uwe Seimet // Copyright (C) 2022 Uwe Seimet
// //
// Helper for serializing/deserializing protobuf messages to/fromn sockets // Helper for serializing/deserializing protobuf messages
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#pragma once #pragma once
#include "google/protobuf/message.h" #include "google/protobuf/message.h"
#include "rascsi_interface.pb.h" #include <vector>
#include "command_context.h"
#include "localizer.h"
#include <string>
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi class ProtobufSerializer
class SocketConnector
{ {
public: public:
SocketConnector() = default; ProtobufSerializer() = default;
~SocketConnector() = default; ~ProtobufSerializer() = default;
int ReadCommand(PbCommand&, int) const;
void SerializeMessage(int, const google::protobuf::Message&) const; void SerializeMessage(int, const google::protobuf::Message&) const;
void DeserializeMessage(int, google::protobuf::Message&) const; void DeserializeMessage(int, google::protobuf::Message&) const;
size_t ReadBytes(int, vector<byte>&) const; size_t ReadBytes(int, vector<byte>&) const;

File diff suppressed because it is too large Load Diff

View File

@ -13,41 +13,31 @@
#include <exception> #include <exception>
#include <string> #include <string>
using namespace std; //NOSONAR Not relevant for rascsi class io_exception : public std::exception
{
class illegal_argument_exception final : public exception {
private:
string msg; string msg;
public: public:
explicit illegal_argument_exception(const string& msg) : msg(msg) {}
~illegal_argument_exception() override = default;
const string& get_msg() const { return msg; }
};
class io_exception : public exception {
private:
string msg;
public:
explicit io_exception(const string& msg) : msg(msg) {} explicit io_exception(const string& msg) : msg(msg) {}
~io_exception() override = default; ~io_exception() override = default;
const string& get_msg() const { return msg; } const string& get_msg() const { return msg; }
}; };
class file_not_found_exception : public io_exception { class file_not_found_exception : public io_exception
{
using io_exception::io_exception; using io_exception::io_exception;
}; };
class scsi_error_exception final : public exception { class scsi_error_exception final : public std::exception
private: {
scsi_defs::sense_key sense_key; scsi_defs::sense_key sense_key;
scsi_defs::asc asc; scsi_defs::asc asc;
scsi_defs::status status; scsi_defs::status status;
public: public:
scsi_error_exception(scsi_defs::sense_key sense_key = scsi_defs::sense_key::ABORTED_COMMAND, scsi_error_exception(scsi_defs::sense_key sense_key = scsi_defs::sense_key::ABORTED_COMMAND,
scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION, scsi_defs::asc asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
scsi_defs::status status = scsi_defs::status::CHECK_CONDITION) scsi_defs::status status = scsi_defs::status::CHECK_CONDITION)

View File

@ -0,0 +1,762 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "log.h"
#include "controllers/controller_manager.h"
#include "controllers/scsi_controller.h"
#include "devices/device_factory.h"
#include "devices/primary_device.h"
#include "devices/disk.h"
#include "devices/file_support.h"
#include "rascsi_service.h"
#include "rascsi_response.h"
#include "rascsi_image.h"
#include "rascsi_exceptions.h"
#include "localizer.h"
#include "command_util.h"
#include "rasutil.h"
#include "spdlog/spdlog.h"
#include "rascsi_executor.h"
#include <sstream>
using namespace spdlog;
using namespace command_util;
using namespace ras_util;
bool RascsiExecutor::ProcessCmd(const CommandContext& context, const PbDeviceDefinition& pb_device,
const PbCommand& command, bool dryRun)
{
PrintCommand(command, pb_device, dryRun);
const int id = pb_device.id();
const int lun = pb_device.unit();
if (!ValidateIdAndLun(context, id, lun)) {
return false;
}
const PbOperation operation = command.operation();
// For all commands except ATTACH the device and LUN must exist
if (operation != ATTACH && !VerifyExistingIdAndLun(context, id, lun)) {
return false;
}
auto device = controller_manager.GetDeviceByIdAndLun(id, lun);
if (!ValidationOperationAgainstDevice(context, device, operation)) {
return false;
}
switch (operation) {
case START:
return Start(device, dryRun);
case STOP:
return Stop(device, dryRun);
case ATTACH:
return Attach(context, pb_device, dryRun);
case DETACH:
return Detach(context, device, dryRun);
case INSERT:
return Insert(context, pb_device, device, dryRun);
case EJECT:
return Eject(device, dryRun);
case PROTECT:
return Protect(device, dryRun);
case UNPROTECT:
return Unprotect(device, dryRun);
break;
case CHECK_AUTHENTICATION:
case NO_OPERATION:
// Do nothing, just log
LOGTRACE("Received %s command", PbOperation_Name(operation).c_str())
break;
default:
return ReturnLocalizedError(context, LocalizationKey::ERROR_OPERATION);
}
return true;
}
bool RascsiExecutor::ProcessCmd(const CommandContext& context, const PbCommand& command)
{
switch (command.operation()) {
case DETACH_ALL:
DetachAll();
return ReturnStatus(context);
case RESERVE_IDS: {
const string ids = GetParam(command, "ids");
if (string error = SetReservedIds(ids); !error.empty()) {
return ReturnStatus(context, false, error);
}
return ReturnStatus(context);
}
case CREATE_IMAGE:
return rascsi_image.CreateImage(context, command);
case DELETE_IMAGE:
return rascsi_image.DeleteImage(context, command);
case RENAME_IMAGE:
return rascsi_image.RenameImage(context, command);
case COPY_IMAGE:
return rascsi_image.CopyImage(context, command);
case PROTECT_IMAGE:
case UNPROTECT_IMAGE:
return rascsi_image.SetImagePermissions(context, command);
default:
// This is a device-specific command handled below
break;
}
// Remember the list of reserved files, than run the dry run
const auto& reserved_files = FileSupport::GetReservedFiles();
for (const auto& device : command.devices()) {
if (!ProcessCmd(context, device, command, true)) {
// Dry run failed, restore the file list
FileSupport::SetReservedFiles(reserved_files);
return false;
}
}
// Restore the list of reserved files before proceeding
FileSupport::SetReservedFiles(reserved_files);
if (string result = ValidateLunSetup(command); !result.empty()) {
return ReturnStatus(context, false, result);
}
for (const auto& device : command.devices()) {
if (!ProcessCmd(context, device, command, false)) {
return false;
}
}
// ATTACH and DETACH return the device list
if (context.fd != -1 && (command.operation() == ATTACH || command.operation() == DETACH)) {
// A new command with an empty device list is required here in order to return data for all devices
PbCommand cmd;
PbResult result;
rascsi_response.GetDevicesInfo(result, cmd, rascsi_image.GetDefaultFolder());
serializer.SerializeMessage(context.fd, result);
return true;
}
return ReturnStatus(context);
}
bool RascsiExecutor::SetLogLevel(const string& log_level) const
{
if (log_level == "trace") {
set_level(level::trace);
}
else if (log_level == "debug") {
set_level(level::debug);
}
else if (log_level == "info") {
set_level(level::info);
}
else if (log_level == "warn") {
set_level(level::warn);
}
else if (log_level == "err") {
set_level(level::err);
}
else if (log_level == "critical") {
set_level(level::critical);
}
else if (log_level == "off") {
set_level(level::off);
}
else {
LOGWARN("Invalid log level '%s'", log_level.c_str())
return false;
}
LOGINFO("Set log level to '%s'", log_level.c_str())
return true;
}
bool RascsiExecutor::Start(shared_ptr<PrimaryDevice> device, bool dryRun) const
{
if (!dryRun) {
LOGINFO("Start requested for %s ID %d, unit %d", device->GetType().c_str(), device->GetId(), device->GetLun())
if (!device->Start()) {
LOGWARN("Starting %s ID %d, unit %d failed", device->GetType().c_str(), device->GetId(), device->GetLun())
}
}
return true;
}
bool RascsiExecutor::Stop(shared_ptr<PrimaryDevice> device, bool dryRun) const
{
if (!dryRun) {
LOGINFO("Stop requested for %s ID %d, unit %d", device->GetType().c_str(), device->GetId(), device->GetLun())
// STOP is idempotent
device->Stop();
}
return true;
}
bool RascsiExecutor::Eject(shared_ptr<PrimaryDevice> device, bool dryRun) const
{
if (!dryRun) {
LOGINFO("Eject requested for %s ID %d, unit %d", device->GetType().c_str(), device->GetId(), device->GetLun())
if (!device->Eject(true)) {
LOGWARN("Ejecting %s ID %d, unit %d failed", device->GetType().c_str(), device->GetId(), device->GetLun())
}
}
return true;
}
bool RascsiExecutor::Protect(shared_ptr<PrimaryDevice> device, bool dryRun) const
{
if (!dryRun) {
LOGINFO("Write protection requested for %s ID %d, unit %d", device->GetType().c_str(), device->GetId(),
device->GetLun())
// PROTECT is idempotent
device->SetProtected(true);
}
return true;
}
bool RascsiExecutor::Unprotect(shared_ptr<PrimaryDevice> device, bool dryRun) const
{
if (!dryRun) {
LOGINFO("Write unprotection requested for %s ID %d, unit %d", device->GetType().c_str(), device->GetId(),
device->GetLun())
// UNPROTECT is idempotent
device->SetProtected(false);
}
return true;
}
bool RascsiExecutor::Attach(const CommandContext& context, const PbDeviceDefinition& pb_device, bool dryRun)
{
const int id = pb_device.id();
const int lun = pb_device.unit();
const PbDeviceType type = pb_device.type();
if (lun >= ScsiController::LUN_MAX) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_INVALID_LUN, to_string(lun), to_string(ScsiController::LUN_MAX));
}
if (controller_manager.GetDeviceByIdAndLun(id, lun) != nullptr) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_DUPLICATE_ID, to_string(id), to_string(lun));
}
if (reserved_ids.find(id) != reserved_ids.end()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_RESERVED_ID, to_string(id));
}
string filename = GetParam(pb_device, "file");
auto device = CreateDevice(context, type, lun, filename);
if (device == nullptr) {
return false;
}
// If no filename was provided the medium is considered removed
auto file_support = dynamic_cast<FileSupport *>(device.get());
device->SetRemoved(file_support != nullptr ? filename.empty() : false);
if (!SetProductData(context, pb_device, device)) {
return false;
}
if (!SetSectorSize(context, PbDeviceType_Name(type), device, pb_device.block_size())) {
return false;
}
string full_path;
if (file_support != nullptr) {
// File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later
if (!device->IsRemovable() && filename.empty()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_MISSING_FILENAME, PbDeviceType_Name(type));
}
if (!ValidateImageFile(context, device, filename, full_path)) {
return false;
}
}
// Only non read-only devices support protect/unprotect
// This operation must not be executed before Open() because Open() overrides some settings.
if (device->IsProtectable() && !device->IsReadOnly()) {
device->SetProtected(pb_device.protected_());
}
// Stop the dry run here, before permanently modifying something
if (dryRun) {
return true;
}
unordered_map<string, string> params = { pb_device.params().begin(), pb_device.params().end() };
if (!device->SupportsFile()) {
params.erase("file");
}
if (!device->Init(params)) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_INITIALIZATION, PbDeviceType_Name(type),
to_string(id), to_string(lun));
}
if (!controller_manager.AttachToScsiController(id, device)) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_SCSI_CONTROLLER);
}
Filepath filepath;
filepath.SetPath(full_path.c_str());
file_support->ReserveFile(filepath, id, lun);
string msg = "Attached ";
if (device->IsReadOnly()) {
msg += "read-only ";
}
else if (device->IsProtectable() && device->IsProtected()) {
msg += "protected ";
}
msg += device->GetType() + " device, ID " + to_string(id) + ", unit " + to_string(lun);
LOGINFO("%s", msg.c_str())
return true;
}
bool RascsiExecutor::Insert(const CommandContext& context, const PbDeviceDefinition& pb_device,
shared_ptr<PrimaryDevice> device, bool dryRun) const
{
if (!device->IsRemoved()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_EJECT_REQUIRED);
}
if (!pb_device.vendor().empty() || !pb_device.product().empty() || !pb_device.revision().empty()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_DEVICE_NAME_UPDATE);
}
string filename = GetParam(pb_device, "file");
if (filename.empty()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_MISSING_FILENAME);
}
if (dryRun) {
return true;
}
LOGINFO("Insert %sfile '%s' requested into %s ID %d, unit %d", pb_device.protected_() ? "protected " : "",
filename.c_str(), device->GetType().c_str(), pb_device.id(), pb_device.unit())
if (!SetSectorSize(context, device->GetType(), device, pb_device.block_size())) {
return false;
}
string full_path;
if (!ValidateImageFile(context, device, filename, full_path)) {
return false;
}
Filepath filepath;
filepath.SetPath(full_path.c_str());
dynamic_pointer_cast<FileSupport>(device)->ReserveFile(filepath, device->GetId(), device->GetLun());
// Only non read-only devices support protect/unprotect.
// This operation must not be executed before Open() because Open() overrides some settings.
if (device->IsProtectable() && !device->IsReadOnly()) {
device->SetProtected(pb_device.protected_());
}
if (auto disk = dynamic_pointer_cast<Disk>(device); disk != nullptr) {
disk->MediumChanged();
}
return true;
}
bool RascsiExecutor::Detach(const CommandContext& context, shared_ptr<PrimaryDevice> device, bool dryRun) const
{
// LUN 0 can only be detached if there is no other LUN anymore
if (!device->GetLun() && controller_manager.FindController(device->GetId())->GetLunCount() > 1) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_LUN0);
}
if (!dryRun) {
// Prepare log string before the device data are lost due to deletion
string s = "Detached " + device->GetType() + " device with ID " + to_string(device->GetId())
+ ", unit " + to_string(device->GetLun());
if (auto file_support = dynamic_pointer_cast<FileSupport>(device); file_support != nullptr) {
file_support->UnreserveFile();
}
auto controller = controller_manager.FindController(device->GetId());
if (controller == nullptr || !controller->DeleteDevice(device)) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_DETACH);
}
// If no LUN is left also delete the controller
if (!controller->GetLunCount()) {
controller_manager.DeleteController(controller);
}
LOGINFO("%s", s.c_str())
}
return true;
}
void RascsiExecutor::DetachAll()
{
controller_manager.DeleteAllControllers();
FileSupport::UnreserveAll();
LOGINFO("Detached all devices")
}
bool RascsiExecutor::ShutDown(const CommandContext& context, const string& mode) {
if (mode.empty()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_SHUTDOWN_MODE_MISSING);
}
PbResult result;
result.set_status(true);
if (mode == "rascsi") {
LOGINFO("RaSCSI shutdown requested")
serializer.SerializeMessage(context.fd, result);
return true;
}
if (mode != "system" && mode != "reboot") {
return ReturnLocalizedError(context, LocalizationKey::ERROR_SHUTDOWN_MODE_INVALID, mode);
}
// The root user has UID 0
if (getuid()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_SHUTDOWN_PERMISSION);
}
if (mode == "system") {
LOGINFO("System shutdown requested")
serializer.SerializeMessage(context.fd, result);
DetachAll();
if (system("init 0") == -1) {
LOGERROR("System shutdown failed: %s", strerror(errno))
}
}
else if (mode == "reboot") {
LOGINFO("System reboot requested")
serializer.SerializeMessage(context.fd, result);
DetachAll();
if (system("init 6") == -1) {
LOGERROR("System reboot failed: %s", strerror(errno))
}
}
else {
assert(false);
}
return false;
}
string RascsiExecutor::SetReservedIds(string_view ids)
{
list<string> ids_to_reserve;
stringstream ss(ids.data());
string id;
while (getline(ss, id, ',')) {
if (!id.empty()) {
ids_to_reserve.push_back(id);
}
}
unordered_set<int> reserved;
for (string id_to_reserve : ids_to_reserve) {
int res_id;
if (!GetAsInt(id_to_reserve, res_id) || res_id > 7) {
return "Invalid ID " + id_to_reserve;
}
if (controller_manager.FindController(res_id) != nullptr) {
return "ID " + id_to_reserve + " is currently in use";
}
reserved.insert(res_id);
}
reserved_ids = reserved;
if (!reserved_ids.empty()) {
string s;
bool isFirst = true;
for (auto const& reserved_id : reserved_ids) {
if (!isFirst) {
s += ", ";
}
isFirst = false;
s += to_string(reserved_id);
}
LOGINFO("Reserved ID(s) set to %s", s.c_str())
}
else {
LOGINFO("Cleared reserved ID(s)")
}
return "";
}
bool RascsiExecutor::ValidateImageFile(const CommandContext& context, shared_ptr<PrimaryDevice> device,
const string& filename, string& full_path) const
{
auto file_support = dynamic_pointer_cast<FileSupport>(device);
if (file_support == nullptr || filename.empty()) {
return true;
}
int id;
int lun;
Filepath filepath;
filepath.SetPath(filename.c_str());
if (FileSupport::GetIdsForReservedFile(filepath, id, lun)) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_IMAGE_IN_USE, filename,
to_string(id), to_string(lun));
}
string initial_filename = filepath.GetPath();
try {
if (!file_support->FileExists(filepath)) {
// If the file does not exist search for it in the default image folder
filepath.SetPath((rascsi_image.GetDefaultFolder() + "/" + filename).c_str());
if (FileSupport::GetIdsForReservedFile(filepath, id, lun)) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_IMAGE_IN_USE, filename,
to_string(id), to_string(lun));
}
file_support->Open(filepath);
}
}
catch(const io_exception& e) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_FILE_OPEN, initial_filename, e.get_msg());
}
full_path = filepath.GetPath();
return true;
}
void RascsiExecutor::PrintCommand(const PbCommand& command, const PbDeviceDefinition& pb_device, bool dryRun) const
{
const map<string, string, less<>> params = { command.params().begin(), command.params().end() };
ostringstream s;
s << (dryRun ? "Validating" : "Executing");
s << ": operation=" << PbOperation_Name(command.operation());
if (!params.empty()) {
s << ", command params=";
bool isFirst = true;
for (const auto& [key, value]: params) {
if (!isFirst) {
s << ", ";
}
isFirst = false;
string v = key != "token" ? value : "???";
s << "'" << key << "=" << v << "'";
}
}
s << ", device id=" << pb_device.id() << ", lun=" << pb_device.unit() << ", type="
<< PbDeviceType_Name(pb_device.type());
if (pb_device.params_size()) {
s << ", device params=";
bool isFirst = true;
for (const auto& [key, value]: pb_device.params()) {
if (!isFirst) {
s << ":";
}
isFirst = false;
s << "'" << key << "=" << value << "'";
}
}
s << ", vendor='" << pb_device.vendor() << "', product='" << pb_device.product()
<< "', revision='" << pb_device.revision() << "', block size=" << pb_device.block_size();
LOGINFO("%s", s.str().c_str())
}
string RascsiExecutor::ValidateLunSetup(const PbCommand& command) const
{
// Mapping of available LUNs (bit vector) to devices
unordered_map<uint32_t, uint32_t> luns;
// Collect LUN bit vectors of new devices
for (const auto& device : command.devices()) {
luns[device.id()] |= 1 << device.unit();
}
// Collect LUN bit vectors of existing devices
for (const auto& device : controller_manager.GetAllDevices()) {
luns[device->GetId()] |= 1 << device->GetLun();
}
// LUN 0 must exist for all devices
for (auto const& [id, lun]: luns) {
if (!(lun & 0x01)) {
return "LUN 0 is missing for device ID " + to_string(id);
}
}
return "";
}
bool RascsiExecutor::VerifyExistingIdAndLun(const CommandContext& context, int id, int lun) const
{
if (controller_manager.FindController(id) == nullptr) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_NON_EXISTING_DEVICE, to_string(id));
}
if (controller_manager.GetDeviceByIdAndLun(id, lun) == nullptr) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_NON_EXISTING_UNIT, to_string(id), to_string(lun));
}
return true;
}
shared_ptr<PrimaryDevice> RascsiExecutor::CreateDevice(const CommandContext& context, const PbDeviceType type,
int lun, const string& filename) const
{
auto device = device_factory.CreateDevice(controller_manager, type, lun, filename);
if (device == nullptr) {
if (type == UNDEFINED) {
ReturnLocalizedError(context, LocalizationKey::ERROR_MISSING_DEVICE_TYPE, filename);
}
else {
ReturnLocalizedError(context, LocalizationKey::ERROR_UNKNOWN_DEVICE_TYPE, PbDeviceType_Name(type));
}
}
return device;
}
bool RascsiExecutor::SetSectorSize(const CommandContext& context, const string& type,
shared_ptr<PrimaryDevice> device, int block_size) const
{
if (block_size) {
auto disk = dynamic_pointer_cast<Disk>(device);
if (disk != nullptr && disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(device_factory, block_size)) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_BLOCK_SIZE, to_string(block_size));
}
}
else {
return ReturnLocalizedError(context, LocalizationKey::ERROR_BLOCK_SIZE_NOT_CONFIGURABLE, type);
}
}
return true;
}
bool RascsiExecutor::ValidationOperationAgainstDevice(const CommandContext& context,
const shared_ptr<PrimaryDevice> device, const PbOperation& operation)
{
const string& type = device->GetType();
if ((operation == START || operation == STOP) && !device->IsStoppable()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_OPERATION_DENIED_STOPPABLE, type);
}
if ((operation == INSERT || operation == EJECT) && !device->IsRemovable()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_OPERATION_DENIED_REMOVABLE, type);
}
if ((operation == PROTECT || operation == UNPROTECT) && !device->IsProtectable()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_OPERATION_DENIED_PROTECTABLE, type);
}
if ((operation == PROTECT || operation == UNPROTECT) && !device->IsReady()) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_OPERATION_DENIED_READY, type);
}
return true;
}
bool RascsiExecutor::ValidateIdAndLun(const CommandContext& context, int id, int lun)
{
// Validate the device ID and LUN
if (id < 0) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_MISSING_DEVICE_ID);
}
if (id >= ControllerManager::DEVICE_MAX) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_INVALID_ID, to_string(id), to_string(ControllerManager::DEVICE_MAX - 1));
}
if (lun < 0 || lun >= ScsiController::LUN_MAX) {
return ReturnLocalizedError(context, LocalizationKey::ERROR_INVALID_LUN, to_string(lun), to_string(ScsiController::LUN_MAX - 1));
}
return true;
}
bool RascsiExecutor::SetProductData(const CommandContext& context, const PbDeviceDefinition& pb_device,
shared_ptr<PrimaryDevice> device)
{
try {
if (!pb_device.vendor().empty()) {
device->SetVendor(pb_device.vendor());
}
if (!pb_device.product().empty()) {
device->SetProduct(pb_device.product());
}
if (!pb_device.revision().empty()) {
device->SetRevision(pb_device.revision());
}
}
catch(const invalid_argument& e) {
return ReturnStatus(context, false, e.what());
}
return true;
}

View File

@ -0,0 +1,71 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2021-2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "protobuf_serializer.h"
class RascsiResponse;
class RascsiImage;
class DeviceFactory;
class ControllerManager;
class PrimaryDevice;
class CommandContext;
class RascsiExecutor
{
RascsiResponse& rascsi_response;
RascsiImage& rascsi_image;
DeviceFactory& device_factory;
ControllerManager& controller_manager;
ProtobufSerializer serializer;
unordered_set<int> reserved_ids;
public:
RascsiExecutor(RascsiResponse& rascsi_response, RascsiImage& rascsi_image, DeviceFactory& device_factory,
ControllerManager& controller_manager)
: rascsi_response(rascsi_response), rascsi_image(rascsi_image), device_factory(device_factory),
controller_manager(controller_manager) {}
~RascsiExecutor() = default;
unordered_set<int> GetReservedIds() const { return reserved_ids; }
bool ProcessCmd(const CommandContext&, const PbDeviceDefinition&, const PbCommand&, bool);
bool ProcessCmd(const CommandContext&, const PbCommand&);
bool SetLogLevel(const string&) const;
bool Start(shared_ptr<PrimaryDevice>, bool) const;
bool Stop(shared_ptr<PrimaryDevice>, bool) const;
bool Eject(shared_ptr<PrimaryDevice>, bool) const;
bool Protect(shared_ptr<PrimaryDevice>, bool) const;
bool Unprotect(shared_ptr<PrimaryDevice>, bool) const;
bool Attach(const CommandContext&, const PbDeviceDefinition&, bool);
bool Insert(const CommandContext&, const PbDeviceDefinition&, shared_ptr<PrimaryDevice>, bool) const;
bool Detach(const CommandContext&, shared_ptr<PrimaryDevice>, bool) const;
void DetachAll();
bool ShutDown(const CommandContext&, const string&);
string SetReservedIds(string_view);
bool ValidateImageFile(const CommandContext&, shared_ptr<PrimaryDevice>, const string&, string&) const;
void PrintCommand(const PbCommand&, const PbDeviceDefinition&, bool) const;
string ValidateLunSetup(const PbCommand&) const;
bool VerifyExistingIdAndLun(const CommandContext&, int, int) const;
shared_ptr<PrimaryDevice> CreateDevice(const CommandContext&, const PbDeviceType, int, const string&) const;
bool SetSectorSize(const CommandContext&, const string& type, shared_ptr<PrimaryDevice>, int) const;
static bool ValidationOperationAgainstDevice(const CommandContext&, const shared_ptr<PrimaryDevice>,
const PbOperation&);
static bool ValidateIdAndLun(const CommandContext&, int, int);
static bool SetProductData(const CommandContext&, const PbDeviceDefinition&, shared_ptr<PrimaryDevice>);
};

View File

@ -18,7 +18,7 @@
#include <string> #include <string>
#include <array> #include <array>
#include <filesystem> #include <filesystem>
#ifdef __linux #ifdef __linux__
#include <sys/sendfile.h> #include <sys/sendfile.h>
#endif #endif
@ -30,7 +30,7 @@ using namespace command_util;
RascsiImage::RascsiImage() RascsiImage::RascsiImage()
{ {
// ~/images is the default folder for device image files, for the root user it is /home/pi/images // ~/images is the default folder for device image files, for the root user it is /home/pi/images
default_image_folder = GetHomeDir() + "/images"; default_folder = GetHomeDir() + "/images";
} }
bool RascsiImage::CheckDepth(string_view filename) const bool RascsiImage::CheckDepth(string_view filename) const
@ -57,7 +57,7 @@ bool RascsiImage::CreateImageFolder(const CommandContext& context, const string&
return true; return true;
} }
string RascsiImage::SetDefaultImageFolder(const string& f) string RascsiImage::SetDefaultFolder(const string& f)
{ {
if (f.empty()) { if (f.empty()) {
return "Can't set default image folder: Missing folder name"; return "Can't set default image folder: Missing folder name";
@ -81,9 +81,9 @@ string RascsiImage::SetDefaultImageFolder(const string& f)
return "Folder '" + f + "' does not exist or is not accessible"; return "Folder '" + f + "' does not exist or is not accessible";
} }
default_image_folder = folder; default_folder = folder;
LOGINFO("Default image folder set to '%s'", default_image_folder.c_str()) LOGINFO("Default image folder set to '%s'", default_folder.c_str())
return ""; return "";
} }
@ -113,7 +113,7 @@ bool RascsiImage::CreateImage(const CommandContext& context, const PbCommand& co
return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + filename + "'").c_str()); return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + filename + "'").c_str());
} }
string full_filename = default_image_folder + "/" + filename; string full_filename = GetFullName(filename);
if (!IsValidDstFilename(full_filename)) { if (!IsValidDstFilename(full_filename)) {
return ReturnStatus(context, false, "Can't create image file: '" + full_filename + "': File already exists"); return ReturnStatus(context, false, "Can't create image file: '" + full_filename + "': File already exists");
} }
@ -127,10 +127,10 @@ bool RascsiImage::CreateImage(const CommandContext& context, const PbCommand& co
try { try {
len = stoull(size); len = stoull(size);
} }
catch(const invalid_argument&) { //NOSONAR This exception is handled properly catch(const invalid_argument&) {
return ReturnStatus(context, false, "Can't create image file '" + full_filename + "': Invalid file size " + size); return ReturnStatus(context, false, "Can't create image file '" + full_filename + "': Invalid file size " + size);
} }
catch(const out_of_range&) { //NOSONAR This exception is handled properly catch(const out_of_range&) {
return ReturnStatus(context, false, "Can't create image file '" + full_filename + "': Invalid file size " + size); return ReturnStatus(context, false, "Can't create image file '" + full_filename + "': Invalid file size " + size);
} }
if (len < 512 || (len & 0x1ff)) { if (len < 512 || (len & 0x1ff)) {
@ -151,7 +151,7 @@ bool RascsiImage::CreateImage(const CommandContext& context, const PbCommand& co
return ReturnStatus(context, false, "Can't create image file '" + full_filename + "': " + string(strerror(errno))); return ReturnStatus(context, false, "Can't create image file '" + full_filename + "': " + string(strerror(errno)));
} }
#ifndef __linux #ifndef __linux__
close(image_fd); close(image_fd);
unlink(full_filename.c_str()); unlink(full_filename.c_str());
@ -186,7 +186,7 @@ bool RascsiImage::DeleteImage(const CommandContext& context, const PbCommand& co
return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + filename + "'").c_str()); return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + filename + "'").c_str());
} }
string full_filename = default_image_folder + "/" + filename; string full_filename = GetFullName(filename);
int id; int id;
int unit; int unit;
@ -205,7 +205,7 @@ bool RascsiImage::DeleteImage(const CommandContext& context, const PbCommand& co
size_t last_slash = filename.rfind('/'); size_t last_slash = filename.rfind('/');
while (last_slash != string::npos) { while (last_slash != string::npos) {
string folder = filename.substr(0, last_slash); string folder = filename.substr(0, last_slash);
string full_folder = default_image_folder + "/" + folder; string full_folder = GetFullName(folder);
if (error_code error; !filesystem::is_empty(full_folder, error) || error) { if (error_code error; !filesystem::is_empty(full_folder, error) || error) {
break; break;
@ -234,7 +234,7 @@ bool RascsiImage::RenameImage(const CommandContext& context, const PbCommand& co
return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + from + "'").c_str()); return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + from + "'").c_str());
} }
from = default_image_folder + "/" + from; from = GetFullName(from);
if (!IsValidSrcFilename(from)) { if (!IsValidSrcFilename(from)) {
return ReturnStatus(context, false, "Can't rename/move image file: '" + from + "': Invalid name or type"); return ReturnStatus(context, false, "Can't rename/move image file: '" + from + "': Invalid name or type");
} }
@ -248,7 +248,7 @@ bool RascsiImage::RenameImage(const CommandContext& context, const PbCommand& co
return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + to + "'").c_str()); return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + to + "'").c_str());
} }
to = default_image_folder + "/" + to; to = GetFullName(to);
if (!IsValidDstFilename(to)) { if (!IsValidDstFilename(to)) {
return ReturnStatus(context, false, "Can't rename/move image file '" + from + "' to '" + to + "': File already exists"); return ReturnStatus(context, false, "Can't rename/move image file '" + from + "' to '" + to + "': File already exists");
} }
@ -277,7 +277,7 @@ bool RascsiImage::CopyImage(const CommandContext& context, const PbCommand& comm
return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + from + "'").c_str()); return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + from + "'").c_str());
} }
from = default_image_folder + "/" + from; from = GetFullName(from);
if (!IsValidSrcFilename(from)) { if (!IsValidSrcFilename(from)) {
return ReturnStatus(context, false, "Can't copy image file: '" + from + "': Invalid name or type"); return ReturnStatus(context, false, "Can't copy image file: '" + from + "': Invalid name or type");
} }
@ -291,7 +291,7 @@ bool RascsiImage::CopyImage(const CommandContext& context, const PbCommand& comm
return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + to + "'").c_str()); return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + to + "'").c_str());
} }
to = default_image_folder + "/" + to; to = GetFullName(to);
if (!IsValidDstFilename(to)) { if (!IsValidDstFilename(to)) {
return ReturnStatus(context, false, "Can't copy image file '" + from + "' to '" + to + "': File already exists"); return ReturnStatus(context, false, "Can't copy image file '" + from + "' to '" + to + "': File already exists");
} }
@ -333,7 +333,7 @@ bool RascsiImage::CopyImage(const CommandContext& context, const PbCommand& comm
return ReturnStatus(context, false, "Can't open destination image file '" + to + "': " + string(strerror(errno))); return ReturnStatus(context, false, "Can't open destination image file '" + to + "': " + string(strerror(errno)));
} }
#ifndef __linux #ifndef __linux__
close(fd_dst); close(fd_dst);
close(fd_src); close(fd_src);
@ -370,7 +370,7 @@ bool RascsiImage::SetImagePermissions(const CommandContext& context, const PbCom
return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + filename + "'").c_str()); return ReturnStatus(context, false, ("Invalid folder hierarchy depth '" + filename + "'").c_str());
} }
filename = default_image_folder + "/" + filename; filename = GetFullName(filename);
if (!IsValidSrcFilename(filename)) { if (!IsValidSrcFilename(filename)) {
return ReturnStatus(context, false, "Can't modify image file '" + filename + "': Invalid name or type"); return ReturnStatus(context, false, "Can't modify image file '" + filename + "': Invalid name or type");
} }

View File

@ -13,7 +13,7 @@
#include "command_context.h" #include "command_context.h"
#include <string> #include <string>
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi using namespace rascsi_interface;
class RascsiImage class RascsiImage
{ {
@ -21,15 +21,13 @@ public:
RascsiImage(); RascsiImage();
~RascsiImage() = default; ~RascsiImage() = default;
RascsiImage(RascsiImage&) = delete;
RascsiImage& operator=(const RascsiImage&) = delete;
void SetDepth(int d) { depth = d; } void SetDepth(int d) { depth = d; }
int GetDepth() const { return depth; } int GetDepth() const { return depth; }
bool CheckDepth(string_view) const; bool CheckDepth(string_view) const;
bool CreateImageFolder(const CommandContext&, const string&) const; bool CreateImageFolder(const CommandContext&, const string&) const;
string GetDefaultImageFolder() const { return default_image_folder; } string GetDefaultFolder() const { return default_folder; }
string SetDefaultImageFolder(const string&); string SetDefaultFolder(const string&);
bool IsValidSrcFilename(const string&) const; bool IsValidSrcFilename(const string&) const;
bool IsValidDstFilename(const string&) const; bool IsValidDstFilename(const string&) const;
bool CreateImage(const CommandContext&, const PbCommand&) const; bool CreateImage(const CommandContext&, const PbCommand&) const;
@ -37,12 +35,13 @@ public:
bool RenameImage(const CommandContext&, const PbCommand&) const; bool RenameImage(const CommandContext&, const PbCommand&) const;
bool CopyImage(const CommandContext&, const PbCommand&) const; bool CopyImage(const CommandContext&, const PbCommand&) const;
bool SetImagePermissions(const CommandContext&, const PbCommand&) const; bool SetImagePermissions(const CommandContext&, const PbCommand&) const;
string GetFullName(const string& filename) const { return default_folder + "/" + filename; }
private: private:
string GetHomeDir() const; string GetHomeDir() const;
string default_image_folder; string default_folder;
int depth = 1; int depth = 1;
}; };

View File

@ -7,59 +7,57 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include <dirent.h> #include "controllers/controller_manager.h"
#include "devices/file_support.h" #include "devices/file_support.h"
#include "devices/disk.h" #include "devices/disk.h"
#include "devices/device_factory.h" #include "devices/device_factory.h"
#include "command_util.h" #include "command_util.h"
#include "rascsi_version.h" #include "rascsi_version.h"
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include "rascsi_image.h"
#include "rascsi_response.h" #include "rascsi_response.h"
using namespace rascsi_interface; using namespace rascsi_interface;
using namespace command_util; using namespace command_util;
PbDeviceProperties *RascsiResponse::GetDeviceProperties(const Device *device) unique_ptr<PbDeviceProperties> RascsiResponse::GetDeviceProperties(const Device& device) const
{ {
auto properties = make_unique<PbDeviceProperties>().release(); auto properties = make_unique<PbDeviceProperties>();
properties->set_luns(ScsiController::LUN_MAX); properties->set_luns(max_luns);
properties->set_read_only(device->IsReadOnly()); properties->set_read_only(device.IsReadOnly());
properties->set_protectable(device->IsProtectable()); properties->set_protectable(device.IsProtectable());
properties->set_stoppable(device->IsStoppable()); properties->set_stoppable(device.IsStoppable());
properties->set_removable(device->IsRemovable()); properties->set_removable(device.IsRemovable());
properties->set_lockable(device->IsLockable()); properties->set_lockable(device.IsLockable());
properties->set_supports_file(dynamic_cast<const FileSupport *>(device) != nullptr); properties->set_supports_file(dynamic_cast<const FileSupport *>(&device) != nullptr);
properties->set_supports_params(device->SupportsParams()); properties->set_supports_params(device.SupportsParams());
PbDeviceType t = UNDEFINED; PbDeviceType t = UNDEFINED;
PbDeviceType_Parse(device->GetType(), &t); PbDeviceType_Parse(device.GetType(), &t);
if (device->SupportsParams()) { if (device.SupportsParams()) {
for (const auto& [key, value] : device_factory->GetDefaultParams(t)) { for (const auto& [key, value] : device_factory.GetDefaultParams(t)) {
auto& map = *properties->mutable_default_params(); auto& map = *properties->mutable_default_params();
map[key] = value; map[key] = value;
} }
} }
for (const auto& block_size : device_factory->GetSectorSizes(t)) { for (const auto& block_size : device_factory.GetSectorSizes(t)) {
properties->add_block_sizes(block_size); properties->add_block_sizes(block_size);
} }
return properties; return properties;
} }
void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type) void RascsiResponse::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type) const
{ {
PbDeviceTypeProperties *type_properties = device_types_info.add_properties(); auto type_properties = device_types_info.add_properties();
type_properties->set_type(type); type_properties->set_type(type);
const PrimaryDevice *device = device_factory->CreateDevice(type, "", -1); auto device = device_factory.CreateDevice(controller_manager, type, 0, "");
type_properties->set_allocated_properties(GetDeviceProperties(device)); type_properties->set_allocated_properties(GetDeviceProperties(*device).release());
device_factory->DeleteDevice(*device); //NOSONAR The allocated memory is managed by protobuf } //NOSONAR The allocated memory is managed by protobuf
}
void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) const
{ {
// Start with 2 instead of 1. 1 was the removed SASI drive type. // Start with 2 instead of 1. 1 was the removed SASI drive type.
int ordinal = 2; int ordinal = 2;
@ -71,60 +69,60 @@ void RascsiResponse::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_
} }
} }
void RascsiResponse::GetDevice(const Device *device, PbDevice *pb_device) void RascsiResponse::GetDevice(const Device& device, PbDevice& pb_device, const string& default_folder) const
{ {
pb_device->set_id(device->GetId()); pb_device.set_id(device.GetId());
pb_device->set_unit(device->GetLun()); pb_device.set_unit(device.GetLun());
pb_device->set_vendor(device->GetVendor()); pb_device.set_vendor(device.GetVendor());
pb_device->set_product(device->GetProduct()); pb_device.set_product(device.GetProduct());
pb_device->set_revision(device->GetRevision()); pb_device.set_revision(device.GetRevision());
PbDeviceType type = UNDEFINED; PbDeviceType type = UNDEFINED;
PbDeviceType_Parse(device->GetType(), &type); PbDeviceType_Parse(device.GetType(), &type);
pb_device->set_type(type); pb_device.set_type(type);
pb_device->set_allocated_properties(GetDeviceProperties(device)); pb_device.set_allocated_properties(GetDeviceProperties(device).release());
auto status = make_unique<PbDeviceStatus>().release(); auto status = make_unique<PbDeviceStatus>().release();
pb_device->set_allocated_status(status); pb_device.set_allocated_status(status);
status->set_protected_(device->IsProtected()); status->set_protected_(device.IsProtected());
status->set_stopped(device->IsStopped()); status->set_stopped(device.IsStopped());
status->set_removed(device->IsRemoved()); status->set_removed(device.IsRemoved());
status->set_locked(device->IsLocked()); status->set_locked(device.IsLocked());
if (device->SupportsParams()) { //NOSONAR The allocated memory is managed by protobuf if (device.SupportsParams()) { //NOSONAR The allocated memory is managed by protobuf
for (const auto& [key, value] : device->GetParams()) { for (const auto& [key, value] : device.GetParams()) {
AddParam(*pb_device, key, value); AddParam(pb_device, key, value);
} }
} }
if (const auto disk = dynamic_cast<const Disk*>(device); disk) { if (const auto disk = dynamic_cast<const Disk*>(&device); disk) {
pb_device->set_block_size(device->IsRemoved()? 0 : disk->GetSectorSizeInBytes()); pb_device.set_block_size(device.IsRemoved()? 0 : disk->GetSectorSizeInBytes());
pb_device->set_block_count(device->IsRemoved() ? 0: disk->GetBlockCount()); pb_device.set_block_count(device.IsRemoved() ? 0: disk->GetBlockCount());
} }
const auto file_support = dynamic_cast<const FileSupport *>(device); const auto file_support = dynamic_cast<const FileSupport *>(&device);
if (file_support) { if (file_support) {
Filepath filepath; Filepath filepath;
file_support->GetPath(filepath); file_support->GetPath(filepath);
auto image_file = make_unique<PbImageFile>().release(); auto image_file = make_unique<PbImageFile>().release();
GetImageFile(image_file, device->IsRemovable() && !device->IsReady() ? "" : filepath.GetPath()); GetImageFile(*image_file, default_folder, device.IsRemovable() && !device.IsReady() ? "" : filepath.GetPath());
pb_device->set_allocated_file(image_file); pb_device.set_allocated_file(image_file);
} }
} //NOSONAR The allocated memory is managed by protobuf } //NOSONAR The allocated memory is managed by protobuf
bool RascsiResponse::GetImageFile(PbImageFile *image_file, const string& filename) const bool RascsiResponse::GetImageFile(PbImageFile& image_file, const string& default_folder, const string& filename) const
{ {
if (!filename.empty()) { if (!filename.empty()) {
image_file->set_name(filename); image_file.set_name(filename);
image_file->set_type(device_factory->GetTypeForFile(filename)); image_file.set_type(device_factory.GetTypeForFile(filename));
string f = filename[0] == '/' ? filename : rascsi_image->GetDefaultImageFolder() + "/" + filename; string f = filename[0] == '/' ? filename : default_folder + "/" + filename;
image_file->set_read_only(access(f.c_str(), W_OK)); image_file.set_read_only(access(f.c_str(), W_OK));
if (struct stat st; !stat(f.c_str(), &st) && !S_ISDIR(st.st_mode)) { if (struct stat st; !stat(f.c_str(), &st) && !S_ISDIR(st.st_mode)) {
image_file->set_size(st.st_size); image_file.set_size(st.st_size);
return true; return true;
} }
} }
@ -132,8 +130,9 @@ bool RascsiResponse::GetImageFile(PbImageFile *image_file, const string& filenam
return false; return false;
} }
void RascsiResponse::GetAvailableImages(PbImageFilesInfo& image_files_info, string_view default_image_folder, void RascsiResponse::GetAvailableImages(PbImageFilesInfo& image_files_info, const string& default_folder,
const string& folder, const string& folder_pattern, const string& file_pattern, int scan_depth) { const string& folder, const string& folder_pattern, const string& file_pattern, int scan_depth) const
{
if (scan_depth-- < 0) { if (scan_depth-- < 0) {
return; return;
} }
@ -151,35 +150,29 @@ void RascsiResponse::GetAvailableImages(PbImageFilesInfo& image_files_info, stri
const dirent *dir; const dirent *dir;
while ((dir = readdir(d))) { while ((dir = readdir(d))) {
bool is_supported_type = dir->d_type == DT_REG || dir->d_type == DT_DIR || dir->d_type == DT_LNK || dir->d_type == DT_BLK; string filename = GetNextImageFile(dir, folder);
if (is_supported_type && dir->d_name[0] != '.') { if (filename.empty()) {
string name_lower = dir->d_name; continue;
if (!file_pattern.empty()) { }
transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
string name_lower = dir->d_name;
if (!file_pattern.empty()) {
transform(name_lower.begin(), name_lower.end(), name_lower.begin(), ::tolower);
}
if (dir->d_type == DT_DIR) {
if (folder_pattern_lower.empty() || name_lower.find(folder_pattern_lower) != string::npos) {
GetAvailableImages(image_files_info, default_folder, filename, folder_pattern,
file_pattern, scan_depth);
} }
string filename = folder + "/" + dir->d_name; continue;
}
if (struct stat st; dir->d_type == DT_REG && !stat(filename.c_str(), &st)) { if (file_pattern_lower.empty() || name_lower.find(file_pattern_lower) != string::npos) {
if (!st.st_size) { if (auto image_file = make_unique<PbImageFile>(); GetImageFile(*image_file.get(), default_folder, filename)) {
LOGWARN("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name, folder.c_str()) GetImageFile(*image_files_info.add_image_files(), default_folder,
continue; filename.substr(default_folder.length() + 1));
}
} else if (dir->d_type == DT_LNK && stat(filename.c_str(), &st)) {
LOGWARN("Symlink '%s' in image folder '%s' is broken", dir->d_name, folder.c_str())
continue;
} else if (dir->d_type == DT_DIR) {
if (folder_pattern_lower.empty() || name_lower.find(folder_pattern_lower) != string::npos) {
GetAvailableImages(image_files_info, default_image_folder, filename, folder_pattern,
file_pattern, scan_depth);
}
continue;
}
if (file_pattern_lower.empty() || name_lower.find(file_pattern_lower) != string::npos) {
if (auto image_file = make_unique<PbImageFile>(); GetImageFile(image_file.get(), filename)) {
GetImageFile(image_files_info.add_image_files(), filename.substr(default_image_folder.length() + 1));
}
} }
} }
} }
@ -187,16 +180,15 @@ void RascsiResponse::GetAvailableImages(PbImageFilesInfo& image_files_info, stri
closedir(d); closedir(d);
} }
PbImageFilesInfo *RascsiResponse::GetAvailableImages(PbResult& result, const string& folder_pattern, unique_ptr<PbImageFilesInfo> RascsiResponse::GetAvailableImages(PbResult& result, const string& default_folder,
const string& file_pattern, int scan_depth) const string& folder_pattern, const string& file_pattern, int scan_depth) const
{ {
auto image_files_info = make_unique<PbImageFilesInfo>().release(); auto image_files_info = make_unique<PbImageFilesInfo>();
string default_image_folder = rascsi_image->GetDefaultImageFolder(); image_files_info->set_default_image_folder(default_folder);
image_files_info->set_default_image_folder(default_image_folder);
image_files_info->set_depth(scan_depth); image_files_info->set_depth(scan_depth);
GetAvailableImages(*image_files_info, default_image_folder, default_image_folder, folder_pattern, GetAvailableImages(*image_files_info, default_folder, default_folder, folder_pattern,
file_pattern, scan_depth); file_pattern, scan_depth);
result.set_status(true); result.set_status(true);
@ -204,19 +196,19 @@ PbImageFilesInfo *RascsiResponse::GetAvailableImages(PbResult& result, const str
return image_files_info; return image_files_info;
} }
void RascsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info, const string& folder_pattern, void RascsiResponse::GetAvailableImages(PbResult& result, PbServerInfo& server_info, const string& default_folder,
const string& file_pattern, int scan_depth) const string& folder_pattern, const string& file_pattern, int scan_depth) const
{ {
PbImageFilesInfo *image_files_info = GetAvailableImages(result, folder_pattern, file_pattern, scan_depth); auto image_files_info = GetAvailableImages(result, default_folder, folder_pattern, file_pattern, scan_depth);
image_files_info->set_default_image_folder(rascsi_image->GetDefaultImageFolder()); image_files_info->set_default_image_folder(default_folder);
server_info.set_allocated_image_files_info(image_files_info); server_info.set_allocated_image_files_info(image_files_info.release());
result.set_status(true); //NOSONAR The allocated memory is managed by protobuf result.set_status(true); //NOSONAR The allocated memory is managed by protobuf
} }
PbReservedIdsInfo *RascsiResponse::GetReservedIds(PbResult& result, const unordered_set<int>& ids) unique_ptr<PbReservedIdsInfo> RascsiResponse::GetReservedIds(PbResult& result, const unordered_set<int>& ids) const
{ {
auto reserved_ids_info = make_unique<PbReservedIdsInfo>().release(); auto reserved_ids_info = make_unique<PbReservedIdsInfo>();
for (int id : ids) { for (int id : ids) {
reserved_ids_info->add_ids(id); reserved_ids_info->add_ids(id);
} }
@ -226,48 +218,52 @@ PbReservedIdsInfo *RascsiResponse::GetReservedIds(PbResult& result, const unorde
return reserved_ids_info; return reserved_ids_info;
} }
void RascsiResponse::GetDevices(PbServerInfo& server_info) void RascsiResponse::GetDevices(PbServerInfo& server_info, const string& default_folder) const
{ {
for (const Device *device : device_factory->GetAllDevices()) { for (const auto& device : controller_manager.GetAllDevices()) {
PbDevice *pb_device = server_info.mutable_devices_info()->add_devices(); PbDevice *pb_device = server_info.mutable_devices_info()->add_devices();
GetDevice(device, pb_device); GetDevice(*device, *pb_device, default_folder);
} }
} }
void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command) void RascsiResponse::GetDevicesInfo(PbResult& result, const PbCommand& command, const string& default_folder) const
{ {
set<id_set> id_sets; set<id_set> id_sets;
const auto& devices = controller_manager.GetAllDevices();
// If no device list was provided in the command get information on all devices
if (!command.devices_size()) { if (!command.devices_size()) {
for (const Device *device : device_factory->GetAllDevices()) { for (const auto& device : devices) {
id_sets.insert(make_pair(device->GetId(), device->GetLun())); id_sets.insert(make_pair(device->GetId(), device->GetLun()));
} }
} }
// Otherwise get information on the devices provided in the command
else { else {
for (const auto& device : command.devices()) { id_sets = MatchDevices(result, command);
if (device_factory->GetDeviceByIdAndLun(device.id(), device.unit())) { if (id_sets.empty()) {
id_sets.insert(make_pair(device.id(), device.unit())); return;
} }
else { }
result.set_status(false);
result.set_msg("No device for ID " + to_string(device.id()) + ", unit " + to_string(device.unit())); auto devices_info = make_unique<PbDevicesInfo>();
return;
for (const auto& [id, lun] : id_sets) {
for (const auto& d : devices) {
if (d->GetId() == id && d->GetLun() == lun) {
GetDevice(*d, *devices_info->add_devices(), default_folder);
break;
} }
} }
} }
auto devices_info = make_unique<PbDevicesInfo>().release(); result.set_allocated_devices_info(devices_info.release());
result.set_allocated_devices_info(devices_info);
for (const auto& [id, lun] : id_sets) {
GetDevice(device_factory->GetDeviceByIdAndLun(id, lun), devices_info->add_devices());
}
result.set_status(true); result.set_status(true);
} }
PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result) unique_ptr<PbDeviceTypesInfo> RascsiResponse::GetDeviceTypesInfo(PbResult& result) const
{ {
auto device_types_info = make_unique<PbDeviceTypesInfo>().release(); auto device_types_info = make_unique<PbDeviceTypesInfo>();
GetAllDeviceTypeProperties(*device_types_info); GetAllDeviceTypeProperties(*device_types_info);
@ -276,29 +272,30 @@ PbDeviceTypesInfo *RascsiResponse::GetDeviceTypesInfo(PbResult& result)
return device_types_info; return device_types_info;
} }
PbServerInfo *RascsiResponse::GetServerInfo(PbResult& result, const unordered_set<int>& reserved_ids, unique_ptr<PbServerInfo> RascsiResponse::GetServerInfo(PbResult& result, const unordered_set<int>& reserved_ids,
const string& current_log_level, const string& folder_pattern, const string& file_pattern, int scan_depth) const string& current_log_level, const string& default_folder, const string& folder_pattern,
const string& file_pattern, int scan_depth) const
{ {
auto server_info = make_unique<PbServerInfo>().release(); auto server_info = make_unique<PbServerInfo>();
server_info->set_allocated_version_info(GetVersionInfo(result)); server_info->set_allocated_version_info(GetVersionInfo(result).release());
server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level)); //NOSONAR The allocated memory is managed by protobuf server_info->set_allocated_log_level_info(GetLogLevelInfo(result, current_log_level).release()); //NOSONAR The allocated memory is managed by protobuf
GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); //NOSONAR The allocated memory is managed by protobuf GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); //NOSONAR The allocated memory is managed by protobuf
GetAvailableImages(result, *server_info, folder_pattern, file_pattern, scan_depth); GetAvailableImages(result, *server_info, default_folder, folder_pattern, file_pattern, scan_depth);
server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result)); server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result).release());
server_info->set_allocated_mapping_info(GetMappingInfo(result)); //NOSONAR The allocated memory is managed by protobuf server_info->set_allocated_mapping_info(GetMappingInfo(result).release()); //NOSONAR The allocated memory is managed by protobuf
GetDevices(*server_info); //NOSONAR The allocated memory is managed by protobuf GetDevices(*server_info, default_folder); //NOSONAR The allocated memory is managed by protobuf
server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids)); server_info->set_allocated_reserved_ids_info(GetReservedIds(result, reserved_ids).release());
server_info->set_allocated_operation_info(GetOperationInfo(result, scan_depth)); //NOSONAR The allocated memory is managed by protobuf server_info->set_allocated_operation_info(GetOperationInfo(result, scan_depth).release()); //NOSONAR The allocated memory is managed by protobuf
result.set_status(true); result.set_status(true);
return server_info; return server_info;
} }
PbVersionInfo *RascsiResponse::GetVersionInfo(PbResult& result) unique_ptr<PbVersionInfo> RascsiResponse::GetVersionInfo(PbResult& result) const
{ {
auto version_info = make_unique<PbVersionInfo>().release(); auto version_info = make_unique<PbVersionInfo>();
version_info->set_major_version(rascsi_major_version); version_info->set_major_version(rascsi_major_version);
version_info->set_minor_version(rascsi_minor_version); version_info->set_minor_version(rascsi_minor_version);
@ -309,9 +306,9 @@ PbVersionInfo *RascsiResponse::GetVersionInfo(PbResult& result)
return version_info; return version_info;
} }
PbLogLevelInfo *RascsiResponse::GetLogLevelInfo(PbResult& result, const string& current_log_level) unique_ptr<PbLogLevelInfo> RascsiResponse::GetLogLevelInfo(PbResult& result, const string& current_log_level) const
{ {
auto log_level_info = make_unique<PbLogLevelInfo>().release(); auto log_level_info = make_unique<PbLogLevelInfo>();
for (const auto& log_level : log_levels) { for (const auto& log_level : log_levels) {
log_level_info->add_log_levels(log_level); log_level_info->add_log_levels(log_level);
@ -324,11 +321,11 @@ PbLogLevelInfo *RascsiResponse::GetLogLevelInfo(PbResult& result, const string&
return log_level_info; return log_level_info;
} }
PbNetworkInterfacesInfo *RascsiResponse::GetNetworkInterfacesInfo(PbResult& result) unique_ptr<PbNetworkInterfacesInfo> RascsiResponse::GetNetworkInterfacesInfo(PbResult& result) const
{ {
auto network_interfaces_info = make_unique<PbNetworkInterfacesInfo>().release(); auto network_interfaces_info = make_unique<PbNetworkInterfacesInfo>();
for (const auto& network_interface : device_factory->GetNetworkInterfaces()) { for (const auto& network_interface : device_factory.GetNetworkInterfaces()) {
network_interfaces_info->add_name(network_interface); network_interfaces_info->add_name(network_interface);
} }
@ -337,11 +334,11 @@ PbNetworkInterfacesInfo *RascsiResponse::GetNetworkInterfacesInfo(PbResult& resu
return network_interfaces_info; return network_interfaces_info;
} }
PbMappingInfo *RascsiResponse::GetMappingInfo(PbResult& result) unique_ptr<PbMappingInfo> RascsiResponse::GetMappingInfo(PbResult& result) const
{ {
auto mapping_info = make_unique<PbMappingInfo>().release(); auto mapping_info = make_unique<PbMappingInfo>();
for (const auto& [name, type] : device_factory->GetExtensionMapping()) { for (const auto& [name, type] : device_factory.GetExtensionMapping()) {
(*mapping_info->mutable_mapping())[name] = type; (*mapping_info->mutable_mapping())[name] = type;
} }
@ -350,131 +347,147 @@ PbMappingInfo *RascsiResponse::GetMappingInfo(PbResult& result)
return mapping_info; return mapping_info;
} }
PbOperationInfo *RascsiResponse::GetOperationInfo(PbResult& result, int depth) unique_ptr<PbOperationInfo> RascsiResponse::GetOperationInfo(PbResult& result, int depth) const
{ {
auto operation_info = make_unique<PbOperationInfo>(); auto operation_info = make_unique<PbOperationInfo>();
auto operation = CreateOperation(*operation_info, ATTACH, "Attach device, device-specific parameters are required"); auto operation = CreateOperation(*operation_info, ATTACH, "Attach device, device-specific parameters are required");
AddOperationParameter(*operation, "name", "Image file name in case of a mass storage device"); AddOperationParameter(*operation, "name", "Image file name in case of a mass storage device").release();
AddOperationParameter(*operation, "interface", "Comma-separated prioritized network interface list"); AddOperationParameter(*operation, "interface", "Comma-separated prioritized network interface list").release();
AddOperationParameter(*operation, "inet", "IP address and netmask of the network bridge"); AddOperationParameter(*operation, "inet", "IP address and netmask of the network bridge").release();
AddOperationParameter(*operation, "cmd", "Print command for the printer device"); AddOperationParameter(*operation, "cmd", "Print command for the printer device").release();
AddOperationParameter(*operation, "timeout", "Reservation timeout for the printer device in seconds"); AddOperationParameter(*operation, "timeout", "Reservation timeout for the printer device in seconds").release();
operation.release();
CreateOperation(*operation_info, DETACH, "Detach device, device-specific parameters are required"); CreateOperation(*operation_info, DETACH, "Detach device, device-specific parameters are required").release();
CreateOperation(*operation_info, DETACH_ALL, "Detach all devices"); CreateOperation(*operation_info, DETACH_ALL, "Detach all devices").release();
CreateOperation(*operation_info, START, "Start device, device-specific parameters are required"); CreateOperation(*operation_info, START, "Start device, device-specific parameters are required").release();
CreateOperation(*operation_info, STOP, "Stop device, device-specific parameters are required"); CreateOperation(*operation_info, STOP, "Stop device, device-specific parameters are required").release();
operation = CreateOperation(*operation_info, INSERT, "Insert medium, device-specific parameters are required"); operation = CreateOperation(*operation_info, INSERT, "Insert medium, device-specific parameters are required");
AddOperationParameter(*operation, "file", "Image file name", "", true); AddOperationParameter(*operation, "file", "Image file name", "", true).release();
operation.release();
CreateOperation(*operation_info, EJECT, "Eject medium, device-specific parameters are required"); CreateOperation(*operation_info, EJECT, "Eject medium, device-specific parameters are required").release();
CreateOperation(*operation_info, PROTECT, "Protect medium, device-specific parameters are required"); CreateOperation(*operation_info, PROTECT, "Protect medium, device-specific parameters are required").release();
CreateOperation(*operation_info, UNPROTECT, "Unprotect medium, device-specific parameters are required"); CreateOperation(*operation_info, UNPROTECT, "Unprotect medium, device-specific parameters are required").release();
operation = CreateOperation(*operation_info, SERVER_INFO, "Get rascsi server information"); operation = CreateOperation(*operation_info, SERVER_INFO, "Get rascsi server information");
if (depth) { if (depth) {
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names"); AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names").release();
} }
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names"); AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names").release();
operation.release();
CreateOperation(*operation_info, VERSION_INFO, "Get rascsi server version"); CreateOperation(*operation_info, VERSION_INFO, "Get rascsi server version").release();
CreateOperation(*operation_info, DEVICES_INFO, "Get information on attached devices"); CreateOperation(*operation_info, DEVICES_INFO, "Get information on attached devices").release();
CreateOperation(*operation_info, DEVICE_TYPES_INFO, "Get device properties by device type"); CreateOperation(*operation_info, DEVICE_TYPES_INFO, "Get device properties by device type").release();
operation = CreateOperation(*operation_info, DEFAULT_IMAGE_FILES_INFO, "Get information on available image files"); operation = CreateOperation(*operation_info, DEFAULT_IMAGE_FILES_INFO, "Get information on available image files");
if (depth) { if (depth) {
AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names"); AddOperationParameter(*operation, "folder_pattern", "Pattern for filtering image folder names").release();
} }
AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names"); AddOperationParameter(*operation, "file_pattern", "Pattern for filtering image file names").release();
operation.release();
operation = CreateOperation(*operation_info, IMAGE_FILE_INFO, "Get information on image file"); operation = CreateOperation(*operation_info, IMAGE_FILE_INFO, "Get information on image file");
AddOperationParameter(*operation, "file", "Image file name", "", true); AddOperationParameter(*operation, "file", "Image file name", "", true).release();
operation.release();
CreateOperation(*operation_info, LOG_LEVEL_INFO, "Get log level information"); CreateOperation(*operation_info, LOG_LEVEL_INFO, "Get log level information").release();
CreateOperation(*operation_info, NETWORK_INTERFACES_INFO, "Get the available network interfaces"); CreateOperation(*operation_info, NETWORK_INTERFACES_INFO, "Get the available network interfaces").release();
CreateOperation(*operation_info, MAPPING_INFO, "Get mapping of extensions to device types"); CreateOperation(*operation_info, MAPPING_INFO, "Get mapping of extensions to device types").release();
CreateOperation(*operation_info, RESERVED_IDS_INFO, "Get list of reserved device IDs"); CreateOperation(*operation_info, RESERVED_IDS_INFO, "Get list of reserved device IDs").release();
operation = CreateOperation(*operation_info, DEFAULT_FOLDER, "Set default image file folder"); operation = CreateOperation(*operation_info, DEFAULT_FOLDER, "Set default image file folder");
AddOperationParameter(*operation, "folder", "Default image file folder name", "", true); AddOperationParameter(*operation, "folder", "Default image file folder name", "", true).release();
operation.release();
operation = CreateOperation(*operation_info, LOG_LEVEL, "Set log level"); operation = CreateOperation(*operation_info, LOG_LEVEL, "Set log level");
AddOperationParameter(*operation, "level", "New log level", "", true); AddOperationParameter(*operation, "level", "New log level", "", true).release();
operation.release();
operation = CreateOperation(*operation_info, RESERVE_IDS, "Reserve device IDs"); operation = CreateOperation(*operation_info, RESERVE_IDS, "Reserve device IDs");
AddOperationParameter(*operation, "ids", "Comma-separated device ID list", "", true); AddOperationParameter(*operation, "ids", "Comma-separated device ID list", "", true).release();
operation.release();
operation = CreateOperation(*operation_info, SHUT_DOWN, "Shut down or reboot"); operation = CreateOperation(*operation_info, SHUT_DOWN, "Shut down or reboot");
PbOperationParameter *parameter = AddOperationParameter(*operation, "mode", "Shutdown mode", "", true); auto parameter = AddOperationParameter(*operation, "mode", "Shutdown mode", "", true).release();
parameter->add_permitted_values("rascsi"); parameter->add_permitted_values("rascsi");
// System shutdown/reboot requires root permissions // System shutdown/reboot requires root permissions
if (!getuid()) { if (!getuid()) {
parameter->add_permitted_values("system"); parameter->add_permitted_values("system");
parameter->add_permitted_values("reboot"); parameter->add_permitted_values("reboot");
} }
operation.release();
operation = CreateOperation(*operation_info, CREATE_IMAGE, "Create an image file"); operation = CreateOperation(*operation_info, CREATE_IMAGE, "Create an image file");
AddOperationParameter(*operation, "file", "Image file name", "", true); AddOperationParameter(*operation, "file", "Image file name", "", true).release();
AddOperationParameter(*operation, "size", "Image file size in bytes", "", true); AddOperationParameter(*operation, "size", "Image file size in bytes", "", true).release();
parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false"); parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false").release();
parameter->add_permitted_values("true"); parameter->add_permitted_values("true");
parameter->add_permitted_values("false"); parameter->add_permitted_values("false");
operation.release();
operation = CreateOperation(*operation_info, DELETE_IMAGE, "Delete image file"); operation = CreateOperation(*operation_info, DELETE_IMAGE, "Delete image file");
AddOperationParameter(*operation, "file", "Image file name", "", true); AddOperationParameter(*operation, "file", "Image file name", "", true).release();
operation.release();
operation = CreateOperation(*operation_info, RENAME_IMAGE, "Rename image file"); operation = CreateOperation(*operation_info, RENAME_IMAGE, "Rename image file");
AddOperationParameter(*operation, "from", "Source image file name", "", true); AddOperationParameter(*operation, "from", "Source image file name", "", true).release();
AddOperationParameter(*operation, "to", "Destination image file name", "", true); AddOperationParameter(*operation, "to", "Destination image file name", "", true).release();
operation.release();
operation = CreateOperation(*operation_info, COPY_IMAGE, "Copy image file"); operation = CreateOperation(*operation_info, COPY_IMAGE, "Copy image file");
AddOperationParameter(*operation, "from", "Source image file name", "", true); AddOperationParameter(*operation, "from", "Source image file name", "", true).release();
AddOperationParameter(*operation, "to", "Destination image file name", "", true); AddOperationParameter(*operation, "to", "Destination image file name", "", true).release();
parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false"); parameter = AddOperationParameter(*operation, "read_only", "Read-only flag", "false").release();
parameter->add_permitted_values("true"); parameter->add_permitted_values("true");
parameter->add_permitted_values("false"); parameter->add_permitted_values("false");
operation.release();
operation = CreateOperation(*operation_info, PROTECT_IMAGE, "Write-protect image file"); operation = CreateOperation(*operation_info, PROTECT_IMAGE, "Write-protect image file");
AddOperationParameter(*operation, "file", "Image file name", "", true); AddOperationParameter(*operation, "file", "Image file name", "", true).release();
operation.release();
operation = CreateOperation(*operation_info, UNPROTECT_IMAGE, "Make image file writable"); operation = CreateOperation(*operation_info, UNPROTECT_IMAGE, "Make image file writable");
AddOperationParameter(*operation, "file", "Image file name", "", true); AddOperationParameter(*operation, "file", "Image file name", "", true).release();
operation.release();
operation = CreateOperation(*operation_info, CHECK_AUTHENTICATION, "Check whether an authentication token is valid"); operation = CreateOperation(*operation_info, CHECK_AUTHENTICATION, "Check whether an authentication token is valid");
AddOperationParameter(*operation, "token", "Authentication token to be checked", "", true); AddOperationParameter(*operation, "token", "Authentication token to be checked", "", true).release();
operation.release();
CreateOperation(*operation_info, OPERATION_INFO, "Get operation meta data"); CreateOperation(*operation_info, OPERATION_INFO, "Get operation meta data").release();
result.set_status(true); result.set_status(true);
return operation_info.release(); return operation_info;
} }
PbOperationMetaData *RascsiResponse::CreateOperation(PbOperationInfo& operation_info, const PbOperation& operation, unique_ptr<PbOperationMetaData> RascsiResponse::CreateOperation(PbOperationInfo& operation_info, const PbOperation& operation,
const string& description) const const string& description) const
{ {
auto meta_data = make_shared<PbOperationMetaData>(); auto meta_data = make_unique<PbOperationMetaData>();
meta_data->set_server_side_name(PbOperation_Name(operation)); meta_data->set_server_side_name(PbOperation_Name(operation));
meta_data->set_description(description); meta_data->set_description(description);
int ordinal = PbOperation_descriptor()->FindValueByName(PbOperation_Name(operation))->index(); int ordinal = PbOperation_descriptor()->FindValueByName(PbOperation_Name(operation))->index();
(*operation_info.mutable_operations())[ordinal] = *meta_data.get(); (*operation_info.mutable_operations())[ordinal] = *meta_data.release();
return &(*operation_info.mutable_operations())[ordinal]; return unique_ptr<PbOperationMetaData>(&(*operation_info.mutable_operations())[ordinal]);
} }
PbOperationParameter *RascsiResponse::AddOperationParameter(PbOperationMetaData& meta_data, const string& name, unique_ptr<PbOperationParameter> RascsiResponse::AddOperationParameter(PbOperationMetaData& meta_data,
const string& description, const string& default_value, bool is_mandatory) const string& name, const string& description, const string& default_value, bool is_mandatory) const
{ {
auto parameter = unique_ptr<PbOperationParameter>(meta_data.add_parameters()); auto parameter = unique_ptr<PbOperationParameter>(meta_data.add_parameters());
parameter->set_name(name); parameter->set_name(name);
@ -482,5 +495,59 @@ PbOperationParameter *RascsiResponse::AddOperationParameter(PbOperationMetaData&
parameter->set_default_value(default_value); parameter->set_default_value(default_value);
parameter->set_is_mandatory(is_mandatory); parameter->set_is_mandatory(is_mandatory);
return parameter.release(); return parameter;
}
set<id_set> RascsiResponse::MatchDevices(PbResult& result, const PbCommand& command) const
{
set<id_set> id_sets;
for (const auto& device : command.devices()) {
bool has_device = false;
for (const auto& d : controller_manager.GetAllDevices()) {
if (d->GetId() == device.id() && d->GetLun() == device.unit()) {
id_sets.insert(make_pair(device.id(), device.unit()));
has_device = true;
break;
}
}
if (!has_device) {
id_sets.clear();
result.set_status(false);
result.set_msg("No device for ID " + to_string(device.id()) + ", unit " + to_string(device.unit()));
break;
}
}
return id_sets;
}
string RascsiResponse::GetNextImageFile(const dirent *dir, const string& folder)
{
// Ignore unknown folder types and folder names starting with '.'
if ((dir->d_type != DT_REG && dir->d_type != DT_DIR && dir->d_type != DT_LNK && dir->d_type != DT_BLK)
|| dir->d_name[0] == '.') {
return "";
}
string filename = folder + "/" + dir->d_name;
struct stat st;
bool file_exists = !stat(filename.c_str(), &st);
if (dir->d_type == DT_REG && file_exists && !st.st_size) {
LOGWARN("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name, folder.c_str())
return "";
}
if (dir->d_type == DT_LNK && !file_exists) {
LOGWARN("Symlink '%s' in image folder '%s' is broken", dir->d_name, folder.c_str())
return "";
}
return filename;
} }

View File

@ -9,54 +9,60 @@
#pragma once #pragma once
#include "devices/device_factory.h"
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include <dirent.h>
#include <list> #include <list>
#include <string> #include <string>
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi using namespace std;
using namespace rascsi_interface;
class DeviceFactory; class DeviceFactory;
class RascsiImage; class ControllerManager;
class Device; class Device;
class RascsiResponse class RascsiResponse
{ {
public: public:
RascsiResponse(DeviceFactory *device_factory, const RascsiImage *rascsi_image) RascsiResponse(DeviceFactory& device_factory, const ControllerManager& controller_manager, int max_luns)
: device_factory(device_factory), rascsi_image(rascsi_image) {} : device_factory(device_factory), controller_manager(controller_manager), max_luns(max_luns) {}
~RascsiResponse() = default; ~RascsiResponse() = default;
RascsiResponse(RascsiResponse&) = delete;
RascsiResponse& operator=(const RascsiResponse&) = delete;
bool GetImageFile(PbImageFile *, const string&) const; bool GetImageFile(PbImageFile&, const string&, const string&) const;
PbImageFilesInfo *GetAvailableImages(PbResult&, const string&, const string&, int); unique_ptr<PbImageFilesInfo> GetAvailableImages(PbResult&, const string&, const string&, const string&, int) const;
PbReservedIdsInfo *GetReservedIds(PbResult&, const unordered_set<int>&); unique_ptr<PbReservedIdsInfo> GetReservedIds(PbResult&, const unordered_set<int>&) const;
void GetDevices(PbServerInfo&); void GetDevices(PbServerInfo&, const string&) const;
void GetDevicesInfo(PbResult&, const PbCommand&); void GetDevicesInfo(PbResult&, const PbCommand&, const string&) const;
PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&); unique_ptr<PbDeviceTypesInfo> GetDeviceTypesInfo(PbResult&) const;
PbVersionInfo *GetVersionInfo(PbResult&); unique_ptr<PbVersionInfo> GetVersionInfo(PbResult&) const;
PbServerInfo *GetServerInfo(PbResult&, const unordered_set<int>&, const string&, const string&, const string&, int); unique_ptr<PbServerInfo> GetServerInfo(PbResult&, const unordered_set<int>&, const string&, const string&, const string&,
PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&); const string&, int) const;
PbMappingInfo *GetMappingInfo(PbResult&); unique_ptr<PbNetworkInterfacesInfo> GetNetworkInterfacesInfo(PbResult&) const;
PbLogLevelInfo *GetLogLevelInfo(PbResult&, const string&); unique_ptr<PbMappingInfo> GetMappingInfo(PbResult&) const;
PbOperationInfo *GetOperationInfo(PbResult&, int); unique_ptr<PbLogLevelInfo> GetLogLevelInfo(PbResult&, const string&) const;
unique_ptr<PbOperationInfo> GetOperationInfo(PbResult&, int) const;
private: private:
DeviceFactory *device_factory; DeviceFactory& device_factory;
const RascsiImage *rascsi_image;
const ControllerManager& controller_manager;
int max_luns;
const list<string> log_levels = { "trace", "debug", "info", "warn", "err", "critical", "off" }; const list<string> log_levels = { "trace", "debug", "info", "warn", "err", "critical", "off" };
PbDeviceProperties *GetDeviceProperties(const Device *); unique_ptr<PbDeviceProperties> GetDeviceProperties(const Device&) const;
void GetDevice(const Device *, PbDevice *); void GetDevice(const Device&, PbDevice&, const string&) const;
void GetAllDeviceTypeProperties(PbDeviceTypesInfo&); void GetAllDeviceTypeProperties(PbDeviceTypesInfo&) const;
void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType); void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType) const;
void GetAvailableImages(PbImageFilesInfo&, string_view, const string&, const string&, const string&, int); void GetAvailableImages(PbImageFilesInfo&, const string&, const string&, const string&, const string&, int) const;
void GetAvailableImages(PbResult& result, PbServerInfo&, const string&, const string&, int); void GetAvailableImages(PbResult& result, PbServerInfo&, const string&, const string&, const string&, int) const;
PbOperationMetaData *CreateOperation(PbOperationInfo&, const PbOperation&, const string&) const; unique_ptr<PbOperationMetaData> CreateOperation(PbOperationInfo&, const PbOperation&, const string&) const;
PbOperationParameter *AddOperationParameter(PbOperationMetaData&, const string&, const string&, unique_ptr<PbOperationParameter> AddOperationParameter(PbOperationMetaData&, const string&, const string&,
const string& = "", bool = false); const string& = "", bool = false) const;
set<id_set> MatchDevices(PbResult&, const PbCommand&) const;
static string GetNextImageFile(const dirent *, const string&);
}; };

View File

@ -0,0 +1,138 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "log.h"
#include "rascsi_exceptions.h"
#include "command_context.h"
#include "protobuf_serializer.h"
#include "localizer.h"
#include "rasutil.h"
#include "rascsi_service.h"
#include <netinet/in.h>
#include <csignal>
using namespace rascsi_interface;
volatile bool RascsiService::running = false;
RascsiService::~RascsiService()
{
if (service_socket != -1) {
close(service_socket);
}
}
bool RascsiService::Init(bool (e)(PbCommand&, CommandContext&), int port)
{
// Create socket for monitor
sockaddr_in server = {};
service_socket = socket(PF_INET, SOCK_STREAM, 0);
if (service_socket == -1) {
LOGERROR("Unable to create socket");
return false;
}
server.sin_family = PF_INET;
server.sin_port = htons(port);
server.sin_addr.s_addr = htonl(INADDR_ANY);
// Allow address reuse
if (int yes = 1; setsockopt(service_socket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) {
return false;
}
signal(SIGPIPE, SIG_IGN);
if (bind(service_socket, (sockaddr *)&server, sizeof(sockaddr_in)) < 0) {
cerr << "Error: Port " << port << " is in use, is rascsi already running?" << endl;
return false;
}
execute = e;
monthread = thread(&RascsiService::Execute, this);
monthread.detach();
// Interrupt handler settings
return signal(SIGINT, KillHandler) != SIG_ERR && signal(SIGHUP, KillHandler) != SIG_ERR
&& signal(SIGTERM, KillHandler) != SIG_ERR;
}
void RascsiService::Execute()
{
#ifdef __linux__
// Scheduler Settings
sched_param schedparam;
schedparam.sched_priority = 0;
sched_setscheduler(0, SCHED_IDLE, &schedparam);
#endif
// Set the affinity to a specific processor core
ras_util::FixCpu(2);
// Wait for the execution to start
while (!running) {
usleep(1);
}
// Set up the monitor socket to receive commands
listen(service_socket, 1);
ProtobufSerializer serializer;
Localizer localizer;
while (true) {
CommandContext context(serializer, localizer, -1, "");
try {
PbCommand command;
context.fd = ReadCommand(serializer, command);
if (context.fd == -1) {
continue;
}
execute(command, context);
}
catch(const io_exception& e) {
LOGWARN("%s", e.get_msg().c_str())
// Fall through
}
if (context.fd != -1) {
close(context.fd);
}
}
}
int RascsiService::ReadCommand(ProtobufSerializer& serializer, PbCommand& command)
{
// Wait for connection
sockaddr client = {};
socklen_t socklen = sizeof(client);
int fd = accept(service_socket, &client, &socklen);
if (fd < 0) {
throw io_exception("accept() failed");
}
// Read magic string
vector<byte> magic(6);
size_t bytes_read = serializer.ReadBytes(fd, magic);
if (!bytes_read) {
return -1;
}
if (bytes_read != magic.size() || memcmp(magic.data(), "RASCSI", magic.size())) {
throw io_exception("Invalid magic");
}
// Fetch the command
serializer.DeserializeMessage(fd, command);
return fd;
}

View File

@ -0,0 +1,43 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#pragma once
#include "rascsi_interface.pb.h"
#include <thread>
class CommandContext;
class ProtobufSerializer;
class RascsiService
{
bool (*execute)(rascsi_interface::PbCommand&, CommandContext&) = nullptr;
int service_socket = -1;
thread monthread;
static volatile bool running;
public:
RascsiService() = default;
~RascsiService();
bool Init(bool (ExecuteCommand)(rascsi_interface::PbCommand&, CommandContext&), int);
bool IsRunning() const { return running; }
void SetRunning(bool b) const { running = b; }
void Execute();
int ReadCommand(ProtobufSerializer&, rascsi_interface::PbCommand&);
static void KillHandler(int) { running = false; }
};

View File

@ -14,7 +14,7 @@
// The following should be updated for each release // The following should be updated for each release
const int rascsi_major_version = 22; // Last two digits of year const int rascsi_major_version = 22; // Last two digits of year
const int rascsi_minor_version = 9; // Month const int rascsi_minor_version = 10; // Month
const int rascsi_patch_version = -1; // Patch number - increment for each update const int rascsi_patch_version = -1; // Patch number - increment for each update
using namespace std; using namespace std;

View File

@ -8,7 +8,7 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "socket_connector.h" #include "protobuf_serializer.h"
#include "command_util.h" #include "command_util.h"
#include "rasutil.h" #include "rasutil.h"
#include "rasctl_commands.h" #include "rasctl_commands.h"
@ -151,7 +151,7 @@ void RasctlCommands::SendCommand()
throw io_exception("Can't write magic"); throw io_exception("Can't write magic");
} }
socket_connector.SerializeMessage(fd, command); serializer.SerializeMessage(fd, command);
} }
catch(const io_exception& e) { catch(const io_exception& e) {
cerr << "Error: " << e.get_msg() << endl; cerr << "Error: " << e.get_msg() << endl;
@ -165,7 +165,7 @@ void RasctlCommands::SendCommand()
// Receive result // Receive result
try { try {
socket_connector.DeserializeMessage(fd, result); serializer.DeserializeMessage(fd, result);
if (!result.status()) { if (!result.status()) {
throw io_exception(result.msg()); throw io_exception(result.msg());
@ -305,7 +305,7 @@ void RasctlCommands::CommandServerInfo()
if (server_info.devices_info().devices_size()) { if (server_info.devices_info().devices_size()) {
list<PbDevice> sorted_devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() }; list<PbDevice> sorted_devices = { server_info.devices_info().devices().begin(), server_info.devices_info().devices().end() };
sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id(); }); sorted_devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() || a.unit() < b.unit(); });
cout << "Attached devices:" << endl; cout << "Attached devices:" << endl;

View File

@ -9,12 +9,12 @@
#pragma once #pragma once
#include "socket_connector.h" #include "protobuf_serializer.h"
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include "rasctl_display.h" #include "rasctl_display.h"
#include <string> #include <string>
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi using namespace rascsi_interface;
class RasctlCommands class RasctlCommands
{ {
@ -22,8 +22,6 @@ public:
RasctlCommands(const PbCommand&, const string&, int, const string&, const string&); RasctlCommands(const PbCommand&, const string&, int, const string&, const string&);
~RasctlCommands() = default; ~RasctlCommands() = default;
RasctlCommands(RasctlCommands&) = delete;
RasctlCommands& operator=(const RasctlCommands&) = delete;
void Execute(const string&, const string&, const string&, const string&, const string&); void Execute(const string&, const string&, const string&, const string&, const string&);
@ -51,7 +49,7 @@ private:
void CommandOperationInfo(); void CommandOperationInfo();
void SendCommand(); void SendCommand();
SocketConnector socket_connector; ProtobufSerializer serializer;
PbCommand command; PbCommand command;
string hostname; string hostname;
int port; int port;

View File

@ -11,7 +11,7 @@
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi using namespace rascsi_interface;
class RasctlDisplay class RasctlDisplay
{ {
@ -19,8 +19,6 @@ class RasctlDisplay
RasctlDisplay() = default; RasctlDisplay() = default;
~RasctlDisplay() = default; ~RasctlDisplay() = default;
RasctlDisplay(RasctlDisplay&) = delete;
RasctlDisplay& operator=(const RasctlDisplay&) = delete;
void DisplayDevices(const PbDevicesInfo&) const; void DisplayDevices(const PbDevicesInfo&) const;
void DisplayDeviceInfo(const PbDevice&) const; void DisplayDeviceInfo(const PbDevice&) const;

View File

@ -22,7 +22,8 @@ bool ras_util::GetAsInt(const string& value, int& result)
} }
try { try {
result = (int)stoul(value); auto v = stoul(value);
result = (int)v;
} }
catch(const invalid_argument&) { catch(const invalid_argument&) {
return false; return false;
@ -46,7 +47,7 @@ string ras_util::ListDevices(const list<PbDevice>& pb_devices)
<< "+----+-----+------+-------------------------------------" << endl; << "+----+-----+------+-------------------------------------" << endl;
list<PbDevice> devices = pb_devices; list<PbDevice> devices = pb_devices;
devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() && a.unit() < b.unit(); }); devices.sort([](const auto& a, const auto& b) { return a.id() < b.id() || a.unit() < b.unit(); });
for (const auto& device : devices) { for (const auto& device : devices) {
string filename; string filename;
@ -82,3 +83,22 @@ string ras_util::ListDevices(const list<PbDevice>& pb_devices)
return s.str(); return s.str();
} }
// Pin the thread to a specific CPU
void ras_util::FixCpu(int cpu)
{
#ifdef __linux__
// Get the number of CPUs
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
sched_getaffinity(0, sizeof(cpu_set_t), &cpuset);
int cpus = CPU_COUNT(&cpuset);
// Set the thread affinity
if (cpu < cpus) {
CPU_ZERO(&cpuset);
CPU_SET(cpu, &cpuset);
sched_setaffinity(0, sizeof(cpu_set_t), &cpuset);
}
#endif
}

View File

@ -19,4 +19,6 @@ namespace ras_util
{ {
bool GetAsInt(const std::string&, int&); bool GetAsInt(const std::string&, int&);
std::string ListDevices(const std::list<rascsi_interface::PbDevice>&); std::string ListDevices(const std::list<rascsi_interface::PbDevice>&);
void FixCpu(int);
} }

View File

@ -9,7 +9,7 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "scsi.h" #include "bus.h"
using namespace std; using namespace std;
@ -31,7 +31,7 @@ BUS::phase_t BUS::GetPhase()
} }
// Get target phase from bus signal line // Get target phase from bus signal line
DWORD mci = GetMSG() ? 0x04 : 0x00; int mci = GetMSG() ? 0x04 : 0x00;
mci |= GetCD() ? 0x02 : 0x00; mci |= GetCD() ? 0x02 : 0x00;
mci |= GetIO() ? 0x01 : 0x00; mci |= GetIO() ? 0x01 : 0x00;
return GetPhase(mci); return GetPhase(mci);
@ -42,13 +42,9 @@ BUS::phase_t BUS::GetPhase()
// Determine Phase String phase enum // Determine Phase String phase enum
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const char* BUS::GetPhaseStrRaw(phase_t current_phase){ const char* BUS::GetPhaseStrRaw(phase_t current_phase) {
if(current_phase <= phase_t::reserved) { const auto& it = phase_str_mapping.find(current_phase);
return phase_str_table[(int)current_phase]; return it != phase_str_mapping.end() ? it->second : "INVALID";
}
else {
return "INVALID";
}
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -70,24 +66,21 @@ const array<BUS::phase_t, 8> BUS::phase_table = {
phase_t::msgin // | 1 | 1 | 1 | phase_t::msgin // | 1 | 1 | 1 |
}; };
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// Phase Table // Phase string to phase mapping
// This MUST be kept in sync with the phase_t enum type!
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
const array<const char*, 12> BUS::phase_str_table = { const unordered_map<BUS::phase_t, const char*> BUS::phase_str_mapping = {
"busfree", { phase_t::busfree, "busfree" },
"arbitration", { phase_t::arbitration, "arbitration" },
"selection", { phase_t::selection, "selection" },
"reselection", { phase_t::reselection, "reselection" },
"command", { phase_t::command, "command" },
"execute", { phase_t::datain, "datain" },
"datain", { phase_t::dataout, "dataout" },
"dataout", { phase_t::status, "status" },
"status", { phase_t::msgin, "msgin" },
"msgin", { phase_t::msgout, "msgout" },
"msgout", { phase_t::reserved, "reserved" }
"reserved"
}; };

View File

@ -5,118 +5,12 @@
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp) // Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS // Copyright (C) 2014-2020 GIMONS
// //
// [ SCSI Common Functionality ]
//
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#pragma once #pragma once
#include "os.h" // TODO Remove this include as soon as gpiobus.cpp/h is open for editing (adding the include there) again
#include <array> #include "bus.h"
using namespace std; //NOSONAR Not relevant for rascsi
//===========================================================================
//
// SCSI Bus
//
//===========================================================================
class BUS
{
public:
// Operation modes definition
enum class mode_e {
TARGET = 0,
INITIATOR = 1,
MONITOR = 2,
};
// Phase definitions
enum class phase_t : int {
busfree,
arbitration,
selection,
reselection,
command,
execute, // Execute phase is an extension of the command phase
datain,
dataout,
status,
msgin,
msgout,
reserved // Unused
};
BUS() = default;
virtual ~BUS() = default;
// Basic Functions
virtual bool Init(mode_e mode) = 0;
virtual void Reset() = 0;
virtual void Cleanup() = 0;
phase_t GetPhase();
static phase_t GetPhase(DWORD mci)
{
return phase_table[mci];
}
// Get the string phase name, based upon the raw data
static const char* GetPhaseStrRaw(phase_t current_phase);
// Extract as specific pin field from a raw data capture
static inline DWORD GetPinRaw(DWORD raw_data, DWORD pin_num)
{
return ((raw_data >> pin_num) & 1);
}
virtual bool GetBSY() const = 0;
virtual void SetBSY(bool ast) = 0;
virtual bool GetSEL() const = 0;
virtual void SetSEL(bool ast) = 0;
virtual bool GetATN() const = 0;
virtual void SetATN(bool ast) = 0;
virtual bool GetACK() const = 0;
virtual void SetACK(bool ast) = 0;
virtual bool GetRST() const = 0;
virtual void SetRST(bool ast) = 0;
virtual bool GetMSG() const = 0;
virtual void SetMSG(bool ast) = 0;
virtual bool GetCD() const = 0;
virtual void SetCD(bool ast) = 0;
virtual bool GetIO() = 0;
virtual void SetIO(bool ast) = 0;
virtual bool GetREQ() const = 0;
virtual void SetREQ(bool ast) = 0;
virtual BYTE GetDAT() = 0;
virtual void SetDAT(BYTE dat) = 0;
virtual bool GetDP() const = 0; // Get parity signal
virtual uint32_t Acquire() = 0;
virtual int CommandHandShake(BYTE *buf) = 0;
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
virtual bool GetSignal(int pin) const = 0;
// Get SCSI input signal value
virtual void SetSignal(int pin, bool ast) = 0;
// Set SCSI output signal value
static const int SEND_NO_DELAY = -1;
// Passed into SendHandShake when we don't want to delay
private:
static const array<phase_t, 8> phase_table;
static const array<const char *, 12> phase_str_table;
};
namespace scsi_defs { namespace scsi_defs {
enum class scsi_level : int { enum class scsi_level : int {

View File

@ -33,7 +33,7 @@ static const int _MAX_FNAME = 256;
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static volatile bool running; // Running flag static volatile bool running; // Running flag
unique_ptr<GPIOBUS> bus; // GPIO Bus GPIOBUS bus; // GPIO Bus
DWORD buff_size = 1000000; DWORD buff_size = 1000000;
data_capture *data_buffer; data_capture *data_buffer;
@ -186,15 +186,14 @@ bool Init()
} }
// GPIO Initialization // GPIO Initialization
bus = make_unique<GPIOBUS>(); if (!bus.Init())
if (!bus->Init())
{ {
LOGERROR("Unable to intiailize the GPIO bus. Exiting....") LOGERROR("Unable to intiailize the GPIO bus. Exiting....")
return false; return false;
} }
// Bus Reset // Bus Reset
bus->Reset(); bus.Reset();
// Other // Other
running = false; running = false;
@ -204,8 +203,7 @@ bool Init()
void Cleanup() void Cleanup()
{ {
if (!import_data) if (!import_data) {
{
LOGINFO("Stopping data collection....") LOGINFO("Stopping data collection....")
} }
LOGINFO(" ") LOGINFO(" ")
@ -216,17 +214,14 @@ void Cleanup()
LOGINFO("Generating %s...", html_file_name) LOGINFO("Generating %s...", html_file_name)
scsimon_generate_html(html_file_name, data_buffer, data_idx); scsimon_generate_html(html_file_name, data_buffer, data_idx);
if (bus) // Cleanup the Bus
{ bus.Cleanup();
// Cleanup the Bus
bus->Cleanup();
}
} }
void Reset() void Reset()
{ {
// Reset the bus // Reset the bus
bus->Reset(); bus.Reset();
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -234,7 +229,7 @@ void Reset()
// Pin the thread to a specific CPU (Only applies to Linux) // Pin the thread to a specific CPU (Only applies to Linux)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#ifdef __linux #ifdef __linux__
void FixCpu(int cpu) void FixCpu(int cpu)
{ {
// Get the number of CPUs // Get the number of CPUs
@ -326,7 +321,7 @@ int main(int argc, char *argv[])
// Reset // Reset
Reset(); Reset();
#ifdef __linux #ifdef __linux__
// Set the affinity to a specific processor core // Set the affinity to a specific processor core
FixCpu(3); FixCpu(3);
@ -338,7 +333,7 @@ int main(int argc, char *argv[])
// Start execution // Start execution
running = true; running = true;
bus->SetACT(false); bus.SetACT(false);
(void)gettimeofday(&start_time, nullptr); (void)gettimeofday(&start_time, nullptr);
@ -348,7 +343,7 @@ int main(int argc, char *argv[])
while (running) while (running)
{ {
// Work initialization // Work initialization
this_sample = (bus->Acquire() & ALL_SCSI_PINS); this_sample = (bus.Acquire() & ALL_SCSI_PINS);
loop_count++; loop_count++;
if (loop_count > LLONG_MAX - 1) if (loop_count > LLONG_MAX - 1)
{ {

View File

@ -7,54 +7,35 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "bus.h"
#include "rascsi_exceptions.h"
#include "controllers/abstract_controller.h" #include "controllers/abstract_controller.h"
using namespace scsi_defs;
TEST(AbstractControllerTest, Reset) TEST(AbstractControllerTest, Reset)
{ {
MockAbstractController controller(0); MockAbstractController controller(0);
auto device = make_shared<MockPrimaryDevice>(0);
controller.AddDevice(device);
controller.SetPhase(BUS::phase_t::status); controller.SetPhase(BUS::phase_t::status);
EXPECT_EQ(BUS::phase_t::status, controller.GetPhase()); EXPECT_EQ(BUS::phase_t::status, controller.GetPhase());
EXPECT_CALL(*device, Reset()).Times(1);
controller.Reset(); controller.Reset();
EXPECT_TRUE(controller.IsBusFree()); EXPECT_TRUE(controller.IsBusFree());
EXPECT_EQ(status::GOOD, controller.GetStatus());
EXPECT_EQ(0, controller.GetLength());
} }
TEST(AbstractControllerTest, SetGetStatus) TEST(AbstractControllerTest, SetGetStatus)
{ {
MockAbstractController controller(0); MockAbstractController controller(0);
controller.SetStatus(0x1234); controller.SetStatus(status::BUSY);
EXPECT_EQ(0x1234, controller.GetStatus()); EXPECT_EQ(status::BUSY, controller.GetStatus());
}
TEST(AbstractControllerTest, SetPhase)
{
MockAbstractController controller(0);
controller.SetPhase(BUS::phase_t::selection);
EXPECT_TRUE(controller.IsSelection());
controller.SetPhase(BUS::phase_t::busfree);
EXPECT_TRUE(controller.IsBusFree());
controller.SetPhase(BUS::phase_t::command);
EXPECT_TRUE(controller.IsCommand());
controller.SetPhase(BUS::phase_t::status);
EXPECT_TRUE(controller.IsStatus());
controller.SetPhase(BUS::phase_t::datain);
EXPECT_TRUE(controller.IsDataIn());
controller.SetPhase(BUS::phase_t::dataout);
EXPECT_TRUE(controller.IsDataOut());
controller.SetPhase(BUS::phase_t::msgin);
EXPECT_TRUE(controller.IsMsgIn());
controller.SetPhase(BUS::phase_t::msgout);
EXPECT_TRUE(controller.IsMsgOut());
} }
TEST(AbstractControllerTest, ProcessPhase) TEST(AbstractControllerTest, ProcessPhase)
@ -92,6 +73,12 @@ TEST(AbstractControllerTest, ProcessPhase)
controller.SetPhase(BUS::phase_t::msgout); controller.SetPhase(BUS::phase_t::msgout);
EXPECT_CALL(controller, MsgOut()).Times(1); EXPECT_CALL(controller, MsgOut()).Times(1);
controller.ProcessPhase(); controller.ProcessPhase();
controller.SetPhase(BUS::phase_t::reselection);
EXPECT_THROW(controller.ProcessPhase(), scsi_error_exception);
controller.SetPhase(BUS::phase_t::reserved);
EXPECT_THROW(controller.ProcessPhase(), scsi_error_exception);
} }
TEST(AbstractControllerTest, DeviceLunLifeCycle) TEST(AbstractControllerTest, DeviceLunLifeCycle)
@ -100,24 +87,22 @@ TEST(AbstractControllerTest, DeviceLunLifeCycle)
const int LUN = 4; const int LUN = 4;
MockAbstractController controller(ID); MockAbstractController controller(ID);
MockPrimaryDevice device1; auto device1 = make_shared<MockPrimaryDevice>(LUN);
MockPrimaryDevice device2; auto device2 = make_shared<MockPrimaryDevice>(32);
auto device3 = make_shared<MockPrimaryDevice>(-1);
EXPECT_FALSE(controller.HasLuns());
device1.SetLun(LUN);
device2.SetLun(32);
EXPECT_EQ(0, controller.GetLunCount());
EXPECT_EQ(ID, controller.GetTargetId()); EXPECT_EQ(ID, controller.GetTargetId());
EXPECT_TRUE(controller.AddDevice(&device1)); EXPECT_TRUE(controller.AddDevice(device1));
EXPECT_FALSE(controller.AddDevice(&device2)); EXPECT_FALSE(controller.AddDevice(device2));
EXPECT_TRUE(controller.HasLuns()); EXPECT_FALSE(controller.AddDevice(device3));
EXPECT_TRUE(controller.GetLunCount() > 0);
EXPECT_TRUE(controller.HasDeviceForLun(LUN)); EXPECT_TRUE(controller.HasDeviceForLun(LUN));
EXPECT_FALSE(controller.HasDeviceForLun(0)); EXPECT_FALSE(controller.HasDeviceForLun(0));
EXPECT_EQ(&device1, controller.GetDeviceForLun(LUN)); EXPECT_NE(nullptr, controller.GetDeviceForLun(LUN));
EXPECT_EQ(nullptr, controller.GetDeviceForLun(0)); EXPECT_EQ(nullptr, controller.GetDeviceForLun(0));
EXPECT_TRUE(controller.DeleteDevice(&device1)); EXPECT_TRUE(controller.DeleteDevice(device1));
EXPECT_FALSE(controller.HasLuns()); EXPECT_EQ(0, controller.GetLunCount());
} }
TEST(AbstractControllerTest, ExtractInitiatorId) TEST(AbstractControllerTest, ExtractInitiatorId)
@ -136,7 +121,7 @@ TEST(AbstractControllerTest, GetOpcode)
{ {
MockAbstractController controller(0); MockAbstractController controller(0);
vector<int>& cmd = controller.InitCmd(2); vector<int>& cmd = controller.InitCmd(1);
cmd[0] = 0x12; cmd[0] = 0x12;
EXPECT_EQ(0x12, (int)controller.GetOpcode()); EXPECT_EQ(0x12, (int)controller.GetOpcode());
@ -154,10 +139,23 @@ TEST(AbstractControllerTest, GetLun)
EXPECT_EQ(LUN, controller.GetLun()); EXPECT_EQ(LUN, controller.GetLun());
} }
TEST(AbstractControllerTest, Ctrl) TEST(AbstractControllerTest, Length)
{ {
MockAbstractController controller(0); MockAbstractController controller(0);
EXPECT_FALSE(controller.HasValidLength()); EXPECT_FALSE(controller.HasValidLength());
controller.UpdateOffsetAndLength();
EXPECT_EQ(0, controller.GetLength());
} }
TEST(AbstractControllerTest, Offset)
{
MockAbstractController controller(0);
controller.ResetOffset();
EXPECT_EQ(0, controller.GetOffset());
controller.UpdateOffsetAndLength();
EXPECT_EQ(0, controller.GetOffset());
}

View File

@ -7,7 +7,7 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "rascsi_interface.pb.h" #include "rascsi_interface.pb.h"
#include "command_util.h" #include "command_util.h"
@ -59,3 +59,10 @@ TEST(CommandUtil, ParseParameters)
TestSpecialDevice("printer"); TestSpecialDevice("printer");
TestSpecialDevice("services"); TestSpecialDevice("services");
} }
TEST(CommandUtil, ReturnLocalizedError)
{
MockCommandContext context;
EXPECT_FALSE(ReturnLocalizedError(context, LocalizationKey::ERROR_LOG_LEVEL));
}

View File

@ -7,31 +7,56 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "devices/device_factory.h" #include "devices/device_factory.h"
#include "controllers/controller_manager.h" #include "controllers/controller_manager.h"
TEST(ControllerManagerTest, ControllerManager) TEST(ControllerManagerTest, LifeCycle)
{ {
const int ID = 4; const int ID = 4;
const int LUN = 6; const int LUN1 = 2;
const int LUN2 = 3;
MockBus bus;
ControllerManager controller_manager(bus);
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager;
auto device = device_factory.CreateDevice(UNDEFINED, "services", ID); auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, LUN1, "services");
device->SetId(ID); controller_manager.AttachToScsiController(ID, device);
device->SetLun(LUN); auto controller = controller_manager.FindController(ID);
EXPECT_NE(nullptr, controller);
controller_manager.CreateScsiController(nullptr, device); EXPECT_EQ(1, controller->GetLunCount());
EXPECT_NE(nullptr, controller_manager.IdentifyController(1 << ID)); EXPECT_NE(nullptr, controller_manager.IdentifyController(1 << ID));
EXPECT_EQ(nullptr, controller_manager.IdentifyController(0)); EXPECT_EQ(nullptr, controller_manager.IdentifyController(0));
EXPECT_NE(nullptr, controller_manager.FindController(ID));
EXPECT_EQ(nullptr, controller_manager.FindController(0)); EXPECT_EQ(nullptr, controller_manager.FindController(0));
EXPECT_EQ(device, controller_manager.GetDeviceByIdAndLun(ID, LUN)); EXPECT_NE(nullptr, controller_manager.GetDeviceByIdAndLun(ID, LUN1));
EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(0, 0)); EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(0, 0));
controller_manager.DeleteAllControllers(); device = device_factory.CreateDevice(controller_manager, UNDEFINED, LUN2, "services");
device_factory.DeleteAllDevices(); controller_manager.AttachToScsiController(ID, device);
controller = controller_manager.FindController(ID);
controller_manager.DeleteController(controller);
EXPECT_EQ(nullptr, controller_manager.FindController(ID)); EXPECT_EQ(nullptr, controller_manager.FindController(ID));
EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(ID, LUN));
controller_manager.DeleteAllControllers();
EXPECT_EQ(nullptr, controller_manager.FindController(ID));
EXPECT_EQ(nullptr, controller_manager.GetDeviceByIdAndLun(ID, LUN1));
}
TEST(ControllerManagerTest, ResetAllControllers)
{
const int ID = 2;
MockBus bus;
ControllerManager controller_manager(bus);
DeviceFactory device_factory;
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "services");
controller_manager.AttachToScsiController(ID, device);
auto controller = controller_manager.FindController(ID);
EXPECT_NE(nullptr, controller);
controller->SetStatus(status::BUSY);
controller_manager.ResetAllControllers();
EXPECT_EQ(status::GOOD, controller->GetStatus());
} }

View File

@ -7,9 +7,10 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "rascsi_version.h" #include "rascsi_version.h"
#include "controllers/controller_manager.h"
#include "devices/device.h" #include "devices/device.h"
#include "devices/device_factory.h" #include "devices/device_factory.h"
#include <unordered_map> #include <unordered_map>
@ -37,27 +38,6 @@ TEST(DeviceFactoryTest, GetTypeForFile)
EXPECT_EQ(device_factory.GetTypeForFile("test.iso.suffix"), UNDEFINED); EXPECT_EQ(device_factory.GetTypeForFile("test.iso.suffix"), UNDEFINED);
} }
TEST(DeviceFactoryTest, LifeCycle)
{
DeviceFactory device_factory;
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "services", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHS", device->GetType());
list<PrimaryDevice *> devices = device_factory.GetAllDevices();
EXPECT_EQ(1, devices.size());
EXPECT_EQ(device, devices.front());
EXPECT_EQ(device, device_factory.GetDeviceByIdAndLun(-1, 0));
EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 1));
device_factory.DeleteDevice(*device);
devices = device_factory.GetAllDevices();
EXPECT_EQ(0, devices.size());
EXPECT_EQ(nullptr, device_factory.GetDeviceByIdAndLun(-1, 0));
}
TEST(DeviceFactoryTest, GetSectorSizes) TEST(DeviceFactoryTest, GetSectorSizes)
{ {
DeviceFactory device_factory; DeviceFactory device_factory;
@ -150,23 +130,27 @@ TEST(DeviceFactoryTest, GetDefaultParams)
TEST(DeviceFactoryTest, UnknownDeviceType) TEST(DeviceFactoryTest, UnknownDeviceType)
{ {
MockBus bus;
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager(bus);
PrimaryDevice *device1 = device_factory.CreateDevice(UNDEFINED, "test", -1); auto device1 = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test");
EXPECT_EQ(nullptr, device1); EXPECT_EQ(nullptr, device1);
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations" #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
PrimaryDevice *device2 = device_factory.CreateDevice(SAHD, "test", -1); auto device2 = device_factory.CreateDevice(controller_manager, SAHD, 0, "test");
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
EXPECT_EQ(nullptr, device2); EXPECT_EQ(nullptr, device2);
} }
TEST(DeviceFactoryTest, SCHD_Device_Defaults) TEST(DeviceFactoryTest, SCHD_Device_Defaults)
{ {
MockBus bus;
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager(bus);
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.hda", -1); auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test.hda");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType()); EXPECT_EQ("SCHD", device->GetType());
EXPECT_TRUE(device->SupportsFile()); EXPECT_TRUE(device->SupportsFile());
@ -186,83 +170,64 @@ TEST(DeviceFactoryTest, SCHD_Device_Defaults)
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision()); device->GetRevision());
device_factory.DeleteDevice(*device); device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test.hds");
device = device_factory.CreateDevice(UNDEFINED, "test.hds", -1);
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType()); EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(*device);
device = device_factory.CreateDevice(UNDEFINED, "test.hdi", -1); device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test.hdi");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType()); EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(*device);
device = device_factory.CreateDevice(UNDEFINED, "test.nhd", -1); device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test.nhd");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHD", device->GetType()); EXPECT_EQ("SCHD", device->GetType());
device_factory.DeleteDevice(*device); }
void TestRemovableDrive(const string& type, const string& filename, const string& product)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, filename);
EXPECT_NE(nullptr, device);
EXPECT_EQ(type, device->GetType());
EXPECT_TRUE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_TRUE(device->IsProtectable());
EXPECT_FALSE(device->IsProtected());
EXPECT_FALSE(device->IsReadOnly());
EXPECT_TRUE(device->IsRemovable());
EXPECT_FALSE(device->IsRemoved());
EXPECT_TRUE(device->IsLockable());
EXPECT_FALSE(device->IsLocked());
EXPECT_TRUE(device->IsStoppable());
EXPECT_FALSE(device->IsStopped());
EXPECT_EQ("RaSCSI", device->GetVendor());
EXPECT_EQ(product, device->GetProduct());
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
} }
TEST(DeviceFactoryTest, SCRM_Device_Defaults) TEST(DeviceFactoryTest, SCRM_Device_Defaults)
{ {
DeviceFactory device_factory; TestRemovableDrive("SCRM", "test.hdr", "SCSI HD (REM.)");
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.hdr", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCRM", device->GetType());
EXPECT_TRUE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_TRUE(device->IsProtectable());
EXPECT_FALSE(device->IsProtected());
EXPECT_FALSE(device->IsReadOnly());
EXPECT_TRUE(device->IsRemovable());
EXPECT_FALSE(device->IsRemoved());
EXPECT_TRUE(device->IsLockable());
EXPECT_FALSE(device->IsLocked());
EXPECT_TRUE(device->IsStoppable());
EXPECT_FALSE(device->IsStopped());
EXPECT_EQ("RaSCSI", device->GetVendor());
EXPECT_EQ("SCSI HD (REM.)", device->GetProduct());
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(*device);
} }
TEST(DeviceFactoryTest, SCMO_Device_Defaults) TEST(DeviceFactoryTest, SCMO_Device_Defaults)
{ {
DeviceFactory device_factory; TestRemovableDrive("SCMO", "test.mos", "SCSI MO");
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.mos", -1);
EXPECT_NE(nullptr, device);
EXPECT_EQ("SCMO", device->GetType());
EXPECT_TRUE(device->SupportsFile());
EXPECT_FALSE(device->SupportsParams());
EXPECT_TRUE(device->IsProtectable());
EXPECT_FALSE(device->IsProtected());
EXPECT_FALSE(device->IsReadOnly());
EXPECT_TRUE(device->IsRemovable());
EXPECT_FALSE(device->IsRemoved());
EXPECT_TRUE(device->IsLockable());
EXPECT_FALSE(device->IsLocked());
EXPECT_TRUE(device->IsStoppable());
EXPECT_FALSE(device->IsStopped());
EXPECT_EQ("RaSCSI", device->GetVendor());
EXPECT_EQ("SCSI MO", device->GetProduct());
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision());
device_factory.DeleteDevice(*device);
} }
TEST(DeviceFactoryTest, SCCD_Device_Defaults) TEST(DeviceFactoryTest, SCCD_Device_Defaults)
{ {
MockBus bus;
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager(bus);
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "test.iso", -1); auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "test.iso");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCCD", device->GetType()); EXPECT_EQ("SCCD", device->GetType());
EXPECT_TRUE(device->SupportsFile()); EXPECT_TRUE(device->SupportsFile());
@ -281,15 +246,15 @@ TEST(DeviceFactoryTest, SCCD_Device_Defaults)
EXPECT_EQ("SCSI CD-ROM", device->GetProduct()); EXPECT_EQ("SCSI CD-ROM", device->GetProduct());
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision()); device->GetRevision());
device_factory.DeleteDevice(*device);
} }
TEST(DeviceFactoryTest, SCBR_Device_Defaults) TEST(DeviceFactoryTest, SCBR_Device_Defaults)
{ {
MockBus bus;
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager(bus);
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "bridge", -1); auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "bridge");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCBR", device->GetType()); EXPECT_EQ("SCBR", device->GetType());
EXPECT_FALSE(device->SupportsFile()); EXPECT_FALSE(device->SupportsFile());
@ -308,15 +273,15 @@ TEST(DeviceFactoryTest, SCBR_Device_Defaults)
EXPECT_EQ("RASCSI BRIDGE", device->GetProduct()); EXPECT_EQ("RASCSI BRIDGE", device->GetProduct());
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision()); device->GetRevision());
device_factory.DeleteDevice(*device);
} }
TEST(DeviceFactoryTest, SCDP_Device_Defaults) TEST(DeviceFactoryTest, SCDP_Device_Defaults)
{ {
MockBus bus;
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager(bus);
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "daynaport", -1); auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "daynaport");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCDP", device->GetType()); EXPECT_EQ("SCDP", device->GetType());
EXPECT_FALSE(device->SupportsFile()); EXPECT_FALSE(device->SupportsFile());
@ -334,15 +299,15 @@ TEST(DeviceFactoryTest, SCDP_Device_Defaults)
EXPECT_EQ("Dayna", device->GetVendor()); EXPECT_EQ("Dayna", device->GetVendor());
EXPECT_EQ("SCSI/Link", device->GetProduct()); EXPECT_EQ("SCSI/Link", device->GetProduct());
EXPECT_EQ("1.4a", device->GetRevision()); EXPECT_EQ("1.4a", device->GetRevision());
device_factory.DeleteDevice(*device);
} }
TEST(DeviceFactoryTest, SCHS_Device_Defaults) TEST(DeviceFactoryTest, SCHS_Device_Defaults)
{ {
MockBus bus;
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager(bus);
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "services", -1); auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "services");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCHS", device->GetType()); EXPECT_EQ("SCHS", device->GetType());
EXPECT_FALSE(device->SupportsFile()); EXPECT_FALSE(device->SupportsFile());
@ -361,15 +326,15 @@ TEST(DeviceFactoryTest, SCHS_Device_Defaults)
EXPECT_EQ("Host Services", device->GetProduct()); EXPECT_EQ("Host Services", device->GetProduct());
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision()); device->GetRevision());
device_factory.DeleteDevice(*device);
} }
TEST(DeviceFactoryTest, SCLP_Device_Defaults) TEST(DeviceFactoryTest, SCLP_Device_Defaults)
{ {
MockBus bus;
DeviceFactory device_factory; DeviceFactory device_factory;
ControllerManager controller_manager(bus);
PrimaryDevice *device = device_factory.CreateDevice(UNDEFINED, "printer", -1); auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "printer");
EXPECT_NE(nullptr, device); EXPECT_NE(nullptr, device);
EXPECT_EQ("SCLP", device->GetType()); EXPECT_EQ("SCLP", device->GetType());
EXPECT_FALSE(device->SupportsFile()); EXPECT_FALSE(device->SupportsFile());
@ -388,6 +353,4 @@ TEST(DeviceFactoryTest, SCLP_Device_Defaults)
EXPECT_EQ("SCSI PRINTER", device->GetProduct()); EXPECT_EQ("SCSI PRINTER", device->GetProduct());
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2), EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
device->GetRevision()); device->GetRevision());
device_factory.DeleteDevice(*device);
} }

View File

@ -7,31 +7,15 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "devices/device.h" #include "devices/device.h"
class TestDevice final : public Device
{
FRIEND_TEST(DeviceTest, Params);
FRIEND_TEST(DeviceTest, StatusCode);
FRIEND_TEST(DeviceTest, Reset);
FRIEND_TEST(DeviceTest, Start);
FRIEND_TEST(DeviceTest, Stop);
FRIEND_TEST(DeviceTest, Eject);
public:
TestDevice() : Device("test") {}
~TestDevice() override = default;
};
TEST(DeviceTest, Properties) TEST(DeviceTest, Properties)
{ {
const int ID = 4;
const int LUN = 5; const int LUN = 5;
TestDevice device; MockDevice device(LUN);
EXPECT_FALSE(device.IsProtectable()); EXPECT_FALSE(device.IsProtectable());
device.SetProtectable(true); device.SetProtectable(true);
@ -69,31 +53,50 @@ TEST(DeviceTest, Properties)
device.SetLocked(true); device.SetLocked(true);
EXPECT_TRUE(device.IsLocked()); EXPECT_TRUE(device.IsLocked());
device.SetId(ID); EXPECT_FALSE(device.SupportsParams());
EXPECT_EQ(ID, device.GetId()); EXPECT_TRUE(device.SupportsFile());
device.SupportsParams(true);
EXPECT_TRUE(device.SupportsParams());
EXPECT_FALSE(device.SupportsFile());
device.SetLun(LUN);
EXPECT_EQ(LUN, device.GetLun()); EXPECT_EQ(LUN, device.GetLun());
} }
TEST(DeviceTest, ProductData) TEST(DeviceTest, Vendor)
{ {
TestDevice device; MockDevice device(0);
EXPECT_THROW(device.SetVendor(""), illegal_argument_exception); EXPECT_THROW(device.SetVendor(""), invalid_argument);
EXPECT_THROW(device.SetVendor("123456789"), illegal_argument_exception); EXPECT_THROW(device.SetVendor("123456789"), invalid_argument);
device.SetVendor("12345678"); device.SetVendor("12345678");
EXPECT_EQ("12345678", device.GetVendor()); EXPECT_EQ("12345678", device.GetVendor());
}
EXPECT_THROW(device.SetProduct(""), illegal_argument_exception); TEST(DeviceTest, Product)
EXPECT_THROW(device.SetProduct("12345678901234567"), illegal_argument_exception); {
MockDevice device(0);
EXPECT_THROW(device.SetProduct(""), invalid_argument);
EXPECT_THROW(device.SetProduct("12345678901234567"), invalid_argument);
device.SetProduct("1234567890123456"); device.SetProduct("1234567890123456");
EXPECT_EQ("1234567890123456", device.GetProduct()); EXPECT_EQ("1234567890123456", device.GetProduct());
device.SetProduct("xyz", false);
EXPECT_EQ("1234567890123456", device.GetProduct()) << "Changing vital product data is not SCSI complient";
}
EXPECT_THROW(device.SetRevision(""), illegal_argument_exception); TEST(DeviceTest, Revision)
EXPECT_THROW(device.SetRevision("12345"), illegal_argument_exception); {
MockDevice device(0);
EXPECT_THROW(device.SetRevision(""), invalid_argument);
EXPECT_THROW(device.SetRevision("12345"), invalid_argument);
device.SetRevision("1234"); device.SetRevision("1234");
EXPECT_EQ("1234", device.GetRevision()); EXPECT_EQ("1234", device.GetRevision());
}
TEST(DeviceTest, GetPaddedName)
{
MockDevice device(0);
device.SetVendor("V"); device.SetVendor("V");
device.SetProduct("P"); device.SetProduct("P");
@ -104,7 +107,7 @@ TEST(DeviceTest, ProductData)
TEST(DeviceTest, Params) TEST(DeviceTest, Params)
{ {
TestDevice device; MockDevice device(0);
unordered_map<string, string> params; unordered_map<string, string> params;
params["key"] = "value"; params["key"] = "value";
@ -124,23 +127,15 @@ TEST(DeviceTest, Params)
TEST(DeviceTest, StatusCode) TEST(DeviceTest, StatusCode)
{ {
TestDevice device; MockDevice device(0);
device.SetStatusCode(123); device.SetStatusCode(123);
EXPECT_EQ(123, device.GetStatusCode()); EXPECT_EQ(123, device.GetStatusCode());
} }
TEST(DeviceTest, Init)
{
TestDevice device;
unordered_map<string, string> params;
EXPECT_TRUE(device.Init(params));
}
TEST(DeviceTest, Reset) TEST(DeviceTest, Reset)
{ {
TestDevice device; MockDevice device(0);
device.SetLocked(true); device.SetLocked(true);
device.SetAttn(true); device.SetAttn(true);
@ -153,7 +148,7 @@ TEST(DeviceTest, Reset)
TEST(DeviceTest, Start) TEST(DeviceTest, Start)
{ {
TestDevice device; MockDevice device(0);
device.SetStopped(true); device.SetStopped(true);
device.SetReady(false); device.SetReady(false);
@ -166,7 +161,7 @@ TEST(DeviceTest, Start)
TEST(DeviceTest, Stop) TEST(DeviceTest, Stop)
{ {
TestDevice device; MockDevice device(0);
device.SetReady(true); device.SetReady(true);
device.SetAttn(true); device.SetAttn(true);
@ -179,7 +174,7 @@ TEST(DeviceTest, Stop)
TEST(DeviceTest, Eject) TEST(DeviceTest, Eject)
{ {
TestDevice device; MockDevice device(0);
device.SetReady(false); device.SetReady(false);
device.SetRemovable(false); device.SetRemovable(false);

View File

@ -7,151 +7,169 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "scsi.h"
#include "devices/disk.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
using namespace scsi_defs;
TEST(DiskTest, Dispatch)
{
MockAbstractController controller(0);
const unordered_set<uint32_t> sector_sizes;
auto disk = make_shared<MockDisk>();
controller.AddDevice(disk);
controller.InitCmd(6);
disk->MediumChanged();
EXPECT_THROW(disk->Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception);
}
TEST(DiskTest, Rezero) TEST(DiskTest, Rezero)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdRezero), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdRezero), scsi_error_exception)
<< "REZERO must fail because drive is not ready"; << "REZERO must fail because drive is not ready";
disk.SetReady(true); disk->SetReady(true);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRezero)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRezero));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(DiskTest, FormatUnit) TEST(DiskTest, FormatUnit)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
vector<int>& cmd = controller.InitCmd(6); vector<int>& cmd = controller.InitCmd(6);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdFormat), scsi_error_exception); EXPECT_THROW(disk->Dispatch(scsi_command::eCmdFormat), scsi_error_exception);
disk.SetReady(true); disk->SetReady(true);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdFormat)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdFormat));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
cmd[1] = 0x10; cmd[1] = 0x10;
cmd[4] = 1; cmd[4] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdFormat), scsi_error_exception); EXPECT_THROW(disk->Dispatch(scsi_command::eCmdFormat), scsi_error_exception);
} }
TEST(DiskTest, ReassignBlocks) TEST(DiskTest, ReassignBlocks)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReassign), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReassign), scsi_error_exception)
<< "REASSIGN must fail because drive is not ready"; << "REASSIGN must fail because drive is not ready";
disk.SetReady(true); disk->SetReady(true);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReassign)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReassign));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(DiskTest, Seek) TEST(DiskTest, Seek)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
vector<int>& cmd = controller.InitCmd(10); vector<int>& cmd = controller.InitCmd(10);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek6), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdSeek6), scsi_error_exception)
<< "SEEK(6) must fail for a medium with 0 blocks"; << "SEEK(6) must fail for a medium with 0 blocks";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdSeek10), scsi_error_exception)
<< "SEEK(10) must fail for a medium with 0 blocks"; << "SEEK(10) must fail for a medium with 0 blocks";
disk.SetBlockCount(1); disk->SetBlockCount(1);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek6), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdSeek6), scsi_error_exception)
<< "SEEK(6) must fail because drive is not ready"; << "SEEK(6) must fail because drive is not ready";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSeek10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdSeek10), scsi_error_exception)
<< "SEEK(10) must fail because drive is not ready"; << "SEEK(10) must fail because drive is not ready";
disk.SetReady(true); disk->SetReady(true);
// Block count for SEEK(6) // Block count for SEEK(6)
cmd[4] = 1; cmd[4] = 1;
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSeek6)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSeek6));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
cmd[4] = 0; cmd[4] = 0;
// Block count for SEEK(10) // Block count for SEEK(10)
cmd[5] = 1; cmd[5] = 1;
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSeek10)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSeek10));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(DiskTest, ReadCapacity) TEST(DiskTest, ReadCapacity)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
vector<int>& cmd = controller.InitCmd(16); vector<int>& cmd = controller.InitCmd(16);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "Neithed READ CAPACITY(16) nor READ LONG(16)"; << "Neithed READ CAPACITY(16) nor READ LONG(16)";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadCapacity10), scsi_error_exception)
<< "READ CAPACITY(10) must fail because drive is not ready"; << "READ CAPACITY(10) must fail because drive is not ready";
// READ CAPACITY(16), not READ LONG(16) // READ CAPACITY(16), not READ LONG(16)
cmd[1] = 0x10; cmd[1] = 0x10;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ CAPACITY(16) must fail because drive is not ready"; << "READ CAPACITY(16) must fail because drive is not ready";
cmd[1] = 0x00; cmd[1] = 0x00;
disk.SetReady(true); disk->SetReady(true);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadCapacity10), scsi_error_exception)
<< "READ CAPACITY(10) must fail because the medium has no capacity"; << "READ CAPACITY(10) must fail because the medium has no capacity";
// READ CAPACITY(16), not READ LONG(16) // READ CAPACITY(16), not READ LONG(16)
cmd[1] = 0x10; cmd[1] = 0x10;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ CAPACITY(16) must fail because the medium has no capacity"; << "READ CAPACITY(16) must fail because the medium has no capacity";
cmd[1] = 0x00; cmd[1] = 0x00;
disk.SetBlockCount(0x12345678); disk->SetBlockCount(0x12345678);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity10)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity10));
EXPECT_EQ(0x12, controller.GetBuffer()[0]); EXPECT_EQ(0x12, controller.GetBuffer()[0]);
EXPECT_EQ(0x34, controller.GetBuffer()[1]); EXPECT_EQ(0x34, controller.GetBuffer()[1]);
EXPECT_EQ(0x56, controller.GetBuffer()[2]); EXPECT_EQ(0x56, controller.GetBuffer()[2]);
EXPECT_EQ(0x77, controller.GetBuffer()[3]); EXPECT_EQ(0x77, controller.GetBuffer()[3]);
disk.SetBlockCount(0x1234567887654321); disk->SetBlockCount(0x1234567887654321);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity10)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity10));
EXPECT_EQ(0xff, controller.GetBuffer()[0]); EXPECT_EQ(0xff, controller.GetBuffer()[0]);
EXPECT_EQ(0xff, controller.GetBuffer()[1]); EXPECT_EQ(0xff, controller.GetBuffer()[1]);
EXPECT_EQ(0xff, controller.GetBuffer()[2]); EXPECT_EQ(0xff, controller.GetBuffer()[2]);
EXPECT_EQ(0xff, controller.GetBuffer()[3]); EXPECT_EQ(0xff, controller.GetBuffer()[3]);
disk.SetSectorSizeInBytes(1024); disk->SetSectorSizeInBytes(1024);
// READ CAPACITY(16), not READ LONG(16) // READ CAPACITY(16), not READ LONG(16)
cmd[1] = 0x10; cmd[1] = 0x10;
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16));
EXPECT_EQ(0x12, controller.GetBuffer()[0]); EXPECT_EQ(0x12, controller.GetBuffer()[0]);
EXPECT_EQ(0x34, controller.GetBuffer()[1]); EXPECT_EQ(0x34, controller.GetBuffer()[1]);
EXPECT_EQ(0x56, controller.GetBuffer()[2]); EXPECT_EQ(0x56, controller.GetBuffer()[2]);
@ -168,172 +186,172 @@ TEST(DiskTest, ReadCapacity)
TEST(DiskTest, ReadWriteLong) TEST(DiskTest, ReadWriteLong)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
vector<int>& cmd = controller.InitCmd(16); vector<int>& cmd = controller.InitCmd(16);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadLong10)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadLong10));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdWriteLong10)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdWriteLong10));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
cmd[2] = 1; cmd[2] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadLong10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadLong10), scsi_error_exception)
<< "READ LONG(10) must fail because the capacity is exceeded"; << "READ LONG(10) must fail because the capacity is exceeded";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdWriteLong10), scsi_error_exception)
<< "WRITE LONG(10) must fail because the capacity is exceeded"; << "WRITE LONG(10) must fail because the capacity is exceeded";
// READ LONG(16), not READ CAPACITY(16) // READ LONG(16), not READ CAPACITY(16)
cmd[1] = 0x11; cmd[1] = 0x11;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ LONG(16) must fail because the capacity is exceeded"; << "READ LONG(16) must fail because the capacity is exceeded";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong16), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdWriteLong16), scsi_error_exception)
<< "WRITE LONG(16) must fail because the capacity is exceeded"; << "WRITE LONG(16) must fail because the capacity is exceeded";
cmd[2] = 0; cmd[2] = 0;
cmd[7] = 1; cmd[7] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadLong10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadLong10), scsi_error_exception)
<< "READ LONG(10) must fail because it currently only supports 0 bytes transfer length"; << "READ LONG(10) must fail because it currently only supports 0 bytes transfer length";
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong10), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdWriteLong10), scsi_error_exception)
<< "WRITE LONG(10) must fail because it currently only supports 0 bytes transfer length"; << "WRITE LONG(10) must fail because it currently only supports 0 bytes transfer length";
cmd[7] = 0; cmd[7] = 0;
// READ LONG(16), not READ CAPACITY(16) // READ LONG(16), not READ CAPACITY(16)
cmd[1] = 0x11; cmd[1] = 0x11;
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
cmd[1] = 0x00; cmd[1] = 0x00;
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdWriteLong16)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdWriteLong16));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
cmd[13] = 1; cmd[13] = 1;
// READ LONG(16), not READ CAPACITY(16) // READ LONG(16), not READ CAPACITY(16)
cmd[1] = 0x11; cmd[1] = 0x11;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdReadCapacity16_ReadLong16), scsi_error_exception)
<< "READ LONG(16) must fail because it currently only supports 0 bytes transfer length"; << "READ LONG(16) must fail because it currently only supports 0 bytes transfer length";
cmd[1] = 0x00; cmd[1] = 0x00;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdWriteLong16), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdWriteLong16), scsi_error_exception)
<< "WRITE LONG(16) must fail because it currently only supports 0 bytes transfer length"; << "WRITE LONG(16) must fail because it currently only supports 0 bytes transfer length";
} }
TEST(DiskTest, ReserveRelease) TEST(DiskTest, ReserveRelease)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
controller.InitCmd(6); controller.InitCmd(6);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReserve6)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReserve6));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRelease6)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRelease6));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(DiskTest, SendDiagnostic) TEST(DiskTest, SendDiagnostic)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
vector<int>& cmd = controller.InitCmd(6); vector<int>& cmd = controller.InitCmd(6);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSendDiag)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSendDiag));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
cmd[1] = 0x10; cmd[1] = 0x10;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception)
<< "SEND DIAGNOSTIC must fail because PF bit is not supported"; << "SEND DIAGNOSTIC must fail because PF bit is not supported";
cmd[1] = 0; cmd[1] = 0;
cmd[3] = 1; cmd[3] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception)
<< "SEND DIAGNOSTIC must fail because parameter list is not supported"; << "SEND DIAGNOSTIC must fail because parameter list is not supported";
cmd[3] = 0; cmd[3] = 0;
cmd[4] = 1; cmd[4] = 1;
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdSendDiag), scsi_error_exception)
<< "SEND DIAGNOSTIC must fail because parameter list is not supported"; << "SEND DIAGNOSTIC must fail because parameter list is not supported";
} }
TEST(DiskTest, PreventAllowMediumRemoval) TEST(DiskTest, PreventAllowMediumRemoval)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
vector<int>& cmd = controller.InitCmd(6); vector<int>& cmd = controller.InitCmd(6);
EXPECT_THROW(disk.Dispatch(scsi_command::eCmdRemoval), scsi_error_exception) EXPECT_THROW(disk->Dispatch(scsi_command::eCmdRemoval), scsi_error_exception)
<< "REMOVAL must fail because drive is not ready"; << "REMOVAL must fail because drive is not ready";
disk.SetReady(true); disk->SetReady(true);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRemoval)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRemoval));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
EXPECT_FALSE(disk.IsLocked()); EXPECT_FALSE(disk->IsLocked());
cmd[4] = 1; cmd[4] = 1;
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdRemoval)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdRemoval));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
EXPECT_TRUE(disk.IsLocked()); EXPECT_TRUE(disk->IsLocked());
} }
TEST(DiskTest, SynchronizeCache) TEST(DiskTest, SynchronizeCache)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
controller.InitCmd(10); controller.InitCmd(10);
EXPECT_CALL(disk, FlushCache()).Times(1); EXPECT_CALL(*disk, FlushCache()).Times(1);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSynchronizeCache10)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSynchronizeCache10));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
controller.InitCmd(16); controller.InitCmd(16);
EXPECT_CALL(disk, FlushCache()).Times(1); EXPECT_CALL(*disk, FlushCache()).Times(1);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdSynchronizeCache16)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdSynchronizeCache16));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(DiskTest, ReadDefectData) TEST(DiskTest, ReadDefectData)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockSCSIHD_NEC disk; auto disk = make_shared<MockDisk>();
controller.AddDevice(&disk); controller.AddDevice(disk);
controller.InitCmd(10); controller.InitCmd(10);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(disk.Dispatch(scsi_command::eCmdReadDefectData10)); EXPECT_TRUE(disk->Dispatch(scsi_command::eCmdReadDefectData10));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(DiskTest, SectorSize) TEST(DiskTest, SectorSize)
{ {
MockSCSIHD_NEC disk; MockDisk disk;
unordered_set<uint32_t> sizes = { 1, 2, 3 }; unordered_set<uint32_t> sizes = { 512, 1024 };
disk.SetSectorSizes(sizes); disk.SetSectorSizes(sizes);
EXPECT_TRUE(disk.IsSectorSizeConfigurable()); EXPECT_TRUE(disk.IsSectorSizeConfigurable());
@ -375,7 +393,8 @@ TEST(DiskTest, SectorSize)
TEST(DiskTest, ConfiguredSectorSize) TEST(DiskTest, ConfiguredSectorSize)
{ {
DeviceFactory device_factory; DeviceFactory device_factory;
MockSCSIHD_NEC disk; const unordered_set<uint32_t> sector_sizes;
MockSCSIHD disk(0, sector_sizes, false);
EXPECT_TRUE(disk.SetConfiguredSectorSize(device_factory, 512)); EXPECT_TRUE(disk.SetConfiguredSectorSize(device_factory, 512));
EXPECT_EQ(512, disk.GetConfiguredSectorSize()); EXPECT_EQ(512, disk.GetConfiguredSectorSize());
@ -386,7 +405,7 @@ TEST(DiskTest, ConfiguredSectorSize)
TEST(DiskTest, BlockCount) TEST(DiskTest, BlockCount)
{ {
MockSCSIHD_NEC disk; MockDisk disk;
disk.SetBlockCount(0x1234567887654321); disk.SetBlockCount(0x1234567887654321);
EXPECT_EQ(0x1234567887654321, disk.GetBlockCount()); EXPECT_EQ(0x1234567887654321, disk.GetBlockCount());

View File

@ -7,7 +7,7 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "devices/file_support.h" #include "devices/file_support.h"
class TestFileSupport : public FileSupport class TestFileSupport : public FileSupport

View File

@ -11,25 +11,102 @@
#include <gmock/gmock.h> #include <gmock/gmock.h>
#include "controllers/controller_manager.h" #include "controllers/scsi_controller.h"
#include "devices/device_factory.h"
#include "devices/primary_device.h" #include "devices/primary_device.h"
#include "devices/scsihd.h" #include "devices/scsihd.h"
#include "devices/scsihd_nec.h" #include "devices/scsihd_nec.h"
#include "devices/scsicd.h" #include "devices/scsicd.h"
#include "devices/scsimo.h" #include "devices/scsimo.h"
#include "devices/host_services.h" #include "devices/host_services.h"
#include "protobuf_serializer.h"
#include "command_context.h"
#include "localizer.h"
class MockBus final : public BUS //NOSONAR Having many fields/methods cannot be avoided
{
public:
MOCK_METHOD(bool, Init, (mode_e), (override));
MOCK_METHOD(void, Reset, (), (override));
MOCK_METHOD(void, Cleanup, (), (override));
MOCK_METHOD(bool, GetBSY, (), (const override));
MOCK_METHOD(void, SetBSY, (bool), (override));
MOCK_METHOD(bool, GetSEL, (), (const override));
MOCK_METHOD(void, SetSEL, (bool), (override));
MOCK_METHOD(bool, GetATN, (), (const override));
MOCK_METHOD(void, SetATN, (bool), (override));
MOCK_METHOD(bool, GetACK, (), (const override));
MOCK_METHOD(void, SetACK, (bool), (override));
MOCK_METHOD(bool, GetRST, (), (const override));
MOCK_METHOD(void, SetRST, (bool), (override));
MOCK_METHOD(bool, GetMSG, (), (const override));
MOCK_METHOD(void, SetMSG, (bool), (override));
MOCK_METHOD(bool, GetCD, (), (const override));
MOCK_METHOD(void, SetCD, (bool), (override));
MOCK_METHOD(bool, GetIO, (), (override));
MOCK_METHOD(void, SetIO, (bool), (override));
MOCK_METHOD(bool, GetREQ, (), (const override));
MOCK_METHOD(void, SetREQ, (bool), (override));
MOCK_METHOD(BYTE, GetDAT, (), (override));
MOCK_METHOD(void, SetDAT, (BYTE), (override));
MOCK_METHOD(bool, GetDP, (), (const override));
MOCK_METHOD(uint32_t, Acquire, (), (override));
MOCK_METHOD(int, CommandHandShake, (BYTE *), (override));
MOCK_METHOD(int, ReceiveHandShake, (BYTE *, int), (override));
MOCK_METHOD(int, SendHandShake, (BYTE *, int, int), (override));
MOCK_METHOD(bool, GetSignal, (int), (const override));
MOCK_METHOD(void, SetSignal, (int, bool), (override));
MockBus() = default;
~MockBus() override = default;
};
class MockPhaseHandler : public PhaseHandler
{
FRIEND_TEST(PhaseHandlerTest, Phases);
public:
MOCK_METHOD(BUS::phase_t, Process, (int), (override));
MOCK_METHOD(void, Status, (), ());
MOCK_METHOD(void, DataIn, (), ());
MOCK_METHOD(void, DataOut, (), ());
MOCK_METHOD(void, BusFree, (), ());
MOCK_METHOD(void, Selection, (), ());
MOCK_METHOD(void, Command, (), ());
MOCK_METHOD(void, MsgIn, (), ());
MOCK_METHOD(void, MsgOut, (), ());
using PhaseHandler::PhaseHandler;
};
class MockAbstractController final : public AbstractController //NOSONAR Having many fields/methods cannot be avoided class MockAbstractController final : public AbstractController //NOSONAR Having many fields/methods cannot be avoided
{ {
FRIEND_TEST(AbstractControllerTest, Reset); FRIEND_TEST(AbstractControllerTest, Reset);
FRIEND_TEST(AbstractControllerTest, SetPhase);
FRIEND_TEST(AbstractControllerTest, ProcessPhase); FRIEND_TEST(AbstractControllerTest, ProcessPhase);
FRIEND_TEST(AbstractControllerTest, DeviceLunLifeCycle); FRIEND_TEST(AbstractControllerTest, DeviceLunLifeCycle);
FRIEND_TEST(AbstractControllerTest, ExtractInitiatorId); FRIEND_TEST(AbstractControllerTest, ExtractInitiatorId);
FRIEND_TEST(AbstractControllerTest, GetOpcode); FRIEND_TEST(AbstractControllerTest, GetOpcode);
FRIEND_TEST(AbstractControllerTest, GetLun); FRIEND_TEST(AbstractControllerTest, GetLun);
FRIEND_TEST(AbstractControllerTest, Ctrl); FRIEND_TEST(AbstractControllerTest, Length);
FRIEND_TEST(AbstractControllerTest, Offset);
FRIEND_TEST(PrimaryDeviceTest, Inquiry);
FRIEND_TEST(PrimaryDeviceTest, TestUnitReady);
FRIEND_TEST(PrimaryDeviceTest, RequestSense);
FRIEND_TEST(PrimaryDeviceTest, ReportLuns);
FRIEND_TEST(PrimaryDeviceTest, UnknownCommand);
FRIEND_TEST(DiskTest, Dispatch);
FRIEND_TEST(DiskTest, Rezero);
FRIEND_TEST(DiskTest, FormatUnit);
FRIEND_TEST(DiskTest, ReassignBlocks);
FRIEND_TEST(DiskTest, Seek);
FRIEND_TEST(DiskTest, ReadCapacity);
FRIEND_TEST(DiskTest, ReadWriteLong);
FRIEND_TEST(DiskTest, ReserveRelease);
FRIEND_TEST(DiskTest, SendDiagnostic);
FRIEND_TEST(DiskTest, PreventAllowMediumRemoval);
FRIEND_TEST(DiskTest, SynchronizeCache);
FRIEND_TEST(DiskTest, ReadDefectData);
public: public:
@ -48,36 +125,48 @@ public:
MOCK_METHOD(void, SetByteTransfer, (bool), (override)); MOCK_METHOD(void, SetByteTransfer, (bool), (override));
MOCK_METHOD(void, ScheduleShutdown, (rascsi_shutdown_mode), (override)); MOCK_METHOD(void, ScheduleShutdown, (rascsi_shutdown_mode), (override));
explicit MockAbstractController(int target_id) : AbstractController(nullptr, target_id, 32) {} explicit MockAbstractController(int target_id) : AbstractController(bus, target_id, 32) {
AllocateBuffer(512);
}
~MockAbstractController() override = default; ~MockAbstractController() override = default;
MockBus bus;
}; };
class MockScsiController final : public ScsiController class MockScsiController final : public ScsiController
{ {
FRIEND_TEST(PrimaryDeviceTest, TestUnitReady); FRIEND_TEST(ScsiControllerTest, RequestSense);
FRIEND_TEST(PrimaryDeviceTest, Inquiry);
FRIEND_TEST(PrimaryDeviceTest, RequestSense); FRIEND_TEST(PrimaryDeviceTest, RequestSense);
FRIEND_TEST(PrimaryDeviceTest, ReportLuns);
FRIEND_TEST(PrimaryDeviceTest, UnknownCommand);
FRIEND_TEST(DiskTest, Rezero);
FRIEND_TEST(DiskTest, FormatUnit);
FRIEND_TEST(DiskTest, ReassignBlocks);
FRIEND_TEST(DiskTest, Seek);
FRIEND_TEST(DiskTest, ReadCapacity);
FRIEND_TEST(DiskTest, ReadWriteLong);
FRIEND_TEST(DiskTest, ReserveRelease);
FRIEND_TEST(DiskTest, SendDiagnostic);
FRIEND_TEST(DiskTest, PreventAllowMediumRemoval);
FRIEND_TEST(DiskTest, SynchronizeCache);
FRIEND_TEST(DiskTest, ReadDefectData);
public: public:
MOCK_METHOD(void, Reset, (), ());
MOCK_METHOD(void, Status, (), ()); MOCK_METHOD(void, Status, (), ());
MOCK_METHOD(void, DataIn, (), ()); MOCK_METHOD(void, DataIn, (), ());
MOCK_METHOD(void, DataOut, (), ()); MOCK_METHOD(void, DataOut, (), ());
MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override));
using ScsiController::ScsiController; explicit MockScsiController(int target_id) : ScsiController(bus, target_id) {}
~MockScsiController() override = default;
MockBus bus;
};
class MockDevice final : public Device
{
FRIEND_TEST(DeviceTest, Params);
FRIEND_TEST(DeviceTest, StatusCode);
FRIEND_TEST(DeviceTest, Reset);
FRIEND_TEST(DeviceTest, Start);
FRIEND_TEST(DeviceTest, Stop);
FRIEND_TEST(DeviceTest, Eject);
public:
MOCK_METHOD(int, GetId, (), (const));
explicit MockDevice(int lun) : Device("test", lun) {}
~MockDevice() override = default;
}; };
class MockPrimaryDevice final : public PrimaryDevice class MockPrimaryDevice final : public PrimaryDevice
@ -86,12 +175,15 @@ class MockPrimaryDevice final : public PrimaryDevice
FRIEND_TEST(PrimaryDeviceTest, TestUnitReady); FRIEND_TEST(PrimaryDeviceTest, TestUnitReady);
FRIEND_TEST(PrimaryDeviceTest, RequestSense); FRIEND_TEST(PrimaryDeviceTest, RequestSense);
FRIEND_TEST(PrimaryDeviceTest, Inquiry); FRIEND_TEST(PrimaryDeviceTest, Inquiry);
FRIEND_TEST(ScsiControllerTest, RequestSense);
FRIEND_TEST(RascsiExecutorTest, ValidationOperationAgainstDevice);
public: public:
MOCK_METHOD(void, Reset, (), ());
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const)); MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
MockPrimaryDevice() : PrimaryDevice("test") {} explicit MockPrimaryDevice(int lun) : PrimaryDevice("test", lun) {}
~MockPrimaryDevice() override = default; ~MockPrimaryDevice() override = default;
}; };
@ -99,12 +191,12 @@ class MockModePageDevice final : public ModePageDevice
{ {
FRIEND_TEST(ModePagesTest, ModePageDevice_AddModePages); FRIEND_TEST(ModePagesTest, ModePageDevice_AddModePages);
MockModePageDevice() : ModePageDevice("test") {} explicit MockModePageDevice(int lun) : ModePageDevice("test", lun) {}
~MockModePageDevice() override = default; ~MockModePageDevice() override = default;
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const)); MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
MOCK_METHOD(int, ModeSense6, (const vector<int>&, vector<BYTE>&, int), (const override)); MOCK_METHOD(int, ModeSense6, (const vector<int>&, vector<BYTE>&), (const override));
MOCK_METHOD(int, ModeSense10, (const vector<int>&, vector<BYTE>&, int), (const override)); MOCK_METHOD(int, ModeSense10, (const vector<int>&, vector<BYTE>&), (const override));
void SetUpModePages(map<int, vector<byte>>& pages, int page, bool) const override { void SetUpModePages(map<int, vector<byte>>& pages, int page, bool) const override {
// Return dummy data for other pages than page 0 // Return dummy data for other pages than page 0
@ -115,17 +207,8 @@ class MockModePageDevice final : public ModePageDevice
} }
}; };
class MockSCSIHD final : public SCSIHD class MockDisk final : public Disk
{ {
FRIEND_TEST(ModePagesTest, SCSIHD_SetUpModePages);
explicit MockSCSIHD(const unordered_set<uint32_t>& sector_sizes) : SCSIHD(sector_sizes, false) {}
~MockSCSIHD() override = default;
};
class MockSCSIHD_NEC final : public SCSIHD_NEC //NOSONAR Ignore inheritance hierarchy depth in unit tests
{
FRIEND_TEST(ModePagesTest, SCSIHD_NEC_SetUpModePages);
FRIEND_TEST(DiskTest, Rezero); FRIEND_TEST(DiskTest, Rezero);
FRIEND_TEST(DiskTest, FormatUnit); FRIEND_TEST(DiskTest, FormatUnit);
FRIEND_TEST(DiskTest, ReassignBlocks); FRIEND_TEST(DiskTest, ReassignBlocks);
@ -138,9 +221,30 @@ class MockSCSIHD_NEC final : public SCSIHD_NEC //NOSONAR Ignore inheritance hier
FRIEND_TEST(DiskTest, SynchronizeCache); FRIEND_TEST(DiskTest, SynchronizeCache);
FRIEND_TEST(DiskTest, ReadDefectData); FRIEND_TEST(DiskTest, ReadDefectData);
FRIEND_TEST(DiskTest, SectorSize); FRIEND_TEST(DiskTest, SectorSize);
FRIEND_TEST(DiskTest, ConfiguredSectorSize);
FRIEND_TEST(DiskTest, BlockCount); FRIEND_TEST(DiskTest, BlockCount);
public:
MOCK_METHOD(vector<byte>, InquiryInternal, (), (const));
MOCK_METHOD(void, FlushCache, (), (override));
MockDisk() : Disk("test", 0) {}
~MockDisk() override = default;
};
class MockSCSIHD final : public SCSIHD
{
FRIEND_TEST(DiskTest, ConfiguredSectorSize);
FRIEND_TEST(ModePagesTest, SCSIHD_SetUpModePages);
FRIEND_TEST(RascsiExecutorTest, SetSectorSize);
using SCSIHD::SCSIHD;
};
class MockSCSIHD_NEC final : public SCSIHD_NEC //NOSONAR Ignore inheritance hierarchy depth in unit tests
{
FRIEND_TEST(ModePagesTest, SCSIHD_NEC_SetUpModePages);
MOCK_METHOD(void, FlushCache, (), (override)); MOCK_METHOD(void, FlushCache, (), (override));
using SCSIHD_NEC::SCSIHD_NEC; using SCSIHD_NEC::SCSIHD_NEC;
@ -166,3 +270,14 @@ class MockHostServices final : public HostServices
using HostServices::HostServices; using HostServices::HostServices;
}; };
class MockCommandContext : public CommandContext
{
ProtobufSerializer s;
Localizer l;
public:
MockCommandContext() : CommandContext(s, l, STDOUT_FILENO, "") {}
~MockCommandContext() = default;
};

View File

@ -7,9 +7,10 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "controllers/controller_manager.h"
#include "devices/scsi_command_util.h" #include "devices/scsi_command_util.h"
#include "devices/scsihd.h" #include "devices/scsihd.h"
#include "devices/scsihd_nec.h" #include "devices/scsihd_nec.h"
@ -24,11 +25,12 @@ TEST(ModePagesTest, ModePageDevice_AddModePages)
vector<int> cdb(6); vector<int> cdb(6);
vector<BYTE> buf(512); vector<BYTE> buf(512);
MockModePageDevice device; MockModePageDevice device(0);
cdb[2] = 0x3f; cdb[2] = 0x3f;
EXPECT_EQ(0, device.AddModePages(cdb, buf, 0, 0)) << "Allocation length was not limited"; EXPECT_EQ(0, device.AddModePages(cdb, buf, 0, -1)) << "Negative maximum length must be rejected";
EXPECT_EQ(1, device.AddModePages(cdb, buf, 0, 1)) << "Allocation length was not limited"; EXPECT_EQ(0, device.AddModePages(cdb, buf, 0, 0)) << "Allocation length 0 must be rejected";
EXPECT_EQ(1, device.AddModePages(cdb, buf, 0, 1)) << "Allocation length 1 must be rejected";
cdb[2] = 0x00; cdb[2] = 0x00;
EXPECT_THROW(device.AddModePages(cdb, buf, 0, 12), scsi_error_exception) << "Data for non-existing mode page 0 were returned"; EXPECT_THROW(device.AddModePages(cdb, buf, 0, 12), scsi_error_exception) << "Data for non-existing mode page 0 were returned";
@ -38,7 +40,7 @@ TEST(ModePagesTest, SCSIHD_SetUpModePages)
{ {
map<int, vector<byte>> mode_pages; map<int, vector<byte>> mode_pages;
const unordered_set<uint32_t> sector_sizes; const unordered_set<uint32_t> sector_sizes;
MockSCSIHD device(sector_sizes); MockSCSIHD device(0, sector_sizes, false);
device.SetUpModePages(mode_pages, 0x3f, false); device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of mode pages"; EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of mode pages";
@ -52,7 +54,7 @@ TEST(ModePagesTest, SCSIHD_SetUpModePages)
TEST(ModePagesTest, SCSIHD_NEC_SetUpModePages) TEST(ModePagesTest, SCSIHD_NEC_SetUpModePages)
{ {
map<int, vector<byte>> mode_pages; map<int, vector<byte>> mode_pages;
MockSCSIHD_NEC device; MockSCSIHD_NEC device(0);
device.SetUpModePages(mode_pages, 0x3f, false); device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of mode pages"; EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of mode pages";
@ -67,7 +69,7 @@ TEST(ModePagesTest, SCSICD_SetUpModePages)
{ {
map<int, vector<byte>> mode_pages; map<int, vector<byte>> mode_pages;
const unordered_set<uint32_t> sector_sizes; const unordered_set<uint32_t> sector_sizes;
MockSCSICD device(sector_sizes); MockSCSICD device(0, sector_sizes);
device.SetUpModePages(mode_pages, 0x3f, false); device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(7, mode_pages.size()) << "Unexpected number of mode pages"; EXPECT_EQ(7, mode_pages.size()) << "Unexpected number of mode pages";
@ -84,7 +86,7 @@ TEST(ModePagesTest, SCSIMO_SetUpModePages)
{ {
map<int, vector<byte>> mode_pages; map<int, vector<byte>> mode_pages;
const unordered_set<uint32_t> sector_sizes; const unordered_set<uint32_t> sector_sizes;
MockSCSIMO device(sector_sizes); MockSCSIMO device(0, sector_sizes);
device.SetUpModePages(mode_pages, 0x3f, false); device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of mode pages"; EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of mode pages";
@ -98,11 +100,12 @@ TEST(ModePagesTest, SCSIMO_SetUpModePages)
TEST(ModePagesTest, HostServices_SetUpModePages) TEST(ModePagesTest, HostServices_SetUpModePages)
{ {
MockBus bus;
ControllerManager controller_manager(bus);
MockHostServices device(0, controller_manager);
map<int, vector<byte>> mode_pages; map<int, vector<byte>> mode_pages;
DeviceFactory device_factory;
MockHostServices device(device_factory);
device.SetUpModePages(mode_pages, 0x3f, false);
device.SetUpModePages(mode_pages, 0x3f, false);
EXPECT_EQ(1, mode_pages.size()) << "Unexpected number of mode pages"; EXPECT_EQ(1, mode_pages.size()) << "Unexpected number of mode pages";
EXPECT_EQ(10, mode_pages[32].size()); EXPECT_EQ(10, mode_pages[32].size());
} }

View File

@ -0,0 +1,40 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "mocks.h"
#include "controllers/phase_handler.h"
TEST(PhaseHandlerTest, Phases)
{
MockPhaseHandler handler;
handler.SetPhase(BUS::phase_t::selection);
EXPECT_TRUE(handler.IsSelection());
handler.SetPhase(BUS::phase_t::busfree);
EXPECT_TRUE(handler.IsBusFree());
handler.SetPhase(BUS::phase_t::command);
EXPECT_TRUE(handler.IsCommand());
handler.SetPhase(BUS::phase_t::status);
EXPECT_TRUE(handler.IsStatus());
handler.SetPhase(BUS::phase_t::datain);
EXPECT_TRUE(handler.IsDataIn());
handler.SetPhase(BUS::phase_t::dataout);
EXPECT_TRUE(handler.IsDataOut());
handler.SetPhase(BUS::phase_t::msgin);
EXPECT_TRUE(handler.IsMsgIn());
handler.SetPhase(BUS::phase_t::msgout);
EXPECT_TRUE(handler.IsMsgOut());
}

View File

@ -7,101 +7,117 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "scsi.h"
#include "rascsi_exceptions.h" #include "rascsi_exceptions.h"
#include "devices/primary_device.h" #include "devices/primary_device.h"
#include "devices/device_factory.h" #include "devices/device_factory.h"
using namespace scsi_defs;
TEST(PrimaryDeviceTest, GetId)
{
const int ID = 5;
MockAbstractController controller(ID);
auto device = make_shared<MockPrimaryDevice>(0);
EXPECT_EQ(-1, device->GetId()) << "Device ID cannot be known without assignment to a controller";
controller.AddDevice(device);
EXPECT_EQ(ID, device->GetId());
}
TEST(PrimaryDeviceTest, PhaseChange) TEST(PrimaryDeviceTest, PhaseChange)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockPrimaryDevice device; auto device = make_shared<MockPrimaryDevice>(0);
controller.AddDevice(&device); controller.AddDevice(device);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
device.EnterStatusPhase(); device->EnterStatusPhase();
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
device.EnterDataInPhase(); device->EnterDataInPhase();
EXPECT_CALL(controller, DataOut()).Times(1); EXPECT_CALL(controller, DataOut()).Times(1);
device.EnterDataOutPhase(); device->EnterDataOutPhase();
} }
TEST(PrimaryDeviceTest, TestUnitReady) TEST(PrimaryDeviceTest, TestUnitReady)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockPrimaryDevice device; auto device = make_shared<MockPrimaryDevice>(0);
controller.AddDevice(&device); controller.AddDevice(device);
device.SetReset(true); device->SetReset(true);
device.SetAttn(true); device->SetAttn(true);
device.SetReady(false); device->SetReady(false);
EXPECT_CALL(controller, DataIn()).Times(0); EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception); EXPECT_THROW(device->Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception);
device.SetReset(false); device->SetReset(false);
EXPECT_CALL(controller, DataIn()).Times(0); EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception); EXPECT_THROW(device->Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception);
device.SetReset(true); device->SetReset(true);
device.SetAttn(false); device->SetAttn(false);
EXPECT_CALL(controller, DataIn()).Times(0); EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception); EXPECT_THROW(device->Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception);
device.SetReset(false); device->SetReset(false);
device.SetAttn(true); device->SetAttn(true);
EXPECT_CALL(controller, DataIn()).Times(0); EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception); EXPECT_THROW(device->Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception);
device.SetAttn(false); device->SetAttn(false);
EXPECT_CALL(controller, DataIn()).Times(0); EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception); EXPECT_THROW(device->Dispatch(scsi_command::eCmdTestUnitReady), scsi_error_exception);
device.SetReady(true); device->SetReady(true);
EXPECT_CALL(controller, Status()).Times(1); EXPECT_CALL(controller, Status()).Times(1);
EXPECT_TRUE(device.Dispatch(scsi_command::eCmdTestUnitReady)); EXPECT_TRUE(device->Dispatch(scsi_command::eCmdTestUnitReady));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(PrimaryDeviceTest, Inquiry) TEST(PrimaryDeviceTest, Inquiry)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockPrimaryDevice device; auto device = make_shared<MockPrimaryDevice>(0);
device.SetController(&controller); device->SetController(&controller);
vector<int>& cmd = controller.InitCmd(6); vector<int>& cmd = controller.InitCmd(6);
// ALLOCATION LENGTH // ALLOCATION LENGTH
cmd[4] = 255; cmd[4] = 255;
ON_CALL(device, InquiryInternal()).WillByDefault([&device]() { ON_CALL(*device, InquiryInternal()).WillByDefault([&device]() {
return device.HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false); return device->HandleInquiry(device_type::PROCESSOR, scsi_level::SPC_3, false);
}); });
EXPECT_CALL(device, InquiryInternal()).Times(1); EXPECT_CALL(*device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch(scsi_command::eCmdInquiry)); EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
EXPECT_EQ(0x7F, controller.GetBuffer()[0]) << "Invalid LUN was not reported"; EXPECT_EQ(0x7F, controller.GetBuffer()[0]) << "Invalid LUN was not reported";
EXPECT_TRUE(controller.AddDevice(&device)); EXPECT_TRUE(controller.AddDevice(device));
EXPECT_FALSE(controller.AddDevice(&device)) << "Duplicate LUN was not rejected"; EXPECT_FALSE(controller.AddDevice(device)) << "Duplicate LUN was not rejected";
EXPECT_CALL(device, InquiryInternal()).Times(1); EXPECT_CALL(*device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch(scsi_command::eCmdInquiry)); EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
EXPECT_EQ(device_type::PROCESSOR, (device_type)controller.GetBuffer()[0]); EXPECT_EQ(device_type::PROCESSOR, (device_type)controller.GetBuffer()[0]);
EXPECT_EQ(0x00, controller.GetBuffer()[1]) << "Device was not reported as non-removable"; EXPECT_EQ(0x00, controller.GetBuffer()[1]) << "Device was not reported as non-removable";
EXPECT_EQ(scsi_level::SPC_3, (scsi_level)controller.GetBuffer()[2]) << "Wrong SCSI level"; EXPECT_EQ(scsi_level::SPC_3, (scsi_level)controller.GetBuffer()[2]) << "Wrong SCSI level";
EXPECT_EQ(scsi_level::SCSI_2, (scsi_level)controller.GetBuffer()[3]) << "Wrong response level"; EXPECT_EQ(scsi_level::SCSI_2, (scsi_level)controller.GetBuffer()[3]) << "Wrong response level";
EXPECT_EQ(0x1f, controller.GetBuffer()[4]) << "Wrong additional data size"; EXPECT_EQ(0x1f, controller.GetBuffer()[4]) << "Wrong additional data size";
ON_CALL(device, InquiryInternal()).WillByDefault([&device]() { ON_CALL(*device, InquiryInternal()).WillByDefault([&device]() {
return device.HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, true); return device->HandleInquiry(device_type::DIRECT_ACCESS, scsi_level::SCSI_1_CCS, true);
}); });
EXPECT_CALL(device, InquiryInternal()).Times(1); EXPECT_CALL(*device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch(scsi_command::eCmdInquiry)); EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
EXPECT_EQ(device_type::DIRECT_ACCESS, (device_type)controller.GetBuffer()[0]); EXPECT_EQ(device_type::DIRECT_ACCESS, (device_type)controller.GetBuffer()[0]);
EXPECT_EQ(0x80, controller.GetBuffer()[1]) << "Device was not reported as removable"; EXPECT_EQ(0x80, controller.GetBuffer()[1]) << "Device was not reported as removable";
EXPECT_EQ(scsi_level::SCSI_1_CCS, (scsi_level)controller.GetBuffer()[2]) << "Wrong SCSI level"; EXPECT_EQ(scsi_level::SCSI_1_CCS, (scsi_level)controller.GetBuffer()[2]) << "Wrong SCSI level";
@ -110,41 +126,41 @@ TEST(PrimaryDeviceTest, Inquiry)
cmd[1] = 0x01; cmd[1] = 0x01;
EXPECT_CALL(controller, DataIn()).Times(0); EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdInquiry), scsi_error_exception) << "EVPD bit is not supported"; EXPECT_THROW(device->Dispatch(scsi_command::eCmdInquiry), scsi_error_exception) << "EVPD bit is not supported";
cmd[2] = 0x01; cmd[2] = 0x01;
EXPECT_CALL(controller, DataIn()).Times(0); EXPECT_CALL(controller, DataIn()).Times(0);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdInquiry), scsi_error_exception) << "PAGE CODE field is not supported"; EXPECT_THROW(device->Dispatch(scsi_command::eCmdInquiry), scsi_error_exception) << "PAGE CODE field is not supported";
cmd[1] = 0x00; cmd[1] = 0x00;
cmd[2] = 0x00; cmd[2] = 0x00;
// ALLOCATION LENGTH // ALLOCATION LENGTH
cmd[4] = 1; cmd[4] = 1;
EXPECT_CALL(device, InquiryInternal()).Times(1); EXPECT_CALL(*device, InquiryInternal()).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch(scsi_command::eCmdInquiry)); EXPECT_TRUE(device->Dispatch(scsi_command::eCmdInquiry));
EXPECT_EQ(0x1F, controller.GetBuffer()[4]) << "Wrong additional data size"; EXPECT_EQ(0x1F, controller.GetBuffer()[4]) << "Wrong additional data size";
EXPECT_EQ(1, controller.GetLength()) << "Wrong ALLOCATION LENGTH handling"; EXPECT_EQ(1, controller.GetLength()) << "Wrong ALLOCATION LENGTH handling";
} }
TEST(PrimaryDeviceTest, RequestSense) TEST(PrimaryDeviceTest, RequestSense)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockPrimaryDevice device; auto device = make_shared<MockPrimaryDevice>(0);
controller.AddDevice(&device); controller.AddDevice(device);
vector<int>& cmd = controller.InitCmd(6); vector<int>& cmd = controller.InitCmd(6);
// ALLOCATION LENGTH // ALLOCATION LENGTH
cmd[4] = 255; cmd[4] = 255;
device.SetReady(false); device->SetReady(false);
EXPECT_THROW(device.Dispatch(scsi_command::eCmdRequestSense), scsi_error_exception); EXPECT_THROW(device->Dispatch(scsi_command::eCmdRequestSense), scsi_error_exception);
device.SetReady(true); device->SetReady(true);
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device.Dispatch(scsi_command::eCmdRequestSense)); EXPECT_TRUE(device->Dispatch(scsi_command::eCmdRequestSense));
EXPECT_EQ(0, controller.GetStatus()); EXPECT_EQ(status::GOOD, controller.GetStatus());
} }
TEST(PrimaryDeviceTest, ReportLuns) TEST(PrimaryDeviceTest, ReportLuns)
@ -152,15 +168,13 @@ TEST(PrimaryDeviceTest, ReportLuns)
const int LUN1 = 1; const int LUN1 = 1;
const int LUN2 = 4; const int LUN2 = 4;
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockPrimaryDevice device1; auto device1 = make_shared<MockPrimaryDevice>(LUN1);
MockPrimaryDevice device2; auto device2 = make_shared<MockPrimaryDevice>(LUN2);
device1.SetLun(LUN1); controller.AddDevice(device1);
controller.AddDevice(&device1);
EXPECT_TRUE(controller.HasDeviceForLun(LUN1)); EXPECT_TRUE(controller.HasDeviceForLun(LUN1));
device2.SetLun(LUN2); controller.AddDevice(device2);
controller.AddDevice(&device2);
EXPECT_TRUE(controller.HasDeviceForLun(LUN2)); EXPECT_TRUE(controller.HasDeviceForLun(LUN2));
vector<int>& cmd = controller.InitCmd(10); vector<int>& cmd = controller.InitCmd(10);
@ -168,7 +182,7 @@ TEST(PrimaryDeviceTest, ReportLuns)
cmd[9] = 255; cmd[9] = 255;
EXPECT_CALL(controller, DataIn()).Times(1); EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device1.Dispatch(scsi_command::eCmdReportLuns)); EXPECT_TRUE(device1->Dispatch(scsi_command::eCmdReportLuns));
const vector<BYTE>& buffer = controller.GetBuffer(); const vector<BYTE>& buffer = controller.GetBuffer();
EXPECT_EQ(0x00, buffer[0]) << "Wrong data length"; EXPECT_EQ(0x00, buffer[0]) << "Wrong data length";
EXPECT_EQ(0x00, buffer[1]) << "Wrong data length"; EXPECT_EQ(0x00, buffer[1]) << "Wrong data length";
@ -192,23 +206,47 @@ TEST(PrimaryDeviceTest, ReportLuns)
EXPECT_EQ(LUN2, buffer[23]) << "Wrong LUN2 number"; EXPECT_EQ(LUN2, buffer[23]) << "Wrong LUN2 number";
cmd[2] = 0x01; cmd[2] = 0x01;
EXPECT_THROW(device1.Dispatch(scsi_command::eCmdReportLuns), scsi_error_exception) << "Only SELECT REPORT mode 0 is supported"; EXPECT_THROW(device1->Dispatch(scsi_command::eCmdReportLuns), scsi_error_exception) << "Only SELECT REPORT mode 0 is supported";
} }
TEST(PrimaryDeviceTest, UnknownCommand) TEST(PrimaryDeviceTest, UnknownCommand)
{ {
MockScsiController controller(nullptr, 0); MockAbstractController controller(0);
MockPrimaryDevice device; auto device = make_shared<MockPrimaryDevice>(0);
controller.AddDevice(&device); controller.AddDevice(device);
controller.InitCmd(1); controller.InitCmd(1);
EXPECT_FALSE(device.Dispatch((scsi_command)0xFF)); EXPECT_FALSE(device->Dispatch((scsi_command)0xFF));
}
TEST(PrimaryDeviceTest, WriteByteSequence)
{
vector<BYTE> data;
MockPrimaryDevice device(0);
EXPECT_FALSE(device.WriteByteSequence(data, 0)) << "Primary device must not support writing byte sequences";
} }
TEST(PrimaryDeviceTest, GetSendDelay) TEST(PrimaryDeviceTest, GetSendDelay)
{ {
MockPrimaryDevice device; MockPrimaryDevice device(0);
EXPECT_EQ(-1, device.GetSendDelay()); EXPECT_EQ(-1, device.GetSendDelay());
} }
TEST(PrimaryDeviceTest, Init)
{
unordered_map<string, string> params;
MockPrimaryDevice device(0);
EXPECT_TRUE(device.Init(params)) << "Initialization of primary device must not fail";
}
TEST(PrimaryDeviceTest, FlushCache)
{
MockPrimaryDevice device(0);
// Method must be present
device.FlushCache();
}

View File

@ -0,0 +1,24 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "mocks.h"
#include "rascsi_exceptions.h"
#include "rascsi_interface.pb.h"
#include "protobuf_serializer.h"
using namespace rascsi_interface;
TEST(ProtobufSerializerTest, SerializeMessage)
{
PbResult message;
ProtobufSerializer serializer;
serializer.SerializeMessage(STDOUT_FILENO, message);
EXPECT_THROW(serializer.SerializeMessage(-1, message), io_exception);
}

View File

@ -0,0 +1,72 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "mocks.h"
#include "rascsi_exceptions.h"
using namespace scsi_defs;
TEST(RascsiExceptionsTest, IoException)
{
try {
throw io_exception("msg");
}
catch(const io_exception& e) {
EXPECT_EQ("msg", e.get_msg());
}
}
TEST(RascsiExceptionsTest, FileNotFoundException)
{
try {
throw file_not_found_exception("msg");
}
catch(const file_not_found_exception& e) {
EXPECT_EQ("msg", e.get_msg());
}
}
TEST(RascsiExceptionsTest, ScsiErrorException)
{
try {
throw scsi_error_exception();
}
catch(const scsi_error_exception& e) {
EXPECT_EQ(sense_key::ABORTED_COMMAND, e.get_sense_key());
EXPECT_EQ(asc::NO_ADDITIONAL_SENSE_INFORMATION, e.get_asc());
EXPECT_EQ(status::CHECK_CONDITION, e.get_status());
}
try {
throw scsi_error_exception(sense_key::UNIT_ATTENTION);
}
catch(const scsi_error_exception& e) {
EXPECT_EQ(sense_key::UNIT_ATTENTION, e.get_sense_key());
EXPECT_EQ(asc::NO_ADDITIONAL_SENSE_INFORMATION, e.get_asc());
EXPECT_EQ(status::CHECK_CONDITION, e.get_status());
}
try {
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::LBA_OUT_OF_RANGE);
}
catch(const scsi_error_exception& e) {
EXPECT_EQ(sense_key::UNIT_ATTENTION, e.get_sense_key());
EXPECT_EQ(asc::LBA_OUT_OF_RANGE, e.get_asc());
EXPECT_EQ(status::CHECK_CONDITION, e.get_status());
}
try {
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::LBA_OUT_OF_RANGE, status::BUSY);
}
catch(const scsi_error_exception& e) {
EXPECT_EQ(sense_key::UNIT_ATTENTION, e.get_sense_key());
EXPECT_EQ(asc::LBA_OUT_OF_RANGE, e.get_asc());
EXPECT_EQ(status::BUSY, e.get_status());
}
}

View File

@ -0,0 +1,491 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "mocks.h"
#include "controllers/controller_manager.h"
#include "devices/device_factory.h"
#include "command_util.h"
#include "rascsi_response.h"
#include "rascsi_image.h"
#include "rascsi_executor.h"
using namespace rascsi_interface;
using namespace command_util;
TEST(RascsiExecutorTest, ProcessCmd)
{
const int ID = 3;
const int LUN = 0;
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
PbDeviceDefinition definition;
PbCommand command;
MockCommandContext context;
definition.set_id(8);
definition.set_unit(32);
EXPECT_FALSE(executor.ProcessCmd(context, definition, command, true)) << "Invalid ID must fail";
definition.set_id(ID);
EXPECT_FALSE(executor.ProcessCmd(context, definition, command, true)) << "Invalid LUN must fail";
definition.set_unit(LUN);
EXPECT_FALSE(executor.ProcessCmd(context, definition, command, true)) << "Unknown operation must fail";
command.set_operation(ATTACH);
EXPECT_FALSE(executor.ProcessCmd(context, definition, command, true)) << "Operation for unknown device must fail";
}
TEST(RascsiExecutorTest, SetLogLevel)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
EXPECT_TRUE(executor.SetLogLevel("trace"));
EXPECT_TRUE(executor.SetLogLevel("debug"));
EXPECT_TRUE(executor.SetLogLevel("info"));
EXPECT_TRUE(executor.SetLogLevel("warn"));
EXPECT_TRUE(executor.SetLogLevel("err"));
EXPECT_TRUE(executor.SetLogLevel("critical"));
EXPECT_TRUE(executor.SetLogLevel("off"));
EXPECT_FALSE(executor.SetLogLevel("xyz"));
}
TEST(RascsiExecutorTest, Attach)
{
const int ID = 3;
const int LUN = 0;
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
PbDeviceDefinition definition;
MockCommandContext context;
definition.set_unit(32);
EXPECT_FALSE(executor.Attach(context, definition, false));
auto device = device_factory.CreateDevice(controller_manager, SCHD, LUN, "");
definition.set_id(ID);
definition.set_unit(LUN);
executor.SetReservedIds(to_string(ID));
EXPECT_FALSE(executor.Attach(context, definition, false)) << "Reserved ID not rejected";
executor.SetReservedIds("");
EXPECT_FALSE(executor.Attach(context, definition, false)) << "Unknown device type not rejected";
definition.set_type(PbDeviceType::SCHS);
EXPECT_TRUE(executor.Attach(context, definition, false));
controller_manager.DeleteAllControllers();
definition.set_type(PbDeviceType::SCHD);
EXPECT_FALSE(executor.Attach(context, definition, false)) << "Drive without sectors not rejected";
definition.set_block_size(1);
EXPECT_FALSE(executor.Attach(context, definition, false)) << "Drive with invalid sector size not rejeced";
definition.set_block_size(1024);
EXPECT_FALSE(executor.Attach(context, definition, false)) << "Drive without image file not rejected";
AddParam(definition, "file", "/non_existing_file");
EXPECT_FALSE(executor.Attach(context, definition, false));
AddParam(definition, "file", "/dev/zero");
EXPECT_FALSE(executor.Attach(context, definition, false)) << "Empty image file not rejected";
// Further testing is not possible without a filesystem
}
TEST(RascsiExecutorTest, Insert)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
PbDeviceDefinition definition;
MockCommandContext context;
auto device = device_factory.CreateDevice(controller_manager, SCRM, 0, "test");
device->SetRemoved(false);
EXPECT_FALSE(executor.Insert(context, definition, device, false)) << "Medium is not removed";
device->SetRemoved(true);
definition.set_vendor("v");
EXPECT_FALSE(executor.Insert(context, definition, device, false)) << "Product data must not be set";
definition.set_vendor("");
definition.set_product("p");
EXPECT_FALSE(executor.Insert(context, definition, device, false)) << "Product data must not be set";
definition.set_product("");
definition.set_revision("r");
EXPECT_FALSE(executor.Insert(context, definition, device, false)) << "Product data must not be set";
definition.set_revision("");
EXPECT_FALSE(executor.Insert(context, definition, device, false)) << "Filename is missing";
AddParam(definition, "file", "filename");
EXPECT_TRUE(executor.Insert(context, definition, device, true)) << "Dry-run must not fail";
EXPECT_FALSE(executor.Insert(context, definition, device, false));
definition.set_block_size(1);
EXPECT_FALSE(executor.Insert(context, definition, device, false));
definition.set_block_size(0);
EXPECT_FALSE(executor.Insert(context, definition, device, false)) << "Image file validation has to fail";
AddParam(definition, "file", "/non_existing_file");
EXPECT_FALSE(executor.Insert(context, definition, device, false));
AddParam(definition, "file", "/dev/zero");
EXPECT_FALSE(executor.Insert(context, definition, device, false)) << "File has 0 bytes";
// Further testing is not possible without a filesystem
}
TEST(RascsiExecutorTest, Detach)
{
const int ID = 3;
const int LUN1 = 0;
const int LUN2 = 1;
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
auto device1 = device_factory.CreateDevice(controller_manager, UNDEFINED, LUN1, "services");
controller_manager.AttachToScsiController(ID, device1);
auto device2 = device_factory.CreateDevice(controller_manager, UNDEFINED, LUN2, "services");
controller_manager.AttachToScsiController(ID, device2);
auto d1 = controller_manager.GetDeviceByIdAndLun(ID, LUN1);
EXPECT_FALSE(executor.Detach(context, d1, false)) << "LUNs > 0 have to be detached first";
auto d2 = controller_manager.GetDeviceByIdAndLun(ID, LUN2);
EXPECT_TRUE(executor.Detach(context, d2, false));
EXPECT_TRUE(executor.Detach(context, d1, false));
EXPECT_TRUE(controller_manager.GetAllDevices().empty());
}
TEST(RascsiExecutorTest, DetachAll)
{
const int ID = 4;
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "services");
controller_manager.AttachToScsiController(ID, device);
EXPECT_NE(nullptr, controller_manager.FindController(ID));
EXPECT_FALSE(controller_manager.GetAllDevices().empty());
executor.DetachAll();
EXPECT_EQ(nullptr, controller_manager.FindController(ID));
EXPECT_TRUE(controller_manager.GetAllDevices().empty());
}
TEST(RascsiExecutorTest, ShutDown)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
EXPECT_FALSE(executor.ShutDown(context, ""));
EXPECT_FALSE(executor.ShutDown(context, "xyz"));
EXPECT_TRUE(executor.ShutDown(context, "rascsi"));
EXPECT_FALSE(executor.ShutDown(context, "system"));
EXPECT_FALSE(executor.ShutDown(context, "reboot"));
}
TEST(RascsiExecutorTest, SetReservedIds)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
string error = executor.SetReservedIds("xyz");
EXPECT_FALSE(error.empty());
EXPECT_TRUE(executor.GetReservedIds().empty());
error = executor.SetReservedIds("8");
EXPECT_FALSE(error.empty());
EXPECT_TRUE(executor.GetReservedIds().empty());
error = executor.SetReservedIds("-1");
EXPECT_FALSE(error.empty());
EXPECT_TRUE(executor.GetReservedIds().empty());
error = executor.SetReservedIds("");
EXPECT_TRUE(error.empty());
EXPECT_TRUE(executor.GetReservedIds().empty());
error = executor.SetReservedIds("7,1,2,3,5");
EXPECT_TRUE(error.empty());
unordered_set<int> reserved_ids = executor.GetReservedIds();
EXPECT_EQ(5, reserved_ids.size());
EXPECT_NE(reserved_ids.end(), reserved_ids.find(1));
EXPECT_NE(reserved_ids.end(), reserved_ids.find(2));
EXPECT_NE(reserved_ids.end(), reserved_ids.find(3));
EXPECT_NE(reserved_ids.end(), reserved_ids.find(5));
EXPECT_NE(reserved_ids.end(), reserved_ids.find(7));
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "services");
controller_manager.AttachToScsiController(5, device);
error = executor.SetReservedIds("5");
EXPECT_FALSE(error.empty());
}
TEST(RascsiExecutorTest, ValidateImageFile)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
string full_path;
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "services");
EXPECT_TRUE(executor.ValidateImageFile(context, device, "", full_path));
EXPECT_TRUE(full_path.empty());
device = device_factory.CreateDevice(controller_manager, SCHD, 0, "test");
EXPECT_TRUE(executor.ValidateImageFile(context, device, "", full_path));
EXPECT_TRUE(full_path.empty());
EXPECT_FALSE(executor.ValidateImageFile(context, device, "/non_existing_file", full_path));
EXPECT_TRUE(full_path.empty());
}
TEST(RascsiExecutorTest, ValidateLunSetup)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
PbCommand command;
auto device1 = command.add_devices();
device1->set_unit(0);
string error = executor.ValidateLunSetup(command);
EXPECT_TRUE(error.empty());
device1->set_unit(1);
error = executor.ValidateLunSetup(command);
EXPECT_FALSE(error.empty());
auto device2 = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, "services");
controller_manager.AttachToScsiController(0, device2);
error = executor.ValidateLunSetup(command);
EXPECT_TRUE(error.empty());
}
TEST(RascsiExecutorTest, VerifyExistingIdAndLun)
{
const int ID = 1;
const int LUN1 = 2;
const int LUN2 = 3;
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
EXPECT_FALSE(executor.VerifyExistingIdAndLun(context, ID, LUN1));
auto device = device_factory.CreateDevice(controller_manager, UNDEFINED, LUN1, "services");
controller_manager.AttachToScsiController(ID, device);
EXPECT_TRUE(executor.VerifyExistingIdAndLun(context, ID, LUN1));
EXPECT_FALSE(executor.VerifyExistingIdAndLun(context, ID, LUN2));
}
TEST(RascsiExecutorTest, CreateDevice)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
EXPECT_EQ(nullptr, executor.CreateDevice(context, UNDEFINED, 0, ""));
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
EXPECT_EQ(nullptr, executor.CreateDevice(context, SAHD, 0, ""));
#pragma GCC diagnostic pop
EXPECT_NE(nullptr, executor.CreateDevice(context, UNDEFINED, 0, "services"));
EXPECT_NE(nullptr, executor.CreateDevice(context, SCHS, 0, ""));
}
TEST(RascsiExecutorTest, SetSectorSize)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
unordered_set<uint32_t> sizes;
auto disk = make_shared<MockSCSIHD>(0, sizes, false);
EXPECT_FALSE(executor.SetSectorSize(context, "test", disk, 512));
sizes.insert(512);
disk = make_shared<MockSCSIHD>(0, sizes, false);
EXPECT_TRUE(executor.SetSectorSize(context, "test", disk, 0));
EXPECT_FALSE(executor.SetSectorSize(context, "test", disk, 1));
EXPECT_TRUE(executor.SetSectorSize(context, "test", disk, 512));
}
TEST(RascsiExecutorTest, ValidationOperationAgainstDevice)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
auto device = make_shared<MockPrimaryDevice>(0);
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, ATTACH));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, DETACH));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, START));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, STOP));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, INSERT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, EJECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, PROTECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, UNPROTECT));
device->SetStoppable(true);
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, START));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, STOP));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, INSERT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, EJECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, PROTECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, UNPROTECT));
device->SetRemovable(true);
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, START));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, STOP));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, INSERT));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, EJECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, PROTECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, UNPROTECT));
device->SetProtectable(true);
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, START));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, STOP));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, INSERT));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, EJECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, PROTECT));
EXPECT_FALSE(executor.ValidationOperationAgainstDevice(context, device, UNPROTECT));
device->SetReady(true);
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, START));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, STOP));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, INSERT));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, EJECT));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, PROTECT));
EXPECT_TRUE(executor.ValidationOperationAgainstDevice(context, device, UNPROTECT));
}
TEST(RascsiExecutorTest, ValidateIdAndLun)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
EXPECT_FALSE(executor.ValidateIdAndLun(context, -1, 0));
EXPECT_FALSE(executor.ValidateIdAndLun(context, 8, 0));
EXPECT_FALSE(executor.ValidateIdAndLun(context, 7, -1));
EXPECT_FALSE(executor.ValidateIdAndLun(context, 7, 32));
EXPECT_TRUE(executor.ValidateIdAndLun(context, 7, 0));
EXPECT_TRUE(executor.ValidateIdAndLun(context, 7, 31));
}
TEST(RascsiExecutorTest, SetProductData)
{
MockBus bus;
DeviceFactory device_factory;
ControllerManager controller_manager(bus);
RascsiImage rascsi_image;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
RascsiExecutor executor(rascsi_response, rascsi_image, device_factory, controller_manager);
MockCommandContext context;
PbDeviceDefinition definition;
auto device = make_shared<MockPrimaryDevice>(0);
EXPECT_TRUE(executor.SetProductData(context, definition, device));
definition.set_vendor("123456789");
EXPECT_FALSE(executor.SetProductData(context, definition, device));
definition.set_vendor("1");
EXPECT_TRUE(executor.SetProductData(context, definition, device));
definition.set_vendor("12345678");
EXPECT_TRUE(executor.SetProductData(context, definition, device));
definition.set_product("12345678901234567");
EXPECT_FALSE(executor.SetProductData(context, definition, device));
definition.set_product("1");
EXPECT_TRUE(executor.SetProductData(context, definition, device));
definition.set_product("1234567890123456");
EXPECT_TRUE(executor.SetProductData(context, definition, device));
definition.set_revision("12345");
EXPECT_FALSE(executor.SetProductData(context, definition, device));
definition.set_revision("1");
EXPECT_TRUE(executor.SetProductData(context, definition, device));
definition.set_revision("1234");
EXPECT_TRUE(executor.SetProductData(context, definition, device));
}

View File

@ -0,0 +1,92 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "mocks.h"
#include "controllers/controller_manager.h"
#include "devices/device_factory.h"
#include "rascsi_interface.pb.h"
#include "rascsi_response.h"
using namespace rascsi_interface;
TEST(RascsiResponseTest, Operation_Count)
{
MockBus bus;
ControllerManager controller_manager(bus);
DeviceFactory device_factory;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
PbResult pb_operation_info_result;
const auto operation_info = rascsi_response.GetOperationInfo(pb_operation_info_result, 0);
EXPECT_EQ(PbOperation_ARRAYSIZE - 1, operation_info->operations_size());
}
void TestNonDiskDevice(const string& name, int default_param_count)
{
MockBus bus;
ControllerManager controller_manager(bus);
DeviceFactory device_factory;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
auto d = device_factory.CreateDevice(controller_manager, UNDEFINED, 0, name);
controller_manager.AttachToScsiController(0, d);
PbServerInfo server_info;
rascsi_response.GetDevices(server_info, "image_folder");
EXPECT_EQ(1, server_info.devices_info().devices().size());
const auto& device = server_info.devices_info().devices()[0];
EXPECT_FALSE(device.properties().read_only());
EXPECT_FALSE(device.properties().protectable());
EXPECT_FALSE(device.properties().stoppable());
EXPECT_FALSE(device.properties().removable());
EXPECT_FALSE(device.properties().lockable());
EXPECT_EQ(0, device.params().size());
EXPECT_EQ(32, device.properties().luns());
EXPECT_EQ(0, device.block_size());
EXPECT_EQ(0, device.block_count());
EXPECT_EQ(default_param_count, device.properties().default_params().size());
EXPECT_FALSE(device.properties().supports_file());
if (default_param_count) {
EXPECT_TRUE(device.properties().supports_params());
}
else {
EXPECT_FALSE(device.properties().supports_params());
}
}
TEST(RascsiResponseTest, GetDevice_Printer)
{
TestNonDiskDevice("printer", 2);
}
TEST(RascsiResponseTest, GetDevice_HostServices)
{
TestNonDiskDevice("services", 0);
}
TEST(RascsiResponseTest, GetReservedIds)
{
MockBus bus;
ControllerManager controller_manager(bus);
DeviceFactory device_factory;
RascsiResponse rascsi_response(device_factory, controller_manager, 32);
unordered_set<int> ids;
PbResult result;
const auto& reserved_ids_info1 = rascsi_response.GetReservedIds(result, ids);
EXPECT_TRUE(result.status());
EXPECT_TRUE(reserved_ids_info1->ids().empty());
ids.insert(3);
const auto& reserved_ids_info2 = rascsi_response.GetReservedIds(result, ids);
EXPECT_TRUE(result.status());
EXPECT_EQ(1, reserved_ids_info2->ids().size());
EXPECT_EQ(3, reserved_ids_info2->ids()[0]);
}

View File

@ -0,0 +1,27 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "mocks.h"
#include "rasutil.h"
using namespace ras_util;
TEST(RasUtilTest, GetAsInt)
{
int result;
EXPECT_FALSE(GetAsInt("", result));
EXPECT_FALSE(GetAsInt("xyz", result));
EXPECT_FALSE(GetAsInt("-1", result));
EXPECT_FALSE(GetAsInt("1234567898765432112345678987654321", result)) << "Value is out of range";
EXPECT_TRUE(GetAsInt("0", result));
EXPECT_EQ(0, result);
EXPECT_TRUE(GetAsInt("1234", result));
EXPECT_EQ(1234, result);
}

View File

@ -1,27 +0,0 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "testing.h"
#include "devices/device_factory.h"
#include "rascsi_interface.pb.h"
#include "rascsi_response.h"
#include "rascsi_image.h"
using namespace rascsi_interface;
TEST(ResponseTest, Operation_Count)
{
DeviceFactory device_factory;
RascsiImage rascsi_image;
RascsiResponse rascsi_response(&device_factory, &rascsi_image);
PbResult pb_operation_info_result;
const auto operation_info = unique_ptr<PbOperationInfo>(rascsi_response.GetOperationInfo(pb_operation_info_result, 0));
EXPECT_EQ(PbOperation_ARRAYSIZE - 1, operation_info->operations_size());
}

View File

@ -7,7 +7,7 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "devices/scsi_command_util.h" #include "devices/scsi_command_util.h"
using namespace scsi_command_util; using namespace scsi_command_util;

View File

@ -7,12 +7,35 @@
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#include "testing.h" #include "mocks.h"
#include "scsi.h"
#include "controllers/scsi_controller.h" #include "controllers/scsi_controller.h"
using namespace scsi_defs;
TEST(ScsiControllerTest, GetMaxLuns) TEST(ScsiControllerTest, GetMaxLuns)
{ {
MockScsiController controller(nullptr, 0); MockScsiController controller(0);
EXPECT_EQ(32, controller.GetMaxLuns()); EXPECT_EQ(32, controller.GetMaxLuns());
} }
TEST(ScsiControllerTest, RequestSense)
{
MockScsiController controller(0);
auto device = make_shared<MockPrimaryDevice>(0);
controller.AddDevice(device);
vector<int>& cmd = controller.InitCmd(6);
// ALLOCATION LENGTH
cmd[4] = 255;
// Non-existing LUN
cmd[1] = 0x20;
device->SetReady(true);
EXPECT_CALL(controller, Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN, status::CHECK_CONDITION)).Times(1);
EXPECT_CALL(controller, DataIn()).Times(1);
EXPECT_TRUE(device->Dispatch(scsi_command::eCmdRequestSense));
EXPECT_EQ(status::GOOD, controller.GetStatus()) << "Illegal CHECK CONDITION for non-exsting LUN";
}

View File

@ -0,0 +1,20 @@
//---------------------------------------------------------------------------
//
// SCSI Target Emulator RaSCSI Reloaded
// for Raspberry Pi
//
// Copyright (C) 2022 Uwe Seimet
//
//---------------------------------------------------------------------------
#include "mocks.h"
#include "rascsi_exceptions.h"
#include "devices/scsihd.h"
TEST(ScsiHdTest, FinalizeSetup)
{
MockSCSIHD_NEC disk(0);
Filepath filepath;
EXPECT_THROW(disk.FinalizeSetup(filepath, 2LL * 1024 * 1024 * 1024 * 1024 + 1, 0), io_exception);
}

View File

@ -11,14 +11,14 @@
#include "spdlog/spdlog.h" #include "spdlog/spdlog.h"
class Environment : public ::testing::Environment class Environment final : public ::testing::Environment
{ {
spdlog::level::level_enum log_level; spdlog::level::level_enum log_level;
public: public:
explicit Environment(spdlog::level::level_enum level) : log_level(level) {} explicit Environment(spdlog::level::level_enum level) : log_level(level) {}
~Environment() final = default; ~Environment() override = default;
void SetUp() override { spdlog::set_level(log_level); } void SetUp() override { spdlog::set_level(log_level); }
}; };