Skip to content

Commit

Permalink
Add options to only run INQUIRY and to scan the bus to scsidump (#1092)…
Browse files Browse the repository at this point in the history
… (#1261)

* Add options to only run INQUIRY and to scan the bus to scsidump
  • Loading branch information
uweseimet committed Oct 30, 2023
1 parent 7e8cd73 commit 637b0bd
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 175 deletions.
299 changes: 193 additions & 106 deletions cpp/scsidump/scsidump_core.cpp

Large diffs are not rendered by default.

65 changes: 52 additions & 13 deletions cpp/scsidump/scsidump_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,45 +10,52 @@
#pragma once

#include "hal/bus.h"
#include "shared/scsi.h"
#include <memory>
#include <string>
#include <span>
#include <vector>
#include <unordered_map>
#include <stdexcept>

using namespace std;

class phase_exception : public runtime_error
{
using runtime_error::runtime_error;
};

class ScsiDump
{
static const int MINIMUM_BUFFER_SIZE = 1024 * 64;
static const int DEFAULT_BUFFER_SIZE = 1024 * 1024;

public:

ScsiDump() = default;
~ScsiDump() = default;

int run(const span<char *>);

struct inquiry_info_struct {
struct inquiry_info {
string vendor;
string product;
string revision;
uint32_t sector_size;
uint64_t capacity;
};
using inquiry_info_t = struct inquiry_info_struct;

protected:
// Protected for testability
static void GeneratePropertiesFile(const string& filename, const inquiry_info_t& inq_info);
void GeneratePropertiesFile(const string&) const;
};
using inquiry_info_t = struct inquiry_info;

private:

bool Banner(span<char *>) const;
bool Init() const;
void ParseArguments(span<char *>);
void DisplayBoardId() const;
void ScanBus();
bool DisplayInquiry(inquiry_info_t&, bool);
int DumpRestore();
inquiry_info_t GetDeviceInfo();
void WaitPhase(phase_t) const;
bool GetDeviceInfo(inquiry_info_t&);
void WaitForPhase(phase_t) const;
void Selection() const;
void Command(scsi_defs::scsi_command, vector<uint8_t>&) const;
void DataIn(int);
Expand All @@ -65,7 +72,7 @@ class ScsiDump
void WaitForBusy() const;

static void CleanUp();
static void KillHandler(int);
static void TerminationHandler(int);

// A static instance is needed because of the signal handler
static inline unique_ptr<BUS> bus;
Expand All @@ -80,9 +87,41 @@ class ScsiDump

string filename;

bool inquiry = false;

bool scan_bus = false;

bool restore = false;

bool properties_file = false;

static inline const string divider_str = "----------------------------------------";
static const int MINIMUM_BUFFER_SIZE = 1024 * 64;
static const int DEFAULT_BUFFER_SIZE = 1024 * 1024;

static inline const string DIVIDER = "----------------------------------------";

static inline const unordered_map<byte, string> DEVICE_TYPES = {
{ byte{0}, "Direct Access" },
{ byte{1}, "Sequential Access" },
{ byte{2}, "Printer" },
{ byte{3}, "Processor" },
{ byte{4}, "Write-Once" },
{ byte{5}, "CD-ROM/DVD/BD/DVD-RAM" },
{ byte{6}, "Scanner" },
{ byte{7}, "Optical Memory" },
{ byte{8}, "Media Changer" },
{ byte{9}, "Communications" },
{ byte{10}, "Graphic Arts Pre-Press" },
{ byte{11}, "Graphic Arts Pre-Press" },
{ byte{12}, "Storage Array Controller" },
{ byte{13}, "Enclosure Services" },
{ byte{14}, "Simplified Direct Access" },
{ byte{15}, "Optical Card Reader/Writer" },
{ byte{16}, "Bridge Controller" },
{ byte{17}, "Object-based Storage" },
{ byte{18}, "Automation/Drive Interface" },
{ byte{19}, "Security Manager" },
{ byte{20}, "Host Managed Zoned Block" },
{ byte{30}, "Well Known Logical Unit" }
};
};
2 changes: 1 addition & 1 deletion cpp/test/gpiobus_raspberry_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ TEST(GpiobusRaspberry, GetDtRanges)
EXPECT_EQ(0x20000000, GPIOBUS_Raspberry::bcm_host_get_peripheral_address());
DeleteTempFile("/proc/device-tree/soc/ranges");

CleanUpAllTempFiles();
remove_all(test_data_temp_path);
}

TEST(GpiobusRaspberry, GetDat)
Expand Down
44 changes: 19 additions & 25 deletions cpp/test/scsidump_test.cpp
Original file line number Diff line number Diff line change
@@ -1,72 +1,66 @@

//---------------------------------------------------------------------------
//
// SCSI Target Emulator PiSCSI
// for Raspberry Pi
//
// Copyright (C) 2022 akuker
// Copyright (C) 2023 Uwe Seimet
//
//---------------------------------------------------------------------------

#include "mocks.h"
#include <gtest/gtest.h>

#include "scsidump/scsidump_core.h"
#include "test/test_shared.h"
#include <filesystem>
#include <fstream>

using namespace std;
using namespace filesystem;

class TestableScsidump : public ScsiDump
{
public:
static void PublicGeneratePropertiesFile(const string& filename, const inquiry_info_t& inq_info)
{
ScsiDump::GeneratePropertiesFile(filename, inq_info);
}
};

TEST(ScsiDumpTest, GeneratePropertiesFile)
{
// Basic test
const string prop_file_name = "test.properties";
ScsiDump::inquiry_info_t test_data = {
// Basic test
auto filename = CreateTempFile(0);
ScsiDump::inquiry_info_t test_data = {
.vendor = "PISCSI", .product = "TEST PRODUCT", .revision = "REV1", .sector_size = 1000, .capacity = 100};
TestableScsidump::PublicGeneratePropertiesFile("test", test_data);
test_data.GeneratePropertiesFile(filename);

string expected_str = "{\n"
" \"vendor\": \"PISCSI\",\n"
" \"product\": \"TEST PRODUCT\",\n"
" \"revision\": \"REV1\",\n"
" \"block_size\": \"1000\",\n}"
" \"block_size\": \"1000\"\n}"
"\n";
EXPECT_EQ(ReadTempFileToString(prop_file_name), expected_str);
EXPECT_EQ(expected_str, ReadTempFileToString(filename));

// Long string test
filename = CreateTempFile(0);
test_data = {.vendor = "01234567",
.product = "0123456789ABCDEF",
.revision = "0123",
.sector_size = UINT32_MAX,
.capacity = UINT64_MAX};
TestableScsidump::PublicGeneratePropertiesFile("test", test_data);
test_data.GeneratePropertiesFile(filename);

expected_str = "{\n"
" \"vendor\": \"01234567\",\n"
" \"product\": \"0123456789ABCDEF\",\n"
" \"revision\": \"0123\",\n"
" \"block_size\": \"4294967295\",\n"
" \"block_size\": \"4294967295\"\n"
"}\n";
EXPECT_EQ(ReadTempFileToString(prop_file_name), expected_str);
EXPECT_EQ(expected_str, ReadTempFileToString(filename));
remove(filename);

// Empty data test
filename = CreateTempFile(0);
test_data = {.vendor = "", .product = "", .revision = "", .sector_size = 0, .capacity = 0};
TestableScsidump::PublicGeneratePropertiesFile("test", test_data);
test_data.GeneratePropertiesFile(filename);

expected_str = "{\n"
" \"vendor\": \"\",\n"
" \"product\": \"\",\n"
" \"revision\": \"\",\n"
" \"block_size\": \"0\",\n"
" \"block_size\": \"0\"\n"
"}\n";
EXPECT_EQ(ReadTempFileToString(prop_file_name), expected_str);
EXPECT_EQ(expected_str, ReadTempFileToString(filename));
remove(filename);
}
16 changes: 6 additions & 10 deletions cpp/test/test_shared.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -121,24 +121,20 @@ void CreateTempFileWithData(const string& filename, vector<uint8_t>& data)
fclose(fp);
}

// TODO Move this code, it is not shared
void DeleteTempFile(const string& filename)
{
path temp_file = test_data_temp_path;
temp_file += path(filename);
remove(temp_file);
}

void CleanUpAllTempFiles()
{
remove_all(test_data_temp_path);
path temp_file = test_data_temp_path;
temp_file += path(filename);
remove(temp_file);
}

string ReadTempFileToString(const string& filename)
{
const path temp_file = test_data_temp_path / path(filename);
ifstream in_fs(temp_file);
ifstream in(temp_file);
stringstream buffer;
buffer << in_fs.rdbuf();
buffer << in.rdbuf();

return buffer.str();
}
Expand Down
2 changes: 0 additions & 2 deletions cpp/test/test_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ path CreateTempFileWithData(span<const byte>);
void CreateTempFileWithData(const string&, vector<uint8_t>&);

void DeleteTempFile(const string&);
// Call this at the end of every test case to make sure things are cleaned up
void CleanUpAllTempFiles();

string ReadTempFileToString(const string& filename);

Expand Down
8 changes: 8 additions & 0 deletions doc/scsidump.1
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ scsidump \- SCSI disk dumping tool for PiSCSI
[\fB\-r\fR]
[\fB\-v\fR]
[\fB\-p\fR]
[\fB\-I\fR] ID[:LUN]
[\fB\-S\fR]
.SH DESCRIPTION
.B scsidump
has two modes of operation: dump and restore. These can be used with physical storage media, including hard drives and magneto optical drives. Dump mode can be used with read-only media such as CD/DVD drives.
Expand Down Expand Up @@ -47,6 +49,12 @@ Enable verbose logging.
.TP
.BR \-p\fI
Generate a .properties file that is compatible with the PiSCSI web interface. The output filename will match the image filename with ".properties" appended. The generated file should be moved to ~/.config/piscsi.
.TP
.BR \-I\fI " "\fIID[:LUN]
Display INQUIRY data of ID[:LUN].
.TP
.BR \-S\fI
Scan SCSI bus for devices.

.SH EXAMPLES
Dump Mode: [SCSI Drive] ---> [PiSCSI host]
Expand Down
63 changes: 45 additions & 18 deletions doc/scsidump_man_page.txt
Original file line number Diff line number Diff line change
@@ -1,56 +1,83 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
!! ------ The native file is scsidump.1. Re-run 'make docs' after updating


scsidump(1) General Commands Manual scsidump(1)
!! ------ The native file is scsidump.1. Re-run 'make docs' after updating\n\n
scsidump(1) General Commands Manual scsidump(1)

NAME
scsidump - SCSI disk dumping tool for PiSCSI

SYNOPSIS
scsidump -t ID[:LUN] [-i BID] -f FILE [-s BUFFER_SIZE] [-r] [-v] [-p]
scsidump -t ID[:LUN] [-i BID] -f FILE [-s BUFFER_SIZE] [-r] [-v] [-p]
[-I] ID[:LUN] [-S]

DESCRIPTION
scsidump has two modes of operation: dump and restore. These can be used with physical storage media, including hard drives and magneto optical drives. Dump mode can be used with read-only media such as CD/DVD drives.
scsidump has two modes of operation: dump and restore. These can be
used with physical storage media, including hard drives and magneto op‐
tical drives. Dump mode can be used with read-only media such as CD/DVD
drives.

When operating in dump mode, scsidump will copy all data from a remote SCSI drive to an image on the local filesystem. If enabled, it will also generate a .properties file that can be used to more closely emulate the source drive.
When operating in dump mode, scsidump will copy all data from a remote
SCSI drive to an image on the local filesystem. If enabled, it will
also generate a .properties file that can be used to more closely emu‐
late the source drive.

If you are operating in restore mode, scsidump will copy the data from a local binary image to a remote physical SCSI drive. The remote SCSI drive MUST be writable.
If you are operating in restore mode, scsidump will copy the data from
a local binary image to a remote physical SCSI drive. The remote SCSI
drive MUST be writable.

NOTES
scsidump requires either a direct connection (one without transceivers) or a FULLSPEC PiSCSI/RaSCSI board.
scsidump requires either a direct connection (one without transceivers)
or a FULLSPEC PiSCSI/RaSCSI board.

If the generated drive image is intended to be used with PiSCSI, the drive image should be moved to ~/images (or the location specified to the piscsi service).
If the generated drive image is intended to be used with PiSCSI, the
drive image should be moved by the user to ~/images (or the location
specified to the piscsi service).

OPTIONS
-t ID[:LUN]
SCSI ID and optional LUN of the remote SCSI device. The remote SCSI device will be functioning as the "Target" device.
SCSI ID and optional LUN of the remote SCSI device. The remote
SCSI device will be functioning as the "Target" device.

-i BID SCSI ID of the PiSCSI device. If not specified, the PiSCSI device will use ID 7. The PiSCSI host will be functioning as the "Initiator" device.
-i BID SCSI ID of the PiSCSI device. If not specified, the PiSCSI de‐
vice will use ID 7. The PiSCSI host will be functioning as the
"Initiator" device.

-f FILE
Path to the dump file.

-s BUFFER_SIZE
The transfer buffer size, specified in bytes. Default is 1 MiB. This is specified in bytes with a minimum value of 65536 (64 KiB).
The transfer buffer size, specified in bytes. Default is 1 MiB.
This is specified in bytes with a minimum value of 65536 (64
KiB).

-r Run in restore mode. Defaults to dump mode if not specified.

-v Enable verbose logging.

-p Generate a .properties file that is compatible with the PiSCSI web interface. The output filename will match the image filename with ".properties" appended. The generated file should be moved to ~/.config/piscsi.
-p Generate a .properties file that is compatible with the PiSCSI
web interface. The output filename will match the image filename
with ".properties" appended. The generated file should be moved
to ~/.config/piscsi.

-I ID[:LUN]
Display INQUIRY data of ID[:LUN].

-S Scan SCSI bus for devices.

EXAMPLES
Dump Mode: [SCSI Drive] ---> [PiSCSI host] Launch scsidump to dump an all data from SCSI ID 3 with block size 64 KiB, store it to the local filesystem as a drive image named outimage.hda, and generate the outimage.hda.properties file with the drive's INQUIRY
information:
Dump Mode: [SCSI Drive] ---> [PiSCSI host] Launch scsidump to dump an
all data from SCSI ID 3 with block size 64 KiB, store it to the local
filesystem as a drive image named outimage.hda, and generate the outim‐
age.hda.properties file with the drive's INQUIRY information:
scsidump -t 3 -f ./outimage.hda -s 65536 -p

Restore Mode: [PiSCSI host] ---> [SCSI Drive] Launch scsidump to restore/upload a drive image from the local file system to SCSI ID 0 with block size 1MiB:
Restore Mode: [PiSCSI host] ---> [SCSI Drive] Launch scsidump to re‐
store/upload a drive image from the local file system to SCSI ID 0 with
block size 1MiB:
scsidump -r -t 0 -f ./outimage.hda -s 1048576

SEE ALSO
scsictl(1), piscsi(1), scsimon(1)

Full documentation is available at: <https://www.piscsi.com>

scsidump(1)
scsidump(1)

0 comments on commit 637b0bd

Please sign in to comment.