Initial image file creation

This commit is contained in:
Uwe Seimet 2021-09-15 10:47:51 +02:00
parent 8a3642bf9a
commit 8172c9fdfe
5 changed files with 132 additions and 25 deletions

View File

@ -28,8 +28,11 @@ Note: The command and type arguments are case insensitive. Only the first letter
.SH OPTIONS .SH OPTIONS
.TP .TP
.BR \-a\fI " "\fIFILENAME:FILESIZE
Create a disk image file with the specified name and size in bytes.
.TP
.BR \-g\fI " "\fILOG_LEVEL .BR \-g\fI " "\fILOG_LEVEL
The rascsi log level to set (trace, debug, info, warn, err, critical, off) Set the rascsi log level (trace, debug, info, warn, err, critical, off).
.TP .TP
.BR \-h\fI " " \fIHOST .BR \-h\fI " " \fIHOST
The rascsi host to connect to, default is 'localhost'. The rascsi host to connect to, default is 'localhost'.

View File

@ -1,32 +1,39 @@
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!! !! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating !! ------ The native file is rasctl.1. Re-run 'make docs' after updating\n\n
rascsi(1) General Commands Manual rascsi(1)
rascsi(1) General Commands Manual rascsi(1)
NAME NAME
rasctl - Sends management commands to the rascsi process rasctl - Sends management commands to the rascsi process
SYNOPSIS SYNOPSIS
rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS] [-v] -i ID [-c CMD] [-f FILE] [-n NAME] [-t TYPE] [-u UNIT] rasctl -l | -s | [-g LOG_LEVEL] [-h HOST] [-p PORT] [-r RESERVED_IDS]
[-v] -i ID [-c CMD] [-f FILE] [-n NAME] [-t TYPE] [-u UNIT]
DESCRIPTION DESCRIPTION
rasctl Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices. 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. Either the -i or -l option should be specified at one time. Not both.
You do NOT need root privileges to use rasctl. You do NOT need root privileges to use rasctl.
Note: The command and type arguments are case insensitive. Only the first letter of the command/type is evaluated by the tool. Note: The command and type arguments are case insensitive. Only the
first letter of the command/type is evaluated by the tool.
OPTIONS OPTIONS
-a FILENAME:FILESIZE
Create a disk image file with the specified name and size in
bytes.
-g LOG_LEVEL -g LOG_LEVEL
The rascsi log level to set (trace, debug, info, warn, err, critical, off) Set the rascsi log level (trace, debug, info, warn, err, criti
cal, off).
-h HOST -h HOST
The rascsi host to connect to, default is 'localhost'. The rascsi host to connect to, default is 'localhost'.
-l List all of the devices that are currently being emulated by RaSCSI, as well as their current status. -l List all of the devices that are currently being emulated by
RaSCSI, as well as their current status.
-p PORT -p PORT
The rascsi port to connect to, default is 6868. The rascsi port to connect to, default is 6868.
@ -34,7 +41,8 @@ OPTIONS
-r RESERVED_IDS -r RESERVED_IDS
Comma-separated list of IDs to reserve. Comma-separated list of IDs to reserve.
-s Display server-side settings like available images or supported device types. -s Display server-side settings like available images or supported
device types.
-v Display the rascsi version. -v Display the rascsi version.
@ -45,21 +53,28 @@ OPTIONS
d(etach): Detach disk d(etach): Detach disk
i(nsert): Insert media (removable media devices only) i(nsert): Insert media (removable media devices only)
e(ject): Eject media (removable media devices only) e(ject): Eject media (removable media devices only)
p(rotect): Write protect the medium (not for CD-ROMs, which are always read-only) p(rotect): Write protect the medium (not for CD-ROMs, which
u(nprotect): Remove write protection from the medium (not for CD-ROMs, which are always read-only) are always read-only)
u(nprotect): Remove write protection from the medium (not for
CD-ROMs, which are always read-only)
s(how): Display device information s(how): Display device information
eject, protect and unprotect are idempotent. eject, protect and unprotect are idempotent.
-b BLOCK_SIZE -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 is 256 bytes. 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 is 256 bytes.
-f FILE -f FILE
Path to the disk image file. See the rascsi(1) man page for allowable file types. Path to the disk image file. See the rascsi(1) man page for al
lowable file types.
-t TYPE -t TYPE
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. Specifies the device type. This type overrides the type derived
For some types there are shortcuts (only the first letter is required): from the file extension of the specified image. See the
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 hd: SCSI hard disk drive
rm: SCSI removable media drive rm: SCSI removable media drive
cd: CD-ROM cd: CD-ROM
@ -68,11 +83,17 @@ OPTIONS
daynaport: DaynaPORT network adapter daynaport: DaynaPORT network adapter
-n VENDOR:PRODUCT:REVISION -n VENDOR:PRODUCT:REVISION
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 The vendor, product and revision for the device, to be returned
up to 16, REVISION up to 4 characters. Padding with blanks to the maxium length is automatically applied. Once set the name of a device cannot be changed. 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
changed.
-u UNIT -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 controller. (This is not common) 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 EXAMPLES
Show a listing of all of the SCSI devices and their current status. Show a listing of all of the SCSI devices and their current status.
@ -85,12 +106,14 @@ EXAMPLES
| 0 | 1 | SCHD | /home/pi/harddisk.hda | 0 | 1 | SCHD | /home/pi/harddisk.hda
+----+----+------+------------------------------------- +----+----+------+-------------------------------------
Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the contents of the file system image "HDIIMAGE0.HDS". Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with
the contents of the file system image "HDIIMAGE0.HDS".
rasctl -i 0 -f HDIIMAGE0.HDS rasctl -i 0 -f HDIIMAGE0.HDS
SEE ALSO SEE ALSO
rascsi(1) scsimon(1) rascsi(1) scsimon(1)
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/> Full documentation is available at:
<https://www.github.com/akuker/RASCSI/wiki/>
rascsi(1) rascsi(1)

View File

@ -705,6 +705,55 @@ string SetReservedIds(const list<string>& ids_to_reserve)
return ""; return "";
} }
bool CreateImage(int fd, const PbCommand& command)
{
if (command.params().size() < 2 || command.params().Get(0).empty() || command.params().Get(1).empty()) {
return ReturnStatus(fd, false, "Can't create image file: Missing filename or file size");
}
string filename = command.params().Get(0);
if (filename[0] != '/') {
filename = default_image_folder + "/" + filename;
}
off_t len;
try {
len = stoul(command.params().Get(1));
}
catch(const invalid_argument& e) {
return ReturnStatus(fd, false, "Invalid image file size " + command.params().Get(1));
}
catch(const out_of_range& e) {
return ReturnStatus(fd, false, "Invalid image file size " + command.params().Get(1));
}
if (len < 256) {
ostringstream error;
error << "Invalid image file size " << len;
return ReturnStatus(fd, false, error.str());
}
struct stat st;
if (!stat(filename.c_str(), &st)) {
return ReturnStatus(fd, false, "Image file '" + filename + "' already exists");
}
// Since rascsi is running as root ensure that others can access the file
int image_fd = open(filename.c_str(), O_CREAT|O_WRONLY, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);
if (image_fd == -1) {
return ReturnStatus(fd, false, "Can't create image file '" + filename + "': " + string(strerror(errno)));
}
if (fallocate(image_fd, 0, 0, len) == -1) {
close(image_fd);
return ReturnStatus(fd, false, "Can't allocate space for image file '" + filename + "': " + string(strerror(errno)));
}
close(image_fd);
return ReturnStatus(fd);
}
void DetachAll() void DetachAll()
{ {
Device *map[devices.size()]; Device *map[devices.size()];
@ -1146,11 +1195,14 @@ bool ProcessCmd(const int fd, const PbCommand& command)
const list<string> ids = { command.params().begin(), command.params().end() }; const list<string> ids = { command.params().begin(), command.params().end() };
string invalid_id = SetReservedIds(ids); string invalid_id = SetReservedIds(ids);
if (!invalid_id.empty()) { if (!invalid_id.empty()) {
return ReturnStatus(fd, false,"Invalid ID " + invalid_id + " for " + PbOperation_Name(RESERVE)); return ReturnStatus(fd, false, "Invalid ID " + invalid_id + " for " + PbOperation_Name(RESERVE));
} }
return ReturnStatus(fd); return ReturnStatus(fd);
} }
else if (command.operation() == CREATE_IMAGE) {
return CreateImage(fd, command);
}
const vector<string> params = { command.params().begin(), command.params().end() }; const vector<string> params = { command.params().begin(), command.params().end() };

View File

@ -59,6 +59,10 @@ enum PbOperation {
// IDs blocked from being used, usually the IDs of the initiators (computers) in the SCSI chain. // IDs blocked from being used, usually the IDs of the initiators (computers) in the SCSI chain.
// PbCommand.params contains the list of IDs to reserve, or is empty in order not to reserve any ID. // PbCommand.params contains the list of IDs to reserve, or is empty in order not to reserve any ID.
RESERVE = 14; RESERVE = 14;
// Create an image file. The image file must not yet exist.
// PbCommand.params(0) contains the filename, PbCommand.params(1) contains the file size in bytes.
// If the filename is relative (does not start with a slash) the file is created in the default image folder.
CREATE_IMAGE = 15;
} }
// The properties supported by a device, helping clients to offer a good user experience // The properties supported by a device, helping clients to offer a good user experience

View File

@ -206,6 +206,21 @@ void CommandReserve(const string&hostname, int port, const string& reserved_ids)
SendCommand(hostname.c_str(), port, command, result); SendCommand(hostname.c_str(), port, command, result);
} }
void CommandCreateImage(const string&hostname, int port, const string& image_params)
{
PbCommand command;
command.set_operation(CREATE_IMAGE);
size_t separatorPos = image_params.find(COMPONENT_SEPARATOR);
if (separatorPos != string::npos) {
command.add_params(image_params.substr(0, separatorPos));
command.add_params(image_params.substr(separatorPos + 1));
}
PbResult result;
SendCommand(hostname.c_str(), port, command, result);
}
void CommandDefaultImageFolder(const string& hostname, int port, const string& folder) void CommandDefaultImageFolder(const string& hostname, int port, const string& folder)
{ {
PbCommand command; PbCommand command;
@ -502,11 +517,12 @@ int main(int argc, char* argv[])
string log_level; string log_level;
string default_folder; string default_folder;
string reserved_ids; string reserved_ids;
string image_params;
bool list = false; bool list = false;
opterr = 1; opterr = 1;
int opt; int opt;
while ((opt = getopt(argc, argv, "b:c:d:f:g:h:i:n:p:r:t:u:lsv")) != -1) { while ((opt = getopt(argc, argv, "a:b:c:d:f:g:h:i:n:p:r:t:u:lsv")) != -1) {
switch (opt) { switch (opt) {
case 'i': case 'i':
device->set_id(optarg[0] - '0'); device->set_id(optarg[0] - '0');
@ -516,6 +532,11 @@ int main(int argc, char* argv[])
device->set_unit(optarg[0] - '0'); device->set_unit(optarg[0] - '0');
break; break;
case 'a':
command.set_operation(CREATE_IMAGE);
image_params = optarg;
break;
case 'b': case 'b':
int block_size; int block_size;
if (!GetAsInt(optarg, block_size)) { if (!GetAsInt(optarg, block_size)) {
@ -632,6 +653,10 @@ int main(int argc, char* argv[])
CommandReserve(hostname, port, reserved_ids); CommandReserve(hostname, port, reserved_ids);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
case CREATE_IMAGE:
CommandCreateImage(hostname, port, image_params);
exit(EXIT_SUCCESS);
case DEVICE_INFO: case DEVICE_INFO:
CommandDeviceInfo(hostname, port, command); CommandDeviceInfo(hostname, port, command);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);