mirror of
https://github.com/akuker/RASCSI.git
synced 2024-06-26 07:29:31 +00:00
Merge branch 'develop' into feature_bpi3
This commit is contained in:
commit
a3b91d0e1f
2
.github/workflows/run_tests.yml
vendored
2
.github/workflows/run_tests.yml
vendored
|
@ -57,5 +57,5 @@ jobs:
|
|||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||
run: |
|
||||
cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.cfamily.cache.enabled=false --define sonar.coverage.exclusions="tests/*" --define sonar.python.version=3
|
||||
cd $SOURCES | sonar-scanner --define sonar.host.url="${{ env.SONAR_SERVER_URL }}" --define sonar.cfamily.build-wrapper-output="${{ env.BUILD_WRAPPER_OUT_DIR }}" --define sonar.projectKey=akuker_RASCSI --define sonar.organization=rascsi --define sonar.cfamily.gcov.reportsPath=. --define sonar.cfamily.cache.enabled=false --define sonar.coverage.exclusions="**/test/**" --define sonar.cpd.exclusions="**test/**" --define sonar.python.version=3
|
||||
|
||||
|
|
24
doc/rascsi.1
24
doc/rascsi.1
|
@ -3,13 +3,13 @@
|
|||
rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
|
||||
.SH SYNOPSIS
|
||||
.B rascsi
|
||||
[\fB\-F\f® \fIFOLDER\fR]
|
||||
[\fB\-L\f® \fILOG_LEVEL\fR]
|
||||
[\fB\-P\f® \fIACCESS_TOKEN_FILE\fR]
|
||||
[\fB\-F\fR \fIFOLDER\fR]
|
||||
[\fB\-L\fR \fILOG_LEVEL\fR]
|
||||
[\fB\-P\fR \fIACCESS_TOKEN_FILE\fR]
|
||||
[\fB\-R\fR \fISCAN_DEPTH\fR]
|
||||
[\fB\-h\fR]
|
||||
[\fB\-n\fR \fIVENDOR:PRODUCT:REVISION\fR]
|
||||
[\fB\-p\f® \fIPORT\fR]
|
||||
[\fB\-p\fR \fIPORT\fR]
|
||||
[\fB\-r\fR \fIRESERVED_IDS\fR]
|
||||
[\fB\-n\fR \fITYPE\fR]
|
||||
[\fB\-v\fR]
|
||||
|
@ -18,7 +18,7 @@ rascsi \- Emulates SCSI devices using the Raspberry Pi GPIO pins
|
|||
[\fB\-HDn[:u]\fR \fIFILE\fR]...
|
||||
.SH DESCRIPTION
|
||||
.B rascsi
|
||||
Emulates SCSI devices using the Raspberry Pi GPIO pins.
|
||||
emulates SCSI devices using the Raspberry Pi GPIO pins.
|
||||
.PP
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be specified.
|
||||
The number (n) after the ID or HD identifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device. The default LUN is 0.
|
||||
|
@ -28,12 +28,12 @@ RaSCSI will determine the type of device based upon the file extension of the FI
|
|||
hd1: SCSI Hard Disk image (generic, non-removable, SCSI-1)
|
||||
hds: SCSI Hard Disk image (generic, non-removable)
|
||||
hdr: SCSI Hard Disk image (generic, removable)
|
||||
hdn: SCSI Hard Disk image (NEC GENUINE)
|
||||
hdi: SCSI Hard Disk image (Anex86 HD image)
|
||||
nhd: SCSI Hard Disk image (T98Next HD image)
|
||||
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac SCSI emulation)
|
||||
mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only used with X68000)
|
||||
iso: SCSI CD-ROM image (ISO 9660 image)
|
||||
hdn: SCSI Hard Disk image (NEC compatible - only used with PC-98 computers)
|
||||
hdi: SCSI Hard Disk image (Anex86 proprietary - only used with PC-98 computers)
|
||||
nhd: SCSI Hard Disk image (T98Next proprietary - only used with PC-98 computers)
|
||||
hda: SCSI Hard Disk image (Apple compatible - typically used with Macintosh computers)
|
||||
mos: SCSI Magneto-Optical image (generic - typically used with NeXT, X68000, etc.)
|
||||
iso: SCSI CD-ROM or DVD-ROM image (ISO 9660 image)
|
||||
|
||||
For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
|
||||
sudo rascsi -ID0 /path/to/drive/hdimage.hda
|
||||
|
@ -106,6 +106,6 @@ In case the fallocate command is available a much faster alternative to the dd c
|
|||
fallocate -l 104857600 /path/to/newimage.hda
|
||||
|
||||
.SH SEE ALSO
|
||||
rasctl(1), scsimon(1), rasdump(1), sasidump(1)
|
||||
rasctl(1), scsimon(1), rasdump(1)
|
||||
|
||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
|
|
@ -1,117 +1,92 @@
|
|||
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
|
||||
!! ------ The native file is rascsi.1. Re-run 'make docs' after updating\n\n
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
!! ------ The native file is rascsi.1. Re-run 'make docs' after updating
|
||||
|
||||
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
|
||||
NAME
|
||||
rascsi - Emulates SCSI devices using the Raspberry Pi GPIO pins
|
||||
|
||||
SYNOPSIS
|
||||
rascsi [-F[u00AE] FOLDER] [-L[u00AE] LOG_LEVEL] [-P[u00AE] ACCESS_TO‐
|
||||
KEN_FILE] [-R SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p[u00AE]
|
||||
PORT] [-r RESERVED_IDS] [-n TYPE] [-v] [-z LOCALE] [-IDn:[u] FILE]
|
||||
[-HDn[:u] FILE]...
|
||||
rascsi [-F FOLDER] [-L LOG_LEVEL] [-P ACCESS_TOKEN_FILE] [-R SCAN_DEPTH] [-h] [-n VENDOR:PRODUCT:REVISION] [-p PORT] [-r
|
||||
RESERVED_IDS] [-n TYPE] [-v] [-z LOCALE] [-IDn:[u] FILE] [-HDn[:u] FILE]...
|
||||
|
||||
DESCRIPTION
|
||||
rascsi Emulates SCSI devices using the Raspberry Pi GPIO pins.
|
||||
rascsi emulates SCSI devices using the Raspberry Pi GPIO pins.
|
||||
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be
|
||||
specified. The number (n) after the ID or HD identifier specifies the
|
||||
ID number for that device. The optional number (u) specifies the LUN
|
||||
(logical unit) for that device. The default LUN is 0. For SCSI: The ID
|
||||
is limited from 0-7. However, typically SCSI ID 7 is reserved for the
|
||||
"initiator" (the host computer). The LUN is limited from 0-31.
|
||||
In the arguments to RaSCSI, one or more SCSI (-IDn[:u]) devices can be specified. The number (n) after the ID or HD iden‐
|
||||
tifier specifies the ID number for that device. The optional number (u) specifies the LUN (logical unit) for that device.
|
||||
The default LUN is 0. For SCSI: The ID is limited from 0-7. However, typically SCSI ID 7 is reserved for the "initiator"
|
||||
(the host computer). The LUN is limited from 0-31.
|
||||
|
||||
RaSCSI will determine the type of device based upon the file extension
|
||||
of the FILE argument.
|
||||
RaSCSI will determine the type of device based upon the file extension of the FILE argument.
|
||||
hd1: SCSI Hard Disk image (generic, non-removable, SCSI-1)
|
||||
hds: SCSI Hard Disk image (generic, non-removable)
|
||||
hdr: SCSI Hard Disk image (generic, removable)
|
||||
hdn: SCSI Hard Disk image (NEC GENUINE)
|
||||
hdi: SCSI Hard Disk image (Anex86 HD image)
|
||||
nhd: SCSI Hard Disk image (T98Next HD image)
|
||||
hda: SCSI Hard Disk image (APPLE GENUINE - typically used with Mac
|
||||
SCSI emulation)
|
||||
mos: SCSI Magneto-optical image (XM6 SCSI MO image - typically only
|
||||
used with X68000)
|
||||
iso: SCSI CD-ROM image (ISO 9660 image)
|
||||
hdn: SCSI Hard Disk image (NEC compatible - only used with PC-98 computers)
|
||||
hdi: SCSI Hard Disk image (Anex86 proprietary - only used with PC-98 computers)
|
||||
nhd: SCSI Hard Disk image (T98Next proprietary - only used with PC-98 computers)
|
||||
hda: SCSI Hard Disk image (Apple compatible - typically used with Macintosh computers)
|
||||
mos: SCSI Magneto-Optical image (generic - typically used with NeXT, X68000, etc.)
|
||||
iso: SCSI CD-ROM or DVD-ROM image (ISO 9660 image)
|
||||
|
||||
For example, if you want to specify an Apple-compatible HD image on ID
|
||||
0, you can use the following command:
|
||||
For example, if you want to specify an Apple-compatible HD image on ID 0, you can use the following command:
|
||||
sudo rascsi -ID0 /path/to/drive/hdimage.hda
|
||||
|
||||
Once RaSCSI starts, it will open a socket (default port is 6868) to al‐
|
||||
low external management commands. If another process is using the
|
||||
rascsi port, RaSCSI will terminate, since it is likely another instance
|
||||
of RaSCSI. Once RaSCSI has initialized, the rasctl utility can be used
|
||||
to send commands.
|
||||
Once RaSCSI starts, it will open a socket (default port is 6868) to allow external management commands. If another process
|
||||
is using the rascsi port, RaSCSI will terminate, since it is likely another instance of RaSCSI. Once RaSCSI has initial‐
|
||||
ized, the rasctl utility can be used to send commands.
|
||||
|
||||
To quit RaSCSI, press Control + C. If it is running in the background,
|
||||
you can kill it using an INT signal.
|
||||
To quit RaSCSI, press Control + C. If it is running in the background, you can kill it using an INT signal.
|
||||
|
||||
OPTIONS
|
||||
-b BLOCK_SIZE
|
||||
The optional block size, either 512, 1024, 2048 or 4096 bytes.
|
||||
Default size is 512 bytes.
|
||||
The optional block size, either 512, 1024, 2048 or 4096 bytes. Default size is 512 bytes.
|
||||
|
||||
-F FOLDER
|
||||
The default folder for image files. For files in this folder no
|
||||
absolute path needs to be specified. The initial default folder
|
||||
is '~/images'.
|
||||
The default folder for image files. For files in this folder no absolute path needs to be specified. The initial de‐
|
||||
fault folder is '~/images'.
|
||||
|
||||
-L LOG_LEVEL
|
||||
The rascsi log level (trace, debug, info, warn, err, critical,
|
||||
off). The default log level is 'info'.
|
||||
The rascsi log level (trace, debug, info, warn, err, critical, off). The default log level is 'info'.
|
||||
|
||||
-P ACCESS_TOKEN_FILE
|
||||
Enable authentication and read the access token from the speci‐
|
||||
fied file. The access token file must be owned by root and must
|
||||
be readable by root only.
|
||||
Enable authentication and read the access token from the specified file. The access token file must be owned by root
|
||||
and must be readable by root only.
|
||||
|
||||
-R SCAN_DEPTH
|
||||
Scan for image files recursively, up to a depth of SCAN_DEPTH.
|
||||
Depth 0 means to ignore any folders within the default image
|
||||
filder. Be careful when using this option with many sub-folders
|
||||
in the default image folder. The default depth is 1.
|
||||
Scan for image files recursively, up to a depth of SCAN_DEPTH. Depth 0 means to ignore any folders within the de‐
|
||||
fault image filder. Be careful when using this option with many sub-folders in the default image folder. The default
|
||||
depth is 1.
|
||||
|
||||
-h Show a help page.
|
||||
|
||||
-n VENDOR:PRODUCT:REVISION
|
||||
Set the vendor, product and revision for the device, to be re‐
|
||||
turned with the INQUIRY data. A complete set of name components
|
||||
must be provided. VENDOR may have up to 8, PRODUCT up to 16, RE‐
|
||||
VISION up to 4 characters. Padding with blanks to the maxium
|
||||
length is automatically applied. Once set the name of a device
|
||||
cannot be changed.
|
||||
Set 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 automatically applied. Once set the name of a device cannot be changed.
|
||||
|
||||
-p PORT
|
||||
The rascsi server port, default is 6868.
|
||||
|
||||
-r RESERVED_IDS
|
||||
Comma-separated list of IDs to reserve. Pass an empty list in
|
||||
order to not reserve anything. -p TYPE The optional case-insen‐
|
||||
sitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP,
|
||||
SCLP, SCHS). If no type is specified for devices that support an
|
||||
image file, rascsi tries to derive the type from the file exten‐
|
||||
sion.
|
||||
Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything. -p TYPE The optional
|
||||
case-insensitive device type (SAHD, SCHD, SCRM, SCCD, SCMO, SCBR, SCDP, SCLP, SCHS). If no type is specified for de‐
|
||||
vices that support an image file, rascsi tries to derive the type from the file extension.
|
||||
|
||||
-v Display the rascsi version.
|
||||
|
||||
-z LOCALE
|
||||
Overrides the default locale for client-faces error messages.
|
||||
The client can override the locale.
|
||||
Overrides the default locale for client-faces error messages. The client can override the locale.
|
||||
|
||||
-IDn[:u] FILE
|
||||
n is the SCSI ID number (0-7). u (0-31) is the optional LUN
|
||||
(logical unit). The default LUN is 0.
|
||||
n is the SCSI ID number (0-7). u (0-31) is the optional LUN (logical unit). The default LUN is 0.
|
||||
|
||||
FILE is the name of the image file to use for the SCSI device.
|
||||
For devices that do not support an image file (SCBR, SCDP, SCLP,
|
||||
SCHS) the filename may have a special meaning or a dummy name
|
||||
can be provided. For SCBR and SCDP it is an optioinal priori‐
|
||||
tized list of network interfaces, an optional IP address and
|
||||
netmask, e.g. "interfaces=eth0,eth1,wlan0:inet=10.10.20.1/24".
|
||||
For SCLP it is the print command to be used and a reservation
|
||||
timeout in seconds, e.g. "cmd=lp -oraw %f:timeout=60".
|
||||
FILE is the name of the image file to use for the SCSI device. For devices that do not support an image file (SCBR,
|
||||
SCDP, SCLP, SCHS) the filename may have a special meaning or a dummy name can be provided. For SCBR and SCDP it is
|
||||
an optioinal prioritized list of network interfaces, an optional IP address and netmask, e.g. "inter‐
|
||||
faces=eth0,eth1,wlan0:inet=10.10.20.1/24". For SCLP it is the print command to be used and a reservation timeout in
|
||||
seconds, e.g. "cmd=lp -oraw %f:timeout=60".
|
||||
|
||||
FILE is the name of the image file to use for the SCSI device.
|
||||
|
||||
|
@ -119,26 +94,22 @@ EXAMPLES
|
|||
Launch RaSCSI with no emulated drives attached:
|
||||
rascsi
|
||||
|
||||
Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID
|
||||
2
|
||||
Launch RaSCSI with an Apple hard drive image as ID 0 and a CD-ROM as ID 2
|
||||
rascsi -ID0 /path/to/harddrive.hda -ID2 /path/to/cdimage.iso
|
||||
|
||||
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw de‐
|
||||
vice file /dev/hdb (e.g. a USB stick) and a DaynaPort network adapter
|
||||
as ID 6:
|
||||
Launch RaSCSI with a removable SCSI drive image as ID 0 and the raw device file /dev/hdb (e.g. a USB stick) and a DaynaPort
|
||||
network adapter as ID 6:
|
||||
rascsi -ID0 -t scrm /dev/hdb -ID6 -t scdp daynaport
|
||||
|
||||
To create an empty, 100MiB HD image, use the following command:
|
||||
dd if=/dev/zero of=/path/to/newimage.hda bs=512 count=204800
|
||||
|
||||
In case the fallocate command is available a much faster alternative to
|
||||
the dd command is:
|
||||
In case the fallocate command is available a much faster alternative to the dd command is:
|
||||
fallocate -l 104857600 /path/to/newimage.hda
|
||||
|
||||
SEE ALSO
|
||||
rasctl(1), scsimon(1), rasdump(1), sasidump(1)
|
||||
rasctl(1), scsimon(1), rasdump(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)
|
||||
|
|
|
@ -35,7 +35,7 @@ rasctl \- Sends management commands to the rascsi process
|
|||
[\fB\-z\fR \fILOCALE\fR]
|
||||
.SH DESCRIPTION
|
||||
.B rasctl
|
||||
Sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices.
|
||||
sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the devices.
|
||||
|
||||
Either the -i or -l option should be specified at one time. Not both.
|
||||
|
||||
|
@ -174,6 +174,6 @@ Request the RaSCSI process to attach a disk (assumed) to SCSI ID 0 with the cont
|
|||
rasctl -i 0 -f HDIIMAGE0.HDS
|
||||
|
||||
.SH SEE ALSO
|
||||
rascsi(1), scsimon(1), rasdump(1), sasidump(1)
|
||||
rascsi(1), scsimon(1), rasdump(1)
|
||||
|
||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
|
|
@ -1,32 +1,31 @@
|
|||
!! ------ THIS FILE IS AUTO_GENERATED! DO NOT MANUALLY UPDATE!!!
|
||||
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating\n\n
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
!! ------ The native file is rasctl.1. Re-run 'make docs' after updating
|
||||
|
||||
|
||||
rascsi(1) General Commands Manual rascsi(1)
|
||||
|
||||
NAME
|
||||
rasctl - Sends management commands to the rascsi process
|
||||
|
||||
SYNOPSIS
|
||||
rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V |
|
||||
-X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IMAGE_FOLDER] [-R CUR‐
|
||||
RENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST]
|
||||
[-i ID [-n NAME] [-p PORT] [-r RESERVED_IDS] [-t TYPE] [-u UNIT] [-x
|
||||
CURRENT_NAME:NEW_NAME] [-z LOCALE]
|
||||
rasctl -e | -l | -m | -o | -s | -v | -D | -I | -L | -O | -P | -T | -V | -X | [-C FILENAME:FILESIZE] [-E FILENAME] [-F IM‐
|
||||
AGE_FOLDER] [-R CURRENT_NAME:NEW_NAME] [-c CMD] [-f FILE|PARAM] [-g LOG_LEVEL] [-h HOST] [-i ID [-n NAME] [-p PORT] [-r RE‐
|
||||
SERVED_IDS] [-t TYPE] [-u UNIT] [-x CURRENT_NAME:NEW_NAME] [-z LOCALE]
|
||||
|
||||
DESCRIPTION
|
||||
rasctl Sends commands to the rascsi process to make configuration ad‐
|
||||
justments at runtime or to check the status of the devices.
|
||||
rasctl sends commands to the rascsi process to make configuration adjustments at runtime or to check the status of the de‐
|
||||
vices.
|
||||
|
||||
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
|
||||
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
|
||||
-C FILENAME:FILESIZE
|
||||
Create an image file in the default image folder with the speci‐
|
||||
fied name and size in bytes.
|
||||
Create an image file in the default image folder with the specified name and size in bytes.
|
||||
|
||||
-D Detach all devices.
|
||||
|
||||
|
@ -39,28 +38,22 @@ OPTIONS
|
|||
-I Gets the list of reserved device IDs.
|
||||
|
||||
-L LOG_LEVEL
|
||||
Set the rascsi log level (trace, debug, info, warn, err, criti‐
|
||||
cal, off).
|
||||
Set the rascsi log level (trace, debug, info, warn, err, critical, off).
|
||||
|
||||
-h HOST
|
||||
The rascsi host to connect to, default is 'localhost'.
|
||||
|
||||
-e List all images files in the default image folder.
|
||||
|
||||
-N Lists all available network interfaces provided that they are
|
||||
up.
|
||||
-N Lists all available network interfaces provided that they are up.
|
||||
|
||||
-O Display the available rascsi server log levels and the current
|
||||
log level.
|
||||
-O Display the available rascsi server log levels and the current log level.
|
||||
|
||||
-P Prompt for the access token in case rascsi requires authentica‐
|
||||
tion.
|
||||
-P Prompt for the access token in case rascsi requires authentication.
|
||||
|
||||
-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.
|
||||
|
||||
-m List all file extensions recognized by RaSCSI and the device
|
||||
types they map to.
|
||||
-m List all file extensions recognized by RaSCSI and the device types they map to.
|
||||
|
||||
-o Display operation meta data information.
|
||||
|
||||
|
@ -71,11 +64,9 @@ OPTIONS
|
|||
The rascsi port to connect to, default is 6868.
|
||||
|
||||
-r RESERVED_IDS
|
||||
Comma-separated list of IDs to reserve. Pass an empty list in
|
||||
order to not reserve anything.
|
||||
Comma-separated list of IDs to reserve. Pass an empty list in order to not reserve anything.
|
||||
|
||||
-s Display server-side settings like available images or supported
|
||||
device types.
|
||||
-s Display server-side settings like available images or supported device types.
|
||||
|
||||
-T Display all device types and their properties.
|
||||
|
||||
|
@ -101,28 +92,23 @@ 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
|
||||
are always read-only)
|
||||
u(nprotect): Remove write protection from the medium (not for
|
||||
CD-ROMs, which are always read-only)
|
||||
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)
|
||||
s(how): Display device information
|
||||
|
||||
eject, protect and unprotect are idempotent.
|
||||
|
||||
-b BLOCK_SIZE
|
||||
The optional block size, either 512, 1024, 2048 or 4096 bytes.
|
||||
The default size is 512 bytes.
|
||||
The optional block size, either 512, 1024, 2048 or 4096 bytes. The default size is 512 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‐
|
||||
mitted file types.
|
||||
Device-specific: Either a path to a disk image file, or a parameter for a non-disk device. See the rascsi(1) man
|
||||
page for permitted file types.
|
||||
|
||||
-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. For some
|
||||
types there are shortcuts (only the first letter is required):
|
||||
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 types there are shortcuts (only the first letter is
|
||||
required):
|
||||
hd: SCSI hard disk drive
|
||||
rm: SCSI removable media drive
|
||||
cd: CD-ROM
|
||||
|
@ -133,17 +119,13 @@ OPTIONS
|
|||
services: Host services device
|
||||
|
||||
-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 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.
|
||||
The vendor, product and revision for the device, to be returned with the INQUIRY data. A complete set of name compo‐
|
||||
nents 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 automatically applied. Once set the name of a device cannot be changed.
|
||||
|
||||
-u UNIT
|
||||
Unit number (0-31). This will default to 0. This option is only
|
||||
used when there are multiple SCSI devices on a shared SCSI con‐
|
||||
troller. (This is not common)
|
||||
Unit number (0-31). 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
|
||||
Show a listing of all of the SCSI devices and their current status.
|
||||
|
@ -156,14 +138,13 @@ EXAMPLES
|
|||
| 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 "HDIIM‐
|
||||
AGE0.HDS".
|
||||
rasctl -i 0 -f HDIIMAGE0.HDS
|
||||
|
||||
SEE ALSO
|
||||
rascsi(1), scsimon(1), rasdump(1), sasidump(1)
|
||||
rascsi(1), scsimon(1), rasdump(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)
|
||||
|
|
|
@ -9,7 +9,7 @@ rasdump \- SCSI disk dumping tool for RaSCSI
|
|||
[\fB\-r\fR]
|
||||
.SH DESCRIPTION
|
||||
.B rasdump
|
||||
Samples the data on physical SCSI storage media, including hard drives and MO drives, and stores it to an image file. It can also restore from a dumped file onto physical SCSI storage media. Can be connected directly, through a STANDARD RaSCSI board, or a FULLSPEC RaSCSI board.
|
||||
samples the data on physical SCSI storage media, including hard drives and MO drives, and stores it to an image file. It can also restore from a dumped file onto physical SCSI storage media. Can be connected directly, through a STANDARD RaSCSI board, or a FULLSPEC RaSCSI board.
|
||||
|
||||
Set its own ID with the BID option. Defaults to 7 if ommitted.
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ scsimon \- Acts as a data capture tool for all traffic on the SCSI bus. Data is
|
|||
.B scsimon
|
||||
.SH DESCRIPTION
|
||||
.B scsimon
|
||||
Monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal.
|
||||
monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue to run until the user presses CTRL-C, or the process receives a SIGINT signal.
|
||||
.PP
|
||||
The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched.
|
||||
|
||||
|
@ -22,6 +22,6 @@ Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware:
|
|||
scsimon
|
||||
|
||||
.SH SEE ALSO
|
||||
rasctl(1), rascsi(1), rasdump(1), sasidump(1)
|
||||
rasctl(1), rascsi(1), rasdump(1)
|
||||
|
||||
Full documentation is available at: <https://www.github.com/akuker/RASCSI/wiki/>
|
||||
|
|
|
@ -2,24 +2,20 @@
|
|||
!! ------ The native file is scsimon.1. Re-run 'make docs' after updating
|
||||
|
||||
|
||||
scsimon(1) General Commands Manual scsimon(1)
|
||||
scsimon(1) General Commands Manual scsimon(1)
|
||||
|
||||
NAME
|
||||
scsimon - Acts as a data capture tool for all traffic on the SCSI bus.
|
||||
Data is stored in a Value Change Dump (VCD) file.
|
||||
scsimon - Acts as a data capture tool for all traffic on the SCSI bus. Data is stored in a Value Change Dump (VCD) file.
|
||||
|
||||
SYNOPSIS
|
||||
scsimon
|
||||
|
||||
DESCRIPTION
|
||||
scsimon Monitors all of the traffic on the SCSI bus, using a RaSCSI de‐
|
||||
vice. The data is cached in memory while the tool is running. A circu‐
|
||||
lar buffer is used so that only the most recent 1,000,000 transactions
|
||||
are stored. The tool will continue to run until the user presses CTRL-
|
||||
C, or the process receives a SIGINT signal.
|
||||
scsimon monitors all of the traffic on the SCSI bus, using a RaSCSI device. The data is cached in memory while the tool is
|
||||
running. A circular buffer is used so that only the most recent 1,000,000 transactions are stored. The tool will continue
|
||||
to run until the user presses CTRL-C, or the process receives a SIGINT signal.
|
||||
|
||||
The logged data is stored in a file called "log.vcd" in the current
|
||||
working directory from where scsimon was launched.
|
||||
The logged data is stored in a file called "log.vcd" in the current working directory from where scsimon was launched.
|
||||
|
||||
Currently, scsimon doesn't accept any arguments.
|
||||
|
||||
|
@ -29,14 +25,12 @@ OPTIONS
|
|||
None
|
||||
|
||||
EXAMPLES
|
||||
Launch scsimon to capture all SCSI traffic available to the RaSCSI
|
||||
hardware:
|
||||
Launch scsimon to capture all SCSI traffic available to the RaSCSI hardware:
|
||||
scsimon
|
||||
|
||||
SEE ALSO
|
||||
rasctl(1), rascsi(1), rasdump(1), sasidump(1)
|
||||
rasctl(1), rascsi(1), rasdump(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/>
|
||||
|
||||
scsimon(1)
|
||||
scsimon(1)
|
||||
|
|
|
@ -55,3 +55,17 @@ services:
|
|||
"--log-level=${WEB_LOG_LEVEL:-info}",
|
||||
"--dev-mode"
|
||||
]
|
||||
|
||||
pytest:
|
||||
container_name: pytest
|
||||
image: rascsi:pytest
|
||||
pull_policy: never
|
||||
profiles:
|
||||
- webui-tests
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: docker/pytest/Dockerfile
|
||||
volumes:
|
||||
- ../python/web:/src:delegated
|
||||
working_dir: /src
|
||||
entrypoint: "pytest"
|
||||
|
|
5
docker/pytest/Dockerfile
Normal file
5
docker/pytest/Dockerfile
Normal file
|
@ -0,0 +1,5 @@
|
|||
FROM python:3.7-bullseye
|
||||
ENV DOCKER=1
|
||||
|
||||
COPY python/web/requirements-dev.txt /requirements-dev.txt
|
||||
RUN pip install -r /requirements-dev.txt
|
|
@ -6,7 +6,7 @@ FROM "${OS_ARCH}/${OS_DISTRO}:${OS_VERSION}"
|
|||
EXPOSE 80 443
|
||||
|
||||
ARG DEBIAN_FRONTEND=noninteractive
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends sudo systemd rsyslog procps
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends sudo systemd rsyslog procps man-db man2html
|
||||
|
||||
RUN groupadd pi
|
||||
RUN useradd --create-home --shell /bin/bash -g pi pi
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
theme: jekyll-theme-dinky
|
|
@ -1,37 +0,0 @@
|
|||
## Welcome to GitHub Pages
|
||||
|
||||
You can use the [editor on GitHub](https://github.com/akuker/RASCSI/edit/master/docs/index.md) to maintain and preview the content for your website in Markdown files.
|
||||
|
||||
Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files.
|
||||
|
||||
### Markdown
|
||||
|
||||
Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for
|
||||
|
||||
```markdown
|
||||
Syntax highlighted code block
|
||||
|
||||
# Header 1
|
||||
## Header 2
|
||||
### Header 3
|
||||
|
||||
- Bulleted
|
||||
- List
|
||||
|
||||
1. Numbered
|
||||
2. List
|
||||
|
||||
**Bold** and _Italic_ and `Code` text
|
||||
|
||||
[Link](url) and ![Image](src)
|
||||
```
|
||||
|
||||
For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/).
|
||||
|
||||
### Jekyll Themes
|
||||
|
||||
Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/akuker/RASCSI/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file.
|
||||
|
||||
### Support or Contact
|
||||
|
||||
Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out.
|
|
@ -104,7 +104,8 @@ function installPackages() {
|
|||
unzip \
|
||||
unar \
|
||||
disktype \
|
||||
libgmock-dev
|
||||
libgmock-dev \
|
||||
man2html
|
||||
}
|
||||
|
||||
# install Debian packges for RaSCSI standalone
|
||||
|
@ -116,7 +117,8 @@ function installPackagesStandalone() {
|
|||
libprotobuf-dev \
|
||||
protobuf-compiler \
|
||||
disktype \
|
||||
libgmock-dev
|
||||
libgmock-dev \
|
||||
man2html
|
||||
}
|
||||
|
||||
# cache the pip packages
|
||||
|
@ -832,6 +834,20 @@ function installNetatalk() {
|
|||
NETATALK_VERSION="2-220801"
|
||||
AFP_SHARE_PATH="$HOME/afpshare"
|
||||
AFP_SHARE_NAME="Pi File Server"
|
||||
NETATALK_CONFIG_PATH="/etc/netatalk"
|
||||
|
||||
if [ -d "$NETATALK_CONFIG_PATH" ]; then
|
||||
echo
|
||||
echo "WARNING: Netatalk configuration dir $NETATALK_CONFIG_PATH already exists."
|
||||
echo "This installation process will overwrite existing Netatalk applications and configurations."
|
||||
echo "No shared files will be deleted, but you may have to manually restore your settings after the installation."
|
||||
echo
|
||||
echo "Do you want to proceed with the installation? [y/N]"
|
||||
read -r REPLY
|
||||
if ! [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Downloading netatalk-$NETATALK_VERSION to $HOME"
|
||||
cd $HOME || exit 1
|
||||
|
@ -842,6 +858,39 @@ function installNetatalk() {
|
|||
./debian_install.sh -j="${CORES:-1}" -n="$AFP_SHARE_NAME" -p="$AFP_SHARE_PATH" || exit 1
|
||||
}
|
||||
|
||||
# Appends the images dir as a shared Netatalk volume
|
||||
function shareImagesWithNetatalk() {
|
||||
APPLEVOLUMES_PATH="/etc/netatalk/AppleVolumes.default"
|
||||
if ! [ -f "$APPLEVOLUMES_PATH" ]; then
|
||||
echo "Could not find $APPLEVOLUMES_PATH ... is Netatalk installed?"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$(grep -c "$VIRTUAL_DRIVER_PATH" "$APPLEVOLUMES_PATH")" -ge 1 ]; then
|
||||
echo "The $VIRTUAL_DRIVER_PATH dir is already shared in $APPLEVOLUMES_PATH"
|
||||
echo "Do you want to turn off the sharing? [y/N]"
|
||||
read -r REPLY
|
||||
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
|
||||
sudo systemctl stop afpd
|
||||
sudo sed -i '\,^'"$VIRTUAL_DRIVER_PATH"',d' "$APPLEVOLUMES_PATH"
|
||||
echo "Sharing for $VIRTUAL_DRIVER_PATH disabled!"
|
||||
sudo systemctl start afpd
|
||||
exit 0
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sudo systemctl stop afpd
|
||||
echo "Appended to AppleVolumes.default:"
|
||||
echo "$VIRTUAL_DRIVER_PATH \"RaSCSI Images\"" | sudo tee -a "$APPLEVOLUMES_PATH"
|
||||
sudo systemctl start afpd
|
||||
|
||||
echo
|
||||
echo "WARNING: Do not inadvertently move or rename image files that are in use by RaSCSI."
|
||||
echo "Doing so may lead to data loss."
|
||||
echo
|
||||
}
|
||||
|
||||
# Downloads, compiles, and installs Macproxy (web proxy)
|
||||
function installMacproxy {
|
||||
PORT=5000
|
||||
|
@ -1301,6 +1350,10 @@ function runChoice() {
|
|||
showRaScsiCtrlBoardStatus
|
||||
echo "Installing / Updating RaSCSI Control Board UI - Complete!"
|
||||
;;
|
||||
13)
|
||||
shareImagesWithNetatalk
|
||||
echo "Configuring AppleShare File Server - Complete!"
|
||||
;;
|
||||
-h|--help|h|help)
|
||||
showMenu
|
||||
;;
|
||||
|
@ -1314,8 +1367,8 @@ function runChoice() {
|
|||
function readChoice() {
|
||||
choice=-1
|
||||
|
||||
until [ $choice -ge "0" ] && [ $choice -le "12" ]; do
|
||||
echo -n "Enter your choice (0-12) or CTRL-C to exit: "
|
||||
until [ $choice -ge "0" ] && [ $choice -le "13" ]; do
|
||||
echo -n "Enter your choice (0-13) or CTRL-C to exit: "
|
||||
read -r choice
|
||||
done
|
||||
|
||||
|
@ -1345,6 +1398,7 @@ function showMenu() {
|
|||
echo " 11) configure the RaSCSI Web Interface stand-alone"
|
||||
echo "EXPERIMENTAL FEATURES"
|
||||
echo " 12) install or update RaSCSI Control Board UI (requires hardware)"
|
||||
echo " 13) share the images dir over AppleShare (requires Netatalk)"
|
||||
}
|
||||
|
||||
# parse arguments passed to the script
|
||||
|
|
|
@ -23,4 +23,7 @@ ARCHIVE_FILE_SUFFIXES = [
|
|||
|
||||
# The RESERVATIONS list is used to keep track of the reserved ID memos.
|
||||
# Initialize with a list of 8 empty strings.
|
||||
RESERVATIONS = ["" for x in range(0, 8)]
|
||||
RESERVATIONS = ["" for _ in range(0, 8)]
|
||||
|
||||
# Standard error message for shell commands
|
||||
SHELL_ERROR = "Shell command: \"%s\" led to error: %s"
|
||||
|
|
|
@ -6,22 +6,33 @@ import os
|
|||
import logging
|
||||
import asyncio
|
||||
from functools import lru_cache
|
||||
from pathlib import PurePath
|
||||
from pathlib import PurePath, Path
|
||||
from zipfile import ZipFile, is_zipfile
|
||||
from time import time
|
||||
from subprocess import run, CalledProcessError
|
||||
from json import dump, load
|
||||
from shutil import copyfile
|
||||
from urllib.parse import quote
|
||||
|
||||
import requests
|
||||
|
||||
import rascsi_interface_pb2 as proto
|
||||
from rascsi.common_settings import CFG_DIR, CONFIG_FILE_SUFFIX, PROPERTIES_SUFFIX, ARCHIVE_FILE_SUFFIXES, RESERVATIONS
|
||||
from rascsi.common_settings import (
|
||||
CFG_DIR,
|
||||
CONFIG_FILE_SUFFIX,
|
||||
PROPERTIES_SUFFIX,
|
||||
ARCHIVE_FILE_SUFFIXES,
|
||||
RESERVATIONS,
|
||||
SHELL_ERROR,
|
||||
)
|
||||
from rascsi.ractl_cmds import RaCtlCmds
|
||||
from rascsi.return_codes import ReturnCodes
|
||||
from rascsi.socket_cmds import SocketCmds
|
||||
from util import unarchiver
|
||||
|
||||
FILE_READ_ERROR = "Unhandled exception when reading file: %s"
|
||||
FILE_WRITE_ERROR = "Unhandled exception when writing to file: %s"
|
||||
URL_SAFE = "/:?&"
|
||||
|
||||
class FileCmds:
|
||||
"""
|
||||
|
@ -94,7 +105,9 @@ class FileCmds:
|
|||
for file in result.image_files_info.image_files:
|
||||
# Add properties meta data for the image, if applicable
|
||||
if file.name in prop_files:
|
||||
process = self.read_drive_properties(f"{CFG_DIR}/{file.name}.{PROPERTIES_SUFFIX}")
|
||||
process = self.read_drive_properties(
|
||||
Path(CFG_DIR) / f"{file.name}.{PROPERTIES_SUFFIX}"
|
||||
)
|
||||
prop = process["conf"]
|
||||
else:
|
||||
prop = False
|
||||
|
@ -148,7 +161,7 @@ class FileCmds:
|
|||
command.params["token"] = self.token
|
||||
command.params["locale"] = self.locale
|
||||
|
||||
command.params["file"] = file_name + "." + file_type
|
||||
command.params["file"] = f"{file_name}.{file_type}"
|
||||
command.params["size"] = str(size)
|
||||
command.params["read_only"] = "false"
|
||||
|
||||
|
@ -216,14 +229,15 @@ class FileCmds:
|
|||
# noinspection PyMethodMayBeStatic
|
||||
def delete_file(self, file_path):
|
||||
"""
|
||||
Takes (str) file_path with the full path to the file to delete
|
||||
Takes (Path) file_path for the file to delete
|
||||
Returns (dict) with (bool) status and (str) msg
|
||||
"""
|
||||
parameters = {
|
||||
"file_path": file_path
|
||||
}
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
if file_path.exists():
|
||||
file_path.unlink()
|
||||
return {
|
||||
"status": True,
|
||||
"return_code": ReturnCodes.DELETEFILE_SUCCESS,
|
||||
|
@ -238,14 +252,16 @@ class FileCmds:
|
|||
# noinspection PyMethodMayBeStatic
|
||||
def rename_file(self, file_path, target_path):
|
||||
"""
|
||||
Takes (str) file_path and (str) target_path
|
||||
Takes:
|
||||
- (Path) file_path for the file to rename
|
||||
- (Path) target_path for the name to rename
|
||||
Returns (dict) with (bool) status and (str) msg
|
||||
"""
|
||||
parameters = {
|
||||
"target_path": target_path
|
||||
}
|
||||
if os.path.exists(PurePath(target_path).parent):
|
||||
os.rename(file_path, target_path)
|
||||
if target_path.parent.exists:
|
||||
file_path.rename(target_path)
|
||||
return {
|
||||
"status": True,
|
||||
"return_code": ReturnCodes.RENAMEFILE_SUCCESS,
|
||||
|
@ -260,14 +276,16 @@ class FileCmds:
|
|||
# noinspection PyMethodMayBeStatic
|
||||
def copy_file(self, file_path, target_path):
|
||||
"""
|
||||
Takes (str) file_path and (str) target_path
|
||||
Takes:
|
||||
- (Path) file_path for the file to copy from
|
||||
- (Path) target_path for the name to copy to
|
||||
Returns (dict) with (bool) status and (str) msg
|
||||
"""
|
||||
parameters = {
|
||||
"target_path": target_path
|
||||
}
|
||||
if os.path.exists(PurePath(target_path).parent):
|
||||
copyfile(file_path, target_path)
|
||||
if target_path.parent.exists:
|
||||
copyfile(str(file_path), str(target_path))
|
||||
return {
|
||||
"status": True,
|
||||
"return_code": ReturnCodes.WRITEFILE_SUCCESS,
|
||||
|
@ -305,21 +323,22 @@ class FileCmds:
|
|||
properties_files_moved = []
|
||||
if move_properties_files_to_config:
|
||||
for file in extract_result["extracted"]:
|
||||
if file.get("name").endswith(".properties"):
|
||||
if file.get("name").endswith(f".{PROPERTIES_SUFFIX}"):
|
||||
prop_path = Path(CFG_DIR) / file["name"]
|
||||
if (self.rename_file(
|
||||
file["absolute_path"],
|
||||
f"{CFG_DIR}/{file['name']}"
|
||||
Path(file["absolute_path"]),
|
||||
prop_path,
|
||||
)):
|
||||
properties_files_moved.append({
|
||||
"status": True,
|
||||
"name": file["path"],
|
||||
"path": f"{CFG_DIR}/{file['name']}",
|
||||
"path": str(prop_path),
|
||||
})
|
||||
else:
|
||||
properties_files_moved.append({
|
||||
"status": False,
|
||||
"name": file["path"],
|
||||
"path": f"{CFG_DIR}/{file['name']}",
|
||||
"path": str(prop_path),
|
||||
})
|
||||
|
||||
return {
|
||||
|
@ -362,7 +381,7 @@ class FileCmds:
|
|||
tmp_full_path = tmp_dir + file_name
|
||||
iso_filename = f"{server_info['image_dir']}/{file_name}.iso"
|
||||
|
||||
req_proc = self.download_to_dir(url, tmp_dir, file_name)
|
||||
req_proc = self.download_to_dir(quote(url, safe=URL_SAFE), tmp_dir, file_name)
|
||||
|
||||
if not req_proc["status"]:
|
||||
return {"status": False, "msg": req_proc["msg"]}
|
||||
|
@ -386,7 +405,7 @@ class FileCmds:
|
|||
"%s was successfully unzipped. Deleting the zipfile.",
|
||||
tmp_full_path,
|
||||
)
|
||||
self.delete_file(tmp_full_path)
|
||||
self.delete_file(Path(tmp_full_path))
|
||||
|
||||
try:
|
||||
run(
|
||||
|
@ -401,8 +420,7 @@ class FileCmds:
|
|||
check=True,
|
||||
)
|
||||
except CalledProcessError as error:
|
||||
logging.warning("Executed shell command: %s", " ".join(error.cmd))
|
||||
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
|
||||
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||
|
||||
parameters = {
|
||||
|
@ -424,7 +442,11 @@ class FileCmds:
|
|||
logging.info("Making a request to download %s", url)
|
||||
|
||||
try:
|
||||
with requests.get(url, stream=True, headers={"User-Agent": "Mozilla/5.0"}) as req:
|
||||
with requests.get(
|
||||
quote(url, safe=URL_SAFE),
|
||||
stream=True,
|
||||
headers={"User-Agent": "Mozilla/5.0"},
|
||||
) as req:
|
||||
req.raise_for_status()
|
||||
with open(f"{save_dir}/{file_name}", "wb") as download:
|
||||
for chunk in req.iter_content(chunk_size=8192):
|
||||
|
@ -452,9 +474,9 @@ class FileCmds:
|
|||
Takes (str) file_name
|
||||
Returns (dict) with (bool) status and (str) msg
|
||||
"""
|
||||
file_name = f"{CFG_DIR}/{file_name}"
|
||||
file_path = f"{CFG_DIR}/{file_name}"
|
||||
try:
|
||||
with open(file_name, "w", encoding="ISO-8859-1") as json_file:
|
||||
with open(file_path, "w", encoding="ISO-8859-1") as json_file:
|
||||
version = self.ractl.get_server_info()["version"]
|
||||
devices = self.ractl.list_devices()["device_list"]
|
||||
for device in devices:
|
||||
|
@ -485,7 +507,7 @@ class FileCmds:
|
|||
indent=4
|
||||
)
|
||||
parameters = {
|
||||
"target_path": file_name
|
||||
"target_path": file_path
|
||||
}
|
||||
return {
|
||||
"status": True,
|
||||
|
@ -494,28 +516,21 @@ class FileCmds:
|
|||
}
|
||||
except (IOError, ValueError, EOFError, TypeError) as error:
|
||||
logging.error(str(error))
|
||||
self.delete_file(file_name)
|
||||
self.delete_file(Path(file_path))
|
||||
return {"status": False, "msg": str(error)}
|
||||
except:
|
||||
logging.error("Could not write to file: %s", file_name)
|
||||
self.delete_file(file_name)
|
||||
parameters = {
|
||||
"file_name": file_name
|
||||
}
|
||||
return {
|
||||
"status": False,
|
||||
"return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE,
|
||||
"parameters": parameters,
|
||||
}
|
||||
logging.error(FILE_WRITE_ERROR, file_name)
|
||||
self.delete_file(Path(file_path))
|
||||
raise
|
||||
|
||||
def read_config(self, file_name):
|
||||
"""
|
||||
Takes (str) file_name
|
||||
Returns (dict) with (bool) status and (str) msg
|
||||
"""
|
||||
file_name = f"{CFG_DIR}/{file_name}"
|
||||
file_path = Path(CFG_DIR) / file_name
|
||||
try:
|
||||
with open(file_name, encoding="ISO-8859-1") as json_file:
|
||||
with open(file_path, encoding="ISO-8859-1") as json_file:
|
||||
config = load(json_file)
|
||||
# If the config file format changes again in the future,
|
||||
# introduce more sophisticated format detection logic here.
|
||||
|
@ -577,15 +592,8 @@ class FileCmds:
|
|||
logging.error(str(error))
|
||||
return {"status": False, "msg": str(error)}
|
||||
except:
|
||||
logging.error("Could not read file: %s", file_name)
|
||||
parameters = {
|
||||
"file_name": file_name
|
||||
}
|
||||
return {
|
||||
"status": False,
|
||||
"return_code": ReturnCodes.READCONFIG_COULD_NOT_READ,
|
||||
"parameters": parameters
|
||||
}
|
||||
logging.error(FILE_READ_ERROR, str(file_path))
|
||||
raise
|
||||
|
||||
def write_drive_properties(self, file_name, conf):
|
||||
"""
|
||||
|
@ -593,12 +601,12 @@ class FileCmds:
|
|||
Takes file name base (str) and (list of dicts) conf as arguments
|
||||
Returns (dict) with (bool) status and (str) msg
|
||||
"""
|
||||
file_path = f"{CFG_DIR}/{file_name}"
|
||||
file_path = Path(CFG_DIR) / file_name
|
||||
try:
|
||||
with open(file_path, "w") as json_file:
|
||||
dump(conf, json_file, indent=4)
|
||||
parameters = {
|
||||
"target_path": file_path
|
||||
"target_path": str(file_path)
|
||||
}
|
||||
return {
|
||||
"status": True,
|
||||
|
@ -610,29 +618,22 @@ class FileCmds:
|
|||
self.delete_file(file_path)
|
||||
return {"status": False, "msg": str(error)}
|
||||
except:
|
||||
logging.error("Could not write to file: %s", file_path)
|
||||
logging.error(FILE_WRITE_ERROR, str(file_path))
|
||||
self.delete_file(file_path)
|
||||
parameters = {
|
||||
"target_path": file_path
|
||||
}
|
||||
return {
|
||||
"status": False,
|
||||
"return_code": ReturnCodes.WRITEFILE_COULD_NOT_WRITE,
|
||||
"parameters": parameters,
|
||||
}
|
||||
raise
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
def read_drive_properties(self, file_path):
|
||||
"""
|
||||
Reads drive properties from json formatted file.
|
||||
Takes (str) file_path as argument.
|
||||
Takes (Path) file_path as argument.
|
||||
Returns (dict) with (bool) status, (str) msg, (dict) conf
|
||||
"""
|
||||
try:
|
||||
with open(file_path) as json_file:
|
||||
conf = load(json_file)
|
||||
parameters = {
|
||||
"file_path": file_path
|
||||
"file_path": str(file_path)
|
||||
}
|
||||
return {
|
||||
"status": True,
|
||||
|
@ -644,15 +645,8 @@ class FileCmds:
|
|||
logging.error(str(error))
|
||||
return {"status": False, "msg": str(error)}
|
||||
except:
|
||||
logging.error("Could not read file: %s", file_path)
|
||||
parameters = {
|
||||
"file_path": file_path
|
||||
}
|
||||
return {
|
||||
"status": False,
|
||||
"return_codes": ReturnCodes.READDRIVEPROPS_COULD_NOT_READ,
|
||||
"parameters": parameters,
|
||||
}
|
||||
logging.error(FILE_READ_ERROR, str(file_path))
|
||||
raise
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
async def run_async(self, program, args):
|
||||
|
@ -687,5 +681,6 @@ class FileCmds:
|
|||
"""
|
||||
try:
|
||||
return unarchiver.inspect_archive(file_path)
|
||||
except (unarchiver.LsarCommandError, unarchiver.LsarOutputError):
|
||||
except (unarchiver.LsarCommandError, unarchiver.LsarOutputError) as error:
|
||||
logging.error(str(error))
|
||||
raise
|
||||
|
|
|
@ -224,16 +224,13 @@ class RaCtlCmds:
|
|||
devices = proto.PbDeviceDefinition()
|
||||
devices.id = int(scsi_id)
|
||||
|
||||
if "device_type" in kwargs.keys():
|
||||
if kwargs["device_type"]:
|
||||
devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"]))
|
||||
if "unit" in kwargs.keys():
|
||||
if kwargs["unit"]:
|
||||
devices.unit = kwargs["unit"]
|
||||
if "params" in kwargs.keys():
|
||||
if isinstance(kwargs["params"], dict):
|
||||
for param in kwargs["params"]:
|
||||
devices.params[param] = kwargs["params"][param]
|
||||
if kwargs.get("device_type"):
|
||||
devices.type = proto.PbDeviceType.Value(str(kwargs["device_type"]))
|
||||
if kwargs.get("unit"):
|
||||
devices.unit = kwargs["unit"]
|
||||
if kwargs.get("params") and isinstance(kwargs["params"], dict):
|
||||
for param in kwargs["params"]:
|
||||
devices.params[param] = kwargs["params"][param]
|
||||
|
||||
# Handling the inserting of media into an attached removable type device
|
||||
device_type = kwargs.get("device_type", None)
|
||||
|
@ -260,18 +257,14 @@ class RaCtlCmds:
|
|||
# Handling attaching a new device
|
||||
else:
|
||||
command.operation = proto.PbOperation.ATTACH
|
||||
if "vendor" in kwargs.keys():
|
||||
if kwargs["vendor"]:
|
||||
devices.vendor = kwargs["vendor"]
|
||||
if "product" in kwargs.keys():
|
||||
if kwargs["product"]:
|
||||
devices.product = kwargs["product"]
|
||||
if "revision" in kwargs.keys():
|
||||
if kwargs["revision"]:
|
||||
devices.revision = kwargs["revision"]
|
||||
if "block_size" in kwargs.keys():
|
||||
if kwargs["block_size"]:
|
||||
devices.block_size = int(kwargs["block_size"])
|
||||
if kwargs.get("vendor"):
|
||||
devices.vendor = kwargs["vendor"]
|
||||
if kwargs.get("product"):
|
||||
devices.product = kwargs["product"]
|
||||
if kwargs.get("revision"):
|
||||
devices.revision = kwargs["revision"]
|
||||
if kwargs.get("block_size"):
|
||||
devices.block_size = int(kwargs["block_size"])
|
||||
|
||||
command.devices.append(devices)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ from shutil import disk_usage
|
|||
from re import findall, match
|
||||
from socket import socket, gethostname, AF_INET, SOCK_DGRAM
|
||||
|
||||
from rascsi.common_settings import SHELL_ERROR
|
||||
|
||||
class SysCmds:
|
||||
"""
|
||||
|
@ -32,8 +33,7 @@ class SysCmds:
|
|||
.strip()
|
||||
)
|
||||
except subprocess.CalledProcessError as error:
|
||||
logging.warning("Executed shell command: %s", " ".join(error.cmd))
|
||||
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
|
||||
logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8"))
|
||||
ra_git_version = ""
|
||||
|
||||
try:
|
||||
|
@ -47,9 +47,8 @@ class SysCmds:
|
|||
.strip()
|
||||
)
|
||||
except subprocess.CalledProcessError as error:
|
||||
logging.warning("Executed shell command: %s", " ".join(error.cmd))
|
||||
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
|
||||
pi_version = "Unknown"
|
||||
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||
pi_version = "?"
|
||||
|
||||
return {"git": ra_git_version, "env": pi_version}
|
||||
|
||||
|
@ -70,8 +69,7 @@ class SysCmds:
|
|||
.strip()
|
||||
)
|
||||
except subprocess.CalledProcessError as error:
|
||||
logging.warning("Executed shell command: %s", " ".join(error.cmd))
|
||||
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
|
||||
logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8"))
|
||||
processes = ""
|
||||
|
||||
matching_processes = findall(daemon, processes)
|
||||
|
@ -93,8 +91,7 @@ class SysCmds:
|
|||
.strip()
|
||||
)
|
||||
except subprocess.CalledProcessError as error:
|
||||
logging.warning("Executed shell command: %s", " ".join(error.cmd))
|
||||
logging.warning("Got error: %s", error.stderr.decode("utf-8"))
|
||||
logging.warning(SHELL_ERROR, error.cmd, error.stderr.decode("utf-8"))
|
||||
bridges = ""
|
||||
|
||||
if "rascsi_bridge" in bridges:
|
||||
|
@ -181,3 +178,18 @@ class SysCmds:
|
|||
return process.returncode, process.stdout.decode("utf-8")
|
||||
|
||||
return process.returncode, process.stderr.decode("utf-8")
|
||||
|
||||
@staticmethod
|
||||
def get_manpage(file_path):
|
||||
"""
|
||||
Takes (str) file_path path to image file to generate manpage for.
|
||||
Returns either the man2html output, or the stderr output.
|
||||
"""
|
||||
process = run(
|
||||
["man2html", file_path, "-M", "/"],
|
||||
capture_output=True,
|
||||
)
|
||||
if process.returncode == 0:
|
||||
return process.returncode, process.stdout.decode("utf-8")
|
||||
|
||||
return process.returncode, process.stderr.decode("utf-8")
|
||||
|
|
|
@ -69,7 +69,7 @@ def extract_archive(file_path, **kwargs):
|
|||
unar_result_success = r'^Successfully extracted to "(?P<destination>.+)".$'
|
||||
unar_result_no_files = "No files extracted."
|
||||
unar_file_extracted = \
|
||||
r"^ (?P<path>.+). \(((?P<size>[0-9]+) B)?(?P<types>(dir)?(, )?(rsrc)?)\)\.\.\. (?P<status>[A-Z]+)\.$"
|
||||
r"^ {2}(?P<path>.+). \(((?P<size>\d+) B)?(?P<types>(dir)?(, )?(rsrc)?)\)\.\.\. (?P<status>[A-Z]+)\.$"
|
||||
|
||||
lines = process["stdout"].rstrip("\n").split("\n")
|
||||
|
||||
|
@ -141,7 +141,7 @@ def extract_archive(file_path, **kwargs):
|
|||
# The parent dir may not be specified as a member, so ensure it exists
|
||||
target_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
logging.debug("Moving temp file: %s -> %s", source_path, target_path)
|
||||
move(source_path, target_path)
|
||||
move(str(source_path), str(target_path))
|
||||
moved.append(member)
|
||||
|
||||
return {
|
||||
|
@ -152,7 +152,7 @@ def extract_archive(file_path, **kwargs):
|
|||
raise UnarUnexpectedOutputError(lines[-1])
|
||||
|
||||
|
||||
def inspect_archive(file_path, **kwargs):
|
||||
def inspect_archive(file_path):
|
||||
"""
|
||||
Calls `lsar` to inspect the contents of an archive
|
||||
Takes (str) file_path
|
||||
|
|
|
@ -2,3 +2,5 @@ pytest==7.1.3
|
|||
pytest-httpserver==1.0.6
|
||||
black==22.8.0
|
||||
flake8==5.0.4
|
||||
watchdog==2.1.9
|
||||
requests==2.28.1
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<html>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>RaSCSI-Web is Starting</title>
|
||||
<meta http-equiv="refresh" content="2">
|
||||
|
|
|
@ -434,7 +434,7 @@
|
|||
{
|
||||
"device_type": "SCCD",
|
||||
"vendor": null,
|
||||
"product": null,
|
||||
"product": "SCSI CD-ROM 512",
|
||||
"revision": null,
|
||||
"block_size": 512,
|
||||
"size": null,
|
||||
|
|
|
@ -2,19 +2,7 @@ body {
|
|||
color: black;
|
||||
background-color: white;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: white;
|
||||
font-size:20px;
|
||||
background-color:black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: black;
|
||||
font-size:16px;
|
||||
margin: 0px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a {
|
||||
|
@ -27,10 +15,50 @@ form {
|
|||
|
||||
table, tr, td {
|
||||
border: 1px solid black;
|
||||
border-collapse:collapse;
|
||||
border-collapse: collapse;
|
||||
margin: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
color: black;
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
summary.heading {
|
||||
color: black;
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
div.header {
|
||||
color: white;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
div.logged_in {
|
||||
background-color: green;
|
||||
}
|
||||
|
||||
div.logged_out {
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
input.lun {
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
div.flash {
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
|
@ -61,71 +89,76 @@ div.flash div.info {
|
|||
}
|
||||
|
||||
td.inactive {
|
||||
text-align:center;
|
||||
background-color:tan;
|
||||
text-align: center;
|
||||
background-color: tan;
|
||||
}
|
||||
|
||||
summary.heading {
|
||||
color: black;
|
||||
font-size: large;
|
||||
font-weight: bold;
|
||||
margin: 0px;
|
||||
ul.inline_list {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.dropzone, .dropzone * {
|
||||
box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.dropzone {
|
||||
position: relative;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.dropzone .dz-button {
|
||||
position: relative;
|
||||
background-color: white;
|
||||
color: black;
|
||||
border: 2px dashed blue;
|
||||
padding: 12px 28px;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
margin: .5em;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 120px;
|
||||
margin: .5em;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-progress {
|
||||
display: block;
|
||||
height: 15px;
|
||||
border: 1px solid #aaa;
|
||||
display: block;
|
||||
height: 15px;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-progress .dz-upload {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: green;
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background: green;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-error-message {
|
||||
color: red;
|
||||
display: none;
|
||||
color: red;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview.dz-error .dz-error-message {
|
||||
display: block;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview.dz-error .dz-error-mark {
|
||||
display: block;
|
||||
filter: drop-shadow(0px 0px 2px red);
|
||||
display: block;
|
||||
filter: drop-shadow(0px 0px 2px red);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.dropzone .dz-preview.dz-success .dz-success-mark {
|
||||
display: block;
|
||||
filter: drop-shadow(0px 0px 2px green);
|
||||
display: block;
|
||||
filter: drop-shadow(0px 0px 2px green);
|
||||
}
|
||||
|
||||
.dropzone .dz-preview .dz-error-mark, .dropzone .dz-preview .dz-success-mark {
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 30px;
|
||||
top: 30px;
|
||||
width: 54px;
|
||||
height: 58px;
|
||||
left: 50%;
|
||||
margin-left: -27px;
|
||||
position: absolute;
|
||||
display: none;
|
||||
top: 30px;
|
||||
width: 54px;
|
||||
height: 58px;
|
||||
left: 50%;
|
||||
margin-left: -27px;
|
||||
}
|
||||
|
|
|
@ -1,80 +1,76 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<html lang="{{ env["locale"] }}">
|
||||
<head>
|
||||
<title>{{ _("RaSCSI Reloaded Control Page") }} [{{ env["host"] }}]</title>
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/pwa/apple-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/pwa/apple-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/pwa/apple-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/pwa/apple-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/pwa/apple-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/pwa/apple-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/pwa/apple-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/pwa/apple-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/pwa/apple-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/pwa/android-icon-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/pwa/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="/pwa/favicon-96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/pwa/favicon-16x16.png">
|
||||
<link rel="manifest" href="/pwa/manifest.json">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-TileImage" content="/pwa/ms-icon-144x144.png">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="/pwa/apple-icon-57x57.png">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="/pwa/apple-icon-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="/pwa/apple-icon-72x72.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="/pwa/apple-icon-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="/pwa/apple-icon-114x114.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="/pwa/apple-icon-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="/pwa/apple-icon-144x144.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="/pwa/apple-icon-152x152.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/pwa/apple-icon-180x180.png">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="/pwa/android-icon-192x192.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/pwa/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="/pwa/favicon-96x96.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/pwa/favicon-16x16.png">
|
||||
<link rel="manifest" href="/pwa/manifest.json">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-TileImage" content="/pwa/ms-icon-144x144.png">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
|
||||
<script type="application/javascript">
|
||||
var processNotify = function(Notification) {
|
||||
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>";
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
<script type="application/javascript">
|
||||
var processNotify = function(Notification) {
|
||||
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" This process may take a while, and will continue in the background if you navigate away from this page.") }}</div>";
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
|
||||
var shutdownNotify = function(Notification) {
|
||||
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>";
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
|
||||
var shutdownNotify = function(Notification) {
|
||||
document.getElementById("flash").innerHTML = "<div class=\"info\">" + Notification + "{{ _(" The Web Interface will become unresponsive momentarily. Reload this page after the Pi has started up again.") }}</div>";
|
||||
window.scrollTo(0,0);
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="application/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.9.3/min/dropzone.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="content">
|
||||
<div class="header">
|
||||
{% if env["auth_active"] %}
|
||||
{% if env["username"] %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }} – <a href="/logout">{{ _("Log Out") }}</a></span>
|
||||
<div align="center" class="logged_in">
|
||||
{{ _("Logged in as <em>%(username)s</em>", username=env["username"]) }} - <a href="/logout">{{ _("Log Out") }}</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: red; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">
|
||||
<div align="center" class="logged_out">
|
||||
<form method="POST" action="/login">
|
||||
<div>{{ _("Log In to Use Web Interface") }}</div>
|
||||
<input type="text" name="username" placeholder="{{ _("Username") }}">
|
||||
<input type="password" name="password" placeholder="{{ _("Password") }}">
|
||||
<label for="username">{{ _("Username") }}</label>
|
||||
<input type="text" name="username" id="username">
|
||||
<label for="password">{{ _("Password") }}</label>
|
||||
<input type="password" name="password" id="password">
|
||||
<input type="submit" value="Login">
|
||||
</form>
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span style="display: inline-block; width: 100%; color: white; background-color: green; text-align: center; vertical-align: center; font-family: Arial, Helvetica, sans-serif;">{{ _("Web Interface Authentication Disabled") }} – {{ _("See <a href=\"%(url)s\" target=\"_blank\">Wiki</a> for more information", url="https://github.com/akuker/RASCSI/wiki/Web-Interface#enable-authentication") }}</span>
|
||||
<div align="center" class="logged_out">
|
||||
{{ _("Web Interface Authentication Disabled") }} - {{ _("See <a href=\"%(url)s\" target=\"_blank\">Wiki</a> for more information", url="https://github.com/akuker/RASCSI/wiki/Web-Interface#enable-authentication") }}
|
||||
</div>
|
||||
{% endif %}
|
||||
<table width="100%" style="background-color: black;">
|
||||
<tbody>
|
||||
<tr align="center">
|
||||
<td>
|
||||
<a href="/">
|
||||
<h1>{{ _("RaSCSI Reloaded Control Page") }}</h1>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="color: white;">
|
||||
hostname: {{ env["host"] }} ip: {{ env["ip_addr"] }}
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div align="center">
|
||||
<a href="/">
|
||||
<h1>{{ _("RaSCSI Reloaded Control Page") }}</h1>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
hostname: {{ env["host"] }} ip: {{ env["ip_addr"] }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="flash" id="flash">
|
||||
{% for category, message in get_flashed_messages(with_categories=true) %}
|
||||
|
@ -88,9 +84,27 @@
|
|||
<div class="content">
|
||||
{% block content %}{% endblock content %}
|
||||
</div>
|
||||
<div class="footer">
|
||||
<center><tt>{{ _("RaSCSI Reloaded version: ") }}<strong>{{ version }} <a href="https://github.com/akuker/RASCSI/commit/{{ env["running_env"]["git"] }}" target="_blank">{{ env["running_env"]["git"][:7] }}</a></strong></tt></center>
|
||||
<center><tt>{{ _("Pi environment: ") }}{{ env["running_env"]["env"] }}</tt></center>
|
||||
<div align="center" class="footer">
|
||||
<div>
|
||||
{% if env["netatalk_configured"] == 1 %}
|
||||
{{ _("The AppleShare server is running. No active connections.") }}
|
||||
{% endif %}
|
||||
{% if env["netatalk_configured"] == 2 %}
|
||||
{{ _("%(value)d active AFP connection", value=(env["netatalk_configured"] - 1)) }}
|
||||
{% elif env["netatalk_configured"] > 2 %}
|
||||
{{ _("%(value)d active AFP connections", value=(env["netatalk_configured"] - 1)) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{% if env["macproxy_configured"] %}
|
||||
{{ _("Macproxy is running at %(ip_addr)s (default port 5000)", ip_addr=env['ip_addr']) }}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
{{ _("RaSCSI Reloaded version: ") }}<b>{{ env["version"] }} <a href="https://github.com/akuker/RASCSI/commit/{{ env["running_env"]["git"] }}" target="_blank">{{ env["running_env"]["git"][:7] }}</a></b>
|
||||
</div>
|
||||
<div>
|
||||
{{ _("Pi environment: ") }}{{ env["running_env"]["env"] }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1,52 +1,52 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ _("Detailed Info for Attached Devices") }}</h3>
|
||||
<h2>{{ _("Detailed Info for Attached Devices") }}</h2>
|
||||
{% for device in devices %}
|
||||
<p>
|
||||
<table border="black" cellpadding="3">
|
||||
<table border="black" cellpadding="3" summary="Detailed information for attached devices">
|
||||
<tr>
|
||||
<td><i>{{ _("SCSI ID") }}</i></td>
|
||||
<th scope="row">{{ _("SCSI ID") }}</th>
|
||||
<td>{{ device["id"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("LUN") }}</i></td>
|
||||
<th scope="row">{{ _("LUN") }}</th>
|
||||
<td>{{ device["unit"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Type") }}</i></td>
|
||||
<th scope="row">{{ _("Type") }}</th>
|
||||
<td>{{ device["device_type"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Status") }}</i></td>
|
||||
<th scope="row">{{ _("Status") }}</th>
|
||||
<td>{{ device["status"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("File") }}</i></td>
|
||||
<th scope="row">{{ _("File") }}</th>
|
||||
<td>{{ device["image"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Parameters") }}</i></td>
|
||||
<th scope="row">{{ _("Parameters") }}</th>
|
||||
<td>{{ device["params"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Vendor") }}</i></td>
|
||||
<th scope="row">{{ _("Vendor") }}</th>
|
||||
<td>{{ device["vendor"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Product") }}</i></td>
|
||||
<th scope="row">{{ _("Product") }}</th>
|
||||
<td>{{ device["product"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Revision") }}</i></td>
|
||||
<th scope="row">{{ _("Revision") }}</th>
|
||||
<td>{{ device["revision"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Block Size") }}</i></td>
|
||||
<th scope="row">{{ _("Block Size") }}</th>
|
||||
<td>{{ device["block_size"] }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><i>{{ _("Image Size") }}</i></td>
|
||||
<th scope="row">{{ _("Image Size") }}</th>
|
||||
<td>{{ device["size"] }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ _("Disk Image Details: %(file_name)s", file_name=file_name) }}</h3>
|
||||
<h2>{{ _("Disk Image Details: %(file_name)s", file_name=file_name) }}</h2>
|
||||
<p><pre>{{ diskinfo }}</pre></p>
|
||||
<p><a href="/">{{ _("Go to Home") }}</a></p>
|
||||
|
||||
|
|
|
@ -5,38 +5,30 @@
|
|||
<p>{{ _("These device profiles are provided as-is with no guarantee to work equally to the actual physical device they are named after. You may need to provide appropirate device drivers and/or configuration parameters for them to function properly. If you would like to see data modified, or have additional devices to add to the list, please raise an issue ticket at <a href=\"%(url)s\">GitHub</a>.", url="https://github.com/akuker/RASCSI/issues") }}</p>
|
||||
<h2>{{ _("Hard Disk Drives") }}</h2>
|
||||
|
||||
<table cellpadding="3" border="black">
|
||||
<table cellpadding="3" border="black" summary="List of hard drives">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>{{ _("Name") }}</b></td>
|
||||
<td><b>{{ _("Size (MiB)") }}</b></td>
|
||||
<td><b>{{ _("Description") }}</b></td>
|
||||
<td><b>{{ _("Ref.") }}</b></td>
|
||||
<td><b>{{ _("Action") }}</b></td>
|
||||
<th scope="col">{{ _("Name") }}</th>
|
||||
<th scope="col">{{ _("Size (MiB)") }}</th>
|
||||
<th scope="col">{{ _("Description") }}</th>
|
||||
<th scope="col">{{ _("Action") }}</th>
|
||||
</tr>
|
||||
{% for hd in drive_properties['hd_conf']|sort(attribute='name') %}
|
||||
{% for hd in env['drive_properties']['hd_conf']|sort(attribute='name') %}
|
||||
<tr>
|
||||
<td style="text-align:center">{{ hd.name }}</td>
|
||||
<td style="text-align:center">{{ hd.size_mb }}</td>
|
||||
<td style="text-align:left">{{ hd.description }}</td>
|
||||
<td style="text-align:left">
|
||||
{% if hd.url != "" %}
|
||||
<a href="{{ hd.url }}">{{ _("Link") }}</a>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align:left">
|
||||
<td align="center">
|
||||
{% if hd.url != "" %}
|
||||
<a href="{{ hd.url }}">{{ hd.name }}</a>
|
||||
{% else %}
|
||||
{{ hd.name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td align="center">{{ hd.size_mb }}</td>
|
||||
<td>{{ hd.description }}</td>
|
||||
<td>
|
||||
<form action="/drive/create" method="post">
|
||||
<input type="hidden" name="vendor" value="{{ hd.vendor }}">
|
||||
<input type="hidden" name="product" value="{{ hd.product }}">
|
||||
<input type="hidden" name="revision" value="{{ hd.revision }}">
|
||||
<input type="hidden" name="blocks" value="{{ hd.blocks }}">
|
||||
<input type="hidden" name="block_size" value="{{ hd.block_size }}">
|
||||
{{ _("Size:") }} <input type="number" name="size" min="512" max="274877906944" step="512" value="{{ hd.size }}">{{ _("B") }}
|
||||
<input type="hidden" name="file_type" value="{{ hd.file_type }}">
|
||||
<label for="file_name">{{ _("Save as:") }}</label>
|
||||
<input type="text" name="file_name" value="{{ hd.secure_name }}" required />.{{ hd.file_type }}
|
||||
<input type="hidden" name="drive_name" value="{{ hd.name }}">
|
||||
<label for="file_name_{{ hd.name }}">{{ _("Save as:") }}</label>
|
||||
<input type="text" name="file_name" id="file_name_{{ hd.name }}" value="{{ hd.secure_name }}" required />.{{ hd.file_type }}
|
||||
<input type="submit" value="{{ _("Create") }}" />
|
||||
</form>
|
||||
</td>
|
||||
|
@ -49,41 +41,34 @@
|
|||
|
||||
<h2>{{ _("CD/DVD Drives") }}</h2>
|
||||
<p><em>{{ _("This will create a properties file for the given CD-ROM or DVD image. No new image file will be created.") }}</em></p>
|
||||
<table cellpadding="3" border="black">
|
||||
<table cellpadding="3" border="black" summary="List of CD-ROM or DVD drives">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>{{ _("Name") }}</b></td>
|
||||
<td><b>{{ _("Size (MiB)") }}</b></td>
|
||||
<td><b>{{ _("Description") }}</b></td>
|
||||
<td><b>{{ _("Ref.") }}</b></td>
|
||||
<td><b>{{ _("Action") }}</b></td>
|
||||
<th scope="col">{{ _("Name") }}</th>
|
||||
<th scope="col">{{ _("Description") }}</th>
|
||||
<th scope="col">{{ _("Action") }}</th>
|
||||
</tr>
|
||||
{% for cd in drive_properties['cd_conf']|sort(attribute='name') %}
|
||||
{% for cd in env['drive_properties']['cd_conf']|sort(attribute='name') %}
|
||||
<tr>
|
||||
<td style="text-align:center">{{ cd.name }}</td>
|
||||
<td style="text-align:center">{{ cd.size_mb }}</td>
|
||||
<td style="text-align:left">{{ cd.description }}</td>
|
||||
<td style="text-align:left">
|
||||
{% if cd.url != "" %}
|
||||
<a href="{{ cd.url }}">{{ _("Link") }}</a>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
<td align="center">
|
||||
{% if cd.url != "" %}
|
||||
<a href="{{ cd.url }}">{{ cd.name }}</a>
|
||||
{% else %}
|
||||
{{ cd.name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align:left">
|
||||
<td>{{ cd.description }}</td>
|
||||
<td>
|
||||
<form action="/drive/cdrom" method="post">
|
||||
<input type="hidden" name="vendor" value="{{ cd.vendor }}">
|
||||
<input type="hidden" name="product" value="{{ cd.product }}">
|
||||
<input type="hidden" name="revision" value="{{ cd.revision }}">
|
||||
<input type="hidden" name="block_size" value="{{ cd.block_size }}">
|
||||
<label for="file_name">{{ _("Create for:") }}</label>
|
||||
<select type="select" name="file_name">
|
||||
<input type="hidden" name="drive_name" value="{{ cd.name }}">
|
||||
<label for="file_name_{{ cd.name }}">{{ _("Create for:") }}</label>
|
||||
<select type="select" name="file_name" id="file_name_{{ cd.name }}">
|
||||
{% for file in files|sort(attribute='name') %}
|
||||
{% if file["name"].lower().endswith(cdrom_file_suffix) %}
|
||||
<option value="{{ file["name"] }}">{{ file["name"].replace(base_dir, '') }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% if file["name"].lower().endswith(env['cd_suffixes']) %}
|
||||
<option value="{{ file["name"] }}">{{ file["name"].replace(env["image_dir"], '') }}</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Create") }}" />
|
||||
</form>
|
||||
</td>
|
||||
|
@ -95,38 +80,30 @@
|
|||
<hr/>
|
||||
|
||||
<h2>{{ _("Removable Disk Drives") }}</h2>
|
||||
<table cellpadding="3" border="black">
|
||||
<table cellpadding="3" border="black" summary="List of removable disk drives">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>{{ _("Name") }}</b></td>
|
||||
<td><b>{{ _("Size (MiB)") }}</b></td>
|
||||
<td><b>{{ _("Description") }}</b></td>
|
||||
<td><b>{{ _("Ref.") }}</b></td>
|
||||
<td><b>{{ _("Action") }}</b></td>
|
||||
<th scope="col">{{ _("Name") }}</th>
|
||||
<th scope="col">{{ _("Size (MiB)") }}</th>
|
||||
<th scope="col">{{ _("Description") }}</th>
|
||||
<th scope="col">{{ _("Action") }}</th>
|
||||
</tr>
|
||||
{% for rm in drive_properties['rm_conf']|sort(attribute='name') %}
|
||||
{% for rm in env['drive_properties']['rm_conf']|sort(attribute='name') %}
|
||||
<tr>
|
||||
<td style="text-align:center">{{ rm.name }}</td>
|
||||
<td style="text-align:center">{{ rm.size_mb }}</td>
|
||||
<td style="text-align:left">{{ rm.description }}</td>
|
||||
<td style="text-align:left">
|
||||
{% if rm.url != "" %}
|
||||
<a href="{{ rm.url }}">{{ _("Link") }}</a>
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
<td align="center">
|
||||
{% if rm.url != "" %}
|
||||
<a href="{{ rm.url }}">{{ rm.name }}</a>
|
||||
{% else %}
|
||||
{{ rm.name }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align:left">
|
||||
<td align="center">{{ rm.size_mb }}</td>
|
||||
<td>{{ rm.description }}</td>
|
||||
<td>
|
||||
<form action="/drive/create" method="post">
|
||||
<input type="hidden" name="vendor" value="{{ rm.vendor }}">
|
||||
<input type="hidden" name="product" value="{{ rm.product }}">
|
||||
<input type="hidden" name="revision" value="{{ rm.revision }}">
|
||||
<input type="hidden" name="blocks" value="{{ rm.blocks }}">
|
||||
<input type="hidden" name="block_size" value="{{ rm.block_size }}">
|
||||
{{ _("Size:") }} <input type="number" name="size" min="512" max="274877906944" step="512" value="{{ rm.size }}">{{ _("B") }}
|
||||
<input type="hidden" name="file_type" value="{{ rm.file_type }}">
|
||||
<label for="file_name">{{ _("Save as:") }}</label>
|
||||
<input type="text" name="file_name" value="{{ rm.secure_name }}" required />.{{ rm.file_type }}
|
||||
<input type="hidden" name="drive_name" value="{{ rm.name }}">
|
||||
<label for="file_name_{{ rm.name }}">{{ _("Save as:") }}</label>
|
||||
<input type="text" name="file_name" id="file_name_{{ rm.name }}" value="{{ rm.secure_name }}" required />.{{ rm.file_type }}
|
||||
<input type="submit" value="{{ _("Create") }}" />
|
||||
</form>
|
||||
</td>
|
||||
|
|
|
@ -6,18 +6,19 @@
|
|||
{{ _("Current RaSCSI Configuration") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Displays the currently attached devices for each available SCSI ID.") }}</li>
|
||||
<li>{{ _("Save and load device configurations, stored as json files in <tt>%(config_dir)s</tt>", config_dir=CFG_DIR) }}</tt></li>
|
||||
<li>{{ _("Save and load device configurations, stored as json files in <tt>%(config_dir)s</tt>", config_dir=CFG_DIR) }}</li>
|
||||
<li>{{ _("To have a particular device configuration load when RaSCSI starts, save it as <em>default</em>.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<p><form action="/config/load" method="post">
|
||||
<select name="name" required="" width="14">
|
||||
<p>
|
||||
<form action="/config/load" method="post">
|
||||
<label for="config_load_name">{{ _("File name") }}</label>
|
||||
<select name="name" id="config_load_name" required="" width="14">
|
||||
{% if config_files %}
|
||||
{% for config in config_files|sort %}
|
||||
<option value="{{ config }}">
|
||||
{{ config.replace(".json", '') }}
|
||||
{{ config }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
@ -28,55 +29,59 @@
|
|||
</select>
|
||||
<input name="load" type="submit" value="{{ _("Load") }}" onclick="return confirm('{{ _("Detach all current device and Load configuration?") }}')">
|
||||
<input name="delete" type="submit" value="{{ _("Delete") }}" onclick="return confirm('{{ _("Delete configuration file?") }}')">
|
||||
</form></p>
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<p><form action="/config/save" method="post">
|
||||
<input name="name" placeholder="default" size="20">
|
||||
<p>
|
||||
<form action="/config/save" method="post">
|
||||
<label for="config_save_name">{{ _("File name") }}</label>
|
||||
<input name="name" id="config_save_name" placeholder="default" size="20">
|
||||
.{{ CONFIG_FILE_SUFFIX }}
|
||||
<input type="submit" value="{{ _("Save") }}">
|
||||
</form></p>
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<table border="black" cellpadding="3">
|
||||
<table border="black" cellpadding="3" summary="List of attached devices">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><b>{{ _("ID") }}</b></td>
|
||||
<th scope="col">{{ _("ID") }}</th>
|
||||
{% if units %}
|
||||
<td><b>{{ _("LUN") }}</b></td>
|
||||
<th scope="col">{{ _("LUN") }}</th>
|
||||
{% endif %}
|
||||
<td><b>{{ _("Type") }}</b></td>
|
||||
<td><b>{{ _("Status") }}</b></td>
|
||||
<td><b>{{ _("File") }}</b></td>
|
||||
<td><b>{{ _("Product") }}</b></td>
|
||||
<td><b>{{ _("Actions") }}</b></td>
|
||||
<th scope="col">{{ _("Device") }}</th>
|
||||
<th scope="col">{{ _("Parameters") }}</th>
|
||||
<th scope="col">{{ _("Product") }}</th>
|
||||
<th scope="col">{{ _("Actions") }}</th>
|
||||
</tr>
|
||||
{% for device in devices %}
|
||||
{% for device in devices | sort(attribute='id') %}
|
||||
<tr>
|
||||
{% if device["id"] not in reserved_scsi_ids %}
|
||||
<td style="text-align:center">{{ device.id }}</td>
|
||||
<td align="center">{{ device.id }}</td>
|
||||
{% if units %}
|
||||
<td style="text-align:center">{{ device.unit }}</td>
|
||||
<td align="center">{{ device.unit }}</td>
|
||||
{% endif %}
|
||||
<td style="text-align:center">{{ device.device_type }}</td>
|
||||
<td style="text-align:center">{{ device.status }}</td>
|
||||
<td style="text-align:left">
|
||||
<td align="center">{{ device.device_name }}</td>
|
||||
<td>
|
||||
{% if "No Media" in device.status %}
|
||||
<form action="/scsi/attach" method="post">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input name="unit" type="hidden" value="{{ device.unit }}">
|
||||
<input name="type" type="hidden" value="{{ device.device_type }}">
|
||||
<input name="file_size" type="hidden" value="{{ device.size }}">
|
||||
<select type="select" name="file_name">
|
||||
<label for="device_list_file_name_{{ device.id }}_{{ device.unit }}">{{ _("File name") }}</label>
|
||||
<select type="select" name="file_name" id="device_list_file_name_{{ device.id }}_{{ device.unit }}">
|
||||
{% for f in files|sort(attribute='name') %}
|
||||
{% if device.device_type == "SCCD" %}
|
||||
{% if f["name"].lower().endswith(cdrom_file_suffix) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(base_dir, '') }}</option>
|
||||
{% if f["name"].lower().endswith(env['cd_suffixes']) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
|
||||
{% endif %}
|
||||
{% elif device.device_type == "SCRM" %}
|
||||
{% if f["name"].lower().endswith(removable_file_suffix) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(base_dir, '') }}</option>
|
||||
{% if f["name"].lower().endswith(env['rm_suffixes']) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
|
||||
{% endif %}
|
||||
{% elif device.device_type == "SCMO" %}
|
||||
{% if f["name"].lower().endswith(mo_file_suffix) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(base_dir, '') }}</option>
|
||||
{% if f["name"].lower().endswith(env['mo_suffixes']) %}
|
||||
<option value="{{ f["name"] }}">{{ f["name"].replace(env["image_dir"], '') }}</option>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
@ -84,16 +89,32 @@
|
|||
<input type="submit" value="{{ _("Attach") }}">
|
||||
</form>
|
||||
{% else %}
|
||||
{% if device.params %}
|
||||
{% for key in device.params %}
|
||||
{% if key == "interface" %}
|
||||
({{device.params[key]}})
|
||||
{% elif key == "timeout" %}
|
||||
({{key}}:{{device.params[key]}})
|
||||
{% else %}
|
||||
{{device.params[key]}}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% elif device.file %}
|
||||
{{ device.file }}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% if device.vendor == "RaSCSI" %}
|
||||
<td style="text-align:center">{{ device.product }}</td>
|
||||
{% else %}
|
||||
<td style="text-align:center">{{ device.vendor }} {{ device.product }}</td>
|
||||
{% endif %}
|
||||
<td style="text-align:center">
|
||||
{% if device.device_type != "-" %}
|
||||
</td>
|
||||
<td align="center">
|
||||
{% if device.vendor != "RaSCSI" %}
|
||||
{{ device.vendor }}
|
||||
{% endif %}
|
||||
{{ device.product }}
|
||||
{% if device.vendor != "RaSCSI" %}
|
||||
{{ device.revision }}
|
||||
{% endif %}
|
||||
</td>
|
||||
<td align="center">
|
||||
{% if device.id in scsi_ids["occupied_ids"] %}
|
||||
{% if device.device_type in REMOVABLE_DEVICE_TYPES and "No Media" not in device.status %}
|
||||
<form action="/scsi/eject" method="post" onsubmit="return confirm('{{ _("Eject Disk? WARNING: On Mac OS, eject the Disk in the Finder instead!") }}')">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
|
@ -106,20 +127,19 @@
|
|||
<input name="unit" type="hidden" value="{{ device.unit }}">
|
||||
<input type="submit" value="{{ _("Detach") }}">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/scsi/reserve" method="post" onsubmit="var memo = prompt('{{ _("Enter a memo for this reservation") }}'); if (memo === null) event.preventDefault(); document.getElementById('memo_{{ device.id }}').value = memo;">
|
||||
{% else %}
|
||||
<form action="/scsi/reserve" method="post" onsubmit="var memo = prompt('{{ _("Enter a memo for this reservation") }}'); if (memo === null) event.preventDefault(); document.getElementById('memo_{{ device.id }}').value = memo;">
|
||||
<input name="scsi_id" type="hidden" value="{{ device.id }}">
|
||||
<input name="memo" id="memo_{{ device.id }}" type="hidden" value="">
|
||||
<input type="submit" value="{{ _("Reserve") }}">
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
{% else %}
|
||||
<td class="inactive">{{ device.id }}</td>
|
||||
{% if units %}
|
||||
<td class="inactive"></td>
|
||||
{% endif %}
|
||||
<td class="inactive"></td>
|
||||
<td class="inactive">{{ _("Reserved ID") }}</td>
|
||||
<td class="inactive">{{ RESERVATIONS[device.id] }}</td>
|
||||
<td class="inactive"></td>
|
||||
|
@ -135,20 +155,14 @@
|
|||
</tbody>
|
||||
</table>
|
||||
|
||||
<table style="border: none;" cellpadding="3">
|
||||
<tr style="border: none;">
|
||||
<td style="border: none;">
|
||||
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('{{ _("Detach all SCSI Devices?") }}')">
|
||||
<input type="submit" value="{{ _("Detach All Devices") }}">
|
||||
</form>
|
||||
</td>
|
||||
<td style="border: none;">
|
||||
<form action="/scsi/info" method="post">
|
||||
<input type="submit" value="{{ _("Show Device Info") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<p>
|
||||
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('{{ _("Detach all SCSI Devices?") }}')">
|
||||
<input type="submit" value="{{ _("Detach All Devices") }}">
|
||||
</form>
|
||||
<form action="/scsi/info" method="post">
|
||||
<input type="submit" value="{{ _("Show Device Info") }}">
|
||||
</form>
|
||||
</p>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -157,10 +171,9 @@
|
|||
{{ _("Image File Management") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Manage image files in the active RaSCSI image directory: <tt>%(directory)s</tt> with a scan depth of %(scan_depth)s.", directory=base_dir, scan_depth=scan_depth) }}</li>
|
||||
<li>{{ _("Select a valid SCSI ID and <a href=\"%(url)s\">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.", url="https://en.wikipedia.org/wiki/Logical_unit_number") }}
|
||||
<li>{{ _("Manage image files in the active RaSCSI image directory: <tt>%(directory)s</tt> with a scan depth of %(scan_depth)s.", directory=env["image_dir"], scan_depth=scan_depth) }}</li>
|
||||
<li>{{ _("Select a valid SCSI ID and <a href=\"%(url)s\" target=\"_blank\">LUN</a> to attach to. Unless you know what you're doing, always use LUN 0.", url="https://en.wikipedia.org/wiki/Logical_unit_number") }}
|
||||
</li>
|
||||
<li>{{ _("If RaSCSI was unable to detect the media type associated with the image, you get to choose the type from the dropdown.") }}</li>
|
||||
<li>
|
||||
{{ _("Recognized image file types:") }}
|
||||
{% set comma = joiner(", ") %}
|
||||
|
@ -174,12 +187,12 @@
|
|||
</ul>
|
||||
</details>
|
||||
|
||||
<table border="black" cellpadding="3">
|
||||
<table border="black" cellpadding="3" summary="List of files in the image directory">
|
||||
<tbody>
|
||||
<tr style="font-weight: bold;">
|
||||
<td>{{ _("File") }}</td>
|
||||
<td>{{ _("Size") }}</td>
|
||||
<td>{{ _("Parameters and Actions") }}</td>
|
||||
<tr>
|
||||
<th scope="col">{{ _("File") }}</th>
|
||||
<th scope="col">{{ _("Size") }}</th>
|
||||
<th scope="col">{{ _("Actions") }}</th>
|
||||
</tr>
|
||||
{% for file in files|sort(attribute='name') %}
|
||||
<tr>
|
||||
|
@ -189,12 +202,12 @@
|
|||
<summary>
|
||||
{{ file["name"] }}
|
||||
</summary>
|
||||
<ul style="list-style: none;">
|
||||
<ul class="inline_list">
|
||||
{% for key in file["prop"] %}
|
||||
<li>{{ key }}: {{ file['prop'][key] }}</li>
|
||||
{% endfor %}
|
||||
<form action="/files/download" method="post">
|
||||
<input name="file" type="hidden" value="{{ CFG_DIR }}/{{ file['name'].replace(base_dir, '') }}.{{ PROPERTIES_SUFFIX }}">
|
||||
<input name="file" type="hidden" value="{{ CFG_DIR }}/{{ file['name'].replace(env['image_dir'], '') }}.{{ PROPERTIES_SUFFIX }}">
|
||||
<input type="submit" value="{{ _("Properties File") }} ↓">
|
||||
</form>
|
||||
</ul>
|
||||
|
@ -206,7 +219,7 @@
|
|||
<summary>
|
||||
{{ file["name"] }}
|
||||
</summary>
|
||||
<ul style="list-style: none;">
|
||||
<ul class="inline_list">
|
||||
{% for member in file["archive_contents"] %}
|
||||
{% if not member["is_properties_file"] %}
|
||||
<li>
|
||||
|
@ -220,7 +233,7 @@
|
|||
<input type="submit" value="{{ _("Extract") }}" onclick="processNotify('{{ _("Extracting a single file...") }}')">
|
||||
</form>
|
||||
</summary>
|
||||
<ul style="list-style: none;">
|
||||
<ul class="inline_list">
|
||||
<li>{{ member["related_properties_file"] }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
@ -241,9 +254,9 @@
|
|||
{% else %}
|
||||
<td>{{ file["name"] }}</td>
|
||||
{% endif %}
|
||||
<td style="text-align:center">
|
||||
<td align="center">
|
||||
<form action="/files/download" method="post">
|
||||
<input name="file" type="hidden" value="{{ base_dir }}/{{ file['name'] }}">
|
||||
<input name="file" type="hidden" value="{{ file['name'] }}">
|
||||
<input type="submit" value="{{ file['size_mb'] }} {{ _("MiB") }} ↓">
|
||||
</form>
|
||||
</td>
|
||||
|
@ -262,23 +275,24 @@
|
|||
<form action="/scsi/attach" method="post">
|
||||
<input name="file_name" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="file_size" type="hidden" value="{{ file['size'] }}">
|
||||
<label for="id">{{ _("ID") }}</label>
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option name="id" value="{{id}}"{% if id == recommended_id %} selected{% endif %}>
|
||||
<label for="image_list_scsi_id_{{ file["name"] }}">{{ _("ID") }}</label>
|
||||
<select name="scsi_id" id="image_list_scsi_id_{{ file["name"] }}">
|
||||
{% for id in scsi_ids["valid_ids"] %}
|
||||
<option name="id" value="{{id}}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
|
||||
{{ id }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="unit">{{ _("LUN") }}</label>
|
||||
<input name="unit" type="number" value="0" min="0" max="31" step="1">
|
||||
<label for="image_list_unit_{{ file["name"] }}">{{ _("LUN") }}</label>
|
||||
<input class="lun" name="unit" id="image_list_unit_{{ file["name"] }}" type="number" value="0" min="0" max="31" step="1" size="3">
|
||||
{% if file["detected_type"] != "UNDEFINED" %}
|
||||
<input name="type" type="hidden" value="{{ file['detected_type'] }}">
|
||||
{{ file['detected_type_name'] }}
|
||||
{% else %}
|
||||
<select name="type">
|
||||
<label for="image_list_type_{{ file["name"] }}">{{ _("Type") }}</label>
|
||||
<select name="type" id="image_list_type_{{ file["name"] }}">
|
||||
<option selected disabled value="">
|
||||
{{ _("Select media type") }}
|
||||
{{ _("Unknown") }}
|
||||
</option>
|
||||
{% for key, value in device_types.items() %}
|
||||
{% if key in DISK_DEVICE_TYPES %}
|
||||
|
@ -326,27 +340,23 @@
|
|||
{{ _("Attach Peripheral Device") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Before using a networking device, it is recommended to run easyinstall.sh from the command line to configure your Raspberry Pi.") }}
|
||||
</li>
|
||||
<ul>
|
||||
{% if bridge_configured %}
|
||||
<li>{{ _("The <tt>rascsi_bridge</tt> network bridge is active and ready to be used by an emulated network adapter!") }}</li>
|
||||
{% else %}
|
||||
{% else %}
|
||||
<li>{{ _("Please configure the <tt>rascsi_bridge</tt> network bridge before attaching an emulated network adapter!") }}</li>
|
||||
{% endif %}
|
||||
<li>{{ _("If you have a DHCP setup, choose only the interface you have configured the bridge with. You can ignore the inet field when attaching.") }}</li>
|
||||
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\">Macproxy</a>.", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}</li>
|
||||
<li>{{ _("To browse the modern web, install a vintage web proxy such as <a href=\"%(url)s\" target=\"_blank\">Macproxy</a>.", url="https://github.com/akuker/RASCSI/wiki/Vintage-Web-Proxy#macproxy") }}</li>
|
||||
</li>
|
||||
</ul>
|
||||
<li>{{ _("Read more about <a href=\"%(url)s\">supported device types</a> on the wiki.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types") }}
|
||||
<li>{{ _("Read more about <a href=\"%(url)s\" target=\"_blank\">supported device types</a> on the wiki.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types") }}
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table border="black" cellpadding="3">
|
||||
<tr style="font-weight: bold;">
|
||||
<td>{{ _("Device") }}</td>
|
||||
<td>{{ _("Code") }}</td>
|
||||
<td>{{ _("Parameters and Actions") }}</td>
|
||||
<table border="black" cellpadding="3" summary="List of peripheral devices">
|
||||
<tr>
|
||||
<th scope="col">{{ _("Device") }}</th>
|
||||
<th scope="col">{{ _("Key") }}</th>
|
||||
<th scope="col">{{ _("Parameters and Actions") }}</th>
|
||||
</tr>
|
||||
{% for type in REMOVABLE_DEVICE_TYPES + PERIPHERAL_DEVICE_TYPES %}
|
||||
<tr>
|
||||
|
@ -360,11 +370,11 @@
|
|||
<form action="/scsi/attach_device" method="post">
|
||||
<input name="type" type="hidden" value="{{ type }}">
|
||||
{% for key, value in device_types[type]["params"] | dictsort %}
|
||||
<label for="{{ key }}">{{ key }}:</label>
|
||||
<label for="param_{{ type }}_{{ key }}">{{ key }}:</label>
|
||||
{% if value.isnumeric() %}
|
||||
<input name="{{ key }}" type="number" value="{{ value }}">
|
||||
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="number" value="{{ value }}">
|
||||
{% elif key == "interface" %}
|
||||
<select name="interface">
|
||||
<select name="param_{{ key }}" id="param_{{ type }}_{{ key }}">
|
||||
{% for if in netinfo["ifs"] %}
|
||||
<option value="{{ if }}">
|
||||
{{ if }}
|
||||
|
@ -372,31 +382,31 @@
|
|||
{% endfor %}
|
||||
</select>
|
||||
{% else %}
|
||||
<input name="{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
|
||||
<input name="param_{{ key }}" id="param_{{ type }}_{{ key }}" type="text" size="{{ value|length }}" placeholder="{{ value }}">
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if type in REMOVABLE_DEVICE_TYPES %}
|
||||
<label for="drive_name">{{ _("Masquerade as:") }}</label>
|
||||
<select name="drive_name">
|
||||
<label for="{{ type }}_drive_name">{{ _("Masquerade as:") }}</label>
|
||||
<select name="drive_name" id="{{ type }}_drive_name">
|
||||
<option value="">
|
||||
{{ _("None") }}
|
||||
</option>
|
||||
{% if type == "SCCD" %}
|
||||
{% for drive in drive_properties["cd_conf"] | sort(attribute='name') %}
|
||||
{% for drive in env["drive_properties"]["cd_conf"] | sort(attribute='name') %}
|
||||
<option value="{{ drive.name }}">
|
||||
{{ drive.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if type == "SCRM" %}
|
||||
{% for drive in drive_properties["rm_conf"] | sort(attribute='name') %}
|
||||
{% for drive in env["drive_properties"]["rm_conf"] | sort(attribute='name') %}
|
||||
<option value="{{ drive.name }}">
|
||||
{{ drive.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if type == "SCMO" %}
|
||||
{% for drive in drive_properties["mo_conf"] | sort(attribute='name') %}
|
||||
{% for drive in env["drive_properties"]["mo_conf"] | sort(attribute='name') %}
|
||||
<option value="{{ drive.name }}">
|
||||
{{ drive.name }}
|
||||
</option>
|
||||
|
@ -404,16 +414,16 @@
|
|||
{% endif %}
|
||||
</select>
|
||||
{% endif %}
|
||||
<label for="scsi_id">{{ _("SCSI ID:") }}</label>
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option value="{{ id }}"{% if id == recommended_id %} selected{% endif %}>
|
||||
<label for="{{ type }}_scsi_id">{{ _("ID") }}</label>
|
||||
<select name="scsi_id" id="{{ type }}_scsi_id">
|
||||
{% for id in scsi_ids["valid_ids"] %}
|
||||
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
|
||||
{{ id }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="unit">{{ _("LUN") }}</label>
|
||||
<input name="unit" type="number" value="0" min="0" max="31" step="1">
|
||||
<label for="{{ type }}_unit">{{ _("LUN") }}</label>
|
||||
<input class="lun" name="unit" id="{{ type }}_unit" type="number" value="0" min="0" max="31" step="1" size="3">
|
||||
<input type="submit" value="{{ _("Attach") }}">
|
||||
</form>
|
||||
</td>
|
||||
|
@ -429,25 +439,20 @@
|
|||
<ul>
|
||||
<li>{{ _("The largest file size accepted in this form is %(max_file_size)s MiB. Use other file transfer means for larger files.", max_file_size=max_file_size) }}</li>
|
||||
<li>{{ _("File uploads will progress only if you stay on this page. If you navigate away before the transfer is completed, you will end up with an incomplete file.") }}</li>
|
||||
<li>{{ _("Install <a href=\"%(url)s\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
|
||||
<li>{{ _("Install <a href=\"%(url)s\" target=\"_blank\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<table style="border: none;">
|
||||
<tr style="border: none;">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form name="dropper" action="/files/upload" method="post" class="dropzone dz-clickable" enctype="multipart/form-data" id="dropper">
|
||||
<p>
|
||||
<label for="destination">{{ _("Target directory:") }}</label>
|
||||
<select name="destination">
|
||||
<option value="images">{{ base_dir }}</option>
|
||||
<option value="afp">{{ AFP_DIR }}</option>
|
||||
</select>
|
||||
</p>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form name="dropper" action="/files/upload" method="post" class="dropzone dz-clickable" enctype="multipart/form-data" id="dropper">
|
||||
<p>
|
||||
<label for="upload_destination">{{ _("Target directory:") }}</label>
|
||||
<select name="destination" id="upload_destination">
|
||||
<option value="images">Images - {{ env["image_dir"] }}</option>
|
||||
<option value="afp">AppleShare - {{ AFP_DIR }}</option>
|
||||
</select>
|
||||
</p>
|
||||
</form>
|
||||
|
||||
<script type="application/javascript">
|
||||
Dropzone.options.dropper = {
|
||||
paramName: 'file',
|
||||
|
@ -484,27 +489,20 @@
|
|||
{{ _("Download File from the Web") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Choose the desination directory and download a file from the Web to your Raspberry Pi.") }}</li>
|
||||
<li>{{ _("Install <a href=\"%(url)s\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
|
||||
<li>{{ _("Install <a href=\"%(url)s\" target=\"_blank\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/files/download_url" method="post">
|
||||
<label for="destination">{{ _("Target directory:") }}</label>
|
||||
<select name="destination">
|
||||
<option value="images">{{ base_dir }}</option>
|
||||
<option value="afp">{{ AFP_DIR }}</option>
|
||||
</select>
|
||||
<label for="url">{{ _("URL:") }}</label>
|
||||
<input name="url" placeholder="{{ _("URL") }}" required="" type="url">
|
||||
<input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File...") }}')">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<form action="/files/download_url" method="post">
|
||||
<label for="download_destination">{{ _("Target directory:") }}</label>
|
||||
<select name="destination" id="download_destination">
|
||||
<option value="images">Images - {{ env["image_dir"] }}</option>
|
||||
<option value="afp">AppleShare - {{ AFP_DIR }}</option>
|
||||
</select>
|
||||
<label for="download_url">{{ _("URL:") }}</label>
|
||||
<input name="url" id="download_url" required="" type="url">
|
||||
<input type="submit" value="{{ _("Download") }}" onclick="processNotify('{{ _("Downloading File...") }}')">
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -518,46 +516,41 @@
|
|||
<li>{{ _("If the downloaded file is a zip archive, we will attempt to unzip it and store the resulting files.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<label for="scsi_id">{{ _("SCSI ID:") }}</label>
|
||||
<form action="/files/download_to_iso" method="post">
|
||||
<select name="scsi_id">
|
||||
{% for id in scsi_ids %}
|
||||
<option value="{{ id }}"{% if id == recommended_id %} selected{% endif %}>
|
||||
{{ id }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="url">{{ _("URL:") }}</label>
|
||||
<input name="url" placeholder="{{ _("URL") }}" required="" type="url">
|
||||
<label for="type">{{ _("Type:") }}</label>
|
||||
<select name="type">
|
||||
<option value="-hfs">
|
||||
HFS
|
||||
</option>
|
||||
<option value="-iso-level 1">
|
||||
ISO-9660 Level 1
|
||||
</option>
|
||||
<option value="-iso-level 2">
|
||||
ISO-9660 Level 2
|
||||
</option>
|
||||
<option value="-iso-level 3">
|
||||
ISO-9660 Level 3
|
||||
</option>
|
||||
<option value="-J">
|
||||
Joliet
|
||||
</option>
|
||||
<option value="-r">
|
||||
Rock Ridge
|
||||
</option>
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Download and Mount CD-ROM image") }}" onclick="processNotify('{{ _("Downloading File and generating CD-ROM image...") }}')">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<form action="/files/download_to_iso" method="post">
|
||||
<label for="iso_url">{{ _("URL:") }}</label>
|
||||
<input name="url" id="iso_url" required="" type="url">
|
||||
<label for="iso_type">{{ _("Type:") }}</label>
|
||||
<select name="type" id="iso_type">
|
||||
<option value="-hfs">
|
||||
HFS
|
||||
</option>
|
||||
<option value="-iso-level 1">
|
||||
ISO-9660 Level 1
|
||||
</option>
|
||||
<option value="-iso-level 2">
|
||||
ISO-9660 Level 2
|
||||
</option>
|
||||
<option value="-iso-level 3">
|
||||
ISO-9660 Level 3
|
||||
</option>
|
||||
<option value="-J">
|
||||
Joliet
|
||||
</option>
|
||||
<option value="-r">
|
||||
Rock Ridge
|
||||
</option>
|
||||
</select>
|
||||
<label for="iso_scsi_id">{{ _("ID") }}</label>
|
||||
<select name="scsi_id" id="iso_scsi_id">
|
||||
{% for id in scsi_ids["valid_ids"] %}
|
||||
<option value="{{ id }}"{% if id == scsi_ids["recommended_id"] %} selected{% endif %}>
|
||||
{{ id }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Download and Mount CD-ROM image") }}" onclick="processNotify('{{ _("Downloading File and generating CD-ROM image...") }}')">
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -566,46 +559,37 @@
|
|||
{{ _("Create Empty Disk Image File") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("The Generic Hard Disk image type is recommended for most computer platforms.") }}</li>
|
||||
<li>{{ _("The Apple image type improves compatibility with Apple Macintosh computers.") }}</li>
|
||||
<li>{{ _("The NEC image type improves compatibility with NEC PC-98 computers.") }}</li>
|
||||
<li>{{ _("The SCSI-1 image type makes RaSCSI behave like a legacy SCSI-1 device, which may improve compatibility with very old SCSI controllers.") }}</li>
|
||||
<li>{{ _("The Removable Disk image type can be used with SCSI floppy drives, SyQuest drives, Zip drives, etc.") }}</li>
|
||||
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types#image-types") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/files/create" method="post">
|
||||
<label for="file_name">{{ _("File Name:") }}</label>
|
||||
<input name="file_name" placeholder="{{ _("File Name") }}" required="" type="text">
|
||||
<label for="type">{{ _("Type:") }}</label>
|
||||
<select name="type">
|
||||
{% for key, value in image_suffixes_to_create.items() %}
|
||||
<option value="{{ key }}">
|
||||
{{ value }} [.{{ key }}]
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="size">{{ _("Size:") }}</label>
|
||||
<input name="size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
|
||||
<input type="submit" value="{{ _("Create") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
<form action="/files/create" method="post">
|
||||
<label for="image_create_file_name">{{ _("File Name:") }}</label>
|
||||
<input name="file_name" id="image_create_file_name" required="" type="text">
|
||||
<label for="image_create_type">{{ _("Type:") }}</label>
|
||||
<select name="type" id="image_create_type">
|
||||
{% for key, value in image_suffixes_to_create.items() %}
|
||||
<option value="{{ key }}">
|
||||
{{ value }} [.{{ key }}]
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="image_create_size">{{ _("Size:") }}</label>
|
||||
<input name="size" id="image_create_size" type="number" placeholder="{{ _("MiB") }}" min="1" max="262144" required>
|
||||
<label for="image_create_drive_name">{{ _("Masquerade as:") }}</label>
|
||||
<select name="drive_name" id="image_create_drive_name">
|
||||
<option value="">
|
||||
{{ _("None") }}
|
||||
</option>
|
||||
{% for drive in env["drive_properties"]["hd_conf"] | sort(attribute='name') %}
|
||||
<option value="{{ drive.name }}">
|
||||
{{ drive.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Create") }}">
|
||||
</form>
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
{{ _("Create Named Drive") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Create pairs of images and properties files from a list of real-life drives.") }}</li>
|
||||
<li>{{ _("This will make RaSCSI use certain vendor strings and block sizes that may improve compatibility with certain systems.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<p><a href="/drive/list">{{ _("Create a named disk image that mimics real-life drives") }}</a></p>
|
||||
|
||||
<hr/>
|
||||
|
@ -615,67 +599,49 @@
|
|||
{{ _("Logging") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Fetch a certain number of lines of system logs with the given scope.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/logs/show" method="post">
|
||||
<label for="lines">{{ _("Log Lines:") }}</label>
|
||||
<input name="lines" type="number" value="200" min="0" max="99999" step="100">
|
||||
<label for="scope">{{ _("Scope:") }}</label>
|
||||
<select name="scope">
|
||||
<option value="">
|
||||
{{ _("All logs") }}
|
||||
</option>
|
||||
<option value="rascsi">
|
||||
rascsi
|
||||
</option>
|
||||
<option value="rascsi-web">
|
||||
rascsi-web
|
||||
</option>
|
||||
<option value="rascsi-oled">
|
||||
rascsi-oled
|
||||
</option>
|
||||
<option value="rascsi-ctrlboard">
|
||||
rascsi-ctrlboard
|
||||
</option>
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Show Logs") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr/>
|
||||
|
||||
<details>
|
||||
<summary class="heading">
|
||||
{{ _("Server Log Level") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Change the log level of the RaSCSI backend process.") }}</li>
|
||||
<li>{{ _("The current dropdown selection indicates the active log level.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/logs/level" method="post">
|
||||
<label for="level">{{ _("Log Level:") }}</label>
|
||||
<select name="level">
|
||||
{% for level in log_levels %}
|
||||
<option value="{{ level }}"{% if level == current_log_level %} selected{% endif %}>
|
||||
{{ level }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Set Log Level") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div>
|
||||
<form action="/logs/show" method="post">
|
||||
<label for="log_lines">{{ _("Log Lines:") }}</label>
|
||||
<input name="lines" id="log_lines" type="number" value="200" min="0" max="99999" step="100">
|
||||
<label for="log_scope">{{ _("Scope:") }}</label>
|
||||
<select name="scope" id="log_scope">
|
||||
<option value="">
|
||||
{{ _("All logs") }}
|
||||
</option>
|
||||
<option value="rascsi">
|
||||
rascsi
|
||||
</option>
|
||||
<option value="rascsi-web">
|
||||
rascsi-web
|
||||
</option>
|
||||
<option value="rascsi-oled">
|
||||
rascsi-oled
|
||||
</option>
|
||||
<option value="rascsi-ctrlboard">
|
||||
rascsi-ctrlboard
|
||||
</option>
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Show Logs") }}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<form action="/logs/level" method="post">
|
||||
<label for="log_level">{{ _("Log Level:") }}</label>
|
||||
<select name="level" id="log_level">
|
||||
{% for level in log_levels %}
|
||||
<option value="{{ level }}"{% if level == current_log_level %} selected{% endif %}>
|
||||
{{ level }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Set Log Level") }}">
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -687,23 +653,18 @@
|
|||
<li>{{ _("Change the Web Interface language.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/language" method="post">
|
||||
<label for="language">{{ _("Language:") }}</label>
|
||||
<select name="locale">
|
||||
{% for locale in locales %}
|
||||
<option value="{{ locale.language }}">
|
||||
{{ locale.language }} - {{ locale.display_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Change Language") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<form action="/language" method="post">
|
||||
<label for="locale">{{ _("Language:") }}</label>
|
||||
<select name="locale" id="locale">
|
||||
{% for locale in locales %}
|
||||
<option value="{{ locale.language }}">
|
||||
{{ locale.language }} - {{ locale.display_name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="submit" value="{{ _("Change Language") }}">
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
|
@ -712,39 +673,19 @@
|
|||
{{ _("Raspberry Pi Operations") }}
|
||||
</summary>
|
||||
<ul>
|
||||
<li>{{ _("Reboot or shut down the Raspberry Pi that RaSCSI is running on.") }}</li>
|
||||
<li>{{ _("IMPORTANT: Always shut down the Pi before turning off the power. Failing to do so may lead to data loss.") }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
<table style="border: none">
|
||||
<tr style="border: none">
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/pi/reboot" method="post" onclick="if (confirm('{{ _("Reboot the Raspberry Pi?") }}')) shutdownNotify('{{ _("Rebooting the Raspberry Pi...") }}'); else event.preventDefault();">
|
||||
<input type="submit" value="{{ _("Reboot Raspberry Pi") }}">
|
||||
</form>
|
||||
</td>
|
||||
<td style="border: none; vertical-align:top;">
|
||||
<form action="/pi/shutdown" method="post" onclick="if (confirm('{{ _("Shut down the Raspberry Pi?") }}')) shutdownNotify('{{ _("Shutting down the Raspberry Pi...") }}'); else event.preventDefault();">
|
||||
<input type="submit" value="{{ _("Shut Down Raspberry Pi") }}">
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<center><tt>
|
||||
{% if netatalk_configured == 1 %}
|
||||
{{ _("The AppleShare server is running. No active connections.") }}
|
||||
{% endif %}
|
||||
{% if netatalk_configured == 2 %}
|
||||
{{ _("%(value)d active AFP connection", value=(netatalk_configured - 1)) }}
|
||||
{% elif netatalk_configured > 2 %}
|
||||
{{ _("%(value)d active AFP connections", value=(netatalk_configured - 1)) }}
|
||||
{% endif %}
|
||||
</center></tt>
|
||||
<center><tt>
|
||||
{% if macproxy_configured %}
|
||||
<center><tt>{{ _("Macproxy is running at %(ip_addr)s (default port 5000)", ip_addr=env['ip_addr']) }}</tt></center>
|
||||
{% endif %}
|
||||
</center></tt>
|
||||
<form action="/pi/reboot" method="post" onclick="if (confirm('{{ _("Reboot the Raspberry Pi?") }}')) shutdownNotify('{{ _("Rebooting the Raspberry Pi...") }}'); else event.preventDefault();">
|
||||
<input type="submit" value="{{ _("Reboot Raspberry Pi") }}">
|
||||
</form>
|
||||
<form action="/pi/shutdown" method="post" onclick="if (confirm('{{ _("Shut down the Raspberry Pi?") }}')) shutdownNotify('{{ _("Shutting down the Raspberry Pi...") }}'); else event.preventDefault();">
|
||||
<input type="submit" value="{{ _("Shut Down Raspberry Pi") }}">
|
||||
</form>
|
||||
|
||||
<hr/>
|
||||
|
||||
<a href="/sys/manpage?app=rascsi"><p>{{ _("Read the RaSCSI Manual") }}</p></a>
|
||||
|
||||
{% endblock content %}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h3>{{ _("System Logs: %(scope)s %(lines)s lines", scope=scope, lines=lines) }}</h3>
|
||||
<h2>{{ _("System Logs: %(scope)s %(lines)s lines", scope=scope, lines=lines) }}</h2>
|
||||
<p><pre>{{ logs }}</pre></p>
|
||||
<p><a href="/">{{ _("Go to Home") }}</a></p>
|
||||
|
||||
|
|
8
python/web/src/templates/manpage.html
Normal file
8
python/web/src/templates/manpage.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<h2>{{ _("Manual for %(app)s", app=app) }}</h2>
|
||||
{{ manpage | safe }}
|
||||
<p><a href="/">{{ _("Go to Home") }}</a></p>
|
||||
|
||||
{% endblock content %}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -22,7 +22,6 @@ from flask import (
|
|||
flash,
|
||||
url_for,
|
||||
redirect,
|
||||
send_file,
|
||||
send_from_directory,
|
||||
make_response,
|
||||
session,
|
||||
|
@ -52,11 +51,13 @@ from web_utils import (
|
|||
get_device_name,
|
||||
map_image_file_descriptions,
|
||||
format_drive_properties,
|
||||
get_properties_by_drive_name,
|
||||
auth_active,
|
||||
is_bridge_configured,
|
||||
upload_with_dropzonejs,
|
||||
)
|
||||
from settings import (
|
||||
WEB_DIR,
|
||||
AFP_DIR,
|
||||
MAX_FILE_SIZE,
|
||||
DEFAULT_CONFIG,
|
||||
|
@ -73,6 +74,7 @@ def get_env_info():
|
|||
"""
|
||||
Get information about the app/host environment
|
||||
"""
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
ip_addr, host = sys_cmd.get_ip_and_host()
|
||||
|
||||
if "username" in session:
|
||||
|
@ -87,6 +89,15 @@ def get_env_info():
|
|||
"ip_addr": ip_addr,
|
||||
"host": host,
|
||||
"free_disk_space": int(sys_cmd.disk_space()["free"] / 1024 / 1024),
|
||||
"locale": get_locale(),
|
||||
"version": server_info["version"],
|
||||
"image_dir": server_info["image_dir"],
|
||||
"netatalk_configured": sys_cmd.running_proc("afpd"),
|
||||
"macproxy_configured": sys_cmd.running_proc("macproxy"),
|
||||
"cd_suffixes": tuple(server_info["sccd"]),
|
||||
"rm_suffixes": tuple(server_info["scrm"]),
|
||||
"mo_suffixes": tuple(server_info["scmo"]),
|
||||
"drive_properties": format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"]),
|
||||
}
|
||||
|
||||
|
||||
|
@ -139,7 +150,7 @@ def get_locale():
|
|||
language = session["language"]
|
||||
except KeyError:
|
||||
language = ""
|
||||
logging.warning("The default locale could not be detected. Falling back to English.")
|
||||
logging.info("The default locale could not be detected. Falling back to English.")
|
||||
if language:
|
||||
return language
|
||||
# Hardcoded fallback to "en" when the user agent does not send an accept-language header
|
||||
|
@ -195,7 +206,7 @@ def index():
|
|||
units += int(device["unit"])
|
||||
|
||||
reserved_scsi_ids = server_info["reserved_ids"]
|
||||
scsi_ids, recommended_id = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids)
|
||||
scsi_ids = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids)
|
||||
formatted_devices = sort_and_format_devices(devices["device_list"])
|
||||
|
||||
image_suffixes_to_create = map_image_file_descriptions(
|
||||
|
@ -218,49 +229,31 @@ def index():
|
|||
server_info["sccd"]
|
||||
)
|
||||
|
||||
try:
|
||||
drive_properties = format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"])
|
||||
except:
|
||||
drive_properties = {
|
||||
"hd_conf": [],
|
||||
"cd_conf": [],
|
||||
"rm_conf": [],
|
||||
"mo_conf": [],
|
||||
}
|
||||
|
||||
return response(
|
||||
template="index.html",
|
||||
locales=get_supported_locales(),
|
||||
netinfo=ractl_cmd.get_network_info(),
|
||||
bridge_configured=sys_cmd.is_bridge_setup(),
|
||||
netatalk_configured=sys_cmd.running_proc("afpd"),
|
||||
macproxy_configured=sys_cmd.running_proc("macproxy"),
|
||||
devices=formatted_devices,
|
||||
attached_images=attached_images,
|
||||
files=extended_image_files,
|
||||
config_files=config_files,
|
||||
base_dir=server_info["image_dir"],
|
||||
device_types=device_types,
|
||||
scan_depth=server_info["scan_depth"],
|
||||
CFG_DIR=CFG_DIR,
|
||||
AFP_DIR=AFP_DIR,
|
||||
scsi_ids=scsi_ids,
|
||||
recommended_id=recommended_id,
|
||||
attached_images=attached_images,
|
||||
units=units,
|
||||
reserved_scsi_ids=reserved_scsi_ids,
|
||||
RESERVATIONS=RESERVATIONS,
|
||||
max_file_size=int(int(MAX_FILE_SIZE) / 1024 / 1024),
|
||||
version=server_info["version"],
|
||||
log_levels=server_info["log_levels"],
|
||||
current_log_level=server_info["current_log_level"],
|
||||
netinfo=ractl_cmd.get_network_info(),
|
||||
device_types=device_types,
|
||||
scsi_ids=scsi_ids,
|
||||
units=units,
|
||||
reserved_scsi_ids=reserved_scsi_ids,
|
||||
image_suffixes_to_create=image_suffixes_to_create,
|
||||
valid_image_suffixes=valid_image_suffixes,
|
||||
cdrom_file_suffix=tuple(server_info["sccd"]),
|
||||
removable_file_suffix=tuple(server_info["scrm"]),
|
||||
mo_file_suffix=tuple(server_info["scmo"]),
|
||||
drive_properties=drive_properties,
|
||||
max_file_size=int(int(MAX_FILE_SIZE) / 1024 / 1024),
|
||||
RESERVATIONS=RESERVATIONS,
|
||||
CFG_DIR=CFG_DIR,
|
||||
AFP_DIR=AFP_DIR,
|
||||
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
|
||||
ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES,
|
||||
CONFIG_FILE_SUFFIX=CONFIG_FILE_SUFFIX,
|
||||
REMOVABLE_DEVICE_TYPES=ractl_cmd.get_removable_device_types(),
|
||||
DISK_DEVICE_TYPES=ractl_cmd.get_disk_device_types(),
|
||||
PERIPHERAL_DEVICE_TYPES=ractl_cmd.get_peripheral_device_types(),
|
||||
|
@ -280,25 +273,10 @@ def drive_list():
|
|||
"""
|
||||
Sets up the data structures and kicks off the rendering of the drive list page
|
||||
"""
|
||||
try:
|
||||
drive_properties = format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"])
|
||||
except:
|
||||
drive_properties = {
|
||||
"hd_conf": [],
|
||||
"cd_conf": [],
|
||||
"rm_conf": [],
|
||||
"mo_conf": [],
|
||||
}
|
||||
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
|
||||
return response(
|
||||
template="drives.html",
|
||||
files=file_cmd.list_images()["files"],
|
||||
base_dir=server_info["image_dir"],
|
||||
drive_properties=drive_properties,
|
||||
version=server_info["version"],
|
||||
cdrom_file_suffix=tuple(server_info["sccd"]),
|
||||
)
|
||||
|
||||
|
||||
|
@ -311,10 +289,9 @@ def login():
|
|||
password = request.form["password"]
|
||||
groups = [g.gr_name for g in getgrall() if username in g.gr_mem]
|
||||
|
||||
if AUTH_GROUP in groups:
|
||||
if authenticate(str(username), str(password)):
|
||||
session["username"] = request.form["username"]
|
||||
return response(env=get_env_info())
|
||||
if AUTH_GROUP in groups and authenticate(str(username), str(password)):
|
||||
session["username"] = request.form["username"]
|
||||
return response(env=get_env_info())
|
||||
|
||||
return response(error=True, status_code=401, message=_(
|
||||
"You must log in with valid credentials for a user in the '%(group)s' group",
|
||||
|
@ -358,34 +335,36 @@ def drive_create():
|
|||
"""
|
||||
Creates the image and properties file pair
|
||||
"""
|
||||
vendor = request.form.get("vendor")
|
||||
product = request.form.get("product")
|
||||
revision = request.form.get("revision")
|
||||
block_size = request.form.get("block_size")
|
||||
size = request.form.get("size")
|
||||
file_type = request.form.get("file_type")
|
||||
drive_name = request.form.get("drive_name")
|
||||
file_name = request.form.get("file_name")
|
||||
full_file_name = file_name + "." + file_type
|
||||
|
||||
properties = get_properties_by_drive_name(
|
||||
APP.config["RASCSI_DRIVE_PROPERTIES"],
|
||||
drive_name
|
||||
)
|
||||
|
||||
# Creating the image file
|
||||
process = file_cmd.create_new_image(file_name, file_type, size)
|
||||
process = file_cmd.create_new_image(
|
||||
file_name,
|
||||
properties["file_type"],
|
||||
properties["size"],
|
||||
)
|
||||
if not process["status"]:
|
||||
return response(error=True, message=process["msg"])
|
||||
|
||||
# Creating the drive properties file
|
||||
prop_file_name = f"{file_name}.{file_type}.{PROPERTIES_SUFFIX}"
|
||||
properties = {
|
||||
"vendor": vendor,
|
||||
"product": product,
|
||||
"revision": revision,
|
||||
"block_size": block_size,
|
||||
}
|
||||
full_file_name = f"{file_name}.{properties['file_type']}"
|
||||
prop_file_name = f"{full_file_name}.{PROPERTIES_SUFFIX}"
|
||||
process = file_cmd.write_drive_properties(prop_file_name, properties)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if not process["status"]:
|
||||
return response(error=True, message=process["msg"])
|
||||
|
||||
return response(message=_("Image file created: %(file_name)s", file_name=full_file_name))
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(message=[
|
||||
(_("Image file created: %(file_name)s", file_name=full_file_name), "success"),
|
||||
(process["msg"], "success"),
|
||||
])
|
||||
|
||||
|
||||
@APP.route("/drive/cdrom", methods=["POST"])
|
||||
|
@ -394,20 +373,15 @@ def drive_cdrom():
|
|||
"""
|
||||
Creates a properties file for a CD-ROM image
|
||||
"""
|
||||
vendor = request.form.get("vendor")
|
||||
product = request.form.get("product")
|
||||
revision = request.form.get("revision")
|
||||
block_size = request.form.get("block_size")
|
||||
drive_name = request.form.get("drive_name")
|
||||
file_name = request.form.get("file_name")
|
||||
|
||||
# Creating the drive properties file
|
||||
file_name = f"{file_name}.{PROPERTIES_SUFFIX}"
|
||||
properties = {
|
||||
"vendor": vendor,
|
||||
"product": product,
|
||||
"revision": revision,
|
||||
"block_size": block_size,
|
||||
}
|
||||
properties = get_properties_by_drive_name(
|
||||
APP.config["RASCSI_DRIVE_PROPERTIES"],
|
||||
drive_name
|
||||
)
|
||||
process = file_cmd.write_drive_properties(file_name, properties)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if process["status"]:
|
||||
|
@ -450,7 +424,8 @@ def config_load():
|
|||
return response(error=True, message=process["msg"])
|
||||
|
||||
if "delete" in request.form:
|
||||
process = file_cmd.delete_file(f"{CFG_DIR}/{file_name}")
|
||||
file_path = Path(CFG_DIR) / file_name
|
||||
process = file_cmd.delete_file(file_path)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if process["status"]:
|
||||
return response(message=process["msg"])
|
||||
|
@ -478,7 +453,6 @@ def show_diskinfo():
|
|||
template="diskinfo.html",
|
||||
file_name=file_name,
|
||||
diskinfo=diskinfo,
|
||||
version=server_info["version"],
|
||||
)
|
||||
|
||||
return response(
|
||||
|
@ -487,6 +461,56 @@ def show_diskinfo():
|
|||
)
|
||||
|
||||
|
||||
@APP.route("/sys/manpage", methods=["GET"])
|
||||
def show_manpage():
|
||||
"""
|
||||
Displays manpage
|
||||
"""
|
||||
app_allowlist = ["rascsi", "rasctl", "rasdump", "scsimon"]
|
||||
|
||||
app = request.args.get("app", type = str)
|
||||
|
||||
if app not in app_allowlist:
|
||||
return response(
|
||||
error=True,
|
||||
message=_("%(app)s is not a recognized RaSCSI app", app=app)
|
||||
)
|
||||
|
||||
file_path = f"{WEB_DIR}/../../../doc/{app}.1"
|
||||
html_to_strip = [
|
||||
"Content-type",
|
||||
"!DOCTYPE",
|
||||
"<HTML>",
|
||||
"<HEAD>",
|
||||
"<BODY>",
|
||||
"<H1>",
|
||||
]
|
||||
|
||||
returncode, manpage = sys_cmd.get_manpage(file_path)
|
||||
if returncode == 0:
|
||||
formatted_manpage = ""
|
||||
for line in manpage.splitlines(True):
|
||||
# Make URIs compatible with the Flask webapp
|
||||
if "/?1+" in line:
|
||||
line = line.replace("/?1+", "manpage?app=")
|
||||
# Strip out useless hyperlink
|
||||
elif "man2html" in line:
|
||||
line = line.replace("<A HREF=\"/\">man2html</A>", "man2html")
|
||||
if not any(ele in line for ele in html_to_strip):
|
||||
formatted_manpage += line
|
||||
|
||||
return response(
|
||||
template="manpage.html",
|
||||
app=app,
|
||||
manpage=formatted_manpage,
|
||||
)
|
||||
|
||||
return response(
|
||||
error=True,
|
||||
message=_("An error occurred when accessing man page: %(error)s", error=manpage)
|
||||
)
|
||||
|
||||
|
||||
@APP.route("/logs/show", methods=["POST"])
|
||||
def show_logs():
|
||||
"""
|
||||
|
@ -497,13 +521,11 @@ def show_logs():
|
|||
|
||||
returncode, logs = sys_cmd.get_logs(lines, scope)
|
||||
if returncode == 0:
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
return response(
|
||||
template="logs.html",
|
||||
scope=scope,
|
||||
lines=lines,
|
||||
logs=logs,
|
||||
version=server_info["version"],
|
||||
)
|
||||
|
||||
return response(
|
||||
|
@ -533,35 +555,40 @@ def attach_device():
|
|||
"""
|
||||
Attaches a peripheral device that doesn't take an image file as argument
|
||||
"""
|
||||
params = {}
|
||||
scsi_id = request.form.get("scsi_id")
|
||||
unit = request.form.get("unit")
|
||||
device_type = request.form.get("type")
|
||||
drive_name = request.form.get("drive_name")
|
||||
|
||||
if not scsi_id:
|
||||
return response(error=True, message=_("No SCSI ID specified"))
|
||||
|
||||
# Attempt to fetch the drive properties based on drive name
|
||||
drive_props = None
|
||||
if drive_name:
|
||||
for drive in APP.config["RASCSI_DRIVE_PROPERTIES"]:
|
||||
if drive["name"] == drive_name:
|
||||
drive_props = drive
|
||||
break
|
||||
|
||||
# Collect device parameters into a dictionary
|
||||
PARAM_PREFIX = "param_"
|
||||
params = {}
|
||||
for item in request.form:
|
||||
if item == "scsi_id":
|
||||
scsi_id = request.form.get(item)
|
||||
elif item == "unit":
|
||||
unit = request.form.get(item)
|
||||
elif item == "type":
|
||||
device_type = request.form.get(item)
|
||||
elif item == "drive_name":
|
||||
drive_name = request.form.get(item)
|
||||
for drive in APP.config["RASCSI_DRIVE_PROPERTIES"]:
|
||||
if drive["name"] == drive_name:
|
||||
drive_props = drive
|
||||
break
|
||||
else:
|
||||
if item.startswith(PARAM_PREFIX):
|
||||
param = request.form.get(item)
|
||||
if param:
|
||||
params.update({item: param})
|
||||
params.update({item.replace(PARAM_PREFIX, ""): param})
|
||||
|
||||
error_url = "https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link"
|
||||
error_msg = _("Please follow the instructions at %(url)s", url=error_url)
|
||||
|
||||
if "interface" in params.keys():
|
||||
# Note: is_bridge_configured returns False if the bridge is configured
|
||||
bridge_status = is_bridge_configured(params["interface"])
|
||||
if bridge_status:
|
||||
if not bridge_status["status"]:
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(bridge_status, "error"),
|
||||
(bridge_status["msg"], "error"),
|
||||
(error_msg, "error")
|
||||
])
|
||||
|
||||
|
@ -601,6 +628,11 @@ def attach_image():
|
|||
unit = request.form.get("unit")
|
||||
device_type = request.form.get("type")
|
||||
|
||||
if not scsi_id:
|
||||
return response(error=True, message=_("No SCSI ID specified"))
|
||||
if not file_name:
|
||||
return response(error=True, message=_("No image file to insert"))
|
||||
|
||||
kwargs = {"unit": int(unit), "params": {"file": file_name}}
|
||||
|
||||
if device_type:
|
||||
|
@ -646,6 +678,7 @@ def attach_image():
|
|||
|
||||
return response(message=response_messages)
|
||||
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to attach %(file_name)s to SCSI ID %(id_number)s LUN %(unit_number)s",
|
||||
file_name=file_name, id_number=scsi_id, unit_number=unit), "error"),
|
||||
|
@ -679,6 +712,7 @@ def detach():
|
|||
return response(message=_("Detached SCSI ID %(id_number)s LUN %(unit_number)s",
|
||||
id_number=scsi_id, unit_number=unit))
|
||||
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to detach SCSI ID %(id_number)s LUN %(unit_number)s",
|
||||
id_number=scsi_id, unit_number=unit), "error"),
|
||||
|
@ -700,6 +734,7 @@ def eject():
|
|||
return response(message=_("Ejected SCSI ID %(id_number)s LUN %(unit_number)s",
|
||||
id_number=scsi_id, unit_number=unit))
|
||||
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to eject SCSI ID %(id_number)s LUN %(unit_number)s",
|
||||
id_number=scsi_id, unit_number=unit), "error"),
|
||||
|
@ -712,13 +747,11 @@ def device_info():
|
|||
"""
|
||||
Displays detailed info for all attached devices
|
||||
"""
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
process = ractl_cmd.list_devices()
|
||||
if process["status"]:
|
||||
return response(
|
||||
template="deviceinfo.html",
|
||||
devices=process["device_list"],
|
||||
version=server_info["version"],
|
||||
)
|
||||
|
||||
return response(error=True, message=_("No devices attached"))
|
||||
|
@ -739,6 +772,7 @@ def reserve_id():
|
|||
RESERVATIONS[int(scsi_id)] = memo
|
||||
return response(message=_("Reserved SCSI ID %(id_number)s", id_number=scsi_id))
|
||||
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to reserve SCSI ID %(id_number)s", id_number=scsi_id), "error"),
|
||||
(process["msg"], "error"),
|
||||
|
@ -759,6 +793,7 @@ def release_id():
|
|||
RESERVATIONS[int(scsi_id)] = ""
|
||||
return response(message=_("Released the reservation for SCSI ID %(id_number)s", id_number=scsi_id))
|
||||
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to release the reservation for SCSI ID %(id_number)s", id_number=scsi_id), "error"),
|
||||
(process["msg"], "error"),
|
||||
|
@ -799,6 +834,7 @@ def download_to_iso():
|
|||
process = file_cmd.download_file_to_iso(url, *iso_args)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if not process["status"]:
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to create CD-ROM image from %(url)s", url=url), "error"),
|
||||
(process["msg"], "error"),
|
||||
|
@ -817,6 +853,7 @@ def download_to_iso():
|
|||
response_messages.append((_("Attached to SCSI ID %(id_number)s", id_number=scsi_id), "success"))
|
||||
return response(message=response_messages)
|
||||
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to attach image to SCSI ID %(id_number)s. Try attaching it manually.", id_number=scsi_id), "error"),
|
||||
(process_attach["msg"], "error"),
|
||||
|
@ -841,6 +878,7 @@ def download_file():
|
|||
if process["status"]:
|
||||
return response(message=process["msg"])
|
||||
|
||||
# TODO: Refactor the return messages into one string
|
||||
return response(error=True, message=[
|
||||
(_("Failed to download file from %(url)s", url=url), "error"),
|
||||
(process["msg"], "error"),
|
||||
|
@ -876,17 +914,34 @@ def create_file():
|
|||
file_name = request.form.get("file_name")
|
||||
size = (int(request.form.get("size")) * 1024 * 1024)
|
||||
file_type = request.form.get("type")
|
||||
drive_name = request.form.get("drive_name")
|
||||
|
||||
full_file_name = file_name + "." + file_type
|
||||
|
||||
process = file_cmd.create_new_image(file_name, file_type, size)
|
||||
if process["status"]:
|
||||
return response(
|
||||
status_code=201,
|
||||
message=_("Image file created: %(file_name)s", file_name=full_file_name),
|
||||
image=full_file_name,
|
||||
)
|
||||
if not process["status"]:
|
||||
return response(error=True, message=process["msg"])
|
||||
|
||||
return response(error=True, message=process["msg"])
|
||||
# Creating the drive properties file, if one is chosen
|
||||
if drive_name:
|
||||
properties = get_properties_by_drive_name(
|
||||
APP.config["RASCSI_DRIVE_PROPERTIES"],
|
||||
drive_name
|
||||
)
|
||||
prop_file_name = f"{full_file_name}.{PROPERTIES_SUFFIX}"
|
||||
process = file_cmd.write_drive_properties(prop_file_name, properties)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if not process["status"]:
|
||||
return response(error=True, message=process["msg"])
|
||||
|
||||
return response(
|
||||
status_code=201,
|
||||
# TODO: Refactor the return messages into one string
|
||||
message=[
|
||||
(_("Image file created: %(file_name)s", file_name=full_file_name), "success"),
|
||||
(process["msg"], "success"),
|
||||
],
|
||||
image=full_file_name,
|
||||
)
|
||||
|
||||
|
||||
@APP.route("/files/download", methods=["POST"])
|
||||
|
@ -895,8 +950,9 @@ def download():
|
|||
"""
|
||||
Downloads a file from the Pi to the local computer
|
||||
"""
|
||||
image = request.form.get("file")
|
||||
return send_file(image, as_attachment=True)
|
||||
file_name = request.form.get("file")
|
||||
server_info = ractl_cmd.get_server_info()
|
||||
return send_from_directory(server_info["image_dir"], file_name, as_attachment=True)
|
||||
|
||||
|
||||
@APP.route("/files/delete", methods=["POST"])
|
||||
|
@ -915,8 +971,8 @@ def delete():
|
|||
(_("Image file deleted: %(file_name)s", file_name=file_name), "success")]
|
||||
|
||||
# Delete the drive properties file, if it exists
|
||||
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}"
|
||||
if Path(prop_file_path).is_file():
|
||||
prop_file_path = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
|
||||
if prop_file_path.is_file():
|
||||
process = file_cmd.delete_file(prop_file_path)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if process["status"]:
|
||||
|
@ -944,9 +1000,9 @@ def rename():
|
|||
(_("Image file renamed to: %(file_name)s", file_name=new_file_name), "success")]
|
||||
|
||||
# Rename the drive properties file, if it exists
|
||||
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}"
|
||||
new_prop_file_path = f"{CFG_DIR}/{new_file_name}.{PROPERTIES_SUFFIX}"
|
||||
if Path(prop_file_path).is_file():
|
||||
prop_file_path = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
|
||||
new_prop_file_path = Path(CFG_DIR) / f"{new_file_name}.{PROPERTIES_SUFFIX}"
|
||||
if prop_file_path.is_file():
|
||||
process = file_cmd.rename_file(prop_file_path, new_prop_file_path)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if process["status"]:
|
||||
|
@ -974,9 +1030,9 @@ def copy():
|
|||
(_("Copy of image file saved as: %(file_name)s", file_name=new_file_name), "success")]
|
||||
|
||||
# Create a copy of the drive properties file, if it exists
|
||||
prop_file_path = f"{CFG_DIR}/{file_name}.{PROPERTIES_SUFFIX}"
|
||||
new_prop_file_path = f"{CFG_DIR}/{new_file_name}.{PROPERTIES_SUFFIX}"
|
||||
if Path(prop_file_path).is_file():
|
||||
prop_file_path = Path(CFG_DIR) / f"{file_name}.{PROPERTIES_SUFFIX}"
|
||||
new_prop_file_path = Path(CFG_DIR) / f"{new_file_name}.{PROPERTIES_SUFFIX}"
|
||||
if prop_file_path.is_file():
|
||||
process = file_cmd.copy_file(prop_file_path, new_prop_file_path)
|
||||
process = ReturnCodeMapper.add_msg(process)
|
||||
if process["status"]:
|
||||
|
@ -1112,8 +1168,10 @@ if __name__ == "__main__":
|
|||
if process["status"]:
|
||||
APP.config["RASCSI_DRIVE_PROPERTIES"] = process["conf"]
|
||||
else:
|
||||
APP.config["RASCSI_DRIVE_PROPERTIES"] = []
|
||||
logging.error(process["msg"])
|
||||
else:
|
||||
APP.config["RASCSI_DRIVE_PROPERTIES"] = []
|
||||
logging.warning("Could not read drive properties from %s", DRIVE_PROPERTIES_FILE)
|
||||
|
||||
logging.basicConfig(stream=sys.stdout,
|
||||
|
|
|
@ -18,6 +18,7 @@ def get_valid_scsi_ids(devices, reserved_ids):
|
|||
Takes a list of (dict)s devices, and list of (int)s reserved_ids.
|
||||
Returns:
|
||||
- (list) of (int)s valid_ids, which are the SCSI ids that are not reserved
|
||||
- (list) of (int)s occupied_ids, which are the SCSI ids were a device is attached
|
||||
- (int) recommended_id, which is the id that the Web UI should default to recommend
|
||||
"""
|
||||
occupied_ids = []
|
||||
|
@ -33,11 +34,15 @@ def get_valid_scsi_ids(devices, reserved_ids):
|
|||
recommended_id = unoccupied_ids[-1]
|
||||
else:
|
||||
if occupied_ids:
|
||||
recommended_id = occupied_ids.pop(0)
|
||||
recommended_id = occupied_ids[0]
|
||||
else:
|
||||
recommended_id = 0
|
||||
|
||||
return valid_ids, recommended_id
|
||||
return {
|
||||
"valid_ids": valid_ids,
|
||||
"occupied_ids": occupied_ids,
|
||||
"recommended_id": recommended_id,
|
||||
}
|
||||
|
||||
|
||||
def sort_and_format_devices(devices):
|
||||
|
@ -47,18 +52,25 @@ def sort_and_format_devices(devices):
|
|||
For SCSI IDs where no device is attached, inject a (dict) with placeholder text.
|
||||
"""
|
||||
occupied_ids = []
|
||||
formatted_devices = []
|
||||
for device in devices:
|
||||
occupied_ids.append(device["id"])
|
||||
device["device_name"] = get_device_name(device["device_type"])
|
||||
formatted_devices.append(device)
|
||||
|
||||
formatted_devices = devices
|
||||
|
||||
# Add padding devices and sort the list
|
||||
for i in range(8):
|
||||
if i not in occupied_ids:
|
||||
formatted_devices.append({"id": i, "device_type": "-", \
|
||||
"status": "-", "file": "-", "product": "-"})
|
||||
# Sort list of devices by id
|
||||
formatted_devices.sort(key=lambda dic: str(dic["id"]))
|
||||
# Add placeholder data for non-occupied IDs
|
||||
for scsi_id in range(8):
|
||||
if scsi_id not in occupied_ids:
|
||||
formatted_devices.append(
|
||||
{
|
||||
"id": scsi_id,
|
||||
"unit": "-",
|
||||
"device_name": "-",
|
||||
"status": "-",
|
||||
"file": "-",
|
||||
"product": "-",
|
||||
}
|
||||
)
|
||||
|
||||
return formatted_devices
|
||||
|
||||
|
@ -91,7 +103,7 @@ def get_device_name(device_type):
|
|||
if device_type == "SCBR":
|
||||
return _("Host Bridge")
|
||||
if device_type == "SCDP":
|
||||
return _("DaynaPORT SCSI/Link")
|
||||
return _("Ethernet Adapter")
|
||||
if device_type == "SCLP":
|
||||
return _("Printer")
|
||||
if device_type == "SCHS":
|
||||
|
@ -144,6 +156,12 @@ def format_drive_properties(drive_properties):
|
|||
FORMAT_FILTER = "{:,.2f}"
|
||||
|
||||
for device in drive_properties:
|
||||
# Add fallback device names, since other code relies on this data for display
|
||||
if not device["name"]:
|
||||
if device["product"]:
|
||||
device["name"] = device["product"]
|
||||
else:
|
||||
device["name"] = "Unknown Device"
|
||||
if device["device_type"] == "SCHD":
|
||||
device["secure_name"] = secure_filename(device["name"])
|
||||
device["size_mb"] = FORMAT_FILTER.format(device["size"] / 1024 / 1024)
|
||||
|
@ -167,6 +185,41 @@ def format_drive_properties(drive_properties):
|
|||
"mo_conf": mo_conf,
|
||||
}
|
||||
|
||||
def get_properties_by_drive_name(drives, drive_name):
|
||||
"""
|
||||
Takes (list) of (dict) drives, and (str) drive_name
|
||||
Returns (dict) with the collection of drive properties that matches drive_name
|
||||
"""
|
||||
drives.sort(key=lambda item: item.get("name"))
|
||||
|
||||
drive_props = None
|
||||
prev_drive = {"name": ""}
|
||||
for drive in drives:
|
||||
# TODO: Make this check into an integration test
|
||||
if "name" not in drive:
|
||||
logging.warning(
|
||||
"Device without a name exists in the drive properties database. This is a bug."
|
||||
)
|
||||
break
|
||||
# TODO: Make this check into an integration test
|
||||
if drive["name"] == prev_drive["name"]:
|
||||
logging.warning(
|
||||
"Device with duplicate name \"%s\" in drive properties database. This is a bug.",
|
||||
drive["name"],
|
||||
)
|
||||
prev_drive = drive
|
||||
if drive["name"] == drive_name:
|
||||
drive_props = drive
|
||||
|
||||
return {
|
||||
"file_type": drive_props["file_type"],
|
||||
"vendor": drive_props["vendor"],
|
||||
"product": drive_props["product"],
|
||||
"revision": drive_props["revision"],
|
||||
"block_size": drive_props["block_size"],
|
||||
"size": drive_props["size"],
|
||||
}
|
||||
|
||||
def auth_active(group):
|
||||
"""
|
||||
Inspects if the group defined in (str) group exists on the system.
|
||||
|
@ -185,24 +238,31 @@ def auth_active(group):
|
|||
def is_bridge_configured(interface):
|
||||
"""
|
||||
Takes (str) interface of a network device being attached.
|
||||
Returns (bool) False if the network bridge is configured.
|
||||
Returns (str) with an error message if the network bridge is not configured.
|
||||
Returns a (dict) with (bool) status and (str) msg
|
||||
"""
|
||||
# TODO: Reduce the nesting of these checks, and streamline how the results are notified
|
||||
status = True
|
||||
return_msg = ""
|
||||
sys_cmd = SysCmds()
|
||||
if interface.startswith("wlan"):
|
||||
if not sys_cmd.introspect_file("/etc/sysctl.conf", r"^net\.ipv4\.ip_forward=1$"):
|
||||
return _("Configure IPv4 forwarding before using a wireless network device.")
|
||||
if not Path("/etc/iptables/rules.v4").is_file():
|
||||
return _("Configure NAT before using a wireless network device.")
|
||||
status = False
|
||||
return_msg = _("Configure IPv4 forwarding before using a wireless network device.")
|
||||
elif not Path("/etc/iptables/rules.v4").is_file():
|
||||
status = False
|
||||
return_msg = _("Configure NAT before using a wireless network device.")
|
||||
else:
|
||||
if not sys_cmd.introspect_file(
|
||||
"/etc/dhcpcd.conf",
|
||||
r"^denyinterfaces " + interface + r"$",
|
||||
):
|
||||
return _("Configure the network bridge before using a wired network device.")
|
||||
if not Path("/etc/network/interfaces.d/rascsi_bridge").is_file():
|
||||
return _("Configure the network bridge before using a wired network device.")
|
||||
return False
|
||||
status = False
|
||||
return_msg = _("Configure the network bridge before using a wired network device.")
|
||||
elif not Path("/etc/network/interfaces.d/rascsi_bridge").is_file():
|
||||
status = False
|
||||
return_msg = _("Configure the network bridge before using a wired network device.")
|
||||
|
||||
return {"status": status, "msg": return_msg + f" ({interface})"}
|
||||
|
||||
|
||||
def upload_with_dropzonejs(image_dir):
|
||||
|
@ -235,18 +295,7 @@ def upload_with_dropzonejs(image_dir):
|
|||
if current_chunk + 1 == total_chunks:
|
||||
# Validate the resulting file size after writing the last chunk
|
||||
if path.getsize(save_path) != int(request.form["dztotalfilesize"]):
|
||||
log.error(
|
||||
"Finished transferring %s, "
|
||||
"but it has a size mismatch with the original file. "
|
||||
"Got %s but we expected %s.",
|
||||
file_object.filename,
|
||||
path.getsize(save_path),
|
||||
request.form['dztotalfilesize'],
|
||||
)
|
||||
log.error("File size mismatch between the original file and transferred file.")
|
||||
return make_response(_("Transferred file corrupted!"), 500)
|
||||
|
||||
log.info("File %s has been uploaded successfully", file_object.filename)
|
||||
log.debug("Chunk %s of %s for file %s completed.",
|
||||
current_chunk + 1, total_chunks, file_object.filename)
|
||||
|
||||
return make_response(_("File upload successful!"), 200)
|
||||
|
|
|
@ -2,9 +2,6 @@ import pytest
|
|||
import uuid
|
||||
import warnings
|
||||
|
||||
CFG_DIR = "/home/pi/.config/rascsi"
|
||||
IMAGES_DIR = "/home/pi/images"
|
||||
AFP_DIR = "/home/pi/afpshare"
|
||||
SCSI_ID = 6
|
||||
FILE_SIZE_1_MIB = 1048576
|
||||
STATUS_SUCCESS = "success"
|
||||
|
|
|
@ -3,6 +3,7 @@ from conftest import STATUS_SUCCESS, STATUS_ERROR
|
|||
|
||||
# route("/login", methods=["POST"])
|
||||
def test_login_with_valid_credentials(pytestconfig, http_client_unauthenticated):
|
||||
# Note: This test depends on the rascsi group existing and 'username' a member the group
|
||||
response = http_client_unauthenticated.post(
|
||||
"/login",
|
||||
data={
|
||||
|
|
|
@ -42,8 +42,8 @@ def test_attach_image(http_client, create_test_image, detach_devices):
|
|||
{
|
||||
"type": "SCRM",
|
||||
"drive_props": {
|
||||
"vendor": "VENDOR",
|
||||
"product": "PRODUCT",
|
||||
"vendor": "HD VENDOR",
|
||||
"product": "HD PRODUCT",
|
||||
"revision": "0123",
|
||||
"block_size": "512",
|
||||
},
|
||||
|
@ -54,8 +54,8 @@ def test_attach_image(http_client, create_test_image, detach_devices):
|
|||
{
|
||||
"type": "SCMO",
|
||||
"drive_props": {
|
||||
"vendor": "VENDOR",
|
||||
"product": "PRODUCT",
|
||||
"vendor": "MO VENDOR",
|
||||
"product": "MO PRODUCT",
|
||||
"revision": "0123",
|
||||
"block_size": "512",
|
||||
},
|
||||
|
@ -66,20 +66,25 @@ def test_attach_image(http_client, create_test_image, detach_devices):
|
|||
{
|
||||
"type": "SCCD",
|
||||
"drive_props": {
|
||||
"vendor": "VENDOR",
|
||||
"product": "PRODUCT",
|
||||
"vendor": "CD VENDOR",
|
||||
"product": "CD PRODUCT",
|
||||
"revision": "0123",
|
||||
"block_size": "512",
|
||||
},
|
||||
},
|
||||
),
|
||||
("Host Bridge", {"type": "SCBR", "interface": "eth0", "inet": "10.10.20.1/24"}),
|
||||
("DaynaPORT SCSI/Link", {"type": "SCDP", "interface": "eth0", "inet": "10.10.20.1/24"}),
|
||||
# TODO: Find a portable way to detect network interfaces for testing
|
||||
("Host Bridge", {"type": "SCBR", "param_inet": "192.168.0.1/24"}),
|
||||
# TODO: Find a portable way to detect network interfaces for testing
|
||||
("Ethernet Adapter", {"type": "SCDP", "param_inet": "192.168.0.1/24"}),
|
||||
("Host Services", {"type": "SCHS"}),
|
||||
("Printer", {"type": "SCLP", "timeout": 30, "cmd": "lp -oraw %f"}),
|
||||
("Printer", {"type": "SCLP", "param_timeout": 60, "param_cmd": "lp -fart %f"}),
|
||||
],
|
||||
)
|
||||
def test_attach_device(http_client, detach_devices, device_name, device_config):
|
||||
def test_attach_device(env, http_client, detach_devices, device_name, device_config):
|
||||
if env["is_docker"] and device_name == "Host Bridge":
|
||||
pytest.skip("Test not supported in Docker environment.")
|
||||
|
||||
device_config["scsi_id"] = SCSI_ID
|
||||
device_config["unit"] = 0
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@ import uuid
|
|||
import os
|
||||
|
||||
from conftest import (
|
||||
IMAGES_DIR,
|
||||
SCSI_ID,
|
||||
FILE_SIZE_1_MIB,
|
||||
STATUS_SUCCESS,
|
||||
|
@ -21,6 +20,7 @@ def test_create_file(http_client, list_files, delete_file):
|
|||
"file_name": file_prefix,
|
||||
"type": "hds",
|
||||
"size": 1,
|
||||
"drive_name": "DEC RZ22",
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -85,7 +85,7 @@ def test_copy_file(http_client, create_test_image, list_files, delete_file):
|
|||
|
||||
# route("/files/delete", methods=["POST"])
|
||||
def test_delete_file(http_client, create_test_image, list_files):
|
||||
file_name = create_test_image()
|
||||
file_name = create_test_image(auto_delete=False)
|
||||
|
||||
response = http_client.post("/files/delete", data={"file_name": file_name})
|
||||
|
||||
|
@ -200,7 +200,7 @@ def test_upload_file(http_client, delete_file):
|
|||
def test_download_file(http_client, create_test_image):
|
||||
file_name = create_test_image()
|
||||
|
||||
response = http_client.post("/files/download", data={"file": f"{IMAGES_DIR}/{file_name}"})
|
||||
response = http_client.post("/files/download", data={"file": file_name})
|
||||
|
||||
assert response.status_code == 200
|
||||
assert response.headers["content-type"] == "application/octet-stream"
|
||||
|
@ -209,7 +209,7 @@ def test_download_file(http_client, create_test_image):
|
|||
|
||||
|
||||
# route("/files/download_url", methods=["POST"])
|
||||
def test_download_url_to_dir(httpserver, http_client, list_files, delete_file):
|
||||
def test_download_url_to_dir(env, httpserver, http_client, list_files, delete_file):
|
||||
file_name = str(uuid.uuid4())
|
||||
http_path = f"/images/{file_name}"
|
||||
url = httpserver.url_for(http_path)
|
||||
|
@ -235,7 +235,9 @@ def test_download_url_to_dir(httpserver, http_client, list_files, delete_file):
|
|||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert file_name in list_files()
|
||||
assert response_data["messages"][0]["message"] == f"{file_name} downloaded to {IMAGES_DIR}"
|
||||
assert (
|
||||
response_data["messages"][0]["message"] == f"{file_name} downloaded to {env['images_dir']}"
|
||||
)
|
||||
|
||||
# Cleanup
|
||||
delete_file(file_name)
|
||||
|
@ -243,6 +245,7 @@ def test_download_url_to_dir(httpserver, http_client, list_files, delete_file):
|
|||
|
||||
# route("/files/download_to_iso", methods=["POST"])
|
||||
def test_download_url_to_iso(
|
||||
env,
|
||||
httpserver,
|
||||
http_client,
|
||||
list_files,
|
||||
|
@ -282,7 +285,7 @@ def test_download_url_to_iso(
|
|||
|
||||
m = response_data["messages"]
|
||||
assert m[0]["message"] == 'Created CD-ROM ISO image with arguments "-hfs"'
|
||||
assert m[1]["message"] == f"Saved image as: {IMAGES_DIR}/{iso_file_name}"
|
||||
assert m[1]["message"] == f"Saved image as: {env['images_dir']}/{iso_file_name}"
|
||||
assert m[2]["message"] == f"Attached to SCSI ID {SCSI_ID}"
|
||||
|
||||
# Cleanup
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import uuid
|
||||
|
||||
from conftest import (
|
||||
CFG_DIR,
|
||||
FILE_SIZE_1_MIB,
|
||||
STATUS_SUCCESS,
|
||||
)
|
||||
|
@ -42,20 +41,17 @@ def test_show_named_drive_presets(http_client):
|
|||
|
||||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert "drive_properties" in response_data["data"]
|
||||
assert "files" in response_data["data"]
|
||||
|
||||
|
||||
# route("/drive/cdrom", methods=["POST"])
|
||||
def test_create_cdrom_properties_file(http_client):
|
||||
def test_create_cdrom_properties_file(env, http_client):
|
||||
file_name = f"{uuid.uuid4()}.iso"
|
||||
|
||||
response = http_client.post(
|
||||
"/drive/cdrom",
|
||||
data={
|
||||
"vendor": "TEST_AAA",
|
||||
"product": "TEST_BBB",
|
||||
"revision": "1.0A",
|
||||
"block_size": 2048,
|
||||
"drive_name": "Sony CDU-8012",
|
||||
"file_name": file_name,
|
||||
},
|
||||
)
|
||||
|
@ -65,7 +61,7 @@ def test_create_cdrom_properties_file(http_client):
|
|||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert response_data["messages"][0]["message"] == (
|
||||
f"File created: {CFG_DIR}/{file_name}.properties"
|
||||
f"File created: {env['cfg_dir']}/{file_name}.properties"
|
||||
)
|
||||
|
||||
|
||||
|
@ -77,12 +73,8 @@ def test_create_image_with_properties_file(http_client, delete_file):
|
|||
response = http_client.post(
|
||||
"/drive/create",
|
||||
data={
|
||||
"vendor": "TEST_AAA",
|
||||
"product": "TEST_BBB",
|
||||
"revision": "1.0A",
|
||||
"block_size": 512,
|
||||
"drive_name": "Miniscribe M8425",
|
||||
"size": FILE_SIZE_1_MIB,
|
||||
"file_type": "hds",
|
||||
"file_name": file_prefix,
|
||||
},
|
||||
)
|
||||
|
@ -95,3 +87,12 @@ def test_create_image_with_properties_file(http_client, delete_file):
|
|||
|
||||
# Cleanup
|
||||
delete_file(file_name)
|
||||
|
||||
|
||||
# route("/sys/manpage", methods=["POST"])
|
||||
def test_show_manpage(http_client):
|
||||
response = http_client.get("/sys/manpage?app=rascsi")
|
||||
response_data = response.json()
|
||||
|
||||
assert response.status_code == 200
|
||||
assert "rascsi" in response_data["data"]["manpage"]
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import pytest
|
||||
import uuid
|
||||
|
||||
from conftest import CFG_DIR, STATUS_SUCCESS
|
||||
from conftest import STATUS_SUCCESS
|
||||
|
||||
|
||||
# route("/language", methods=["POST"])
|
||||
|
@ -74,7 +74,7 @@ def test_show_logs(http_client):
|
|||
|
||||
# route("/config/save", methods=["POST"])
|
||||
# route("/config/load", methods=["POST"])
|
||||
def test_save_load_and_delete_configs(http_client):
|
||||
def test_save_load_and_delete_configs(env, http_client):
|
||||
config_name = str(uuid.uuid4())
|
||||
config_json_file = f"{config_name}.json"
|
||||
reserved_scsi_id = 0
|
||||
|
@ -96,7 +96,7 @@ def test_save_load_and_delete_configs(http_client):
|
|||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert response_data["messages"][0]["message"] == (
|
||||
f"File created: {CFG_DIR}/{config_json_file}"
|
||||
f"File created: {env['cfg_dir']}/{config_json_file}"
|
||||
)
|
||||
|
||||
assert config_json_file in http_client.get("/").json()["data"]["config_files"]
|
||||
|
@ -126,7 +126,7 @@ def test_save_load_and_delete_configs(http_client):
|
|||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert response_data["messages"][0]["message"] == (
|
||||
f"Loaded configurations from: {CFG_DIR}/{config_json_file}"
|
||||
f"Loaded configurations from: {config_json_file}"
|
||||
)
|
||||
|
||||
# Confirm the application has returned to its initial state
|
||||
|
@ -146,7 +146,7 @@ def test_save_load_and_delete_configs(http_client):
|
|||
assert response.status_code == 200
|
||||
assert response_data["status"] == STATUS_SUCCESS
|
||||
assert response_data["messages"][0]["message"] == (
|
||||
f"File deleted: {CFG_DIR}/{config_json_file}"
|
||||
f"File deleted: {env['cfg_dir']}/{config_json_file}"
|
||||
)
|
||||
|
||||
assert config_json_file not in http_client.get("/").json()["data"]["config_files"]
|
||||
|
|
|
@ -1,15 +1,32 @@
|
|||
import pytest
|
||||
import requests
|
||||
import socket
|
||||
import os
|
||||
|
||||
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--base_url", action="store", default="http://localhost:8080")
|
||||
parser.addoption("--httpserver_host", action="store", default="host.docker.internal")
|
||||
parser.addoption("--httpserver_listen_address", action="store", default="127.0.0.1")
|
||||
default_base_url = "http://rascsi_web" if os.getenv("DOCKER") else "http://localhost:8080"
|
||||
|
||||
parser.addoption("--home_dir", action="store", default="/home/pi")
|
||||
parser.addoption("--base_url", action="store", default=default_base_url)
|
||||
parser.addoption("--httpserver_host", action="store", default=socket.gethostname())
|
||||
parser.addoption("--httpserver_listen_address", action="store", default="0.0.0.0")
|
||||
parser.addoption("--rascsi_username", action="store", default="pi")
|
||||
parser.addoption("--rascsi_password", action="store", default="rascsi")
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def env(pytestconfig):
|
||||
home_dir = pytestconfig.getoption("home_dir")
|
||||
return {
|
||||
"is_docker": bool(os.getenv("DOCKER")),
|
||||
"home_dir": home_dir,
|
||||
"cfg_dir": f"{home_dir}/.config/rascsi",
|
||||
"images_dir": f"{home_dir}/images",
|
||||
"afp_dir": f"{home_dir}/afpshare",
|
||||
}
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def httpserver_listen_address(pytestconfig):
|
||||
return (pytestconfig.getoption("httpserver_listen_address"), 0)
|
||||
|
@ -17,8 +34,7 @@ def httpserver_listen_address(pytestconfig):
|
|||
|
||||
@pytest.fixture(scope="function", autouse=True)
|
||||
def set_httpserver_hostname(pytestconfig, httpserver):
|
||||
# The HTTP requests are made by Python from within the container so we need
|
||||
# httpserver.url_for to generate URLs which point to the Docker host
|
||||
# We need httpserver.url_for() to generate URLs pointing to the correct host
|
||||
httpserver.host = pytestconfig.getoption("httpserver_host")
|
||||
|
||||
|
||||
|
|
|
@ -89,45 +89,37 @@ SRC_PROTOC = \
|
|||
SRC_PROTOBUF = \
|
||||
rascsi_interface.pb.cpp
|
||||
|
||||
SRC_RASCSI_CORE = \
|
||||
scsi.cpp \
|
||||
filepath.cpp \
|
||||
fileio.cpp \
|
||||
SRC_SHARED = \
|
||||
rascsi_version.cpp \
|
||||
rascsi_image.cpp \
|
||||
rascsi_response.cpp \
|
||||
rasutil.cpp \
|
||||
command_util.cpp \
|
||||
socket_connector.cpp \
|
||||
localizer.cpp
|
||||
protobuf_util.cpp \
|
||||
protobuf_serializer.cpp
|
||||
|
||||
SRC_RASCSI_CORE = \
|
||||
bus.cpp \
|
||||
filepath.cpp \
|
||||
fileio.cpp
|
||||
SRC_RASCSI_CORE += $(shell find ./rascsi -name '*.cpp')
|
||||
SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp')
|
||||
SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp')
|
||||
SRC_RASCSI_CORE += $(shell find ./hal -name '*.cpp')
|
||||
SRC_RASCSI_CORE += $(SRC_PROTOBUF)
|
||||
|
||||
SRC_RASCSI = rascsi.cpp
|
||||
|
||||
SRC_SCSIMON = \
|
||||
scsimon.cpp \
|
||||
scsi.cpp \
|
||||
bus.cpp \
|
||||
rascsi_version.cpp
|
||||
SRC_SCSIMON += $(shell find ./monitor -name '*.cpp')
|
||||
SRC_SCSIMON += $(shell find ./hal -name '*.cpp')
|
||||
|
||||
SRC_RASCTL = \
|
||||
rasctl.cpp\
|
||||
rasctl_commands.cpp \
|
||||
rasctl_display.cpp \
|
||||
rascsi_version.cpp \
|
||||
rasutil.cpp \
|
||||
command_util.cpp \
|
||||
socket_connector.cpp \
|
||||
localizer.cpp
|
||||
SRC_RASCTL += $(SRC_PROTOBUF)
|
||||
SRC_RASCTL_CORE = $(shell find ./rasctl -name '*.cpp')
|
||||
|
||||
SRC_RASCTL = rasctl.cpp
|
||||
|
||||
SRC_RASDUMP = \
|
||||
rasdump.cpp \
|
||||
scsi.cpp \
|
||||
bus.cpp \
|
||||
filepath.cpp \
|
||||
fileio.cpp \
|
||||
rascsi_version.cpp
|
||||
|
@ -140,18 +132,21 @@ SRC_GPIO_TEST = \
|
|||
gpiotest.cpp
|
||||
SRC_GPIO_TEST += $(shell find ./hal -name '*.cpp')
|
||||
|
||||
vpath %.h ./ ./controllers ./devices ./monitor ./hal ../BPI-WiringPi2/wiringPi/
|
||||
vpath %.cpp ./ ./controllers ./devices ./monitor ./test ./hal
|
||||
vpath %.h ./ ./controllers ./devices ./monitor ./hal ./rascsi ./rasctl ../BPI-WiringPi2/wiringPi/
|
||||
vpath %.cpp ./ ./controllers ./devices ./monitor ./test ./hal ./rascsi ./rasctl
|
||||
vpath %.o ./$(OBJDIR)
|
||||
vpath ./$(BINDIR)
|
||||
|
||||
|
||||
OBJ_RASCSI_CORE := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_CORE:%.cpp=%.o)))
|
||||
OBJ_RASCSI := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI:%.cpp=%.o)))
|
||||
OBJ_RASCTL_CORE := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL_CORE:%.cpp=%.o)))
|
||||
OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o)))
|
||||
OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o)))
|
||||
OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o)))
|
||||
OBJ_RASCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_TEST:%.cpp=%.o)))
|
||||
OBJ_SHARED := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SHARED:%.cpp=%.o)))
|
||||
OBJ_PROTOBUF := $(addprefix $(OBJDIR)/,$(notdir $(SRC_PROTOBUF:%.cpp=%.o)))
|
||||
OBJ_GPIO_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_GPIO_TEST:%.cpp=%.o)))
|
||||
|
||||
GEN_PROTOBUF := $(SRC_PROTOBUF) rascsi_interface.pb.h
|
||||
|
@ -160,7 +155,7 @@ LIB_BPI_WIRINGPI := $(OBJDIR)/libwiringPi.a
|
|||
|
||||
# The following will include all of the auto-generated dependency files (*.d)
|
||||
# if they exist. This will trigger a rebuild of a source file if a header changes
|
||||
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST))
|
||||
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST))
|
||||
-include $(ALL_DEPS)
|
||||
|
||||
$(OBJDIR) $(BINDIR):
|
||||
|
@ -201,11 +196,13 @@ lcov: test
|
|||
|
||||
docs: $(DOC_DIR)/rascsi_man_page.txt $(DOC_DIR)/rasctl_man_page.txt $(DOC_DIR)/scsimon_man_page.txt
|
||||
|
||||
$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs
|
||||
$(SRC_SHARED): $(SRC_PROTOBUF)
|
||||
|
||||
$(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL) -lpthread -lprotobuf
|
||||
$(BINDIR)/$(RASCSI): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs
|
||||
|
||||
$(BINDIR)/$(RASCTL): $(SRC_PROTOBUF) $(OBJ_RASCTL_CORE) $(OBJ_RASCTL) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCTL_CORE) $(OBJ_RASCTL) $(OBJ_SHARED) $(OBJ_PROTOBUF) -lpthread -lprotobuf
|
||||
|
||||
$(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) -lpthread
|
||||
|
@ -213,14 +210,13 @@ $(BINDIR)/$(RASDUMP): $(OBJ_RASDUMP) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
|||
$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) $(LIB_BPI_WIRINGPI) -lpthread
|
||||
|
||||
$(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCSI_TEST) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCSI_TEST) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest
|
||||
$(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI_TEST) $(OBJ_RASCTL_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_CORE) $(OBJ_RASCTL_CORE) $(OBJ_RASCSI_TEST) $(OBJ_SHARED) $(OBJ_PROTOBUF) $(LIB_BPI_WIRINGPI) -lpthread -lpcap -lprotobuf -lstdc++fs -lgmock -lgtest
|
||||
|
||||
$(BINDIR)/$(GPIO_TEST): $(OBJ_GPIO_TEST) $(LIB_BPI_WIRINGPI) | $(BINDIR)
|
||||
echo $(OBJ_GPIO_TEST)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_GPIO_TEST) $(LIB_BPI_WIRINGPI) -lpthread
|
||||
|
||||
|
||||
# Phony rules for building individual utilities
|
||||
.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SCSIMON) $(GPIO_TEST)
|
||||
$(RASCSI) : $(BINDIR)/$(RASCSI)
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
// [ SCSI Common Functionality ]
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "scsi.h"
|
||||
#include "bus.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
@ -31,9 +30,9 @@ BUS::phase_t BUS::GetPhase()
|
|||
}
|
||||
|
||||
// Get target phase from bus signal line
|
||||
DWORD mci = GetMSG() ? 0x04 : 0x00;
|
||||
mci |= GetCD() ? 0x02 : 0x00;
|
||||
mci |= GetIO() ? 0x01 : 0x00;
|
||||
int mci = GetMSG() ? 0b100 : 0b000;
|
||||
mci |= GetCD() ? 0b010 : 0b000;
|
||||
mci |= GetIO() ? 0b001 : 0b000;
|
||||
return GetPhase(mci);
|
||||
}
|
||||
|
||||
|
@ -42,13 +41,9 @@ BUS::phase_t BUS::GetPhase()
|
|||
// Determine Phase String phase enum
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
const char* BUS::GetPhaseStrRaw(phase_t current_phase){
|
||||
if(current_phase <= phase_t::reserved) {
|
||||
return phase_str_table[(int)current_phase];
|
||||
}
|
||||
else {
|
||||
return "INVALID";
|
||||
}
|
||||
const char* BUS::GetPhaseStrRaw(phase_t current_phase) {
|
||||
const auto& it = phase_str_mapping.find(current_phase);
|
||||
return it != phase_str_mapping.end() ? it->second : "INVALID";
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -70,24 +65,21 @@ const array<BUS::phase_t, 8> BUS::phase_table = {
|
|||
phase_t::msgin // | 1 | 1 | 1 |
|
||||
};
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Phase Table
|
||||
// This MUST be kept in sync with the phase_t enum type!
|
||||
// Phase string to phase mapping
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
const array<const char*, 12> BUS::phase_str_table = {
|
||||
"busfree",
|
||||
"arbitration",
|
||||
"selection",
|
||||
"reselection",
|
||||
"command",
|
||||
"execute",
|
||||
"datain",
|
||||
"dataout",
|
||||
"status",
|
||||
"msgin",
|
||||
"msgout",
|
||||
"reserved"
|
||||
const unordered_map<BUS::phase_t, const char*> BUS::phase_str_mapping = {
|
||||
{ phase_t::busfree, "busfree" },
|
||||
{ phase_t::arbitration, "arbitration" },
|
||||
{ phase_t::selection, "selection" },
|
||||
{ phase_t::reselection, "reselection" },
|
||||
{ phase_t::command, "command" },
|
||||
{ phase_t::datain, "datain" },
|
||||
{ phase_t::dataout, "dataout" },
|
||||
{ phase_t::status, "status" },
|
||||
{ phase_t::msgin, "msgin" },
|
||||
{ phase_t::msgout, "msgout" },
|
||||
{ phase_t::reserved, "reserved" }
|
||||
};
|
113
src/raspberrypi/bus.h
Normal file
113
src/raspberrypi/bus.h
Normal file
|
@ -0,0 +1,113 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// X68000 EMULATOR "XM6"
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
// Copyright (C) 2014-2020 GIMONS
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "os.h"
|
||||
#include "scsi.h"
|
||||
#include <array>
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class BUS
|
||||
{
|
||||
public:
|
||||
// Operation modes definition
|
||||
enum class mode_e {
|
||||
TARGET = 0,
|
||||
INITIATOR = 1,
|
||||
MONITOR = 2,
|
||||
};
|
||||
|
||||
// Phase definitions
|
||||
enum class phase_t : int {
|
||||
busfree,
|
||||
arbitration,
|
||||
selection,
|
||||
reselection,
|
||||
command,
|
||||
datain,
|
||||
dataout,
|
||||
status,
|
||||
msgin,
|
||||
msgout,
|
||||
reserved
|
||||
};
|
||||
|
||||
BUS() = default;
|
||||
virtual ~BUS() = default;
|
||||
|
||||
// Basic Functions
|
||||
virtual bool Init(mode_e mode) = 0;
|
||||
virtual void Reset() = 0;
|
||||
virtual void Cleanup() = 0;
|
||||
phase_t GetPhase();
|
||||
|
||||
static phase_t GetPhase(int mci)
|
||||
{
|
||||
return phase_table[mci];
|
||||
}
|
||||
|
||||
// Get the string phase name, based upon the raw data
|
||||
static const char* GetPhaseStrRaw(phase_t current_phase);
|
||||
|
||||
// Extract as specific pin field from a raw data capture
|
||||
static inline uint32_t GetPinRaw(uint32_t raw_data, uint32_t pin_num)
|
||||
{
|
||||
return ((raw_data >> pin_num) & 1);
|
||||
}
|
||||
|
||||
virtual bool GetBSY() const = 0;
|
||||
virtual void SetBSY(bool ast) = 0;
|
||||
|
||||
virtual bool GetSEL() const = 0;
|
||||
virtual void SetSEL(bool ast) = 0;
|
||||
|
||||
virtual bool GetATN() const = 0;
|
||||
virtual void SetATN(bool ast) = 0;
|
||||
|
||||
virtual bool GetACK() const = 0;
|
||||
virtual void SetACK(bool ast) = 0;
|
||||
|
||||
virtual bool GetRST() const = 0;
|
||||
virtual void SetRST(bool ast) = 0;
|
||||
|
||||
virtual bool GetMSG() const = 0;
|
||||
virtual void SetMSG(bool ast) = 0;
|
||||
|
||||
virtual bool GetCD() const = 0;
|
||||
virtual void SetCD(bool ast) = 0;
|
||||
|
||||
virtual bool GetIO() = 0;
|
||||
virtual void SetIO(bool ast) = 0;
|
||||
|
||||
virtual bool GetREQ() const = 0;
|
||||
virtual void SetREQ(bool ast) = 0;
|
||||
|
||||
virtual BYTE GetDAT() = 0;
|
||||
virtual void SetDAT(BYTE dat) = 0;
|
||||
virtual bool GetDP() const = 0; // Get parity signal
|
||||
|
||||
virtual uint32_t Acquire() = 0;
|
||||
virtual int CommandHandShake(BYTE *buf) = 0;
|
||||
virtual int ReceiveHandShake(BYTE *buf, int count) = 0;
|
||||
virtual int SendHandShake(BYTE *buf, int count, int delay_after_bytes) = 0;
|
||||
|
||||
virtual bool GetSignal(int pin) const = 0;
|
||||
// Get SCSI input signal value
|
||||
virtual void SetSignal(int pin, bool ast) = 0;
|
||||
// Set SCSI output signal value
|
||||
static const int SEND_NO_DELAY = -1;
|
||||
// Passed into SendHandShake when we don't want to delay
|
||||
private:
|
||||
static const array<phase_t, 8> phase_table;
|
||||
|
||||
static const unordered_map<phase_t, const char *> phase_str_mapping;
|
||||
};
|
|
@ -1,27 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class SocketConnector;
|
||||
class Localizer;
|
||||
|
||||
struct CommandContext
|
||||
{
|
||||
CommandContext(const SocketConnector& c, const Localizer& l, int f, const std::string& s)
|
||||
: connector(c), localizer(l), fd(f), locale(s) {}
|
||||
~CommandContext() = default;
|
||||
|
||||
const SocketConnector& connector;
|
||||
const Localizer& localizer;
|
||||
int fd;
|
||||
std::string locale;
|
||||
};
|
|
@ -9,8 +9,7 @@
|
|||
|
||||
#include "log.h"
|
||||
#include "rascsi_interface.pb.h"
|
||||
#include "localizer.h"
|
||||
#include "socket_connector.h"
|
||||
#include "protobuf_serializer.h"
|
||||
#include "command_util.h"
|
||||
#include <sstream>
|
||||
|
||||
|
@ -86,51 +85,3 @@ void command_util::AddParam(PbDeviceDefinition& device, const string& key, strin
|
|||
map[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool command_util::ReturnLocalizedError(const CommandContext& context, LocalizationKey key,
|
||||
const string& arg1, const string& arg2, const string& arg3)
|
||||
{
|
||||
return ReturnLocalizedError(context, key, NO_ERROR_CODE, arg1, arg2, arg3);
|
||||
}
|
||||
|
||||
bool command_util::ReturnLocalizedError(const CommandContext& context, LocalizationKey key,
|
||||
PbErrorCode error_code, const string& arg1, const string& arg2, const string& arg3)
|
||||
{
|
||||
// For the logfile always use English
|
||||
LOGERROR("%s", context.localizer.Localize(key, "en", arg1, arg2, arg3).c_str())
|
||||
|
||||
return ReturnStatus(context, false, context.localizer.Localize(key, context.locale, arg1, arg2, arg3), error_code,
|
||||
false);
|
||||
}
|
||||
|
||||
bool command_util::ReturnStatus(const CommandContext& context, bool status, const string& msg,
|
||||
PbErrorCode error_code, bool log)
|
||||
{
|
||||
// Do not log twice if logging has already been done in the localized error handling above
|
||||
if (log && !status && !msg.empty()) {
|
||||
LOGERROR("%s", msg.c_str())
|
||||
}
|
||||
|
||||
if (context.fd == -1) {
|
||||
if (!msg.empty()) {
|
||||
if (status) {
|
||||
FPRT(stderr, "Error: ");
|
||||
FPRT(stderr, "%s", msg.c_str());
|
||||
FPRT(stderr, "\n");
|
||||
}
|
||||
else {
|
||||
FPRT(stdout, "%s", msg.c_str());
|
||||
FPRT(stderr, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
PbResult result;
|
||||
result.set_status(status);
|
||||
result.set_error_code(error_code);
|
||||
result.set_msg(msg);
|
||||
context.connector.SerializeMessage(context.fd, result);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,6 @@
|
|||
#define USE_SEL_EVENT_ENABLE // Check SEL signal by event
|
||||
// This avoids an indefinite loop with warnings if there is no RaSCSI hardware
|
||||
// and thus helps with running rasctl and unit test on x86 hardware.
|
||||
#if defined(__x86_64__) || defined(__X86__) || !defined(__linux)
|
||||
#if defined(__x86_64__) || defined(__X86__) || !defined(__linux__)
|
||||
#undef USE_SEL_EVENT_ENABLE
|
||||
#endif
|
||||
|
|
|
@ -7,8 +7,9 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "abstract_controller.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "abstract_controller.h"
|
||||
|
||||
void AbstractController::AllocateBuffer(size_t size)
|
||||
{
|
||||
|
@ -17,7 +18,18 @@ void AbstractController::AllocateBuffer(size_t size)
|
|||
}
|
||||
}
|
||||
|
||||
PrimaryDevice *AbstractController::GetDeviceForLun(int lun) const {
|
||||
unordered_set<shared_ptr<PrimaryDevice>> AbstractController::GetDevices() const
|
||||
{
|
||||
unordered_set<shared_ptr<PrimaryDevice>> devices;
|
||||
|
||||
for (const auto& [id, lun] : luns) {
|
||||
devices.insert(lun);
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
shared_ptr<PrimaryDevice> AbstractController::GetDeviceForLun(int lun) const {
|
||||
const auto& it = luns.find(lun);
|
||||
return it == luns.end() ? nullptr : it->second;
|
||||
}
|
||||
|
@ -26,7 +38,7 @@ void AbstractController::Reset()
|
|||
{
|
||||
SetPhase(BUS::phase_t::busfree);
|
||||
|
||||
ctrl.status = 0x00;
|
||||
ctrl.status = status::GOOD;
|
||||
ctrl.message = 0x00;
|
||||
ctrl.blocks = 0;
|
||||
ctrl.next = 0;
|
||||
|
@ -75,18 +87,15 @@ void AbstractController::ProcessPhase()
|
|||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
LOGERROR("Cannot process phase %s", BUS::GetPhaseStrRaw(GetPhase()))
|
||||
throw scsi_exception();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool AbstractController::AddDevice(PrimaryDevice *device)
|
||||
bool AbstractController::AddDevice(shared_ptr<PrimaryDevice> device)
|
||||
{
|
||||
if (device->GetLun() >= GetMaxLuns()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (HasDeviceForLun(device->GetLun())) {
|
||||
if (device->GetLun() < 0 || device->GetLun() >= GetMaxLuns() || HasDeviceForLun(device->GetLun())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -96,7 +105,7 @@ bool AbstractController::AddDevice(PrimaryDevice *device)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool AbstractController::DeleteDevice(const PrimaryDevice *device)
|
||||
bool AbstractController::DeleteDevice(const shared_ptr<PrimaryDevice> device)
|
||||
{
|
||||
return luns.erase(device->GetLun()) == 1;
|
||||
}
|
||||
|
|
|
@ -12,23 +12,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "scsi.h"
|
||||
#include "bus.h"
|
||||
#include "phase_handler.h"
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
class PrimaryDevice;
|
||||
|
||||
class AbstractController
|
||||
class AbstractController : public PhaseHandler
|
||||
{
|
||||
friend class PrimaryDevice;
|
||||
friend class ScsiController;
|
||||
|
||||
BUS::phase_t phase = BUS::phase_t::busfree;
|
||||
|
||||
// Logical units of this device controller mapped to their LUN numbers
|
||||
unordered_map<int, PrimaryDevice *> luns;
|
||||
// Logical units of this controller mapped to their LUN numbers
|
||||
unordered_map<int, shared_ptr<PrimaryDevice>> luns;
|
||||
|
||||
public:
|
||||
|
||||
|
@ -41,7 +42,7 @@ public:
|
|||
|
||||
using ctrl_t = struct _ctrl_t {
|
||||
vector<int> cmd; // Command data, dynamically allocated per received command
|
||||
uint32_t status; // Status data
|
||||
scsi_defs::status status; // Status data
|
||||
int message; // Message data
|
||||
|
||||
// Transfer
|
||||
|
@ -52,21 +53,8 @@ public:
|
|||
uint32_t length; // Transfer remaining length
|
||||
};
|
||||
|
||||
AbstractController(shared_ptr<BUS> bus, int target_id, int luns) : target_id(target_id), bus(bus), max_luns(luns) {}
|
||||
virtual ~AbstractController() = default;
|
||||
AbstractController(AbstractController&) = delete;
|
||||
AbstractController& operator=(const AbstractController&) = delete;
|
||||
|
||||
virtual void BusFree() = 0;
|
||||
virtual void Selection() = 0;
|
||||
virtual void Command() = 0;
|
||||
virtual void Status() = 0;
|
||||
virtual void DataIn() = 0;
|
||||
virtual void DataOut() = 0;
|
||||
virtual void MsgIn() = 0;
|
||||
virtual void MsgOut() = 0;
|
||||
|
||||
virtual BUS::phase_t Process(int) = 0;
|
||||
AbstractController(BUS& bus, int target_id, int max_luns) : target_id(target_id), bus(bus), max_luns(max_luns) {}
|
||||
~AbstractController() override = default;
|
||||
|
||||
virtual void Error(scsi_defs::sense_key, scsi_defs::asc = scsi_defs::asc::NO_ADDITIONAL_SENSE_INFORMATION,
|
||||
scsi_defs::status = scsi_defs::status::CHECK_CONDITION) = 0;
|
||||
|
@ -81,19 +69,19 @@ public:
|
|||
|
||||
int GetTargetId() const { return target_id; }
|
||||
int GetMaxLuns() const { return max_luns; }
|
||||
bool HasLuns() const { return !luns.empty(); }
|
||||
int GetLunCount() const { return (int)luns.size(); }
|
||||
|
||||
PrimaryDevice *GetDeviceForLun(int) const;
|
||||
bool AddDevice(PrimaryDevice *);
|
||||
bool DeleteDevice(const PrimaryDevice *);
|
||||
unordered_set<shared_ptr<PrimaryDevice>> GetDevices() const;
|
||||
shared_ptr<PrimaryDevice> GetDeviceForLun(int) const;
|
||||
bool AddDevice(shared_ptr<PrimaryDevice>);
|
||||
bool DeleteDevice(const shared_ptr<PrimaryDevice>);
|
||||
bool HasDeviceForLun(int) const;
|
||||
int ExtractInitiatorId(int id_data) const;
|
||||
int ExtractInitiatorId(int) const;
|
||||
|
||||
void AllocateBuffer(size_t);
|
||||
vector<BYTE>& GetBuffer() { return ctrl.buffer; }
|
||||
size_t GetBufferSize() const { return ctrl.buffer.size(); }
|
||||
uint32_t GetStatus() const { return ctrl.status; }
|
||||
void SetStatus(uint32_t s) { ctrl.status = s; }
|
||||
scsi_defs::status GetStatus() const { return ctrl.status; }
|
||||
void SetStatus(scsi_defs::status s) { ctrl.status = s; }
|
||||
uint32_t GetLength() const { return ctrl.length; }
|
||||
|
||||
protected:
|
||||
|
@ -106,25 +94,15 @@ protected:
|
|||
vector<int>& InitCmd(int size) { ctrl.cmd.resize(size); return ctrl.cmd; }
|
||||
|
||||
bool HasValidLength() const { return ctrl.length != 0; }
|
||||
int GetOffset() const { return ctrl.offset; }
|
||||
void ResetOffset() { ctrl.offset = 0; }
|
||||
void UpdateOffsetAndLength() { ctrl.offset += ctrl.length; ctrl.length = 0; }
|
||||
|
||||
BUS::phase_t GetPhase() const { return phase; }
|
||||
void SetPhase(BUS::phase_t p) { phase = p; }
|
||||
bool IsSelection() const { return phase == BUS::phase_t::selection; }
|
||||
bool IsBusFree() const { return phase == BUS::phase_t::busfree; }
|
||||
bool IsCommand() const { return phase == BUS::phase_t::command; }
|
||||
bool IsStatus() const { return phase == BUS::phase_t::status; }
|
||||
bool IsDataIn() const { return phase == BUS::phase_t::datain; }
|
||||
bool IsDataOut() const { return phase == BUS::phase_t::dataout; }
|
||||
bool IsMsgIn() const { return phase == BUS::phase_t::msgin; }
|
||||
bool IsMsgOut() const { return phase == BUS::phase_t::msgout; }
|
||||
|
||||
private:
|
||||
|
||||
int target_id;
|
||||
|
||||
shared_ptr<BUS> bus;
|
||||
BUS& bus;
|
||||
|
||||
int max_luns;
|
||||
|
||||
|
|
|
@ -9,23 +9,33 @@
|
|||
|
||||
#include "devices/device_factory.h"
|
||||
#include "devices/primary_device.h"
|
||||
#include "devices/file_support.h"
|
||||
#include "scsi_controller.h"
|
||||
#include "controller_manager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool ControllerManager::CreateScsiController(shared_ptr<BUS> bus, PrimaryDevice *device)
|
||||
bool ControllerManager::AttachToScsiController(int id, shared_ptr<PrimaryDevice> device)
|
||||
{
|
||||
shared_ptr<AbstractController> controller = FindController(device->GetId());
|
||||
auto controller = FindController(id);
|
||||
if (controller == nullptr) {
|
||||
controller = make_shared<ScsiController>(bus, device->GetId());
|
||||
controllers[device->GetId()] = controller;
|
||||
controller = make_shared<ScsiController>(bus, id);
|
||||
if (controller->AddDevice(device)) {
|
||||
controllers[id] = controller;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return controller->AddDevice(device);
|
||||
}
|
||||
|
||||
bool ControllerManager::DeleteController(shared_ptr<AbstractController> controller)
|
||||
{
|
||||
return controllers.erase(controller->GetTargetId()) == 1;
|
||||
}
|
||||
|
||||
shared_ptr<AbstractController> ControllerManager::IdentifyController(int data) const
|
||||
{
|
||||
for (const auto& [id, controller] : controllers) {
|
||||
|
@ -43,6 +53,18 @@ shared_ptr<AbstractController> ControllerManager::FindController(int target_id)
|
|||
return it == controllers.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
unordered_set<shared_ptr<PrimaryDevice>> ControllerManager::GetAllDevices() const
|
||||
{
|
||||
unordered_set<shared_ptr<PrimaryDevice>> devices;
|
||||
|
||||
for (const auto& [id, controller] : controllers) {
|
||||
const auto& d = controller->GetDevices();
|
||||
devices.insert(d.begin(), d.end());
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
void ControllerManager::DeleteAllControllers()
|
||||
{
|
||||
controllers.clear();
|
||||
|
@ -55,7 +77,7 @@ void ControllerManager::ResetAllControllers() const
|
|||
}
|
||||
}
|
||||
|
||||
PrimaryDevice *ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
|
||||
shared_ptr<PrimaryDevice> ControllerManager::GetDeviceByIdAndLun(int id, int lun) const
|
||||
{
|
||||
if (const auto controller = FindController(id); controller != nullptr) {
|
||||
return controller->GetDeviceForLun(lun);
|
||||
|
|
|
@ -12,9 +12,10 @@
|
|||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
class BUS;
|
||||
class AbstractController;
|
||||
|
@ -22,23 +23,24 @@ class PrimaryDevice;
|
|||
|
||||
class ControllerManager
|
||||
{
|
||||
BUS& bus;
|
||||
|
||||
unordered_map<int, shared_ptr<AbstractController>> controllers;
|
||||
|
||||
public:
|
||||
|
||||
ControllerManager() = default;
|
||||
explicit ControllerManager(BUS& bus) : bus(bus) {}
|
||||
~ControllerManager() = default;
|
||||
ControllerManager(ControllerManager&) = delete;
|
||||
ControllerManager& operator=(const ControllerManager&) = delete;
|
||||
|
||||
// Maximum number of controller devices
|
||||
static const int DEVICE_MAX = 8;
|
||||
|
||||
bool CreateScsiController(shared_ptr<BUS>, PrimaryDevice *);
|
||||
bool AttachToScsiController(int, shared_ptr<PrimaryDevice>);
|
||||
bool DeleteController(shared_ptr<AbstractController>);
|
||||
shared_ptr<AbstractController> IdentifyController(int) const;
|
||||
shared_ptr<AbstractController> FindController(int) const;
|
||||
unordered_set<shared_ptr<PrimaryDevice>> GetAllDevices() const;
|
||||
void DeleteAllControllers();
|
||||
void ResetAllControllers() const;
|
||||
PrimaryDevice *GetDeviceByIdAndLun(int, int) const;
|
||||
|
||||
unordered_map<int, shared_ptr<AbstractController>> controllers;
|
||||
shared_ptr<PrimaryDevice> GetDeviceByIdAndLun(int, int) const;
|
||||
};
|
||||
|
|
46
src/raspberrypi/controllers/phase_handler.h
Normal file
46
src/raspberrypi/controllers/phase_handler.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "scsi.h"
|
||||
|
||||
class PhaseHandler
|
||||
{
|
||||
BUS::phase_t phase = BUS::phase_t::busfree;
|
||||
|
||||
public:
|
||||
|
||||
PhaseHandler() = default;
|
||||
virtual ~PhaseHandler() = default;
|
||||
|
||||
virtual void BusFree() = 0;
|
||||
virtual void Selection() = 0;
|
||||
virtual void Command() = 0;
|
||||
virtual void Status() = 0;
|
||||
virtual void DataIn() = 0;
|
||||
virtual void DataOut() = 0;
|
||||
virtual void MsgIn() = 0;
|
||||
virtual void MsgOut() = 0;
|
||||
|
||||
virtual BUS::phase_t Process(int) = 0;
|
||||
|
||||
protected:
|
||||
|
||||
BUS::phase_t GetPhase() const { return phase; }
|
||||
void SetPhase(BUS::phase_t p) { phase = p; }
|
||||
bool IsSelection() const { return phase == BUS::phase_t::selection; }
|
||||
bool IsBusFree() const { return phase == BUS::phase_t::busfree; }
|
||||
bool IsCommand() const { return phase == BUS::phase_t::command; }
|
||||
bool IsStatus() const { return phase == BUS::phase_t::status; }
|
||||
bool IsDataIn() const { return phase == BUS::phase_t::datain; }
|
||||
bool IsDataOut() const { return phase == BUS::phase_t::dataout; }
|
||||
bool IsMsgIn() const { return phase == BUS::phase_t::msgin; }
|
||||
bool IsMsgOut() const { return phase == BUS::phase_t::msgout; }
|
||||
};
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -21,13 +21,13 @@
|
|||
#include "scsi_controller.h"
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
#include <linux/if_tun.h>
|
||||
#endif
|
||||
|
||||
using namespace scsi_defs;
|
||||
|
||||
ScsiController::ScsiController(shared_ptr<BUS> bus, int target_id) : AbstractController(bus, target_id, LUN_MAX)
|
||||
ScsiController::ScsiController(BUS& bus, int target_id) : AbstractController(bus, target_id, LUN_MAX)
|
||||
{
|
||||
// The initial buffer size will default to either the default buffer size OR
|
||||
// the size of an Ethernet message, whichever is larger.
|
||||
|
@ -52,17 +52,17 @@ void ScsiController::Reset()
|
|||
BUS::phase_t ScsiController::Process(int id)
|
||||
{
|
||||
// Get bus information
|
||||
bus->Acquire();
|
||||
bus.Acquire();
|
||||
|
||||
// Check to see if the reset signal was asserted
|
||||
if (bus->GetRST()) {
|
||||
if (bus.GetRST()) {
|
||||
LOGWARN("RESET signal received!")
|
||||
|
||||
// Reset the controller
|
||||
Reset();
|
||||
|
||||
// Reset the bus
|
||||
bus->Reset();
|
||||
bus.Reset();
|
||||
|
||||
return GetPhase();
|
||||
}
|
||||
|
@ -79,14 +79,14 @@ BUS::phase_t ScsiController::Process(int id)
|
|||
try {
|
||||
ProcessPhase();
|
||||
}
|
||||
catch(const scsi_error_exception&) {
|
||||
catch(const scsi_exception&) {
|
||||
// Any exception should have been handled during the phase processing
|
||||
assert(false);
|
||||
|
||||
LOGERROR("%s Unhandled SCSI error, resetting controller and bus and entering bus free phase", __PRETTY_FUNCTION__)
|
||||
|
||||
Reset();
|
||||
bus->Reset();
|
||||
bus.Reset();
|
||||
|
||||
BusFree();
|
||||
}
|
||||
|
@ -101,14 +101,14 @@ void ScsiController::BusFree()
|
|||
|
||||
SetPhase(BUS::phase_t::busfree);
|
||||
|
||||
bus->SetREQ(false);
|
||||
bus->SetMSG(false);
|
||||
bus->SetCD(false);
|
||||
bus->SetIO(false);
|
||||
bus->SetBSY(false);
|
||||
bus.SetREQ(false);
|
||||
bus.SetMSG(false);
|
||||
bus.SetCD(false);
|
||||
bus.SetIO(false);
|
||||
bus.SetBSY(false);
|
||||
|
||||
// Initialize status and message
|
||||
SetStatus(0);
|
||||
SetStatus(status::GOOD);
|
||||
ctrl.message = 0x00;
|
||||
|
||||
// Initialize ATN message reception status
|
||||
|
@ -120,21 +120,21 @@ void ScsiController::BusFree()
|
|||
bytes_to_transfer = 0;
|
||||
|
||||
// When the bus is free RaSCSI or the Pi may be shut down.
|
||||
// TODO Try to find a better place for this code without breaking encapsulation
|
||||
// This code has to be executed in the bus free phase and thus has to be located in the controller.
|
||||
switch(shutdown_mode) {
|
||||
case AbstractController::rascsi_shutdown_mode::STOP_RASCSI:
|
||||
case rascsi_shutdown_mode::STOP_RASCSI:
|
||||
LOGINFO("RaSCSI shutdown requested")
|
||||
exit(0);
|
||||
break;
|
||||
|
||||
case AbstractController::rascsi_shutdown_mode::STOP_PI:
|
||||
case rascsi_shutdown_mode::STOP_PI:
|
||||
LOGINFO("Raspberry Pi shutdown requested")
|
||||
if (system("init 0") == -1) {
|
||||
LOGERROR("Raspberry Pi shutdown failed: %s", strerror(errno))
|
||||
}
|
||||
break;
|
||||
|
||||
case AbstractController::rascsi_shutdown_mode::RESTART_PI:
|
||||
case rascsi_shutdown_mode::RESTART_PI:
|
||||
LOGINFO("Raspberry Pi restart requested")
|
||||
if (system("init 6") == -1) {
|
||||
LOGERROR("Raspberry Pi restart failed: %s", strerror(errno))
|
||||
|
@ -149,7 +149,7 @@ void ScsiController::BusFree()
|
|||
}
|
||||
|
||||
// Move to selection phase
|
||||
if (bus->GetSEL() && !bus->GetBSY()) {
|
||||
if (bus.GetSEL() && !bus.GetBSY()) {
|
||||
Selection();
|
||||
}
|
||||
}
|
||||
|
@ -158,12 +158,12 @@ void ScsiController::Selection()
|
|||
{
|
||||
if (!IsSelection()) {
|
||||
// A different device controller was selected
|
||||
if (int id = 1 << GetTargetId(); ((int)bus->GetDAT() & id) == 0) {
|
||||
if (int id = 1 << GetTargetId(); ((int)bus.GetDAT() & id) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Abort if there is no LUN for this controller
|
||||
if (!HasLuns()) {
|
||||
if (!GetLunCount()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -172,14 +172,14 @@ void ScsiController::Selection()
|
|||
SetPhase(BUS::phase_t::selection);
|
||||
|
||||
// Raise BSY and respond
|
||||
bus->SetBSY(true);
|
||||
bus.SetBSY(true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Selection completed
|
||||
if (!bus->GetSEL() && bus->GetBSY()) {
|
||||
if (!bus.GetSEL() && bus.GetBSY()) {
|
||||
// Message out phase if ATN=1, otherwise command phase
|
||||
if (bus->GetATN()) {
|
||||
if (bus.GetATN()) {
|
||||
MsgOut();
|
||||
} else {
|
||||
Command();
|
||||
|
@ -194,12 +194,12 @@ void ScsiController::Command()
|
|||
|
||||
SetPhase(BUS::phase_t::command);
|
||||
|
||||
bus->SetMSG(false);
|
||||
bus->SetCD(true);
|
||||
bus->SetIO(false);
|
||||
bus.SetMSG(false);
|
||||
bus.SetCD(true);
|
||||
bus.SetIO(false);
|
||||
|
||||
int actual_count = bus->CommandHandShake(GetBuffer().data());
|
||||
int command_byte_count = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
|
||||
const int actual_count = bus.CommandHandShake(GetBuffer().data());
|
||||
const int command_byte_count = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
|
||||
|
||||
// If not able to receive all, move to the status phase
|
||||
if (actual_count != command_byte_count) {
|
||||
|
@ -229,8 +229,6 @@ void ScsiController::Execute()
|
|||
{
|
||||
LOGDEBUG("++++ CMD ++++ %s Executing command $%02X", __PRETTY_FUNCTION__, (int)GetOpcode())
|
||||
|
||||
SetPhase(BUS::phase_t::execute);
|
||||
|
||||
// Initialization for data transfer
|
||||
ResetOffset();
|
||||
ctrl.blocks = 1;
|
||||
|
@ -238,7 +236,7 @@ void ScsiController::Execute()
|
|||
|
||||
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
|
||||
if (GetOpcode() != scsi_command::eCmdRequestSense) {
|
||||
SetStatus(0);
|
||||
SetStatus(status::GOOD);
|
||||
}
|
||||
|
||||
int lun = GetEffectiveLun();
|
||||
|
@ -258,21 +256,21 @@ void ScsiController::Execute()
|
|||
}
|
||||
}
|
||||
|
||||
PrimaryDevice *device = GetDeviceForLun(lun);
|
||||
auto device = GetDeviceForLun(lun);
|
||||
|
||||
// Discard pending sense data from the previous command if the current command is not REQUEST SENSE
|
||||
if (GetOpcode() != scsi_command::eCmdRequestSense) {
|
||||
device->SetStatusCode(0);
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
if (!device->Dispatch(GetOpcode())) {
|
||||
LOGTRACE("ID %d LUN %d received unsupported command: $%02X", GetTargetId(), lun, (int)GetOpcode())
|
||||
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
}
|
||||
catch(const scsi_error_exception& e) { //NOSONAR This exception is handled properly
|
||||
catch(const scsi_exception& e) { //NOSONAR This exception is handled properly
|
||||
Error(e.get_sense_key(), e.get_asc(), e.get_status());
|
||||
|
||||
// Fall through
|
||||
|
@ -298,14 +296,14 @@ void ScsiController::Status()
|
|||
SysTimer::instance().SleepUsec(5);
|
||||
}
|
||||
|
||||
LOGTRACE("%s Status Phase $%02X",__PRETTY_FUNCTION__, GetStatus())
|
||||
LOGTRACE("%s Status Phase, status is $%02X",__PRETTY_FUNCTION__, (int)GetStatus())
|
||||
|
||||
SetPhase(BUS::phase_t::status);
|
||||
|
||||
// Signal line operated by the target
|
||||
bus->SetMSG(false);
|
||||
bus->SetCD(true);
|
||||
bus->SetIO(true);
|
||||
bus.SetMSG(false);
|
||||
bus.SetCD(true);
|
||||
bus.SetIO(true);
|
||||
|
||||
// Data transfer is 1 byte x 1 block
|
||||
ResetOffset();
|
||||
|
@ -326,9 +324,9 @@ void ScsiController::MsgIn()
|
|||
|
||||
SetPhase(BUS::phase_t::msgin);
|
||||
|
||||
bus->SetMSG(true);
|
||||
bus->SetCD(true);
|
||||
bus->SetIO(true);
|
||||
bus.SetMSG(true);
|
||||
bus.SetCD(true);
|
||||
bus.SetIO(true);
|
||||
|
||||
// length, blocks are already set
|
||||
assert(HasValidLength());
|
||||
|
@ -357,9 +355,9 @@ void ScsiController::MsgOut()
|
|||
|
||||
SetPhase(BUS::phase_t::msgout);
|
||||
|
||||
bus->SetMSG(true);
|
||||
bus->SetCD(true);
|
||||
bus->SetIO(false);
|
||||
bus.SetMSG(true);
|
||||
bus.SetCD(true);
|
||||
bus.SetIO(false);
|
||||
|
||||
// Data transfer is 1 byte x 1 block
|
||||
ResetOffset();
|
||||
|
@ -390,9 +388,9 @@ void ScsiController::DataIn()
|
|||
|
||||
SetPhase(BUS::phase_t::datain);
|
||||
|
||||
bus->SetMSG(false);
|
||||
bus->SetCD(false);
|
||||
bus->SetIO(true);
|
||||
bus.SetMSG(false);
|
||||
bus.SetCD(false);
|
||||
bus.SetIO(true);
|
||||
|
||||
// length, blocks are already set
|
||||
assert(ctrl.blocks > 0);
|
||||
|
@ -423,9 +421,9 @@ void ScsiController::DataOut()
|
|||
SetPhase(BUS::phase_t::dataout);
|
||||
|
||||
// Signal line operated by the target
|
||||
bus->SetMSG(false);
|
||||
bus->SetCD(false);
|
||||
bus->SetIO(false);
|
||||
bus.SetMSG(false);
|
||||
bus.SetCD(false);
|
||||
bus.SetIO(false);
|
||||
|
||||
ResetOffset();
|
||||
return;
|
||||
|
@ -437,12 +435,12 @@ void ScsiController::DataOut()
|
|||
void ScsiController::Error(sense_key sense_key, asc asc, status status)
|
||||
{
|
||||
// Get bus information
|
||||
bus->Acquire();
|
||||
bus.Acquire();
|
||||
|
||||
// Reset check
|
||||
if (bus->GetRST()) {
|
||||
if (bus.GetRST()) {
|
||||
Reset();
|
||||
bus->Reset();
|
||||
bus.Reset();
|
||||
|
||||
return;
|
||||
}
|
||||
|
@ -461,11 +459,13 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
|
|||
}
|
||||
|
||||
if (sense_key != sense_key::NO_SENSE || asc != asc::NO_ADDITIONAL_SENSE_INFORMATION) {
|
||||
LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X", (int)sense_key, (int)asc)
|
||||
|
||||
// Set Sense Key and ASC for a subsequent REQUEST SENSE
|
||||
GetDeviceForLun(lun)->SetStatusCode(((int)sense_key << 16) | ((int)asc << 8));
|
||||
}
|
||||
|
||||
SetStatus((uint32_t)status);
|
||||
SetStatus(status);
|
||||
ctrl.message = 0x00;
|
||||
|
||||
LOGTRACE("%s Error (to status phase)", __PRETTY_FUNCTION__)
|
||||
|
@ -475,16 +475,16 @@ void ScsiController::Error(sense_key sense_key, asc asc, status status)
|
|||
|
||||
void ScsiController::Send()
|
||||
{
|
||||
assert(!bus->GetREQ());
|
||||
assert(bus->GetIO());
|
||||
assert(!bus.GetREQ());
|
||||
assert(bus.GetIO());
|
||||
|
||||
if (HasValidLength()) {
|
||||
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(ctrl.offset) + ", length "
|
||||
LOGTRACE("%s%s", __PRETTY_FUNCTION__, (" Sending handhake with offset " + to_string(GetOffset()) + ", length "
|
||||
+ to_string(ctrl.length)).c_str())
|
||||
|
||||
// TODO The delay has to be taken from ctrl.unit[lun], but as there are currently no Daynaport drivers for
|
||||
// LUNs other than 0 this work-around works.
|
||||
if (int len = bus->SendHandShake(GetBuffer().data() + ctrl.offset, ctrl.length,
|
||||
if (const int len = bus.SendHandShake(GetBuffer().data() + ctrl.offset, ctrl.length,
|
||||
HasDeviceForLun(0) ? GetDeviceForLun(0)->GetSendDelay() : 0);
|
||||
len != (int)ctrl.length) {
|
||||
// If you cannot send all, move to status phase
|
||||
|
@ -571,15 +571,15 @@ void ScsiController::Receive()
|
|||
LOGTRACE("%s",__PRETTY_FUNCTION__)
|
||||
|
||||
// REQ is low
|
||||
assert(!bus->GetREQ());
|
||||
assert(!bus->GetIO());
|
||||
assert(!bus.GetREQ());
|
||||
assert(!bus.GetIO());
|
||||
|
||||
// Length != 0 if received
|
||||
if (HasValidLength()) {
|
||||
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
|
||||
|
||||
// If not able to receive all, move to status phase
|
||||
if (int len = bus->ReceiveHandShake(GetBuffer().data() + ctrl.offset, ctrl.length);
|
||||
if (int len = bus.ReceiveHandShake(GetBuffer().data() + GetOffset(), ctrl.length);
|
||||
len != (int)ctrl.length) {
|
||||
LOGERROR("%s Not able to receive %d bytes of data, only received %d",__PRETTY_FUNCTION__, ctrl.length, len)
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
|
@ -625,6 +625,7 @@ void ScsiController::Receive()
|
|||
}
|
||||
|
||||
// If result FALSE, move to status phase
|
||||
// TODO Check whether we can raise scsi_exception here in order to simplify the error handling
|
||||
if (!result) {
|
||||
Error(sense_key::ABORTED_COMMAND);
|
||||
return;
|
||||
|
@ -648,7 +649,8 @@ void ScsiController::Receive()
|
|||
break;
|
||||
|
||||
case BUS::phase_t::dataout:
|
||||
FlushUnit();
|
||||
// Block-oriented data have been handled above
|
||||
DataOutNonBlockOriented();
|
||||
|
||||
Status();
|
||||
break;
|
||||
|
@ -675,14 +677,14 @@ bool ScsiController::XferMsg(int msg)
|
|||
|
||||
void ScsiController::ReceiveBytes()
|
||||
{
|
||||
assert(!bus->GetREQ());
|
||||
assert(!bus->GetIO());
|
||||
assert(!bus.GetREQ());
|
||||
assert(!bus.GetIO());
|
||||
|
||||
if (HasValidLength()) {
|
||||
LOGTRACE("%s Length is %d bytes", __PRETTY_FUNCTION__, ctrl.length)
|
||||
|
||||
// If not able to receive all, move to status phase
|
||||
if (uint32_t len = bus->ReceiveHandShake(GetBuffer().data() + ctrl.offset, ctrl.length);
|
||||
if (uint32_t len = bus.ReceiveHandShake(GetBuffer().data() + GetOffset(), ctrl.length);
|
||||
len != ctrl.length) {
|
||||
LOGERROR("%s Not able to receive %d bytes of data, only received %d",
|
||||
__PRETTY_FUNCTION__, ctrl.length, len)
|
||||
|
@ -769,17 +771,12 @@ bool ScsiController::XferOut(bool cont)
|
|||
return false;
|
||||
}
|
||||
|
||||
void ScsiController::FlushUnit()
|
||||
void ScsiController::DataOutNonBlockOriented()
|
||||
{
|
||||
assert(IsDataOut());
|
||||
|
||||
auto disk = dynamic_cast<Disk *>(GetDeviceForLun(GetEffectiveLun()));
|
||||
if (disk == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// WRITE system only
|
||||
switch (GetOpcode()) {
|
||||
// TODO Check why these cases are needed
|
||||
case scsi_command::eCmdWrite6:
|
||||
case scsi_command::eCmdWrite10:
|
||||
case scsi_command::eCmdWrite16:
|
||||
|
@ -790,26 +787,24 @@ void ScsiController::FlushUnit()
|
|||
break;
|
||||
|
||||
case scsi_command::eCmdModeSelect6:
|
||||
case scsi_command::eCmdModeSelect10:
|
||||
// TODO What is this special handling of ModeSelect good for?
|
||||
// Without it we would not need this method at all.
|
||||
// ModeSelect is already handled in XferOutBlockOriented(). Why would it have to be handled once more?
|
||||
try {
|
||||
disk->ModeSelect(ctrl.cmd, GetBuffer(), ctrl.offset);
|
||||
case scsi_command::eCmdModeSelect10: {
|
||||
// TODO Try to get rid of this cast
|
||||
if (auto device = dynamic_pointer_cast<ModePageDevice>(GetDeviceForLun(GetEffectiveLun()));
|
||||
device != nullptr) {
|
||||
device->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
|
||||
}
|
||||
else {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
}
|
||||
catch(const scsi_error_exception& e) {
|
||||
LOGWARN("Error occured while processing Mode Select command %02X\n", (int)GetOpcode())
|
||||
Error(e.get_sense_key(), e.get_asc(), e.get_status());
|
||||
return;
|
||||
}
|
||||
break;
|
||||
break;
|
||||
|
||||
case scsi_command::eCmdSetMcastAddr:
|
||||
// TODO: Eventually, we should store off the multicast address configuration data here...
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGWARN("Received an unexpected flush command $%02X\n", (int)GetOpcode())
|
||||
LOGWARN("Unexpected Data Out phase for command $%02X", (int)GetOpcode())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -838,9 +833,9 @@ bool ScsiController::XferIn(vector<BYTE>& buf)
|
|||
case scsi_command::eCmdRead16:
|
||||
// Read from disk
|
||||
try {
|
||||
ctrl.length = (static_cast<Disk *>(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next);
|
||||
ctrl.length = (dynamic_pointer_cast<Disk>(GetDeviceForLun(lun)))->Read(ctrl.cmd, buf, ctrl.next);
|
||||
}
|
||||
catch(const scsi_error_exception&) {
|
||||
catch(const scsi_exception&) {
|
||||
// If there is an error, go to the status phase
|
||||
return false;
|
||||
}
|
||||
|
@ -868,7 +863,7 @@ bool ScsiController::XferIn(vector<BYTE>& buf)
|
|||
//---------------------------------------------------------------------------
|
||||
bool ScsiController::XferOutBlockOriented(bool cont)
|
||||
{
|
||||
auto disk = dynamic_cast<Disk *>(GetDeviceForLun(GetEffectiveLun()));
|
||||
auto disk = dynamic_pointer_cast<Disk>(GetDeviceForLun(GetEffectiveLun()));
|
||||
if (disk == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
@ -878,9 +873,9 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
|||
case scsi_command::eCmdModeSelect6:
|
||||
case scsi_command::eCmdModeSelect10:
|
||||
try {
|
||||
disk->ModeSelect(ctrl.cmd, GetBuffer(), ctrl.offset);
|
||||
disk->ModeSelect(ctrl.cmd, GetBuffer(), GetOffset());
|
||||
}
|
||||
catch(const scsi_error_exception& e) {
|
||||
catch(const scsi_exception& e) {
|
||||
Error(e.get_sense_key(), e.get_asc(), e.get_status());
|
||||
return false;
|
||||
}
|
||||
|
@ -895,7 +890,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
|||
{
|
||||
// Special case Write function for brige
|
||||
// TODO This class must not know about SCSIBR
|
||||
if (auto bridge = dynamic_cast<SCSIBR *>(disk); bridge) {
|
||||
if (auto bridge = dynamic_pointer_cast<SCSIBR>(disk); bridge) {
|
||||
if (!bridge->WriteBytes(ctrl.cmd, GetBuffer(), ctrl.length)) {
|
||||
// Write failed
|
||||
return false;
|
||||
|
@ -907,7 +902,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
|||
|
||||
// Special case Write function for DaynaPort
|
||||
// TODO This class must not know about DaynaPort
|
||||
if (auto daynaport = dynamic_cast<SCSIDaynaPort *>(disk); daynaport) {
|
||||
if (auto daynaport = dynamic_pointer_cast<SCSIDaynaPort>(disk); daynaport) {
|
||||
daynaport->WriteBytes(ctrl.cmd, GetBuffer(), 0);
|
||||
|
||||
ResetOffset();
|
||||
|
@ -918,7 +913,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
|||
try {
|
||||
disk->Write(ctrl.cmd, GetBuffer(), ctrl.next - 1);
|
||||
}
|
||||
catch(const scsi_error_exception& e) {
|
||||
catch(const scsi_exception& e) {
|
||||
Error(e.get_sense_key(), e.get_asc(), e.get_status());
|
||||
|
||||
// Write failed
|
||||
|
@ -935,7 +930,7 @@ bool ScsiController::XferOutBlockOriented(bool cont)
|
|||
try {
|
||||
ctrl.length = disk->WriteCheck(ctrl.next - 1);
|
||||
}
|
||||
catch(const scsi_error_exception&) {
|
||||
catch(const scsi_exception&) {
|
||||
// Cannot write
|
||||
return false;
|
||||
}
|
||||
|
@ -961,9 +956,10 @@ void ScsiController::ProcessCommand()
|
|||
uint32_t len = GPIOBUS::GetCommandByteCount(GetBuffer()[0]);
|
||||
|
||||
stringstream s;
|
||||
s << setfill('0') << setw(2) << hex;
|
||||
for (uint32_t i = 0; i < len; i++) {
|
||||
ctrl.cmd[i] = GetBuffer()[i];
|
||||
s << setfill('0') << setw(2) << hex << ctrl.cmd[i];
|
||||
s << ctrl.cmd[i];
|
||||
}
|
||||
LOGTRACE("%s CDB=$%s",__PRETTY_FUNCTION__, s.str().c_str())
|
||||
|
||||
|
@ -974,7 +970,7 @@ void ScsiController::ParseMessage()
|
|||
{
|
||||
int i = 0;
|
||||
while (i < scsi.msc) {
|
||||
BYTE message_type = scsi.msb[i];
|
||||
const BYTE message_type = scsi.msb[i];
|
||||
|
||||
if (message_type == 0x06) {
|
||||
LOGTRACE("Received ABORT message")
|
||||
|
@ -1036,7 +1032,7 @@ void ScsiController::ParseMessage()
|
|||
void ScsiController::ProcessMessage()
|
||||
{
|
||||
// Continue message out phase as long as ATN keeps asserting
|
||||
if (bus->GetATN()) {
|
||||
if (bus.GetATN()) {
|
||||
// Data transfer is 1 byte x 1 block
|
||||
ResetOffset();
|
||||
ctrl.length = 1;
|
||||
|
@ -1062,8 +1058,8 @@ int ScsiController::GetEffectiveLun() const
|
|||
|
||||
void ScsiController::Sleep()
|
||||
{
|
||||
if (uint32_t time = SysTimer::instance().GetTimerLow() - execstart; time < MIN_EXEC_TIME) {
|
||||
SysTimer::instance().SleepUsec(MIN_EXEC_TIME - time);
|
||||
if (const uint32_t time = SysTimer::GetTimerLow() - execstart; time < MIN_EXEC_TIME) {
|
||||
SysTimer::SleepUsec(MIN_EXEC_TIME - time);
|
||||
}
|
||||
execstart = 0;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -54,10 +54,8 @@ public:
|
|||
// Maximum number of logical units
|
||||
static const int LUN_MAX = 32;
|
||||
|
||||
ScsiController(shared_ptr<BUS>, int);
|
||||
ScsiController(BUS&, int);
|
||||
~ScsiController() override = default;
|
||||
ScsiController(ScsiController&) = delete;
|
||||
ScsiController& operator=(const ScsiController&) = delete;
|
||||
|
||||
void Reset() override;
|
||||
|
||||
|
@ -105,7 +103,7 @@ private:
|
|||
void ReceiveBytes();
|
||||
|
||||
void Execute();
|
||||
void FlushUnit();
|
||||
void DataOutNonBlockOriented();
|
||||
void Receive();
|
||||
|
||||
void ProcessCommand();
|
||||
|
|
|
@ -7,12 +7,13 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "cd_track.h"
|
||||
#include <cassert>
|
||||
|
||||
void CDTrack::Init(int track, uint32_t first, uint32_t last)
|
||||
{
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -22,10 +22,8 @@ public:
|
|||
|
||||
CDTrack() = default;
|
||||
~CDTrack() = default;
|
||||
CDTrack(CDTrack&) = delete;
|
||||
CDTrack& operator=(const CDTrack&) = delete;
|
||||
|
||||
void Init(int track, DWORD first, DWORD last);
|
||||
void Init(int track, uint32_t first, uint32_t last);
|
||||
|
||||
// Properties
|
||||
void SetPath(bool cdda, const Filepath& path); // Set the path
|
||||
|
@ -34,7 +32,7 @@ public:
|
|||
uint32_t GetLast() const; // Get the last LBA
|
||||
uint32_t GetBlocks() const; // Get the number of blocks
|
||||
int GetTrackNo() const; // Get the track number
|
||||
bool IsValid(DWORD lba) const; // Is this a valid LBA?
|
||||
bool IsValid(uint32_t lba) const; // Is this a valid LBA?
|
||||
bool IsAudio() const; // Is this an audio track?
|
||||
|
||||
private:
|
||||
|
|
|
@ -48,7 +48,7 @@ static void convert(char const *src, char const *dest,
|
|||
return;
|
||||
}
|
||||
|
||||
if (size_t ret = iconv(cd, &inbuf, &in, &outbuf, &out); ret == (size_t)-1) {
|
||||
if (const size_t ret = iconv(cd, &inbuf, &in, &outbuf, &out); ret == (size_t)-1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const
|
|||
if (i >= 8) {
|
||||
// Transfer the extraneous part
|
||||
for (i = 0; i < 10; i++) {
|
||||
BYTE c = add[i];
|
||||
const BYTE c = add[i];
|
||||
if (c == '\0')
|
||||
break;
|
||||
*p++ = c;
|
||||
|
@ -178,7 +178,7 @@ void Human68k::namests_t::GetCopyFilename(BYTE* szFilename) const
|
|||
if (ext[0] != ' ' || ext[1] != ' ' || ext[2] != ' ') {
|
||||
*p++ = '.';
|
||||
for (i = 0; i < 3; i++) {
|
||||
BYTE c = ext[i];
|
||||
const BYTE c = ext[i];
|
||||
if (c == ' ') {
|
||||
// Check that the file extension continues after a space is detected
|
||||
/// TODO: Should change this function to be compatible with 8+3 chars and TwentyOne
|
||||
|
@ -226,7 +226,7 @@ CHostDrv::~CHostDrv()
|
|||
// Initialization (device boot and load)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag)
|
||||
void CHostDrv::Init(const TCHAR* szBase, uint32_t nFlag)
|
||||
{
|
||||
assert(szBase);
|
||||
assert(strlen(szBase) < FILEPATH_MAX);
|
||||
|
@ -253,7 +253,7 @@ void CHostDrv::Init(const TCHAR* szBase, DWORD nFlag)
|
|||
TCHAR* pClear = nullptr;
|
||||
TCHAR* p = m_szBase;
|
||||
for (;;) {
|
||||
TCHAR c = *p;
|
||||
const TCHAR c = *p;
|
||||
if (c == '\0')
|
||||
break;
|
||||
if (c == '/' || c == '\\') {
|
||||
|
@ -301,7 +301,7 @@ BYTE CHostDrv::GetMediaByte() const
|
|||
// Get drive status
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CHostDrv::GetStatus() const
|
||||
uint32_t CHostDrv::GetStatus() const
|
||||
{
|
||||
return 0x40 | (m_bEnable ? (m_bWriteProtect ? 0x08 : 0) | 0x02 : 0);
|
||||
}
|
||||
|
@ -408,15 +408,15 @@ bool CHostDrv::GetVolumeCache(TCHAR* szLabel) const
|
|||
return m_bVolumeCache;
|
||||
}
|
||||
|
||||
DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity)
|
||||
uint32_t CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity)
|
||||
{
|
||||
assert(pCapacity);
|
||||
assert(m_bEnable);
|
||||
|
||||
DWORD nFree = 0x7FFF8000;
|
||||
DWORD freearea;
|
||||
DWORD clusters;
|
||||
DWORD sectors;
|
||||
const uint32_t nFree = 0x7FFF8000;
|
||||
uint32_t freearea;
|
||||
uint32_t clusters;
|
||||
uint32_t sectors;
|
||||
|
||||
freearea = 0xFFFF;
|
||||
clusters = 0xFFFF;
|
||||
|
@ -428,9 +428,9 @@ DWORD CHostDrv::GetCapacity(Human68k::capacity_t* pCapacity)
|
|||
assert(sectors <= 64);
|
||||
|
||||
// Update cache
|
||||
m_capCache.freearea = (WORD)freearea;
|
||||
m_capCache.clusters = (WORD)clusters;
|
||||
m_capCache.sectors = (WORD)sectors;
|
||||
m_capCache.freearea = (uint16_t)freearea;
|
||||
m_capCache.clusters = (uint16_t)clusters;
|
||||
m_capCache.sectors = (uint16_t)sectors;
|
||||
m_capCache.bytes = 512;
|
||||
|
||||
// Transfer contents
|
||||
|
@ -808,7 +808,7 @@ void CHostFilename::ConvertHuman(int nCount)
|
|||
}
|
||||
|
||||
size_t nMax = 18; // Number of bytes for the base segment (base name and extension)
|
||||
DWORD nOption = CFileSys::GetFileOption();
|
||||
uint32_t nOption = CFileSys::GetFileOption();
|
||||
if (nOption & WINDRV_OPT_CONVERT_LENGTH)
|
||||
nMax = 8;
|
||||
|
||||
|
@ -817,7 +817,7 @@ void CHostFilename::ConvertHuman(int nCount)
|
|||
BYTE* pNumber = nullptr;
|
||||
if (nCount >= 0) {
|
||||
pNumber = &szNumber[8];
|
||||
for (DWORD i = 0; i < 5; i++) { // Max 5+1 digits (always leave the first 2 bytes of the base name)
|
||||
for (uint32_t i = 0; i < 5; i++) { // Max 5+1 digits (always leave the first 2 bytes of the base name)
|
||||
int n = nCount % 36;
|
||||
nMax--;
|
||||
pNumber--;
|
||||
|
@ -1082,7 +1082,7 @@ bool CHostFilename::isReduce() const
|
|||
/// Evaluate Human68k directory entry attribute
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CHostFilename::CheckAttribute(DWORD nHumanAttribute) const
|
||||
int CHostFilename::CheckAttribute(uint32_t nHumanAttribute) const
|
||||
{
|
||||
BYTE nAttribute = m_dirHuman.attr;
|
||||
if ((nAttribute & (Human68k::AT_ARCHIVE | Human68k::AT_DIRECTORY | Human68k::AT_VOLUME)) == 0)
|
||||
|
@ -1099,7 +1099,7 @@ int CHostFilename::CheckAttribute(DWORD nHumanAttribute) const
|
|||
const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static
|
||||
{
|
||||
// Obtain the file name length
|
||||
size_t nLength = strlen((const char*)szHuman);
|
||||
const size_t nLength = strlen((const char*)szHuman);
|
||||
const BYTE* pFirst = szHuman;
|
||||
const BYTE* pLast = pFirst + nLength;
|
||||
|
||||
|
@ -1124,7 +1124,7 @@ const BYTE* CHostFilename::SeparateExt(const BYTE* szHuman) // static
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
DWORD CHostPath::g_nId; ///< Identifier creation counter
|
||||
uint32_t CHostPath::g_nId; ///< Identifier creation counter
|
||||
|
||||
CHostPath::~CHostPath()
|
||||
{
|
||||
|
@ -1144,7 +1144,7 @@ CHostPath::ring_t* CHostPath::Alloc(size_t nLength) // static
|
|||
{
|
||||
assert(nLength < FILEPATH_MAX);
|
||||
|
||||
size_t n = offsetof(ring_t, f) + CHostFilename::Offset() + (nLength + 1) * sizeof(TCHAR);
|
||||
const size_t n = offsetof(ring_t, f) + CHostFilename::Offset() + (nLength + 1) * sizeof(TCHAR);
|
||||
auto p = (ring_t*)malloc(n);
|
||||
assert(p);
|
||||
|
||||
|
@ -1295,8 +1295,8 @@ bool CHostPath::isSameHuman(const BYTE* szHuman) const
|
|||
assert(szHuman);
|
||||
|
||||
// Calulate number of chars
|
||||
size_t nLength = strlen((const char*)m_szHuman);
|
||||
size_t n = strlen((const char*)szHuman);
|
||||
const size_t nLength = strlen((const char*)m_szHuman);
|
||||
const size_t n = strlen((const char*)szHuman);
|
||||
|
||||
// Check number of chars
|
||||
if (nLength != n)
|
||||
|
@ -1331,7 +1331,7 @@ bool CHostPath::isSameChild(const BYTE* szHuman) const
|
|||
/// Make sure to lock from the top.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAttribute) const
|
||||
const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, uint32_t nHumanAttribute) const
|
||||
{
|
||||
assert(szHuman);
|
||||
|
||||
|
@ -1368,7 +1368,7 @@ const CHostFilename* CHostPath::FindFilename(const BYTE* szHuman, DWORD nHumanAt
|
|||
/// Make sure to lock from the top.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const
|
||||
const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, uint32_t nHumanAttribute, find_t* pFind) const
|
||||
{
|
||||
assert(szHuman);
|
||||
assert(pFind);
|
||||
|
@ -1386,7 +1386,7 @@ const CHostFilename* CHostPath::FindFilenameWildcard(const BYTE* szHuman, DWORD
|
|||
p = pFind->pos;
|
||||
} else {
|
||||
// Find the start position in the directory entry contents
|
||||
DWORD n = 0;
|
||||
uint32_t n = 0;
|
||||
for (;; p = (const ring_t*)p->r.Next()) {
|
||||
if (p == (const ring_t*)&m_cRing) {
|
||||
// Extrapolate from the count when the same entry isn't found (just in case)
|
||||
|
@ -1547,7 +1547,7 @@ void CHostPath::Refresh()
|
|||
// - No duplicated names in previous entries
|
||||
// - No entity with the same name exists
|
||||
if (pFilename->isReduce() || !pFilename->isCorrect()) { // Confirm that file name update is required
|
||||
for (DWORD n = 0; n < XM6_HOST_FILENAME_PATTERN_MAX; n++) {
|
||||
for (uint32_t n = 0; n < XM6_HOST_FILENAME_PATTERN_MAX; n++) {
|
||||
// Confirm file name validity
|
||||
if (pFilename->isCorrect()) {
|
||||
// Confirm match with previous entry
|
||||
|
@ -1583,14 +1583,14 @@ void CHostPath::Refresh()
|
|||
nHumanAttribute |= Human68k::AT_READONLY;
|
||||
pFilename->SetEntryAttribute(nHumanAttribute);
|
||||
|
||||
auto nHumanSize = (DWORD)sb.st_size;
|
||||
auto nHumanSize = (uint32_t)sb.st_size;
|
||||
pFilename->SetEntrySize(nHumanSize);
|
||||
|
||||
WORD nHumanDate = 0;
|
||||
WORD nHumanTime = 0;
|
||||
uint16_t nHumanDate = 0;
|
||||
uint16_t nHumanTime = 0;
|
||||
if (tm pt = {}; localtime_r(&sb.st_mtime, &pt) != nullptr) {
|
||||
nHumanDate = (WORD)(((pt.tm_year - 80) << 9) | ((pt.tm_mon + 1) << 5) | pt.tm_mday);
|
||||
nHumanTime = (WORD)((pt.tm_hour << 11) | (pt.tm_min << 5) | (pt.tm_sec >> 1));
|
||||
nHumanDate = (uint16_t)(((pt.tm_year - 80) << 9) | ((pt.tm_mon + 1) << 5) | pt.tm_mday);
|
||||
nHumanTime = (uint16_t)((pt.tm_hour << 11) | (pt.tm_min << 5) | (pt.tm_sec >> 1));
|
||||
}
|
||||
pFilename->SetEntryDate(nHumanDate);
|
||||
pFilename->SetEntryTime(nHumanTime);
|
||||
|
@ -1760,7 +1760,7 @@ void CHostEntry::CleanCache() const
|
|||
/// Update the cache for the specified unit
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostEntry::CleanCache(DWORD nUnit) const
|
||||
void CHostEntry::CleanCache(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1773,7 +1773,7 @@ void CHostEntry::CleanCache(DWORD nUnit) const
|
|||
/// Update the cache for the specified path
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) const
|
||||
void CHostEntry::CleanCache(uint32_t nUnit, const BYTE* szHumanPath) const
|
||||
{
|
||||
assert(szHumanPath);
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
|
@ -1787,7 +1787,7 @@ void CHostEntry::CleanCache(DWORD nUnit, const BYTE* szHumanPath) const
|
|||
/// Update all cache for the specified path and below
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) const
|
||||
void CHostEntry::CleanCacheChild(uint32_t nUnit, const BYTE* szHumanPath) const
|
||||
{
|
||||
assert(szHumanPath);
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
|
@ -1801,7 +1801,7 @@ void CHostEntry::CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) const
|
|||
/// Delete cache for the specified path
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) const
|
||||
void CHostEntry::DeleteCache(uint32_t nUnit, const BYTE* szHumanPath) const
|
||||
{
|
||||
assert(szHumanPath);
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
|
@ -1815,7 +1815,7 @@ void CHostEntry::DeleteCache(DWORD nUnit, const BYTE* szHumanPath) const
|
|||
/// Find host side names (path name + file name (can be abbreviated) + attribute)
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostEntry::Find(DWORD nUnit, CHostFiles* pFiles) const
|
||||
bool CHostEntry::Find(uint32_t nUnit, CHostFiles* pFiles) const
|
||||
{
|
||||
assert(pFiles);
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
|
@ -1829,7 +1829,7 @@ bool CHostEntry::Find(DWORD nUnit, CHostFiles* pFiles) const
|
|||
/// Drive settings
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv)
|
||||
void CHostEntry::SetDrv(uint32_t nUnit, CHostDrv* pDrv)
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit] == nullptr);
|
||||
|
@ -1842,7 +1842,7 @@ void CHostEntry::SetDrv(DWORD nUnit, CHostDrv* pDrv)
|
|||
/// Is it write-protected?
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostEntry::isWriteProtect(DWORD nUnit) const
|
||||
bool CHostEntry::isWriteProtect(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1855,7 +1855,7 @@ bool CHostEntry::isWriteProtect(DWORD nUnit) const
|
|||
/// Is it accessible?
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostEntry::isEnable(DWORD nUnit) const
|
||||
bool CHostEntry::isEnable(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1868,7 +1868,7 @@ bool CHostEntry::isEnable(DWORD nUnit) const
|
|||
/// Media check
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostEntry::isMediaOffline(DWORD nUnit) const
|
||||
bool CHostEntry::isMediaOffline(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1881,7 +1881,7 @@ bool CHostEntry::isMediaOffline(DWORD nUnit) const
|
|||
/// Get media byte
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
BYTE CHostEntry::GetMediaByte(DWORD nUnit) const
|
||||
BYTE CHostEntry::GetMediaByte(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1894,7 +1894,7 @@ BYTE CHostEntry::GetMediaByte(DWORD nUnit) const
|
|||
/// Get drive status
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CHostEntry::GetStatus(DWORD nUnit) const
|
||||
uint32_t CHostEntry::GetStatus(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1907,7 +1907,7 @@ DWORD CHostEntry::GetStatus(DWORD nUnit) const
|
|||
/// Media change check
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostEntry::CheckMedia(DWORD nUnit) const
|
||||
bool CHostEntry::CheckMedia(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1920,7 +1920,7 @@ bool CHostEntry::CheckMedia(DWORD nUnit) const
|
|||
/// Eject
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostEntry::Eject(DWORD nUnit) const
|
||||
void CHostEntry::Eject(uint32_t nUnit) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1933,7 +1933,7 @@ void CHostEntry::Eject(DWORD nUnit) const
|
|||
/// Get volume label
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) const
|
||||
void CHostEntry::GetVolume(uint32_t nUnit, TCHAR* szLabel) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1946,7 +1946,7 @@ void CHostEntry::GetVolume(DWORD nUnit, TCHAR* szLabel) const
|
|||
/// Get volume label from cache
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const
|
||||
bool CHostEntry::GetVolumeCache(uint32_t nUnit, TCHAR* szLabel) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1959,7 +1959,7 @@ bool CHostEntry::GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const
|
|||
/// Get capacity
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const
|
||||
uint32_t CHostEntry::GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -1972,7 +1972,7 @@ DWORD CHostEntry::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) cons
|
|||
/// Get cluster size from cache
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostEntry::GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const
|
||||
bool CHostEntry::GetCapacityCache(uint32_t nUnit, Human68k::capacity_t* pCapacity) const
|
||||
{
|
||||
assert(nUnit < DRIVE_MAX);
|
||||
assert(m_pDrv[nUnit]);
|
||||
|
@ -2056,7 +2056,7 @@ void CHostFiles::SetPath(const Human68k::namests_t* pNamests)
|
|||
/// Find file on the Human68k side and create data on the host side
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostFiles::Find(DWORD nUnit, const CHostEntry* pEntry)
|
||||
bool CHostFiles::Find(uint32_t nUnit, const CHostEntry* pEntry)
|
||||
{
|
||||
assert(pEntry);
|
||||
|
||||
|
@ -2159,7 +2159,7 @@ void CHostFilesManager::Init()
|
|||
assert(m_cRing.Prev() == &m_cRing);
|
||||
|
||||
// Allocate memory
|
||||
for (DWORD i = 0; i < XM6_HOST_FILES_MAX; i++) {
|
||||
for (uint32_t i = 0; i < XM6_HOST_FILES_MAX; i++) {
|
||||
auto p = new ring_t();
|
||||
p->r.Insert(&m_cRing);
|
||||
}
|
||||
|
@ -2180,7 +2180,7 @@ void CHostFilesManager::Clean()
|
|||
}
|
||||
}
|
||||
|
||||
CHostFiles* CHostFilesManager::Alloc(DWORD nKey)
|
||||
CHostFiles* CHostFilesManager::Alloc(uint32_t nKey)
|
||||
{
|
||||
assert(nKey);
|
||||
|
||||
|
@ -2195,7 +2195,7 @@ CHostFiles* CHostFilesManager::Alloc(DWORD nKey)
|
|||
return &p->f;
|
||||
}
|
||||
|
||||
CHostFiles* CHostFilesManager::Search(DWORD nKey)
|
||||
CHostFiles* CHostFilesManager::Search(uint32_t nKey)
|
||||
{
|
||||
// assert(nKey); // The search key may become 0 due to DPB damage
|
||||
|
||||
|
@ -2235,7 +2235,7 @@ void CHostFilesManager::Free(CHostFiles* pFiles)
|
|||
/// Set file open mode
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostFcb::SetMode(DWORD nHumanMode)
|
||||
bool CHostFcb::SetMode(uint32_t nHumanMode)
|
||||
{
|
||||
switch (nHumanMode & Human68k::OP_MASK) {
|
||||
case Human68k::OP_READ:
|
||||
|
@ -2279,7 +2279,7 @@ void CHostFcb::SetHumanPath(const BYTE* szHumanPath)
|
|||
/// Return false if error is thrown.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostFcb::Create(DWORD, bool bForce)
|
||||
bool CHostFcb::Create(uint32_t, bool bForce)
|
||||
{
|
||||
assert((Human68k::AT_DIRECTORY | Human68k::AT_VOLUME) == 0);
|
||||
assert(strlen(m_szFilename) > 0);
|
||||
|
@ -2328,7 +2328,7 @@ bool CHostFcb::Open()
|
|||
/// Return -1 if error is thrown.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize)
|
||||
uint32_t CHostFcb::Read(BYTE* pBuffer, uint32_t nSize)
|
||||
{
|
||||
assert(pBuffer);
|
||||
assert(m_pFile);
|
||||
|
@ -2337,7 +2337,7 @@ DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize)
|
|||
if (ferror(m_pFile))
|
||||
nResult = (size_t)-1;
|
||||
|
||||
return (DWORD)nResult;
|
||||
return (uint32_t)nResult;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -2348,7 +2348,7 @@ DWORD CHostFcb::Read(BYTE* pBuffer, DWORD nSize)
|
|||
/// Return -1 if error is thrown.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize)
|
||||
uint32_t CHostFcb::Write(const BYTE* pBuffer, uint32_t nSize)
|
||||
{
|
||||
assert(pBuffer);
|
||||
assert(m_pFile);
|
||||
|
@ -2357,7 +2357,7 @@ DWORD CHostFcb::Write(const BYTE* pBuffer, DWORD nSize)
|
|||
if (ferror(m_pFile))
|
||||
nResult = (size_t)-1;
|
||||
|
||||
return (DWORD)nResult;
|
||||
return (uint32_t)nResult;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -2381,18 +2381,16 @@ bool CHostFcb::Truncate() const
|
|||
/// Return -1 if error is thrown.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek)
|
||||
uint32_t CHostFcb::Seek(uint32_t nOffset, Human68k::seek_t nHumanSeek)
|
||||
{
|
||||
assert(nHumanSeek == Human68k::SK_BEGIN ||
|
||||
nHumanSeek == Human68k::SK_CURRENT || nHumanSeek == Human68k::SK_END);
|
||||
assert(m_pFile);
|
||||
|
||||
int nSeek;
|
||||
switch (nHumanSeek) {
|
||||
case Human68k::SK_BEGIN:
|
||||
case Human68k::seek_t::SK_BEGIN:
|
||||
nSeek = SEEK_SET;
|
||||
break;
|
||||
case Human68k::SK_CURRENT:
|
||||
case Human68k::seek_t::SK_CURRENT:
|
||||
nSeek = SEEK_CUR;
|
||||
break;
|
||||
// case SK_END:
|
||||
|
@ -2401,9 +2399,9 @@ DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek)
|
|||
break;
|
||||
}
|
||||
if (fseek(m_pFile, nOffset, nSeek))
|
||||
return (DWORD)-1;
|
||||
return (uint32_t)-1;
|
||||
|
||||
return (DWORD)ftell(m_pFile);
|
||||
return (uint32_t)ftell(m_pFile);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -2413,7 +2411,7 @@ DWORD CHostFcb::Seek(DWORD nOffset, DWORD nHumanSeek)
|
|||
/// Return false if error is thrown.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CHostFcb::TimeStamp(DWORD nHumanTime) const
|
||||
bool CHostFcb::TimeStamp(uint32_t nHumanTime) const
|
||||
{
|
||||
assert(m_pFile || m_bFlag);
|
||||
|
||||
|
@ -2482,7 +2480,7 @@ void CHostFcbManager::Init()
|
|||
assert(m_cRing.Prev() == &m_cRing);
|
||||
|
||||
// Memory allocation
|
||||
for (DWORD i = 0; i < XM6_HOST_FCB_MAX; i++) {
|
||||
for (uint32_t i = 0; i < XM6_HOST_FCB_MAX; i++) {
|
||||
auto p = new ring_t;
|
||||
assert(p);
|
||||
p->r.Insert(&m_cRing);
|
||||
|
@ -2503,7 +2501,7 @@ void CHostFcbManager::Clean() const
|
|||
}
|
||||
}
|
||||
|
||||
CHostFcb* CHostFcbManager::Alloc(DWORD nKey)
|
||||
CHostFcb* CHostFcbManager::Alloc(uint32_t nKey)
|
||||
{
|
||||
assert(nKey);
|
||||
|
||||
|
@ -2525,7 +2523,7 @@ CHostFcb* CHostFcbManager::Alloc(DWORD nKey)
|
|||
return &p->f;
|
||||
}
|
||||
|
||||
CHostFcb* CHostFcbManager::Search(DWORD nKey)
|
||||
CHostFcb* CHostFcbManager::Search(uint32_t nKey)
|
||||
{
|
||||
assert(nKey);
|
||||
|
||||
|
@ -2562,7 +2560,7 @@ void CHostFcbManager::Free(CHostFcb* pFcb)
|
|||
//
|
||||
//===========================================================================
|
||||
|
||||
DWORD CFileSys::g_nOption; // File name conversion flag
|
||||
uint32_t CFileSys::g_nOption; // File name conversion flag
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
|
@ -2609,7 +2607,7 @@ void CFileSys::Init()
|
|||
m_cEntry.Init();
|
||||
|
||||
// Evaluate per-path setting validity
|
||||
DWORD nDrives = m_nDrives;
|
||||
uint32_t nDrives = m_nDrives;
|
||||
if (nDrives == 0) {
|
||||
// Use root directory instead of per-path settings
|
||||
strcpy(m_szBase[0], "/");
|
||||
|
@ -2618,8 +2616,8 @@ void CFileSys::Init()
|
|||
}
|
||||
|
||||
// Register file system
|
||||
DWORD nUnit = 0;
|
||||
for (DWORD n = 0; n < nDrives; n++) {
|
||||
uint32_t nUnit = 0;
|
||||
for (uint32_t n = 0; n < nDrives; n++) {
|
||||
// Don't register is base path do not exist
|
||||
if (m_szBase[n][0] == '\0')
|
||||
continue;
|
||||
|
@ -2644,7 +2642,7 @@ void CFileSys::Init()
|
|||
/// $40 - Device startup
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument)
|
||||
uint32_t CFileSys::InitDevice(const Human68k::argument_t* pArgument)
|
||||
{
|
||||
InitOption(pArgument);
|
||||
|
||||
|
@ -2659,7 +2657,7 @@ DWORD CFileSys::InitDevice(const Human68k::argument_t* pArgument)
|
|||
/// $41 - Directory check
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
||||
int CFileSys::CheckDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const
|
||||
{
|
||||
assert(pNamests);
|
||||
|
||||
|
@ -2686,7 +2684,7 @@ int CFileSys::CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
|||
/// $42 - Create directory
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
||||
int CFileSys::MakeDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const
|
||||
{
|
||||
assert(pNamests);
|
||||
|
||||
|
@ -2728,7 +2726,7 @@ int CFileSys::MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
|||
/// $43 - Delete directory
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
||||
int CFileSys::RemoveDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const
|
||||
{
|
||||
assert(pNamests);
|
||||
|
||||
|
@ -2778,7 +2776,7 @@ int CFileSys::RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
|||
/// $44 - Change file name
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const
|
||||
int CFileSys::Rename(uint32_t nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const
|
||||
{
|
||||
assert(pNamests);
|
||||
|
||||
|
@ -2836,7 +2834,7 @@ int CFileSys::Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Hum
|
|||
/// $45 - Delete file
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
||||
int CFileSys::Delete(uint32_t nUnit, const Human68k::namests_t* pNamests) const
|
||||
{
|
||||
assert(pNamests);
|
||||
|
||||
|
@ -2876,7 +2874,7 @@ int CFileSys::Delete(DWORD nUnit, const Human68k::namests_t* pNamests) const
|
|||
/// $46 - Get/set file attribute
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute) const
|
||||
int CFileSys::Attribute(uint32_t nUnit, const Human68k::namests_t* pNamests, uint32_t nHumanAttribute) const
|
||||
{
|
||||
assert(pNamests);
|
||||
|
||||
|
@ -2910,7 +2908,7 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD
|
|||
return FS_FATAL_WRITEPROTECT;
|
||||
|
||||
// Generate attribute
|
||||
if (DWORD nAttribute = (nHumanAttribute & Human68k::AT_READONLY) | (f.GetAttribute() & ~Human68k::AT_READONLY);
|
||||
if (uint32_t nAttribute = (nHumanAttribute & Human68k::AT_READONLY) | (f.GetAttribute() & ~Human68k::AT_READONLY);
|
||||
f.GetAttribute() != nAttribute) {
|
||||
struct stat sb;
|
||||
if (stat(S2U(f.GetPath()), &sb))
|
||||
|
@ -2941,7 +2939,7 @@ int CFileSys::Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD
|
|||
/// $47 - File search
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles)
|
||||
int CFileSys::Files(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles)
|
||||
{
|
||||
assert(pNamests);
|
||||
assert(nKey);
|
||||
|
@ -3027,7 +3025,7 @@ int CFileSys::Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests
|
|||
/// $48 - Search next file
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles)
|
||||
int CFileSys::NFiles(uint32_t nUnit, uint32_t nKey, Human68k::files_t* pFiles)
|
||||
{
|
||||
assert(nKey);
|
||||
assert(pFiles);
|
||||
|
@ -3071,7 +3069,7 @@ int CFileSys::NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles)
|
|||
/// $49 - Create new file
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, bool bForce)
|
||||
int CFileSys::Create(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, uint32_t nHumanAttribute, bool bForce)
|
||||
{
|
||||
assert(pNamests);
|
||||
assert(nKey);
|
||||
|
@ -3116,7 +3114,7 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest
|
|||
pHostFcb->SetHumanPath(f.GetHumanPath());
|
||||
|
||||
// Set open mode
|
||||
pFcb->mode = (WORD)((pFcb->mode & ~Human68k::OP_MASK) | Human68k::OP_FULL);
|
||||
pFcb->mode = (uint16_t)((pFcb->mode & ~Human68k::OP_MASK) | Human68k::OP_FULL);
|
||||
if (!pHostFcb->SetMode(pFcb->mode)) {
|
||||
m_cFcb.Free(pHostFcb);
|
||||
return FS_ILLEGALMOD;
|
||||
|
@ -3139,7 +3137,7 @@ int CFileSys::Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamest
|
|||
/// $4A - File open
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb)
|
||||
int CFileSys::Open(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb)
|
||||
{
|
||||
assert(pNamests);
|
||||
assert(nKey);
|
||||
|
@ -3210,7 +3208,7 @@ int CFileSys::Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests,
|
|||
/// $4B - File close
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Close(DWORD nUnit, DWORD nKey, const Human68k::fcb_t* /* pFcb */)
|
||||
int CFileSys::Close(uint32_t nUnit, uint32_t nKey, const Human68k::fcb_t* /* pFcb */)
|
||||
{
|
||||
assert(nKey);
|
||||
|
||||
|
@ -3248,7 +3246,7 @@ int CFileSys::Close(DWORD nUnit, DWORD nKey, const Human68k::fcb_t* /* pFcb */)
|
|||
/// Clean exit when 0 bytes are read.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize)
|
||||
int CFileSys::Read(uint32_t nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, uint32_t nSize)
|
||||
{
|
||||
assert(nKey);
|
||||
assert(pFcb);
|
||||
|
@ -3267,9 +3265,8 @@ int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize
|
|||
}
|
||||
|
||||
// Read
|
||||
DWORD nResult;
|
||||
nResult = pHostFcb->Read(pBuffer, nSize);
|
||||
if (nResult == (DWORD)-1) {
|
||||
const uint32_t nResult = pHostFcb->Read(pBuffer, nSize);
|
||||
if (nResult == (uint32_t)-1) {
|
||||
m_cFcb.Free(pHostFcb);
|
||||
return FS_INVALIDFUNC; // TODO: Should return error code 10 (read error) as well here
|
||||
}
|
||||
|
@ -3288,7 +3285,7 @@ int CFileSys::Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pBuffer, DWORD nSize
|
|||
/// Truncate file if 0 bytes are written.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWORD nSize)
|
||||
int CFileSys::Write(uint32_t nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, uint32_t nSize)
|
||||
{
|
||||
assert(nKey);
|
||||
assert(pFcb);
|
||||
|
@ -3300,7 +3297,7 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR
|
|||
if (pHostFcb == nullptr)
|
||||
return FS_NOTOPENED;
|
||||
|
||||
DWORD nResult;
|
||||
uint32_t nResult;
|
||||
if (nSize == 0) {
|
||||
// Truncate
|
||||
if (!pHostFcb->Truncate()) {
|
||||
|
@ -3321,7 +3318,7 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR
|
|||
|
||||
// Write
|
||||
nResult = pHostFcb->Write(pBuffer, nSize);
|
||||
if (nResult == (DWORD)-1) {
|
||||
if (nResult == (uint32_t)-1) {
|
||||
m_cFcb.Free(pHostFcb);
|
||||
return FS_CANTWRITE; /// TODO: Should return error code 11 (write error) as well here
|
||||
}
|
||||
|
@ -3346,7 +3343,7 @@ int CFileSys::Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pBuffer, DWOR
|
|||
/// $4E - File seek
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset)
|
||||
int CFileSys::Seek(uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nSeek, int nOffset)
|
||||
{
|
||||
assert(pFcb);
|
||||
|
||||
|
@ -3356,14 +3353,14 @@ int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset)
|
|||
return FS_NOTOPENED;
|
||||
|
||||
// Parameter check
|
||||
if (nSeek > Human68k::SK_END) {
|
||||
if (nSeek > (uint32_t)Human68k::seek_t::SK_END) {
|
||||
m_cFcb.Free(pHostFcb);
|
||||
return FS_INVALIDPRM;
|
||||
}
|
||||
|
||||
// File seek
|
||||
DWORD nResult = pHostFcb->Seek(nOffset, nSeek);
|
||||
if (nResult == (DWORD)-1) {
|
||||
uint32_t nResult = pHostFcb->Seek(nOffset, (Human68k::seek_t)nSeek);
|
||||
if (nResult == (uint32_t)-1) {
|
||||
m_cFcb.Free(pHostFcb);
|
||||
return FS_CANTSEEK;
|
||||
}
|
||||
|
@ -3381,14 +3378,14 @@ int CFileSys::Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset)
|
|||
/// Throw error when the top 16 bits are $FFFF.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime)
|
||||
uint32_t CFileSys::TimeStamp(uint32_t nUnit, uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nHumanTime)
|
||||
{
|
||||
assert(nKey);
|
||||
assert(pFcb);
|
||||
|
||||
// Get only
|
||||
if (nHumanTime == 0)
|
||||
return ((DWORD)pFcb->date << 16) | pFcb->time;
|
||||
return ((uint32_t)pFcb->date << 16) | pFcb->time;
|
||||
|
||||
// Unit check
|
||||
if (nUnit >= CHostEntry::DRIVE_MAX)
|
||||
|
@ -3415,8 +3412,8 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD
|
|||
m_cFcb.Free(pHostFcb);
|
||||
return FS_INVALIDPRM;
|
||||
}
|
||||
pFcb->date = (WORD)(nHumanTime >> 16);
|
||||
pFcb->time = (WORD)nHumanTime;
|
||||
pFcb->date = (uint16_t)(nHumanTime >> 16);
|
||||
pFcb->time = (uint16_t)nHumanTime;
|
||||
|
||||
// Update cache
|
||||
m_cEntry.CleanCache(nUnit, pHostFcb->GetHumanPath());
|
||||
|
@ -3429,7 +3426,7 @@ DWORD CFileSys::TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD
|
|||
/// $50 - Get capacity
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const
|
||||
int CFileSys::GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const
|
||||
{
|
||||
assert(pCapacity);
|
||||
|
||||
|
@ -3453,7 +3450,7 @@ int CFileSys::GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const
|
|||
/// $51 - Inspect/control drive status
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) const
|
||||
int CFileSys::CtrlDrive(uint32_t nUnit, Human68k::ctrldrive_t* pCtrlDrive) const
|
||||
{
|
||||
assert(pCtrlDrive);
|
||||
|
||||
|
@ -3496,7 +3493,7 @@ int CFileSys::CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) const
|
|||
/// Therefore, treat even a unit out of bounds as normal operation.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const
|
||||
int CFileSys::GetDPB(uint32_t nUnit, Human68k::dpb_t* pDpb) const
|
||||
{
|
||||
assert(pDpb);
|
||||
|
||||
|
@ -3530,8 +3527,8 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const
|
|||
}
|
||||
|
||||
// Calculate number of shifts
|
||||
DWORD nSize = 1;
|
||||
DWORD nShift = 0;
|
||||
uint32_t nSize = 1;
|
||||
uint32_t nShift = 0;
|
||||
for (;;) {
|
||||
if (nSize >= cap.sectors)
|
||||
break;
|
||||
|
@ -3546,23 +3543,23 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const
|
|||
// Cluster 1: FAT
|
||||
// Cluster 2: Root directory
|
||||
// Cluster 3: Data memory (pseudo-sector)
|
||||
DWORD nFat = cap.sectors;
|
||||
DWORD nRoot = cap.sectors * 2;
|
||||
DWORD nData = cap.sectors * 3;
|
||||
const uint32_t nFat = cap.sectors;
|
||||
const uint32_t nRoot = cap.sectors * 2;
|
||||
const uint32_t nData = cap.sectors * 3;
|
||||
|
||||
// Set DPB
|
||||
pDpb->sector_size = cap.bytes; // Bytes per sector
|
||||
pDpb->cluster_size =
|
||||
(BYTE)(cap.sectors - 1); // Sectors per cluster - 1
|
||||
pDpb->shift = (BYTE)nShift; // Number of cluster → sector shifts
|
||||
pDpb->fat_sector = (WORD)nFat; // First FAT sector number
|
||||
pDpb->fat_sector = (uint16_t)nFat; // First FAT sector number
|
||||
pDpb->fat_max = 1; // Number of FAT memory spaces
|
||||
pDpb->fat_size = (BYTE)cap.sectors; // Number of sectors controlled by FAT (excluding copies)
|
||||
pDpb->file_max =
|
||||
(WORD)(cap.sectors * cap.bytes / 0x20); // Number of files in the root directory
|
||||
pDpb->data_sector = (WORD)nData; // First sector number of data memory
|
||||
(uint16_t)(cap.sectors * cap.bytes / 0x20); // Number of files in the root directory
|
||||
pDpb->data_sector = (uint16_t)nData; // First sector number of data memory
|
||||
pDpb->cluster_max = cap.clusters; // Total number of clusters + 1
|
||||
pDpb->root_sector = (WORD)nRoot; // First sector number of the root directory
|
||||
pDpb->root_sector = (uint16_t)nRoot; // First sector number of the root directory
|
||||
pDpb->media = media; // Media byte
|
||||
|
||||
return 0;
|
||||
|
@ -3576,7 +3573,7 @@ int CFileSys::GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const
|
|||
/// Buffer size is hard coded to $200 byte.
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
|
||||
int CFileSys::DiskRead(uint32_t nUnit, BYTE* pBuffer, uint32_t nSector, uint32_t nSize)
|
||||
{
|
||||
assert(pBuffer);
|
||||
|
||||
|
@ -3613,7 +3610,7 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
|
|||
// Note that in lzdsys the sector number to read is calculated by the following formula:
|
||||
// (dirent.cluster - 2) * (dpb.cluster_size + 1) + dpb.data_sector
|
||||
/// @warning little endian only
|
||||
dir->cluster = (WORD)(m_nHostSectorCount + 2); // Pseudo-sector number
|
||||
dir->cluster = (uint16_t)(m_nHostSectorCount + 2); // Pseudo-sector number
|
||||
m_nHostSectorBuffer[m_nHostSectorCount] = nSector; // Entity that points to the pseudo-sector
|
||||
m_nHostSectorCount++;
|
||||
m_nHostSectorCount %= XM6_HOST_PSEUDO_CLUSTER_MAX;
|
||||
|
@ -3622,8 +3619,8 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
|
|||
}
|
||||
|
||||
// Calculate the sector number from the cluster number
|
||||
DWORD n = nSector - (3 * cap.sectors);
|
||||
DWORD nMod = 1;
|
||||
uint32_t n = nSector - (3 * cap.sectors);
|
||||
uint32_t nMod = 1;
|
||||
if (cap.sectors) {
|
||||
// Beware that cap.sectors becomes 0 when media does not exist
|
||||
nMod = n % cap.sectors;
|
||||
|
@ -3641,9 +3638,9 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
|
|||
if (!f.Open())
|
||||
return FS_INVALIDPRM;
|
||||
memset(pBuffer, 0, 0x200);
|
||||
DWORD nResult = f.Read(pBuffer, 0x200);
|
||||
uint32_t nResult = f.Read(pBuffer, 0x200);
|
||||
f.Close();
|
||||
if (nResult == (DWORD)-1)
|
||||
if (nResult == (uint32_t)-1)
|
||||
return FS_INVALIDPRM;
|
||||
|
||||
return 0;
|
||||
|
@ -3658,7 +3655,7 @@ int CFileSys::DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize)
|
|||
/// $54 - Write sector
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::DiskWrite(DWORD nUnit) const
|
||||
int CFileSys::DiskWrite(uint32_t nUnit) const
|
||||
{
|
||||
// Unit check
|
||||
if (nUnit >= CHostEntry::DRIVE_MAX)
|
||||
|
@ -3684,7 +3681,7 @@ int CFileSys::DiskWrite(DWORD nUnit) const
|
|||
/// $55 - IOCTRL
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
|
||||
int CFileSys::Ioctrl(uint32_t nUnit, uint32_t nFunction, Human68k::ioctrl_t* pIoctrl)
|
||||
{
|
||||
assert(pIoctrl);
|
||||
|
||||
|
@ -3707,7 +3704,7 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
|
|||
|
||||
case 2:
|
||||
switch (pIoctrl->param) {
|
||||
case (DWORD)-1:
|
||||
case (uint32_t)-1:
|
||||
// Re-identify media
|
||||
m_cEntry.isMediaOffline(nUnit);
|
||||
return 0;
|
||||
|
@ -3722,17 +3719,17 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
|
|||
}
|
||||
break;
|
||||
|
||||
case (DWORD)-1:
|
||||
case (uint32_t)-1:
|
||||
// Resident evaluation
|
||||
memcpy(pIoctrl->buffer, "WindrvXM", 8);
|
||||
return 0;
|
||||
|
||||
case (DWORD)-2:
|
||||
case (uint32_t)-2:
|
||||
// Set options
|
||||
SetOption(pIoctrl->param);
|
||||
return 0;
|
||||
|
||||
case (DWORD)-3:
|
||||
case (uint32_t)-3:
|
||||
// Get options
|
||||
pIoctrl->param = GetOption();
|
||||
return 0;
|
||||
|
@ -3749,7 +3746,7 @@ int CFileSys::Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl)
|
|||
/// $56 - Flush
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Flush(DWORD nUnit) const
|
||||
int CFileSys::Flush(uint32_t nUnit) const
|
||||
{
|
||||
// Unit check
|
||||
if (nUnit >= CHostEntry::DRIVE_MAX)
|
||||
|
@ -3766,7 +3763,7 @@ int CFileSys::Flush(DWORD nUnit) const
|
|||
/// $57 - Media change check
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::CheckMedia(DWORD nUnit) const
|
||||
int CFileSys::CheckMedia(uint32_t nUnit) const
|
||||
{
|
||||
// Unit check
|
||||
if (nUnit >= CHostEntry::DRIVE_MAX)
|
||||
|
@ -3788,7 +3785,7 @@ int CFileSys::CheckMedia(DWORD nUnit) const
|
|||
/// $58 - Lock
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int CFileSys::Lock(DWORD nUnit) const
|
||||
int CFileSys::Lock(uint32_t nUnit) const
|
||||
{
|
||||
// Unit check
|
||||
if (nUnit >= CHostEntry::DRIVE_MAX)
|
||||
|
@ -3810,7 +3807,7 @@ int CFileSys::Lock(DWORD nUnit) const
|
|||
/// Set options
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void CFileSys::SetOption(DWORD nOption)
|
||||
void CFileSys::SetOption(uint32_t nOption)
|
||||
{
|
||||
// Clear cache when option settings change
|
||||
if (m_nOption ^ nOption)
|
||||
|
@ -3835,7 +3832,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
|
|||
const BYTE* pp = pArgument->buf;
|
||||
pp += strlen((const char*)pp) + 1;
|
||||
|
||||
DWORD nOption = m_nOptionDefault;
|
||||
uint32_t nOption = m_nOptionDefault;
|
||||
for (;;) {
|
||||
assert(pp < pArgument->buf + sizeof(*pArgument));
|
||||
const BYTE* p = pp;
|
||||
|
@ -3843,7 +3840,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
|
|||
if (c == '\0')
|
||||
break;
|
||||
|
||||
DWORD nMode;
|
||||
uint32_t nMode;
|
||||
if (c == '+') {
|
||||
nMode = 1;
|
||||
} else if (c == '-') {
|
||||
|
@ -3868,7 +3865,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
|
|||
if (c == '\0')
|
||||
break;
|
||||
|
||||
DWORD nBit = 0;
|
||||
uint32_t nBit = 0;
|
||||
switch (c) {
|
||||
case 'A': case 'a': nBit = WINDRV_OPT_CONVERT_LENGTH; break;
|
||||
case 'T': case 't': nBit = WINDRV_OPT_COMPARE_LENGTH; nMode ^= 1; break;
|
||||
|
@ -3911,7 +3908,7 @@ void CFileSys::InitOption(const Human68k::argument_t* pArgument)
|
|||
/// Get volume label
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool CFileSys::FilesVolume(DWORD nUnit, Human68k::files_t* pFiles) const
|
||||
bool CFileSys::FilesVolume(uint32_t nUnit, Human68k::files_t* pFiles) const
|
||||
{
|
||||
assert(pFiles);
|
||||
|
||||
|
|
|
@ -92,39 +92,14 @@ namespace Human68k {
|
|||
};
|
||||
|
||||
/// Seek types
|
||||
enum seek_t {
|
||||
enum class seek_t {
|
||||
SK_BEGIN = 0, ///< From the beginning of a file
|
||||
SK_CURRENT = 1, ///< From the current location
|
||||
SK_END = 2, ///< From the end of the file
|
||||
};
|
||||
|
||||
/// Media byte
|
||||
enum media_t {
|
||||
MEDIA_2DD_10 = 0xE0, ///< 2DD/10 sector
|
||||
MEDIA_1D_9 = 0xE5, ///< 1D/9 sector
|
||||
MEDIA_2D_9 = 0xE6, ///< 2D/9 sector
|
||||
MEDIA_1D_8 = 0xE7, ///< 1D/8 sector
|
||||
MEDIA_2D_8 = 0xE8, ///< 2D/8 sector
|
||||
MEDIA_2HT = 0xEA, ///< 2HT
|
||||
MEDIA_2HS = 0xEB, ///< 2HS
|
||||
MEDIA_2HDE = 0xEC, ///< 2DDE
|
||||
MEDIA_1DD_9 = 0xEE, ///< 1DD/9 sector
|
||||
MEDIA_1DD_8 = 0xEF, ///< 1DD/8 sector
|
||||
MEDIA_MANUAL = 0xF1, ///< Remote drive (manual eject)
|
||||
MEDIA_REMOVABLE = 0xF2, ///< Remote drive (removable)
|
||||
MEDIA_REMOTE = 0xF3, ///< Remote drive
|
||||
MEDIA_DAT = 0xF4, ///< SCSI-DAT
|
||||
MEDIA_CDROM = 0xF5, ///< SCSI-CDROM
|
||||
MEDIA_MO = 0xF6, ///< SCSI-MO
|
||||
MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD
|
||||
MEDIA_SASI_HD = 0xF8, ///< SASI-HD
|
||||
MEDIA_RAMDISK = 0xF9, ///< RAM disk
|
||||
MEDIA_2HQ = 0xFA, ///< 2HQ
|
||||
MEDIA_2DD_8 = 0xFB, ///< 2DD/8 sector
|
||||
MEDIA_2DD_9 = 0xFC, ///< 2DD/9 sector
|
||||
MEDIA_2HC = 0xFD, ///< 2HC
|
||||
MEDIA_2HD = 0xFE, ///< 2HD
|
||||
};
|
||||
// Media byte
|
||||
const static int MEDIA_REMOTE = 0xF3; ///< Remote drive
|
||||
|
||||
struct namests_t {
|
||||
BYTE wildcard; ///< Wildcard character length
|
||||
|
@ -141,41 +116,41 @@ namespace Human68k {
|
|||
struct files_t {
|
||||
BYTE fatr; ///< + 0 search attribute; read-only
|
||||
// BYTE drive; ///< + 1 drive number; read-only
|
||||
DWORD sector; ///< + 2 directory sector; DOS _FILES first address substitute
|
||||
// WORD cluster; ///< + 6 directory cluster; details unknown (unused)
|
||||
WORD offset; ///< + 8 directory entry; write-only
|
||||
uint32_t sector; ///< + 2 directory sector; DOS _FILES first address substitute
|
||||
// uint16_t cluster; ///< + 6 directory cluster; details unknown (unused)
|
||||
uint16_t offset; ///< + 8 directory entry; write-only
|
||||
// BYTE name[8]; ///< +10 working file name; write-only (unused)
|
||||
// BYTE ext[3]; ///< +18 working extension; write-only (unused)
|
||||
BYTE attr; ///< +21 file attribute; write-only
|
||||
WORD time; ///< +22 last change time of day; write-only
|
||||
WORD date; ///< +24 last change date; write-only
|
||||
DWORD size; ///< +26 file size; write-only
|
||||
uint16_t time; ///< +22 last change time of day; write-only
|
||||
uint16_t date; ///< +24 last change date; write-only
|
||||
uint32_t size; ///< +26 file size; write-only
|
||||
BYTE full[23]; ///< +30 full name; write-only
|
||||
};
|
||||
|
||||
struct fcb_t {
|
||||
// BYTE pad00[6]; ///< + 0~+ 5 (unused)
|
||||
DWORD fileptr; ///< + 6~+ 9 file pointer
|
||||
uint32_t fileptr; ///< + 6~+ 9 file pointer
|
||||
// BYTE pad01[4]; ///< +10~+13 (unused)
|
||||
WORD mode; ///< +14~+15 open mode
|
||||
uint16_t mode; ///< +14~+15 open mode
|
||||
// BYTE pad02[16]; ///< +16~+31 (unused)
|
||||
// DWORD zero; ///< +32~+35 zeros are written when opened (unused)
|
||||
// uint32_t zero; ///< +32~+35 zeros are written when opened (unused)
|
||||
// BYTE name[8]; ///< +36~+43 file name (PADDING 0x20) (unused)
|
||||
// BYTE ext[3]; ///< +44~+46 extension (PADDING 0x20) (unused)
|
||||
BYTE attr; ///< +47 file attribute
|
||||
// BYTE add[10]; ///< +48~+57 file name addition (PADDING 0x00) (unused)
|
||||
WORD time; ///< +58~+59 last change time of day
|
||||
WORD date; ///< +60~+61 last change date
|
||||
// WORD cluster; ///< +62~+63 cluster number (unused)
|
||||
DWORD size; ///< +64~+67 file size
|
||||
uint16_t time; ///< +58~+59 last change time of day
|
||||
uint16_t date; ///< +60~+61 last change date
|
||||
// uint16_t cluster; ///< +62~+63 cluster number (unused)
|
||||
uint32_t size; ///< +64~+67 file size
|
||||
// BYTE pad03[28]; ///< +68~+95 FAT cache (unused)
|
||||
};
|
||||
|
||||
struct capacity_t {
|
||||
WORD freearea; ///< + 0 Number of available clusters
|
||||
WORD clusters; ///< + 2 Total number of clusters
|
||||
WORD sectors; ///< + 4 Number of sectors per cluster
|
||||
WORD bytes; ///< + 6 Number of bytes per sector
|
||||
uint16_t freearea; ///< + 0 Number of available clusters
|
||||
uint16_t clusters; ///< + 2 Total number of clusters
|
||||
uint16_t sectors; ///< + 4 Number of sectors per cluster
|
||||
uint16_t bytes; ///< + 6 Number of bytes per sector
|
||||
};
|
||||
|
||||
struct ctrldrive_t {
|
||||
|
@ -184,17 +159,17 @@ namespace Human68k {
|
|||
};
|
||||
|
||||
struct dpb_t {
|
||||
WORD sector_size; ///< + 0 Number of bytes in one sector
|
||||
uint16_t sector_size; ///< + 0 Number of bytes in one sector
|
||||
BYTE cluster_size; ///< + 2 Number sectors in one cluster -1
|
||||
BYTE shift; ///< + 3 Number of cluster→sector shifts
|
||||
WORD fat_sector; ///< + 4 FAT first sector number
|
||||
uint16_t fat_sector; ///< + 4 FAT first sector number
|
||||
BYTE fat_max; ///< + 6 FAT storage quantity
|
||||
BYTE fat_size; ///< + 7 FAT controlled sector number (excluding duplicates)
|
||||
WORD file_max; ///< + 8 Number of files in the root directory
|
||||
WORD data_sector; ///< +10 First sector number of data storage
|
||||
WORD cluster_max; ///< +12 Total number of clusters +1
|
||||
WORD root_sector; ///< +14 First sector number of root directory
|
||||
// DWORD driverentry; ///< +16 Device driver pointer (unused)
|
||||
uint16_t file_max; ///< + 8 Number of files in the root directory
|
||||
uint16_t data_sector; ///< +10 First sector number of data storage
|
||||
uint16_t cluster_max; ///< +12 Total number of clusters +1
|
||||
uint16_t root_sector; ///< +14 First sector number of root directory
|
||||
// uint32_t driverentry; ///< +16 Device driver pointer (unused)
|
||||
BYTE media; ///< +20 Media identifier
|
||||
// BYTE flag; ///< +21 Flag used by DPB (unused)
|
||||
};
|
||||
|
@ -205,22 +180,22 @@ namespace Human68k {
|
|||
BYTE ext[3]; ///< + 8 Extension (PADDING 0x20)
|
||||
BYTE attr; ///< +11 File attribute
|
||||
BYTE add[10]; ///< +12 File name addition (PADDING 0x00)
|
||||
WORD time; ///< +22 Last change time of day
|
||||
WORD date; ///< +24 Last change date
|
||||
WORD cluster; ///< +26 Cluster number
|
||||
DWORD size; ///< +28 File size
|
||||
uint16_t time; ///< +22 Last change time of day
|
||||
uint16_t date; ///< +24 Last change date
|
||||
uint16_t cluster; ///< +26 Cluster number
|
||||
uint32_t size; ///< +28 File size
|
||||
};
|
||||
|
||||
/// IOCTRL parameter union
|
||||
union ioctrl_t {
|
||||
BYTE buffer[8]; ///< Access in byte units
|
||||
DWORD param; ///< Parameter (First 4 bytes)
|
||||
WORD media; ///< Media byte (First 2 bytes)
|
||||
uint32_t param; ///< Parameter (First 4 bytes)
|
||||
uint16_t media; ///< Media byte (First 2 bytes)
|
||||
};
|
||||
|
||||
/// Command line parameter struct
|
||||
/**
|
||||
The driver itself is included in the beginning of the argument,
|
||||
The driver itself is included in the beginning of the argument,
|
||||
so setting to a length longer than HUMAN68K_PATH_MAX
|
||||
*/
|
||||
struct argument_t {
|
||||
|
@ -257,9 +232,9 @@ static const int XM6_HOST_PSEUDO_CLUSTER_MAX = 10;
|
|||
|
||||
/// Number of caches for directory entries
|
||||
/**
|
||||
Human68k carries out a large number of checks of directory entries when doing an operation
|
||||
inside a subdirectory. This specifies the number of caches used to speed up this operation.
|
||||
Cache is allocated per drive. The more you add the faster it gets, but use too many
|
||||
Human68k carries out a large number of checks of directory entries when doing an operation
|
||||
inside a subdirectory. This specifies the number of caches used to speed up this operation.
|
||||
Cache is allocated per drive. The more you add the faster it gets, but use too many
|
||||
and the host OS gets under a heavy load, so be careful.
|
||||
|
||||
Default is 16.
|
||||
|
@ -268,10 +243,10 @@ static const int XM6_HOST_DIRENTRY_CACHE_MAX = 16;
|
|||
|
||||
/// Max number of entries that can be stored per directory
|
||||
/**
|
||||
When a large number of files are stored in a directory, a larger amount of data than
|
||||
contemporanous applications can handle will be returned. This may lead to errors such as
|
||||
When a large number of files are stored in a directory, a larger amount of data than
|
||||
contemporanous applications can handle will be returned. This may lead to errors such as
|
||||
partial data being recognized, performance dropping significantly, or OOM crashes.
|
||||
To guard against this, an upper limit is defined here. In the case of a particular
|
||||
To guard against this, an upper limit is defined here. In the case of a particular
|
||||
file manager, the upper limit is 2560 files. This is one good example to use as reference.
|
||||
|
||||
Default is around 60000 entries. (Upper limit of the FAT root directory)
|
||||
|
@ -280,17 +255,17 @@ static const int XM6_HOST_DIRENTRY_FILE_MAX = 65535;
|
|||
|
||||
/// Max number of patterns for file name deduplication
|
||||
/**
|
||||
The file names on the Human68k side are automatically created based on the file system on
|
||||
the host side. However, Human68k have stricter file name length restrictions than the host has.
|
||||
Because of this, there is a risk that file name duplication will occur. When this happens,
|
||||
WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve
|
||||
the duplication. Theoretically, there are over 60 million (36^5) unique file names that
|
||||
can be generated by this method. However, in reality any more than a few hundred
|
||||
deduplications will take excessive processing time. So here an upper limit to deduplication
|
||||
is set in order to maintain system performance. If a system is operated with common sense,
|
||||
you should only need a few dozen deduplication patterns, so this value can be kept low
|
||||
to further improve performance. In the case deduplication is not carried out, multiple files
|
||||
with the same name will be created. When trying to access such files,
|
||||
The file names on the Human68k side are automatically created based on the file system on
|
||||
the host side. However, Human68k have stricter file name length restrictions than the host has.
|
||||
Because of this, there is a risk that file name duplication will occur. When this happens,
|
||||
WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve
|
||||
the duplication. Theoretically, there are over 60 million (36^5) unique file names that
|
||||
can be generated by this method. However, in reality any more than a few hundred
|
||||
deduplications will take excessive processing time. So here an upper limit to deduplication
|
||||
is set in order to maintain system performance. If a system is operated with common sense,
|
||||
you should only need a few dozen deduplication patterns, so this value can be kept low
|
||||
to further improve performance. In the case deduplication is not carried out, multiple files
|
||||
with the same name will be created. When trying to access such files,
|
||||
only the first entry will ever be accessed.
|
||||
|
||||
Default is 36 patterns.
|
||||
|
@ -338,11 +313,7 @@ Normal is 0. Becomes 1 if attempting to mount in read-only mode.
|
|||
Reserving the other values for future use.
|
||||
Insurance against hard-to-detect devices such as homemade USB storage.
|
||||
*/
|
||||
enum {
|
||||
FSFLAG_WRITE_PROTECT = 0x00000001, ///< Bit0: Force write protect
|
||||
FSFLAG_REMOVABLE = 0x00000002, ///< Bit1: Force removable media
|
||||
FSFLAG_MANUAL = 0x00000004, ///< Bit2: Force manual eject
|
||||
};
|
||||
static const uint32_t FSFLAG_WRITE_PROTECT = 0x00000001; ///< Bit0: Force write protect
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
@ -357,8 +328,8 @@ class CRing {
|
|||
public:
|
||||
CRing() { Init(); }
|
||||
~CRing() { Remove(); }
|
||||
CRing(CRing&) = delete;
|
||||
CRing& operator=(const CRing&) = delete;
|
||||
CRing(CRing&) = default;
|
||||
CRing& operator=(const CRing&) = default;
|
||||
|
||||
void Init() { next = prev = this; }
|
||||
|
||||
|
@ -442,8 +413,6 @@ class CHostFilename {
|
|||
public:
|
||||
CHostFilename() = default;
|
||||
~CHostFilename() = default;
|
||||
CHostFilename(CHostFilename&) = delete;
|
||||
CHostFilename& operator=(const CHostFilename&) = delete;
|
||||
|
||||
static size_t Offset() { return offsetof(CHostFilename, m_szHost); } ///< Get offset location
|
||||
|
||||
|
@ -460,17 +429,17 @@ public:
|
|||
void SetEntryName(); ///< Set Human68k directory entry
|
||||
void SetEntryAttribute(BYTE nHumanAttribute)
|
||||
{ m_dirHuman.attr = nHumanAttribute; } ///< Set Human68k directory entry
|
||||
void SetEntrySize(DWORD nHumanSize)
|
||||
void SetEntrySize(uint32_t nHumanSize)
|
||||
{ m_dirHuman.size = nHumanSize; } ///< Set Human68k directory entry
|
||||
void SetEntryDate(WORD nHumanDate)
|
||||
void SetEntryDate(uint16_t nHumanDate)
|
||||
{ m_dirHuman.date = nHumanDate; } ///< Set Human68k directory entry
|
||||
void SetEntryTime(WORD nHumanTime)
|
||||
void SetEntryTime(uint16_t nHumanTime)
|
||||
{ m_dirHuman.time = nHumanTime; } ///< Set Human68k directory entry
|
||||
void SetEntryCluster(WORD nHumanCluster)
|
||||
void SetEntryCluster(uint16_t nHumanCluster)
|
||||
{ m_dirHuman.cluster = nHumanCluster; } ///< Set Human68k directory entry
|
||||
const Human68k::dirent_t* GetEntry() const
|
||||
const Human68k::dirent_t* GetEntry() const
|
||||
{ return &m_dirHuman; } ///< Get Human68k directory entry
|
||||
int CheckAttribute(DWORD nHumanAttribute) const; ///< Determine Human68k directory entry attributes
|
||||
int CheckAttribute(uint32_t nHumanAttribute) const; ///< Determine Human68k directory entry attributes
|
||||
bool isSameEntry(const Human68k::dirent_t* pdirHuman) const
|
||||
{ assert(pdirHuman); return memcmp(&m_dirHuman, pdirHuman, sizeof(m_dirHuman)) == 0; }
|
||||
///< Determine Human68k directory entry match
|
||||
|
@ -505,7 +474,7 @@ get updated for new directories created as a result of file operations, which
|
|||
triggers updates to directory entires.
|
||||
However, on the host file system, new directories do typically get an updated time stamp.
|
||||
|
||||
The unfortunate outcome is that when copying a directory for instance, the time stamp
|
||||
The unfortunate outcome is that when copying a directory for instance, the time stamp
|
||||
will get overwritten even if the application did not intend for the time stamp to get updated.
|
||||
|
||||
Here follows an implementation of a directory cache FAT time stamp emulation feature.
|
||||
|
@ -522,8 +491,8 @@ class CHostPath: public CRing {
|
|||
public:
|
||||
/// Search buffer
|
||||
struct find_t {
|
||||
DWORD count; ///< Search execution count + 1 (When 0 the below value is invalid)
|
||||
DWORD id; ///< Entry unique ID for the path of the next search
|
||||
uint32_t count; ///< Search execution count + 1 (When 0 the below value is invalid)
|
||||
uint32_t id; ///< Entry unique ID for the path of the next search
|
||||
const ring_t* pos; ///< Position of the next search (When identical to unique ID)
|
||||
Human68k::dirent_t entry; ///< Contents of the next seach entry
|
||||
|
||||
|
@ -532,8 +501,8 @@ public:
|
|||
|
||||
CHostPath() = default;
|
||||
~CHostPath();
|
||||
CHostPath(CHostPath&) = delete;
|
||||
CHostPath& operator=(const CHostPath&) = delete;
|
||||
CHostPath(CHostPath&) = default;
|
||||
CHostPath& operator=(const CHostPath&) = default;
|
||||
|
||||
void Clean(); ///< Initialialize for reuse
|
||||
|
||||
|
@ -542,9 +511,9 @@ public:
|
|||
bool isSameHuman(const BYTE* szHuman) const; ///< Compare the name on the Human68k side
|
||||
bool isSameChild(const BYTE* szHuman) const; ///< Compare the name on the Human68k side
|
||||
const TCHAR* GetHost() const { return m_szHost; } ///< Obtain the name on the host side
|
||||
const CHostFilename* FindFilename(const BYTE* szHuman, DWORD nHumanAttribute = Human68k::AT_ALL) const;
|
||||
const CHostFilename* FindFilename(const BYTE* szHuman, uint32_t nHumanAttribute = Human68k::AT_ALL) const;
|
||||
///< Find file name
|
||||
const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, DWORD nHumanAttribute, find_t* pFind) const;
|
||||
const CHostFilename* FindFilenameWildcard(const BYTE* szHuman, uint32_t nHumanAttribute, find_t* pFind) const;
|
||||
///< Find file name (with support for wildcards)
|
||||
bool isRefresh() const; ///< Check that the file change has been done
|
||||
void Refresh(); ///< Refresh file
|
||||
|
@ -564,20 +533,20 @@ private:
|
|||
CRing m_cRing; ///< For CHostFilename linking
|
||||
time_t m_tBackup = 0; ///< For time stamp restoration
|
||||
bool m_bRefresh = true; ///< Refresh flag
|
||||
DWORD m_nId = 0; ///< Unique ID (When the value has changed, it means an update has been made)
|
||||
uint32_t m_nId = 0; ///< Unique ID (When the value has changed, it means an update has been made)
|
||||
BYTE m_szHuman[HUMAN68K_PATH_MAX]; ///< The internal Human68k name for the relevant entry
|
||||
TCHAR m_szHost[FILEPATH_MAX]; ///< The host side name for the relevant entry
|
||||
|
||||
static DWORD g_nId; ///< Counter for the unique ID generation
|
||||
static uint32_t g_nId; ///< Counter for the unique ID generation
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
/// File search processing
|
||||
///
|
||||
/// It's pretty much impossible to process Human68k file names as Unicode internally.
|
||||
/// It's pretty much impossible to process Human68k file names as Unicode internally.
|
||||
/// So, we carry out binary conversion for processing. We leave it up to the
|
||||
/// directory entry cache to handle the conversion, which allows WINDRV to read
|
||||
/// directory entry cache to handle the conversion, which allows WINDRV to read
|
||||
/// everything as Shift-JIS. Additionally, it allows Human68k names to be
|
||||
/// fully independent of base path assignments.
|
||||
///
|
||||
|
@ -596,20 +565,18 @@ class CHostFiles {
|
|||
public:
|
||||
CHostFiles() = default;
|
||||
~CHostFiles() = default;
|
||||
CHostFiles(CHostFiles&) = delete;
|
||||
CHostFiles& operator=(const CHostFiles&) = delete;
|
||||
|
||||
void Init();
|
||||
|
||||
void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key
|
||||
bool isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key
|
||||
void SetKey(uint32_t nKey) { m_nKey = nKey; } ///< Set search key
|
||||
bool isSameKey(uint32_t nKey) const { return m_nKey == nKey; } ///< Compare search key
|
||||
void SetPath(const Human68k::namests_t* pNamests); ///< Create path and file name internally
|
||||
bool isRootPath() const { return m_szHumanPath[1] == '\0'; } ///< Check if root directory
|
||||
void SetPathWildcard() { m_nHumanWildcard = 1; } ///< Enable file search using wildcards
|
||||
void SetPathOnly() { m_nHumanWildcard = 0xFF; } ///< Enable only path names
|
||||
bool isPathOnly() const { return m_nHumanWildcard == 0xFF; } ///< Check if set to only path names
|
||||
void SetAttribute(DWORD nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } ///< Set search attribute
|
||||
bool Find(DWORD nUnit, const class CHostEntry* pEntry); ///< Find files on the Human68k side, generating data on the host side
|
||||
void SetAttribute(uint32_t nHumanAttribute) { m_nHumanAttribute = nHumanAttribute; } ///< Set search attribute
|
||||
bool Find(uint32_t nUnit, const class CHostEntry* pEntry); ///< Find files on the Human68k side, generating data on the host side
|
||||
const CHostFilename* Find(const CHostPath* pPath); ///< Find file name
|
||||
void SetEntry(const CHostFilename* pFilename); ///< Store search results on the Human68k side
|
||||
void SetResult(const TCHAR* szPath); ///< Set names on the host side
|
||||
|
@ -620,18 +587,18 @@ public:
|
|||
|
||||
const Human68k::dirent_t* GetEntry() const { return &m_dirHuman; }///< Get Human68k directory entry
|
||||
|
||||
DWORD GetAttribute() const { return m_dirHuman.attr; } ///< Get Human68k attribute
|
||||
WORD GetDate() const { return m_dirHuman.date; } ///< Get Human68k date
|
||||
WORD GetTime() const { return m_dirHuman.time; } ///< Get Human68k time
|
||||
DWORD GetSize() const { return m_dirHuman.size; } ///< Get Human68k file size
|
||||
uint32_t GetAttribute() const { return m_dirHuman.attr; } ///< Get Human68k attribute
|
||||
uint16_t GetDate() const { return m_dirHuman.date; } ///< Get Human68k date
|
||||
uint16_t GetTime() const { return m_dirHuman.time; } ///< Get Human68k time
|
||||
uint32_t GetSize() const { return m_dirHuman.size; } ///< Get Human68k file size
|
||||
const BYTE* GetHumanFilename() const { return m_szHumanFilename; }///< Get Human68k file name
|
||||
const BYTE* GetHumanResult() const { return m_szHumanResult; } ///< Get Human68k file name search results
|
||||
const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name
|
||||
|
||||
private:
|
||||
DWORD m_nKey = 0; ///< FILES buffer address for Human68k; 0 is unused
|
||||
DWORD m_nHumanWildcard = 0; ///< Human68k wildcard data
|
||||
DWORD m_nHumanAttribute = 0; ///< Human68k search attribute
|
||||
uint32_t m_nKey = 0; ///< FILES buffer address for Human68k; 0 is unused
|
||||
uint32_t m_nHumanWildcard = 0; ///< Human68k wildcard data
|
||||
uint32_t m_nHumanAttribute = 0; ///< Human68k search attribute
|
||||
CHostPath::find_t m_findNext = {}; ///< Next search location data
|
||||
Human68k::dirent_t m_dirHuman = {}; ///< Search results: Human68k file data
|
||||
BYTE m_szHumanFilename[24] = {}; ///< Human68k file name
|
||||
|
@ -653,8 +620,8 @@ public:
|
|||
void Init(); ///< Initialization (when the driver is installed)
|
||||
void Clean(); ///< Release (when starting up or resetting)
|
||||
|
||||
CHostFiles* Alloc(DWORD nKey);
|
||||
CHostFiles* Search(DWORD nKey);
|
||||
CHostFiles* Alloc(uint32_t nKey);
|
||||
CHostFiles* Search(uint32_t nKey);
|
||||
void Free(CHostFiles* pFiles);
|
||||
private:
|
||||
/// For memory management
|
||||
|
@ -675,31 +642,31 @@ class CHostFcb {
|
|||
public:
|
||||
CHostFcb() = default;
|
||||
~CHostFcb() { Close(); }
|
||||
CHostFcb(CHostFcb&) = delete;
|
||||
CHostFcb& operator=(const CHostFcb&) = delete;
|
||||
CHostFcb(CHostFcb&) = default;
|
||||
CHostFcb& operator=(const CHostFcb&) = default;
|
||||
|
||||
void Init();
|
||||
|
||||
void SetKey(DWORD nKey) { m_nKey = nKey; } ///< Set search key
|
||||
bool isSameKey(DWORD nKey) const { return m_nKey == nKey; } ///< Compare search key
|
||||
void SetKey(uint32_t nKey) { m_nKey = nKey; } ///< Set search key
|
||||
bool isSameKey(uint32_t nKey) const { return m_nKey == nKey; } ///< Compare search key
|
||||
void SetUpdate() { m_bUpdate = true; } ///< Update
|
||||
bool isUpdate() const { return m_bUpdate; } ///< Get update state
|
||||
bool SetMode(DWORD nHumanMode); ///< Set file open mode
|
||||
bool SetMode(uint32_t nHumanMode); ///< Set file open mode
|
||||
void SetFilename(const TCHAR* szFilename); ///< Set file name
|
||||
void SetHumanPath(const BYTE* szHumanPath); ///< Set Human68k path name
|
||||
const BYTE* GetHumanPath() const { return m_szHumanPath; } ///< Get Human68k path name
|
||||
|
||||
bool Create(DWORD nHumanAttribute, bool bForce); ///< Create file
|
||||
bool Create(uint32_t nHumanAttribute, bool bForce); ///< Create file
|
||||
bool Open(); ///< Open file
|
||||
DWORD Read(BYTE* pBuffer, DWORD nSize); ///< Read file
|
||||
DWORD Write(const BYTE* pBuffer, DWORD nSize); ///< Write file
|
||||
uint32_t Read(BYTE* pBuffer, uint32_t nSize); ///< Read file
|
||||
uint32_t Write(const BYTE* pBuffer, uint32_t nSize); ///< Write file
|
||||
bool Truncate() const; ///< Truncate file
|
||||
DWORD Seek(DWORD nOffset, DWORD nHumanSeek); ///< Seek file
|
||||
bool TimeStamp(DWORD nHumanTime) const; ///< Set file time stamp
|
||||
uint32_t Seek(uint32_t nOffset, Human68k::seek_t nHumanSeek); ///< Seek file
|
||||
bool TimeStamp(uint32_t nHumanTime) const; ///< Set file time stamp
|
||||
void Close(); ///< Close file
|
||||
|
||||
private:
|
||||
DWORD m_nKey = 0; ///< Human68k FCB buffer address (0 if unused)
|
||||
uint32_t m_nKey = 0; ///< Human68k FCB buffer address (0 if unused)
|
||||
bool m_bUpdate = false; ///< Update flag
|
||||
FILE* m_pFile = nullptr; ///< Host side file object
|
||||
const char* m_pszMode = nullptr; ///< Host side file open mode
|
||||
|
@ -721,8 +688,8 @@ public:
|
|||
void Init(); ///< Initialization (when the driver is installed)
|
||||
void Clean() const; ///< Release (when starting up or resetting)
|
||||
|
||||
CHostFcb* Alloc(DWORD nKey);
|
||||
CHostFcb* Search(DWORD nKey);
|
||||
CHostFcb* Alloc(uint32_t nKey);
|
||||
CHostFcb* Search(uint32_t nKey);
|
||||
void Free(CHostFcb* p);
|
||||
|
||||
private:
|
||||
|
@ -747,23 +714,23 @@ class CHostDrv
|
|||
public:
|
||||
CHostDrv() = default;
|
||||
~CHostDrv();
|
||||
CHostDrv(CHostDrv&) = delete;
|
||||
CHostDrv& operator=(const CHostDrv&) = delete;
|
||||
CHostDrv(CHostDrv&) = default;
|
||||
CHostDrv& operator=(const CHostDrv&) = default;
|
||||
|
||||
void Init(const TCHAR* szBase, DWORD nFlag); ///< Initialization (device startup and load)
|
||||
void Init(const TCHAR* szBase, uint32_t nFlag); ///< Initialization (device startup and load)
|
||||
|
||||
bool isWriteProtect() const { return m_bWriteProtect; }
|
||||
bool isEnable() const { return m_bEnable; } ///< Is it accessible?
|
||||
bool isMediaOffline() const;
|
||||
BYTE GetMediaByte() const;
|
||||
DWORD GetStatus() const;
|
||||
uint32_t GetStatus() const;
|
||||
void SetEnable(bool); ///< Set media status
|
||||
bool CheckMedia(); ///< Check if media was changed
|
||||
void Update(); ///< Update media status
|
||||
void Eject();
|
||||
void GetVolume(TCHAR* szLabel); ///< Get volume label
|
||||
bool GetVolumeCache(TCHAR* szLabel) const; ///< Get volume label from cache
|
||||
DWORD GetCapacity(Human68k::capacity_t* pCapacity);
|
||||
uint32_t GetCapacity(Human68k::capacity_t* pCapacity);
|
||||
bool GetCapacityCache(Human68k::capacity_t* pCapacity) const; ///< Get capacity from cache
|
||||
|
||||
// Cache operations
|
||||
|
@ -789,7 +756,7 @@ private:
|
|||
|
||||
bool m_bWriteProtect = false; ///< TRUE if write-protected
|
||||
bool m_bEnable = false; ///< TRUE if media is usable
|
||||
DWORD m_nRing = 0; ///< Number of stored path names
|
||||
uint32_t m_nRing = 0; ///< Number of stored path names
|
||||
CRing m_cRing; ///< For attaching to CHostPath
|
||||
Human68k::capacity_t m_capCache; ///< Sector data cache: if "sectors == 0" then not cached
|
||||
bool m_bVolumeCache = false; ///< TRUE if the volume label has been read
|
||||
|
@ -811,39 +778,39 @@ public:
|
|||
|
||||
CHostEntry() = default;
|
||||
~CHostEntry();
|
||||
CHostEntry(CHostEntry&) = delete;
|
||||
CHostEntry& operator=(const CHostEntry&) = delete;
|
||||
CHostEntry(CHostEntry&) = default;
|
||||
CHostEntry& operator=(const CHostEntry&) = default;
|
||||
|
||||
void Init() const; ///< Initialization (when the driver is installed)
|
||||
void Clean(); ///< Release (when starting up or resetting)
|
||||
|
||||
// Cache operations
|
||||
void CleanCache() const; ///< Update all cache
|
||||
void CleanCache(DWORD nUnit) const; ///< Update cache for the specified unit
|
||||
void CleanCache(DWORD nUnit, const BYTE* szHumanPath) const; ///< Update cache for the specified path
|
||||
void CleanCacheChild(DWORD nUnit, const BYTE* szHumanPath) const; ///< Update cache below the specified path
|
||||
void DeleteCache(DWORD nUnit, const BYTE* szHumanPath) const; ///< Delete cache for the specified path
|
||||
bool Find(DWORD nUnit, CHostFiles* pFiles) const; ///< Find host side name (path + file name (can be abbreviated) + attribute)
|
||||
void ShellNotify(DWORD nEvent, const TCHAR* szPath); ///< Notify status change in the host side file system
|
||||
void CleanCache(uint32_t nUnit) const; ///< Update cache for the specified unit
|
||||
void CleanCache(uint32_t nUnit, const BYTE* szHumanPath) const; ///< Update cache for the specified path
|
||||
void CleanCacheChild(uint32_t nUnit, const BYTE* szHumanPath) const; ///< Update cache below the specified path
|
||||
void DeleteCache(uint32_t nUnit, const BYTE* szHumanPath) const; ///< Delete cache for the specified path
|
||||
bool Find(uint32_t nUnit, CHostFiles* pFiles) const; ///< Find host side name (path + file name (can be abbreviated) + attribute)
|
||||
void ShellNotify(uint32_t nEvent, const TCHAR* szPath); ///< Notify status change in the host side file system
|
||||
|
||||
// Drive object operations
|
||||
void SetDrv(DWORD nUnit, CHostDrv* pDrv);
|
||||
bool isWriteProtect(DWORD nUnit) const;
|
||||
bool isEnable(DWORD nUnit) const; ///< Is it accessible?
|
||||
bool isMediaOffline(DWORD nUnit) const;
|
||||
BYTE GetMediaByte(DWORD nUnit) const;
|
||||
DWORD GetStatus(DWORD nUnit) const; ///< Get drive status
|
||||
bool CheckMedia(DWORD nUnit) const; ///< Media change check
|
||||
void Eject(DWORD nUnit) const;
|
||||
void GetVolume(DWORD nUnit, TCHAR* szLabel) const; ///< Get volume label
|
||||
bool GetVolumeCache(DWORD nUnit, TCHAR* szLabel) const; ///< Get volume label from cache
|
||||
DWORD GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const;
|
||||
bool GetCapacityCache(DWORD nUnit, Human68k::capacity_t* pCapacity) const; ///< Get cluster size from cache
|
||||
void SetDrv(uint32_t nUnit, CHostDrv* pDrv);
|
||||
bool isWriteProtect(uint32_t nUnit) const;
|
||||
bool isEnable(uint32_t nUnit) const; ///< Is it accessible?
|
||||
bool isMediaOffline(uint32_t nUnit) const;
|
||||
BYTE GetMediaByte(uint32_t nUnit) const;
|
||||
uint32_t GetStatus(uint32_t nUnit) const; ///< Get drive status
|
||||
bool CheckMedia(uint32_t nUnit) const; ///< Media change check
|
||||
void Eject(uint32_t nUnit) const;
|
||||
void GetVolume(uint32_t nUnit, TCHAR* szLabel) const; ///< Get volume label
|
||||
bool GetVolumeCache(uint32_t nUnit, TCHAR* szLabel) const; ///< Get volume label from cache
|
||||
uint32_t GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const;
|
||||
bool GetCapacityCache(uint32_t nUnit, Human68k::capacity_t* pCapacity) const; ///< Get cluster size from cache
|
||||
|
||||
private:
|
||||
|
||||
CHostDrv* m_pDrv[DRIVE_MAX] = {}; ///< Host side drive object
|
||||
DWORD m_nTimeout = 0; ///< Last time a timeout check was carried out
|
||||
uint32_t m_nTimeout = 0; ///< Last time a timeout check was carried out
|
||||
};
|
||||
|
||||
//===========================================================================
|
||||
|
@ -854,13 +821,13 @@ private:
|
|||
/** @note
|
||||
Current state of affairs:
|
||||
|
||||
While it violates the design philosophy of XM6, we should find a way for
|
||||
'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'.
|
||||
While it violates the design philosophy of XM6, we should find a way for
|
||||
'class Windrv' and 'class CWindrv' to have a direct pointer to 'class CFileSys'.
|
||||
This way, we get the following benefits.
|
||||
|
||||
Benefit no. 1
|
||||
Makes it possible to manage a large number of command handler methods in one place.
|
||||
There is a high chance the command handlers will change drastically because of
|
||||
Makes it possible to manage a large number of command handler methods in one place.
|
||||
There is a high chance the command handlers will change drastically because of
|
||||
host system architectural changes, so we will save a huge amount of maintenance work
|
||||
in the long run.
|
||||
|
||||
|
@ -870,7 +837,7 @@ It is not feasible to implement code in XM6 for simultaneous use of file system
|
|||
Therefore file system object polymorphism is a waste of CPU cycles.
|
||||
|
||||
I made the change as an experiment. Performance did improve.
|
||||
The improvement was obvious from looking at the source the compiler spit out
|
||||
The improvement was obvious from looking at the source the compiler spit out
|
||||
after changing the FILESYS_FAST_STRUCTURE value in windrv.h.
|
||||
You may understand now why I decided to rant here.
|
||||
|
||||
|
@ -887,71 +854,71 @@ public:
|
|||
void Init(); ///< Initialization (device startup and load)
|
||||
|
||||
// Command handlers
|
||||
DWORD InitDevice(const Human68k::argument_t* pArgument); ///< $40 - Device startup
|
||||
int CheckDir(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $41 - Directory check
|
||||
int MakeDir(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $42 - Create directory
|
||||
int RemoveDir(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $43 - Delete directory
|
||||
int Rename(DWORD nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const;
|
||||
uint32_t InitDevice(const Human68k::argument_t* pArgument); ///< $40 - Device startup
|
||||
int CheckDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $41 - Directory check
|
||||
int MakeDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $42 - Create directory
|
||||
int RemoveDir(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $43 - Delete directory
|
||||
int Rename(uint32_t nUnit, const Human68k::namests_t* pNamests, const Human68k::namests_t* pNamestsNew) const;
|
||||
///< $44 - Change file name
|
||||
int Delete(DWORD nUnit, const Human68k::namests_t* pNamests) const; ///< $45 - Delete file
|
||||
int Attribute(DWORD nUnit, const Human68k::namests_t* pNamests, DWORD nHumanAttribute) const;
|
||||
int Delete(uint32_t nUnit, const Human68k::namests_t* pNamests) const; ///< $45 - Delete file
|
||||
int Attribute(uint32_t nUnit, const Human68k::namests_t* pNamests, uint32_t nHumanAttribute) const;
|
||||
///< $46 - Get / set file attribute
|
||||
int Files(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles);
|
||||
int Files(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::files_t* pFiles);
|
||||
///< $47 - Find file
|
||||
int NFiles(DWORD nUnit, DWORD nKey, Human68k::files_t* pFiles); ///< $48 - Find next file
|
||||
int Create(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, DWORD nHumanAttribute, bool bForce);
|
||||
int NFiles(uint32_t nUnit, uint32_t nKey, Human68k::files_t* pFiles); ///< $48 - Find next file
|
||||
int Create(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb, uint32_t nHumanAttribute, bool bForce);
|
||||
///< $49 - Create file
|
||||
int Open(DWORD nUnit, DWORD nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb);
|
||||
int Open(uint32_t nUnit, uint32_t nKey, const Human68k::namests_t* pNamests, Human68k::fcb_t* pFcb);
|
||||
///< $4A - Open file
|
||||
int Close(DWORD nUnit, DWORD nKey, const Human68k::fcb_t* pFcb); ///< $4B - Close file
|
||||
int Read(DWORD nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, DWORD nSize);
|
||||
int Close(uint32_t nUnit, uint32_t nKey, const Human68k::fcb_t* pFcb); ///< $4B - Close file
|
||||
int Read(uint32_t nKey, Human68k::fcb_t* pFcb, BYTE* pAddress, uint32_t nSize);
|
||||
///< $4C - Read file
|
||||
int Write(DWORD nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, DWORD nSize);
|
||||
int Write(uint32_t nKey, Human68k::fcb_t* pFcb, const BYTE* pAddress, uint32_t nSize);
|
||||
///< $4D - Write file
|
||||
int Seek(DWORD nKey, Human68k::fcb_t* pFcb, DWORD nSeek, int nOffset); ///< $4E - Seek file
|
||||
DWORD TimeStamp(DWORD nUnit, DWORD nKey, Human68k::fcb_t* pFcb, DWORD nHumanTime);
|
||||
int Seek(uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nSeek, int nOffset); ///< $4E - Seek file
|
||||
uint32_t TimeStamp(uint32_t nUnit, uint32_t nKey, Human68k::fcb_t* pFcb, uint32_t nHumanTime);
|
||||
///< $4F - Get / set file timestamp
|
||||
int GetCapacity(DWORD nUnit, Human68k::capacity_t* pCapacity) const; ///< $50 - Get capacity
|
||||
int CtrlDrive(DWORD nUnit, Human68k::ctrldrive_t* pCtrlDrive) const; ///< $51 - Inspect / control drive status
|
||||
int GetDPB(DWORD nUnit, Human68k::dpb_t* pDpb) const; ///< $52 - Get DPB
|
||||
int DiskRead(DWORD nUnit, BYTE* pBuffer, DWORD nSector, DWORD nSize); ///< $53 - Read sectors
|
||||
int DiskWrite(DWORD nUnit) const; ///< $54 - Write sectors
|
||||
int Ioctrl(DWORD nUnit, DWORD nFunction, Human68k::ioctrl_t* pIoctrl); ///< $55 - IOCTRL
|
||||
int Flush(DWORD nUnit) const; ///< $56 - Flush
|
||||
int CheckMedia(DWORD nUnit) const; ///< $57 - Media change check
|
||||
int Lock(DWORD nUnit) const; ///< $58 - Lock
|
||||
int GetCapacity(uint32_t nUnit, Human68k::capacity_t* pCapacity) const; ///< $50 - Get capacity
|
||||
int CtrlDrive(uint32_t nUnit, Human68k::ctrldrive_t* pCtrlDrive) const; ///< $51 - Inspect / control drive status
|
||||
int GetDPB(uint32_t nUnit, Human68k::dpb_t* pDpb) const; ///< $52 - Get DPB
|
||||
int DiskRead(uint32_t nUnit, BYTE* pBuffer, uint32_t nSector, uint32_t nSize); ///< $53 - Read sectors
|
||||
int DiskWrite(uint32_t nUnit) const; ///< $54 - Write sectors
|
||||
int Ioctrl(uint32_t nUnit, uint32_t nFunction, Human68k::ioctrl_t* pIoctrl); ///< $55 - IOCTRL
|
||||
int Flush(uint32_t nUnit) const; ///< $56 - Flush
|
||||
int CheckMedia(uint32_t nUnit) const; ///< $57 - Media change check
|
||||
int Lock(uint32_t nUnit) const; ///< $58 - Lock
|
||||
|
||||
void SetOption(DWORD nOption); ///< Set option
|
||||
DWORD GetOption() const { return m_nOption; } ///< Get option
|
||||
DWORD GetDefault() const { return m_nOptionDefault; } ///< Get default options
|
||||
static DWORD GetFileOption() { return g_nOption; } ///< Get file name change option
|
||||
void SetOption(uint32_t nOption); ///< Set option
|
||||
uint32_t GetOption() const { return m_nOption; } ///< Get option
|
||||
uint32_t GetDefault() const { return m_nOptionDefault; } ///< Get default options
|
||||
static uint32_t GetFileOption() { return g_nOption; } ///< Get file name change option
|
||||
|
||||
static const int DriveMax = CHostEntry::DRIVE_MAX; ///< Max number of drive candidates
|
||||
|
||||
private:
|
||||
void InitOption(const Human68k::argument_t* pArgument);
|
||||
bool FilesVolume(DWORD nUnit, Human68k::files_t* pFiles) const; ///< Get volume label
|
||||
bool FilesVolume(uint32_t nUnit, Human68k::files_t* pFiles) const; ///< Get volume label
|
||||
|
||||
DWORD m_nUnits = 0; ///< Number of current drive objects (Changes for every resume)
|
||||
uint32_t m_nUnits = 0; ///< Number of current drive objects (Changes for every resume)
|
||||
|
||||
DWORD m_nOption = 0; ///< Current runtime flag
|
||||
DWORD m_nOptionDefault = 0; ///< Runtime flag at reset
|
||||
uint32_t m_nOption = 0; ///< Current runtime flag
|
||||
uint32_t m_nOptionDefault = 0; ///< Runtime flag at reset
|
||||
|
||||
DWORD m_nDrives = 0; ///< Number of candidates for base path status restoration (scan every time if 0)
|
||||
uint32_t m_nDrives = 0; ///< Number of candidates for base path status restoration (scan every time if 0)
|
||||
|
||||
DWORD m_nKernel = 0; ///< Counter for kernel check
|
||||
DWORD m_nKernelSearch = 0; ///< Initial address for NUL device
|
||||
uint32_t m_nKernel = 0; ///< Counter for kernel check
|
||||
uint32_t m_nKernelSearch = 0; ///< Initial address for NUL device
|
||||
|
||||
DWORD m_nHostSectorCount = 0; ///< Virtual sector identifier
|
||||
uint32_t m_nHostSectorCount = 0; ///< Virtual sector identifier
|
||||
|
||||
CHostFilesManager m_cFiles; ///< File search memory
|
||||
CHostFcbManager m_cFcb; ///< FCB operation memory
|
||||
CHostEntry m_cEntry; ///< Drive object and directory entry
|
||||
|
||||
DWORD m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX];
|
||||
uint32_t m_nHostSectorBuffer[XM6_HOST_PSEUDO_CLUSTER_MAX];
|
||||
///< Entity that the virtual sector points to
|
||||
|
||||
DWORD m_nFlag[DriveMax] = {}; ///< Candidate runtime flag for base path restoration
|
||||
uint32_t m_nFlag[DriveMax] = {}; ///< Candidate runtime flag for base path restoration
|
||||
TCHAR m_szBase[DriveMax][FILEPATH_MAX] = {}; ///< Candidate for base path restoration
|
||||
static DWORD g_nOption; ///< File name change flag
|
||||
static uint32_t g_nOption; ///< File name change flag
|
||||
};
|
||||
|
|
|
@ -37,7 +37,7 @@ using namespace ras_util;
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
static bool br_setif(int br_socket_fd, const char* bridgename, const char* ifname, bool add) {
|
||||
#ifndef __linux
|
||||
#ifndef __linux__
|
||||
return false;
|
||||
#else
|
||||
ifreq ifr;
|
||||
|
@ -83,7 +83,7 @@ CTapDriver::~CTapDriver()
|
|||
}
|
||||
|
||||
static bool ip_link(int fd, const char* ifname, bool up) {
|
||||
#ifndef __linux
|
||||
#ifndef __linux__
|
||||
return false;
|
||||
#else
|
||||
ifreq ifr;
|
||||
|
@ -126,7 +126,7 @@ static bool is_interface_up(string_view interface) {
|
|||
|
||||
bool CTapDriver::Init(const unordered_map<string, string>& const_params)
|
||||
{
|
||||
#ifndef __linux
|
||||
#ifndef __linux__
|
||||
return false;
|
||||
#else
|
||||
unordered_map<string, string> params = const_params;
|
||||
|
@ -158,7 +158,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
|
|||
}
|
||||
|
||||
LOGTRACE("Opened tap device %d", m_hTAP)
|
||||
|
||||
|
||||
// IFF_NO_PI for no extra packet information
|
||||
ifreq ifr = {};
|
||||
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
|
||||
|
@ -177,7 +177,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
|
|||
|
||||
LOGTRACE("Return code from ioctl was %d", ret)
|
||||
|
||||
int ip_fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
const int ip_fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
if (ip_fd < 0) {
|
||||
LOGERROR("Can't open ip socket: %s", strerror(errno))
|
||||
|
||||
|
@ -185,7 +185,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
|
|||
return false;
|
||||
}
|
||||
|
||||
int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
const int br_socket_fd = socket(AF_LOCAL, SOCK_STREAM, 0);
|
||||
if (br_socket_fd < 0) {
|
||||
LOGERROR("Can't open bridge socket: %s", strerror(errno))
|
||||
|
||||
|
@ -245,7 +245,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
|
|||
}
|
||||
else {
|
||||
string address = inet;
|
||||
string netmask = "255.255.255.0";
|
||||
string netmask = "255.255.255.0"; //NOSONAR This hardcoded IP address is safe
|
||||
if (size_t separatorPos = inet.find('/'); separatorPos != string::npos) {
|
||||
address = inet.substr(0, separatorPos);
|
||||
|
||||
|
@ -260,7 +260,7 @@ bool CTapDriver::Init(const unordered_map<string, string>& const_params)
|
|||
}
|
||||
|
||||
// long long is required for compatibility with 32 bit platforms
|
||||
auto mask = (long long)(pow(2, 32) - (1 << (32 - m)));
|
||||
const auto mask = (long long)(pow(2, 32) - (1 << (32 - m)));
|
||||
netmask = to_string((mask >> 24) & 0xff) + '.' + to_string((mask >> 16) & 0xff) + '.' +
|
||||
to_string((mask >> 8) & 0xff) + '.' + to_string(mask & 0xff);
|
||||
|
||||
|
@ -391,18 +391,18 @@ void CTapDriver::OpenDump(const Filepath& path) {
|
|||
|
||||
bool CTapDriver::Enable() const
|
||||
{
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
const int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
LOGDEBUG("%s: ip link set ras0 up", __PRETTY_FUNCTION__)
|
||||
bool result = ip_link(fd, "ras0", true);
|
||||
const bool result = ip_link(fd, "ras0", true);
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool CTapDriver::Disable() const
|
||||
{
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
const int fd = socket(PF_INET, SOCK_DGRAM, 0);
|
||||
LOGDEBUG("%s: ip link set ras0 down", __PRETTY_FUNCTION__)
|
||||
bool result = ip_link(fd, "ras0", false);
|
||||
const bool result = ip_link(fd, "ras0", false);
|
||||
close(fd);
|
||||
return result;
|
||||
}
|
||||
|
@ -447,12 +447,12 @@ bool CTapDriver::PendingPackets() const
|
|||
}
|
||||
|
||||
// See https://stackoverflow.com/questions/21001659/crc32-algorithm-implementation-in-c-without-a-look-up-table-and-with-a-public-li
|
||||
uint32_t crc32(const BYTE *buf, int length) {
|
||||
uint32_t CTapDriver::Crc32(const BYTE *buf, int length) {
|
||||
uint32_t crc = 0xffffffff;
|
||||
for (int i = 0; i < length; i++) {
|
||||
crc ^= buf[i];
|
||||
for (int j = 0; j < 8; j++) {
|
||||
uint32_t mask = -(crc & 1);
|
||||
const uint32_t mask = -((int)crc & 1);
|
||||
crc = (crc >> 1) ^ (0xEDB88320 & mask);
|
||||
}
|
||||
}
|
||||
|
@ -469,8 +469,8 @@ int CTapDriver::Receive(BYTE *buf)
|
|||
}
|
||||
|
||||
// Receive
|
||||
auto dwReceived = (DWORD)read(m_hTAP, buf, ETH_FRAME_LEN);
|
||||
if (dwReceived == (DWORD)-1) {
|
||||
auto dwReceived = (uint32_t)read(m_hTAP, buf, ETH_FRAME_LEN);
|
||||
if (dwReceived == (uint32_t)-1) {
|
||||
LOGWARN("%s Error occured while receiving a packet", __PRETTY_FUNCTION__)
|
||||
return 0;
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ int CTapDriver::Receive(BYTE *buf)
|
|||
// We need to add the Frame Check Status (FCS) CRC back onto the end of the packet.
|
||||
// The Linux network subsystem removes it, since most software apps shouldn't ever
|
||||
// need it.
|
||||
int crc = crc32(buf, dwReceived);
|
||||
const int crc = Crc32(buf, dwReceived);
|
||||
|
||||
buf[dwReceived + 0] = (BYTE)((crc >> 0) & 0xFF);
|
||||
buf[dwReceived + 1] = (BYTE)((crc >> 8) & 0xFF);
|
||||
|
|
|
@ -19,24 +19,20 @@
|
|||
#include <string>
|
||||
#include <array>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
class CTapDriver
|
||||
{
|
||||
friend class SCSIDaynaPort;
|
||||
friend class SCSIBR;
|
||||
|
||||
static constexpr const char *BRIDGE_NAME = "rascsi_bridge";
|
||||
|
||||
CTapDriver() = default;
|
||||
~CTapDriver();
|
||||
CTapDriver(CTapDriver&) = delete;
|
||||
CTapDriver& operator=(const CTapDriver&) = delete;
|
||||
|
||||
bool Init(const unordered_map<string, string>&);
|
||||
|
||||
public:
|
||||
|
||||
CTapDriver() = default;
|
||||
~CTapDriver();
|
||||
CTapDriver(CTapDriver&) = default;
|
||||
CTapDriver& operator=(const CTapDriver&) = default;
|
||||
|
||||
bool Init(const unordered_map<string, string>&);
|
||||
void OpenDump(const Filepath& path); // Capture packets
|
||||
void GetMacAddr(BYTE *mac) const;
|
||||
int Receive(BYTE *buf);
|
||||
|
@ -46,6 +42,8 @@ public:
|
|||
bool Disable() const; // Disable the ras0 interface
|
||||
void Flush(); // Purge all of the packets that are waiting to be processed
|
||||
|
||||
static uint32_t Crc32(const BYTE *, int);
|
||||
|
||||
private:
|
||||
array<byte, 6> m_MacAddr; // MAC Address
|
||||
|
||||
|
|
|
@ -7,17 +7,16 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <cassert>
|
||||
#include "rascsi_version.h"
|
||||
#include "log.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "device.h"
|
||||
#include <cassert>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
using namespace std;
|
||||
|
||||
Device::Device(const string& t) : type(t)
|
||||
Device::Device(const string& type, int lun) : type(type), lun(lun)
|
||||
{
|
||||
assert(type.length() == 4);
|
||||
|
||||
|
@ -43,7 +42,7 @@ void Device::SetProtected(bool b)
|
|||
void Device::SetVendor(const string& v)
|
||||
{
|
||||
if (v.empty() || v.length() > 8) {
|
||||
throw illegal_argument_exception("Vendor '" + v + "' must be between 1 and 8 characters");
|
||||
throw invalid_argument("Vendor '" + v + "' must be between 1 and 8 characters");
|
||||
}
|
||||
|
||||
vendor = v;
|
||||
|
@ -52,10 +51,10 @@ void Device::SetVendor(const string& v)
|
|||
void Device::SetProduct(const string& p, bool force)
|
||||
{
|
||||
if (p.empty() || p.length() > 16) {
|
||||
throw illegal_argument_exception("Product '" + p + "' must be between 1 and 16 characters");
|
||||
throw invalid_argument("Product '" + p + "' must be between 1 and 16 characters");
|
||||
}
|
||||
|
||||
// Changing the device name is not SCSI compliant
|
||||
// Changing vital product data is not SCSI compliant
|
||||
if (!product.empty() && !force) {
|
||||
return;
|
||||
}
|
||||
|
@ -66,7 +65,7 @@ void Device::SetProduct(const string& p, bool force)
|
|||
void Device::SetRevision(const string& r)
|
||||
{
|
||||
if (r.empty() || r.length() > 4) {
|
||||
throw illegal_argument_exception("Revision '" + r + "' must be between 1 and 4 characters");
|
||||
throw invalid_argument("Revision '" + r + "' must be between 1 and 4 characters");
|
||||
}
|
||||
|
||||
revision = r;
|
||||
|
@ -74,13 +73,10 @@ void Device::SetRevision(const string& r)
|
|||
|
||||
string Device::GetPaddedName() const
|
||||
{
|
||||
string name = vendor;
|
||||
name.append(8 - vendor.length(), ' ');
|
||||
name += product;
|
||||
name.append(16 - product.length(), ' ');
|
||||
name += revision;
|
||||
name.append(4 - revision.length(), ' ');
|
||||
ostringstream os;
|
||||
os << left << setfill(' ') << setw(8) << vendor << setw(16) << product << setw(4) << revision;
|
||||
|
||||
const string name = os.str();
|
||||
assert(name.length() == 28);
|
||||
|
||||
return name;
|
||||
|
@ -107,15 +103,6 @@ void Device::SetParams(const unordered_map<string, string>& set_params)
|
|||
}
|
||||
}
|
||||
|
||||
void Device::SetStatusCode(int s)
|
||||
{
|
||||
if (s) {
|
||||
LOGDEBUG("Error status: Sense Key $%02X, ASC $%02X, ASCQ $%02X", s >> 16, (s >> 8 &0xff), s & 0xff)
|
||||
}
|
||||
|
||||
status_code = s;
|
||||
}
|
||||
|
||||
bool Device::Start()
|
||||
{
|
||||
if (!ready) {
|
||||
|
|
|
@ -9,16 +9,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "scsi.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
class Device
|
||||
class Device //NOSONAR The number of fields and methods is justified, the complexity is low
|
||||
{
|
||||
friend class DeviceFactory;
|
||||
|
||||
const string DEFAULT_VENDOR = "RaSCSI";
|
||||
|
||||
string type;
|
||||
|
@ -45,15 +42,11 @@ class Device
|
|||
bool lockable = false;
|
||||
bool locked = false;
|
||||
|
||||
// The block size is configurable
|
||||
bool block_size_configurable = false;
|
||||
|
||||
// Device can be created with parameters
|
||||
bool supports_params = false;
|
||||
|
||||
// Device ID and LUN
|
||||
int32_t id = 0;
|
||||
int32_t lun = 0;
|
||||
// Immutable LUN
|
||||
int lun;
|
||||
|
||||
// Device identifier (for INQUIRY)
|
||||
string vendor = DEFAULT_VENDOR;
|
||||
|
@ -86,21 +79,16 @@ protected:
|
|||
string GetParam(const string&) const;
|
||||
void SetParams(const unordered_map<string, string>&);
|
||||
|
||||
explicit Device(const string&);
|
||||
Device(const string&, int);
|
||||
|
||||
public:
|
||||
|
||||
virtual ~Device() = default;
|
||||
Device(Device&) = delete;
|
||||
Device& operator=(const Device&) = delete;
|
||||
|
||||
// Override for device specific initializations, to be called after all device properties have been set
|
||||
virtual bool Init(const unordered_map<string, string>&) { return true; };
|
||||
|
||||
const string& GetType() const { return type; }
|
||||
|
||||
bool IsReady() const { return ready; }
|
||||
void Reset();
|
||||
virtual void Reset();
|
||||
|
||||
bool IsProtectable() const { return protectable; }
|
||||
void SetProtectable(bool b) { protectable = b; }
|
||||
|
@ -123,10 +111,8 @@ public:
|
|||
bool IsLocked() const { return locked; }
|
||||
void SetLocked(bool b) { locked = b; }
|
||||
|
||||
int32_t GetId() const { return id; }
|
||||
void SetId(int32_t i) { id = i; }
|
||||
int32_t GetLun() const { return lun; }
|
||||
void SetLun(int32_t l) { lun = l; }
|
||||
virtual int GetId() const = 0;
|
||||
int GetLun() const { return lun; }
|
||||
|
||||
string GetVendor() const { return vendor; }
|
||||
void SetVendor(const string&);
|
||||
|
@ -142,12 +128,9 @@ public:
|
|||
unordered_map<string, string> GetParams() const { return params; }
|
||||
void SetDefaultParams(const unordered_map<string, string>& p) { default_params = p; }
|
||||
|
||||
void SetStatusCode(int);
|
||||
void SetStatusCode(int s) { status_code = s; }
|
||||
|
||||
bool Start();
|
||||
void Stop();
|
||||
virtual bool Eject(bool);
|
||||
virtual void FlushCache() {
|
||||
// Devices with a cache have to implement this method
|
||||
}
|
||||
};
|
||||
|
|
|
@ -25,8 +25,6 @@
|
|||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
multimap<int, unique_ptr<PrimaryDevice>> DeviceFactory::devices;
|
||||
|
||||
DeviceFactory::DeviceFactory()
|
||||
{
|
||||
sector_sizes[SCHD] = { 512, 1024, 2048, 4096 };
|
||||
|
@ -43,9 +41,9 @@ DeviceFactory::DeviceFactory()
|
|||
}
|
||||
|
||||
default_params[SCBR]["interface"] = network_interfaces;
|
||||
default_params[SCBR]["inet"] = "10.10.20.1/24";
|
||||
default_params[SCBR]["inet"] = DEFAULT_IP;
|
||||
default_params[SCDP]["interface"] = network_interfaces;
|
||||
default_params[SCDP]["inet"] = "10.10.20.1/24";
|
||||
default_params[SCDP]["inet"] = DEFAULT_IP;
|
||||
default_params[SCLP]["cmd"] = "lp -oraw %f";
|
||||
default_params[SCLP]["timeout"] = "30";
|
||||
|
||||
|
@ -58,51 +56,17 @@ DeviceFactory::DeviceFactory()
|
|||
extension_mapping["hdr"] = SCRM;
|
||||
extension_mapping["mos"] = SCMO;
|
||||
extension_mapping["iso"] = SCCD;
|
||||
}
|
||||
|
||||
void DeviceFactory::DeleteDevice(const PrimaryDevice& device) const
|
||||
{
|
||||
auto [begin, end] = devices.equal_range(device.GetId());
|
||||
for (auto& it = begin; it != end; ++it) {
|
||||
if (it->second->GetLun() == device.GetLun()) {
|
||||
devices.erase(it);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceFactory::DeleteAllDevices() const
|
||||
{
|
||||
devices.clear();
|
||||
}
|
||||
|
||||
const PrimaryDevice *DeviceFactory::GetDeviceByIdAndLun(int i, int lun) const
|
||||
{
|
||||
for (const auto& [id, device] : devices) {
|
||||
if (device->GetId() == i && device->GetLun() == lun) {
|
||||
return device.get();
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
list<PrimaryDevice *> DeviceFactory::GetAllDevices() const
|
||||
{
|
||||
list<PrimaryDevice *> result;
|
||||
|
||||
for (const auto& [id, device] : devices) {
|
||||
result.push_back(device.get());
|
||||
}
|
||||
|
||||
return result;
|
||||
device_mapping["bridge"] = SCBR;
|
||||
device_mapping["daynaport"] = SCDP;
|
||||
device_mapping["printer"] = SCLP;
|
||||
device_mapping["services"] = SCHS;
|
||||
}
|
||||
|
||||
string DeviceFactory::GetExtension(const string& filename) const
|
||||
{
|
||||
string ext;
|
||||
if (size_t separator = filename.rfind('.'); separator != string::npos) {
|
||||
if (const size_t separator = filename.rfind('.'); separator != string::npos) {
|
||||
ext = filename.substr(separator + 1);
|
||||
}
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), [](unsigned char c){ return std::tolower(c); });
|
||||
|
@ -115,24 +79,17 @@ PbDeviceType DeviceFactory::GetTypeForFile(const string& filename) const
|
|||
if (const auto& it = extension_mapping.find(GetExtension(filename)); it != extension_mapping.end()) {
|
||||
return it->second;
|
||||
}
|
||||
else if (filename == "bridge") {
|
||||
return SCBR;
|
||||
}
|
||||
else if (filename == "daynaport") {
|
||||
return SCDP;
|
||||
}
|
||||
else if (filename == "printer") {
|
||||
return SCLP;
|
||||
}
|
||||
else if (filename == "services") {
|
||||
return SCHS;
|
||||
|
||||
if (const auto& it = device_mapping.find(filename); it != device_mapping.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return UNDEFINED;
|
||||
}
|
||||
|
||||
// ID -1 is used by rascsi to create a temporary device
|
||||
PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& filename, int id)
|
||||
shared_ptr<PrimaryDevice> DeviceFactory::CreateDevice(const ControllerManager& controller_manager, PbDeviceType type,
|
||||
int lun, const string& filename)
|
||||
{
|
||||
// If no type was specified try to derive the device type from the filename
|
||||
if (type == UNDEFINED) {
|
||||
|
@ -142,13 +99,14 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
|
|||
}
|
||||
}
|
||||
|
||||
unique_ptr<PrimaryDevice> device;
|
||||
shared_ptr<PrimaryDevice> device;
|
||||
switch (type) {
|
||||
case SCHD: {
|
||||
if (string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
|
||||
device = make_unique<SCSIHD_NEC>();
|
||||
if (const string ext = GetExtension(filename); ext == "hdn" || ext == "hdi" || ext == "nhd") {
|
||||
device = make_shared<SCSIHD_NEC>(lun);
|
||||
} else {
|
||||
device = make_unique<SCSIHD>(sector_sizes[SCHD], false, ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
|
||||
device = make_shared<SCSIHD>(lun, sector_sizes[SCHD], false,
|
||||
ext == "hd1" ? scsi_level::SCSI_1_CCS : scsi_level::SCSI_2);
|
||||
|
||||
// Some Apple tools require a particular drive identification
|
||||
if (ext == "hda") {
|
||||
|
@ -162,7 +120,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
|
|||
}
|
||||
|
||||
case SCRM:
|
||||
device = make_unique<SCSIHD>(sector_sizes[SCRM], true);
|
||||
device = make_shared<SCSIHD>(lun, sector_sizes[SCRM], true);
|
||||
device->SetProtectable(true);
|
||||
device->SetStoppable(true);
|
||||
device->SetRemovable(true);
|
||||
|
@ -171,7 +129,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
|
|||
break;
|
||||
|
||||
case SCMO:
|
||||
device = make_unique<SCSIMO>(sector_sizes[SCMO]);
|
||||
device = make_shared<SCSIMO>(lun, sector_sizes[SCMO]);
|
||||
device->SetProtectable(true);
|
||||
device->SetStoppable(true);
|
||||
device->SetRemovable(true);
|
||||
|
@ -180,7 +138,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
|
|||
break;
|
||||
|
||||
case SCCD:
|
||||
device = make_unique<SCSICD>(sector_sizes[SCCD]);
|
||||
device = make_shared<SCSICD>(lun, sector_sizes[SCCD]);
|
||||
device->SetReadOnly(true);
|
||||
device->SetStoppable(true);
|
||||
device->SetRemovable(true);
|
||||
|
@ -189,14 +147,15 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
|
|||
break;
|
||||
|
||||
case SCBR:
|
||||
device = make_unique<SCSIBR>();
|
||||
device = make_shared<SCSIBR>(lun);
|
||||
// Since this is an emulation for a specific driver the product name has to be set accordingly
|
||||
device->SetProduct("RASCSI BRIDGE");
|
||||
device->SupportsParams(true);
|
||||
device->SetDefaultParams(default_params[SCBR]);
|
||||
break;
|
||||
|
||||
case SCDP:
|
||||
device = make_unique<SCSIDaynaPort>();
|
||||
device = make_shared<SCSIDaynaPort>(lun);
|
||||
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
|
||||
device->SetVendor("Dayna");
|
||||
device->SetProduct("SCSI/Link");
|
||||
|
@ -206,14 +165,14 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
|
|||
break;
|
||||
|
||||
case SCHS:
|
||||
device = make_unique<HostServices>(*this);
|
||||
device = make_shared<HostServices>(lun, controller_manager);
|
||||
// Since this is an emulation for a specific device the full INQUIRY data have to be set accordingly
|
||||
device->SetVendor("RaSCSI");
|
||||
device->SetProduct("Host Services");
|
||||
break;
|
||||
|
||||
case SCLP:
|
||||
device = make_unique<SCSIPrinter>();
|
||||
device = make_shared<SCSIPrinter>(lun);
|
||||
device->SetProduct("SCSI PRINTER");
|
||||
device->SupportsParams(true);
|
||||
device->SetDefaultParams(default_params[SCLP]);
|
||||
|
@ -223,17 +182,7 @@ PrimaryDevice *DeviceFactory::CreateDevice(PbDeviceType type, const string& file
|
|||
break;
|
||||
}
|
||||
|
||||
if (device != nullptr) {
|
||||
PrimaryDevice *d = device.release();
|
||||
|
||||
d->SetId(id);
|
||||
|
||||
devices.emplace(id, d);
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
return device;
|
||||
}
|
||||
|
||||
const unordered_set<uint32_t>& DeviceFactory::GetSectorSizes(PbDeviceType type) const
|
||||
|
@ -260,7 +209,7 @@ list<string> DeviceFactory::GetNetworkInterfaces() const
|
|||
{
|
||||
list<string> network_interfaces;
|
||||
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
ifaddrs *addrs;
|
||||
getifaddrs(&addrs);
|
||||
ifaddrs *tmp = addrs;
|
||||
|
@ -268,10 +217,10 @@ list<string> DeviceFactory::GetNetworkInterfaces() const
|
|||
while (tmp) {
|
||||
if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_PACKET &&
|
||||
strcmp(tmp->ifa_name, "lo") && strcmp(tmp->ifa_name, "rascsi_bridge")) {
|
||||
int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
const int fd = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
|
||||
ifreq ifr = {};
|
||||
strcpy(ifr.ifr_name, tmp->ifa_name);
|
||||
strcpy(ifr.ifr_name, tmp->ifa_name); //NOSONAR Using strcpy is safe here
|
||||
// Only list interfaces that are up
|
||||
if (!ioctl(fd, SIOCGIFFLAGS, &ifr) && (ifr.ifr_flags & IFF_UP)) {
|
||||
network_interfaces.emplace_back(tmp->ifa_name);
|
||||
|
|
|
@ -11,50 +11,46 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include "rascsi_interface.pb.h"
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace rascsi_interface; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
using namespace rascsi_interface;
|
||||
|
||||
class ControllerManager;
|
||||
class PrimaryDevice;
|
||||
|
||||
class DeviceFactory
|
||||
{
|
||||
const string DEFAULT_IP = "10.10.20.1/24"; //NOSONAR This hardcoded IP address is safe
|
||||
|
||||
public:
|
||||
|
||||
DeviceFactory();
|
||||
~DeviceFactory() = default;
|
||||
DeviceFactory(DeviceFactory&) = delete;
|
||||
DeviceFactory& operator=(const DeviceFactory&) = delete;
|
||||
|
||||
PrimaryDevice *CreateDevice(PbDeviceType, const string&, int);
|
||||
void DeleteDevice(const PrimaryDevice&) const;
|
||||
void DeleteAllDevices() const;
|
||||
const PrimaryDevice *GetDeviceByIdAndLun(int, int) const;
|
||||
list<PrimaryDevice *> GetAllDevices() const;
|
||||
shared_ptr<PrimaryDevice> CreateDevice(const ControllerManager&, PbDeviceType, int, const string&);
|
||||
PbDeviceType GetTypeForFile(const string&) const;
|
||||
const unordered_set<uint32_t>& GetSectorSizes(PbDeviceType type) const;
|
||||
const unordered_set<uint32_t>& GetSectorSizes(const string&) const;
|
||||
const unordered_map<string, string>& GetDefaultParams(PbDeviceType type) const;
|
||||
list<string> GetNetworkInterfaces() const;
|
||||
unordered_map<string, PbDeviceType> GetExtensionMapping() const { return extension_mapping; }
|
||||
const unordered_map<string, PbDeviceType>& GetExtensionMapping() const { return extension_mapping; }
|
||||
|
||||
private:
|
||||
|
||||
string GetExtension(const string&) const;
|
||||
|
||||
unordered_map<PbDeviceType, unordered_set<uint32_t>> sector_sizes;
|
||||
|
||||
unordered_map<PbDeviceType, unordered_map<string, string>> default_params;
|
||||
|
||||
unordered_map<string, PbDeviceType> extension_mapping;
|
||||
|
||||
string GetExtension(const string&) const;
|
||||
|
||||
static std::multimap<int, unique_ptr<PrimaryDevice>> devices;
|
||||
unordered_map<string, PbDeviceType> device_mapping;
|
||||
|
||||
unordered_set<uint32_t> empty_set;
|
||||
unordered_map<string, string> empty_map;
|
||||
|
|
|
@ -14,9 +14,7 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "fileio.h"
|
||||
#include "file_support.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "dispatcher.h"
|
||||
#include "scsi_command_util.h"
|
||||
|
@ -25,7 +23,9 @@
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
Disk::Disk(const string& id) : ModePageDevice(id)
|
||||
unordered_map<string, id_set> Disk::reserved_files;
|
||||
|
||||
Disk::Disk(const string& type, int lun) : ModePageDevice(type, lun)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdRezero, "Rezero", &Disk::Rezero);
|
||||
dispatcher.Add(scsi_command::eCmdFormat, "FormatUnit", &Disk::FormatUnit);
|
||||
|
@ -73,7 +73,7 @@ bool Disk::Dispatch(scsi_command cmd)
|
|||
|
||||
is_medium_changed = false;
|
||||
|
||||
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
}
|
||||
|
||||
// The superclass handles the less specific commands
|
||||
|
@ -88,7 +88,9 @@ bool Disk::Dispatch(scsi_command cmd)
|
|||
//---------------------------------------------------------------------------
|
||||
void Disk::Open(const Filepath& path)
|
||||
{
|
||||
assert(blocks > 0);
|
||||
if (blocks == 0) {
|
||||
throw io_exception("Disk has 0 blocks");
|
||||
}
|
||||
|
||||
SetReady(true);
|
||||
|
||||
|
@ -108,10 +110,17 @@ void Disk::Open(const Filepath& path)
|
|||
SetLocked(false);
|
||||
}
|
||||
|
||||
void Disk::SetUpCache(const Filepath& path, off_t image_offset)
|
||||
void Disk::SetUpCache(const Filepath& path, off_t image_offset, bool raw)
|
||||
{
|
||||
assert(cache == nullptr);
|
||||
cache = make_unique<DiskCache>(path, size_shift_count, (uint32_t)blocks, image_offset);
|
||||
cache->SetRawMode(raw);
|
||||
}
|
||||
|
||||
void Disk::ResizeCache(const Filepath& path, bool raw)
|
||||
{
|
||||
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)blocks));
|
||||
cache->SetRawMode(raw);
|
||||
}
|
||||
|
||||
void Disk::FlushCache()
|
||||
|
@ -132,7 +141,7 @@ void Disk::FormatUnit()
|
|||
|
||||
// FMTDATA=1 is not supported (but OK if there is no DEFECT LIST)
|
||||
if ((ctrl->cmd[1] & 0x10) != 0 && ctrl->cmd[4] != 0) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
|
@ -178,7 +187,7 @@ void Disk::ReadWriteLong10()
|
|||
{
|
||||
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
|
||||
if (GetInt16(ctrl->cmd, 7) != 0) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
ValidateBlockAddress(RW10);
|
||||
|
@ -190,7 +199,7 @@ void Disk::ReadWriteLong16()
|
|||
{
|
||||
// Transfer lengths other than 0 are not supported, which is compliant with the SCSI standard
|
||||
if (GetInt16(ctrl->cmd, 12) != 0) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
ValidateBlockAddress(RW16);
|
||||
|
@ -262,14 +271,14 @@ void Disk::Verify16()
|
|||
|
||||
void Disk::StartStopUnit()
|
||||
{
|
||||
bool start = ctrl->cmd[4] & 0x01;
|
||||
bool load = ctrl->cmd[4] & 0x02;
|
||||
const bool start = ctrl->cmd[4] & 0x01;
|
||||
const bool load = ctrl->cmd[4] & 0x02;
|
||||
|
||||
if (load) {
|
||||
LOGTRACE("%s", start ? "Loading medium" : "Ejecting medium")
|
||||
LOGTRACE(start ? "Loading medium" : "Ejecting medium")
|
||||
}
|
||||
else {
|
||||
LOGTRACE("%s", start ? "Starting unit" : "Stopping unit")
|
||||
LOGTRACE(start ? "Starting unit" : "Stopping unit")
|
||||
|
||||
SetStopped(!start);
|
||||
}
|
||||
|
@ -281,12 +290,12 @@ void Disk::StartStopUnit()
|
|||
if (load) {
|
||||
if (IsLocked()) {
|
||||
// Cannot be ejected because it is locked
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LOAD_OR_EJECT_FAILED);
|
||||
}
|
||||
|
||||
// Eject
|
||||
if (!Eject(false)) {
|
||||
throw scsi_error_exception();
|
||||
throw scsi_exception();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -298,12 +307,12 @@ void Disk::SendDiagnostic()
|
|||
{
|
||||
// Do not support PF bit
|
||||
if (ctrl->cmd[1] & 0x10) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Do not support parameter list
|
||||
if ((ctrl->cmd[3] != 0) || (ctrl->cmd[4] != 0)) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
|
@ -313,9 +322,9 @@ void Disk::PreventAllowMediumRemoval()
|
|||
{
|
||||
CheckReady();
|
||||
|
||||
bool lock = ctrl->cmd[4] & 0x01;
|
||||
const bool lock = ctrl->cmd[4] & 0x01;
|
||||
|
||||
LOGTRACE("%s", lock ? "Locking medium" : "Unlocking medium")
|
||||
LOGTRACE(lock ? "Locking medium" : "Unlocking medium")
|
||||
|
||||
SetLocked(lock);
|
||||
|
||||
|
@ -331,7 +340,7 @@ void Disk::SynchronizeCache()
|
|||
|
||||
void Disk::ReadDefectData10()
|
||||
{
|
||||
size_t allocation_length = min((size_t)GetInt16(ctrl->cmd, 7), (size_t)4);
|
||||
const size_t allocation_length = min((size_t)GetInt16(ctrl->cmd, 7), (size_t)4);
|
||||
|
||||
// The defect list is empty
|
||||
fill_n(controller->GetBuffer().begin(), allocation_length, 0);
|
||||
|
@ -346,30 +355,28 @@ void Disk::MediumChanged()
|
|||
is_medium_changed = true;
|
||||
}
|
||||
else {
|
||||
LOGWARN("%s Medium change requested for non-reomvable medium", __PRETTY_FUNCTION__)
|
||||
LOGERROR("Medium change requested for non-removable medium")
|
||||
}
|
||||
}
|
||||
|
||||
bool Disk::Eject(bool force)
|
||||
{
|
||||
bool status = super::Eject(force);
|
||||
const bool status = super::Eject(force);
|
||||
if (status) {
|
||||
FlushCache();
|
||||
cache.reset();
|
||||
|
||||
// The image file for this drive is not in use anymore
|
||||
if (auto file_support = dynamic_cast<FileSupport *>(this); file_support) {
|
||||
file_support->UnreserveFile();
|
||||
}
|
||||
UnreserveFile();
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const
|
||||
int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
|
||||
{
|
||||
// Get length, clear buffer
|
||||
auto length = (int)min((size_t)max_length, (size_t)cdb[4]);
|
||||
const auto length = (int)min(buf.size(), (size_t)cdb[4]);
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// DEVICE SPECIFIC PARAMETER
|
||||
|
@ -378,7 +385,7 @@ int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
|
|||
}
|
||||
|
||||
// Basic information
|
||||
int info_size = 4;
|
||||
int size = 4;
|
||||
|
||||
// Add block descriptor if DBD is 0
|
||||
if ((cdb[1] & 0x08) == 0) {
|
||||
|
@ -388,33 +395,33 @@ int Disk::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
|
|||
// Only if ready
|
||||
if (IsReady()) {
|
||||
// Short LBA mode parameter block descriptor (number of blocks and block length)
|
||||
SetInt32(buf, 4, (uint32_t)GetBlockCount());
|
||||
SetInt32(buf, 4, (uint32_t)blocks);
|
||||
SetInt32(buf, 8, GetSectorSizeInBytes());
|
||||
}
|
||||
|
||||
info_size = 12;
|
||||
size = 12;
|
||||
}
|
||||
|
||||
info_size += super::AddModePages(cdb, buf, info_size, length - info_size);
|
||||
if (info_size > 255) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
size += super::AddModePages(cdb, buf, size, length - size);
|
||||
if (size > 255) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Do not return more than ALLOCATION LENGTH bytes
|
||||
if (info_size > length) {
|
||||
info_size = length;
|
||||
if (size > length) {
|
||||
size = length;
|
||||
}
|
||||
|
||||
// Final setting of mode data length
|
||||
buf[0] = (BYTE)info_size;
|
||||
buf[0] = (BYTE)size;
|
||||
|
||||
return info_size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const
|
||||
int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
|
||||
{
|
||||
// Get length, clear buffer
|
||||
auto length = (int)min((size_t)max_length, (size_t)GetInt16(cdb, 7));
|
||||
const auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// DEVICE SPECIFIC PARAMETER
|
||||
|
@ -423,11 +430,11 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
|
|||
}
|
||||
|
||||
// Basic Information
|
||||
int info_size = 8;
|
||||
int size = 8;
|
||||
|
||||
// Add block descriptor if DBD is 0, only if ready
|
||||
if ((cdb[1] & 0x08) == 0 && IsReady()) {
|
||||
uint64_t disk_blocks = GetBlockCount();
|
||||
uint64_t disk_blocks = blocks;
|
||||
uint32_t disk_size = GetSectorSizeInBytes();
|
||||
|
||||
// Check LLBAA for short or long block descriptor
|
||||
|
@ -439,7 +446,7 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
|
|||
SetInt32(buf, 8, (uint32_t)disk_blocks);
|
||||
SetInt32(buf, 12, disk_size);
|
||||
|
||||
info_size = 16;
|
||||
size = 16;
|
||||
}
|
||||
else {
|
||||
// Mode parameter header, LONGLBA
|
||||
|
@ -452,24 +459,24 @@ int Disk::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length)
|
|||
SetInt64(buf, 8, disk_blocks);
|
||||
SetInt32(buf, 20, disk_size);
|
||||
|
||||
info_size = 24;
|
||||
size = 24;
|
||||
}
|
||||
}
|
||||
|
||||
info_size += super::AddModePages(cdb, buf, info_size, length - info_size);
|
||||
if (info_size > 65535) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
size += super::AddModePages(cdb, buf, size, length - size);
|
||||
if (size > 65535) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Do not return more than ALLOCATION LENGTH bytes
|
||||
if (info_size > length) {
|
||||
info_size = length;
|
||||
if (size > length) {
|
||||
size = length;
|
||||
}
|
||||
|
||||
// Final setting of mode data length
|
||||
SetInt16(buf, 0, info_size);
|
||||
SetInt16(buf, 0, size);
|
||||
|
||||
return info_size;
|
||||
return size;
|
||||
}
|
||||
|
||||
void Disk::SetUpModePages(map<int, vector<byte>>& pages, int page, bool changeable) const
|
||||
|
@ -614,12 +621,12 @@ int Disk::Read(const vector<int>&, vector<BYTE>& buf, uint64_t block)
|
|||
|
||||
// Error if the total number of blocks is exceeded
|
||||
if (block >= blocks) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// leave it to the cache
|
||||
if (!cache->ReadSector(buf, (uint32_t)block)) {
|
||||
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
|
||||
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::READ_FAULT);
|
||||
}
|
||||
|
||||
// Success
|
||||
|
@ -632,12 +639,12 @@ int Disk::WriteCheck(uint64_t block)
|
|||
|
||||
// Error if the total number of blocks is exceeded
|
||||
if (block >= blocks) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Error if write protected
|
||||
if (IsProtected()) {
|
||||
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
|
||||
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
|
||||
}
|
||||
|
||||
// Success
|
||||
|
@ -651,22 +658,22 @@ void Disk::Write(const vector<int>&, const vector<BYTE>& buf, uint64_t block)
|
|||
|
||||
// Error if not ready
|
||||
if (!IsReady()) {
|
||||
throw scsi_error_exception(sense_key::NOT_READY);
|
||||
throw scsi_exception(sense_key::NOT_READY);
|
||||
}
|
||||
|
||||
// Error if the total number of blocks is exceeded
|
||||
if (block >= blocks) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
}
|
||||
|
||||
// Error if write protected
|
||||
if (IsProtected()) {
|
||||
throw scsi_error_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
|
||||
throw scsi_exception(sense_key::DATA_PROTECT, asc::WRITE_PROTECTED);
|
||||
}
|
||||
|
||||
// Leave it to the cache
|
||||
if (!cache->WriteSector(buf, (uint32_t)block)) {
|
||||
throw scsi_error_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
|
||||
throw scsi_exception(sense_key::MEDIUM_ERROR, asc::WRITE_FAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -700,7 +707,7 @@ void Disk::ReadCapacity10()
|
|||
CheckReady();
|
||||
|
||||
if (blocks == 0) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
vector<BYTE>& buf = controller->GetBuffer();
|
||||
|
@ -728,7 +735,7 @@ void Disk::ReadCapacity16()
|
|||
CheckReady();
|
||||
|
||||
if (blocks == 0) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
vector<BYTE>& buf = controller->GetBuffer();
|
||||
|
@ -763,7 +770,7 @@ void Disk::ReadCapacity16_ReadLong16()
|
|||
break;
|
||||
|
||||
default:
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -796,14 +803,12 @@ void Disk::Release()
|
|||
|
||||
void Disk::ValidateBlockAddress(access_mode mode) const
|
||||
{
|
||||
uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
|
||||
const uint64_t block = mode == RW16 ? GetInt64(ctrl->cmd, 2) : GetInt32(ctrl->cmd, 2);
|
||||
|
||||
uint64_t capacity = GetBlockCount();
|
||||
|
||||
if (block > capacity) {
|
||||
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
|
||||
if (block > blocks) {
|
||||
LOGTRACE("%s", ("Capacity of " + to_string(blocks) + " block(s) exceeded: Trying to access block "
|
||||
+ to_string(block)).c_str())
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -834,10 +839,10 @@ bool Disk::CheckAndGetStartAndCount(uint64_t& start, uint32_t& count, access_mod
|
|||
LOGTRACE("%s READ/WRITE/VERIFY/SEEK command record=$%08X blocks=%d", __PRETTY_FUNCTION__, (uint32_t)start, count)
|
||||
|
||||
// Check capacity
|
||||
if (uint64_t capacity = GetBlockCount(); start > capacity || start + count > capacity) {
|
||||
if (uint64_t capacity = blocks; start > capacity || start + count > capacity) {
|
||||
LOGTRACE("%s", ("Capacity of " + to_string(capacity) + " block(s) exceeded: Trying to access block "
|
||||
+ to_string(start) + ", block count " + to_string(count)).c_str())
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
}
|
||||
|
||||
// Do not process 0 blocks
|
||||
|
@ -879,7 +884,7 @@ void Disk::SetSectorSizeInBytes(uint32_t size_in_bytes)
|
|||
break;
|
||||
|
||||
default:
|
||||
assert(false);
|
||||
throw io_exception("Invalid block size of " + to_string(size_in_bytes) + " bytes");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -900,3 +905,43 @@ bool Disk::SetConfiguredSectorSize(const DeviceFactory& device_factory, uint32_t
|
|||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Disk::ReserveFile(const Filepath& path, int id, int lun) const
|
||||
{
|
||||
reserved_files[path.GetPath()] = make_pair(id, lun);
|
||||
}
|
||||
|
||||
void Disk::UnreserveFile() const
|
||||
{
|
||||
reserved_files.erase(diskpath.GetPath());
|
||||
}
|
||||
|
||||
bool Disk::GetIdsForReservedFile(const Filepath& path, int& id, int& lun)
|
||||
{
|
||||
if (const auto& it = reserved_files.find(path.GetPath()); it != reserved_files.end()) {
|
||||
id = it->second.first;
|
||||
lun = it->second.second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Disk::UnreserveAll()
|
||||
{
|
||||
reserved_files.clear();
|
||||
}
|
||||
|
||||
bool Disk::FileExists(const Filepath& filepath)
|
||||
{
|
||||
try {
|
||||
// Disk::Open closes the file in case it exists
|
||||
Open(filepath);
|
||||
}
|
||||
catch(const file_not_found_exception&) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -25,12 +25,18 @@
|
|||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
class Disk : public ModePageDevice, public ScsiBlockCommands
|
||||
using namespace std;
|
||||
|
||||
using id_set = pair<int, int>;
|
||||
|
||||
class Disk : public ModePageDevice, private ScsiBlockCommands
|
||||
{
|
||||
enum access_mode { RW6, RW10, RW16, SEEK6, SEEK10 };
|
||||
|
||||
Dispatcher<Disk> dispatcher;
|
||||
|
||||
unique_ptr<DiskCache> cache;
|
||||
|
||||
// The supported configurable sector sizes, empty if not configurable
|
||||
unordered_set<uint32_t> sector_sizes;
|
||||
uint32_t configured_sector_size = 0;
|
||||
|
@ -43,12 +49,15 @@ class Disk : public ModePageDevice, public ScsiBlockCommands
|
|||
|
||||
bool is_medium_changed = false;
|
||||
|
||||
Filepath diskpath;
|
||||
|
||||
// The list of image files in use and the IDs and LUNs using these files
|
||||
static unordered_map<string, id_set> reserved_files;
|
||||
|
||||
public:
|
||||
|
||||
explicit Disk(const string&);
|
||||
Disk(const string&, int);
|
||||
~Disk() override;
|
||||
Disk(Disk&) = delete;
|
||||
Disk& operator=(const Disk&) = delete;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
||||
|
@ -67,6 +76,18 @@ public:
|
|||
uint64_t GetBlockCount() const { return blocks; }
|
||||
void FlushCache() override;
|
||||
|
||||
virtual void Open(const Filepath&);
|
||||
void GetPath(Filepath& path) const { path = diskpath; }
|
||||
|
||||
void ReserveFile(const Filepath&, int, int) const;
|
||||
void UnreserveFile() const;
|
||||
static void UnreserveAll();
|
||||
bool FileExists(const Filepath&);
|
||||
|
||||
static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; }
|
||||
static void SetReservedFiles(const unordered_map<string, id_set>& files_in_use) { reserved_files = files_in_use; }
|
||||
static bool GetIdsForReservedFile(const Filepath&, int&, int&);
|
||||
|
||||
private:
|
||||
|
||||
using super = ModePageDevice;
|
||||
|
@ -105,13 +126,13 @@ private:
|
|||
void ValidateBlockAddress(access_mode) const;
|
||||
bool CheckAndGetStartAndCount(uint64_t&, uint32_t&, access_mode) const;
|
||||
|
||||
int ModeSense6(const vector<int>&, vector<BYTE>&, int) const override;
|
||||
int ModeSense10(const vector<int>&, vector<BYTE>&, int) const override;
|
||||
int ModeSense6(const vector<int>&, vector<BYTE>&) const override;
|
||||
int ModeSense10(const vector<int>&, vector<BYTE>&) const override;
|
||||
|
||||
protected:
|
||||
|
||||
virtual void Open(const Filepath&);
|
||||
void SetUpCache(const Filepath&, off_t = 0);
|
||||
void SetUpCache(const Filepath&, off_t, bool = false);
|
||||
void ResizeCache(const Filepath&, bool);
|
||||
|
||||
void SetUpModePages(map<int, vector<byte>>&, int, bool) const override;
|
||||
virtual void AddErrorPage(map<int, vector<byte>>&, bool) const;
|
||||
|
@ -126,6 +147,5 @@ protected:
|
|||
void SetSectorSizeShiftCount(uint32_t count) { size_shift_count = count; }
|
||||
uint32_t GetConfiguredSectorSize() const;
|
||||
void SetBlockCount(uint64_t b) { blocks = b; }
|
||||
|
||||
unique_ptr<DiskCache> cache;
|
||||
void SetPath(const Filepath& path) { diskpath = path; }
|
||||
};
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include "disk_track.h"
|
||||
#include "disk_cache.h"
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
|
||||
DiskCache::DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff)
|
||||
: sec_size(size), sec_blocks(blocks), imgoffset(imgoff)
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
class DiskCache
|
||||
{
|
||||
|
@ -36,8 +36,6 @@ public:
|
|||
|
||||
DiskCache(const Filepath& path, int size, uint32_t blocks, off_t imgoff = 0);
|
||||
~DiskCache() = default;
|
||||
DiskCache(DiskCache&) = delete;
|
||||
DiskCache& operator=(const DiskCache&) = delete;
|
||||
|
||||
void SetRawMode(bool b) { cd_raw = b; } // CD-ROM raw mode setting
|
||||
|
||||
|
|
|
@ -21,9 +21,7 @@
|
|||
DiskTrack::~DiskTrack()
|
||||
{
|
||||
// Release memory, but do not save automatically
|
||||
if (dt.buffer) {
|
||||
free(dt.buffer);
|
||||
}
|
||||
free(dt.buffer);
|
||||
}
|
||||
|
||||
void DiskTrack::Init(int track, int size, int sectors, bool raw, off_t imgoff)
|
||||
|
@ -70,7 +68,7 @@ bool DiskTrack::Load(const Filepath& path)
|
|||
offset += dt.imgoffset;
|
||||
|
||||
// Calculate length (data size of this track)
|
||||
int length = dt.sectors << dt.size;
|
||||
const int length = dt.sectors << dt.size;
|
||||
|
||||
// Allocate buffer memory
|
||||
assert((dt.sectors > 0) && (dt.sectors <= 0x100));
|
||||
|
@ -87,7 +85,7 @@ bool DiskTrack::Load(const Filepath& path)
|
|||
}
|
||||
|
||||
// Reallocate if the buffer length is different
|
||||
if (dt.length != (DWORD)length) {
|
||||
if (dt.length != (uint32_t)length) {
|
||||
free(dt.buffer);
|
||||
if (posix_memalign((void **)&dt.buffer, 512, ((length + 511) / 512) * 512)) {
|
||||
LOGWARN("%s posix_memalign failed", __PRETTY_FUNCTION__)
|
||||
|
@ -169,7 +167,7 @@ bool DiskTrack::Save(const Filepath& path)
|
|||
offset += dt.imgoffset;
|
||||
|
||||
// Calculate length per sector
|
||||
int length = 1 << dt.size;
|
||||
const int length = 1 << dt.size;
|
||||
|
||||
// Open file
|
||||
Fileio fio;
|
||||
|
@ -267,8 +265,8 @@ bool DiskTrack::WriteSector(const vector<BYTE>& buf, int sec)
|
|||
}
|
||||
|
||||
// Calculate offset and length
|
||||
int offset = sec << dt.size;
|
||||
int length = 1 << dt.size;
|
||||
const int offset = sec << dt.size;
|
||||
const int length = 1 << dt.size;
|
||||
|
||||
// Compare
|
||||
assert(dt.buffer);
|
||||
|
|
|
@ -16,9 +16,10 @@
|
|||
#pragma once
|
||||
|
||||
#include "filepath.h"
|
||||
#include <cstdlib>
|
||||
#include <vector>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
class DiskTrack
|
||||
{
|
||||
|
@ -26,7 +27,7 @@ class DiskTrack
|
|||
int track; // Track Number
|
||||
int size; // Sector Size (8=256, 9=512, 10=1024, 11=2048, 12=4096)
|
||||
int sectors; // Number of sectors(<0x100)
|
||||
DWORD length; // Data buffer length
|
||||
uint32_t length; // Data buffer length
|
||||
BYTE *buffer; // Data buffer
|
||||
bool init; // Is it initilized?
|
||||
bool changed; // Changed flag
|
||||
|
|
|
@ -14,8 +14,8 @@
|
|||
#include "log.h"
|
||||
#include <unordered_map>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace scsi_defs; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
using namespace scsi_defs;
|
||||
|
||||
template<class T>
|
||||
class Dispatcher
|
||||
|
@ -24,8 +24,6 @@ public:
|
|||
|
||||
Dispatcher() = default;
|
||||
~Dispatcher() = default;
|
||||
Dispatcher(Dispatcher&) = delete;
|
||||
Dispatcher& operator=(const Dispatcher&) = delete;
|
||||
|
||||
using operation = void (T::*)();
|
||||
using command_t = struct _command_t {
|
||||
|
|
|
@ -1,41 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "file_support.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
unordered_map<string, id_set> FileSupport::reserved_files;
|
||||
|
||||
void FileSupport::ReserveFile(const Filepath& path, int id, int lun) const
|
||||
{
|
||||
reserved_files[path.GetPath()] = make_pair(id, lun);
|
||||
}
|
||||
|
||||
void FileSupport::UnreserveFile() const
|
||||
{
|
||||
reserved_files.erase(diskpath.GetPath());
|
||||
}
|
||||
|
||||
bool FileSupport::GetIdsForReservedFile(const Filepath& path, int& id, int& lun)
|
||||
{
|
||||
if (const auto& it = reserved_files.find(path.GetPath()); it != reserved_files.end()) {
|
||||
id = it->second.first;
|
||||
lun = it->second.second;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void FileSupport::UnreserveAll()
|
||||
{
|
||||
reserved_files.clear();
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021-2022 Uwe Seimet
|
||||
//
|
||||
// Devices inheriting from FileSupport support image files
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include "filepath.h"
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
|
||||
using id_set = pair<int, int>;
|
||||
|
||||
class FileSupport
|
||||
{
|
||||
Filepath diskpath;
|
||||
|
||||
// The list of image files in use and the IDs and LUNs using these files
|
||||
static unordered_map<string, id_set> reserved_files;
|
||||
|
||||
public:
|
||||
|
||||
FileSupport() = default;
|
||||
virtual ~FileSupport() = default;
|
||||
FileSupport(FileSupport&) = delete;
|
||||
FileSupport& operator=(const FileSupport&) = delete;
|
||||
|
||||
void GetPath(Filepath& path) const { path = diskpath; }
|
||||
void SetPath(const Filepath& path) { diskpath = path; }
|
||||
|
||||
void ReserveFile(const Filepath&, int, int) const;
|
||||
void UnreserveFile() const;
|
||||
static void UnreserveAll();
|
||||
|
||||
static unordered_map<string, id_set> GetReservedFiles() { return reserved_files; }
|
||||
static void SetReservedFiles(const unordered_map<string, id_set>& files_in_use)
|
||||
{ FileSupport::reserved_files = files_in_use; }
|
||||
static bool GetIdsForReservedFile(const Filepath&, int&, int&);
|
||||
|
||||
virtual void Open(const Filepath&) = 0;
|
||||
};
|
|
@ -20,8 +20,9 @@
|
|||
// c) start && load (LOAD): Reboot the Raspberry Pi
|
||||
//
|
||||
|
||||
#include "controllers/controller_manager.h"
|
||||
#include "controllers/scsi_controller.h"
|
||||
#include "rascsi_exceptions.h"
|
||||
#include "device_factory.h"
|
||||
#include "scsi_command_util.h"
|
||||
#include "dispatcher.h"
|
||||
#include "host_services.h"
|
||||
|
@ -30,10 +31,14 @@
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
HostServices::HostServices(const DeviceFactory& factory) : ModePageDevice("SCHS"), device_factory(factory)
|
||||
HostServices::HostServices(int lun, const ControllerManager& manager)
|
||||
: ModePageDevice("SCHS", lun), controller_manager(manager)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &HostServices::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdStartStop, "StartStopUnit", &HostServices::StartStopUnit);
|
||||
|
||||
SetReady(true);
|
||||
SetReset(false);
|
||||
}
|
||||
|
||||
bool HostServices::Dispatch(scsi_command cmd)
|
||||
|
@ -55,45 +60,40 @@ vector<byte> HostServices::InquiryInternal() const
|
|||
|
||||
void HostServices::StartStopUnit()
|
||||
{
|
||||
bool start = ctrl->cmd[4] & 0x01;
|
||||
bool load = ctrl->cmd[4] & 0x02;
|
||||
const bool start = ctrl->cmd[4] & 0x01;
|
||||
const bool load = ctrl->cmd[4] & 0x02;
|
||||
|
||||
if (!start) {
|
||||
// Flush any caches
|
||||
for (PrimaryDevice *device : device_factory.GetAllDevices()) {
|
||||
for (const auto& device : controller_manager.GetAllDevices()) {
|
||||
device->FlushCache();
|
||||
}
|
||||
|
||||
if (load) {
|
||||
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_PI);
|
||||
controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_PI);
|
||||
}
|
||||
else {
|
||||
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::STOP_RASCSI);
|
||||
controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::STOP_RASCSI);
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
return;
|
||||
}
|
||||
else if (load) {
|
||||
controller->ScheduleShutdown(AbstractController::rascsi_shutdown_mode::RESTART_PI);
|
||||
}
|
||||
else {
|
||||
if (load) {
|
||||
controller->ScheduleShutdown(ScsiController::rascsi_shutdown_mode::RESTART_PI);
|
||||
|
||||
EnterStatusPhase();
|
||||
return;
|
||||
}
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
EnterStatusPhase();
|
||||
}
|
||||
|
||||
int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const
|
||||
int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf) const
|
||||
{
|
||||
// Block descriptors cannot be returned
|
||||
if (!(cdb[1] & 0x08)) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
auto length = (int)min((size_t)max_length, (size_t)cdb[4]);
|
||||
const auto length = (int)min(buf.size(), (size_t)cdb[4]);
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// Basic Information
|
||||
|
@ -101,7 +101,7 @@ int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_
|
|||
|
||||
size += super::AddModePages(cdb, buf, size, length - size);
|
||||
if (size > 255) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Do not return more than ALLOCATION LENGTH bytes
|
||||
|
@ -114,14 +114,14 @@ int HostServices::ModeSense6(const vector<int>& cdb, vector<BYTE>& buf, int max_
|
|||
return size;
|
||||
}
|
||||
|
||||
int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max_length) const
|
||||
int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf) const
|
||||
{
|
||||
// Block descriptors cannot be returned
|
||||
if (!(cdb[1] & 0x08)) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
auto length = (int)min((size_t)max_length, (size_t)GetInt16(cdb, 7));
|
||||
const auto length = (int)min(buf.size(), (size_t)GetInt16(cdb, 7));
|
||||
fill_n(buf.begin(), length, 0);
|
||||
|
||||
// Basic Information
|
||||
|
@ -129,7 +129,7 @@ int HostServices::ModeSense10(const vector<int>& cdb, vector<BYTE>& buf, int max
|
|||
|
||||
size += super::AddModePages(cdb, buf, size, length - size);
|
||||
if (size > 65535) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Do not return more than ALLOCATION LENGTH bytes
|
||||
|
|
|
@ -15,23 +15,20 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
class DeviceFactory;
|
||||
class ControllerManager;
|
||||
|
||||
class HostServices: public ModePageDevice
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
explicit HostServices(const DeviceFactory&);
|
||||
HostServices(int, const ControllerManager&);
|
||||
~HostServices() override = default;
|
||||
HostServices(HostServices&) = delete;
|
||||
HostServices& operator=(const HostServices&) = delete;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
||||
vector<byte> InquiryInternal() const override;
|
||||
void TestUnitReady() override;
|
||||
void StartStopUnit();
|
||||
|
||||
bool SupportsFile() const override { return false; }
|
||||
|
||||
|
@ -58,10 +55,11 @@ private:
|
|||
|
||||
Dispatcher<HostServices> dispatcher;
|
||||
|
||||
int ModeSense6(const vector<int>&, vector<BYTE>&, int) const override;
|
||||
int ModeSense10(const vector<int>&, vector<BYTE>&, int) const override;
|
||||
const ControllerManager& controller_manager;
|
||||
|
||||
void StartStopUnit();
|
||||
int ModeSense6(const vector<int>&, vector<BYTE>&) const override;
|
||||
int ModeSense10(const vector<int>&, vector<BYTE>&) const override;
|
||||
|
||||
void AddRealtimeClockPage(map<int, vector<byte>>&, bool) const;
|
||||
|
||||
const DeviceFactory& device_factory;
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@ using namespace std;
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
ModePageDevice::ModePageDevice(const string& id) : PrimaryDevice(id)
|
||||
ModePageDevice::ModePageDevice(const string& type, int lun) : PrimaryDevice(type, lun)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdModeSense6, "ModeSense6", &ModePageDevice::ModeSense6);
|
||||
dispatcher.Add(scsi_command::eCmdModeSense10, "ModeSense10", &ModePageDevice::ModeSense10);
|
||||
|
@ -40,10 +40,10 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool changeable = (cdb[2] & 0xc0) == 0x40;
|
||||
const bool changeable = (cdb[2] & 0xc0) == 0x40;
|
||||
|
||||
// Get page code (0x3f means all pages)
|
||||
int page = cdb[2] & 0x3f;
|
||||
const int page = cdb[2] & 0x3f;
|
||||
|
||||
LOGTRACE("%s Requesting mode page $%02X", __PRETTY_FUNCTION__, page)
|
||||
|
||||
|
@ -53,7 +53,7 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
|
|||
|
||||
if (pages.empty()) {
|
||||
LOGTRACE("%s Unsupported mode page $%02X", __PRETTY_FUNCTION__, page)
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Holds all mode page data
|
||||
|
@ -63,14 +63,14 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
|
|||
for (auto const& [index, data] : pages) {
|
||||
// The specification mandates that page 0 must be returned after all others
|
||||
if (index) {
|
||||
size_t offset = result.size();
|
||||
const size_t off = result.size();
|
||||
|
||||
// Page data
|
||||
result.insert(result.end(), data.begin(), data.end());
|
||||
// Page code, PS bit may already have been set
|
||||
result[offset] |= (byte)index;
|
||||
result[off] |= (byte)index;
|
||||
// Page payload size
|
||||
result[offset + 1] = (byte)(data.size() - 2);
|
||||
result[off + 1] = (byte)(data.size() - 2);
|
||||
}
|
||||
else {
|
||||
page0 = data;
|
||||
|
@ -79,10 +79,12 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
|
|||
|
||||
// Page 0 must be last
|
||||
if (!page0.empty()) {
|
||||
size_t off = result.size();
|
||||
|
||||
// Page data
|
||||
result.insert(result.end(), page0.begin(), page0.end());
|
||||
// Page payload size
|
||||
result[result.size() + 1] = (byte)(page0.size() - 2);
|
||||
result[off + 1] = (byte)(page0.size() - 2);
|
||||
}
|
||||
|
||||
// Do not return more than the requested number of bytes
|
||||
|
@ -94,21 +96,21 @@ int ModePageDevice::AddModePages(const vector<int>& cdb, vector<BYTE>& buf, int
|
|||
|
||||
void ModePageDevice::ModeSense6()
|
||||
{
|
||||
ctrl->length = ModeSense6(ctrl->cmd, controller->GetBuffer(), (int)controller->GetBufferSize());
|
||||
ctrl->length = ModeSense6(ctrl->cmd, controller->GetBuffer());
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSense10()
|
||||
{
|
||||
ctrl->length = ModeSense10(ctrl->cmd, controller->GetBuffer(), (int)controller->GetBufferSize());
|
||||
ctrl->length = ModeSense10(ctrl->cmd, controller->GetBuffer());
|
||||
|
||||
EnterDataInPhase();
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect(const vector<int>&, const vector<BYTE>&, int) const
|
||||
{
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_COMMAND_OPERATION_CODE);
|
||||
}
|
||||
|
||||
void ModePageDevice::ModeSelect6()
|
||||
|
@ -130,7 +132,7 @@ int ModePageDevice::ModeSelectCheck(int length) const
|
|||
// Error if save parameters are set for other types than SCHD, SCRM or SCMO
|
||||
// TODO The assumption above is not correct, and this code should be located elsewhere
|
||||
if (GetType() != "SCHD" && GetType() != "SCRM" && GetType() != "SCMO" && (ctrl->cmd[1] & 0x01)) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
return length;
|
||||
|
@ -145,7 +147,7 @@ int ModePageDevice::ModeSelectCheck6() const
|
|||
int ModePageDevice::ModeSelectCheck10() const
|
||||
{
|
||||
// Receive the data specified by the parameter length
|
||||
size_t length = min(controller->GetBufferSize(), (size_t)GetInt16(ctrl->cmd, 7));
|
||||
size_t length = min(controller->GetBuffer().size(), (size_t)GetInt16(ctrl->cmd, 7));
|
||||
|
||||
return ModeSelectCheck((int)length);
|
||||
}
|
||||
|
|
|
@ -18,10 +18,8 @@ class ModePageDevice: public PrimaryDevice
|
|||
{
|
||||
public:
|
||||
|
||||
explicit ModePageDevice(const string&);
|
||||
ModePageDevice(const string&, int);
|
||||
~ModePageDevice()override = default;
|
||||
ModePageDevice(ModePageDevice&) = delete;
|
||||
ModePageDevice& operator=(const ModePageDevice&) = delete;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
||||
|
@ -38,8 +36,8 @@ private:
|
|||
|
||||
Dispatcher<ModePageDevice> dispatcher;
|
||||
|
||||
virtual int ModeSense6(const vector<int>&, vector<BYTE>&, int) const = 0;
|
||||
virtual int ModeSense10(const vector<int>&, vector<BYTE>&, int) const = 0;
|
||||
virtual int ModeSense6(const vector<int>&, vector<BYTE>&) const = 0;
|
||||
virtual int ModeSense10(const vector<int>&, vector<BYTE>&) const = 0;
|
||||
|
||||
void ModeSense6();
|
||||
void ModeSense10();
|
||||
|
|
|
@ -17,7 +17,7 @@ using namespace std;
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
PrimaryDevice::PrimaryDevice(const string& id) : Device(id)
|
||||
PrimaryDevice::PrimaryDevice(const string& type, int lun) : Device(type, lun)
|
||||
{
|
||||
// Mandatory SCSI primary commands
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &PrimaryDevice::TestUnitReady);
|
||||
|
@ -33,6 +33,15 @@ bool PrimaryDevice::Dispatch(scsi_command cmd)
|
|||
return dispatcher.Dispatch(this, cmd);
|
||||
}
|
||||
|
||||
int PrimaryDevice::GetId() const
|
||||
{
|
||||
if (controller == nullptr) {
|
||||
LOGERROR("Device is missing its controller")
|
||||
}
|
||||
|
||||
return controller != nullptr ? controller->GetTargetId() : -1;
|
||||
}
|
||||
|
||||
void PrimaryDevice::SetController(AbstractController *c)
|
||||
{
|
||||
controller = c;
|
||||
|
@ -50,12 +59,12 @@ void PrimaryDevice::Inquiry()
|
|||
{
|
||||
// EVPD and page code check
|
||||
if ((ctrl->cmd[1] & 0x01) || ctrl->cmd[2]) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
vector<byte> buf = InquiryInternal();
|
||||
|
||||
size_t allocation_length = min(buf.size(), (size_t)GetInt16(ctrl->cmd, 3));
|
||||
const size_t allocation_length = min(buf.size(), (size_t)GetInt16(ctrl->cmd, 3));
|
||||
|
||||
memcpy(controller->GetBuffer().data(), buf.data(), allocation_length);
|
||||
ctrl->length = (uint32_t)allocation_length;
|
||||
|
@ -65,7 +74,7 @@ void PrimaryDevice::Inquiry()
|
|||
LOGTRACE("Reporting LUN %d for device ID %d as not supported", lun, GetId())
|
||||
|
||||
// Signal that the requested LUN does not exist
|
||||
controller->GetBuffer()[0] |= 0x7f;
|
||||
controller->GetBuffer().data()[0] = 0x7f;
|
||||
}
|
||||
|
||||
EnterDataInPhase();
|
||||
|
@ -75,13 +84,13 @@ void PrimaryDevice::ReportLuns()
|
|||
{
|
||||
// Only SELECT REPORT mode 0 is supported
|
||||
if (ctrl->cmd[2]) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
uint32_t allocation_length = GetInt32(ctrl->cmd, 6);
|
||||
const uint32_t allocation_length = GetInt32(ctrl->cmd, 6);
|
||||
|
||||
vector<BYTE>& buf = controller->GetBuffer();
|
||||
fill_n(buf.begin(), min(controller->GetBufferSize(), (size_t)allocation_length), 0);
|
||||
fill_n(buf.begin(), min(buf.size(), (size_t)allocation_length), 0);
|
||||
|
||||
uint32_t size = 0;
|
||||
for (int lun = 0; lun < controller->GetMaxLuns(); lun++) {
|
||||
|
@ -115,12 +124,12 @@ void PrimaryDevice::RequestSense()
|
|||
// Do not raise an exception here because the rest of the code must be executed
|
||||
controller->Error(sense_key::ILLEGAL_REQUEST, asc::INVALID_LUN);
|
||||
|
||||
controller->SetStatus(0);
|
||||
controller->SetStatus(status::GOOD);
|
||||
}
|
||||
|
||||
vector<byte> buf = controller->GetDeviceForLun(lun)->HandleRequestSense();
|
||||
|
||||
size_t allocation_length = min(buf.size(), (size_t)ctrl->cmd[4]);
|
||||
const size_t allocation_length = min(buf.size(), (size_t)ctrl->cmd[4]);
|
||||
|
||||
memcpy(controller->GetBuffer().data(), buf.data(), allocation_length);
|
||||
ctrl->length = (uint32_t)allocation_length;
|
||||
|
@ -134,20 +143,20 @@ void PrimaryDevice::CheckReady()
|
|||
if (IsReset()) {
|
||||
SetReset(false);
|
||||
LOGTRACE("%s Device in reset", __PRETTY_FUNCTION__)
|
||||
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET);
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::POWER_ON_OR_RESET);
|
||||
}
|
||||
|
||||
// Not ready if it needs attention
|
||||
if (IsAttn()) {
|
||||
SetAttn(false);
|
||||
LOGTRACE("%s Device in needs attention", __PRETTY_FUNCTION__)
|
||||
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::NOT_READY_TO_READY_CHANGE);
|
||||
}
|
||||
|
||||
// Return status if not ready
|
||||
if (!IsReady()) {
|
||||
LOGTRACE("%s Device not ready", __PRETTY_FUNCTION__)
|
||||
throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
|
||||
throw scsi_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
// Initialization with no error
|
||||
|
@ -171,7 +180,7 @@ vector<byte> PrimaryDevice::HandleInquiry(device_type type, scsi_level level, bo
|
|||
buf[4] = (byte)0x1F;
|
||||
|
||||
// Padded vendor, product, revision
|
||||
memcpy(&buf[8], GetPaddedName().c_str(), 28);
|
||||
memcpy(&buf.data()[8], GetPaddedName().c_str(), 28);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
@ -180,7 +189,7 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
|
|||
{
|
||||
// Return not ready only if there are no errors
|
||||
if (!GetStatusCode() && !IsReady()) {
|
||||
throw scsi_error_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
|
||||
throw scsi_exception(sense_key::NOT_READY, asc::MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
// Set 18 bytes including extended sense data
|
||||
|
@ -195,7 +204,7 @@ vector<byte> PrimaryDevice::HandleRequestSense() const
|
|||
buf[12] = (byte)(GetStatusCode() >> 8);
|
||||
buf[13] = (byte)GetStatusCode();
|
||||
|
||||
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, controller->GetStatus(),
|
||||
LOGTRACE("%s Status $%02X, Sense Key $%02X, ASC $%02X",__PRETTY_FUNCTION__, (int)controller->GetStatus(),
|
||||
(int)buf[2], (int)buf[12])
|
||||
|
||||
return buf;
|
||||
|
|
|
@ -12,27 +12,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "interfaces/scsi_primary_commands.h"
|
||||
#include "controllers/scsi_controller.h"
|
||||
#include "controllers/abstract_controller.h"
|
||||
#include "device.h"
|
||||
#include "dispatcher.h"
|
||||
#include <string>
|
||||
|
||||
class PrimaryDevice: public ScsiPrimaryCommands, public Device
|
||||
class PrimaryDevice: private ScsiPrimaryCommands, public Device
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
explicit PrimaryDevice(const string&);
|
||||
PrimaryDevice(const string&, int);
|
||||
~PrimaryDevice() override = default;
|
||||
PrimaryDevice(PrimaryDevice&) = delete;
|
||||
PrimaryDevice& operator=(const PrimaryDevice&) = delete;
|
||||
|
||||
virtual bool Dispatch(scsi_command);
|
||||
|
||||
int GetId() const override;
|
||||
|
||||
void SetController(AbstractController *);
|
||||
virtual bool WriteByteSequence(vector<BYTE>&, uint32_t);
|
||||
virtual int GetSendDelay() const { return BUS::SEND_NO_DELAY; }
|
||||
|
||||
// Override for device specific initializations, to be called after all device properties have been set
|
||||
virtual bool Init(const unordered_map<string, string>&) { return true; };
|
||||
|
||||
virtual void FlushCache() {
|
||||
// Devices with a cache have to implement this method
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
vector<byte> HandleInquiry(scsi_defs::device_type, scsi_level, bool) const;
|
||||
|
|
|
@ -20,39 +20,37 @@ void scsi_command_util::ModeSelect(const vector<int>& cdb, const vector<BYTE>& b
|
|||
// PF
|
||||
if (!(cdb[1] & 0x10)) {
|
||||
// Vendor-specific parameters (SCSI-1) are not supported
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
}
|
||||
|
||||
// Skip block descriptors
|
||||
int offset;
|
||||
if ((scsi_command)cdb[0] == scsi_command::eCmdModeSelect10) {
|
||||
offset = 8 + GetInt16(buf, 6);
|
||||
}
|
||||
else {
|
||||
offset = 4 + buf[3];
|
||||
}
|
||||
length -= offset;
|
||||
|
||||
bool has_valid_page_code = false;
|
||||
|
||||
// Mode Parameter header
|
||||
int offset = 0;
|
||||
if (length >= 12) {
|
||||
// Check the block length
|
||||
if (buf[9] != (BYTE)(sector_size >> 16) || buf[10] != (BYTE)(sector_size >> 8) ||
|
||||
buf[11] != (BYTE)sector_size) {
|
||||
// See below for details
|
||||
LOGWARN("In order to change the sector size use the -b option when launching rascsi")
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
}
|
||||
|
||||
offset += 12;
|
||||
length -= 12;
|
||||
}
|
||||
|
||||
// Parsing the page
|
||||
// TODO The length handling is wrong in case of length < size
|
||||
// Parse the pages
|
||||
while (length > 0) {
|
||||
// Format device page
|
||||
if (int page = buf[offset]; page == 0x03) {
|
||||
if (length < 14) {
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
}
|
||||
|
||||
// With this page the sector size for a subsequent FORMAT can be selected, but only very few
|
||||
// drives support this, e.g FUJITSU M2624S
|
||||
// We are fine as long as the current sector size remains unchanged
|
||||
if (buf[offset + 0xc] != (BYTE)(sector_size >> 8) || buf[offset + 0xd] != (BYTE)sector_size) {
|
||||
if (GetInt16(buf, offset + 12) != sector_size) {
|
||||
// With rascsi it is not possible to permanently (by formatting) change the sector size,
|
||||
// because the size is an externally configurable setting only
|
||||
LOGWARN("In order to change the sector size use the -b option when launching rascsi")
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
}
|
||||
|
||||
has_valid_page_code = true;
|
||||
|
@ -63,12 +61,13 @@ void scsi_command_util::ModeSelect(const vector<int>& cdb, const vector<BYTE>& b
|
|||
|
||||
// Advance to the next page
|
||||
int size = buf[offset + 1] + 2;
|
||||
|
||||
length -= size;
|
||||
offset += size;
|
||||
}
|
||||
|
||||
if (!has_valid_page_code) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_PARAMETER_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,30 +91,45 @@ void scsi_command_util::AddAppleVendorModePage(map<int, vector<byte>>& pages, bo
|
|||
// No changeable area
|
||||
if (!changeable) {
|
||||
const char APPLE_DATA[] = "APPLE COMPUTER, INC ";
|
||||
memcpy(&buf[2], APPLE_DATA, sizeof(APPLE_DATA));
|
||||
memcpy(&buf.data()[2], APPLE_DATA, sizeof(APPLE_DATA));
|
||||
}
|
||||
|
||||
pages[48] = buf;
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt16(const vector<BYTE>& buf, int offset)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 1);
|
||||
|
||||
return ((int)buf[offset] << 8) | buf[offset + 1];
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt16(const vector<int>& buf, int offset)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 1);
|
||||
|
||||
return (buf[offset] << 8) | buf[offset + 1];
|
||||
}
|
||||
|
||||
int scsi_command_util::GetInt24(const vector<int>& buf, int offset)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 2);
|
||||
|
||||
return (buf[offset] << 16) | (buf[offset + 1] << 8) | buf[offset + 2];
|
||||
}
|
||||
|
||||
uint32_t scsi_command_util::GetInt32(const vector<int>& buf, int offset)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 3);
|
||||
|
||||
return ((uint32_t)buf[offset] << 24) | ((uint32_t)buf[offset + 1] << 16) |
|
||||
((uint32_t)buf[offset + 2] << 8) | (uint32_t)buf[offset + 3];
|
||||
}
|
||||
|
||||
uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 7);
|
||||
|
||||
return ((uint64_t)buf[offset] << 56) | ((uint64_t)buf[offset + 1] << 48) |
|
||||
((uint64_t)buf[offset + 2] << 40) | ((uint64_t)buf[offset + 3] << 32) |
|
||||
((uint64_t)buf[offset + 4] << 24) | ((uint64_t)buf[offset + 5] << 16) |
|
||||
|
@ -124,12 +138,16 @@ uint64_t scsi_command_util::GetInt64(const vector<int>& buf, int offset)
|
|||
|
||||
void scsi_command_util::SetInt16(vector<byte>& buf, int offset, int value)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 1);
|
||||
|
||||
buf[offset] = (byte)(value >> 8);
|
||||
buf[offset + 1] = (byte)value;
|
||||
}
|
||||
|
||||
void scsi_command_util::SetInt32(vector<byte>& buf, int offset, uint32_t value)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 3);
|
||||
|
||||
buf[offset] = (byte)(value >> 24);
|
||||
buf[offset + 1] = (byte)(value >> 16);
|
||||
buf[offset + 2] = (byte)(value >> 8);
|
||||
|
@ -138,12 +156,16 @@ void scsi_command_util::SetInt32(vector<byte>& buf, int offset, uint32_t value)
|
|||
|
||||
void scsi_command_util::SetInt16(vector<BYTE>& buf, int offset, int value)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 1);
|
||||
|
||||
buf[offset] = (BYTE)(value >> 8);
|
||||
buf[offset + 1] = (BYTE)value;
|
||||
}
|
||||
|
||||
void scsi_command_util::SetInt32(vector<BYTE>& buf, int offset, uint32_t value)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 3);
|
||||
|
||||
buf[offset] = (BYTE)(value >> 24);
|
||||
buf[offset + 1] = (BYTE)(value >> 16);
|
||||
buf[offset + 2] = (BYTE)(value >> 8);
|
||||
|
@ -152,6 +174,8 @@ void scsi_command_util::SetInt32(vector<BYTE>& buf, int offset, uint32_t value)
|
|||
|
||||
void scsi_command_util::SetInt64(vector<BYTE>& buf, int offset, uint64_t value)
|
||||
{
|
||||
assert(buf.size() > (size_t)offset + 7);
|
||||
|
||||
buf[offset] = (BYTE)(value >> 56);
|
||||
buf[offset + 1] = (BYTE)(value >> 48);
|
||||
buf[offset + 2] = (BYTE)(value >> 40);
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
namespace scsi_command_util
|
||||
{
|
||||
|
@ -22,6 +22,7 @@ namespace scsi_command_util
|
|||
void EnrichFormatPage(map<int, vector<byte>>&, bool, int);
|
||||
void AddAppleVendorModePage(map<int, vector<byte>>&, bool);
|
||||
|
||||
int GetInt16(const vector<BYTE>&, int);
|
||||
int GetInt16(const vector<int>&, int);
|
||||
int GetInt24(const vector<int>&, int);
|
||||
uint32_t GetInt32(const vector<int>&, int);
|
||||
|
|
|
@ -7,23 +7,20 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
|
||||
//
|
||||
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
|
||||
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
|
||||
// Tiny SCSI Emulator
|
||||
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
|
||||
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
|
||||
//
|
||||
// Additional documentation and clarification is available at the
|
||||
// Additional documentation and clarification is available at the
|
||||
// following link:
|
||||
// - https://github.com/akuker/RASCSI/wiki/Dayna-Port-SCSI-Link
|
||||
//
|
||||
// This does NOT include the file system functionality that is present
|
||||
// in the Sharp X68000 host bridge.
|
||||
//
|
||||
// Note: This requires a DaynaPort SCSI Link driver.
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
@ -36,7 +33,7 @@ using namespace scsi_defs;
|
|||
using namespace scsi_command_util;
|
||||
|
||||
// TODO Disk must not be the superclass
|
||||
SCSIDaynaPort::SCSIDaynaPort() : Disk("SCDP")
|
||||
SCSIDaynaPort::SCSIDaynaPort(int lun) : Disk("SCDP", lun)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIDaynaPort::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdRead6, "Read6", &SCSIDaynaPort::Read6);
|
||||
|
@ -133,13 +130,12 @@ vector<byte> SCSIDaynaPort::InquiryInternal() const
|
|||
int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
|
||||
{
|
||||
int rx_packet_size = 0;
|
||||
auto response = (scsi_resp_read_t*)buf.data();
|
||||
const auto response = (scsi_resp_read_t*)buf.data();
|
||||
|
||||
int requested_length = cdb[4];
|
||||
const int requested_length = cdb[4];
|
||||
LOGTRACE("%s Read maximum length %d, (%04X)", __PRETTY_FUNCTION__, requested_length, requested_length)
|
||||
|
||||
|
||||
// At host startup, it will send a READ(6) command with a length of 1. We should
|
||||
// At host startup, it will send a READ(6) command with a length of 1. We should
|
||||
// respond by going into the status mode with a code of 0x02
|
||||
if (requested_length == 1) {
|
||||
return 0;
|
||||
|
@ -169,7 +165,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
|
|||
LOGTRACE("%s Packet Sz %d (%08X) read: %d", __PRETTY_FUNCTION__, (unsigned int) rx_packet_size, (unsigned int) rx_packet_size, read_count)
|
||||
|
||||
// This is a very basic filter to prevent unnecessary packets from
|
||||
// being sent to the SCSI initiator.
|
||||
// being sent to the SCSI initiator.
|
||||
send_message_to_host = false;
|
||||
|
||||
// The following doesn't seem to work with unicast messages. Temporarily removing the filtering
|
||||
|
@ -192,7 +188,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
|
|||
/////// }
|
||||
send_message_to_host = true;
|
||||
|
||||
// TODO: We should check to see if this message is in the multicast
|
||||
// TODO: We should check to see if this message is in the multicast
|
||||
// configuration from SCSI command 0x0D
|
||||
|
||||
if (!send_message_to_host) {
|
||||
|
@ -207,8 +203,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
|
|||
|
||||
// If there are pending packets to be processed, we'll tell the host that the read
|
||||
// length was 0.
|
||||
if (!m_tap.PendingPackets())
|
||||
{
|
||||
if (!m_tap.PendingPackets()) {
|
||||
response->length = 0;
|
||||
response->flags = read_data_flags_t::e_no_more_data;
|
||||
return DAYNAPORT_READ_HEADER_SZ;
|
||||
|
@ -239,7 +234,7 @@ int SCSIDaynaPort::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
|
|||
// The CRC was already appended by the ctapdriver
|
||||
return size + DAYNAPORT_READ_HEADER_SZ;
|
||||
}
|
||||
// If we got to this point, there are still messages in the queue, so
|
||||
// If we got to this point, there are still messages in the queue, so
|
||||
// we should loop back and get the next one.
|
||||
} // end while
|
||||
|
||||
|
@ -252,8 +247,8 @@ int SCSIDaynaPort::WriteCheck(uint64_t)
|
|||
{
|
||||
CheckReady();
|
||||
|
||||
if (!m_bTapEnable){
|
||||
throw scsi_error_exception(sense_key::UNIT_ATTENTION, asc::MEDIUM_NOT_PRESENT);
|
||||
if (!m_bTapEnable) {
|
||||
throw scsi_exception(sense_key::UNIT_ATTENTION, asc::MEDIUM_NOT_PRESENT);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -279,28 +274,26 @@ int SCSIDaynaPort::WriteCheck(uint64_t)
|
|||
//---------------------------------------------------------------------------
|
||||
bool SCSIDaynaPort::WriteBytes(const vector<int>& cdb, const vector<BYTE>& buf, uint64_t)
|
||||
{
|
||||
int data_format = cdb[5];
|
||||
const int data_format = cdb[5];
|
||||
int data_length = GetInt16(cdb, 3);
|
||||
|
||||
if (data_format == 0x00){
|
||||
if (data_format == 0x00) {
|
||||
m_tap.Send(buf.data(), data_length);
|
||||
LOGTRACE("%s Transmitted %u bytes (00 format)", __PRETTY_FUNCTION__, data_length)
|
||||
}
|
||||
else if (data_format == 0x80){
|
||||
else if (data_format == 0x80) {
|
||||
// The data length is specified in the first 2 bytes of the payload
|
||||
data_length=buf[1] + (buf[0] << 8);
|
||||
data_length = buf[1] + (((int)buf[0] & 0xff) << 8);
|
||||
m_tap.Send(&(buf.data()[4]), data_length);
|
||||
LOGTRACE("%s Transmitted %u bytes (80 format)", __PRETTY_FUNCTION__, data_length)
|
||||
}
|
||||
else
|
||||
{
|
||||
// LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, (unsigned int)command->format)
|
||||
else {
|
||||
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, data_format)
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// RetrieveStats
|
||||
|
@ -321,45 +314,7 @@ int SCSIDaynaPort::RetrieveStats(const vector<int>& cdb, vector<BYTE>& buf) cons
|
|||
{
|
||||
memcpy(buf.data(), &m_scsi_link_stats, sizeof(m_scsi_link_stats));
|
||||
|
||||
return (int)min(sizeof(m_scsi_link_stats), (size_t)GetInt16(cdb, 4));
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Enable or Disable the interface
|
||||
//
|
||||
// Command: 0e 00 00 00 00 XX (XX = 80 or 00)
|
||||
// Function: Enable (80) / disable (00) Ethernet interface
|
||||
// Type: No data transferred
|
||||
// Notes: After issuing an Enable, the initiator should avoid sending
|
||||
// any subsequent commands to the device for approximately 0.5
|
||||
// seconds
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
bool SCSIDaynaPort::EnableInterface(const vector<int>& cdb)
|
||||
{
|
||||
bool result;
|
||||
if (cdb[5] & 0x80) {
|
||||
result = m_tap.Enable();
|
||||
if (result) {
|
||||
LOGINFO("The DaynaPort interface has been ENABLED.")
|
||||
}
|
||||
else{
|
||||
LOGWARN("Unable to enable the DaynaPort Interface")
|
||||
}
|
||||
m_tap.Flush();
|
||||
}
|
||||
else {
|
||||
result = m_tap.Disable();
|
||||
if (result) {
|
||||
LOGINFO("The DaynaPort interface has been DISABLED.")
|
||||
}
|
||||
else{
|
||||
LOGWARN("Unable to disable the DaynaPort Interface")
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return (int)min(sizeof(m_scsi_link_stats), (size_t)GetInt16(cdb, 3));
|
||||
}
|
||||
|
||||
void SCSIDaynaPort::TestUnitReady()
|
||||
|
@ -371,14 +326,14 @@ void SCSIDaynaPort::TestUnitReady()
|
|||
void SCSIDaynaPort::Read6()
|
||||
{
|
||||
// Get record number and block number
|
||||
uint32_t record = GetInt24(ctrl->cmd, 1) & 0x1fffff;
|
||||
const uint32_t record = GetInt24(ctrl->cmd, 1) & 0x1fffff;
|
||||
ctrl->blocks=1;
|
||||
|
||||
// If any commands have a bogus control value, they were probably not
|
||||
// generated by the DaynaPort driver so ignore them
|
||||
if (ctrl->cmd[5] != 0xc0 && ctrl->cmd[5] != 0x80) {
|
||||
LOGTRACE("%s Control value %d, (%04X), returning invalid CDB", __PRETTY_FUNCTION__, ctrl->cmd[5], ctrl->cmd[5])
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
LOGTRACE("%s READ(6) command record=%d blocks=%d", __PRETTY_FUNCTION__, record, ctrl->blocks)
|
||||
|
@ -397,21 +352,21 @@ void SCSIDaynaPort::Write6()
|
|||
// Ensure a sufficient buffer size (because it is not transfer for each block)
|
||||
controller->AllocateBuffer(DAYNAPORT_BUFFER_SIZE);
|
||||
|
||||
int data_format = ctrl->cmd[5];
|
||||
const int data_format = ctrl->cmd[5];
|
||||
|
||||
if (data_format == 0x00) {
|
||||
ctrl->length = GetInt16(ctrl->cmd, 3);
|
||||
}
|
||||
else if (data_format == 0x80) {
|
||||
ctrl->length = GetInt16(ctrl->cmd, 3 + 8);
|
||||
ctrl->length = GetInt16(ctrl->cmd, 3) + 8;
|
||||
}
|
||||
else {
|
||||
LOGWARN("%s Unknown data format %02X", __PRETTY_FUNCTION__, data_format)
|
||||
LOGWARN("%s Unknown data format $%02X", __PRETTY_FUNCTION__, data_format)
|
||||
}
|
||||
LOGTRACE("%s length: %04X (%d) format: %02X", __PRETTY_FUNCTION__, ctrl->length, ctrl->length, data_format)
|
||||
LOGTRACE("%s length: $%04X (%d) format: $%02X", __PRETTY_FUNCTION__, ctrl->length, ctrl->length, data_format)
|
||||
|
||||
if (ctrl->length <= 0) {
|
||||
throw scsi_error_exception();
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Set next block
|
||||
|
@ -478,10 +433,12 @@ void SCSIDaynaPort::SetInterfaceMode()
|
|||
case CMD_SCSILINK_ENABLE:
|
||||
case CMD_SCSILINK_SET:
|
||||
LOGWARN("%s Unsupported SetInterface command received: %02X", __PRETTY_FUNCTION__, ctrl->cmd[5])
|
||||
throw scsi_exception();
|
||||
break;
|
||||
|
||||
default:
|
||||
LOGWARN("%s Unknown SetInterface command received: %02X", __PRETTY_FUNCTION__, ctrl->cmd[5])
|
||||
throw scsi_exception();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -492,16 +449,45 @@ void SCSIDaynaPort::SetMcastAddr()
|
|||
if (ctrl->length == 0) {
|
||||
LOGWARN("%s Not supported SetMcastAddr Command %02X", __PRETTY_FUNCTION__, ctrl->cmd[2])
|
||||
|
||||
throw scsi_error_exception();
|
||||
throw scsi_exception();
|
||||
}
|
||||
|
||||
EnterDataOutPhase();
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// Enable or Disable the interface
|
||||
//
|
||||
// Command: 0e 00 00 00 00 XX (XX = 80 or 00)
|
||||
// Function: Enable (80) / disable (00) Ethernet interface
|
||||
// Type: No data transferred
|
||||
// Notes: After issuing an Enable, the initiator should avoid sending
|
||||
// any subsequent commands to the device for approximately 0.5
|
||||
// seconds
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
void SCSIDaynaPort::EnableInterface()
|
||||
{
|
||||
if (!EnableInterface(ctrl->cmd)) {
|
||||
throw scsi_error_exception();
|
||||
if (ctrl->cmd[5] & 0x80) {
|
||||
if (!m_tap.Enable()) {
|
||||
LOGWARN("Unable to enable the DaynaPort Interface")
|
||||
|
||||
throw scsi_exception();
|
||||
}
|
||||
|
||||
m_tap.Flush();
|
||||
|
||||
LOGINFO("The DaynaPort interface has been ENABLED")
|
||||
}
|
||||
else {
|
||||
if (!m_tap.Disable()) {
|
||||
LOGWARN("Unable to disable the DaynaPort Interface")
|
||||
|
||||
throw scsi_exception();
|
||||
}
|
||||
|
||||
LOGINFO("The DaynaPort interface has been DISABLED")
|
||||
}
|
||||
|
||||
EnterStatusPhase();
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ Emulation of the DaynaPort SCSI Link Ethernet interface ]
|
||||
//
|
||||
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
|
||||
// This design is derived from the SLINKCMD.TXT file, as well as David Kuder's
|
||||
// Tiny SCSI Emulator
|
||||
// - SLINKCMD: http://www.bitsavers.org/pdf/apple/scsi/dayna/daynaPORT/SLINKCMD.TXT
|
||||
// - Tiny SCSI : https://hackaday.io/project/18974-tiny-scsi-emulator
|
||||
|
@ -41,14 +41,12 @@
|
|||
// DaynaPort SCSI Link
|
||||
//
|
||||
//===========================================================================
|
||||
class SCSIDaynaPort final : public Disk
|
||||
class SCSIDaynaPort : public Disk
|
||||
{
|
||||
public:
|
||||
|
||||
SCSIDaynaPort();
|
||||
explicit SCSIDaynaPort(int);
|
||||
~SCSIDaynaPort() override = default;
|
||||
SCSIDaynaPort(SCSIDaynaPort&) = delete;
|
||||
SCSIDaynaPort& operator=(const SCSIDaynaPort&) = delete;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
void Open(const Filepath& path) override;
|
||||
|
@ -60,7 +58,6 @@ public:
|
|||
int WriteCheck(uint64_t block) override;
|
||||
|
||||
int RetrieveStats(const vector<int>&, vector<BYTE>&) const;
|
||||
bool EnableInterface(const vector<int>&);
|
||||
|
||||
void TestUnitReady() override;
|
||||
void Read6() override;
|
||||
|
@ -90,9 +87,10 @@ public:
|
|||
// The READ response has a header which consists of:
|
||||
// 2 bytes - payload size
|
||||
// 4 bytes - status flags
|
||||
static const DWORD DAYNAPORT_READ_HEADER_SZ = 2 + 4;
|
||||
static const uint32_t DAYNAPORT_READ_HEADER_SZ = 2 + 4;
|
||||
|
||||
private:
|
||||
|
||||
using super = Disk;
|
||||
|
||||
Dispatcher<SCSIDaynaPort> dispatcher;
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI Host Bridge for the Sharp X68000 ]
|
||||
|
@ -27,7 +27,7 @@ using namespace std;
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSIBR::SCSIBR() : Disk("SCBR")
|
||||
SCSIBR::SCSIBR(int lun) : Disk("SCBR", lun)
|
||||
{
|
||||
// Create host file system
|
||||
fs.Reset();
|
||||
|
@ -41,7 +41,7 @@ bool SCSIBR::Init(const unordered_map<string, string>& params)
|
|||
{
|
||||
SetParams(params);
|
||||
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
// TAP Driver Generation
|
||||
m_bTapEnable = tap.Init(GetParams());
|
||||
if (!m_bTapEnable){
|
||||
|
@ -107,13 +107,13 @@ void SCSIBR::TestUnitReady()
|
|||
int SCSIBR::GetMessage10(const vector<int>& cdb, vector<BYTE>& buf)
|
||||
{
|
||||
// Type
|
||||
int type = cdb[2];
|
||||
const int type = cdb[2];
|
||||
|
||||
// Function number
|
||||
int func = cdb[3];
|
||||
const int func = cdb[3];
|
||||
|
||||
// Phase
|
||||
int phase = cdb[9];
|
||||
const int phase = cdb[9];
|
||||
|
||||
switch (type) {
|
||||
case 1: // Ethernet
|
||||
|
@ -195,16 +195,16 @@ int SCSIBR::GetMessage10(const vector<int>& cdb, vector<BYTE>& buf)
|
|||
bool SCSIBR::WriteBytes(const vector<int>& cdb, vector<BYTE>& buf, uint64_t)
|
||||
{
|
||||
// Type
|
||||
int type = cdb[2];
|
||||
const int type = cdb[2];
|
||||
|
||||
// Function number
|
||||
int func = cdb[3];
|
||||
const int func = cdb[3];
|
||||
|
||||
// Phase
|
||||
int phase = cdb[9];
|
||||
const int phase = cdb[9];
|
||||
|
||||
// Get the number of lights
|
||||
int len = GetInt24(cdb, 6);
|
||||
const int len = GetInt24(cdb, 6);
|
||||
|
||||
switch (type) {
|
||||
case 1: // Ethernet
|
||||
|
@ -257,7 +257,7 @@ void SCSIBR::GetMessage10()
|
|||
|
||||
ctrl->length = GetMessage10(ctrl->cmd, controller->GetBuffer());
|
||||
if (ctrl->length <= 0) {
|
||||
throw scsi_error_exception();
|
||||
throw scsi_exception();
|
||||
}
|
||||
|
||||
// Set next block
|
||||
|
@ -278,7 +278,7 @@ void SCSIBR::SendMessage10()
|
|||
{
|
||||
ctrl->length = GetInt24(ctrl->cmd, 6);
|
||||
if (ctrl->length <= 0) {
|
||||
throw scsi_error_exception();
|
||||
throw scsi_exception();
|
||||
}
|
||||
|
||||
// Ensure a sufficient buffer size (because it is not a transfer for each block)
|
||||
|
@ -368,8 +368,8 @@ void SCSIBR::FS_InitDevice(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_CheckDir(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
const int i = sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
||||
|
@ -384,8 +384,8 @@ void SCSIBR::FS_CheckDir(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_MakeDir(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
const int i = sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
||||
|
@ -400,8 +400,8 @@ void SCSIBR::FS_MakeDir(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_RemoveDir(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
const int i = sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
||||
|
@ -416,7 +416,7 @@ void SCSIBR::FS_RemoveDir(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Rename(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
@ -435,8 +435,8 @@ void SCSIBR::FS_Rename(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Delete(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
const int i = sizeof(uint32_t);
|
||||
|
||||
const auto *pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
||||
|
@ -451,7 +451,7 @@ void SCSIBR::FS_Delete(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Attribute(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
@ -471,11 +471,11 @@ void SCSIBR::FS_Attribute(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Files(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
@ -512,11 +512,11 @@ void SCSIBR::FS_Files(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_NFiles(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
auto files = (Human68k::files_t*)&(buf.data()[i]);
|
||||
|
@ -550,11 +550,11 @@ void SCSIBR::FS_NFiles(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Create(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
@ -564,11 +564,11 @@ void SCSIBR::FS_Create(vector<BYTE>& buf)
|
|||
i += sizeof(Human68k::fcb_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nAttribute = ntohl(*dp);
|
||||
const uint32_t nAttribute = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
auto bp = (int*)&(buf.data()[i]);
|
||||
uint32_t bForce = ntohl(*bp);
|
||||
const uint32_t bForce = ntohl(*bp);
|
||||
|
||||
pFcb->fileptr = ntohl(pFcb->fileptr);
|
||||
pFcb->mode = ntohs(pFcb->mode);
|
||||
|
@ -599,11 +599,11 @@ void SCSIBR::FS_Create(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Open(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
const auto pNamests = (Human68k::namests_t*)&(buf.data()[i]);
|
||||
|
@ -640,11 +640,11 @@ void SCSIBR::FS_Open(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Close(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
||||
|
@ -678,14 +678,14 @@ void SCSIBR::FS_Close(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Read(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
||||
i += sizeof(Human68k::fcb_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nSize = ntohl(*dp);
|
||||
const uint32_t nSize = ntohl(*dp);
|
||||
|
||||
pFcb->fileptr = ntohl(pFcb->fileptr);
|
||||
pFcb->mode = ntohs(pFcb->mode);
|
||||
|
@ -718,14 +718,14 @@ void SCSIBR::FS_Read(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Write(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
||||
i += sizeof(Human68k::fcb_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nSize = ntohl(*dp);
|
||||
const uint32_t nSize = ntohl(*dp);
|
||||
|
||||
pFcb->fileptr = ntohl(pFcb->fileptr);
|
||||
pFcb->mode = ntohs(pFcb->mode);
|
||||
|
@ -756,14 +756,14 @@ void SCSIBR::FS_Write(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Seek(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
||||
i += sizeof(Human68k::fcb_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nMode = ntohl(*dp);
|
||||
const uint32_t nMode = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
auto ip = (const int*)&(buf.data()[i]);
|
||||
|
@ -798,11 +798,11 @@ void SCSIBR::FS_Seek(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_TimeStamp(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nKey = ntohl(*dp);
|
||||
const uint32_t nKey = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
auto pFcb = (Human68k::fcb_t*)&(buf.data()[i]);
|
||||
|
@ -840,7 +840,7 @@ void SCSIBR::FS_TimeStamp(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_GetCapacity(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
Human68k::capacity_t cap;
|
||||
fsresult = fs.GetCapacity(nUnit, &cap);
|
||||
|
@ -862,8 +862,8 @@ void SCSIBR::FS_GetCapacity(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_CtrlDrive(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
const int i = sizeof(uint32_t);
|
||||
|
||||
auto pCtrlDrive = (Human68k::ctrldrive_t*)&(buf.data()[i]);
|
||||
|
||||
|
@ -881,7 +881,7 @@ void SCSIBR::FS_CtrlDrive(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_GetDPB(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
Human68k::dpb_t dpb;
|
||||
fsresult = fs.GetDPB(nUnit, &dpb);
|
||||
|
@ -905,11 +905,11 @@ void SCSIBR::FS_GetDPB(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_DiskRead(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nSector = ntohl(*dp);
|
||||
const uint32_t nSector = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
|
@ -927,7 +927,7 @@ void SCSIBR::FS_DiskRead(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_DiskWrite(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
fsresult = fs.DiskWrite(nUnit);
|
||||
}
|
||||
|
@ -940,18 +940,18 @@ void SCSIBR::FS_DiskWrite(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Ioctrl(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
int i = sizeof(uint32_t);
|
||||
|
||||
dp = (uint32_t*)&(buf.data()[i]);
|
||||
uint32_t nFunction = ntohl(*dp);
|
||||
const uint32_t nFunction = ntohl(*dp);
|
||||
i += sizeof(uint32_t);
|
||||
|
||||
auto pIoctrl = (Human68k::ioctrl_t*)&(buf.data()[i]);
|
||||
|
||||
switch (nFunction) {
|
||||
case 2:
|
||||
case (DWORD)-2:
|
||||
case (uint32_t)-2:
|
||||
pIoctrl->param = htonl(pIoctrl->param);
|
||||
break;
|
||||
default:
|
||||
|
@ -965,7 +965,7 @@ void SCSIBR::FS_Ioctrl(vector<BYTE>& buf)
|
|||
pIoctrl->media = htons(pIoctrl->media);
|
||||
break;
|
||||
case 1:
|
||||
case (DWORD)-3:
|
||||
case (uint32_t)-3:
|
||||
pIoctrl->param = htonl(pIoctrl->param);
|
||||
break;
|
||||
default:
|
||||
|
@ -986,7 +986,7 @@ void SCSIBR::FS_Ioctrl(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Flush(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
fsresult = fs.Flush(nUnit);
|
||||
}
|
||||
|
@ -999,7 +999,7 @@ void SCSIBR::FS_Flush(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_CheckMedia(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
fsresult = fs.CheckMedia(nUnit);
|
||||
}
|
||||
|
@ -1012,7 +1012,7 @@ void SCSIBR::FS_CheckMedia(vector<BYTE>& buf)
|
|||
void SCSIBR::FS_Lock(vector<BYTE>& buf)
|
||||
{
|
||||
auto dp = (uint32_t*)buf.data();
|
||||
uint32_t nUnit = ntohl(*dp);
|
||||
const uint32_t nUnit = ntohl(*dp);
|
||||
|
||||
fsresult = fs.Lock(nUnit);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI Host Bridge for the Sharp X68000 ]
|
||||
|
@ -24,18 +24,16 @@
|
|||
#include <string>
|
||||
#include <array>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
class SCSIBR final : public Disk
|
||||
class SCSIBR : public Disk
|
||||
{
|
||||
static constexpr const array<BYTE, 6> bcast_addr = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||
|
||||
public:
|
||||
|
||||
SCSIBR();
|
||||
explicit SCSIBR(int);
|
||||
~SCSIBR() override = default;
|
||||
SCSIBR(SCSIBR&) = delete;
|
||||
SCSIBR& operator=(const SCSIBR&) = delete;
|
||||
|
||||
bool Init(const unordered_map<string, string>&) override;
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
@ -101,9 +99,9 @@ private:
|
|||
void FS_Lock(vector<BYTE>&); // $58 - get exclusive control
|
||||
|
||||
CFileSys fs; // File system accessor
|
||||
DWORD fsresult = 0; // File system access result code
|
||||
uint32_t fsresult = 0; // File system access result code
|
||||
array<BYTE, 0x800> fsout; // File system access result buffer
|
||||
DWORD fsoutlen = 0; // File system access result buffer size
|
||||
uint32_t fsoutlen = 0; // File system access result buffer size
|
||||
array<BYTE, 0x1000000> fsopt; // File system access buffer
|
||||
DWORD fsoptlen = 0; // File system access buffer size
|
||||
uint32_t fsoptlen = 0; // File system access buffer size
|
||||
};
|
||||
|
|
|
@ -51,7 +51,7 @@ using namespace scsi_defs;
|
|||
using namespace ras_util;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP")
|
||||
SCSIPrinter::SCSIPrinter(int lun) : PrimaryDevice("SCLP", lun)
|
||||
{
|
||||
dispatcher.Add(scsi_command::eCmdTestUnitReady, "TestUnitReady", &SCSIPrinter::TestUnitReady);
|
||||
dispatcher.Add(scsi_command::eCmdReserve6, "ReserveUnit", &SCSIPrinter::ReserveUnit);
|
||||
|
@ -60,6 +60,9 @@ SCSIPrinter::SCSIPrinter() : PrimaryDevice("SCLP")
|
|||
dispatcher.Add(scsi_command::eCmdSynchronizeBuffer, "SynchronizeBuffer", &SCSIPrinter::SynchronizeBuffer);
|
||||
dispatcher.Add(scsi_command::eCmdSendDiag, "SendDiagnostic", &SCSIPrinter::SendDiagnostic);
|
||||
dispatcher.Add(scsi_command::eCmdStartStop, "StopPrint", &SCSIPrinter::StopPrint);
|
||||
|
||||
SetReady(true);
|
||||
SetReset(false);
|
||||
}
|
||||
|
||||
SCSIPrinter::~SCSIPrinter()
|
||||
|
@ -145,15 +148,15 @@ void SCSIPrinter::Print()
|
|||
{
|
||||
CheckReservation();
|
||||
|
||||
uint32_t length = GetInt24(ctrl->cmd, 2);
|
||||
const uint32_t length = GetInt24(ctrl->cmd, 2);
|
||||
|
||||
LOGTRACE("Receiving %d byte(s) to be printed", length)
|
||||
|
||||
if (length > controller->GetBufferSize()) {
|
||||
LOGERROR("%s", string("Transfer buffer overflow: Buffer size is " + to_string(controller->GetBufferSize()) +
|
||||
if (length > controller->GetBuffer().size()) {
|
||||
LOGERROR("%s", ("Transfer buffer overflow: Buffer size is " + to_string(controller->GetBuffer().size()) +
|
||||
" bytes, " + to_string(length) + " bytes expected").c_str())
|
||||
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
ctrl->length = length;
|
||||
|
@ -167,11 +170,11 @@ void SCSIPrinter::SynchronizeBuffer()
|
|||
CheckReservation();
|
||||
|
||||
if (fd == -1) {
|
||||
throw scsi_error_exception();
|
||||
throw scsi_exception();
|
||||
}
|
||||
|
||||
// Make the file readable for the lp user
|
||||
fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
fchmod(fd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); //NOSONAR Granting permissions to "others" is required here
|
||||
|
||||
struct stat st;
|
||||
fstat(fd, &st);
|
||||
|
@ -180,7 +183,7 @@ void SCSIPrinter::SynchronizeBuffer()
|
|||
fd = -1;
|
||||
|
||||
string cmd = GetParam("cmd");
|
||||
size_t file_position = cmd.find("%f");
|
||||
const size_t file_position = cmd.find("%f");
|
||||
assert(file_position != string::npos);
|
||||
cmd.replace(file_position, 2, filename);
|
||||
cmd = "sudo -u lp " + cmd;
|
||||
|
@ -194,7 +197,7 @@ void SCSIPrinter::SynchronizeBuffer()
|
|||
|
||||
unlink(filename);
|
||||
|
||||
throw scsi_error_exception();
|
||||
throw scsi_exception();
|
||||
}
|
||||
|
||||
unlink(filename);
|
||||
|
@ -217,7 +220,7 @@ void SCSIPrinter::StopPrint()
|
|||
bool SCSIPrinter::WriteByteSequence(vector<BYTE>& buf, uint32_t length)
|
||||
{
|
||||
if (fd == -1) {
|
||||
strcpy(filename, TMP_FILE_PATTERN);
|
||||
strcpy(filename, TMP_FILE_PATTERN); //NOSONAR Using strcpy is safe here
|
||||
fd = mkstemp(filename);
|
||||
if (fd == -1) {
|
||||
LOGERROR("Can't create printer output file '%s': %s", filename, strerror(errno))
|
||||
|
@ -229,7 +232,7 @@ bool SCSIPrinter::WriteByteSequence(vector<BYTE>& buf, uint32_t length)
|
|||
|
||||
LOGTRACE("Appending %d byte(s) to printer output file '%s'", length, filename)
|
||||
|
||||
auto num_written = (uint32_t)write(fd, buf.data(), length);
|
||||
const auto num_written = (uint32_t)write(fd, buf.data(), length);
|
||||
|
||||
return num_written == length;
|
||||
}
|
||||
|
@ -248,7 +251,7 @@ void SCSIPrinter::CheckReservation()
|
|||
LOGTRACE("Unknown initiator tries to access reserved device ID %d, LUN %d", GetId(), GetLun())
|
||||
}
|
||||
|
||||
throw scsi_error_exception(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION,
|
||||
throw scsi_exception(sense_key::ABORTED_COMMAND, asc::NO_ADDITIONAL_SENSE_INFORMATION,
|
||||
status::RESERVATION_CONFLICT);
|
||||
}
|
||||
|
||||
|
|
|
@ -15,19 +15,17 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class SCSIPrinter final : public PrimaryDevice, public ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed
|
||||
class SCSIPrinter : public PrimaryDevice, ScsiPrinterCommands //NOSONAR Custom destructor cannot be removed
|
||||
{
|
||||
static constexpr const char *TMP_FILE_PATTERN = "/tmp/rascsi_sclp-XXXXXX";
|
||||
static constexpr const char *TMP_FILE_PATTERN = "/tmp/rascsi_sclp-XXXXXX"; //NOSONAR Using /tmp is safe
|
||||
static const int TMP_FILENAME_LENGTH = string_view(TMP_FILE_PATTERN).size();
|
||||
|
||||
static const int NOT_RESERVED = -2;
|
||||
|
||||
public:
|
||||
|
||||
SCSIPrinter();
|
||||
explicit SCSIPrinter(int);
|
||||
~SCSIPrinter() override;
|
||||
SCSIPrinter(SCSIPrinter&) = delete;
|
||||
SCSIPrinter& operator=(const SCSIPrinter&) = delete;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI CD-ROM ]
|
||||
|
@ -24,7 +24,7 @@
|
|||
using namespace scsi_defs;
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSICD::SCSICD(const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD")
|
||||
SCSICD::SCSICD(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk("SCCD", lun)
|
||||
{
|
||||
SetSectorSizes(sector_sizes);
|
||||
|
||||
|
@ -90,12 +90,9 @@ void SCSICD::Open(const Filepath& path)
|
|||
assert(GetBlockCount() > 0);
|
||||
|
||||
super::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
SetPath(path);
|
||||
|
||||
SetUpCache(path);
|
||||
|
||||
// Set RAW flag
|
||||
cache->SetRawMode(rawfile);
|
||||
SetUpCache(path, 0, rawfile);
|
||||
|
||||
// Attention if ready
|
||||
if (IsReady()) {
|
||||
|
@ -103,7 +100,7 @@ void SCSICD::Open(const Filepath& path)
|
|||
}
|
||||
}
|
||||
|
||||
void SCSICD::OpenCue(const Filepath& /*path*/) const
|
||||
void SCSICD::OpenCue(const Filepath&) const
|
||||
{
|
||||
throw io_exception("Opening CUE CD-ROM files is not supported");
|
||||
}
|
||||
|
@ -117,8 +114,8 @@ void SCSICD::OpenIso(const Filepath& path)
|
|||
}
|
||||
|
||||
// Get file size
|
||||
off_t file_size = fio.GetFileSize();
|
||||
if (file_size < 0x800) {
|
||||
const off_t size = fio.GetFileSize();
|
||||
if (size < 0x800) {
|
||||
fio.Close();
|
||||
throw io_exception("ISO CD-ROM file size must be at least 2048 bytes");
|
||||
}
|
||||
|
@ -157,16 +154,16 @@ void SCSICD::OpenIso(const Filepath& path)
|
|||
|
||||
if (rawfile) {
|
||||
// Size must be a multiple of 2536
|
||||
if (file_size % 2536) {
|
||||
if (size % 2536) {
|
||||
throw io_exception("Raw ISO CD-ROM file size must be a multiple of 2536 bytes but is "
|
||||
+ to_string(file_size) + " bytes");
|
||||
+ to_string(size) + " bytes");
|
||||
}
|
||||
|
||||
// Set the number of blocks
|
||||
SetBlockCount((DWORD)(file_size / 0x930));
|
||||
SetBlockCount((uint32_t)(size / 0x930));
|
||||
} else {
|
||||
// Set the number of blocks
|
||||
SetBlockCount((DWORD)(file_size >> GetSectorSizeShiftCount()));
|
||||
SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
|
||||
}
|
||||
|
||||
// Create only one data track
|
||||
|
@ -200,7 +197,7 @@ void SCSICD::OpenPhysical(const Filepath& path)
|
|||
size = (size / 512) * 512;
|
||||
|
||||
// Set the number of blocks
|
||||
SetBlockCount((DWORD)(size >> GetSectorSizeShiftCount()));
|
||||
SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
|
||||
|
||||
// Create only one data track
|
||||
assert(!tracks.size());
|
||||
|
@ -278,11 +275,11 @@ int SCSICD::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t block)
|
|||
CheckReady();
|
||||
|
||||
// Search for the track
|
||||
int index = SearchTrack((int)block);
|
||||
const int index = SearchTrack((int)block);
|
||||
|
||||
// If invalid, out of range
|
||||
if (index < 0) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::LBA_OUT_OF_RANGE);
|
||||
}
|
||||
|
||||
assert(tracks[index]);
|
||||
|
@ -296,9 +293,9 @@ int SCSICD::Read(const vector<int>& cdb, vector<BYTE>& buf, uint64_t block)
|
|||
// Recreate the disk cache
|
||||
Filepath path;
|
||||
tracks[index]->GetPath(path);
|
||||
|
||||
// Re-assign disk cache (no need to save)
|
||||
cache.reset(new DiskCache(path, GetSectorSizeShiftCount(), (uint32_t)GetBlockCount()));
|
||||
cache->SetRawMode(rawfile);
|
||||
ResizeCache(path, rawfile);
|
||||
|
||||
// Reset data index
|
||||
dataindex = index;
|
||||
|
@ -318,17 +315,17 @@ int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<BYTE>& buf)
|
|||
assert(tracks[0]);
|
||||
|
||||
// Get allocation length, clear buffer
|
||||
int length = GetInt16(cdb, 7);
|
||||
const int length = GetInt16(cdb, 7);
|
||||
fill_n(buf.data(), length, 0);
|
||||
|
||||
// Get MSF Flag
|
||||
bool msf = cdb[1] & 0x02;
|
||||
const bool msf = cdb[1] & 0x02;
|
||||
|
||||
// Get and check the last track number
|
||||
int last = tracks[tracks.size() - 1]->GetTrackNo();
|
||||
const int last = tracks[tracks.size() - 1]->GetTrackNo();
|
||||
// Except for AA
|
||||
if (cdb[6] > last && cdb[6] != 0xaa) {
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
// Check start index
|
||||
|
@ -361,12 +358,12 @@ int SCSICD::ReadTocInternal(const vector<int>& cdb, vector<BYTE>& buf)
|
|||
}
|
||||
|
||||
// Otherwise, error
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
}
|
||||
|
||||
// Number of track descriptors returned this time (number of loops)
|
||||
int loop = last - tracks[index]->GetTrackNo() + 1;
|
||||
const int loop = last - tracks[index]->GetTrackNo() + 1;
|
||||
assert(loop >= 1);
|
||||
|
||||
// Create header
|
||||
|
@ -410,11 +407,11 @@ void SCSICD::GetEventStatusNotification()
|
|||
{
|
||||
if (!(ctrl->cmd[1] & 0x01)) {
|
||||
// Asynchronous notification is optional and not supported by rascsi
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
LOGTRACE("Received request for event polling, which is currently not supported")
|
||||
throw scsi_error_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
throw scsi_exception(sense_key::ILLEGAL_REQUEST, asc::INVALID_FIELD_IN_CDB);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
@ -427,7 +424,7 @@ void SCSICD::LBAtoMSF(uint32_t lba, BYTE *msf) const
|
|||
// 75 and 75*60 get the remainder
|
||||
uint32_t m = lba / (75 * 60);
|
||||
uint32_t s = lba % (75 * 60);
|
||||
uint32_t f = s % 75;
|
||||
const uint32_t f = s % 75;
|
||||
s /= 75;
|
||||
|
||||
// The base point is M=0, S=2, F=0
|
||||
|
@ -462,7 +459,7 @@ void SCSICD::ClearTrack()
|
|||
// * Returns -1 if not found
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
int SCSICD::SearchTrack(DWORD lba) const
|
||||
int SCSICD::SearchTrack(uint32_t lba) const
|
||||
{
|
||||
// Track loop
|
||||
for (size_t i = 0; i < tracks.size(); i++) {
|
||||
|
|
|
@ -7,30 +7,24 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI CD-ROM ]
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "disk.h"
|
||||
#include "filepath.h"
|
||||
#include "cd_track.h"
|
||||
#include "file_support.h"
|
||||
#include "disk.h"
|
||||
#include "interfaces/scsi_mmc_commands.h"
|
||||
#include "interfaces/scsi_primary_commands.h"
|
||||
|
||||
class SCSICD : public Disk, public ScsiMmcCommands, public FileSupport
|
||||
class SCSICD : public Disk, private ScsiMmcCommands
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SCSICD(const unordered_set<uint32_t>&);
|
||||
SCSICD(int, const unordered_set<uint32_t>&);
|
||||
~SCSICD() override = default;
|
||||
SCSICD(SCSICD&) = delete;
|
||||
SCSICD& operator=(const SCSICD&) = delete;
|
||||
|
||||
bool Dispatch(scsi_command) override;
|
||||
|
||||
|
@ -70,7 +64,7 @@ private:
|
|||
|
||||
// Track management
|
||||
void ClearTrack(); // Clear the track
|
||||
int SearchTrack(DWORD lba) const; // Track search
|
||||
int SearchTrack(uint32_t lba) const; // Track search
|
||||
vector<unique_ptr<CDTrack>> tracks; // Track opbject references
|
||||
int dataindex = -1; // Current data track
|
||||
int audioindex = -1; // Current audio track
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI hard disk ]
|
||||
|
@ -22,11 +22,9 @@
|
|||
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSIHD::SCSIHD(const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level)
|
||||
: Disk(removable ? "SCRM" : "SCHD")
|
||||
SCSIHD::SCSIHD(int lun, const unordered_set<uint32_t>& sector_sizes, bool removable, scsi_defs::scsi_level level)
|
||||
: Disk(removable ? "SCRM" : "SCHD", lun), scsi_level(level)
|
||||
{
|
||||
scsi_level = level;
|
||||
|
||||
SetSectorSizes(sector_sizes);
|
||||
}
|
||||
|
||||
|
@ -41,8 +39,14 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size, off_t image_offset)
|
|||
if (!IsRemovable()) {
|
||||
uint64_t capacity = GetBlockCount() * GetSectorSizeInBytes();
|
||||
string unit;
|
||||
if (capacity >= 1048576) {
|
||||
capacity /= 1048576;
|
||||
// 10 GiB and more
|
||||
if (capacity >= 1'099'511'627'776) {
|
||||
capacity /= 1'099'511'627'776;
|
||||
unit = "GiB";
|
||||
}
|
||||
// 1 MiB and more
|
||||
else if (capacity >= 1'048'576) {
|
||||
capacity /= 1'048'576;
|
||||
unit = "MiB";
|
||||
}
|
||||
else {
|
||||
|
@ -58,8 +62,8 @@ void SCSIHD::FinalizeSetup(const Filepath &path, off_t size, off_t image_offset)
|
|||
SetProtectable(true);
|
||||
SetProtected(false);
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
super::Open(path);
|
||||
SetPath(path);
|
||||
|
||||
SetUpCache(path, image_offset);
|
||||
}
|
||||
|
@ -75,17 +79,17 @@ void SCSIHD::Open(const Filepath& path)
|
|||
}
|
||||
|
||||
// Get file size
|
||||
off_t file_size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
// Sector size (default 512 bytes) and number of blocks
|
||||
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
|
||||
SetBlockCount((DWORD)(file_size >> GetSectorSizeShiftCount()));
|
||||
SetBlockCount((uint32_t)(size >> GetSectorSizeShiftCount()));
|
||||
|
||||
// Effective size must be a multiple of the sector size
|
||||
file_size = (file_size / GetSectorSizeInBytes()) * GetSectorSizeInBytes();
|
||||
size = (size / GetSectorSizeInBytes()) * GetSectorSizeInBytes();
|
||||
|
||||
FinalizeSetup(path, file_size);
|
||||
FinalizeSetup(path, size);
|
||||
}
|
||||
|
||||
vector<byte> SCSIHD::InquiryInternal() const
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI hard disk ]
|
||||
|
@ -17,19 +17,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "disk.h"
|
||||
#include "file_support.h"
|
||||
#include "filepath.h"
|
||||
|
||||
class SCSIHD : public Disk, public FileSupport
|
||||
class SCSIHD : public Disk
|
||||
{
|
||||
static constexpr const char *DEFAULT_PRODUCT = "SCSI HD";
|
||||
|
||||
public:
|
||||
|
||||
SCSIHD(const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2);
|
||||
SCSIHD(int, const unordered_set<uint32_t>&, bool, scsi_defs::scsi_level = scsi_level::SCSI_2);
|
||||
~SCSIHD() override = default;
|
||||
SCSIHD(SCSIHD&) = delete;
|
||||
SCSIHD& operator=(const SCSIHD&) = delete;
|
||||
|
||||
void FinalizeSetup(const Filepath&, off_t, off_t = 0);
|
||||
|
||||
|
@ -44,5 +41,7 @@ public:
|
|||
|
||||
private:
|
||||
|
||||
using super = Disk;
|
||||
|
||||
scsi_defs::scsi_level scsi_level;
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI NEC "Genuine" Hard Disk]
|
||||
|
@ -54,18 +54,18 @@ void SCSIHD_NEC::Open(const Filepath& path)
|
|||
}
|
||||
|
||||
// Get file size
|
||||
off_t file_size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
|
||||
// NEC root sector
|
||||
array<BYTE, 512> root_sector;
|
||||
if (file_size >= (off_t)root_sector.size() && !fio.Read(root_sector.data(), root_sector.size())) {
|
||||
if (size >= (off_t)root_sector.size() && !fio.Read(root_sector.data(), root_sector.size())) {
|
||||
fio.Close();
|
||||
throw io_exception("Can't read NEC hard disk file root sector");
|
||||
}
|
||||
fio.Close();
|
||||
|
||||
// Effective size must be a multiple of 512
|
||||
file_size = (file_size / 512) * 512;
|
||||
size = (size / 512) * 512;
|
||||
|
||||
int image_size = 0;
|
||||
int sector_size = 0;
|
||||
|
@ -76,11 +76,11 @@ void SCSIHD_NEC::Open(const Filepath& path)
|
|||
if (const char *ext = path.GetFileExt(); !strcasecmp(ext, ".hdn")) {
|
||||
// Assuming sector size 512, number of sectors 25, number of heads 8 as default settings
|
||||
image_offset = 0;
|
||||
image_size = (int)file_size;
|
||||
image_size = (int)size;
|
||||
sector_size = 512;
|
||||
sectors = 25;
|
||||
heads = 8;
|
||||
cylinders = (int)(file_size >> 9);
|
||||
cylinders = (int)(size >> 9);
|
||||
cylinders >>= 3;
|
||||
cylinders /= 25;
|
||||
}
|
||||
|
@ -113,23 +113,23 @@ void SCSIHD_NEC::Open(const Filepath& path)
|
|||
}
|
||||
|
||||
// Image size consistency check
|
||||
if (image_offset + image_size > file_size || image_size % sector_size != 0) {
|
||||
if (image_offset + image_size > size || image_size % sector_size != 0) {
|
||||
throw io_exception("Image size consistency check failed");
|
||||
}
|
||||
|
||||
// Calculate sector size
|
||||
for (file_size = 16; file_size > 0; --file_size) {
|
||||
if ((1 << file_size) == sector_size)
|
||||
for (size = 16; size > 0; --size) {
|
||||
if ((1 << size) == sector_size)
|
||||
break;
|
||||
}
|
||||
if (file_size <= 0 || file_size > 16) {
|
||||
if (size <= 0 || size > 16) {
|
||||
throw io_exception("Invalid NEC disk size");
|
||||
}
|
||||
SetSectorSizeShiftCount((uint32_t)file_size);
|
||||
SetSectorSizeShiftCount((uint32_t)size);
|
||||
|
||||
SetBlockCount(image_size >> GetSectorSizeShiftCount());
|
||||
|
||||
FinalizeSetup(path, file_size, image_offset);
|
||||
FinalizeSetup(path, size, image_offset);
|
||||
}
|
||||
|
||||
vector<byte> SCSIHD_NEC::InquiryInternal() const
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Copyright (C) 2014-2020 GIMONS
|
||||
// Copyright (C) akuker
|
||||
//
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// Licensed under the BSD 3-Clause License.
|
||||
// See LICENSE file in the project root folder.
|
||||
//
|
||||
// [ SCSI NEC "Genuine" Hard Disk]
|
||||
|
@ -20,7 +20,7 @@
|
|||
#include <unordered_set>
|
||||
#include <map>
|
||||
|
||||
using namespace std; //NOSONAR Not relevant for rascsi
|
||||
using namespace std;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
|
@ -31,10 +31,8 @@ class SCSIHD_NEC : public SCSIHD
|
|||
{
|
||||
public:
|
||||
|
||||
SCSIHD_NEC() : SCSIHD(sector_sizes, false) {}
|
||||
explicit SCSIHD_NEC(int lun) : SCSIHD(lun, sector_sizes, false) {}
|
||||
~SCSIHD_NEC() override = default;
|
||||
SCSIHD_NEC(SCSIHD_NEC&) = delete;
|
||||
SCSIHD_NEC& operator=(const SCSIHD_NEC&) = delete;
|
||||
|
||||
void Open(const Filepath&) override;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
using namespace scsi_command_util;
|
||||
|
||||
SCSIMO::SCSIMO(const unordered_set<uint32_t>& sector_sizes) : Disk("SCMO")
|
||||
SCSIMO::SCSIMO(int lun, const unordered_set<uint32_t>& sector_sizes) : Disk("SCMO", lun)
|
||||
{
|
||||
SetSectorSizes(sector_sizes);
|
||||
|
||||
|
@ -45,24 +45,24 @@ void SCSIMO::Open(const Filepath& path)
|
|||
}
|
||||
|
||||
// Get file size
|
||||
off_t file_size = fio.GetFileSize();
|
||||
off_t size = fio.GetFileSize();
|
||||
fio.Close();
|
||||
|
||||
// For some capacities there are hard-coded, well-defined sector sizes and block counts
|
||||
if (!SetGeometryForCapacity(file_size)) {
|
||||
if (!SetGeometryForCapacity(size)) {
|
||||
// Sector size (default 512 bytes) and number of blocks
|
||||
SetSectorSizeInBytes(GetConfiguredSectorSize() ? GetConfiguredSectorSize() : 512);
|
||||
SetBlockCount(file_size >> GetSectorSizeShiftCount());
|
||||
SetBlockCount(size >> GetSectorSizeShiftCount());
|
||||
}
|
||||
|
||||
SetReadOnly(false);
|
||||
SetProtectable(true);
|
||||
SetProtected(false);
|
||||
|
||||
Disk::Open(path);
|
||||
FileSupport::SetPath(path);
|
||||
super::Open(path);
|
||||
SetPath(path);
|
||||
|
||||
SetUpCache(path);
|
||||
SetUpCache(path, 0);
|
||||
|
||||
// Attention if ready
|
||||
if (IsReady()) {
|
||||
|
|
|
@ -15,19 +15,16 @@
|
|||
#pragma once
|
||||
|
||||
#include "disk.h"
|
||||
#include "file_support.h"
|
||||
#include "filepath.h"
|
||||
|
||||
using Geometry = pair<uint32_t, uint32_t>;
|
||||
|
||||
class SCSIMO : public Disk, public FileSupport
|
||||
class SCSIMO : public Disk
|
||||
{
|
||||
public:
|
||||
|
||||
explicit SCSIMO(const unordered_set<uint32_t>&);
|
||||
SCSIMO(int, const unordered_set<uint32_t>&);
|
||||
~SCSIMO() override = default;
|
||||
SCSIMO(SCSIMO&) = delete;
|
||||
SCSIMO& operator=(const SCSIMO&) = delete;
|
||||
|
||||
void Open(const Filepath&) override;
|
||||
|
||||
|
@ -42,6 +39,8 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
using super = Disk;
|
||||
|
||||
void AddOptionPage(map<int, vector<byte>>&, bool) const;
|
||||
|
||||
bool SetGeometryForCapacity(uint64_t);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "fileio.h"
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <cassert>
|
||||
|
||||
Fileio::~Fileio()
|
||||
{
|
||||
|
@ -30,7 +31,7 @@ bool Fileio::Open(const char *fname, OpenMode mode, bool directIO)
|
|||
}
|
||||
|
||||
// Default mode
|
||||
mode_t omode = directIO ? O_DIRECT : 0;
|
||||
const mode_t omode = directIO ? O_DIRECT : 0;
|
||||
|
||||
switch (mode) {
|
||||
case OpenMode::ReadOnly:
|
||||
|
@ -111,10 +112,10 @@ off_t Fileio::GetFileSize() const
|
|||
assert(handle >= 0);
|
||||
|
||||
// Get file position in 64bit
|
||||
off_t cur = lseek(handle, 0, SEEK_CUR);
|
||||
const off_t cur = lseek(handle, 0, SEEK_CUR);
|
||||
|
||||
// Get file size in64bitで
|
||||
off_t end = lseek(handle, 0, SEEK_END);
|
||||
const off_t end = lseek(handle, 0, SEEK_END);
|
||||
|
||||
// Return to start position
|
||||
Seek(cur);
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "filepath.h"
|
||||
#include <sys/types.h>
|
||||
#include <cstdlib>
|
||||
|
||||
class Fileio
|
||||
{
|
||||
|
@ -25,8 +25,8 @@ public:
|
|||
|
||||
Fileio() = default;
|
||||
virtual ~Fileio();
|
||||
Fileio(Fileio&) = delete;
|
||||
Fileio& operator=(const Fileio&) = delete;
|
||||
Fileio(Fileio&) = default;
|
||||
Fileio& operator=(const Fileio&) = default;
|
||||
|
||||
bool Open(const char *fname, OpenMode mode);
|
||||
bool Open(const Filepath& path, OpenMode mode);
|
||||
|
|
|
@ -8,16 +8,11 @@
|
|||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "os.h"
|
||||
#include "filepath.h"
|
||||
#include "config.h"
|
||||
#include "fileio.h"
|
||||
#include <libgen.h>
|
||||
|
||||
Filepath::Filepath()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cassert>
|
||||
|
||||
Filepath& Filepath::operator=(const Filepath& path)
|
||||
{
|
||||
|
@ -27,15 +22,6 @@ Filepath& Filepath::operator=(const Filepath& path)
|
|||
return *this;
|
||||
}
|
||||
|
||||
void Filepath::Clear()
|
||||
{
|
||||
// Clear the path and each part
|
||||
m_szPath[0] = '\0';
|
||||
m_szDir[0] = '\0';
|
||||
m_szFile[0] = '\0';
|
||||
m_szExt[0] = '\0';
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// File settings (user) for MBCS
|
||||
|
@ -69,7 +55,7 @@ void Filepath::Split()
|
|||
char *pDir = strdup(m_szPath);
|
||||
const char *pDirName = dirname(pDir);
|
||||
char *pBase = strdup(m_szPath);
|
||||
char *pBaseName = basename(pBase);
|
||||
const char *pBaseName = basename(pBase);
|
||||
const char *pExtName = strrchr(pBaseName, '.');
|
||||
|
||||
// Transmit
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
|
||||
using TCHAR = char;
|
||||
|
||||
class Fileio;
|
||||
|
||||
static const int _MAX_EXT = 256;
|
||||
static const int _MAX_DIR = 256;
|
||||
static const int _MAX_PATH = 260;
|
||||
|
@ -30,15 +28,13 @@ static const int FILEPATH_MAX = _MAX_PATH;
|
|||
//===========================================================================
|
||||
class Filepath
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
Filepath();
|
||||
virtual ~Filepath() = default;
|
||||
Filepath(Filepath&) = delete;
|
||||
Filepath() = default;
|
||||
~Filepath() = default;
|
||||
Filepath(Filepath&) = default;
|
||||
Filepath& operator=(const Filepath&);
|
||||
|
||||
void Clear();
|
||||
void SetPath(const char *); // File settings (user) for MBCS
|
||||
const char *GetPath() const { return m_szPath; } // Get path name
|
||||
const char *GetFileExt() const; // Get short name (LPCTSTR)
|
||||
|
@ -46,10 +42,10 @@ public:
|
|||
private:
|
||||
|
||||
void Split(); // Split the path
|
||||
TCHAR m_szPath[_MAX_PATH]; // File path
|
||||
TCHAR m_szDir[_MAX_DIR]; // Directory
|
||||
TCHAR m_szFile[_MAX_FNAME]; // File
|
||||
TCHAR m_szExt[_MAX_EXT]; // Extension
|
||||
TCHAR m_szPath[_MAX_PATH] = {}; // File path
|
||||
TCHAR m_szDir[_MAX_DIR] = {}; // Directory
|
||||
TCHAR m_szFile[_MAX_FNAME] = {}; // File
|
||||
TCHAR m_szExt[_MAX_EXT] = {}; // Extension
|
||||
|
||||
static TCHAR FileExt[_MAX_FNAME + _MAX_DIR]; // Short name (TCHAR)
|
||||
};
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "config.h"
|
||||
#include "log.h"
|
||||
#include <array>
|
||||
#ifdef __linux
|
||||
#ifdef __linux__
|
||||
#include <sys/epoll.h>
|
||||
#endif
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user