mirror of
https://github.com/akuker/RASCSI.git
synced 2024-12-01 13:51:43 +00:00
Merge tag 'v22.08.01'
RaSCSI version 22.08.01
This commit is contained in:
commit
9d10347b21
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@ -1 +1 @@
|
||||
* @akuker @erichelgeson
|
||||
* @akuker @erichelgeson @rdmark
|
||||
|
10
README.md
10
README.md
@ -9,14 +9,18 @@ RaSCSI Reloaded is using the <a href="https://datasift.github.io/gitflow/Introdu
|
||||
- The *master* branch should always reflect the contents of the last stable release
|
||||
- The *develop* branch should contain the latest tested & approved updates. Pull requests should be used to merge changes into develop.
|
||||
- The rest of the feature branches are for developing new features
|
||||
- A tag will be created for each "release". The releases will be named <year>.<month> (for the first release of the month). Hot fixes, if necessary, will be released as <year>.<month>.<release number>. For example, the first release in January 2021 will be release "21.01". If a hot-fix is needed for this release, the first hotfix will be "21.01.1".
|
||||
- A tag will be created for each "release". The releases will be named <year>.<month>.<release number> where the release number is incremented for each subsequent release tagged in the same calendar month. The first release of the month of January 2021 is called "21.01.01", the second one in the same month "21.01.02" and so on.
|
||||
|
||||
Typically, releases will only be planned every few months.
|
||||
|
||||
When you are ready to contribute code to RaSCSI Reloaded, follow the <a href="https://docs.github.com/en/get-started/quickstart/contributing-to-projects">GitHub Forking and Pull Request workflow</a> to create your own fork where you can make changes, and then contribute it back to the project. Please remember to always make Pull Requests against the *develop* branch.
|
||||
|
||||
If you want to add a new translation, or improve upon an existing one, please follow the <a href="https://github.com/akuker/RASCSI/blob/develop/python/web/README.md#localizing-the-web-interface">instructions in the Web Interface README</a>. Once the translation is complete, please use the same workflow as above to contribute it to the project.
|
||||
|
||||
<a href="https://www.tindie.com/stores/landogriffin/?ref=offsite_badges&utm_source=sellers_akuker&utm_medium=badges&utm_campaign=badge_large"><img src="https://d2ss6ovg47m0r5.cloudfront.net/badges/tindie-larges.png" alt="I sell on Tindie" width="200" height="104"></a>
|
||||
|
||||
# Github Sponsors
|
||||
Thank you to all of the Github sponsors who support the development community!
|
||||
# GitHub Sponsors
|
||||
Thank you to all of the GitHub sponsors who support the development community!
|
||||
|
||||
Special thank you to the Gold level sponsors!
|
||||
- <a href="https://github.com/mikelord68">@mikelord68</a>
|
||||
|
@ -13,7 +13,7 @@ logo="""
|
||||
~ (║|_____|║) ~\n
|
||||
( : ║ . __ ║ : )\n
|
||||
~ .╚╦═════╦╝. ~\n
|
||||
( ¯¯¯¯¯¯¯ ) RaSCSI Assistant\n
|
||||
( ¯¯¯¯¯¯¯ ) RaSCSI Reloaded Assistant\n
|
||||
'~ .~~~. ~'\n
|
||||
'~'\n
|
||||
"""
|
||||
@ -55,6 +55,8 @@ OLED_INSTALL_PATH="$BASE/python/oled"
|
||||
CTRLBOARD_INSTALL_PATH="$BASE/python/ctrlboard"
|
||||
PYTHON_COMMON_PATH="$BASE/python/common"
|
||||
SYSTEMD_PATH="/etc/systemd/system"
|
||||
SSL_CERTS_PATH="/etc/ssl/certs"
|
||||
SSL_KEYS_PATH="/etc/ssl/private"
|
||||
HFS_FORMAT=/usr/bin/hformat
|
||||
HFDISK_BIN=/usr/bin/hfdisk
|
||||
LIDO_DRIVER=$BASE/lido-driver.img
|
||||
@ -80,7 +82,7 @@ function sudoCheck() {
|
||||
|
||||
# install all dependency packages for RaSCSI Service
|
||||
function installPackages() {
|
||||
sudo apt-get update && sudo apt-get install git libspdlog-dev libpcap-dev genisoimage python3 python3-venv python3-dev python3-pip nginx libpcap-dev protobuf-compiler bridge-utils libev-dev libevdev2 -y </dev/null
|
||||
sudo apt-get update && sudo apt-get install git libspdlog-dev libpcap-dev genisoimage python3 python3-venv python3-dev python3-pip nginx libpcap-dev protobuf-compiler bridge-utils libev-dev libevdev2 unar -y </dev/null
|
||||
}
|
||||
|
||||
# cache the pip packages
|
||||
@ -88,7 +90,7 @@ function cachePipPackages(){
|
||||
pushd $WEB_INSTALL_PATH
|
||||
# Refresh the sudo authentication, which shouldn't trigger another password prompt
|
||||
sudo -v
|
||||
sudo pip install -r ./requirements.txt
|
||||
sudo pip3 install -r ./requirements.txt
|
||||
popd
|
||||
}
|
||||
|
||||
@ -147,6 +149,21 @@ function installRaScsiWebInterface() {
|
||||
|
||||
sudo usermod -a -G $USER www-data
|
||||
|
||||
if [ -f "$SSL_CERTS_PATH/rascsi-web.crt" ]; then
|
||||
echo "SSL certificate $SSL_CERTS_PATH/rascsi-web.crt already exists."
|
||||
else
|
||||
echo "SSL certificate $SSL_CERTS_PATH/rascsi-web.crt does not exist; creating self-signed certificate..."
|
||||
sudo mkdir -p "$SSL_CERTS_PATH" || true
|
||||
sudo mkdir -p "$SSL_KEYS_PATH" || true
|
||||
sudo openssl req -x509 -nodes -sha256 -days 3650 \
|
||||
-newkey rsa:4096 \
|
||||
-keyout "$SSL_KEYS_PATH/rascsi-web.key" \
|
||||
-out "$SSL_CERTS_PATH/rascsi-web.crt" \
|
||||
-subj '/CN=rascsi' \
|
||||
-addext 'subjectAltName=DNS:rascsi' \
|
||||
-addext 'extendedKeyUsage=serverAuth'
|
||||
fi
|
||||
|
||||
sudo systemctl reload nginx || true
|
||||
}
|
||||
|
||||
@ -753,7 +770,7 @@ function setupWirelessNetworking() {
|
||||
|
||||
# Downloads, compiles, and installs Netatalk (AppleShare server)
|
||||
function installNetatalk() {
|
||||
NETATALK_VERSION="2-220702"
|
||||
NETATALK_VERSION="2-220801"
|
||||
AFP_SHARE_PATH="$HOME/afpshare"
|
||||
AFP_SHARE_NAME="Pi File Server"
|
||||
|
||||
@ -776,7 +793,7 @@ function installMacproxy {
|
||||
echo -n "Enter a port number 1024 - 65535, or press Enter to use the default port: "
|
||||
|
||||
read -r CHOICE
|
||||
if [ $CHOICE -ge "1024" ] && [ $CHOICE -le "65535" ]; then
|
||||
if [[ $CHOICE -ge "1024" ]] && [[ $CHOICE -le "65535" ]]; then
|
||||
PORT=$CHOICE
|
||||
else
|
||||
echo "Using the default port $PORT"
|
||||
@ -784,20 +801,19 @@ function installMacproxy {
|
||||
|
||||
( sudo apt-get update && sudo apt-get install python3 python3-venv --assume-yes ) </dev/null
|
||||
|
||||
MACPROXY_VER="21.12.3"
|
||||
MACPROXY_VER="22.8"
|
||||
MACPROXY_PATH="$HOME/macproxy-$MACPROXY_VER"
|
||||
if [ -d "$MACPROXY_PATH" ]; then
|
||||
echo "The $MACPROXY_PATH directory already exists. Delete it to proceed with the installation."
|
||||
exit 1
|
||||
echo "The $MACPROXY_PATH directory already exists. Deleting before downloading again..."
|
||||
sudo rm -rf "$MACPROXY_PATH"
|
||||
fi
|
||||
cd "$HOME" || exit 1
|
||||
wget -O "macproxy-$MACPROXY_VER.tar.gz" "https://github.com/rdmark/macproxy/archive/refs/tags/$MACPROXY_VER.tar.gz" </dev/null
|
||||
wget -O "macproxy-$MACPROXY_VER.tar.gz" "https://github.com/rdmark/macproxy/archive/refs/tags/v$MACPROXY_VER.tar.gz" </dev/null
|
||||
tar -xzvf "macproxy-$MACPROXY_VER.tar.gz"
|
||||
|
||||
stopMacproxy
|
||||
sudo cp "$MACPROXY_PATH/macproxy.service" "$SYSTEMD_PATH"
|
||||
sudo sed -i /^ExecStart=/d "$SYSTEMD_PATH/macproxy.service"
|
||||
sudo sed -i "8 i ExecStart=$MACPROXY_PATH/start_macproxy.sh" "$SYSTEMD_PATH/macproxy.service"
|
||||
sudo sed -i "8 i ExecStart=$MACPROXY_PATH/start_macproxy.sh -p=$PORT" "$SYSTEMD_PATH/macproxy.service"
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable macproxy
|
||||
startMacproxy
|
||||
@ -1048,12 +1064,13 @@ function runChoice() {
|
||||
echo "- Modify user groups and permissions"
|
||||
echo "- Install binaries to /usr/local/bin"
|
||||
echo "- Install manpages to /usr/local/man"
|
||||
echo "- Create a self-signed certificate in /etc/ssl"
|
||||
sudoCheck
|
||||
createImagesDir
|
||||
createCfgDir
|
||||
configureTokenAuth
|
||||
stopOldWebInterface
|
||||
updateRaScsiGit
|
||||
createImagesDir
|
||||
createCfgDir
|
||||
installPackages
|
||||
stopRaScsiScreen
|
||||
stopRaScsi
|
||||
@ -1091,9 +1108,10 @@ function runChoice() {
|
||||
echo "- Install binaries to /usr/local/bin"
|
||||
echo "- Install manpages to /usr/local/man"
|
||||
sudoCheck
|
||||
createImagesDir
|
||||
createCfgDir
|
||||
configureTokenAuth
|
||||
updateRaScsiGit
|
||||
createImagesDir
|
||||
installPackages
|
||||
stopRaScsiScreen
|
||||
stopRaScsi
|
||||
@ -1168,6 +1186,7 @@ function runChoice() {
|
||||
echo "- Install additional packages with apt-get"
|
||||
echo "- Add and modify systemd services"
|
||||
sudoCheck
|
||||
stopMacproxy
|
||||
installMacproxy
|
||||
echo "Installing Web Proxy Server - Complete!"
|
||||
;;
|
||||
@ -1179,8 +1198,8 @@ function runChoice() {
|
||||
echo "- Install binaries to /usr/local/bin"
|
||||
echo "- Install manpages to /usr/local/man"
|
||||
sudoCheck
|
||||
updateRaScsiGit
|
||||
createImagesDir
|
||||
updateRaScsiGit
|
||||
installPackages
|
||||
stopRaScsi
|
||||
compileRaScsi
|
||||
@ -1196,9 +1215,10 @@ function runChoice() {
|
||||
echo "- Modify and enable Apache2 and Nginx web service"
|
||||
echo "- Create directories and change permissions"
|
||||
echo "- Modify user groups and permissions"
|
||||
echo "- Create a self-signed certificate in /etc/ssl"
|
||||
sudoCheck
|
||||
updateRaScsiGit
|
||||
createCfgDir
|
||||
updateRaScsiGit
|
||||
installPackages
|
||||
preparePythonCommon
|
||||
cachePipPackages
|
||||
|
@ -1,12 +1,21 @@
|
||||
# RaSCSI Python Apps
|
||||
# RaSCSI Reloaded Python Apps
|
||||
|
||||
This directory contains Python-based clients for RaSCSI as well as common
|
||||
This directory contains Python-based clients for RaSCSI Reloaded as well as common
|
||||
packages that are shared among the clients.
|
||||
|
||||
The following paragraphs in this README contain instructions that are shared
|
||||
among all Python apps.
|
||||
|
||||
### Static analysis with pylint
|
||||
## Supported Python interpreter
|
||||
|
||||
The policy in this project is to support the Python 3 interpreter that comes
|
||||
standard with the current stable, as well as previous stable release of Debian.
|
||||
|
||||
At the time of writing they are:
|
||||
- Python 3.9.2 in [Debian Bullseye](https://packages.debian.org/bullseye/python3)
|
||||
- Python 3.7.3 in [Debian Buster](https://packages.debian.org/buster/python3)
|
||||
|
||||
## Static analysis with pylint
|
||||
|
||||
It is recommended to run pylint against new code to protect against bugs
|
||||
and keep the code readable and maintainable.
|
||||
@ -29,4 +38,4 @@ pylint web/src/web.py
|
||||
pylint common/src
|
||||
pylint web/src
|
||||
pylint oled/src
|
||||
```
|
||||
```
|
||||
|
@ -12,6 +12,15 @@ CONFIG_FILE_SUFFIX = "json"
|
||||
# File ending used for drive properties files
|
||||
PROPERTIES_SUFFIX = "properties"
|
||||
|
||||
# Supported archive file suffixes
|
||||
ARCHIVE_FILE_SUFFIXES = [
|
||||
"zip",
|
||||
"sit",
|
||||
"tar",
|
||||
"gz",
|
||||
"7z"
|
||||
]
|
||||
|
||||
# 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)]
|
||||
|
@ -5,9 +5,9 @@ Module for methods reading from and writing to the file system
|
||||
import os
|
||||
import logging
|
||||
import asyncio
|
||||
from functools import lru_cache
|
||||
from pathlib import PurePath
|
||||
from zipfile import ZipFile, is_zipfile
|
||||
from re import escape, findall
|
||||
from time import time
|
||||
from subprocess import run, CalledProcessError
|
||||
from json import dump, load
|
||||
@ -16,10 +16,11 @@ from shutil import copyfile
|
||||
import requests
|
||||
|
||||
import rascsi_interface_pb2 as proto
|
||||
from rascsi.common_settings import CFG_DIR, CONFIG_FILE_SUFFIX, PROPERTIES_SUFFIX, RESERVATIONS
|
||||
from rascsi.common_settings import CFG_DIR, CONFIG_FILE_SUFFIX, PROPERTIES_SUFFIX, ARCHIVE_FILE_SUFFIXES, RESERVATIONS
|
||||
from rascsi.ractl_cmds import RaCtlCmds
|
||||
from rascsi.return_codes import ReturnCodes
|
||||
from rascsi.socket_cmds import SocketCmds
|
||||
from util import unarchiver
|
||||
|
||||
|
||||
class FileCmds:
|
||||
@ -97,19 +98,31 @@ class FileCmds:
|
||||
prop = process["conf"]
|
||||
else:
|
||||
prop = False
|
||||
if file.name.lower().endswith(".zip"):
|
||||
zip_path = f"{server_info['image_dir']}/{file.name}"
|
||||
if is_zipfile(zip_path):
|
||||
zipfile = ZipFile(zip_path)
|
||||
# Get a list of (str) containing all zipfile members
|
||||
zip_members = zipfile.namelist()
|
||||
# Strip out directories from the list
|
||||
zip_members = [x for x in zip_members if not x.endswith("/")]
|
||||
else:
|
||||
logging.warning("%s is an invalid zip file", zip_path)
|
||||
zip_members = False
|
||||
else:
|
||||
zip_members = False
|
||||
|
||||
archive_contents = []
|
||||
if PurePath(file.name).suffix.lower()[1:] in ARCHIVE_FILE_SUFFIXES:
|
||||
try:
|
||||
archive_info = self._get_archive_info(
|
||||
f"{server_info['image_dir']}/{file.name}",
|
||||
_cache_extra_key=file.size
|
||||
)
|
||||
|
||||
properties_files = [x["path"]
|
||||
for x in archive_info["members"]
|
||||
if x["path"].endswith(PROPERTIES_SUFFIX)]
|
||||
|
||||
for member in archive_info["members"]:
|
||||
if member["is_dir"] or member["is_resource_fork"]:
|
||||
continue
|
||||
|
||||
if PurePath(member["path"]).suffix.lower()[1:] == PROPERTIES_SUFFIX:
|
||||
member["is_properties_file"] = True
|
||||
elif f"{member['path']}.{PROPERTIES_SUFFIX}" in properties_files:
|
||||
member["related_properties_file"] = f"{member['path']}.{PROPERTIES_SUFFIX}"
|
||||
|
||||
archive_contents.append(member)
|
||||
except (unarchiver.LsarCommandError, unarchiver.LsarOutputError):
|
||||
pass
|
||||
|
||||
size_mb = "{:,.1f}".format(file.size / 1024 / 1024)
|
||||
dtype = proto.PbDeviceType.Name(file.type)
|
||||
@ -119,7 +132,7 @@ class FileCmds:
|
||||
"size_mb": size_mb,
|
||||
"detected_type": dtype,
|
||||
"prop": prop,
|
||||
"zip_members": zip_members,
|
||||
"archive_contents": archive_contents,
|
||||
})
|
||||
|
||||
return {"status": result.status, "msg": result.msg, "files": files}
|
||||
@ -266,62 +279,73 @@ class FileCmds:
|
||||
"parameters": parameters,
|
||||
}
|
||||
|
||||
def unzip_file(self, file_name, member=False, members=False):
|
||||
def extract_image(self, file_path, members=None, move_properties_files_to_config=True):
|
||||
"""
|
||||
Takes (str) file_name, optional (str) member, optional (list) of (str) members
|
||||
file_name is the name of the zip file to unzip
|
||||
member is the full path to the particular file in the zip file to unzip
|
||||
members contains all of the full paths to each of the zip archive members
|
||||
Returns (dict) with (boolean) status and (list of str) msg
|
||||
Takes (str) file_path, (list) members, optional (bool) move_properties_files_to_config
|
||||
file_name is the path of the archive file to extract, relative to the images directory
|
||||
members is a list of file paths in the archive file to extract
|
||||
move_properties_files_to_config controls if .properties files are auto-moved to CFG_DIR
|
||||
Returns (dict) result
|
||||
"""
|
||||
server_info = self.ractl.get_server_info()
|
||||
prop_flag = False
|
||||
|
||||
if not member:
|
||||
unzip_proc = asyncio.run(self.run_async("unzip", [
|
||||
"-d",
|
||||
server_info['image_dir'],
|
||||
"-n",
|
||||
"-j",
|
||||
f"{server_info['image_dir']}/{file_name}",
|
||||
]))
|
||||
if members:
|
||||
for path in members:
|
||||
if path.endswith(PROPERTIES_SUFFIX):
|
||||
name = PurePath(path).name
|
||||
self.rename_file(f"{server_info['image_dir']}/{name}", f"{CFG_DIR}/{name}")
|
||||
prop_flag = True
|
||||
else:
|
||||
member = escape(member)
|
||||
unzip_proc = asyncio.run(self.run_async("unzip", [
|
||||
"-d",
|
||||
server_info['image_dir'],
|
||||
"-n",
|
||||
"-j",
|
||||
f"{server_info['image_dir']}/{file_name}",
|
||||
member,
|
||||
]))
|
||||
# Attempt to unzip a properties file in the same archive dir
|
||||
unzip_prop = asyncio.run(self.run_async("unzip", [
|
||||
"-d",
|
||||
CFG_DIR,
|
||||
"-n",
|
||||
"-j",
|
||||
f"{server_info['image_dir']}/{file_name}",
|
||||
f"{member}.{PROPERTIES_SUFFIX}",
|
||||
]))
|
||||
if not members:
|
||||
return {
|
||||
"status": False,
|
||||
"return_code": ReturnCodes.EXTRACTIMAGE_NO_FILES_SPECIFIED,
|
||||
}
|
||||
|
||||
if unzip_prop["returncode"] == 0:
|
||||
prop_flag = True
|
||||
if unzip_proc["returncode"] != 0:
|
||||
logging.warning("Unzipping failed: %s", unzip_proc["stderr"])
|
||||
return {"status": False, "msg": unzip_proc["stderr"]}
|
||||
try:
|
||||
extract_result = unarchiver.extract_archive(
|
||||
f"{server_info['image_dir']}/{file_path}",
|
||||
members=members,
|
||||
output_dir=server_info["image_dir"],
|
||||
)
|
||||
|
||||
properties_files_moved = []
|
||||
if move_properties_files_to_config:
|
||||
for file in extract_result["extracted"]:
|
||||
if file.get("name").endswith(".properties"):
|
||||
if (self.rename_file(
|
||||
file["absolute_path"],
|
||||
f"{CFG_DIR}/{file['name']}"
|
||||
)):
|
||||
properties_files_moved.append({
|
||||
"status": True,
|
||||
"name": file["path"],
|
||||
"path": f"{CFG_DIR}/{file['name']}",
|
||||
})
|
||||
else:
|
||||
properties_files_moved.append({
|
||||
"status": False,
|
||||
"name": file["path"],
|
||||
"path": f"{CFG_DIR}/{file['name']}",
|
||||
})
|
||||
|
||||
return {
|
||||
"status": True,
|
||||
"return_code": ReturnCodes.EXTRACTIMAGE_SUCCESS,
|
||||
"parameters": {
|
||||
"count": len(extract_result["extracted"]),
|
||||
},
|
||||
"extracted": extract_result["extracted"],
|
||||
"skipped": extract_result["skipped"],
|
||||
"properties_files_moved": properties_files_moved,
|
||||
}
|
||||
except unarchiver.UnarNoFilesExtractedError:
|
||||
return {
|
||||
"status": False,
|
||||
"return_code": ReturnCodes.EXTRACTIMAGE_NO_FILES_EXTRACTED,
|
||||
}
|
||||
except (unarchiver.UnarCommandError, unarchiver.UnarUnexpectedOutputError) as error:
|
||||
return {
|
||||
"status": False,
|
||||
"return_code": ReturnCodes.EXTRACTIMAGE_COMMAND_ERROR,
|
||||
"parameters": {
|
||||
"error": error,
|
||||
}
|
||||
}
|
||||
|
||||
unzipped = findall(
|
||||
"(?:inflating|extracting):(.+)\n",
|
||||
unzip_proc["stdout"]
|
||||
)
|
||||
return {"status": True, "msg": unzipped, "prop_flag": prop_flag}
|
||||
|
||||
def download_file_to_iso(self, url, *iso_args):
|
||||
"""
|
||||
@ -652,3 +676,14 @@ class FileCmds:
|
||||
logging.info("stderr: %s", stderr)
|
||||
|
||||
return {"returncode": proc.returncode, "stdout": stdout, "stderr": stderr}
|
||||
|
||||
# noinspection PyMethodMayBeStatic
|
||||
@lru_cache(maxsize=32)
|
||||
def _get_archive_info(self, file_path, **kwargs):
|
||||
"""
|
||||
Cached wrapper method to improve performance, e.g. on index screen
|
||||
"""
|
||||
try:
|
||||
return unarchiver.inspect_archive(file_path)
|
||||
except (unarchiver.LsarCommandError, unarchiver.LsarOutputError):
|
||||
raise
|
||||
|
@ -20,3 +20,7 @@ class ReturnCodes:
|
||||
READDRIVEPROPS_SUCCESS = 70
|
||||
READDRIVEPROPS_COULD_NOT_READ = 71
|
||||
ATTACHIMAGE_COULD_NOT_ATTACH = 80
|
||||
EXTRACTIMAGE_SUCCESS = 90
|
||||
EXTRACTIMAGE_NO_FILES_SPECIFIED = 91
|
||||
EXTRACTIMAGE_NO_FILES_EXTRACTED = 92
|
||||
EXTRACTIMAGE_COMMAND_ERROR = 93
|
||||
|
0
python/common/src/util/__init__.py
Normal file
0
python/common/src/util/__init__.py
Normal file
45
python/common/src/util/run.py
Normal file
45
python/common/src/util/run.py
Normal file
@ -0,0 +1,45 @@
|
||||
"""
|
||||
Utility module for running system commands with basic logging
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
|
||||
|
||||
def run(program, args=None):
|
||||
""" Run a command and return its output """
|
||||
return asyncio.run(run_async(program, args))
|
||||
|
||||
|
||||
async def run_async(program, args=None):
|
||||
""" Run a command in the background """
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
program,
|
||||
*args,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
|
||||
stdout, stderr = await proc.communicate()
|
||||
|
||||
logging.info(
|
||||
"Executed command \"%s %s\" with status code %d",
|
||||
program,
|
||||
" ".join(args),
|
||||
proc.returncode
|
||||
)
|
||||
|
||||
if stdout:
|
||||
stdout = stdout.decode()
|
||||
logging.debug(stdout)
|
||||
|
||||
if stderr:
|
||||
stderr = stderr.decode()
|
||||
logging.warning(stderr)
|
||||
|
||||
return {
|
||||
"returncode": proc.returncode,
|
||||
"stdout": stdout,
|
||||
"stderr": stderr,
|
||||
}
|
206
python/common/src/util/unarchiver.py
Normal file
206
python/common/src/util/unarchiver.py
Normal file
@ -0,0 +1,206 @@
|
||||
"""
|
||||
A minimal wrapper around 'The Unarchiver' command line tools (v1.10.1)
|
||||
https://theunarchiver.com/command-line
|
||||
Later versions (untested) available at: https://github.com/MacPaw/XADMaster
|
||||
"""
|
||||
|
||||
import logging
|
||||
import pathlib
|
||||
|
||||
from tempfile import TemporaryDirectory
|
||||
from re import escape, match
|
||||
from json import loads, JSONDecodeError
|
||||
from util.run import run
|
||||
|
||||
FORK_OUTPUT_TYPE_VISIBLE = "visible"
|
||||
FORK_OUTPUT_TYPE_HIDDEN = "hidden"
|
||||
|
||||
FORK_OUTPUT_TYPES = [FORK_OUTPUT_TYPE_VISIBLE, FORK_OUTPUT_TYPE_HIDDEN]
|
||||
|
||||
|
||||
def extract_archive(file_path, **kwargs):
|
||||
"""
|
||||
Extracts files from an archive
|
||||
Takes (str) file_path, and kwargs:
|
||||
- (list) members - list of (str) files to be extracted (all files are extracted if None)
|
||||
- (str) output_dir - directory to place the extracted files
|
||||
- (str) fork_output_type - output type for resource forks; "visible" for *.rsrc files, "hidden" for ._* files
|
||||
Returns (dict) of extracted and skipped members
|
||||
"""
|
||||
members = kwargs.get("members")
|
||||
|
||||
if kwargs.get("output_dir"):
|
||||
if not pathlib.Path(kwargs["output_dir"]).is_dir():
|
||||
raise ValueError("Argument output_dir must be a directory")
|
||||
output_dir = str(pathlib.Path(kwargs["output_dir"]).resolve())
|
||||
else:
|
||||
output_dir = str(pathlib.Path(file_path).parent.resolve())
|
||||
|
||||
if kwargs.get("fork_output_type"):
|
||||
if kwargs["fork_output_type"] not in FORK_OUTPUT_TYPES:
|
||||
raise ValueError(f"Argument fork_output_type must be one of: {','.join(FORK_OUTPUT_TYPES)} ")
|
||||
fork_output_type = kwargs["fork_output_type"]
|
||||
fork_output_type_args = ["-forks", fork_output_type or FORK_OUTPUT_TYPE_VISIBLE]
|
||||
else:
|
||||
fork_output_type = None
|
||||
fork_output_type_args = []
|
||||
|
||||
with TemporaryDirectory() as tmp_dir:
|
||||
unar_args = [
|
||||
"-output-directory",
|
||||
tmp_dir,
|
||||
"-force-skip",
|
||||
"-no-directory",
|
||||
*fork_output_type_args,
|
||||
'--',
|
||||
file_path,
|
||||
]
|
||||
|
||||
if members:
|
||||
for member in members:
|
||||
unar_args.append(escape(member))
|
||||
|
||||
process = run("unar", unar_args)
|
||||
|
||||
if process["returncode"] != 0:
|
||||
raise UnarCommandError(f"Non-zero return code: {process['returncode']}")
|
||||
|
||||
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]+)\.$"
|
||||
|
||||
lines = process["stdout"].rstrip("\n").split("\n")
|
||||
|
||||
if lines[-1] == unar_result_no_files:
|
||||
raise UnarNoFilesExtractedError
|
||||
|
||||
if match(unar_result_success, lines[-1]):
|
||||
extracted_members = []
|
||||
|
||||
for line in lines[1:-1]:
|
||||
line_matches = match(unar_file_extracted, line)
|
||||
if line_matches:
|
||||
matches = line_matches.groupdict()
|
||||
member = {
|
||||
"name": str(pathlib.PurePath(matches["path"]).name),
|
||||
"path": matches["path"],
|
||||
"size": matches["size"] or 0,
|
||||
"is_dir": False,
|
||||
"is_resource_fork": False,
|
||||
"absolute_path": str(pathlib.PurePath(tmp_dir).joinpath(matches["path"])),
|
||||
}
|
||||
|
||||
member_types = matches.get("types", "")
|
||||
if member_types.startswith(", "):
|
||||
member_types = member_types[2:].split(", ")
|
||||
else:
|
||||
member_types = member_types.split(", ")
|
||||
|
||||
if "dir" in member_types:
|
||||
member["is_dir"] = True
|
||||
|
||||
if "rsrc" in member_types:
|
||||
if not fork_output_type:
|
||||
continue
|
||||
|
||||
member["is_resource_fork"] = True
|
||||
|
||||
# Update names/paths to match unar resource fork naming convention
|
||||
if fork_output_type == FORK_OUTPUT_TYPE_HIDDEN:
|
||||
member["name"] = f"._{member['name']}"
|
||||
else:
|
||||
member["name"] += ".rsrc"
|
||||
member["path"] = str(pathlib.PurePath(member["path"]).parent.joinpath(member["name"]))
|
||||
member["absolute_path"] = str(pathlib.PurePath(tmp_dir).joinpath(member["path"]))
|
||||
|
||||
logging.debug("Extracted: %s -> %s", member['path'], member['absolute_path'])
|
||||
extracted_members.append(member)
|
||||
else:
|
||||
raise UnarUnexpectedOutputError(f"Unexpected output: {line}")
|
||||
|
||||
moved = []
|
||||
skipped = []
|
||||
for member in sorted(extracted_members, key=lambda m: m["path"]):
|
||||
source_path = pathlib.Path(member["absolute_path"])
|
||||
target_path = pathlib.Path(output_dir).joinpath(member["path"])
|
||||
member["absolute_path"] = str(target_path)
|
||||
|
||||
if target_path.exists():
|
||||
logging.info("Skipping temp file/dir as the target already exists: %s", target_path)
|
||||
skipped.append(member)
|
||||
continue
|
||||
|
||||
if member["is_dir"]:
|
||||
logging.debug("Creating empty dir: %s -> %s", source_path, target_path)
|
||||
target_path.mkdir(parents=True, exist_ok=True)
|
||||
moved.append(member)
|
||||
continue
|
||||
|
||||
# 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)
|
||||
source_path.rename(target_path)
|
||||
moved.append(member)
|
||||
|
||||
return {
|
||||
"extracted": moved,
|
||||
"skipped": skipped,
|
||||
}
|
||||
|
||||
raise UnarUnexpectedOutputError(lines[-1])
|
||||
|
||||
|
||||
def inspect_archive(file_path, **kwargs):
|
||||
"""
|
||||
Calls `lsar` to inspect the contents of an archive
|
||||
Takes (str) file_path
|
||||
Returns (dict) of (str) format, (list) members
|
||||
"""
|
||||
if not pathlib.Path(file_path):
|
||||
raise FileNotFoundError(f"File {file_path} does not exist")
|
||||
|
||||
process = run("lsar", ["-json", "--", file_path])
|
||||
|
||||
if process["returncode"] != 0:
|
||||
raise LsarCommandError(f"Non-zero return code: {process['returncode']}")
|
||||
|
||||
try:
|
||||
archive_info = loads(process["stdout"])
|
||||
except JSONDecodeError as error:
|
||||
raise LsarOutputError(f"Unable to read JSON output from lsar: {error.msg}") from error
|
||||
|
||||
members = [{
|
||||
"name": pathlib.PurePath(member.get("XADFileName")).name,
|
||||
"path": member.get("XADFileName"),
|
||||
"size": member.get("XADFileSize"),
|
||||
"is_dir": member.get("XADIsDirectory"),
|
||||
"is_resource_fork": member.get("XADIsResourceFork"),
|
||||
"raw": member,
|
||||
} for member in archive_info.get("lsarContents", [])]
|
||||
|
||||
return {
|
||||
"format": archive_info.get("lsarFormatName"),
|
||||
"members": members,
|
||||
}
|
||||
|
||||
|
||||
class UnarCommandError(Exception):
|
||||
""" Command execution was unsuccessful """
|
||||
pass
|
||||
|
||||
|
||||
class UnarNoFilesExtractedError(Exception):
|
||||
""" Command completed, but no files extracted """
|
||||
|
||||
|
||||
class UnarUnexpectedOutputError(Exception):
|
||||
""" Command output not recognized """
|
||||
|
||||
|
||||
class LsarCommandError(Exception):
|
||||
""" Command execution was unsuccessful """
|
||||
|
||||
|
||||
class LsarOutputError(Exception):
|
||||
""" Command output could not be parsed"""
|
@ -21,7 +21,9 @@ You may edit the files under `mock/bin` to simulate Linux command responses.
|
||||
TODO: rascsi-web uses protobuf commands to send and receive data from rascsi.
|
||||
A separate mocking solution will be needed for this interface.
|
||||
|
||||
## Pushing to the Pi via git
|
||||
## (Optional) Pushing to the Pi via git
|
||||
|
||||
This is a setup for pushing code changes from your local development environment to the Raspberry Pi without a roundtrip to the remote GitHub repository.
|
||||
|
||||
Setup a bare repo on the rascsi
|
||||
```
|
||||
@ -40,7 +42,7 @@ $ git push pi master
|
||||
|
||||
## Localizing the Web Interface
|
||||
|
||||
We use the Flask-Babel library and Flask/Jinja2 extension for i18n.
|
||||
We use the Flask-Babel library and Flask/Jinja2 extension for internationalization (i18n).
|
||||
|
||||
It uses the 'pybabel' command line tool for extracting and compiling localizations. The Web Interface start script will automatically compile localizations upon launch.
|
||||
|
||||
@ -55,7 +57,7 @@ To create a new localization, it needs to be added to the LANGAUGES constant in
|
||||
web/settings.py. To localize messages coming from the RaSCSI backend, update also code in
|
||||
raspberrypi/localizer.cpp in the RaSCSI C++ code.
|
||||
|
||||
Once this is done, it is time to localize the Python code. The below steps are derived from the [Flask-Babel documentation](https://flask-babel.tkte.ch/#translating-applications).
|
||||
Once this is done, it is time to localize the Python code. The below steps are derived from the [Flask-Babel documentation](https://python-babel.github.io/flask-babel/index.html#translating-applications).
|
||||
|
||||
First, generate the raw messages.pot file containing extracted strings.
|
||||
|
||||
@ -68,7 +70,7 @@ $ pybabel extract -F babel.cfg -o messages.pot .
|
||||
When adding a localization for a new language, initialize the directory structure. Replace 'xx' with the two character code for the language.
|
||||
|
||||
```
|
||||
$ pybabel init -i messages.pot -d translations -l xx
|
||||
$ pybabel init -i messages.pot -d src/translations -l xx
|
||||
```
|
||||
|
||||
### Update an existing loclization
|
||||
@ -114,6 +116,10 @@ msgstr ""
|
||||
"drivrutiner och inställningar</a>."
|
||||
```
|
||||
|
||||
### Contributing to the project
|
||||
|
||||
New or updated localizations are treated just like any other code change. See the [project README](https://github.com/akuker/RASCSI/tree/rdmark-readme-contributions#how-do-i-contribute) for further information.
|
||||
|
||||
### (Optional) See translation stats for a localization
|
||||
Install the gettext package and use msgfmt to see the translation progress.
|
||||
```
|
||||
|
@ -3,6 +3,16 @@
|
||||
server {
|
||||
listen [::]:80 default_server;
|
||||
listen 80 default_server;
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
|
||||
ssl_certificate /etc/ssl/certs/rascsi-web.crt;
|
||||
ssl_certificate_key /etc/ssl/private/rascsi-web.key;
|
||||
ssl_session_timeout 1d;
|
||||
ssl_session_cache shared:MozSSL:10m;
|
||||
ssl_session_tickets off;
|
||||
ssl_protocols TLSv1.3;
|
||||
ssl_prefer_server_ciphers off;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
|
@ -430,5 +430,17 @@
|
||||
"file_type": null,
|
||||
"description": "Emulates Apple CD ROM drive for use with Macintosh computers.",
|
||||
"url": ""
|
||||
},
|
||||
{
|
||||
"device_type": "SCCD",
|
||||
"vendor": null,
|
||||
"product": null,
|
||||
"revision": null,
|
||||
"block_size": 512,
|
||||
"size": null,
|
||||
"name": "Generic CD-ROM 512 block size",
|
||||
"file_type": null,
|
||||
"description": "For use with host systems that expect the non-standard 512 byte block size for CD-ROM drives, such as Akai samplers.",
|
||||
"url": ""
|
||||
}
|
||||
]
|
||||
|
@ -9,25 +9,43 @@ class ReturnCodeMapper:
|
||||
"""Class for mapping between rascsi return codes and translated strings"""
|
||||
|
||||
MESSAGES = {
|
||||
ReturnCodes.DELETEFILE_SUCCESS: _("File deleted: %(file_path)s"),
|
||||
ReturnCodes.DELETEFILE_FILE_NOT_FOUND: _("File to delete not found: %(file_path)s"),
|
||||
ReturnCodes.RENAMEFILE_SUCCESS: _("File moved to: %(target_path)s"),
|
||||
ReturnCodes.RENAMEFILE_UNABLE_TO_MOVE: _("Unable to move file to: %(target_path)s"),
|
||||
ReturnCodes.DOWNLOADFILETOISO_SUCCESS: _("Created CD-ROM ISO image with "
|
||||
"arguments \"%(value)s\""),
|
||||
ReturnCodes.DOWNLOADTODIR_SUCCESS: _("%(file_name)s downloaded to %(save_dir)s"),
|
||||
ReturnCodes.WRITEFILE_SUCCESS: _("File created: %(target_path)s"),
|
||||
ReturnCodes.WRITEFILE_COULD_NOT_WRITE: _("Could not create file: %(target_path)s"),
|
||||
ReturnCodes.READCONFIG_SUCCESS: _("Loaded configurations from: %(file_name)s"),
|
||||
ReturnCodes.READCONFIG_COULD_NOT_READ: _("Could not read configuration "
|
||||
"file: %(file_name)s"),
|
||||
ReturnCodes.READCONFIG_INVALID_CONFIG_FILE_FORMAT: _("Invalid configuration file format"),
|
||||
ReturnCodes.READDRIVEPROPS_SUCCESS: _("Read properties from file: %(file_path)s"),
|
||||
ReturnCodes.READDRIVEPROPS_COULD_NOT_READ: _("Could not read properties from "
|
||||
"file: %(file_path)s"),
|
||||
ReturnCodes.ATTACHIMAGE_COULD_NOT_ATTACH: _("Cannot insert an image for %(device_type)s "
|
||||
"into a %(current_device_type)s device"),
|
||||
}
|
||||
ReturnCodes.DELETEFILE_SUCCESS:
|
||||
_("File deleted: %(file_path)s"),
|
||||
ReturnCodes.DELETEFILE_FILE_NOT_FOUND:
|
||||
_("File to delete not found: %(file_path)s"),
|
||||
ReturnCodes.RENAMEFILE_SUCCESS:
|
||||
_("File moved to: %(target_path)s"),
|
||||
ReturnCodes.RENAMEFILE_UNABLE_TO_MOVE:
|
||||
_("Unable to move file to: %(target_path)s"),
|
||||
ReturnCodes.DOWNLOADFILETOISO_SUCCESS:
|
||||
_("Created CD-ROM ISO image with arguments \"%(value)s\""),
|
||||
ReturnCodes.DOWNLOADTODIR_SUCCESS:
|
||||
_("%(file_name)s downloaded to %(save_dir)s"),
|
||||
ReturnCodes.WRITEFILE_SUCCESS:
|
||||
_("File created: %(target_path)s"),
|
||||
ReturnCodes.WRITEFILE_COULD_NOT_WRITE:
|
||||
_("Could not create file: %(target_path)s"),
|
||||
ReturnCodes.READCONFIG_SUCCESS:
|
||||
_("Loaded configurations from: %(file_name)s"),
|
||||
ReturnCodes.READCONFIG_COULD_NOT_READ:
|
||||
_("Could not read configuration file: %(file_name)s"),
|
||||
ReturnCodes.READCONFIG_INVALID_CONFIG_FILE_FORMAT:
|
||||
_("Invalid configuration file format"),
|
||||
ReturnCodes.READDRIVEPROPS_SUCCESS:
|
||||
_("Read properties from file: %(file_path)s"),
|
||||
ReturnCodes.READDRIVEPROPS_COULD_NOT_READ:
|
||||
_("Could not read properties from file: %(file_path)s"),
|
||||
ReturnCodes.ATTACHIMAGE_COULD_NOT_ATTACH:
|
||||
_("Cannot insert an image for %(device_type)s into a %(current_device_type)s device"),
|
||||
ReturnCodes.EXTRACTIMAGE_SUCCESS:
|
||||
_("Extracted %(count)s file(s)"),
|
||||
ReturnCodes.EXTRACTIMAGE_NO_FILES_SPECIFIED:
|
||||
_("Unable to extract archive: No files were specified"),
|
||||
ReturnCodes.EXTRACTIMAGE_NO_FILES_EXTRACTED:
|
||||
_("No files were extracted (existing files are skipped)"),
|
||||
ReturnCodes.EXTRACTIMAGE_COMMAND_ERROR:
|
||||
_("Unable to extract archive: %(error)s"),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def add_msg(payload):
|
||||
@ -36,10 +54,14 @@ class ReturnCodeMapper:
|
||||
if "return_code" not in payload:
|
||||
return payload
|
||||
|
||||
parameters = payload["parameters"]
|
||||
parameters = payload.get("parameters")
|
||||
|
||||
payload["msg"] = lazy_gettext(
|
||||
if parameters:
|
||||
payload["msg"] = lazy_gettext(
|
||||
ReturnCodeMapper.MESSAGES[payload["return_code"]],
|
||||
**parameters,
|
||||
)
|
||||
else:
|
||||
payload["msg"] = lazy_gettext(ReturnCodeMapper.MESSAGES[payload["return_code"]])
|
||||
|
||||
return payload
|
||||
|
@ -12,8 +12,6 @@ AFP_DIR = f"{HOME_DIR}/afpshare"
|
||||
|
||||
MAX_FILE_SIZE = getenv("MAX_FILE_SIZE", str(1024 * 1024 * 1024 * 4)) # 4gb
|
||||
|
||||
ARCHIVE_FILE_SUFFIX = "zip"
|
||||
|
||||
# The file name of the default config file that loads when rascsi-web starts
|
||||
DEFAULT_CONFIG = f"default.{rascsi.common_settings.CONFIG_FILE_SUFFIX}"
|
||||
# File containing canonical drive properties
|
||||
|
@ -35,12 +35,14 @@ table, tr, td {
|
||||
color: white;
|
||||
font-size:20px;
|
||||
background-color:red;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.message {
|
||||
color: white;
|
||||
font-size:20px;
|
||||
background-color:green;
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
td.inactive {
|
||||
|
@ -34,7 +34,7 @@
|
||||
<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 }}">
|
||||
<input type="hidden" name="size" value="{{ hd.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 }}
|
||||
@ -124,7 +124,7 @@
|
||||
<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 }}">
|
||||
<input type="hidden" name="size" value="{{ rm.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 }}
|
||||
|
@ -185,41 +185,41 @@
|
||||
</ul>
|
||||
</details>
|
||||
</td>
|
||||
{% elif file["zip_members"] %}
|
||||
{% elif file["archive_contents"] %}
|
||||
<td>
|
||||
<details>
|
||||
<summary>
|
||||
{{ file["name"] }}
|
||||
</summary>
|
||||
<ul style="list-style: none;">
|
||||
{% for member in file["zip_members"] %}
|
||||
{% if not member.lower().endswith(PROPERTIES_SUFFIX) %}
|
||||
<li>
|
||||
{% if member + "." + PROPERTIES_SUFFIX in file["zip_members"] %}
|
||||
<details><summary>{{ member }}
|
||||
<form action="/files/unzip" method="post">
|
||||
<input name="zip_file" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="zip_member" type="hidden" value="{{ member }}">
|
||||
<input type="submit" value="{{ _("Unzip") }}" onclick="processNotify('{{ _("Unzipping a single file...") }}')">
|
||||
</form>
|
||||
</summary>
|
||||
<ul style="list-style: none;">
|
||||
{% for member in file["archive_contents"] %}
|
||||
{% if not member["is_properties_file"] %}
|
||||
<li>
|
||||
{{ member + "." + PROPERTIES_SUFFIX }}
|
||||
{% if member["related_properties_file"] %}
|
||||
<details>
|
||||
<summary>
|
||||
<label>{{ member["path"] }}</label>
|
||||
<form action="/files/extract_image" method="post">
|
||||
<input name="archive_file" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="archive_members" type="hidden" value="{{ member["path"] }}|{{ member["related_properties_file"] }}">
|
||||
<input type="submit" value="{{ _("Extract") }}" onclick="processNotify('{{ _("Extracting a single file...") }}')">
|
||||
</form>
|
||||
</summary>
|
||||
<ul style="list-style: none;">
|
||||
<li>{{ member["related_properties_file"] }}</li>
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<label>{{ member["path"] }}</label>
|
||||
<form action="/files/extract_image" method="post">
|
||||
<input name="archive_file" type="hidden" value="{{ file["name"] }}">
|
||||
<input name="archive_members" type="hidden" value="{{ member["path"] }}">
|
||||
<input type="submit" value="{{ _("Extract") }}" onclick="processNotify('{{ _("Extracting a single file...") }}')">
|
||||
</form>
|
||||
{% endif %}
|
||||
</li>
|
||||
</ul>
|
||||
</details>
|
||||
{% else %}
|
||||
<label for="zip_member">{{ member }}</label>
|
||||
<form action="/files/unzip" method="post">
|
||||
<input name="zip_file" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="zip_member" type="hidden" value="{{ member }}">
|
||||
<input type="submit" value="{{ _("Unzip") }}" onclick="processNotify('{{ _("Unzipping a single file...") }}')">
|
||||
</form>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</details>
|
||||
</td>
|
||||
@ -238,11 +238,12 @@
|
||||
{{ _("Attached!") }}
|
||||
</center>
|
||||
{% else %}
|
||||
{% if file["name"].lower().endswith(ARCHIVE_FILE_SUFFIX) %}
|
||||
<form action="/files/unzip" method="post">
|
||||
<input name="zip_file" type="hidden" value="{{ file['name'] }}">
|
||||
<input name="zip_members" type="hidden" value="{{ file['zip_members'] }}">
|
||||
<input type="submit" value="{{ _("Unzip All") }}" onclick="processNotify('{{ _("Unzipping all files...") }}')">
|
||||
{% if file["archive_contents"] %}
|
||||
<form action="/files/extract_image" method="post">
|
||||
<input name="archive_file" type="hidden" value="{{ file['name'] }}">
|
||||
{% set pipe = joiner("|") %}
|
||||
<input name="archive_members" type="hidden" value="{% for member in file["archive_contents"] %}{{ pipe() }}{{ member["path"] }}{% endfor %}">
|
||||
<input type="submit" value="{{ _("Extract All") }}" onclick="processNotify('{{ _("Extracting all files...") }}')">
|
||||
</form>
|
||||
{% else %}
|
||||
<form action="/scsi/attach" method="post">
|
||||
@ -257,7 +258,7 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="unit">{{ _("LUN") }}</label>
|
||||
<input name="unit" type="number" size="2" value="0" min="0" max="31">
|
||||
<input name="unit" type="number" value="0" min="0" max="31" step="1">
|
||||
{% if file["detected_type"] != "UNDEFINED" %}
|
||||
<input name="type" type="hidden" value="{{ file['detected_type'] }}">
|
||||
{{ file['detected_type_name'] }}
|
||||
@ -336,7 +337,7 @@
|
||||
{% for key, value in device_types[type]["params"].items() %}
|
||||
<label for="{{ key }}">{{ key }}:</label>
|
||||
{% if value.isnumeric() %}
|
||||
<input name="{{ key }}" type="number" size="{{ value|length }}" value="{{ value }}">
|
||||
<input name="{{ key }}" type="number" value="{{ value }}">
|
||||
{% elif key == "interface" %}
|
||||
<select name="interface">
|
||||
{% for if in netinfo["ifs"] %}
|
||||
@ -358,7 +359,7 @@
|
||||
{% endfor %}
|
||||
</select>
|
||||
<label for="unit">{{ _("LUN") }}</label>
|
||||
<input name="unit" type="number" size="2" value="0" min="0" max="31">
|
||||
<input name="unit" type="number" value="0" min="0" max="31" step="1">
|
||||
<input type="submit" value="{{ _("Attach") }}">
|
||||
</form>
|
||||
</td>
|
||||
@ -415,7 +416,7 @@
|
||||
gb: "{{ _("GB") }}",
|
||||
mb: "{{ _("MB") }}",
|
||||
kb: "{{ _("KB") }}",
|
||||
b: "{{ _("b") }}"
|
||||
b: "{{ _("B") }}"
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -571,7 +572,7 @@
|
||||
</option>
|
||||
</select>
|
||||
<label for="size">{{ _("Size:") }}</label>
|
||||
<input name="size" type="number" placeholder="{{ _("MB") }}" min="1" size="6" required>
|
||||
<input name="size" type="number" placeholder="{{ _("MB") }}" min="1" max="262144" required>
|
||||
<input type="submit" value="{{ _("Create") }}">
|
||||
</form>
|
||||
</td>
|
||||
@ -606,7 +607,7 @@
|
||||
<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="1" size="4">
|
||||
<input name="lines" type="number" value="200" min="0" max="99999" step="100">
|
||||
<label for="scope">{{ _("Scope:") }}</label>
|
||||
<select name="scope">
|
||||
<option value="">
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,9 +8,9 @@ import argparse
|
||||
from pathlib import Path
|
||||
from functools import wraps
|
||||
from grp import getgrall
|
||||
from ast import literal_eval
|
||||
|
||||
import bjoern
|
||||
from rascsi.return_codes import ReturnCodes
|
||||
from werkzeug.utils import secure_filename
|
||||
from simplepam import authenticate
|
||||
from flask_babel import Babel, Locale, refresh, _
|
||||
@ -37,6 +37,7 @@ from rascsi.common_settings import (
|
||||
CFG_DIR,
|
||||
CONFIG_FILE_SUFFIX,
|
||||
PROPERTIES_SUFFIX,
|
||||
ARCHIVE_FILE_SUFFIXES,
|
||||
RESERVATIONS,
|
||||
)
|
||||
|
||||
@ -55,7 +56,6 @@ from web_utils import (
|
||||
from settings import (
|
||||
AFP_DIR,
|
||||
MAX_FILE_SIZE,
|
||||
ARCHIVE_FILE_SUFFIX,
|
||||
DEFAULT_CONFIG,
|
||||
DRIVE_PROPERTIES_FILE,
|
||||
AUTH_GROUP,
|
||||
@ -133,14 +133,13 @@ def index():
|
||||
scsi_ids, recommended_id = get_valid_scsi_ids(devices["device_list"], reserved_scsi_ids)
|
||||
formatted_devices = sort_and_format_devices(devices["device_list"])
|
||||
|
||||
valid_file_suffix = "."+", .".join(
|
||||
valid_file_suffix = "." + ", .".join(
|
||||
server_info["sahd"] +
|
||||
server_info["schd"] +
|
||||
server_info["scrm"] +
|
||||
server_info["scmo"] +
|
||||
server_info["sccd"] +
|
||||
[ARCHIVE_FILE_SUFFIX]
|
||||
)
|
||||
ARCHIVE_FILE_SUFFIXES)
|
||||
|
||||
if "username" in session:
|
||||
username = session["username"]
|
||||
@ -182,7 +181,6 @@ def index():
|
||||
mo_file_suffix=tuple(server_info["scmo"]),
|
||||
username=username,
|
||||
auth_active=auth_active(AUTH_GROUP)["status"],
|
||||
ARCHIVE_FILE_SUFFIX=ARCHIVE_FILE_SUFFIX,
|
||||
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
|
||||
REMOVABLE_DEVICE_TYPES=ractl_cmd.get_removable_device_types(),
|
||||
DISK_DEVICE_TYPES=ractl_cmd.get_disk_device_types(),
|
||||
@ -945,33 +943,38 @@ def copy():
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
@APP.route("/files/unzip", methods=["POST"])
|
||||
@APP.route("/files/extract_image", methods=["POST"])
|
||||
@login_required
|
||||
def unzip():
|
||||
def extract_image():
|
||||
"""
|
||||
Unzips all files in a specified zip archive, or a single file in the zip archive
|
||||
Extracts all or a subset of files in the specified archive
|
||||
"""
|
||||
zip_file = request.form.get("zip_file")
|
||||
zip_member = request.form.get("zip_member") or False
|
||||
zip_members = request.form.get("zip_members") or False
|
||||
archive_file = request.form.get("archive_file")
|
||||
archive_members_raw = request.form.get("archive_members") or None
|
||||
archive_members = archive_members_raw.split("|") if archive_members_raw else None
|
||||
|
||||
if zip_members:
|
||||
zip_members = literal_eval(zip_members)
|
||||
extract_result = file_cmd.extract_image(
|
||||
archive_file,
|
||||
archive_members
|
||||
)
|
||||
|
||||
process = file_cmd.unzip_file(zip_file, zip_member, zip_members)
|
||||
if process["status"]:
|
||||
if not process["msg"]:
|
||||
flash(_("Aborted unzip: File(s) with the same name already exists."), "error")
|
||||
return redirect(url_for("index"))
|
||||
flash(_("Unzipped the following files:"))
|
||||
for msg in process["msg"]:
|
||||
flash(msg)
|
||||
if process["prop_flag"]:
|
||||
flash(_("Properties file(s) have been moved to %(directory)s", directory=CFG_DIR))
|
||||
return redirect(url_for("index"))
|
||||
if extract_result["return_code"] == ReturnCodes.EXTRACTIMAGE_SUCCESS:
|
||||
flash(ReturnCodeMapper.add_msg(extract_result).get("msg"))
|
||||
|
||||
for properties_file in extract_result["properties_files_moved"]:
|
||||
if properties_file["status"]:
|
||||
flash(_("Properties file %(file)s moved to %(directory)s",
|
||||
file=properties_file['name'],
|
||||
directory=CFG_DIR
|
||||
))
|
||||
else:
|
||||
flash(_("Failed to move properties file %(file)s to %(directory)s",
|
||||
file=properties_file['name'],
|
||||
directory=CFG_DIR
|
||||
), "error")
|
||||
else:
|
||||
flash(ReturnCodeMapper.add_msg(extract_result).get("msg"), "error")
|
||||
|
||||
flash(_("Failed to unzip %(zip_file)s", zip_file=zip_file), "error")
|
||||
flash(process["msg"], "error")
|
||||
return redirect(url_for("index"))
|
||||
|
||||
|
||||
|
@ -25,6 +25,11 @@ if ! command -v unzip &> /dev/null ; then
|
||||
echo "Run 'sudo apt install unzip' to fix."
|
||||
ERROR=1
|
||||
fi
|
||||
if ! command -v unar &> /dev/null ; then
|
||||
echo "unar could not be found"
|
||||
echo "Run 'sudo apt install unar' to fix."
|
||||
ERROR=1
|
||||
fi
|
||||
if [ $ERROR = 1 ] ; then
|
||||
echo
|
||||
echo "Fix errors and re-run ./start.sh"
|
||||
|
@ -64,6 +64,7 @@ RASCTL = rasctl
|
||||
RASDUMP = rasdump
|
||||
SASIDUMP = sasidump
|
||||
SCSIMON = scsimon
|
||||
RASCSI_TEST = rascsi_test
|
||||
|
||||
SYSTEMD_CONF = /etc/systemd/system/rascsi.service
|
||||
RSYSLOG_CONF = /etc/rsyslog.d/rascsi.conf
|
||||
@ -90,21 +91,22 @@ SRC_PROTOC = \
|
||||
SRC_PROTOBUF = \
|
||||
rascsi_interface.pb.cpp
|
||||
|
||||
SRC_RASCSI = \
|
||||
rascsi.cpp \
|
||||
scsi.cpp \
|
||||
SRC_RASCSI_CORE = scsi.cpp \
|
||||
gpiobus.cpp \
|
||||
filepath.cpp \
|
||||
fileio.cpp\
|
||||
fileio.cpp \
|
||||
rascsi_version.cpp \
|
||||
rascsi_image.cpp \
|
||||
rascsi_response.cpp \
|
||||
rasutil.cpp \
|
||||
protobuf_util.cpp \
|
||||
localizer.cpp
|
||||
SRC_RASCSI += $(shell find ./controllers -name '*.cpp')
|
||||
SRC_RASCSI += $(shell find ./devices -name '*.cpp')
|
||||
SRC_RASCSI += $(SRC_PROTOBUF)
|
||||
SRC_RASCSI_CORE += $(shell find ./controllers -name '*.cpp')
|
||||
SRC_RASCSI_CORE += $(shell find ./devices -name '*.cpp')
|
||||
SRC_RASCSI_CORE += $(SRC_PROTOBUF)
|
||||
|
||||
SRC_RASCSI = rascsi.cpp
|
||||
SRC_RASCSI += $(SRC_RASCSI_CORE)
|
||||
|
||||
SRC_SCSIMON = \
|
||||
scsimon.cpp \
|
||||
@ -128,7 +130,7 @@ SRC_RASDUMP = \
|
||||
scsi.cpp \
|
||||
gpiobus.cpp \
|
||||
filepath.cpp \
|
||||
fileio.cpp\
|
||||
fileio.cpp \
|
||||
rascsi_version.cpp
|
||||
|
||||
SRC_SASIDUMP = \
|
||||
@ -136,11 +138,16 @@ SRC_SASIDUMP = \
|
||||
scsi.cpp \
|
||||
gpiobus.cpp \
|
||||
filepath.cpp \
|
||||
fileio.cpp\
|
||||
fileio.cpp \
|
||||
rascsi_version.cpp
|
||||
|
||||
|
||||
SRC_RASCSI_TEST = $(shell find ./test -name '*.cpp')
|
||||
SRC_RASCSI_TEST += $(SRC_RASCSI_CORE)
|
||||
|
||||
|
||||
vpath %.h ./ ./controllers ./devices ./monitor
|
||||
vpath %.cpp ./ ./controllers ./devices ./monitor
|
||||
vpath %.cpp ./ ./controllers ./devices ./monitor ./test
|
||||
vpath %.o ./$(OBJDIR)
|
||||
vpath ./$(BINDIR)
|
||||
|
||||
@ -150,13 +157,14 @@ OBJ_RASCTL := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCTL:%.cpp=%.o)))
|
||||
OBJ_RASDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASDUMP:%.cpp=%.o)))
|
||||
OBJ_SASIDUMP := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SASIDUMP:%.cpp=%.o)))
|
||||
OBJ_SCSIMON := $(addprefix $(OBJDIR)/,$(notdir $(SRC_SCSIMON:%.cpp=%.o)))
|
||||
OBJ_RASCSI_TEST := $(addprefix $(OBJDIR)/,$(notdir $(SRC_RASCSI_TEST:%.cpp=%.o)))
|
||||
|
||||
GEN_PROTOBUF := $(SRC_PROTOBUF) rascsi_interface.pb.h
|
||||
|
||||
|
||||
# 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) $(OBJ_RASCTL) $(OBJ_SCSIMON))
|
||||
ALL_DEPS := $(patsubst %.o,%.d,$(OBJ_RASCSI) $(OBJ_RASCTL) $(OBJ_SCSIMON) $(OBJ_RASCSI_TEST))
|
||||
-include $(ALL_DEPS)
|
||||
|
||||
$(OBJDIR) $(BINDIR):
|
||||
@ -180,6 +188,9 @@ $(SRC_PROTOBUF): $(SRC_PROTOC)
|
||||
all: $(BIN_ALL) docs
|
||||
ALL: all
|
||||
|
||||
test: $(BINDIR)/$(RASCSI_TEST)
|
||||
$(BINDIR)/$(RASCSI_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) | $(BINDIR)
|
||||
@ -197,6 +208,9 @@ $(BINDIR)/$(SASIDUMP): $(OBJ_SASIDUMP) | $(BINDIR)
|
||||
$(BINDIR)/$(SCSIMON): $(OBJ_SCSIMON) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_SCSIMON) -lpthread
|
||||
|
||||
$(BINDIR)/$(RASCSI_TEST): $(SRC_PROTOBUF) $(OBJ_RASCSI_TEST) | $(BINDIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $(OBJ_RASCSI_TEST) -lpcap -lprotobuf -lgmock -lgtest -lgtest_main
|
||||
|
||||
|
||||
# Phony rules for building individual utilities
|
||||
.PHONY: $(RASCSI) $(RASCTL) $(RASDUMP) $(SASIDUMP) $(SCSIMON)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
@ -167,7 +167,7 @@ public:
|
||||
const string GetPaddedName() const;
|
||||
|
||||
bool SupportsParams() const { return supports_params; }
|
||||
bool SupportsFile() const { return !supports_params; }
|
||||
virtual bool SupportsFile() const { return !supports_params; }
|
||||
void SupportsParams(bool supports_paams) { this->supports_params = supports_paams; }
|
||||
const unordered_map<string, string> GetParams() const { return params; }
|
||||
const string GetParam(const string&);
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
@ -26,7 +26,6 @@ class Device;
|
||||
|
||||
class DeviceFactory
|
||||
{
|
||||
private:
|
||||
DeviceFactory();
|
||||
~DeviceFactory() {}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
@ -32,12 +32,17 @@ public:
|
||||
int ModeSense6(const DWORD *, BYTE *);
|
||||
int ModeSense10(const DWORD *, BYTE *, int);
|
||||
|
||||
bool SupportsFile() const override { return false; }
|
||||
|
||||
protected:
|
||||
|
||||
void AddModePages(map<int, vector<BYTE>>&, int, bool) const override;
|
||||
|
||||
private:
|
||||
|
||||
typedef ModePageDevice super;
|
||||
|
||||
Dispatcher<HostServices, SCSIDEV> dispatcher;
|
||||
|
||||
void AddModePages(map<int, vector<BYTE>>&, int, bool) const override;
|
||||
void AddRealtimeClockPage(map<int, vector<BYTE>>&, bool) const;
|
||||
};
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
@ -50,43 +50,41 @@ int ModePageDevice::AddModePages(const DWORD *cdb, BYTE *buf, int max_length)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
// Holds all mode page data
|
||||
vector<BYTE> result;
|
||||
|
||||
vector<BYTE> page0;
|
||||
for (auto const& page : pages) {
|
||||
if (size + (int)page.second.size() > max_length) {
|
||||
LOGWARN("Mode page data size exceeds reserved buffer size");
|
||||
// The specification mandates that page 0 must be returned after all others
|
||||
if (page.first) {
|
||||
size_t offset = result.size();
|
||||
|
||||
page0.clear();
|
||||
|
||||
break;
|
||||
// Page data
|
||||
result.insert(result.end(), page.second.begin(), page.second.end());
|
||||
// Page code, PS bit may already have been set
|
||||
result[offset] |= page.first;
|
||||
// Page payload size
|
||||
result[offset + 1] = page.second.size() - 2;
|
||||
}
|
||||
else {
|
||||
// The specification mandates that page 0 must be returned after all others
|
||||
if (page.first) {
|
||||
// Page data
|
||||
memcpy(&buf[size], page.second.data(), page.second.size());
|
||||
// Page code, PS bit may already have been set
|
||||
buf[size] |= page.first;
|
||||
// Page payload size
|
||||
buf[size + 1] = page.second.size() - 2;
|
||||
|
||||
size += page.second.size();
|
||||
}
|
||||
else {
|
||||
page0 = page.second;
|
||||
}
|
||||
page0 = page.second;
|
||||
}
|
||||
}
|
||||
|
||||
// Page 0 must be last
|
||||
if (!page0.empty()) {
|
||||
memcpy(&buf[size], page0.data(), page0.size());
|
||||
size_t offset = result.size();
|
||||
|
||||
// Page data
|
||||
result.insert(result.end(), page0.begin(), page0.end());
|
||||
// Page payload size
|
||||
buf[size + 1] = page0.size() - 2;
|
||||
size += page0.size();
|
||||
result[offset + 1] = page0.size() - 2;
|
||||
}
|
||||
|
||||
// Do not return more than the requested number of bytes
|
||||
size_t size = (size_t)max_length < result.size() ? max_length : result.size();
|
||||
memcpy(buf, result.data(), size);
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020 akuker
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020 akuker
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
@ -257,9 +257,9 @@ bool SCSIPrinter::WriteBytes(BYTE *buf, uint32_t length)
|
||||
|
||||
LOGTRACE("Appending %d byte(s) to printer output file", length);
|
||||
|
||||
write(fd, buf, length);
|
||||
uint32_t num_written = write(fd, buf, length);
|
||||
|
||||
return true;
|
||||
return (num_written == length);
|
||||
}
|
||||
|
||||
bool SCSIPrinter::CheckReservation(SCSIDEV *controller)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2001-2006 PI.(ytanaka@ipc-tokai.or.jp)
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020-2021 akuker
|
||||
//
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020-2021 akuker
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020-2021 akuker
|
||||
//
|
||||
|
@ -1,6 +1,7 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*) for Raspberry Pi
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020-2021 akuker
|
||||
//
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020 akuker
|
||||
@ -13,8 +13,8 @@
|
||||
|
||||
// The following should be updated for each release
|
||||
const int rascsi_major_version = 22; // Last two digits of year
|
||||
const int rascsi_minor_version = 07; // Month
|
||||
const int rascsi_patch_version = 2; // Patch number - increment for each update
|
||||
const int rascsi_minor_version = 8; // Month
|
||||
const int rascsi_patch_version = 1; // Patch number - increment for each update
|
||||
|
||||
static char rascsi_version_string[30]; // Allow for string up to "XX.XX.XXX" + null character + "development build"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2020 akuker
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2021 Uwe Seimet
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
@ -1,6 +1,6 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI (*^..^*)
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Powered by XM6 TypeG Technology.
|
||||
|
286
src/raspberrypi/test/device_factory_test.cpp
Normal file
286
src/raspberrypi/test/device_factory_test.cpp
Normal file
@ -0,0 +1,286 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
// Unit tests based on GoogleTest and GoogleMock
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "rascsi_version.h"
|
||||
#include "../devices/device.h"
|
||||
#include "../devices/device_factory.h"
|
||||
|
||||
using namespace rascsi_interface;
|
||||
|
||||
namespace DeviceFactoryTest
|
||||
{
|
||||
|
||||
DeviceFactory& device_factory = DeviceFactory::instance();
|
||||
|
||||
TEST(DeviceFactoryTest, GetTypeForFile)
|
||||
{
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.hdf"), SAHD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.hds"), SCHD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.HDS"), SCHD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.hda"), SCHD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.hdn"), SCHD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.hdi"), SCHD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.nhd"), SCHD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.hdr"), SCRM);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.mos"), SCMO);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.iso"), SCCD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.suffix.iso"), SCCD);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("bridge"), SCBR);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("daynaport"), SCDP);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("printer"), SCLP);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("services"), SCHS);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("unknown"), UNDEFINED);
|
||||
EXPECT_EQ(device_factory.GetTypeForFile("test.iso.suffix"), UNDEFINED);
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, GetSectorSizes)
|
||||
{
|
||||
unordered_set<uint32_t> sector_sizes;
|
||||
|
||||
sector_sizes = device_factory.GetSectorSizes("SCHD");
|
||||
EXPECT_EQ(4, sector_sizes.size());
|
||||
sector_sizes = device_factory.GetSectorSizes(SCHD);
|
||||
EXPECT_EQ(4, sector_sizes.size());
|
||||
|
||||
EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(1024) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(4096) != sector_sizes.end());
|
||||
|
||||
sector_sizes = device_factory.GetSectorSizes("SCRM");
|
||||
EXPECT_EQ(4, sector_sizes.size());
|
||||
sector_sizes = device_factory.GetSectorSizes(SCRM);
|
||||
EXPECT_EQ(4, sector_sizes.size());
|
||||
|
||||
EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(1024) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(4096) != sector_sizes.end());
|
||||
|
||||
sector_sizes = device_factory.GetSectorSizes("SCMO");
|
||||
EXPECT_EQ(4, sector_sizes.size());
|
||||
sector_sizes = device_factory.GetSectorSizes(SCMO);
|
||||
EXPECT_EQ(4, sector_sizes.size());
|
||||
|
||||
EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(1024) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(4096) != sector_sizes.end());
|
||||
|
||||
sector_sizes = device_factory.GetSectorSizes("SCCD");
|
||||
EXPECT_EQ(2, sector_sizes.size());
|
||||
sector_sizes = device_factory.GetSectorSizes(SCCD);
|
||||
EXPECT_EQ(2, sector_sizes.size());
|
||||
|
||||
EXPECT_TRUE(sector_sizes.find(512) != sector_sizes.end());
|
||||
EXPECT_TRUE(sector_sizes.find(2048) != sector_sizes.end());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, UnknownDeviceType)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "test");
|
||||
EXPECT_EQ(nullptr, device);
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCHD_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "test.hda");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_TRUE(device->SupportsFile());
|
||||
EXPECT_FALSE(device->SupportsParams());
|
||||
EXPECT_TRUE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_FALSE(device->IsReadOnly());
|
||||
EXPECT_FALSE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_FALSE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_TRUE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("QUANTUM", device->GetVendor()) << "Invalid default vendor for Apple drive";
|
||||
EXPECT_EQ("FIREBALL", device->GetProduct()) << "Invalid default vendor for Apple drive";
|
||||
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
|
||||
device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCRM_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "test.hdr");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_TRUE(device->SupportsFile());
|
||||
EXPECT_FALSE(device->SupportsParams());
|
||||
EXPECT_TRUE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_FALSE(device->IsReadOnly());
|
||||
EXPECT_TRUE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_TRUE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_TRUE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("RaSCSI", device->GetVendor());
|
||||
EXPECT_EQ("SCSI HD (REM.)", device->GetProduct());
|
||||
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
|
||||
device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCMO_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "test.mos");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_TRUE(device->SupportsFile());
|
||||
EXPECT_FALSE(device->SupportsParams());
|
||||
EXPECT_TRUE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_FALSE(device->IsReadOnly());
|
||||
EXPECT_TRUE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_TRUE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_TRUE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("RaSCSI", device->GetVendor());
|
||||
EXPECT_EQ("SCSI MO", device->GetProduct());
|
||||
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
|
||||
device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCCD_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "test.iso");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_TRUE(device->SupportsFile());
|
||||
EXPECT_FALSE(device->SupportsParams());
|
||||
EXPECT_FALSE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_TRUE(device->IsReadOnly());
|
||||
EXPECT_TRUE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_TRUE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_TRUE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("RaSCSI", device->GetVendor());
|
||||
EXPECT_EQ("SCSI CD-ROM", device->GetProduct());
|
||||
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
|
||||
device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCBR_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "bridge");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
EXPECT_TRUE(device->SupportsParams());
|
||||
EXPECT_FALSE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_FALSE(device->IsReadOnly());
|
||||
EXPECT_FALSE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_FALSE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_FALSE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("RaSCSI", device->GetVendor());
|
||||
EXPECT_EQ("SCSI HOST BRIDGE", device->GetProduct());
|
||||
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
|
||||
device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCDP_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "daynaport");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
EXPECT_TRUE(device->SupportsParams());
|
||||
EXPECT_FALSE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_FALSE(device->IsReadOnly());
|
||||
EXPECT_FALSE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_FALSE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_FALSE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("Dayna", device->GetVendor());
|
||||
EXPECT_EQ("SCSI/Link", device->GetProduct());
|
||||
EXPECT_EQ("1.4a", device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCHS_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "services");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
EXPECT_FALSE(device->SupportsParams());
|
||||
EXPECT_FALSE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_FALSE(device->IsReadOnly());
|
||||
EXPECT_FALSE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_FALSE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_FALSE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("RaSCSI", device->GetVendor());
|
||||
EXPECT_EQ("Host Services", device->GetProduct());
|
||||
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
|
||||
device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
TEST(DeviceFactoryTest, SCLP_Device_Defaults)
|
||||
{
|
||||
Device *device = device_factory.CreateDevice(UNDEFINED, "printer");
|
||||
EXPECT_NE(nullptr, device);
|
||||
EXPECT_FALSE(device->SupportsFile());
|
||||
EXPECT_TRUE(device->SupportsParams());
|
||||
EXPECT_FALSE(device->IsProtectable());
|
||||
EXPECT_FALSE(device->IsProtected());
|
||||
EXPECT_FALSE(device->IsReadOnly());
|
||||
EXPECT_FALSE(device->IsRemovable());
|
||||
EXPECT_FALSE(device->IsRemoved());
|
||||
EXPECT_FALSE(device->IsLockable());
|
||||
EXPECT_FALSE(device->IsLocked());
|
||||
EXPECT_FALSE(device->IsStoppable());
|
||||
EXPECT_FALSE(device->IsStopped());
|
||||
|
||||
EXPECT_EQ("RaSCSI", device->GetVendor());
|
||||
EXPECT_EQ("SCSI PRINTER", device->GetProduct());
|
||||
EXPECT_EQ(string(rascsi_get_version_string()).substr(0, 2) + string(rascsi_get_version_string()).substr(3, 2),
|
||||
device->GetRevision());
|
||||
|
||||
EXPECT_EQ(32, device->GetSupportedLuns());
|
||||
}
|
||||
|
||||
}
|
72
src/raspberrypi/test/device_test.cpp
Normal file
72
src/raspberrypi/test/device_test.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
// Unit tests based on GoogleTest and GoogleMock
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "gpiobus.h"
|
||||
#include "../devices/scsihd.h"
|
||||
#include "../devices/device_factory.h"
|
||||
|
||||
using namespace rascsi_interface;
|
||||
|
||||
namespace DeviceTest
|
||||
{
|
||||
|
||||
class MockController : public SCSIDEV
|
||||
{
|
||||
MOCK_METHOD(BUS::phase_t, Process, (int), (override));
|
||||
MOCK_METHOD(int, GetEffectiveLun, (), ());
|
||||
MOCK_METHOD(void, Error, (scsi_defs::sense_key, scsi_defs::asc, scsi_defs::status), (override));
|
||||
MOCK_METHOD(int, GetInitiatorId, (), ());
|
||||
MOCK_METHOD(void, SetUnit, (int), ());
|
||||
MOCK_METHOD(void, Connect, (int, BUS *), ());
|
||||
MOCK_METHOD(void, Status, (), ());
|
||||
MOCK_METHOD(void, DataIn, (), ());
|
||||
MOCK_METHOD(void, DataOut, (), ());
|
||||
MOCK_METHOD(void, BusFree, (), ());
|
||||
MOCK_METHOD(void, Selection, (), ());
|
||||
MOCK_METHOD(void, Command, (), ());
|
||||
MOCK_METHOD(void, MsgIn, (), ());
|
||||
MOCK_METHOD(void, MsgOut, (), ());
|
||||
MOCK_METHOD(void, Send, (), (override));
|
||||
MOCK_METHOD(bool, XferMsg, (int), ());
|
||||
MOCK_METHOD(bool, XferIn, (BYTE *), ());
|
||||
MOCK_METHOD(bool, XferOut, (bool), (override));
|
||||
MOCK_METHOD(void, ReceiveBytes, (), ());
|
||||
MOCK_METHOD(void, Execute, (), (override));
|
||||
MOCK_METHOD(void, FlushUnit, (), ());
|
||||
MOCK_METHOD(void, Receive, (), (override));
|
||||
MOCK_METHOD(bool, HasUnit, (), (const override));
|
||||
|
||||
FRIEND_TEST(DeviceTest, TestUnitReady);
|
||||
|
||||
MockController() { }
|
||||
~MockController() { }
|
||||
};
|
||||
|
||||
DeviceFactory& device_factory = DeviceFactory::instance();
|
||||
|
||||
TEST(DeviceTest, TestUnitReady)
|
||||
{
|
||||
MockController controller;
|
||||
Device *device = device_factory.CreateDevice(SCHD, "test");
|
||||
|
||||
controller.ctrl.cmd[0] = eCmdTestUnitReady;
|
||||
|
||||
device->SetReady(false);
|
||||
EXPECT_CALL(controller, Error);
|
||||
EXPECT_TRUE(device->Dispatch(&controller));
|
||||
|
||||
// TODO Add tests for a device that is ready after the SASI code removal
|
||||
}
|
||||
|
||||
}
|
184
src/raspberrypi/test/mode_pages_test.cpp
Normal file
184
src/raspberrypi/test/mode_pages_test.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
//---------------------------------------------------------------------------
|
||||
//
|
||||
// SCSI Target Emulator RaSCSI Reloaded
|
||||
// for Raspberry Pi
|
||||
//
|
||||
// Copyright (C) 2022 Uwe Seimet
|
||||
//
|
||||
// Unit tests based on GoogleTest and GoogleMock
|
||||
//
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include "../devices/scsihd.h"
|
||||
#include "../devices/scsihd_nec.h"
|
||||
#include "../devices/scsicd.h"
|
||||
#include "../devices/scsimo.h"
|
||||
#include "../devices/host_services.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace ModePagesTest
|
||||
{
|
||||
|
||||
unordered_set<uint32_t> sector_sizes;
|
||||
|
||||
class MockModePageDevice : public ModePageDevice
|
||||
{
|
||||
public:
|
||||
|
||||
MockModePageDevice() : ModePageDevice("test") { }
|
||||
~MockModePageDevice() { }
|
||||
|
||||
MOCK_METHOD(vector<BYTE>, Inquiry, (), (const, override));
|
||||
MOCK_METHOD(int, ModeSense6, (const DWORD *, BYTE *), (override));
|
||||
MOCK_METHOD(int, ModeSense10, (const DWORD *, BYTE *, int), (override));
|
||||
|
||||
void AddModePages(map<int, vector<BYTE>>& pages, int page, bool) const override {
|
||||
// Return dummy data for other pages than page 0
|
||||
if (page) {
|
||||
vector<BYTE> buf(255);
|
||||
pages[page] = buf;
|
||||
}
|
||||
}
|
||||
|
||||
// Make protected methods visible for testing
|
||||
// TODO Why does FRIEND_TEST not work for this method?
|
||||
|
||||
int AddModePages(const DWORD *cdb, BYTE *buf, int max_length) {
|
||||
return ModePageDevice::AddModePages(cdb, buf, max_length);
|
||||
}
|
||||
};
|
||||
|
||||
class MockSCSIHD : public SCSIHD
|
||||
{
|
||||
FRIEND_TEST(ModePagesTest, SCSIHD_AddModePages);
|
||||
|
||||
MockSCSIHD(const unordered_set<uint32_t>& sector_sizes) : SCSIHD(sector_sizes, false) { };
|
||||
~MockSCSIHD() { };
|
||||
};
|
||||
|
||||
class MockSCSIHD_NEC : public SCSIHD_NEC
|
||||
{
|
||||
FRIEND_TEST(ModePagesTest, SCSIHD_NEC_AddModePages);
|
||||
|
||||
MockSCSIHD_NEC(const unordered_set<uint32_t>& sector_sizes) : SCSIHD_NEC(sector_sizes) { };
|
||||
~MockSCSIHD_NEC() { };
|
||||
};
|
||||
|
||||
class MockSCSICD : public SCSICD
|
||||
{
|
||||
FRIEND_TEST(ModePagesTest, SCSICD_AddModePages);
|
||||
|
||||
MockSCSICD(const unordered_set<uint32_t>& sector_sizes) : SCSICD(sector_sizes) { };
|
||||
~MockSCSICD() { };
|
||||
};
|
||||
|
||||
class MockSCSIMO : public SCSIMO
|
||||
{
|
||||
FRIEND_TEST(ModePagesTest, SCSIMO_AddModePages);
|
||||
|
||||
MockSCSIMO(const unordered_set<uint32_t>& sector_sizes, const unordered_map<uint64_t, Geometry>& geometries)
|
||||
: SCSIMO(sector_sizes, geometries) { };
|
||||
~MockSCSIMO() { };
|
||||
};
|
||||
|
||||
class MockHostServices : public HostServices
|
||||
{
|
||||
FRIEND_TEST(ModePagesTest, HostServices_AddModePages);
|
||||
|
||||
MockHostServices() { };
|
||||
~MockHostServices() { };
|
||||
};
|
||||
|
||||
TEST(ModePagesTest, ModePageDevice_AddModePages)
|
||||
{
|
||||
DWORD cdb[6];
|
||||
BYTE buf[512];
|
||||
|
||||
MockModePageDevice device;
|
||||
cdb[2] = 0x3f;
|
||||
|
||||
EXPECT_EQ(1, device.AddModePages(cdb, buf, 1)) << "Allocation length was not limited";
|
||||
|
||||
cdb[2] = 0x00;
|
||||
EXPECT_EQ(0, device.AddModePages(cdb, buf, 12)) << "Data for non-existing code page 0 were returned";
|
||||
}
|
||||
|
||||
TEST(ModePagesTest, SCSIHD_AddModePages)
|
||||
{
|
||||
map<int, vector<BYTE>> mode_pages;
|
||||
|
||||
MockSCSIHD device(sector_sizes);
|
||||
device.AddModePages(mode_pages, 0x3f, false);
|
||||
|
||||
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of code pages";
|
||||
EXPECT_EQ(12, mode_pages[1].size());
|
||||
EXPECT_EQ(24, mode_pages[3].size());
|
||||
EXPECT_EQ(24, mode_pages[4].size());
|
||||
EXPECT_EQ(12, mode_pages[8].size());
|
||||
EXPECT_EQ(30, mode_pages[48].size());
|
||||
}
|
||||
|
||||
TEST(ModePagesTest, SCSIHD_NEC_AddModePages)
|
||||
{
|
||||
map<int, vector<BYTE>> mode_pages;
|
||||
|
||||
MockSCSIHD_NEC device(sector_sizes);
|
||||
device.AddModePages(mode_pages, 0x3f, false);
|
||||
|
||||
EXPECT_EQ(5, mode_pages.size()) << "Unexpected number of code pages";
|
||||
EXPECT_EQ(8, mode_pages[1].size());
|
||||
EXPECT_EQ(24, mode_pages[3].size());
|
||||
EXPECT_EQ(20, mode_pages[4].size());
|
||||
EXPECT_EQ(12, mode_pages[8].size());
|
||||
EXPECT_EQ(30, mode_pages[48].size());
|
||||
}
|
||||
|
||||
TEST(ModePagesTest, SCSICD_AddModePages)
|
||||
{
|
||||
map<int, vector<BYTE>> mode_pages;
|
||||
|
||||
MockSCSICD device(sector_sizes);
|
||||
device.AddModePages(mode_pages, 0x3f, false);
|
||||
|
||||
EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of code pages";
|
||||
EXPECT_EQ(12, mode_pages[1].size());
|
||||
EXPECT_EQ(24, mode_pages[3].size());
|
||||
EXPECT_EQ(24, mode_pages[4].size());
|
||||
EXPECT_EQ(12, mode_pages[8].size());
|
||||
EXPECT_EQ(8, mode_pages[13].size());
|
||||
EXPECT_EQ(16, mode_pages[14].size());
|
||||
}
|
||||
|
||||
TEST(ModePagesTest, SCSIMO_AddModePages)
|
||||
{
|
||||
map<int, vector<BYTE>> mode_pages;
|
||||
unordered_map<uint64_t, Geometry> geometries;
|
||||
|
||||
MockSCSIMO device(sector_sizes, geometries);
|
||||
device.AddModePages(mode_pages, 0x3f, false);
|
||||
|
||||
EXPECT_EQ(6, mode_pages.size()) << "Unexpected number of code pages";
|
||||
EXPECT_EQ(12, mode_pages[1].size());
|
||||
EXPECT_EQ(24, mode_pages[3].size());
|
||||
EXPECT_EQ(24, mode_pages[4].size());
|
||||
EXPECT_EQ(4, mode_pages[6].size());
|
||||
EXPECT_EQ(12, mode_pages[8].size());
|
||||
EXPECT_EQ(12, mode_pages[32].size());
|
||||
}
|
||||
|
||||
TEST(ModePagesTest, HostServices_AddModePages)
|
||||
{
|
||||
map<int, vector<BYTE>> mode_pages;
|
||||
|
||||
MockHostServices device;
|
||||
device.AddModePages(mode_pages, 0x3f, false);
|
||||
|
||||
EXPECT_EQ(1, mode_pages.size()) << "Unexpected number of code pages";
|
||||
EXPECT_EQ(10, mode_pages[32].size());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user