mirror of
https://github.com/akuker/RASCSI.git
synced 2024-06-10 02:29:33 +00:00
05db0e4688
* Fixing SonarCloud issues, first round * Fixing SonarCLoud issues, next round * Fixing SonarQube issues, next round * Fixed warning * Replaced empty constructors/destructors with = default; * Fixed warning * Replaced new * Use constants instead of macros * Use structured binding declarations * Use init statements for if * Use string views * Use enum class, use using instead of typedef * Fixed more SonarCloud warnings * Replaced redundant/duplicate types with auto * Devlared methods const * Memory management update * Fixed warning * Added missing const * Improved RaScsiResponse memory management * Improved memory management * Improved memory management * Replaced macros by constants, removed unused constants * Made member private * Fixed warning * Added comment * Fixed shadowing warnings * Cleanup * Cleanup * Cleanup * Fixed shadowing warning * Removed unused code * Fixed more warnings * Removed obsolete casts * Fixed warnings * Removed unused field * Removed library not needed by rasctl * Include cleanup * Updated platform check for better compatibility * Improved check for invalid option. This prevents rasctl to break on macos. * Updated option check * Fixed typo * Added TODO * Removed macro * Scope update * Replaced macro * Added TODO, update memory management * Fixed typo * Replaced NULL by nullptr * Use more structured bindings * Added TODO * Use calloc instead of mallco to not need memset * Fixed warnings * Fixed SonarQube initialization issues * Fixed warning * Cleaned up override/virtual/final * Fixed warnings * Constructor update * Fixed tests * Improved memory management * Added missing const * Added const * Fixed two bugs reported by SonarCloud * Fix SonarCloud hotspot * Fixed memory management * Memory management update * Addressing hotspot by using strncpy * Fixed SonarCloud issues * Fixed SonarQube issues * Added missing const * Added const * Added const * Suppress false positive * Added SonarQube suppressions for false positives * Added suppresoin * Fixed code smells * Reverted changes that is a SonarQube issue, but caused problems with -O3 * Removed TODO based on review
304 lines
7.4 KiB
C++
304 lines
7.4 KiB
C++
//---------------------------------------------------------------------------
|
|
//
|
|
// SCSI Target Emulator RaSCSI Reloaded
|
|
// for Raspberry Pi
|
|
//
|
|
// Copyright (C) 2021-2022 Uwe Seimet
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "scsihd.h"
|
|
#include "scsihd_nec.h"
|
|
#include "scsimo.h"
|
|
#include "scsicd.h"
|
|
#include "scsi_printer.h"
|
|
#include "scsi_host_bridge.h"
|
|
#include "scsi_daynaport.h"
|
|
#include "rascsi_exceptions.h"
|
|
#include "device_factory.h"
|
|
#include <ifaddrs.h>
|
|
#include "host_services.h"
|
|
|
|
using namespace std;
|
|
using namespace rascsi_interface;
|
|
|
|
multimap<int, Device *> DeviceFactory::devices;
|
|
|
|
DeviceFactory::DeviceFactory() : sector_sizes({}), geometries({}), default_params({}), extension_mapping({})
|
|
{
|
|
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
|
|
sector_sizes[SCRM] = { 512, 1024, 2048, 4096 };
|
|
sector_sizes[SCMO] = { 512, 1024, 2048, 4096 };
|
|
sector_sizes[SCCD] = { 512, 2048};
|
|
|
|
// 128 MB, 512 bytes per sector, 248826 sectors
|
|
geometries[SCMO][0x797f400] = make_pair(512, 248826);
|
|
// 230 MB, 512 bytes per block, 446325 sectors
|
|
geometries[SCMO][0xd9eea00] = make_pair(512, 446325);
|
|
// 540 MB, 512 bytes per sector, 1041500 sectors
|
|
geometries[SCMO][0x1fc8b800] = make_pair(512, 1041500);
|
|
// 640 MB, 20248 bytes per sector, 310352 sectors
|
|
geometries[SCMO][0x25e28000] = make_pair(2048, 310352);
|
|
|
|
string network_interfaces;
|
|
for (const auto& network_interface : GetNetworkInterfaces()) {
|
|
if (!network_interfaces.empty()) {
|
|
network_interfaces += ",";
|
|
}
|
|
network_interfaces += network_interface;
|
|
}
|
|
|
|
default_params[SCBR]["interface"] = network_interfaces;
|
|
default_params[SCBR]["inet"] = "10.10.20.1/24";
|
|
default_params[SCDP]["interface"] = network_interfaces;
|
|
default_params[SCDP]["inet"] = "10.10.20.1/24";
|
|
default_params[SCLP]["cmd"] = "lp -oraw %f";
|
|
default_params[SCLP]["timeout"] = "30";
|
|
|
|
extension_mapping["hds"] = SCHD;
|
|
extension_mapping["hda"] = SCHD;
|
|
extension_mapping["hdn"] = SCHD;
|
|
extension_mapping["hdi"] = SCHD;
|
|
extension_mapping["nhd"] = SCHD;
|
|
extension_mapping["hdr"] = SCRM;
|
|
extension_mapping["mos"] = SCMO;
|
|
extension_mapping["iso"] = SCCD;
|
|
}
|
|
|
|
DeviceFactory::~DeviceFactory()
|
|
{
|
|
DeleteAllDevices();
|
|
}
|
|
|
|
DeviceFactory& DeviceFactory::instance()
|
|
{
|
|
static DeviceFactory instance;
|
|
return instance;
|
|
}
|
|
|
|
void DeviceFactory::DeleteDevice(const Device *device) const
|
|
{
|
|
auto iterpair = devices.equal_range(device->GetId());
|
|
|
|
for (auto it = iterpair.first; it != iterpair.second; ++it) {
|
|
if (it->second->GetLun() == device->GetLun()) {
|
|
devices.erase(it);
|
|
delete device;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void DeviceFactory::DeleteAllDevices() const
|
|
{
|
|
for (const auto& [id, device] : devices) {
|
|
delete device;
|
|
}
|
|
|
|
devices.clear();
|
|
}
|
|
|
|
const Device * DeviceFactory::GetDeviceByIdAndLun(int i, int lun) const
|
|
{
|
|
for (const auto& [id, device] : devices) {
|
|
if (device->GetId() == i && device->GetLun() == lun) {
|
|
return device;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
list<Device *> DeviceFactory::GetAllDevices() const
|
|
{
|
|
list<Device *> result;
|
|
|
|
for (const auto& [id, device] : devices) {
|
|
result.push_back(device);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
string DeviceFactory::GetExtension(const string& filename) const
|
|
{
|
|
string ext;
|
|
size_t separator = filename.rfind('.');
|
|
if (separator != string::npos) {
|
|
ext = filename.substr(separator + 1);
|
|
}
|
|
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
|
|
|
|
return ext;
|
|
}
|
|
|
|
PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
|
|
{
|
|
const auto& it = extension_mapping.find(GetExtension(filename));
|
|
if (it != extension_mapping.end()) {
|
|
return it->second;
|
|
}
|
|
else if (filename == "bridge") {
|
|
return SCBR;
|
|
}
|
|
else if (filename == "daynaport") {
|
|
return SCDP;
|
|
}
|
|
else if (filename == "printer") {
|
|
return SCLP;
|
|
}
|
|
else if (filename == "services") {
|
|
return SCHS;
|
|
}
|
|
|
|
return UNDEFINED;
|
|
}
|
|
|
|
// ID -1 is used by rascsi to create a temporary device
|
|
Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, int id)
|
|
{
|
|
// If no type was specified try to derive the device type from the filename
|
|
if (type == UNDEFINED) {
|
|
type = GetTypeForFile(filename);
|
|
if (type == UNDEFINED) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
unique_ptr<Device> device;
|
|
switch (type) {
|
|
case SCHD: {
|
|
if (string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
|
|
device = make_unique<SCSIHD_NEC>();
|
|
} else {
|
|
device = make_unique<SCSIHD>(sector_sizes[SCHD], false);
|
|
|
|
// Some Apple tools require a particular drive identification
|
|
if (ext == "hda") {
|
|
device->SetVendor("QUANTUM");
|
|
device->SetProduct("FIREBALL");
|
|
}
|
|
}
|
|
device->SetProtectable(true);
|
|
device->SetStoppable(true);
|
|
break;
|
|
}
|
|
|
|
case SCRM:
|
|
device = make_unique<SCSIHD>(sector_sizes[SCRM], true);
|
|
device->SetProtectable(true);
|
|
device->SetStoppable(true);
|
|
device->SetRemovable(true);
|
|
device->SetLockable(true);
|
|
device->SetProduct("SCSI HD (REM.)");
|
|
break;
|
|
|
|
case SCMO:
|
|
device = make_unique<SCSIMO>(sector_sizes[SCMO], geometries[SCMO]);
|
|
device->SetProtectable(true);
|
|
device->SetStoppable(true);
|
|
device->SetRemovable(true);
|
|
device->SetLockable(true);
|
|
device->SetProduct("SCSI MO");
|
|
break;
|
|
|
|
case SCCD:
|
|
device = make_unique<SCSICD>(sector_sizes[SCCD]);
|
|
device->SetReadOnly(true);
|
|
device->SetStoppable(true);
|
|
device->SetRemovable(true);
|
|
device->SetLockable(true);
|
|
device->SetProduct("SCSI CD-ROM");
|
|
break;
|
|
|
|
case SCBR:
|
|
device = make_unique<SCSIBR>();
|
|
device->SetProduct("SCSI HOST BRIDGE");
|
|
device->SupportsParams(true);
|
|
device->SetDefaultParams(default_params[SCBR]);
|
|
break;
|
|
|
|
case SCDP:
|
|
device = make_unique<SCSIDaynaPort>();
|
|
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
|
|
device->SetVendor("Dayna");
|
|
device->SetProduct("SCSI/Link");
|
|
device->SetRevision("1.4a");
|
|
device->SupportsParams(true);
|
|
device->SetDefaultParams(default_params[SCDP]);
|
|
break;
|
|
|
|
case SCHS:
|
|
device = make_unique<HostServices>();
|
|
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
|
|
device->SetVendor("RaSCSI");
|
|
device->SetProduct("Host Services");
|
|
break;
|
|
|
|
case SCLP:
|
|
device = make_unique<SCSIPrinter>();
|
|
device->SetProduct("SCSI PRINTER");
|
|
device->SupportsParams(true);
|
|
device->SetDefaultParams(default_params[SCLP]);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
assert(device != nullptr);
|
|
|
|
Device *d = device.release();
|
|
|
|
if (d != nullptr) {
|
|
d->SetId(id);
|
|
|
|
devices.emplace(id, d);
|
|
}
|
|
|
|
return d;
|
|
}
|
|
|
|
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(const string& type) const
|
|
{
|
|
PbDeviceType t = UNDEFINED;
|
|
PbDeviceType_Parse(type, &t);
|
|
|
|
const auto it = sector_sizes.find(t);
|
|
assert (it != sector_sizes.end());
|
|
|
|
return it->second;
|
|
}
|
|
|
|
list<string> DeviceFactory::GetNetworkInterfaces() const
|
|
{
|
|
list<string> network_interfaces;
|
|
|
|
struct ifaddrs *addrs;
|
|
getifaddrs(&addrs);
|
|
struct ifaddrs *tmp = addrs;
|
|
|
|
while (tmp) {
|
|
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET &&
|
|
strcmp(tmp->ifa_name, "lo") && strcmp(tmp->ifa_name, "rascsi_bridge")) {
|
|
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
|
|
|
ifreq ifr = {};
|
|
strcpy(ifr.ifr_name, tmp->ifa_name);
|
|
// Only list interfaces that are up
|
|
if (!ioctl(fd, SIOCGIFFLAGS, &ifr) && ifr.ifr_flags & IFF_UP) {
|
|
network_interfaces.push_back(tmp->ifa_name);
|
|
}
|
|
|
|
close(fd);
|
|
}
|
|
|
|
tmp = tmp->ifa_next;
|
|
}
|
|
|
|
freeifaddrs(addrs);
|
|
|
|
return network_interfaces;
|
|
}
|