Fixed issue with protected devices/media, added IMAGE_FILE_INFO command (#288)

* Fixed issue with attaching protected devices

* Fixed same issue when inserting a file

* Added IMAGE_FILE_INFO command

* rasctl output update

* Added file mode and type

* Datat type update

* Reverted change

* Update web ui for protobuf changes

Co-authored-by: Daniel Markstedt <markstedt@gmail.com>
This commit is contained in:
Uwe Seimet 2021-09-30 19:22:57 +02:00 committed by GitHub
parent 998cc2d66f
commit 969220565b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 154 additions and 80 deletions

View File

@ -9,6 +9,7 @@ rasctl \- Sends management commands to the rascsi process
\fB\-m\fR |
\fB\-s\fR |
\fB\-T\fR |
[\fB\-E\fR \fIFILENAME\fR]
[\fB\-F\fR \fIIMAGE_FOLDER\fR]
[\fB\-C\fR \fIFILENAME:FILESIZE\fR]
[\fB\-x\fR \fICURRENT_NAME:NEW_NAME\fR]
@ -39,6 +40,9 @@ Note: The command and type arguments are case insensitive. Only the first letter
.BR \-C\fI " "\fIFILENAME:FILESIZE
Create an image file in the default image folder with the specified name and size in bytes.
.TP
.BR \-E\fI " " \fIFILENAME
Display information on an image file.
.TP
.BR \-F\fI " "\fIIMAGE_FOLDER
Set the default image folder.
.TP

View File

@ -6,20 +6,21 @@ NAME
rasctl - Sends management commands to the rascsi process
SYNOPSIS
rasctl -L | -e | -l | -m | -s | -T | [-F IMAGE_FOLDER] [-C FILE
NAME:FILESIZE] [-x CURRENT_NAME:NEW_NAME] [-R CURRENT_NAME:NEW_NAME]
[-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-v] -i ID [-c
CMD] [-f FILE|PARAM] [-n NAME] [-t TYPE] [-u UNIT]
rasctl -L | -e | -l | -m | -s | -T | [-E FILENAME] [-F IMAGE_FOLDER]
[-C FILENAME:FILESIZE] [-x CURRENT_NAME:NEW_NAME] [-R CUR
RENT_NAME:NEW_NAME] [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RE
SERVED_IDS] [-v] -i ID [-c CMD] [-f FILE|PARAM] [-n NAME] [-t TYPE] [-u
UNIT]
DESCRIPTION
rasctl Sends commands to the rascsi process to make configuration ad
rasctl Sends commands to the rascsi process to make configuration ad
justments at runtime or to check the status of the devices.
Either the -i or -l option should be specified at one time. Not both.
You do NOT need root privileges to use rasctl.
Note: The command and type arguments are case insensitive. Only the
Note: The command and type arguments are case insensitive. Only the
first letter of the command/type is evaluated by the tool.
OPTIONS
@ -27,11 +28,14 @@ OPTIONS
Create an image file in the default image folder with the speci
fied name and size in bytes.
-E FILENAME
Display information on an image file.
-F IMAGE_FOLDER
Set the default image folder.
-L LOG_LEVEL
Set the rascsi log level (trace, debug, info, warn, err, criti
Set the rascsi log level (trace, debug, info, warn, err, criti
cal, off).
-h HOST
@ -39,13 +43,13 @@ OPTIONS
-e List all images files in the default image folder.
-N Lists all available network interfaces provided that they are
-N Lists all available network interfaces provided that they are
up.
-l List all of the devices that are currently being emulated by
-l List all of the devices that are currently being emulated by
RaSCSI, as well as their current status.
-m List all file extensions recognized by RaSCSI and the device
-m List all file extensions recognized by RaSCSI and the device
types they map to.
-R CURRENT_NAME:NEW_NAME
@ -57,7 +61,7 @@ OPTIONS
-r RESERVED_IDS
Comma-separated list of IDs to reserve.
-s Display server-side settings like available images or supported
-s Display server-side settings like available images or supported
device types.
-T Display all device types and their properties.
@ -77,7 +81,7 @@ OPTIONS
d(etach): Detach disk
i(nsert): Insert media (removable media devices only)
e(ject): Eject media (removable media devices only)
p(rotect): Write protect the medium (not for CD-ROMs, which
p(rotect): Write protect the medium (not for CD-ROMs, which
are always read-only)
u(nprotect): Remove write protection from the medium (not for
CD-ROMs, which are always read-only)
@ -87,18 +91,18 @@ OPTIONS
-b BLOCK_SIZE
The optional block size. For SCSI drives 512, 1024, 2048 or 4096
bytes, default size is 512 bytes. For SASI drives 256 or 1024
bytes, default size is 512 bytes. For SASI drives 256 or 1024
bytes, default is 256 bytes.
-f FILE|PARAM
Device-specific: Either a path to a disk image file, or a param
eter for a non-disk device. See the rascsi(1) man page for per
eter for a non-disk device. See the rascsi(1) man page for per
mitted file types.
-t TYPE
Specifies the device type. This type overrides the type derived
Specifies the device type. This type overrides the type derived
from the file extension of the specified image. See the
rascsi(1) man page for the available device types. For some
rascsi(1) man page for the available device types. For some
types there are shortcuts (only the first letter is required):
hd: SCSI hard disk drive
rm: SCSI removable media drive
@ -108,16 +112,16 @@ OPTIONS
daynaport: DaynaPORT network adapter
-n VENDOR:PRODUCT:REVISION
The vendor, product and revision for the device, to be returned
The vendor, product and revision for the device, to be returned
with the INQUIRY data. A complete set of name components must be
provided. VENDOR may have up to 8, PRODUCT up to 16, REVISION up
to 4 characters. Padding with blanks to the maxium length is au
tomatically applied. Once set the name of a device cannot be
tomatically applied. Once set the name of a device cannot be
changed.
-u UNIT
Unit number (0 or 1). This will default to 0. This option is
only used when there are multiple SCSI devices on a shared SCSI
Unit number (0 or 1). This will default to 0. This option is
only used when there are multiple SCSI devices on a shared SCSI
controller. (This is not common)
EXAMPLES

View File

@ -131,10 +131,10 @@ void ProtobufResponseHandler::GetDevice(const Device *device, PbDevice *pb_devic
}
}
void ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string& filename, const string& image_folder)
bool ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string& filename, const string& image_folder)
{
image_file->set_name(filename);
if (!filename.empty()) {
image_file->set_name(filename);
image_file->set_type(device_factory.GetTypeForFile(filename));
string f = filename[0] == '/' ? filename : image_folder + "/" + filename;
@ -144,8 +144,11 @@ void ProtobufResponseHandler::GetImageFile(PbImageFile *image_file, const string
struct stat st;
if (!stat(f.c_str(), &st)) {
image_file->set_size(st.st_size);
return true;
}
}
return false;
}
PbImageFilesInfo *ProtobufResponseHandler::GetAvailableImages(PbResult& result, const string& image_folder)

View File

@ -30,6 +30,7 @@ public:
static ProtobufResponseHandler& instance();
bool GetImageFile(PbImageFile *, const string&, const string&);
PbImageFilesInfo *GetAvailableImages(PbResult&, const string&);
void GetDevices(PbServerInfo&, const vector<Device *>&, const string&);
void GetDevicesInfo(PbResult&, const PbCommand&, const vector<Device *>&, const string&, int);
@ -49,6 +50,5 @@ private:
void GetAllDeviceTypeProperties(PbDeviceTypesInfo&);
void GetDeviceTypeProperties(PbDeviceTypesInfo&, PbDeviceType);
void GetAvailableImages(PbResult& result, PbServerInfo&, const string&);
void GetImageFile(PbImageFile *, const string&, const string&);
void GetLogLevels(PbServerInfo&);
};

View File

@ -839,11 +839,6 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
device->SetId(id);
device->SetLun(unit);
// Only non read-only devices support protect/unprotect
if (!device->IsReadOnly()) {
device->SetProtected(pb_device.protected_());
}
try {
if (!pb_device.vendor().empty()) {
device->SetVendor(pb_device.vendor());
@ -859,19 +854,6 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
return ReturnStatus(fd, false, e.getmsg());
}
if (pb_device.block_size()) {
Disk *disk = dynamic_cast<Disk *>(device);
if (disk && disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(pb_device.block_size())) {
error << "Invalid block size " << pb_device.block_size() << " bytes";
return ReturnStatus(fd, false, error);
}
}
else {
return ReturnStatus(fd, false, "Block size is not configurable for device type " + PbDeviceType_Name(type));
}
}
// File check (type is HD, for removable media drives, CD and MO the medium (=file) may be inserted later)
if (file_support && !device->IsRemovable() && filename.empty()) {
delete device;
@ -912,6 +894,26 @@ bool Attach(int fd, const PbDeviceDefinition& pb_device, Device *map[], bool dry
file_support->ReserveFile(filepath, device->GetId(), device->GetLun());
}
// The operations below must not be executed before Open() because Open() overrides some settings
if (pb_device.block_size()) {
Disk *disk = dynamic_cast<Disk *>(device);
if (disk && disk->IsSectorSizeConfigurable()) {
if (!disk->SetConfiguredSectorSize(pb_device.block_size())) {
error << "Invalid block size " << pb_device.block_size() << " bytes";
return ReturnStatus(fd, false, error);
}
}
else {
return ReturnStatus(fd, false, "Block size is not configurable for device type " + PbDeviceType_Name(type));
}
}
// Only non read-only devices support protect/unprotect
if (device->IsProtectable() && !device->IsReadOnly()) {
device->SetProtected(pb_device.protected_());
}
// Stop the dry run here, before permanently modifying something
if (dryRun) {
delete device;
@ -1027,7 +1029,6 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
}
FileSupport *file_support = dynamic_cast<FileSupport *>(device);
try {
try {
file_support->Open(filepath);
@ -1041,9 +1042,14 @@ bool Insert(int fd, const PbDeviceDefinition& pb_device, Device *device, bool dr
catch(const io_exception& e) {
return ReturnStatus(fd, false, "Tried to open an invalid file '" + initial_filename + "': " + e.getmsg());
}
file_support->ReserveFile(filepath, device->GetId(), device->GetLun());
// Only non read-only devices support protect/unprotect.
// This operation must not be executed before Open() because Open() overrides some settings.
if (device->IsProtectable() && !device->IsReadOnly()) {
device->SetProtected(pb_device.protected_());
}
return true;
}
@ -1574,7 +1580,7 @@ static void *MonThread(void *param)
break;
}
case IMAGE_FILES_INFO: {
case DEFAULT_IMAGE_FILES_INFO: {
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
PbResult result;
@ -1583,6 +1589,29 @@ static void *MonThread(void *param)
break;
}
case IMAGE_FILE_INFO: {
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());
string filename = GetParam(command, "file");
if (filename.empty()) {
ReturnStatus(fd, false, "Can't get image file info: Missing filename");
}
else {
PbResult result;
PbImageFile* image_file = new PbImageFile();
bool status = response_helper.GetImageFile(image_file, filename, default_image_folder);
if (status) {
result.set_status(true);
result.set_allocated_image_file_info(image_file);
SerializeMessage(fd, result);
}
else {
ReturnStatus(fd, false, "Can't get image file info for '" + filename + "'");
}
}
break;
}
case NETWORK_INTERFACES_INFO: {
LOGTRACE("Received %s command", PbOperation_Name(command.operation()).c_str());

View File

@ -72,65 +72,70 @@ enum PbOperation {
// Device properties by device type
DEVICE_TYPES_INFO = 12;
// Gets information on available image files. A lightweight alternative to getting the complete server info.
IMAGE_FILES_INFO = 13;
// Gets information on available image files in the default image folder.
DEFAULT_IMAGE_FILES_INFO = 13;
// Gets information on an image file (not necessarily in the default image folder) based on an absolute path.
// Parameters:
// "file": The filename. Either an absolute path or a path relative to the default image folder.
IMAGE_FILE_INFO = 14;
// Gets the names of the available network interfaces. Only lists interfaces that are up.
NETWORK_INTERFACES_INFO = 14;
NETWORK_INTERFACES_INFO = 15;
// Gets the mapping of extensions to device types
MAPPING_INFO = 15;
MAPPING_INFO = 16;
// Set the default folder for image files.
// Parameters:
// "folder": The default folder name.
DEFAULT_FOLDER = 16;
DEFAULT_FOLDER = 17;
// Set server log level.
// Parameters:
// "level": The new log level
LOG_LEVEL = 17;
LOG_LEVEL = 18;
// Block IDs from being used, usually the IDs of the initiators (computers) in the SCSI chain.
// Parameters:
// "ids": A comma-separated list of IDs to reserve, or an empty string in order not to reserve any ID.
RESERVE = 18;
RESERVE = 19;
// Create an image file. The image file must not yet exist.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
// "size": The file size in bytes, must be a multiple of 512
// "read_only": "true" (case-insensitive) in order to create a read-only file, otherwise "false"
CREATE_IMAGE = 19;
CREATE_IMAGE = 20;
// Delete an image file.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
DELETE_IMAGE = 20;
DELETE_IMAGE = 21;
// Rename an image file.
// Parameters:
// "from": The old filename, relative to the default image folder. It must not contain a slash.
// "to": The new filename, relative to the default image folder. It must not contain a slash.
// The new filename must not yet exist.
RENAME_IMAGE = 21;
RENAME_IMAGE = 22;
// Copy an image file.
// Parameters:
// "from": The source filename, relative to the default image folder. It must not contain a slash.
// "to": The destination filename, relative to the default image folder. It must not contain a slash.
// The destination filename must not yet exist.
COPY_IMAGE = 22;
COPY_IMAGE = 23;
// Write-protect an image file.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
PROTECT_IMAGE = 23;
PROTECT_IMAGE = 24;
// Make an image file writable.
// Parameters:
// "file": The filename, relative to the default image folder. It must not contain a slash.
UNPROTECT_IMAGE = 24;
UNPROTECT_IMAGE = 25;
}
// The supported file extensions mapped to their respective device types
@ -189,9 +194,9 @@ message PbImageFile {
string name = 1;
// 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 = 4;
uint64 size = 3;
bool read_only = 4;
}
// The default image folder and the image files it contains
@ -269,12 +274,14 @@ message PbResult {
PbDevices device_info = 4;
// The result of a DEVICE_TYPES_INFO command
PbDeviceTypesInfo device_types_info = 5;
// The result of an IMAGE_FILES_INFO command
// The result of a DEFAULT_IMAGE_FILES_INFO command
PbImageFilesInfo image_files_info = 6;
// The result of an IMAGE_FILE_INFO command
PbImageFile image_file_info = 7;
// The result of a NETWORK_INTERFACES_INFO command
PbNetworkInterfacesInfo network_interfaces_info = 7;
PbNetworkInterfacesInfo network_interfaces_info = 8;
// The result of an MAPPING_INFO command
PbMappingInfo mapping_info = 8;
PbMappingInfo mapping_info = 9;
}
}

View File

@ -234,6 +234,19 @@ void DisplayDeviceTypesInfo(const PbDeviceTypesInfo& device_types_info)
}
}
void DisplayImageFile(const PbImageFile& image_file_info)
{
cout << image_file_info.name() << " " << image_file_info.size() << " bytes";
if (image_file_info.read_only()) {
cout << " read-only";
}
if (image_file_info.type() != UNDEFINED) {
cout << " " << PbDeviceType_Name(image_file_info.type());
}
cout << endl;
}
void DisplayImageFiles(const PbImageFilesInfo& image_files_info)
{
const list<PbImageFile> image_files = { image_files_info.image_files().begin(), image_files_info.image_files().end() };
@ -249,14 +262,8 @@ void DisplayImageFiles(const PbImageFilesInfo& image_files_info)
cout << "Available image files:" << endl;
for (const auto& file : files) {
cout << " " << file.name() << " " << file.size() << " bytes";
if (file.read_only()) {
cout << " read-only";
}
if (file.type() != UNDEFINED) {
cout << " " << PbDeviceType_Name(file.type());
}
cout << endl;
cout << " ";
DisplayImageFile(file);
}
}
}
@ -469,7 +476,7 @@ void CommandServerInfo(PbCommand& command, const string& hostname, int port)
}
}
void CommandImageFilesInfo(const PbCommand& command, const string& hostname, int port)
void CommandDefaultImageFilesInfo(const PbCommand& command, const string& hostname, int port)
{
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
@ -477,6 +484,16 @@ void CommandImageFilesInfo(const PbCommand& command, const string& hostname, int
DisplayImageFiles(result.image_files_info());
}
void CommandImageFileInfo(PbCommand& command, const string& hostname, int port, const string& filename)
{
AddParam(command, "file", filename);
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
DisplayImageFile(result.image_file_info());
}
void CommandNetworkInterfacesInfo(const PbCommand& command, const string&hostname, int port)
{
PbResult result;
@ -573,7 +590,7 @@ int main(int argc, char* argv[])
cerr << "Usage: " << argv[0] << " -i ID [-u UNIT] [-c CMD] [-C FILE] [-t TYPE] [-b BLOCK_SIZE] [-n NAME] [-f FILE|PARAM] ";
cerr << "[-F IMAGE_FOLDER] [-L LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] ";
cerr << "[-C FILENAME:FILESIZE] [-w FILENAME] [-R CURRENT_NAME:NEW_NAME] [-x CURRENT_NAME:NEW_NAME] ";
cerr << "[-L] [-l] [-m] [-s] [-v] [-y]" << endl;
cerr << "[-e] [-E FILENAME] [-l] [-L] [-m] [-s] [-v] [-y]" << endl;
cerr << " where ID := {0-7}" << endl;
cerr << " UNIT := {0|1}, default is 0" << endl;
cerr << " CMD := {attach|detach|insert|eject|protect|unprotect|show}" << endl;
@ -605,11 +622,12 @@ int main(int argc, char* argv[])
string default_folder;
string reserved_ids;
string image_params;
string filename;
bool list = false;
opterr = 1;
int opt;
while ((opt = getopt(argc, argv, "elmsvNTD:L:R:a:b:c:f:h:i:n:p:r:t:u:x:C:F:L:")) != -1) {
while ((opt = getopt(argc, argv, "elmsvNTD:L:R:a:b:c:f:h:i:n:p:r:t:u:x:C:E:F:L:")) != -1) {
switch (opt) {
case 'i':
device->set_id(optarg[0] - '0');
@ -641,13 +659,18 @@ int main(int argc, char* argv[])
}
break;
case 'F':
command.set_operation(DEFAULT_FOLDER);
default_folder = optarg;
case 'E':
command.set_operation(IMAGE_FILE_INFO);
filename = optarg;
break;
case 'e':
command.set_operation(IMAGE_FILES_INFO);
command.set_operation(DEFAULT_IMAGE_FILES_INFO);
break;
case 'F':
command.set_operation(DEFAULT_FOLDER);
default_folder = optarg;
break;
case 'f':
@ -799,8 +822,12 @@ int main(int argc, char* argv[])
CommandServerInfo(command, hostname, port);
exit(EXIT_SUCCESS);
case IMAGE_FILES_INFO:
CommandImageFilesInfo(command, hostname, port);
case DEFAULT_IMAGE_FILES_INFO:
CommandDefaultImageFilesInfo(command, hostname, port);
exit(EXIT_SUCCESS);
case IMAGE_FILE_INFO:
CommandImageFileInfo(command, hostname, port, filename);
exit(EXIT_SUCCESS);
case NETWORK_INTERFACES_INFO:

View File

@ -13,7 +13,7 @@ import rascsi_interface_pb2 as proto
def list_files():
command = proto.PbCommand()
command.operation = proto.PbOperation.IMAGE_FILES_INFO
command.operation = proto.PbOperation.DEFAULT_IMAGE_FILES_INFO
data = send_pb_command(command.SerializeToString())
result = proto.PbResult()