From 829356a32cd49886536249b5a7d62f58dadaa31c Mon Sep 17 00:00:00 2001 From: Uwe Seimet <48174652+uweseimet@users.noreply.github.com> Date: Fri, 24 Sep 2021 08:48:48 +0200 Subject: [PATCH] protobuf interface returns assumed device type (#259) * Added device type to PbImageFile * Comment update * Code cleanup * More code cleanup * Extraced methods * Extracted methods * Renaming * Moved code * Visility update * Updated error handling * Removed duplicate error handling * Removed duplicate code * Code cleanup * Moved code * Moved code --- src/raspberrypi/Makefile | 1 + src/raspberrypi/devices/device_factory.cpp | 67 +-- src/raspberrypi/devices/device_factory.h | 8 +- src/raspberrypi/protobuf_response_helper.cpp | 306 ++++++++++++++ src/raspberrypi/protobuf_response_helper.h | 53 +++ src/raspberrypi/rascsi.cpp | 406 +++---------------- src/raspberrypi/rascsi_interface.proto | 6 +- src/raspberrypi/rasctl.cpp | 7 +- 8 files changed, 478 insertions(+), 376 deletions(-) create mode 100644 src/raspberrypi/protobuf_response_helper.cpp create mode 100644 src/raspberrypi/protobuf_response_helper.h diff --git a/src/raspberrypi/Makefile b/src/raspberrypi/Makefile index c3fee54c..17774269 100644 --- a/src/raspberrypi/Makefile +++ b/src/raspberrypi/Makefile @@ -83,6 +83,7 @@ SRC_RASCSI = \ fileio.cpp\ rascsi_version.cpp \ rasutil.cpp \ + protobuf_response_helper.cpp \ protobuf_util.cpp SRC_RASCSI += $(shell find ./controllers -name '*.cpp') SRC_RASCSI += $(shell find ./devices -name '*.cpp') diff --git a/src/raspberrypi/devices/device_factory.cpp b/src/raspberrypi/devices/device_factory.cpp index 8fbef222..b512997d 100644 --- a/src/raspberrypi/devices/device_factory.cpp +++ b/src/raspberrypi/devices/device_factory.cpp @@ -72,33 +72,50 @@ DeviceFactory& DeviceFactory::instance() return instance; } -Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, const string& extension) +string DeviceFactory::GetExtension(const string& filename) { - string ext = extension; + 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); }); - // If no type was specified try to derive the device type from the filename and extension + return ext; +} + +PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) +{ + string ext = GetExtension(filename); + if (ext == "hdf") { + return SAHD; + } + else if (ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") { + return SCHD; + } + else if (ext == "hdr") { + return SCRM; + } else if (ext == "mos") { + return SCMO; + } else if (ext == "iso") { + return SCCD; + } + else if (filename == "bridge") { + return SCBR; + } + else if (filename == "daynaport") { + return SCDP; + } + + return UNDEFINED; +} + +Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename) +{ + // If no type was specified try to derive the device type from the filename if (type == UNDEFINED) { - if (ext == "hdf") { - type = SAHD; - } - else if (ext == "hds" || ext == "hdn" || ext == "hdi" || ext == "nhd" || ext == "hda") { - type = SCHD; - } - else if (ext == "hdr") { - type = SCRM; - } else if (ext == "mos") { - type = SCMO; - } else if (ext == "iso") { - type = SCCD; - } - else if (filename == "bridge") { - type = SCBR; - } - else if (filename == "daynaport") { - type = SCDP; - } - else { + type = GetTypeForFile(filename); + if (type == UNDEFINED) { return NULL; } } @@ -112,7 +129,8 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, c ((Disk *)device)->SetSectorSizes(sector_sizes[SAHD]); break; - case SCHD: + case SCHD: { + string ext = GetExtension(filename); if (ext == "hdn" || ext == "hdi" || ext == "nhd") { device = new SCSIHD_NEC(); ((Disk *)device)->SetSectorSizes({ 512 }); @@ -124,6 +142,7 @@ Device *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, c device->SetProtectable(true); device->SetStoppable(true); break; + } case SCRM: device = new SCSIHD(true); diff --git a/src/raspberrypi/devices/device_factory.h b/src/raspberrypi/devices/device_factory.h index 3ed972c0..2bae6b22 100644 --- a/src/raspberrypi/devices/device_factory.h +++ b/src/raspberrypi/devices/device_factory.h @@ -5,7 +5,7 @@ // // Copyright (C) 2021 Uwe Seimet // -// The DeviceFactory singleton creates devices based on their type and the extension of their image file +// The DeviceFactory singleton creates devices based on their type and the image file extension // //--------------------------------------------------------------------------- @@ -33,7 +33,9 @@ public: static DeviceFactory& instance(); - Device *CreateDevice(PbDeviceType type, const string& filename, const string& ext); + static PbDeviceType GetTypeForFile(const string&); + + Device *CreateDevice(PbDeviceType, const string&); const set& GetSectorSizes(PbDeviceType type) { return sector_sizes[type]; } const set& GetSectorSizes(const string&); @@ -48,4 +50,6 @@ private: map> geometries; map> default_params; + + static string GetExtension(const string&); }; diff --git a/src/raspberrypi/protobuf_response_helper.cpp b/src/raspberrypi/protobuf_response_helper.cpp new file mode 100644 index 00000000..20cb59cf --- /dev/null +++ b/src/raspberrypi/protobuf_response_helper.cpp @@ -0,0 +1,306 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +//--------------------------------------------------------------------------- + +#include "devices/file_support.h" +#include "devices/disk.h" +#include "devices/device_factory.h" +#include "devices/device.h" +#include "protobuf_util.h" +#include "rascsi_version.h" +#include "rascsi_interface.pb.h" +#include "protobuf_response_helper.h" +#include + +using namespace rascsi_interface; + +ProtobufResponseHandler::ProtobufResponseHandler() +{ + device_factory = DeviceFactory::instance(); + + log_levels.push_back("trace"); + log_levels.push_back("debug"); + log_levels.push_back("info"); + log_levels.push_back("warn"); + log_levels.push_back("err"); + log_levels.push_back("critical"); + log_levels.push_back("off"); +} + +ProtobufResponseHandler& ProtobufResponseHandler::instance() +{ + static ProtobufResponseHandler instance; + return instance; +} + +PbDeviceProperties *ProtobufResponseHandler::GetDeviceProperties(const Device *device) +{ + PbDeviceProperties *properties = new PbDeviceProperties(); + + properties->set_luns(device->GetSupportedLuns()); + properties->set_read_only(device->IsReadOnly()); + properties->set_protectable(device->IsProtectable()); + properties->set_stoppable(device->IsStoppable()); + properties->set_removable(device->IsRemovable()); + properties->set_lockable(device->IsLockable()); + properties->set_supports_file(dynamic_cast(device)); + properties->set_supports_params(device->SupportsParams()); + + PbDeviceType t = UNDEFINED; + PbDeviceType_Parse(device->GetType(), &t); + + if (device->SupportsParams()) { + for (const auto& param : device_factory.GetDefaultParams(t)) { + auto& map = *properties->mutable_default_params(); + map[param.first] = param.second; + } + } + + for (const auto& block_size : device_factory.GetSectorSizes(t)) { + properties->add_block_sizes(block_size); + } + + for (const auto& capacity : device_factory.GetCapacities(t)) { + properties->add_capacities(capacity); + } + + return properties; +} + +void ProtobufResponseHandler::GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type) +{ + PbDeviceTypeProperties *type_properties = device_types_info.add_properties(); + type_properties->set_type(type); + Device *device = device_factory.CreateDevice(type, ""); + type_properties->set_allocated_properties(GetDeviceProperties(device)); + delete device; +} + +void ProtobufResponseHandler::GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) +{ + GetDeviceTypeProperties(device_types_info, SAHD); + GetDeviceTypeProperties(device_types_info, SCHD); + GetDeviceTypeProperties(device_types_info, SCRM); + GetDeviceTypeProperties(device_types_info, SCMO); + GetDeviceTypeProperties(device_types_info, SCCD); + GetDeviceTypeProperties(device_types_info, SCBR); + GetDeviceTypeProperties(device_types_info, SCDP); +} + +void ProtobufResponseHandler::GetDevice(const Device *device, PbDevice *pb_device, const string& image_folder) +{ + pb_device->set_id(device->GetId()); + pb_device->set_unit(device->GetLun()); + pb_device->set_vendor(device->GetVendor()); + pb_device->set_product(device->GetProduct()); + pb_device->set_revision(device->GetRevision()); + + PbDeviceType type = UNDEFINED; + PbDeviceType_Parse(device->GetType(), &type); + pb_device->set_type(type); + + pb_device->set_allocated_properties(GetDeviceProperties(device)); + + PbDeviceStatus *status = new PbDeviceStatus(); + pb_device->set_allocated_status(status); + status->set_protected_(device->IsProtected()); + status->set_stopped(device->IsStopped()); + status->set_removed(device->IsRemoved()); + status->set_locked(device->IsLocked()); + + if (device->SupportsParams()) { + for (const auto& param : device->GetParams()) { + AddParam(*pb_device, param.first, param.second); + } + } + + const Disk *disk = dynamic_cast(device); + if (disk) { + pb_device->set_block_size(device->IsRemoved()? 0 : disk->GetSectorSizeInBytes()); + pb_device->set_block_count(device->IsRemoved() ? 0: disk->GetBlockCount()); + } + + const FileSupport *file_support = dynamic_cast(device); + if (file_support) { + Filepath filepath; + file_support->GetPath(filepath); + PbImageFile *image_file = new PbImageFile(); + GetImageFile(image_file, device->IsRemovable() && !device->IsReady() ? "" : filepath.GetPath(), image_folder); + pb_device->set_allocated_file(image_file); + } +} + +void ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string& filename, const string& image_folder) +{ + image_file->set_name(filename); + if (!filename.empty()) { + image_file->set_type(DeviceFactory::GetTypeForFile(filename)); + + string f = filename[0] == '/' ? filename : image_folder + "/" + filename; + + image_file->set_read_only(access(f.c_str(), W_OK)); + + struct stat st; + if (!stat(f.c_str(), &st)) { + image_file->set_size(st.st_size); + } + } +} + +PbImageFilesInfo *ProtobufResponseHandler::GetAvailableImages(PbResult& result, const string& image_folder) +{ + PbImageFilesInfo *image_files_info = new PbImageFilesInfo(); + + image_files_info->set_default_image_folder(image_folder); + + // filesystem::directory_iterator cannot be used because libstdc++ 8.3.0 does not support big files + DIR *d = opendir(image_folder.c_str()); + if (d) { + struct dirent *dir; + while ((dir = readdir(d))) { + if (dir->d_type == DT_REG || dir->d_type == DT_LNK || dir->d_type == DT_BLK) { + string filename = image_folder + "/" + dir->d_name; + + struct stat st; + if (dir->d_type == DT_REG && !stat(filename.c_str(), &st)) { + if (!st.st_size) { + LOGTRACE("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name, image_folder.c_str()); + continue; + } + + if (st.st_size % 512) { + LOGTRACE("Size of file '%s' in image folder '%s' is not a multiple of 512", dir->d_name, image_folder.c_str()); + continue; + } + } else if (dir->d_type == DT_LNK && stat(filename.c_str(), &st)) { + LOGTRACE("Symlink '%s' in image folder '%s' is broken", dir->d_name, image_folder.c_str()); + continue; + } + + GetImageFile(image_files_info->add_image_files(), dir->d_name, image_folder); + } + } + + closedir(d); + } + + result.set_status(true); + + return image_files_info; +} + +void ProtobufResponseHandler::GetAvailableImages(PbResult& result, PbServerInfo& server_info, const string& image_folder) +{ + PbImageFilesInfo *image_files_info = GetAvailableImages(result, image_folder); + image_files_info->set_default_image_folder(image_folder); + server_info.set_allocated_image_files_info(image_files_info); + + result.set_status(true); +} + +void ProtobufResponseHandler::GetDevices(PbServerInfo& serverInfo, const vector& devices, const string& image_folder) +{ + for (const Device *device : devices) { + // Skip if unit does not exist or is not assigned + if (device) { + PbDevice *pb_device = serverInfo.mutable_devices()->add_devices(); + GetDevice(device, pb_device, image_folder); + } + } +} + +void ProtobufResponseHandler::GetDevicesInfo(PbResult& result, const PbCommand& command, const vector& devices, + const string& image_folder, int unit_count) +{ + set id_sets; + if (!command.devices_size()) { + for (const Device *device : devices) { + if (device) { + id_sets.insert(make_pair(device->GetId(), device->GetLun())); + } + } + } + else { + for (const auto& device : command.devices()) { + if (devices[device.id() * unit_count + device.unit()]) { + id_sets.insert(make_pair(device.id(), device.unit())); + } + else { + ostringstream error; + error << "No device for ID " << device.id() << ", unit " << device.unit(); + result.set_status(false); + result.set_msg(error.str()); + return; + } + } + } + + PbDevices *pb_devices = new PbDevices(); + result.set_allocated_device_info(pb_devices); + + for (const auto& id_set : id_sets) { + const Device *device = devices[id_set.first * unit_count + id_set.second]; + GetDevice(device, pb_devices->add_devices(), image_folder); + } + + result.set_status(true); +} + +PbDeviceTypesInfo *ProtobufResponseHandler::GetDeviceTypesInfo(PbResult& result, const PbCommand& command) +{ + PbDeviceTypesInfo *device_types_info = new PbDeviceTypesInfo(); + + GetAllDeviceTypeProperties(*device_types_info); + + result.set_status(true); + + return device_types_info; +} + +PbServerInfo *ProtobufResponseHandler::GetServerInfo(PbResult& result, const vector& devices, const set& reserved_ids, + const string& image_folder, const string& log_level) +{ + PbServerInfo *server_info = new PbServerInfo(); + + server_info->set_major_version(rascsi_major_version); + server_info->set_minor_version(rascsi_minor_version); + server_info->set_patch_version(rascsi_patch_version); + GetLogLevels(*server_info); + server_info->set_current_log_level(log_level); + GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); + GetAvailableImages(result, *server_info, image_folder); + server_info->set_allocated_network_interfaces_info(GetNetworkInterfacesInfo(result)); + GetDevices(*server_info, devices, image_folder); + for (int id : reserved_ids) { + server_info->add_reserved_ids(id); + } + + result.set_status(true); + + return server_info; +} + +void ProtobufResponseHandler::GetLogLevels(PbServerInfo& server_info) +{ + for (const auto& log_level : log_levels) { + server_info.add_log_levels(log_level); + } +} + +PbNetworkInterfacesInfo *ProtobufResponseHandler::GetNetworkInterfacesInfo(PbResult& result) +{ + PbNetworkInterfacesInfo *network_interfaces_info = new PbNetworkInterfacesInfo(); + + for (const auto& network_interface : device_factory.GetNetworkInterfaces()) { + network_interfaces_info->add_name(network_interface); + } + + result.set_status(true); + + return network_interfaces_info; +} diff --git a/src/raspberrypi/protobuf_response_helper.h b/src/raspberrypi/protobuf_response_helper.h new file mode 100644 index 00000000..adffd728 --- /dev/null +++ b/src/raspberrypi/protobuf_response_helper.h @@ -0,0 +1,53 @@ +//--------------------------------------------------------------------------- +// +// SCSI Target Emulator RaSCSI (*^..^*) +// for Raspberry Pi +// +// Copyright (C) 2021 Uwe Seimet +// +// A singleton that creates responses for protobuf interface requests +// +//--------------------------------------------------------------------------- + +#pragma once + +#include "devices/device_factory.h" +#include "rascsi_interface.pb.h" +#include +#include + +using namespace std; +using namespace rascsi_interface; + +class Device; + +class ProtobufResponseHandler +{ +public: + + ProtobufResponseHandler(); + ~ProtobufResponseHandler() {}; + + static ProtobufResponseHandler& instance(); + + PbImageFilesInfo *GetAvailableImages(PbResult&, const string&); + void GetDevices(PbServerInfo&, const vector&, const string&); + void GetDevicesInfo(PbResult&, const PbCommand&, const vector&, const string&, int); + PbDeviceTypesInfo *GetDeviceTypesInfo(PbResult&, const PbCommand&); + PbServerInfo *GetServerInfo(PbResult&, const vector&, const set&, const string&, const string&); + PbNetworkInterfacesInfo *GetNetworkInterfacesInfo(PbResult&); + +private: + + DeviceFactory device_factory; + + vector log_levels; + + PbDeviceProperties *GetDeviceProperties(const Device *); + void GetDevice(const Device *, PbDevice *, const string&); + void GetAllDeviceTypeProperties(PbDeviceTypesInfo&); + void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType); + void GetAvailableImages(PbResult& result, PbServerInfo&, const string&); + void GetImageFile(PbImageFile *, const string&, const string&); + void GetLogLevels(PbServerInfo&); +}; diff --git a/src/raspberrypi/rascsi.cpp b/src/raspberrypi/rascsi.cpp index 09c42a45..20bfcd0d 100644 --- a/src/raspberrypi/rascsi.cpp +++ b/src/raspberrypi/rascsi.cpp @@ -10,7 +10,6 @@ //--------------------------------------------------------------------------- #include "rascsi.h" - #include "os.h" #include "filepath.h" #include "fileio.h" @@ -24,6 +23,7 @@ #include "devices/file_support.h" #include "gpiobus.h" #include "exceptions.h" +#include "protobuf_response_helper.h" #include "protobuf_util.h" #include "rascsi_version.h" #include "rasutil.h" @@ -69,11 +69,11 @@ int monsocket; // Monitor Socket pthread_t monthread; // Monitor Thread pthread_mutex_t ctrl_mutex; // Semaphore for the ctrl array static void *MonThread(void *param); -vector log_levels; string current_log_level; // Some versions of spdlog do not support get_log_level() string default_image_folder; set reserved_ids; DeviceFactory& device_factory = DeviceFactory::instance(); +ProtobufResponseHandler& response_helper = ProtobufResponseHandler::instance(); //--------------------------------------------------------------------------- // @@ -255,21 +255,6 @@ void Reset() bus->Reset(); } -void GetImageFile(PbImageFile *image_file, const string& filename) -{ - image_file->set_name(filename); - if (!filename.empty()) { - string f = filename[0] == '/' ? filename : default_image_folder + "/" + filename; - - image_file->set_read_only(access(f.c_str(), W_OK)); - - struct stat st; - if (!stat(f.c_str(), &st)) { - image_file->set_size(st.st_size); - } - } -} - //--------------------------------------------------------------------------- // // Controller Mapping @@ -466,241 +451,6 @@ void LogDevices(const string& devices) } } -void GetLogLevels(PbServerInfo& server_info) -{ - for (const auto& log_level : log_levels) { - server_info.add_log_levels(log_level); - } -} - -PbDeviceProperties *GetDeviceProperties(const Device *device) -{ - PbDeviceProperties *properties = new PbDeviceProperties(); - - properties->set_luns(device->GetSupportedLuns()); - properties->set_read_only(device->IsReadOnly()); - properties->set_protectable(device->IsProtectable()); - properties->set_stoppable(device->IsStoppable()); - properties->set_removable(device->IsRemovable()); - properties->set_lockable(device->IsLockable()); - properties->set_supports_file(dynamic_cast(device)); - properties->set_supports_params(device->SupportsParams()); - - PbDeviceType t = UNDEFINED; - PbDeviceType_Parse(device->GetType(), &t); - - if (device->SupportsParams()) { - for (const auto& param : device_factory.GetDefaultParams(t)) { - auto& map = *properties->mutable_default_params(); - map[param.first] = param.second; - } - } - - for (const auto& block_size : device_factory.GetSectorSizes(t)) { - properties->add_block_sizes(block_size); - } - - for (const auto& capacity : device_factory.GetCapacities(t)) { - properties->add_capacities(capacity); - } - - return properties; -} - -void GetDeviceTypeProperties(PbDeviceTypesInfo& device_types_info, PbDeviceType type) -{ - PbDeviceTypeProperties *type_properties = device_types_info.add_properties(); - type_properties->set_type(type); - Device *device = device_factory.CreateDevice(type, "", ""); - type_properties->set_allocated_properties(GetDeviceProperties(device)); - delete device; -} - -void GetAllDeviceTypeProperties(PbDeviceTypesInfo& device_types_info) -{ - GetDeviceTypeProperties(device_types_info, SAHD); - GetDeviceTypeProperties(device_types_info, SCHD); - GetDeviceTypeProperties(device_types_info, SCRM); - GetDeviceTypeProperties(device_types_info, SCMO); - GetDeviceTypeProperties(device_types_info, SCCD); - GetDeviceTypeProperties(device_types_info, SCBR); - GetDeviceTypeProperties(device_types_info, SCDP); -} - -void GetAvailableImages(PbImageFilesInfo& image_files_info) -{ - image_files_info.set_default_image_folder(default_image_folder); - - // filesystem::directory_iterator cannot be used because libstdc++ 8.3.0 does not support big files - DIR *d = opendir(default_image_folder.c_str()); - if (d) { - struct dirent *dir; - while ((dir = readdir(d))) { - if (dir->d_type == DT_REG || dir->d_type == DT_LNK || dir->d_type == DT_BLK) { - string filename = default_image_folder + "/" + dir->d_name; - - struct stat st; - if (dir->d_type == DT_REG && !stat(filename.c_str(), &st)) { - if (!st.st_size) { - LOGTRACE("File '%s' in image folder '%s' has a size of 0 bytes", dir->d_name, - default_image_folder.c_str()); - continue; - } - - if (st.st_size % 512) { - LOGTRACE("Size of file '%s' in image folder '%s' is not a multiple of 512", dir->d_name, - default_image_folder.c_str()); - continue; - } - } else if (dir->d_type == DT_LNK && stat(filename.c_str(), &st)) { - LOGTRACE("Symlink '%s' in image folder '%s' is broken", dir->d_name, - default_image_folder.c_str()); - continue; - } - - GetImageFile(image_files_info.add_image_files(), dir->d_name); - } - } - - closedir(d); - } -} - -void GetAvailableImages(PbServerInfo& server_info) -{ - PbImageFilesInfo *image_files_info = new PbImageFilesInfo(); - server_info.set_allocated_image_files_info(image_files_info); - - image_files_info->set_default_image_folder(default_image_folder); - - GetAvailableImages(*image_files_info); -} - -void GetNetworkInterfacesInfo(PbNetworkInterfacesInfo& network_interfaces_info) -{ - for (const auto& network_interface : device_factory.GetNetworkInterfaces()) { - network_interfaces_info.add_name(network_interface); - } -} - -void GetDevice(const Device *device, PbDevice *pb_device) -{ - pb_device->set_id(device->GetId()); - pb_device->set_unit(device->GetLun()); - pb_device->set_vendor(device->GetVendor()); - pb_device->set_product(device->GetProduct()); - pb_device->set_revision(device->GetRevision()); - - PbDeviceType type = UNDEFINED; - PbDeviceType_Parse(device->GetType(), &type); - pb_device->set_type(type); - - pb_device->set_allocated_properties(GetDeviceProperties(device)); - - PbDeviceStatus *status = new PbDeviceStatus(); - pb_device->set_allocated_status(status); - status->set_protected_(device->IsProtected()); - status->set_stopped(device->IsStopped()); - status->set_removed(device->IsRemoved()); - status->set_locked(device->IsLocked()); - - if (device->SupportsParams()) { - for (const auto& param : device->GetParams()) { - AddParam(*pb_device, param.first, param.second); - } - } - - const Disk *disk = dynamic_cast(device); - if (disk) { - pb_device->set_block_size(device->IsRemoved()? 0 : disk->GetSectorSizeInBytes()); - pb_device->set_block_count(device->IsRemoved() ? 0: disk->GetBlockCount()); - } - - const FileSupport *file_support = dynamic_cast(device); - if (file_support) { - Filepath filepath; - file_support->GetPath(filepath); - PbImageFile *image_file = new PbImageFile(); - GetImageFile(image_file, device->IsRemovable() && !device->IsReady() ? "" : filepath.GetPath()); - pb_device->set_allocated_file(image_file); - } -} - -void GetDevices(PbServerInfo& serverInfo) -{ - for (const Device *device : devices) { - // Skip if unit does not exist or is not assigned - if (device) { - PbDevice *pb_device = serverInfo.mutable_devices()->add_devices(); - GetDevice(device, pb_device); - } - } -} - -void GetDevicesInfo(const PbCommand& command, PbResult& result) -{ - set id_sets; - if (!command.devices_size()) { - for (const Device *device : devices) { - if (device) { - id_sets.insert(make_pair(device->GetId(), device->GetLun())); - } - } - } - else { - for (const auto& device : command.devices()) { - if (devices[device.id() * UnitNum + device.unit()]) { - id_sets.insert(make_pair(device.id(), device.unit())); - } - else { - ostringstream error; - error << "No device for ID " << device.id() << ", unit " << device.unit(); - result.set_status(false); - result.set_msg(error.str()); - return; - } - } - } - - PbDevices *pb_devices = new PbDevices(); - result.set_allocated_device_info(pb_devices); - - for (const auto& id_set : id_sets) { - Device *device = devices[id_set.first * UnitNum + id_set.second]; - GetDevice(device, pb_devices->add_devices()); - } -} - -void GetDeviceTypesInfo(const PbCommand& command, PbResult& result) -{ - PbDeviceTypesInfo *device_types_info = new PbDeviceTypesInfo(); - GetAllDeviceTypeProperties(*device_types_info); - - result.set_allocated_device_types_info(device_types_info); -} - -void GetServerInfo(PbResult& result) -{ - PbServerInfo *server_info = new PbServerInfo(); - - server_info->set_major_version(rascsi_major_version); - server_info->set_minor_version(rascsi_minor_version); - server_info->set_patch_version(rascsi_patch_version); - GetLogLevels(*server_info); - server_info->set_current_log_level(current_log_level); - GetAllDeviceTypeProperties(*server_info->mutable_device_types_info()); - GetAvailableImages(*server_info); - PbNetworkInterfacesInfo * network_interfaces_info = new PbNetworkInterfacesInfo(); - server_info->set_allocated_network_interfaces_info(network_interfaces_info); - GetNetworkInterfacesInfo(*network_interfaces_info); - GetDevices(*server_info); - for (int id : reserved_ids) { - server_info->add_reserved_ids(id); - } - - result.set_allocated_server_info(server_info); -} - bool SetDefaultImageFolder(const string& f) { string folder = f; @@ -777,10 +527,18 @@ string SetReservedIds(const string& ids) return ""; } -bool IsValidFilename(const string& filename) +bool IsValidSrcFilename(const string& filename) { + // Source file must exist and must be a regular file or a symlink struct stat st; - return stat(filename.c_str(), &st) || !S_ISREG(st.st_mode); + return !stat(filename.c_str(), &st) && (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode)); +} + +bool IsValidDstFilename(const string& filename) +{ + // Destination file must not yet exist + struct stat st; + return stat(filename.c_str(), &st); } bool CreateImage(int fd, const PbCommand& command) @@ -789,17 +547,20 @@ bool CreateImage(int fd, const PbCommand& command) if (filename.empty()) { return ReturnStatus(fd, false, "Can't create image file: Missing image filename"); } - - if (!IsValidFilename(filename)) { - return ReturnStatus(fd, false, "Can't create image file: '" + filename + "': Invalid filename"); + if (filename.find('/') != string::npos) { + return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Filename must not contain a path"); + } + filename = default_image_folder + "/" + filename; + if (!IsValidDstFilename(filename)) { + return ReturnStatus(fd, false, "Can't create image file: '" + filename + "': File already exists"); } - string size = GetParam(command, "size"); + const string size = GetParam(command, "size"); if (size.empty()) { return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Missing image size"); } - string permission = GetParam(command, "read_only"); + const string permission = GetParam(command, "read_only"); if (permission.empty()) { return ReturnStatus(fd, false, "Can't create image file'" + filename + "': Missing read-only flag"); } @@ -811,12 +572,6 @@ bool CreateImage(int fd, const PbCommand& command) int permissions = !strcasecmp(permission.c_str(), "true") ? S_IRUSR | S_IRGRP | S_IROTH : S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; - if (filename.find('/') != string::npos) { - return ReturnStatus(fd, false, "Can't create image file '" + filename + "': Filename must not contain a path"); - } - - filename = default_image_folder + "/" + filename; - off_t len; try { len = stoul(size); @@ -866,8 +621,8 @@ bool DeleteImage(int fd, const PbCommand& command) return ReturnStatus(fd, false, "Missing image filename"); } - if (!IsValidFilename(filename)) { - return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': Invalid filename"); + if (!IsValidDstFilename(filename)) { + return ReturnStatus(fd, false, "Can't delete image file '" + filename + "': File already exists"); } if (filename.find('/') != string::npos) { @@ -901,33 +656,24 @@ bool RenameImage(int fd, const PbCommand& command) if (from.empty()) { return ReturnStatus(fd, false, "Can't rename image file: Missing source filename"); } + if (from.find('/') != string::npos) { + return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path"); + } + from = default_image_folder + "/" + from; + if (!IsValidSrcFilename(from)) { + return ReturnStatus(fd, false, "Can't rename image file: '" + from + "': Invalid name or type"); + } string to = GetParam(command, "to"); if (to.empty()) { return ReturnStatus(fd, false, "Can't rename image file '" + from + "': Missing destination filename"); } - - if (!IsValidFilename(from)) { - return ReturnStatus(fd, false, "Can't rename image file: '" + from + "': Invalid filename"); - } - - if (!IsValidFilename(to)) { - return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': Invalid filename"); - } - - if (from.find('/') != string::npos) { - return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path"); - } if (to.find('/') != string::npos) { return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path"); } - - from = default_image_folder + "/" + from; to = default_image_folder + "/" + to; - - struct stat st; - if (!stat(to.c_str(), &st)) { - return ReturnStatus(fd, false, "Image file '" + to + "' already exists"); + if (!IsValidDstFilename(to)) { + return ReturnStatus(fd, false, "Can't rename image file '" + from + "' to '" + to + "': File already exists"); } if (rename(from.c_str(), to.c_str())) { @@ -945,42 +691,33 @@ bool CopyImage(int fd, const PbCommand& command) if (from.empty()) { return ReturnStatus(fd, false, "Can't copy image file: Missing source filename"); } + if (from.find('/') != string::npos) { + return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path"); + } + from = default_image_folder + "/" + from; + if (!IsValidSrcFilename(from)) { + return ReturnStatus(fd, false, "Can't copy image file: '" + from + "': Invalid name or type"); + } string to = GetParam(command, "to"); if (to.empty()) { return ReturnStatus(fd, false, "Can't copy image file '" + from + "': Missing destination filename"); } - - if (!IsValidFilename(from)) { - return ReturnStatus(fd, false, "Can't copy image file: '" + from + "': Invalid filename"); - } - - if (!IsValidFilename(to)) { - return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': Invalid filename"); - } - - if (from.find('/') != string::npos) { - return ReturnStatus(fd, false, "The source filename '" + from + "' must not contain a path"); - } if (to.find('/') != string::npos) { return ReturnStatus(fd, false, "The destination filename '" + to + "' must not contain a path"); } - - from = default_image_folder + "/" + from; to = default_image_folder + "/" + to; - - struct stat st_dst; - if (!stat(to.c_str(), &st_dst)) { - return ReturnStatus(fd, false, "Image file '" + to + "' already exists"); + if (!IsValidDstFilename(to)) { + return ReturnStatus(fd, false, "Can't copy image file '" + from + "' to '" + to + "': File already exists"); } - struct stat st_src; - if (lstat(from.c_str(), &st_src)) { + struct stat st; + if (lstat(from.c_str(), &st)) { return ReturnStatus(fd, false, "Can't access source image file '" + from + "': " + string(strerror(errno))); } // Symbolic links need a special handling - if ((st_src.st_mode & S_IFMT) == S_IFLNK) { + if ((st.st_mode & S_IFMT) == S_IFLNK) { if (symlink(filesystem::read_symlink(from).c_str(), to.c_str())) { return ReturnStatus(fd, false, "Can't copy symlink '" + from + "': " + string(strerror(errno))); } @@ -995,14 +732,14 @@ bool CopyImage(int fd, const PbCommand& command) return ReturnStatus(fd, false, "Can't open source image file '" + from + "': " + string(strerror(errno))); } - int fd_dst = open(to.c_str(), O_WRONLY | O_CREAT, st_src.st_mode); + int fd_dst = open(to.c_str(), O_WRONLY | O_CREAT, st.st_mode); if (fd_dst == -1) { close(fd_src); return ReturnStatus(fd, false, "Can't open destination image file '" + to + "': " + string(strerror(errno))); } - if (sendfile(fd_dst, fd_src, 0, st_src.st_size) == -1) { + if (sendfile(fd_dst, fd_src, 0, st.st_size) == -1) { close(fd_dst); close(fd_src); @@ -1023,16 +760,13 @@ bool SetImagePermissions(int fd, const PbCommand& command) if (filename.empty()) { return ReturnStatus(fd, false, "Missing image filename"); } - - if (!IsValidFilename(filename)) { - return ReturnStatus(fd, false, "Can't modify image file '" + filename + "': Invalid filename"); - } - if (filename.find('/') != string::npos) { return ReturnStatus(fd, false, "The image filename '" + filename + "' must not contain a path"); } - filename = default_image_folder + "/" + filename; + if (!IsValidSrcFilename(filename)) { + return ReturnStatus(fd, false, "Can't modify image file '" + filename + "': Invalid name or type"); + } bool protect = command.operation() == PROTECT_IMAGE; @@ -1081,17 +815,12 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry } string filename = GetParam(pb_device, "file"); - string ext; - size_t separator = filename.rfind('.'); - if (separator != string::npos) { - ext = filename.substr(separator + 1); - } - // Create a new device, based upon the provided type or filename extension - Device *device = device_factory.CreateDevice(type, filename, ext); + // Create a new device, based on the provided type or filename + Device *device = device_factory.CreateDevice(type, filename); if (!device) { if (type == UNDEFINED) { - return ReturnStatus(fd, false, "No device type provided for unknown file extension '" + ext + "'"); + return ReturnStatus(fd, false, "Device type required for unknown extension of file '" + filename + "'"); } else { return ReturnStatus(fd, false, "Unknown device type " + PbDeviceType_Name(type)); @@ -1708,7 +1437,7 @@ bool ParseArgument(int argc, char* argv[], int& port) // Display and log the device list PbServerInfo server_info; - GetDevices(server_info); + response_helper.GetDevices(server_info, devices, default_image_folder); const list& devices = { server_info.devices().devices().begin(), server_info.devices().devices().end() }; const string device_list = ListDevices(devices); LogDevices(device_list); @@ -1814,13 +1543,12 @@ static void *MonThread(void *param) LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); PbResult result; - result.set_status(true); - GetDevicesInfo(command, result); + response_helper.GetDevicesInfo(result, command, devices, default_image_folder, UnitNum); SerializeMessage(fd, result); - const list& devices ={ result.device_info().devices().begin(), result.device_info().devices().end() }; // For backwards compatibility: Log device list if information on all devices was requested. - if (command.devices_size() == 0) { + if (!command.devices_size()) { + const list& devices = { result.device_info().devices().begin(), result.device_info().devices().end() }; LogDevices(ListDevices(devices)); } break; @@ -1830,8 +1558,7 @@ static void *MonThread(void *param) LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); PbResult result; - result.set_status(true); - GetDeviceTypesInfo(command, result); + result.set_allocated_device_types_info(response_helper.GetDeviceTypesInfo(result, command)); SerializeMessage(fd, result); break; } @@ -1841,8 +1568,8 @@ static void *MonThread(void *param) LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); PbResult result; - result.set_status(true); - GetServerInfo(result); + result.set_allocated_server_info(response_helper.GetServerInfo( + result, devices, reserved_ids, default_image_folder, current_log_level)); SerializeMessage(fd, result); break; } @@ -1850,11 +1577,8 @@ static void *MonThread(void *param) case IMAGE_FILES_INFO: { LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); - PbImageFilesInfo *image_files_info = new PbImageFilesInfo(); - GetAvailableImages(*image_files_info); PbResult result; - result.set_status(true); - result.set_allocated_image_files_info(image_files_info); + result.set_allocated_image_files_info(response_helper.GetAvailableImages(result, default_image_folder)); SerializeMessage(fd, result); break; } @@ -1862,11 +1586,8 @@ static void *MonThread(void *param) case NETWORK_INTERFACES_INFO: { LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str()); - PbNetworkInterfacesInfo *network_interfaces_info = new PbNetworkInterfacesInfo(); - GetNetworkInterfacesInfo(*network_interfaces_info); PbResult result; - result.set_status(true); - result.set_allocated_network_interfaces_info(network_interfaces_info); + result.set_allocated_network_interfaces_info(response_helper.GetNetworkInterfacesInfo(result)); SerializeMessage(fd, result); break; } @@ -1923,13 +1644,6 @@ int main(int argc, char* argv[]) } } - log_levels.push_back("trace"); - log_levels.push_back("debug"); - log_levels.push_back("info"); - log_levels.push_back("warn"); - log_levels.push_back("err"); - log_levels.push_back("critical"); - log_levels.push_back("off"); SetLogLevel("info"); // Create a thread-safe stdout logger to process the log messages diff --git a/src/raspberrypi/rascsi_interface.proto b/src/raspberrypi/rascsi_interface.proto index 2264d91e..107750cf 100644 --- a/src/raspberrypi/rascsi_interface.proto +++ b/src/raspberrypi/rascsi_interface.proto @@ -181,9 +181,11 @@ message PbDeviceTypesInfo { // The image file data message PbImageFile { string name = 1; - bool read_only = 2; + // The assumed device type, based on the filename extension + PbDeviceType type = 2; + bool read_only = 3; // The file size in bytes, 0 for block devices - int64 size = 3; + int64 size = 4; } // The default image folder and the image files it contains diff --git a/src/raspberrypi/rasctl.cpp b/src/raspberrypi/rasctl.cpp index 2effa75b..4de7e7d9 100644 --- a/src/raspberrypi/rasctl.cpp +++ b/src/raspberrypi/rasctl.cpp @@ -265,9 +265,12 @@ void DisplayImageFiles(const list image_files, const string& defaul cout << "Available image files:" << endl; for (const auto& file : files) { - cout << " " << file.name() << " (" << file.size() << " bytes)"; + cout << " " << file.name() << " " << file.size() << " bytes"; if (file.read_only()) { - cout << ", read-only"; + cout << " read-only"; + } + if (file.type() != UNDEFINED) { + cout << " " << PbDeviceType_Name(file.type()); } cout << endl; }