2022-10-08 17:26:04 +00:00
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
2022-12-05 17:58:23 +00:00
|
|
|
// SCSI Target Emulator PiSCSI
|
2022-10-08 17:26:04 +00:00
|
|
|
// for Raspberry Pi
|
|
|
|
//
|
|
|
|
// Copyright (C) 2022 Uwe Seimet
|
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
#include "test_shared.h"
|
|
|
|
#include "controllers/controller_manager.h"
|
2022-10-08 17:26:04 +00:00
|
|
|
#include "mocks.h"
|
2022-12-05 17:58:23 +00:00
|
|
|
#include "shared/piscsi_exceptions.h"
|
|
|
|
#include "shared/piscsi_version.h"
|
2022-12-03 04:20:27 +00:00
|
|
|
#include <filesystem>
|
2022-12-09 15:50:45 +00:00
|
|
|
#include <fstream>
|
|
|
|
#include <iostream>
|
2022-12-03 04:20:27 +00:00
|
|
|
#include <sstream>
|
2022-10-23 19:51:39 +00:00
|
|
|
#include <unistd.h>
|
|
|
|
#include <vector>
|
2022-10-08 17:26:04 +00:00
|
|
|
|
|
|
|
using namespace std;
|
2022-10-25 08:29:57 +00:00
|
|
|
using namespace filesystem;
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
// Inlude the process id in the temp file path so that multiple instances of the test procedures
|
|
|
|
// could run on the same host.
|
|
|
|
const path test_data_temp_path(temp_directory_path() /
|
2022-12-05 17:58:23 +00:00
|
|
|
path(fmt::format("piscsi-test-{}",
|
2022-12-03 04:20:27 +00:00
|
|
|
getpid()))); // NOSONAR Publicly writable directory is fine here
|
|
|
|
|
2022-12-09 15:50:45 +00:00
|
|
|
shared_ptr<PrimaryDevice> CreateDevice(PbDeviceType type, MockAbstractController& controller, const string& extension)
|
2022-10-08 17:26:04 +00:00
|
|
|
{
|
2022-12-03 04:20:27 +00:00
|
|
|
DeviceFactory device_factory;
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
auto device = device_factory.CreateDevice(type, 0, extension);
|
|
|
|
unordered_map<string, string> params;
|
|
|
|
device->Init(params);
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
controller.AddDevice(device);
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
return device;
|
2022-10-08 17:26:04 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 15:50:45 +00:00
|
|
|
void TestInquiry(PbDeviceType type, device_type t, scsi_level l, const string& ident, int additional_length,
|
|
|
|
bool removable, const string& extension)
|
2022-10-08 17:26:04 +00:00
|
|
|
{
|
2022-12-03 04:20:27 +00:00
|
|
|
auto bus = make_shared<MockBus>();
|
|
|
|
auto controller_manager = make_shared<ControllerManager>(*bus);
|
|
|
|
auto controller = make_shared<NiceMock<MockAbstractController>>(controller_manager, 0);
|
|
|
|
auto device = CreateDevice(type, *controller, extension);
|
2022-10-08 17:26:04 +00:00
|
|
|
|
2022-12-09 15:50:45 +00:00
|
|
|
auto& cmd = controller->GetCmd();
|
2022-10-08 17:26:04 +00:00
|
|
|
|
|
|
|
// ALLOCATION LENGTH
|
|
|
|
cmd[4] = 255;
|
2022-11-04 07:22:32 +00:00
|
|
|
EXPECT_CALL(*controller, DataIn());
|
2022-11-02 14:36:19 +00:00
|
|
|
device->Dispatch(scsi_command::eCmdInquiry);
|
2022-12-09 15:50:45 +00:00
|
|
|
const vector<uint8_t>& buffer = controller->GetBuffer();
|
2022-12-03 04:20:27 +00:00
|
|
|
EXPECT_EQ(t, static_cast<device_type>(buffer[0]));
|
|
|
|
EXPECT_EQ(removable ? 0x80 : 0x00, buffer[1]);
|
|
|
|
EXPECT_EQ(l, static_cast<scsi_level>(buffer[2]));
|
|
|
|
EXPECT_EQ(l > scsi_level::SCSI_2 ? scsi_level::SCSI_2 : l, static_cast<scsi_level>(buffer[3]));
|
|
|
|
EXPECT_EQ(additional_length, buffer[4]);
|
|
|
|
string product_data;
|
|
|
|
if (ident.size() == 24) {
|
|
|
|
ostringstream s;
|
2022-12-05 17:58:23 +00:00
|
|
|
s << ident << setw(2) << setfill('0') << piscsi_major_version << piscsi_minor_version;
|
2022-12-03 04:20:27 +00:00
|
|
|
product_data = s.str();
|
|
|
|
} else {
|
|
|
|
product_data = ident;
|
|
|
|
}
|
|
|
|
EXPECT_TRUE(!memcmp(product_data.c_str(), &buffer[8], 28));
|
2022-10-08 17:26:04 +00:00
|
|
|
}
|
2022-10-23 19:51:39 +00:00
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
pair<int, path> OpenTempFile()
|
2022-10-23 19:51:39 +00:00
|
|
|
{
|
2022-12-03 04:20:27 +00:00
|
|
|
const string filename =
|
2022-12-05 17:58:23 +00:00
|
|
|
string(test_data_temp_path) + "/piscsi_test-XXXXXX"; // NOSONAR Publicly writable directory is fine here
|
2022-12-03 04:20:27 +00:00
|
|
|
vector<char> f(filename.begin(), filename.end());
|
|
|
|
f.push_back(0);
|
2022-10-23 19:51:39 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
create_directories(path(filename).parent_path());
|
2022-10-23 19:51:39 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
const int fd = mkstemp(f.data());
|
|
|
|
EXPECT_NE(-1, fd) << "Couldn't create temporary file '" << f.data() << "'";
|
|
|
|
|
|
|
|
return make_pair(fd, path(f.data()));
|
2022-10-23 19:51:39 +00:00
|
|
|
}
|
|
|
|
|
2022-10-25 08:29:57 +00:00
|
|
|
path CreateTempFile(int size)
|
2022-10-23 19:51:39 +00:00
|
|
|
{
|
2022-12-03 04:20:27 +00:00
|
|
|
const auto [fd, filename] = OpenTempFile();
|
|
|
|
|
|
|
|
vector<char> data(size);
|
|
|
|
const size_t count = write(fd, data.data(), data.size());
|
|
|
|
close(fd);
|
|
|
|
EXPECT_EQ(count, data.size()) << "Couldn't create temporary file '" << string(filename) << "'";
|
2022-10-23 19:51:39 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
return path(filename);
|
|
|
|
}
|
2022-10-23 19:51:39 +00:00
|
|
|
|
2022-12-05 17:58:23 +00:00
|
|
|
// TODO Replace old-fashinoned C I/O by C++ streams I/O.
|
|
|
|
// This also avoids potential issues with data type sizes and there is no need for c_str().
|
2022-12-09 15:50:45 +00:00
|
|
|
void CreateTempFileWithData(const string& filename, vector<uint8_t>& data)
|
2022-12-03 04:20:27 +00:00
|
|
|
{
|
|
|
|
path new_filename = test_data_temp_path;
|
|
|
|
new_filename += path(filename);
|
|
|
|
|
|
|
|
create_directories(new_filename.parent_path());
|
|
|
|
|
2022-12-09 15:50:45 +00:00
|
|
|
FILE* fp = fopen(new_filename.c_str(), "wb");
|
2022-12-03 04:20:27 +00:00
|
|
|
if (fp == nullptr) {
|
|
|
|
printf("ERROR: Unable to open file %s\n", new_filename.c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-12-05 17:58:23 +00:00
|
|
|
if (const size_t size_written = fwrite(&data[0], sizeof(uint8_t), data.size(), fp);
|
2022-12-09 15:50:45 +00:00
|
|
|
size_written != sizeof(vector<uint8_t>::value_type) * data.size()) {
|
2022-12-03 04:20:27 +00:00
|
|
|
printf("Expected to write %zu bytes, but only wrote %zu to %s", size_written,
|
|
|
|
sizeof(vector<uint8_t>::value_type) * data.size(), filename.c_str());
|
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
2022-12-05 17:58:23 +00:00
|
|
|
void DeleteTempFile(const string& filename)
|
2022-12-03 04:20:27 +00:00
|
|
|
{
|
|
|
|
path temp_file = test_data_temp_path;
|
|
|
|
temp_file += path(filename);
|
|
|
|
remove(temp_file);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CleanupAllTempFiles()
|
|
|
|
{
|
|
|
|
remove_all(test_data_temp_path);
|
2022-10-23 19:51:39 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 15:50:45 +00:00
|
|
|
string ReadTempFileToString(const string& filename)
|
|
|
|
{
|
|
|
|
path temp_file = test_data_temp_path / path(filename);
|
|
|
|
ifstream in_fs(temp_file);
|
|
|
|
stringstream buffer;
|
|
|
|
buffer << in_fs.rdbuf();
|
|
|
|
|
|
|
|
return buffer.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
int GetInt16(const vector<byte>& buf, int offset)
|
2022-10-23 19:51:39 +00:00
|
|
|
{
|
2022-12-03 04:20:27 +00:00
|
|
|
assert(buf.size() > static_cast<size_t>(offset) + 1);
|
2022-10-23 19:51:39 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
return (to_integer<int>(buf[offset]) << 8) | to_integer<int>(buf[offset + 1]);
|
2022-10-23 19:51:39 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 15:50:45 +00:00
|
|
|
uint32_t GetInt32(const vector<byte>& buf, int offset)
|
2022-10-23 19:51:39 +00:00
|
|
|
{
|
2022-12-03 04:20:27 +00:00
|
|
|
assert(buf.size() > static_cast<size_t>(offset) + 3);
|
2022-10-23 19:51:39 +00:00
|
|
|
|
2022-12-03 04:20:27 +00:00
|
|
|
return (to_integer<uint32_t>(buf[offset]) << 24) | (to_integer<uint32_t>(buf[offset + 1]) << 16) |
|
|
|
|
(to_integer<uint32_t>(buf[offset + 2]) << 8) | to_integer<uint32_t>(buf[offset + 3]);
|
2022-10-23 19:51:39 +00:00
|
|
|
}
|