Merge branch 'develop' into feature_bpi4

This commit is contained in:
Tony Kuker 2022-10-25 19:45:10 -05:00
commit 5dc1cb45df
217 changed files with 971 additions and 6405 deletions

View File

@ -34,11 +34,11 @@ jobs:
- name: make standard
run: make all -j6 CONNECT_TYPE=STANDARD CROSS_COMPILE=arm-linux-gnueabihf-
working-directory: ./src/raspberrypi
working-directory: ./cpp
- name: make fullspec
run: make all -j6 CONNECT_TYPE=FULLSPEC CROSS_COMPILE=arm-linux-gnueabihf-
working-directory: ./src/raspberrypi
working-directory: ./cpp
# We need to tar the binary outputs to retain the executable
# file permission. Currently, actions/upload-artifact only
@ -46,11 +46,11 @@ jobs:
# This is workaround for https://github.com/actions/upload-artifact/issues/38
- name: tar binary outputs
run: tar -czvf rascsi.tar.gz ./bin
working-directory: ./src/raspberrypi
working-directory: ./cpp
- name: upload artifacts
uses: actions/upload-artifact@v2
with:
name: arm-binaries
path: ./src/raspberrypi/rascsi.tar.gz
path: ./cpp/rascsi.tar.gz

View File

@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-22.04
env:
MAKEFLAGS: -j2 # Number of available processors
SOURCES: src/raspberrypi
SOURCES: cpp
SONAR_SCANNER_VERSION: 4.7.0.2747
SONAR_SERVER_URL: "https://sonarcloud.io"
BUILD_WRAPPER_OUT_DIR: build_wrapper_output_directory # Directory where build-wrapper output will be placed

View File

@ -68,7 +68,7 @@ RSYSLOG_LOG = /var/log/rascsi.log
USR_LOCAL_BIN = /usr/local/bin
MAN_PAGE_DIR = /usr/local/man/man1
DOC_DIR = ../../doc
DOC_DIR = ../doc
COVERAGE_DIR = ./coverage
COVERAGE_FILE = rascsi.dat
OS_FILES = ./os_integration
@ -95,9 +95,7 @@ SRC_SHARED = \
protobuf_serializer.cpp
SRC_RASCSI_CORE = \
bus.cpp \
filepath.cpp \
fileio.cpp
bus.cpp
SRC_RASCSI_CORE += $(shell find ./rascsi -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp')
SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp')
@ -120,8 +118,7 @@ SRC_RASCTL = rasctl.cpp
SRC_RASDUMP = \
rasdump.cpp \
bus.cpp \
filepath.cpp \
fileio.cpp \
rasdump_fileio.cpp \
rascsi_version.cpp
SRC_RASDUMP += $(shell find ./hal -name '*.cpp')
@ -191,7 +188,7 @@ coverage: test
lcov: CXXFLAGS += --coverage
lcov: test
lcov -q -c -d . --include '*/raspberrypi/*' -o $(COVERAGE_FILE) --exclude '*/test/*' --exclude '*/interfaces/*' --exclude '*/rascsi_interface.pb*'
lcov -q -c -d . --include '*/cpp/*' -o $(COVERAGE_FILE) --exclude '*/test/*' --exclude '*/interfaces/*' --exclude '*/rascsi_interface.pb*'
genhtml -q -o $(COVERAGE_DIR) --legend $(COVERAGE_FILE)
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt

View File

@ -27,6 +27,8 @@
using namespace scsi_defs;
const int ScsiController::LUN_MAX = 32;
ScsiController::ScsiController(shared_ptr<BUS> bus, int target_id) : AbstractController(bus, target_id, LUN_MAX)
{
// The initial buffer size will default to either the default buffer size OR

View File

@ -52,7 +52,7 @@ class ScsiController : public AbstractController
public:
// Maximum number of logical units
static const int LUN_MAX = 32;
static const int LUN_MAX;
ScsiController(shared_ptr<BUS>, int);
~ScsiController() override = default;

View File

@ -30,7 +30,7 @@ void CDTrack::Init(int track, uint32_t first, uint32_t last)
last_lba = last;
}
void CDTrack::SetPath(bool cdda, const Filepath& path)
void CDTrack::SetPath(bool cdda, string_view path)
{
assert(valid);
@ -41,12 +41,11 @@ void CDTrack::SetPath(bool cdda, const Filepath& path)
imgpath = path;
}
void CDTrack::GetPath(Filepath& path) const
string CDTrack::GetPath() const
{
assert(valid);
// Return the path (by reference)
path = imgpath;
return imgpath;
}
//---------------------------------------------------------------------------

View File

@ -14,7 +14,9 @@
#pragma once
#include "filepath.h"
#include <string>
using namespace std;
class CDTrack final
{
@ -26,8 +28,8 @@ public:
void Init(int track, uint32_t first, uint32_t last);
// Properties
void SetPath(bool cdda, const Filepath& path); // Set the path
void GetPath(Filepath& path) const; // Get the path
void SetPath(bool, string_view); // Set the path
string GetPath() const; // Get the path
uint32_t GetFirst() const; // Get the start LBA
uint32_t GetLast() const; // Get the last LBA
uint32_t GetBlocks() const; // Get the number of blocks
@ -41,5 +43,6 @@ private:
uint32_t first_lba = 0; // First LBA
uint32_t last_lba = 0; // Last LBA
bool audio = false; // Audio track flag
Filepath imgpath; // Image file path
string imgpath; // Image file path
};

View File

@ -19,7 +19,6 @@
#include "os.h"
#include "log.h"
#include "filepath.h"
#include "cfilesystem.h"
#include <dirent.h>
#include <iconv.h>

View File

@ -13,6 +13,10 @@
#pragma once
using TCHAR = char;
static const int FILEPATH_MAX = 260;
//---------------------------------------------------------------------------
//
// Status code definitions

View File

@ -373,20 +373,20 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
#endif
}
void CTapDriver::OpenDump(const Filepath& path) {
void CTapDriver::OpenDump(const string& path) {
if (m_pcap == nullptr) {
m_pcap = pcap_open_dead(DLT_EN10MB, 65535);
}
if (m_pcap_dumper != nullptr) {
pcap_dump_close(m_pcap_dumper);
}
m_pcap_dumper = pcap_dump_open(m_pcap, path.GetPath());
m_pcap_dumper = pcap_dump_open(m_pcap, path.c_str());
if (m_pcap_dumper == nullptr) {
LOGERROR("Can't open pcap file: %s", pcap_geterr(m_pcap))
throw io_exception("Can't open pcap file");
}
LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.GetPath())
LOGTRACE("%s Opened %s for dumping", __PRETTY_FUNCTION__, path.c_str())
}
bool CTapDriver::Enable() const

View File

@ -13,7 +13,6 @@
#include <pcap/pcap.h>
#include <net/ethernet.h>
#include "filepath.h"
#include <unordered_map>
#include <list>
#include <string>
@ -33,7 +32,7 @@ public:
CTapDriver& operator=(const CTapDriver&) = default;
bool Init(const unordered_map<string, string>&);
void OpenDump(const Filepath& path); // Capture packets
void OpenDump(const string& path); // Capture packets
void GetMacAddr(BYTE *mac) const;
int Receive(BYTE *buf);
int Send(const BYTE *buf, int len);

View File

@ -14,7 +14,6 @@
//
//---------------------------------------------------------------------------
#include "fileio.h"
#include "rascsi_exceptions.h"
#include "dispatcher.h"
#include "scsi_command_util.h"
@ -79,17 +78,13 @@ bool Disk::Dispatch(scsi_command cmd)
void Disk::SetUpCache(off_t image_offset, bool raw)
{
Filepath path;
path.SetPath(GetFilename().c_str());
cache = make_unique<DiskCache>(path, size_shift_count, (uint32_t)GetBlockCount(), image_offset);
cache = make_unique<DiskCache>(GetFilename(), size_shift_count, (uint32_t)GetBlockCount(), image_offset);
cache->SetRawMode(raw);
}
void Disk::ResizeCache(const string& filename, bool raw)
void Disk::ResizeCache(const string& path, bool raw)
{
Filepath path;
path.SetPath(filename.c_str());
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)GetBlockCount()));
cache.reset(new DiskCache(path, size_shift_count, (uint32_t)GetBlockCount()));
cache->SetRawMode(raw);
}
@ -501,8 +496,7 @@ int Disk::Read(const vector<int>&, vector<BYTE>& buf, uint64_t block)
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
}
// Success
return 1 << size_shift_count;
return GetSectorSizeInBytes();
}
int Disk::WriteCheck(uint64_t block)
@ -519,18 +513,14 @@ int Disk::WriteCheck(uint64_t block)
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
}
// Success
return 1 << size_shift_count;
return GetSectorSizeInBytes();
}
void Disk::Write(const vector<int>&, const vector<BYTE>& buf, uint64_t block)
{
LOGTRACE("%s", __PRETTY_FUNCTION__)
// Error if not ready
if (!IsReady()) {
throw scsi_exception(sense_key::NOT_READY);
}
CheckReady();
// Error if the total number of blocks is exceeded
if (block >= GetBlockCount()) {

View File

@ -14,18 +14,17 @@
//
//---------------------------------------------------------------------------
#include "os.h"
#include "disk_track.h"
#include "disk_cache.h"
#include <cstdlib>
#include <cassert>
DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff)
: sec_size(size), sec_blocks(blocks), imgoffset(imgoff)
DiskCache::DiskCache(const string& path, int size, uint32_t blocks, off_t imgoff)
: sec_path(path), sec_size(size), sec_blocks(blocks), imgoffset(imgoff)
{
assert(blocks > 0);
assert(imgoff >= 0);
sec_path = path;
}
bool DiskCache::Save() const

View File

@ -15,9 +15,9 @@
#pragma once
#include "filepath.h"
#include <array>
#include <memory>
#include <string>
using namespace std;
@ -34,7 +34,7 @@ public:
uint32_t serial; // Serial
};
DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0);
DiskCache(const string&, int, uint32_t, off_t = 0);
~DiskCache() = default;
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting
@ -55,7 +55,7 @@ private:
// Internal data
array<cache_t, CACHE_MAX> cache = {}; // Cache management
uint32_t serial = 0; // Last serial number
Filepath sec_path; // Path
string sec_path; // Path
int sec_size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
int sec_blocks; // Blocks per sector
bool cd_raw = false; // CD-ROM RAW mode

View File

@ -15,8 +15,8 @@
//---------------------------------------------------------------------------
#include "log.h"
#include "fileio.h"
#include "disk_track.h"
#include <fstream>
DiskTrack::~DiskTrack()
{
@ -46,7 +46,7 @@ void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff)
dt.imgoffset = imgoff;
}
bool DiskTrack::Load(const Filepath& path)
bool DiskTrack::Load(const string& path)
{
// Not needed if already loaded
if (dt.init) {
@ -97,24 +97,21 @@ bool DiskTrack::Load(const Filepath& path)
dt.changemap.resize(dt.sectors);
fill(dt.changemap.begin(), dt.changemap.end(), false);
// Read from File
Fileio fio;
if (!fio.OpenDIO(path, Fileio::OpenMode::ReadOnly)) {
ifstream in(path, ios::binary);
if (in.fail()) {
return false;
}
if (dt.raw) {
// Split Reading
for (int i = 0; i < dt.sectors; i++) {
// Seek
if (!fio.Seek(offset)) {
fio.Close();
in.seekg(offset);
if (in.fail()) {
return false;
}
// Read
if (!fio.Read(&dt.buffer[i << dt.size], 1 << dt.size)) {
fio.Close();
in.read((char *)&dt.buffer[i << dt.size], 1 << dt.size);
if (in.fail()) {
return false;
}
@ -123,16 +120,15 @@ bool DiskTrack::Load(const Filepath& path)
}
} else {
// Continuous reading
if (!fio.Seek(offset)) {
fio.Close();
in.seekg(offset);
if (in.fail()) {
return false;
}
if (!fio.Read(dt.buffer, length)) {
fio.Close();
in.read((char *)dt.buffer, length);
if (in.fail()) {
return false;
}
}
fio.Close();
// Set a flag and end normally
dt.init = true;
@ -140,7 +136,7 @@ bool DiskTrack::Load(const Filepath& path)
return true;
}
bool DiskTrack::Save(const Filepath& path)
bool DiskTrack::Save(const string& path)
{
// Not needed if not initialized
if (!dt.init) {
@ -169,9 +165,8 @@ bool DiskTrack::Save(const Filepath& path)
// Calculate length per sector
const int length = 1 << dt.size;
// Open file
Fileio fio;
if (!fio.Open(path, Fileio::OpenMode::ReadWrite)) {
ofstream out(path, ios::in | ios::out | ios::binary);
if (out.fail()) {
return false;
}
@ -183,9 +178,8 @@ bool DiskTrack::Save(const Filepath& path)
// Initialize write size
total = 0;
// Seek
if (!fio.Seek(offset + ((off_t)i << dt.size))) {
fio.Close();
out.seekp(offset + ((off_t)i << dt.size));
if (out.fail()) {
return false;
}
@ -201,9 +195,8 @@ bool DiskTrack::Save(const Filepath& path)
total += length;
}
// Write
if (!fio.Write(&dt.buffer[i << dt.size], total)) {
fio.Close();
out.write((const char *)&dt.buffer[i << dt.size], total);
if (out.fail()) {
return false;
}
@ -215,8 +208,6 @@ bool DiskTrack::Save(const Filepath& path)
}
}
fio.Close();
// Drop the change flag and exit
fill(dt.changemap.begin(), dt.changemap.end(), false);
dt.changed = false;

View File

@ -15,9 +15,10 @@
#pragma once
#include "filepath.h"
#include "os.h"
#include <cstdlib>
#include <vector>
#include <string>
using namespace std;
@ -31,7 +32,7 @@ class DiskTrack
BYTE *buffer; // Data buffer
bool init; // Is it initilized?
bool changed; // Changed flag
std::vector<bool> changemap; // Changed map
vector<bool> changemap; // Changed map
bool raw; // RAW mode flag
off_t imgoffset; // Offset to actual data
} dt = {};
@ -48,12 +49,12 @@ private:
friend class DiskCache;
void Init(int track, int size, int sectors, bool raw = false, off_t imgoff = 0);
bool Load(const Filepath& path);
bool Save(const Filepath& path);
bool Load(const string& path);
bool Save(const string& path);
// Read / Write
bool ReadSector(vector<BYTE>&, int) const; // Sector Read
bool WriteSector(const vector<BYTE>& buf, int); // Sector Write
bool WriteSector(const vector<BYTE>& buf, int); // Sector Write
int GetTrack() const { return dt.track; } // Get track
};

View File

@ -91,9 +91,7 @@ bool SCSIDaynaPort::Init(const unordered_map<string, string>& params)
void SCSIDaynaPort::Open()
{
Filepath path;
path.SetPath(GetFilename().c_str());
m_tap.OpenDump(path);
m_tap.OpenDump(GetFilename().c_str());
}
vector<byte> SCSIDaynaPort::InquiryInternal() const

View File

@ -39,7 +39,7 @@ public:
bool Dispatch(scsi_command) override;
// TODO Remove as soon as SCSIBR is not a subclass of Disk anymore
void Open() override { super::ValidateFile(GetFilename()); }
void Open() override { super::ValidateFile(); }
// Commands
vector<byte> InquiryInternal() const override;

View File

@ -17,6 +17,7 @@
// 2. The client triggers printing with SYNCHRONIZE BUFFER. Each SYNCHRONIZE BUFFER results in
// the print command for this printer (see below) to be called for the data not yet printed.
//
// It is recommended to reserve the printer device before printing and to release it afterwards.
// The command to be used for printing can be set with the "cmd" property when attaching the device.
// By default the data to be printed are sent to the printer unmodified, using "lp -oraw %f". This
// requires that the client uses a printer driver compatible with the respective printer, or that the
@ -33,8 +34,10 @@
#include "scsi_command_util.h"
#include "dispatcher.h"
#include "scsi_printer.h"
#include <filesystem>
using namespace std;
using namespace filesystem;
using namespace scsi_defs;
using namespace scsi_command_util;
@ -43,13 +46,18 @@ SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice(SCLP, lun)
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
dispatcher.Add(scsi_command::eCmdPrint, "Print", &SCSIPrinter::Print);
dispatcher.Add(scsi_command::eCmdSynchronizeBuffer, "SynchronizeBuffer", &SCSIPrinter::SynchronizeBuffer);
dispatcher.Add(scsi_command::eCmdStopPrint, "StopPrint", &SCSIPrinter::StopPrint);
// STOP PRINT is identical with TEST UNIT READY, it just returns the status
dispatcher.Add(scsi_command::eCmdStopPrint, "StopPrint", &SCSIPrinter::TestUnitReady);
// Required also in this class in order to fulfill the ScsiPrinterCommands interface contract
dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);
dispatcher.Add(scsi_command::eCmdRelease6, "ReleaseUnit", &SCSIPrinter::ReleaseUnit);
dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &SCSIPrinter::SendDiagnostic);
error_code error;
file_template = temp_directory_path(error); //NOSONAR Publicly writable directory is fine here
file_template += PRINTER_FILE_PATTERN;
SupportsParams(true);
SetReady(true);
}
@ -109,70 +117,75 @@ void SCSIPrinter::Print()
void SCSIPrinter::SynchronizeBuffer()
{
if (fd == -1) {
LOGWARN("Missing printer output file")
if (!out.is_open()) {
LOGWARN("Nothing to print")
throw scsi_exception(sense_key::ABORTED_COMMAND);
}
struct stat st;
fstat(fd, &st);
close(fd);
fd = -1;
string cmd = GetParam("cmd");
const size_t file_position = cmd.find("%f");
assert(file_position != string::npos);
cmd.replace(file_position, 2, filename);
LOGTRACE("%s", string("Printing file with size of " + to_string(st.st_size) +" byte(s)").c_str())
error_code error;
LOGTRACE("Printing file '%s' with %s byte(s)", filename.c_str(), to_string(file_size(path(filename), error)).c_str())
LOGDEBUG("Executing '%s'", cmd.c_str())
if (system(cmd.c_str())) {
LOGERROR("Printing failed, the printing system might not be configured")
LOGERROR("Printing file '%s' failed, the printing system might not be configured", filename.c_str())
unlink(filename);
Cleanup();
throw scsi_exception(sense_key::ABORTED_COMMAND);
}
unlink(filename);
Cleanup();
EnterStatusPhase();
}
void SCSIPrinter::StopPrint()
{
// Command implementations are identical
TestUnitReady();
}
bool SCSIPrinter::WriteByteSequence(vector<BYTE>& buf, uint32_t length)
{
if (fd == -1) {
strcpy(filename, TMP_FILE_PATTERN); //NOSONAR Using strcpy is safe here
fd = mkstemp(filename);
if (!out.is_open()) {
vector<char> f(file_template.begin(), file_template.end());
f.push_back(0);
// There is no C++ API that generates a file with a unique name
const int fd = mkstemp(f.data());
if (fd == -1) {
LOGERROR("Can't create printer output file '%s': %s", filename, strerror(errno))
LOGERROR("Can't create printer output file for pattern '%s': %s", filename.c_str(), strerror(errno))
return false;
}
close(fd);
LOGTRACE("Created printer output file '%s'", filename)
filename = f.data();
out.open(filename, ios::binary);
if (out.fail()) {
throw scsi_exception(sense_key::ABORTED_COMMAND);
}
LOGTRACE("Created printer output file '%s'", filename.c_str())
}
LOGTRACE("Appending %d byte(s) to printer output file '%s'", length, filename)
LOGTRACE("Appending %d byte(s) to printer output file '%s'", length, filename.c_str())
return (uint32_t)write(fd, buf.data(), length) == length;
out.write((const char*)buf.data(), length);
return !out.fail();
}
void SCSIPrinter::Cleanup()
{
if (fd != -1) {
close(fd);
fd = -1;
if (out.is_open()) {
out.close();
unlink(filename);
error_code error;
remove(path(filename), error);
filename = "";
}
}

View File

@ -12,16 +12,18 @@
#include "interfaces/scsi_printer_commands.h"
#include "primary_device.h"
#include <fstream>
#include <string>
#include <unordered_map>
using namespace std;
class SCSIPrinter : public PrimaryDevice, ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed
{
static constexpr const char *TMP_FILE_PATTERN = "/tmp/rascsi_sclp-XXXXXX"; //NOSONAR Using /tmp is safe
static const int TMP_FILENAME_LENGTH = string_view(TMP_FILE_PATTERN).size();
static const int NOT_RESERVED = -2;
static constexpr const char *PRINTER_FILE_PATTERN = "/rascsi_sclp-XXXXXX";
public:
explicit SCSIPrinter(int);
@ -39,7 +41,6 @@ public:
void SendDiagnostic() override { PrimaryDevice::SendDiagnostic(); }
void Print() override;
void SynchronizeBuffer();
void StopPrint();
bool WriteByteSequence(vector<BYTE>&, uint32_t) override;
@ -49,6 +50,9 @@ private:
Dispatcher<SCSIPrinter> dispatcher;
char filename[TMP_FILENAME_LENGTH + 1]; //NOSONAR mkstemp() requires a modifiable string
int fd = -1;
string file_template;
string filename;
ofstream out;
};

View File

@ -14,12 +14,12 @@
//
//---------------------------------------------------------------------------
#include "fileio.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "dispatcher.h"
#include "scsicd.h"
#include <array>
#include <fstream>
using namespace scsi_defs;
using namespace scsi_command_util;
@ -50,48 +50,29 @@ void SCSICD::Open()
rawfile = false;
ClearTrack();
// Open as read-only
Filepath path;
path.SetPath(GetFilename().c_str());
Fileio fio;
if (!fio.Open(path, Fileio::OpenMode::ReadOnly)) {
throw file_not_found_exception("Can't open CD-ROM file '" + GetFilename() + "'");
}
// Default sector size is 2048 bytes
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 2048);
// Close and transfer for physical CD access
if (path.GetPath()[0] == '\\') {
// Close
fio.Close();
// Open physical CD
if (GetFilename()[0] == '\\') {
OpenPhysical();
} else {
if (GetFileSize() < 4) {
fio.Close();
throw io_exception("CD-ROM file size must be at least 4 bytes");
// Judge whether it is a CUE sheet or an ISO file
array<char, 4> cue;
ifstream in(GetFilename(), ios::binary);
in.read(cue.data(), cue.size());
if (!in.good()) {
throw io_exception("Can't read header of CD-ROM file '" + GetFilename() + "'");
}
// Judge whether it is a CUE sheet or an ISO file
array<TCHAR, 5> file;
fio.Read((BYTE *)file.data(), 4);
file[4] = '\0';
fio.Close();
// If it starts with FILE, consider it as a CUE sheet
if (!strcasecmp(file.data(), "FILE")) {
throw io_exception("Opening CUE CD-ROM files is not supported");
// If it starts with FILE consider it a CUE sheet
if (!strncasecmp(cue.data(), "FILE", cue.size())) {
throw io_exception("CUE CD-ROM files are not supported");
} else {
OpenIso();
}
}
// Successful opening
assert(GetBlockCount() > 0);
super::ValidateFile(GetFilename());
super::ValidateFile();
SetUpCache(0, rawfile);
@ -106,80 +87,56 @@ void SCSICD::Open()
void SCSICD::OpenIso()
{
// Open as read-only
Fileio fio;
if (!fio.Open(GetFilename().c_str(), Fileio::OpenMode::ReadOnly)) {
throw io_exception("Can't open ISO CD-ROM file");
}
// Get file size
const off_t size = GetFileSize();
if (size < 0x800) {
fio.Close();
if (size < 2048) {
throw io_exception("ISO CD-ROM file size must be at least 2048 bytes");
}
// Read the first 12 bytes and close
array<BYTE, 12> header;
if (!fio.Read(header.data(), header.size())) {
fio.Close();
// Validate header
array<char, 16> header;
ifstream in(GetFilename(), ios::binary);
in.read(header.data(), header.size());
if (!in.good()) {
throw io_exception("Can't read header of ISO CD-ROM file");
}
// Check if it is RAW format
array<BYTE, 12> sync;
sync.fill(0xff);
sync[0] = 0x00;
sync[11] = 0x00;
// Check if it is in RAW format
array<char, 12> sync = {};
// 00,FFx10,00 is presumed to be RAW format
fill_n(sync.begin() + 1, 10, 0xff);
rawfile = false;
if (memcmp(header.data(), sync.data(), sync.size()) == 0) {
// 00,FFx10,00, so it is presumed to be RAW format
if (!fio.Read(header.data(), 4)) {
fio.Close();
throw io_exception("Can't read header of raw ISO CD-ROM file");
}
if (memcmp(header.data(), sync.data(), sync.size()) == 0) {
// Supports MODE1/2048 or MODE1/2352 only
if (header[3] != 0x01) {
if (header[15] != 0x01) {
// Different mode
fio.Close();
throw io_exception("Illegal raw ISO CD-ROM file header");
}
// Set to RAW file
rawfile = true;
}
fio.Close();
if (rawfile) {
// Size must be a multiple of 2536
if (size % 2536) {
throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is "
+ to_string(size) + " bytes");
}
// Set the number of blocks
SetBlockCount((uint32_t)(size / 0x930));
SetBlockCount((uint32_t)(size / 2352));
} else {
// Set the number of blocks
SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
}
CreateDataTrack();
}
// TODO This code is only executed if the filename starts with a `\`, but fails to open files starting with `\`.
void SCSICD::OpenPhysical()
{
// Open as read-only
Fileio fio;
if (!fio.Open(GetFilename().c_str(), Fileio::OpenMode::ReadOnly)) {
throw file_not_found_exception("Can't open CD-ROM file '" + GetFilename() + "'");
}
fio.Close();
// Get size
off_t size = GetFileSize();
if (size < 0x800) {
if (size < 2048) {
throw io_exception("CD-ROM file size must be at least 2048 bytes");
}
@ -198,9 +155,7 @@ void SCSICD::CreateDataTrack()
assert(!tracks.size());
auto track = make_unique<CDTrack>();
track->Init(1, 0, (int)GetBlockCount() - 1);
Filepath path;
path.SetPath(GetFilename().c_str());
track->SetPath(false, path);
track->SetPath(false, GetFilename());
tracks.push_back(move(track));
dataindex = 0;
}
@ -287,12 +242,8 @@ int SCSICD::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t block)
SetBlockCount(tracks[index]->GetBlocks());
assert(GetBlockCount() > 0);
// Recreate the disk cache
Filepath path;
tracks[index]->GetPath(path);
// Re-assign disk cache (no need to save)
ResizeCache(path.GetPath(), rawfile);
ResizeCache(tracks[index]->GetPath(), rawfile);
// Reset data index
dataindex = index;

View File

@ -5,17 +5,15 @@
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2022 Uwe Seimet
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI hard disk ]
//
//---------------------------------------------------------------------------
#include "scsihd.h"
#include "fileio.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
@ -56,23 +54,15 @@ string SCSIHD::GetProductData() const
return DEFAULT_PRODUCT + " " + to_string(capacity) + " " + unit;
}
void SCSIHD::FinalizeSetup(off_t size, off_t image_offset)
void SCSIHD::FinalizeSetup(off_t image_offset)
{
// Effective size must be a multiple of the sector size
size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes();
// 2 TiB is the current maximum
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
throw io_exception("Drive capacity cannot exceed 2 TiB");
}
super::ValidateFile();
// For non-removable media drives set the default product name based on the drive capacity
if (!IsRemovable()) {
SetProduct(GetProductData(), false);
}
super::ValidateFile(GetFilename());
SetUpCache(image_offset);
}
@ -86,7 +76,7 @@ void SCSIHD::Open()
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
FinalizeSetup(size);
FinalizeSetup(0);
}
vector<byte> SCSIHD::InquiryInternal() const

View File

@ -5,19 +5,21 @@
//
// Copyright (C) 2001-2006 (ytanaka@ipc-tokai.or.jp)
// Copyright (C) 2014-2020 GIMONS
// Copyright (C) 2022 Uwe Seimet
// Copyright (C) akuker
//
// Licensed under the BSD 3-Clause License.
// See LICENSE file in the project root folder.
//
// [ SCSI hard disk ]
//
//---------------------------------------------------------------------------
#pragma once
#include "scsi.h"
#include "disk.h"
#include <string>
#include <vector>
#include <map>
class SCSIHD : public Disk
{
@ -28,7 +30,7 @@ public:
SCSIHD(int, const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2);
~SCSIHD() override = default;
void FinalizeSetup(off_t, off_t = 0);
void FinalizeSetup(off_t);
void Open() override;

View File

@ -14,11 +14,13 @@
//
//---------------------------------------------------------------------------
#include "scsihd_nec.h"
#include "fileio.h"
#include "rascsi_exceptions.h"
#include "rasutil.h"
#include "scsi_command_util.h"
#include "scsihd_nec.h"
#include <fstream>
using namespace ras_util;
using namespace scsi_command_util;
const unordered_set<uint32_t> SCSIHD_NEC::sector_sizes = { 512 };
@ -27,64 +29,38 @@ void SCSIHD_NEC::Open()
{
assert(!IsReady());
// Open as read-only
Filepath path;
path.SetPath(GetFilename().c_str());
Fileio fio;
if (!fio.Open(path, Fileio::OpenMode::ReadOnly)) {
throw file_not_found_exception("Can't open hard disk file '" + GetFilename() + '"');
}
off_t size = GetFileSize();
// NEC root sector
array<BYTE, 512> root_sector;
if (size < (off_t)root_sector.size() || !fio.Read(root_sector.data(), root_sector.size())) {
fio.Close();
array<char, 512> root_sector;
ifstream in(GetFilename(), ios::binary);
in.read(root_sector.data(), root_sector.size());
if (!in.good() || size < (off_t)root_sector.size()) {
throw io_exception("Can't read NEC hard disk file root sector");
}
fio.Close();
// Effective size must be a multiple of 512
size = (size / 512) * 512;
// Determine parameters by extension
const auto [image_size, sector_size] = SetParameters(path.GetFileExt(), root_sector, (int)size);
const auto [image_size, sector_size] = SetParameters(root_sector, (int)size);
if (sector_size == 0) {
throw io_exception("Invalid NEC drive sector size");
}
// Image size consistency check
if (image_offset + image_size > size || image_size % sector_size != 0) {
throw io_exception("Image size consistency check failed");
}
// Calculate sector size
for (size = 16; size > 0; --size) {
if ((1 << size) == sector_size)
break;
}
if (size <= 0 || size > 16) {
throw io_exception("Invalid NEC disk size");
}
SetSectorSizeShiftCount((uint32_t)size);
SetBlockCount(image_size >> GetSectorSizeShiftCount());
FinalizeSetup(size, image_offset);
FinalizeSetup(image_offset);
}
pair<int, int> SCSIHD_NEC::SetParameters(const string& extension, const array<BYTE, 512>& root_sector, int size)
pair<int, int> SCSIHD_NEC::SetParameters(const array<char, 512>& data, int size)
{
string ext = extension;
transform(ext.begin(), ext.end(), ext.begin(), ::tolower);
array<BYTE, 512> root_sector = {};
memcpy(root_sector.data(), data.data(), root_sector.size());
int image_size;
int sector_size;
// PC-9801-55 NEC compatible?
if (ext == ".hdn") {
if (const string ext = GetExtensionLowerCase(GetFilename()); ext == "hdn") {
// Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
image_offset = 0;
image_size = size;
@ -96,7 +72,7 @@ pair<int, int> SCSIHD_NEC::SetParameters(const string& extension, const array<BY
cylinders /= 25;
}
// Anex86 HD image?
else if (ext == ".hdi") {
else if (ext == "hdi") {
image_offset = GetInt32LittleEndian(&root_sector[8]);
image_size = GetInt32LittleEndian(&root_sector[12]);
sector_size = GetInt32LittleEndian(&root_sector[16]);
@ -105,7 +81,7 @@ pair<int, int> SCSIHD_NEC::SetParameters(const string& extension, const array<BY
cylinders = GetInt32LittleEndian(&root_sector[28]);
}
// T98Next HD image?
else if (ext == ".nhd") {
else if (ext == "nhd") {
if (!memcmp(root_sector.data(), "T98HDDIMAGE.R0\0", 15)) {
image_offset = GetInt32LittleEndian(&root_sector[0x110]);
cylinders = GetInt32LittleEndian(&root_sector[0x114]);
@ -122,6 +98,19 @@ pair<int, int> SCSIHD_NEC::SetParameters(const string& extension, const array<BY
throw io_exception("Invalid NEC image file extension");
}
if (sector_size == 0) {
throw io_exception("Invalid NEC sector size 0");
}
// Image size consistency check
if (image_offset + image_size > size) {
throw io_exception("NEC image offset/size consistency check failed");
}
if (CalculateShiftCount(sector_size) == 0) {
throw io_exception("Invalid NEC sector size of " + to_string(sector_size) + " byte(s)");
}
return make_pair(image_size, sector_size);
}

View File

@ -47,14 +47,14 @@ protected:
private:
pair<int, int> SetParameters(const string&, const array<BYTE, 512>&, int);
pair<int, int> SetParameters(const array<char, 512>&, int);
static int GetInt16LittleEndian(const BYTE *);
static int GetInt32LittleEndian(const BYTE *);
static const unordered_set<uint32_t> sector_sizes;
// Image file offset (NEC only)
// Image file offset
off_t image_offset = 0;
// Geometry data

View File

@ -12,7 +12,6 @@
//
//---------------------------------------------------------------------------
#include "fileio.h"
#include "rascsi_exceptions.h"
#include "scsi_command_util.h"
#include "scsimo.h"
@ -43,21 +42,14 @@ void SCSIMO::Open()
{
assert(!IsReady());
off_t size = GetFileSize();
// 2 TiB is the current maximum
if (size > 2LL * 1024 * 1024 * 1024 * 1024) {
throw io_exception("Drive capacity cannot exceed 2 TiB");
}
// For some capacities there are hard-coded, well-defined sector sizes and block counts
if (!SetGeometryForCapacity(size)) {
if (const off_t size = GetFileSize(); !SetGeometryForCapacity(size)) {
// Sector size (default 512 bytes) and number of blocks
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
SetBlockCount(size >> GetSectorSizeShiftCount());
}
super::ValidateFile(GetFilename());
super::ValidateFile();
SetUpCache(0);

View File

@ -15,6 +15,8 @@
#pragma once
#include "disk.h"
#include <vector>
#include <map>
using Geometry = pair<uint32_t, uint32_t>;

View File

@ -23,14 +23,22 @@ StorageDevice::StorageDevice(PbDeviceType type, int lun) : ModePageDevice(type,
SetStoppable(true);
}
void StorageDevice::ValidateFile(const string& file)
void StorageDevice::ValidateFile()
{
if (blocks == 0) {
throw io_exception(string(GetTypeString()) + " device has 0 blocks");
}
if (!exists(path(filename))) {
throw file_not_found_exception("Image file '" + filename + "' for " + GetTypeString() + " device does not exist");
}
if (GetFileSize() > 2LL * 1024 * 1024 * 1024 * 1024) {
throw io_exception("Drive capacity cannot exceed 2 TiB");
}
// TODO Check for duplicate handling of these properties (-> rascsi_executor.cpp)
if (access(file.c_str(), W_OK)) {
if (access(filename.c_str(), W_OK)) {
// Permanently write-protected
SetReadOnly(true);
SetProtectable(false);
@ -65,16 +73,13 @@ void StorageDevice::UnreserveFile()
filename = "";
}
bool StorageDevice::GetIdsForReservedFile(const string& file, int& id, int& lun)
pair<int, int> StorageDevice::GetIdsForReservedFile(const string& file)
{
if (const auto& it = reserved_files.find(file); it != reserved_files.end()) {
id = it->second.first;
lun = it->second.second;
return true;
return make_pair(it->second.first, it->second.second);
}
return false;
return make_pair(-1, -1);
}
void StorageDevice::UnreserveAll()
@ -94,7 +99,7 @@ bool StorageDevice::IsReadOnlyFile() const
off_t StorageDevice::GetFileSize() const
{
// filesystem::file_size cannot be used here because gcc < 10.3.0 cannot handled more than 2 GiB
// filesystem::file_size cannot be used here because gcc < 10.3.0 cannot handle more than 2 GiB
if (struct stat st; !stat(filename.c_str(), &st)) {
return st.st_size;
}

View File

@ -28,7 +28,6 @@ public:
virtual void Open() = 0;
void ValidateFile(const string&);
string GetFilename() const { return filename; }
void SetFilename(string_view f) { filename = f; }
@ -45,10 +44,12 @@ public:
static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; }
static void SetReservedFiles(const unordered_map<string, id_set>& r) { reserved_files = r; }
static bool GetIdsForReservedFile(const string&, int&, int&);
static pair<int, int> GetIdsForReservedFile(const string&);
protected:
void ValidateFile();
bool IsMediumChanged() const { return medium_changed; }
void SetMediumChanged(bool b) { medium_changed = b; }

Some files were not shown because too many files have changed in this diff Show More