diff --git a/.gitignore b/.gitignore
index bdf7aae8..912c5e48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,7 +7,6 @@ core
__pycache__
current
rascsi_interface_pb2.py
-src/raspberrypi/hfdisk/
*~
messages.pot
messages.mo
@@ -27,3 +26,7 @@ s.sh
# temporary kicad files
*-backups
+
+# submodules
+hfdisk*
+mac-hard-disk-drivers
diff --git a/docker/rascsi-web/Dockerfile b/docker/rascsi-web/Dockerfile
index 95c5578c..106137be 100644
--- a/docker/rascsi-web/Dockerfile
+++ b/docker/rascsi-web/Dockerfile
@@ -28,7 +28,7 @@ RUN ./easyinstall.sh --run_choice=11
RUN ./easyinstall.sh --run_choice=13
# Setup wired network bridge
-RUN ./easyinstall.sh --run_choice=6 --headless
+RUN ./easyinstall.sh --run_choice=5 --headless
USER root
WORKDIR /home/pi
diff --git a/easyinstall.sh b/easyinstall.sh
index b352ccbb..4dba5c65 100755
--- a/easyinstall.sh
+++ b/easyinstall.sh
@@ -58,13 +58,13 @@ 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
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
GIT_REMOTE=${GIT_REMOTE:-origin}
TOKEN=""
SECRET_FILE="$HOME/.config/rascsi/rascsi_secret"
+FILE_SHARE_PATH="$HOME/shared_files"
+FILE_SHARE_NAME="Pi File Server"
set -e
@@ -106,7 +106,10 @@ function installPackages() {
unar \
disktype \
libgmock-dev \
- man2html
+ man2html \
+ hfsutils \
+ dosfstools \
+ kpartx
}
# install Debian packges for RaSCSI standalone
@@ -592,98 +595,30 @@ function createDriveCustom() {
createDrive "$driveSize" "$driveName"
}
-# Creates an HFS file system
-function formatDrive() {
- diskPath="$1"
- volumeName="$2"
-
- if [ ! -x $HFS_FORMAT ]; then
- # Install hfsutils to have hformat to format HFS
- sudo apt-get install hfsutils --assume-yes
{{ _("The largest file size accepted in this form is %(max_file_size)s MiB. Use other file transfer means for larger files.", max_file_size=max_file_size) }}
{{ _("File uploads will progress only if you stay on this page. If you navigate away before the transfer is completed, you will end up with an incomplete file.") }}
- {{ _("Install Netatalk to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}
+ {{ _("Install Netatalk or Samba to use the File Server.") }}
@@ -447,8 +447,8 @@
{{ _("Target directory:") }}
- Images - {{ env["image_dir"] }}
- AppleShare - {{ AFP_DIR }}
+ {{ _("Disk Images") }} - {{ env["image_dir"] }}
+ {{ _("File Server") }} - {{ FILE_SERVER_DIR }}
@@ -489,15 +489,15 @@
{{ _("Download File from the Web") }}
- {{ _("Install Netatalk to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}
+ {{ _("Install Netatalk or Samba to use the File Server.") }}
diff --git a/python/web/src/web.py b/python/web/src/web.py
index 6e79f405..2565d1c7 100644
--- a/python/web/src/web.py
+++ b/python/web/src/web.py
@@ -59,7 +59,7 @@ from web_utils import (
)
from settings import (
WEB_DIR,
- AFP_DIR,
+ FILE_SERVER_DIR,
MAX_FILE_SIZE,
DEFAULT_CONFIG,
DRIVE_PROPERTIES_FILE,
@@ -251,7 +251,7 @@ def index():
drive_properties=format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"]),
RESERVATIONS=RESERVATIONS,
CFG_DIR=CFG_DIR,
- AFP_DIR=AFP_DIR,
+ FILE_SERVER_DIR=FILE_SERVER_DIR,
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES,
CONFIG_FILE_SUFFIX=CONFIG_FILE_SUFFIX,
@@ -864,8 +864,8 @@ def download_file():
"""
destination = request.form.get("destination")
url = request.form.get("url")
- if destination == "afp":
- destination_dir = AFP_DIR
+ if destination == "file_server":
+ destination_dir = FILE_SERVER_DIR
else:
server_info = ractl_cmd.get_server_info()
destination_dir = server_info["image_dir"]
@@ -895,8 +895,8 @@ def upload_file():
return make_response(auth["msg"], 403)
destination = request.form.get("destination")
- if destination == "afp":
- destination_dir = AFP_DIR
+ if destination == "file_server":
+ destination_dir = FILE_SERVER_DIR
else:
server_info = ractl_cmd.get_server_info()
destination_dir = server_info["image_dir"]
@@ -913,6 +913,7 @@ def create_file():
size = (int(request.form.get("size")) * 1024 * 1024)
file_type = request.form.get("type")
drive_name = request.form.get("drive_name")
+ drive_format = request.form.get("drive_format")
safe_path = is_safe_path(file_name)
if not safe_path["status"]:
@@ -922,6 +923,68 @@ def create_file():
if not process["status"]:
return response(error=True, message=process["msg"])
+ message_postfix = ""
+
+ # Formatting and injecting driver, if one is choosen
+ if drive_format:
+ volume_name = f"HD {size / 1024 / 1024:0.0f}M"
+ known_formats = [
+ "Lido 7.56",
+ "SpeedTools 3.6",
+ "FAT16",
+ "FAT32",
+ ]
+ message_postfix = f" ({drive_format})"
+
+ if drive_format not in known_formats:
+ return response(
+ error=True,
+ message=_(
+ "%(drive_format)s is not a valid hard disk format.",
+ drive_format=drive_format,
+ )
+ )
+ elif drive_format.startswith("FAT"):
+ if drive_format == "FAT16":
+ fat_size = "16"
+ elif drive_format == "FAT32":
+ fat_size = "32"
+ else:
+ return response(
+ error=True,
+ message=_(
+ "%(drive_format)s is not a valid hard disk format.",
+ drive_format=drive_format,
+ )
+ )
+
+ process = file_cmd.partition_disk(full_file_name, volume_name, "FAT")
+ if not process["status"]:
+ return response(error=True, message=process["msg"])
+
+ process = file_cmd.format_fat(
+ full_file_name,
+ # FAT volume labels are max 11 chars
+ volume_name[:11],
+ fat_size,
+ )
+ if not process["status"]:
+ return response(error=True, message=process["msg"])
+
+ else:
+ driver_base_path = Path(f"{WEB_DIR}/../../../mac-hard-disk-drivers")
+ process = file_cmd.partition_disk(full_file_name, volume_name, "HFS")
+ if not process["status"]:
+ return response(error=True, message=process["msg"])
+
+ process = file_cmd.format_hfs(
+ full_file_name,
+ volume_name,
+ driver_base_path / Path(drive_format.replace(" ", "-") + ".img"),
+ )
+ if not process["status"]:
+ return response(error=True, message=process["msg"])
+
# Creating the drive properties file, if one is chosen
if drive_name:
properties = get_properties_by_drive_name(
@@ -937,15 +1000,20 @@ def create_file():
return response(
status_code=201,
message=_(
- "Image file with properties created: %(file_name)s",
+ "Image file with properties created: %(file_name)s%(drive_format)s",
file_name=full_file_name,
+ drive_format=message_postfix,
),
image=full_file_name,
)
return response(
status_code=201,
- message=_("Image file created: %(file_name)s", file_name=full_file_name),
+ message=_(
+ "Image file created: %(file_name)s%(drive_format)s",
+ file_name=full_file_name,
+ drive_format=message_postfix,
+ ),
image=full_file_name,
)
diff --git a/python/web/tests/api/test_files.py b/python/web/tests/api/test_files.py
index b36d7f5f..4ad2a4df 100644
--- a/python/web/tests/api/test_files.py
+++ b/python/web/tests/api/test_files.py
@@ -65,6 +65,62 @@ def test_create_file_with_properties(http_client, list_files, delete_file):
delete_file(file_name)
+# route("/files/create", methods=["POST"])
+def test_create_file_and_format_hfs(http_client, list_files, delete_file):
+ file_prefix = str(uuid.uuid4())
+ file_name = f"{file_prefix}.hda"
+
+ response = http_client.post(
+ "/files/create",
+ data={
+ "file_name": file_prefix,
+ "type": "hda",
+ "size": 1,
+ "drive_format": "Lido 7.56",
+ },
+ )
+
+ response_data = response.json()
+
+ assert response.status_code == 201
+ assert response_data["status"] == STATUS_SUCCESS
+ assert response_data["data"]["image"] == file_name
+ assert response_data["messages"][0]["message"] == f"Image file created: {file_name} (Lido 7.56)"
+ assert file_name in list_files()
+
+ # Cleanup
+ delete_file(file_name)
+
+
+# route("/files/create", methods=["POST"])
+def test_create_file_and_format_fat(env, http_client, list_files, delete_file):
+ if env["is_docker"]:
+ pytest.skip("Test not supported in Docker environment.")
+ file_prefix = str(uuid.uuid4())
+ file_name = f"{file_prefix}.hdr"
+
+ response = http_client.post(
+ "/files/create",
+ data={
+ "file_name": file_prefix,
+ "type": "hdr",
+ "size": 1,
+ "drive_format": "FAT32",
+ },
+ )
+
+ response_data = response.json()
+
+ assert response.status_code == 201
+ assert response_data["status"] == STATUS_SUCCESS
+ assert response_data["data"]["image"] == file_name
+ assert response_data["messages"][0]["message"] == f"Image file created: {file_name} (FAT32)"
+ assert file_name in list_files()
+
+ # Cleanup
+ delete_file(file_name)
+
+
# route("/files/rename", methods=["POST"])
def test_rename_file(http_client, create_test_image, list_files, delete_file):
original_file = create_test_image(auto_delete=False)
diff --git a/python/web/tests/conftest.py b/python/web/tests/conftest.py
index 7e70b696..3fdeb92d 100644
--- a/python/web/tests/conftest.py
+++ b/python/web/tests/conftest.py
@@ -23,7 +23,7 @@ def env(pytestconfig):
"home_dir": home_dir,
"cfg_dir": f"{home_dir}/.config/rascsi",
"images_dir": f"{home_dir}/images",
- "afp_dir": f"{home_dir}/afpshare",
+ "file_server_dir": f"{home_dir}/shared_files",
}