mirror of https://github.com/akuker/RASCSI.git
Partition and format HFS/FAT volumes in the Web UI + SMB install (#946)
- New "format as" option when creating new images; removing the image creation options from easyinstall - Bring in HFSer as new submodule providing the driver binaries; removing the Lido driver binary from this repo - Add SpeedTools driver option - Point to github mirror of hfdisk, since the original git server is down - While rearranging the easyinstall options, moved the CtrlBoard option up to the main section - Add an easyinstall script to configure Samba, while consolidating file sharing with Netatalk
This commit is contained in:
parent
9a4f433baf
commit
85edd50047
|
@ -7,7 +7,6 @@ core
|
||||||
__pycache__
|
__pycache__
|
||||||
current
|
current
|
||||||
rascsi_interface_pb2.py
|
rascsi_interface_pb2.py
|
||||||
src/raspberrypi/hfdisk/
|
|
||||||
*~
|
*~
|
||||||
messages.pot
|
messages.pot
|
||||||
messages.mo
|
messages.mo
|
||||||
|
@ -27,3 +26,7 @@ s.sh
|
||||||
|
|
||||||
# temporary kicad files
|
# temporary kicad files
|
||||||
*-backups
|
*-backups
|
||||||
|
|
||||||
|
# submodules
|
||||||
|
hfdisk*
|
||||||
|
mac-hard-disk-drivers
|
||||||
|
|
|
@ -28,7 +28,7 @@ RUN ./easyinstall.sh --run_choice=11
|
||||||
RUN ./easyinstall.sh --run_choice=13
|
RUN ./easyinstall.sh --run_choice=13
|
||||||
|
|
||||||
# Setup wired network bridge
|
# Setup wired network bridge
|
||||||
RUN ./easyinstall.sh --run_choice=6 --headless
|
RUN ./easyinstall.sh --run_choice=5 --headless
|
||||||
|
|
||||||
USER root
|
USER root
|
||||||
WORKDIR /home/pi
|
WORKDIR /home/pi
|
||||||
|
|
283
easyinstall.sh
283
easyinstall.sh
|
@ -58,13 +58,13 @@ PYTHON_COMMON_PATH="$BASE/python/common"
|
||||||
SYSTEMD_PATH="/etc/systemd/system"
|
SYSTEMD_PATH="/etc/systemd/system"
|
||||||
SSL_CERTS_PATH="/etc/ssl/certs"
|
SSL_CERTS_PATH="/etc/ssl/certs"
|
||||||
SSL_KEYS_PATH="/etc/ssl/private"
|
SSL_KEYS_PATH="/etc/ssl/private"
|
||||||
HFS_FORMAT=/usr/bin/hformat
|
|
||||||
HFDISK_BIN=/usr/bin/hfdisk
|
HFDISK_BIN=/usr/bin/hfdisk
|
||||||
LIDO_DRIVER=$BASE/lido-driver.img
|
|
||||||
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||||
GIT_REMOTE=${GIT_REMOTE:-origin}
|
GIT_REMOTE=${GIT_REMOTE:-origin}
|
||||||
TOKEN=""
|
TOKEN=""
|
||||||
SECRET_FILE="$HOME/.config/rascsi/rascsi_secret"
|
SECRET_FILE="$HOME/.config/rascsi/rascsi_secret"
|
||||||
|
FILE_SHARE_PATH="$HOME/shared_files"
|
||||||
|
FILE_SHARE_NAME="Pi File Server"
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
@ -106,7 +106,10 @@ function installPackages() {
|
||||||
unar \
|
unar \
|
||||||
disktype \
|
disktype \
|
||||||
libgmock-dev \
|
libgmock-dev \
|
||||||
man2html
|
man2html \
|
||||||
|
hfsutils \
|
||||||
|
dosfstools \
|
||||||
|
kpartx
|
||||||
}
|
}
|
||||||
|
|
||||||
# install Debian packges for RaSCSI standalone
|
# install Debian packges for RaSCSI standalone
|
||||||
|
@ -592,98 +595,30 @@ function createDriveCustom() {
|
||||||
createDrive "$driveSize" "$driveName"
|
createDrive "$driveSize" "$driveName"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Creates an HFS file system
|
# Clone, compile and install 'hfdisk', partition tool
|
||||||
function formatDrive() {
|
function installHfdisk() {
|
||||||
diskPath="$1"
|
HFDISK_VERSION="2022.11"
|
||||||
volumeName="$2"
|
if [ ! -x "$HFDISK_BIN" ]; then
|
||||||
|
cd "$BASE" || exit 1
|
||||||
if [ ! -x $HFS_FORMAT ]; then
|
wget -O "hfdisk-$HFDISK_VERSION.tar.gz" "https://github.com/rdmark/hfdisk/archive/refs/tags/$HFDISK_VERSION.tar.gz" </dev/null
|
||||||
# Install hfsutils to have hformat to format HFS
|
tar -xzvf "hfdisk-$HFDISK_VERSION.tar.gz"
|
||||||
sudo apt-get install hfsutils --assume-yes </dev/null
|
rm "hfdisk-$HFDISK_VERSION.tar.gz"
|
||||||
fi
|
cd "hfdisk-$HFDISK_VERSION" || exit 1
|
||||||
|
|
||||||
if [ ! -x $HFDISK_BIN ]; then
|
|
||||||
# Clone, compile and install 'hfdisk', partition tool
|
|
||||||
git clone git://www.codesrc.com/git/hfdisk.git
|
|
||||||
cd hfdisk || exit 1
|
|
||||||
make
|
make
|
||||||
|
|
||||||
sudo cp hfdisk /usr/bin/hfdisk
|
sudo cp hfdisk "$HFDISK_BIN"
|
||||||
fi
|
|
||||||
|
|
||||||
# Inject hfdisk commands to create Drive with correct partitions
|
echo "Installed $HFDISK_BIN"
|
||||||
# https://www.codesrc.com/mediawiki/index.php/HFSFromScratch
|
|
||||||
# i initialize partition map
|
|
||||||
# continue with default first block
|
|
||||||
# C Create 1st partition with type specified next)
|
|
||||||
# continue with default
|
|
||||||
# 32 32 blocks (required for HFS+)
|
|
||||||
# Driver_Partition Partition Name
|
|
||||||
# Apple_Driver Partition Type (available types: Apple_Driver, Apple_Driver43, Apple_Free, Apple_HFS...)
|
|
||||||
# C Create 2nd partition with type specified next
|
|
||||||
# continue with default first block
|
|
||||||
# continue with default block size (rest of the disk)
|
|
||||||
# ${volumeName} Partition name provided by user
|
|
||||||
# Apple_HFS Partition Type
|
|
||||||
# w Write partition map to disk
|
|
||||||
# y Confirm partition table
|
|
||||||
# p Print partition map
|
|
||||||
(echo i; echo ; echo C; echo ; echo 32; echo "Driver_Partition"; echo "Apple_Driver"; echo C; echo ; echo ; echo "${volumeName}"; echo "Apple_HFS"; echo w; echo y; echo p;) | $HFDISK_BIN "$diskPath"
|
|
||||||
partitionOk=$?
|
|
||||||
|
|
||||||
if [ $partitionOk -eq 0 ]; then
|
|
||||||
if [ ! -f "$LIDO_DRIVER" ];then
|
|
||||||
echo "Lido driver couldn't be found. Make sure RaSCSI is up-to-date with git pull"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Burn Lido driver to the disk
|
|
||||||
dd if="$LIDO_DRIVER" of="$diskPath" seek=64 count=32 bs=512 conv=notrunc
|
|
||||||
|
|
||||||
driverInstalled=$?
|
|
||||||
if [ $driverInstalled -eq 0 ]; then
|
|
||||||
# Format the partition with HFS file system
|
|
||||||
$HFS_FORMAT -l "${volumeName}" "$diskPath" 1
|
|
||||||
hfsFormattedOk=$?
|
|
||||||
if [ $hfsFormattedOk -eq 0 ]; then
|
|
||||||
echo "Disk created with success."
|
|
||||||
else
|
|
||||||
echo "Unable to format HFS partition."
|
|
||||||
return 4
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Unable to install Lido Driver."
|
|
||||||
return 3
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Unable to create the partition."
|
|
||||||
return 2
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Creates an image file
|
# Fetch HFS drivers that the Web Interface uses
|
||||||
function createDrive() {
|
function fetchHardDiskDrivers() {
|
||||||
if [ $# -ne 2 ]; then
|
if [ ! -f "$BASE/mac-hard-disk-drivers" ]; then
|
||||||
echo "To create a Drive, volume size and volume name must be provided"
|
cd "$BASE" || exit 1
|
||||||
echo "$ createDrive 600 \"RaSCSI Drive\""
|
wget https://macintoshgarden.org/sites/macintoshgarden.org/files/apps/mac-hard-disk-drivers.zip
|
||||||
echo "Drive wasn't created."
|
unzip -d mac-hard-disk-drivers mac-hard-disk-drivers.zip
|
||||||
return
|
rm mac-hard-disk-drivers.zip
|
||||||
fi
|
|
||||||
|
|
||||||
driveSize=$1
|
|
||||||
driveName=$2
|
|
||||||
mkdir -p "$VIRTUAL_DRIVER_PATH"
|
|
||||||
drivePath="${VIRTUAL_DRIVER_PATH}/${driveSize}M.hda"
|
|
||||||
|
|
||||||
if [ ! -f "$drivePath" ]; then
|
|
||||||
echo "Creating a ${driveSize}MiB Drive"
|
|
||||||
truncate --size "${driveSize}m" "$drivePath"
|
|
||||||
|
|
||||||
echo "Formatting drive with HFS"
|
|
||||||
formatDrive "$drivePath" "$driveName"
|
|
||||||
|
|
||||||
else
|
|
||||||
echo "Error: drive already exists"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -836,14 +771,12 @@ function setupWirelessNetworking() {
|
||||||
# Downloads, compiles, and installs Netatalk (AppleShare server)
|
# Downloads, compiles, and installs Netatalk (AppleShare server)
|
||||||
function installNetatalk() {
|
function installNetatalk() {
|
||||||
NETATALK_VERSION="2-220801"
|
NETATALK_VERSION="2-220801"
|
||||||
AFP_SHARE_PATH="$HOME/afpshare"
|
|
||||||
AFP_SHARE_NAME="Pi File Server"
|
|
||||||
NETATALK_CONFIG_PATH="/etc/netatalk"
|
NETATALK_CONFIG_PATH="/etc/netatalk"
|
||||||
|
|
||||||
if [ -d "$NETATALK_CONFIG_PATH" ]; then
|
if [ -d "$NETATALK_CONFIG_PATH" ]; then
|
||||||
echo
|
echo
|
||||||
echo "WARNING: Netatalk configuration dir $NETATALK_CONFIG_PATH already exists."
|
echo "WARNING: Netatalk configuration dir $NETATALK_CONFIG_PATH already exists."
|
||||||
echo "This installation process will overwrite existing Netatalk applications and configurations."
|
echo "This installation process will overwrite existing binaries and configurations."
|
||||||
echo "No shared files will be deleted, but you may have to manually restore your settings after the installation."
|
echo "No shared files will be deleted, but you may have to manually restore your settings after the installation."
|
||||||
echo
|
echo
|
||||||
echo "Do you want to proceed with the installation? [y/N]"
|
echo "Do you want to proceed with the installation? [y/N]"
|
||||||
|
@ -853,13 +786,27 @@ function installNetatalk() {
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$FILE_SHARE_PATH" ] && [ -d "$HOME/afpshare" ]; then
|
||||||
|
echo
|
||||||
|
echo "File server dir $HOME/afpshare detected. This script will rename it to $FILE_SHARE_PATH."
|
||||||
|
echo
|
||||||
|
echo "Do you want to proceed with the installation? [y/N]"
|
||||||
|
read -r REPLY
|
||||||
|
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
|
||||||
|
sudo mv "$HOME/afpshare" "$FILE_SHARE_PATH" || exit 1
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Downloading netatalk-$NETATALK_VERSION to $HOME"
|
echo "Downloading netatalk-$NETATALK_VERSION to $HOME"
|
||||||
cd $HOME || exit 1
|
cd $HOME || exit 1
|
||||||
wget -O "netatalk-$NETATALK_VERSION.tar.gz" "https://github.com/rdmark/Netatalk-2.x/archive/refs/tags/netatalk-$NETATALK_VERSION.tar.gz" </dev/null
|
wget -O "netatalk-$NETATALK_VERSION.tar.gz" "https://github.com/rdmark/Netatalk-2.x/archive/refs/tags/netatalk-$NETATALK_VERSION.tar.gz" </dev/null
|
||||||
tar -xzvf netatalk-$NETATALK_VERSION.tar.gz
|
tar -xzvf "netatalk-$NETATALK_VERSION.tar.gz"
|
||||||
|
rm "netatalk-$NETATALK_VERSION.tar.gz"
|
||||||
|
|
||||||
cd "$HOME/Netatalk-2.x-netatalk-$NETATALK_VERSION/contrib/shell_utils" || exit 1
|
cd "$HOME/Netatalk-2.x-netatalk-$NETATALK_VERSION/contrib/shell_utils" || exit 1
|
||||||
./debian_install.sh -j="${CORES:-1}" -n="$AFP_SHARE_NAME" -p="$AFP_SHARE_PATH" || exit 1
|
./debian_install.sh -j="${CORES:-1}" -n="$FILE_SHARE_NAME" -p="$FILE_SHARE_PATH" || exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Appends the images dir as a shared Netatalk volume
|
# Appends the images dir as a shared Netatalk volume
|
||||||
|
@ -937,6 +884,65 @@ function installMacproxy {
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Installs and configures Samba (SMB server)
|
||||||
|
function installSamba() {
|
||||||
|
SAMBA_CONFIG_PATH="/etc/samba"
|
||||||
|
|
||||||
|
if [ -d "$SAMBA_CONFIG_PATH" ]; then
|
||||||
|
echo
|
||||||
|
echo "Samba configuration dir $SAMBA_CONFIG_PATH already exists."
|
||||||
|
echo "This installation process may overwrite existing binaries and configurations."
|
||||||
|
echo "No shared files will be deleted, but you may have to manually restore your settings after the installation."
|
||||||
|
echo
|
||||||
|
echo "Do you want to proceed with the installation? [y/N]"
|
||||||
|
read -r REPLY
|
||||||
|
if ! [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$FILE_SHARE_PATH" ] && [ -d "$HOME/afpshare" ]; then
|
||||||
|
echo
|
||||||
|
echo "File server dir $HOME/afpshare detected. This script will rename it to $FILE_SHARE_PATH."
|
||||||
|
echo
|
||||||
|
echo "Do you want to proceed with the installation? [y/N]"
|
||||||
|
read -r REPLY
|
||||||
|
if [ "$REPLY" == "y" ] || [ "$REPLY" == "Y" ]; then
|
||||||
|
sudo mv "$HOME/afpshare" "$FILE_SHARE_PATH" || exit 1
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
elif [ -d "$FILE_SHARE_PATH" ]; then
|
||||||
|
echo "Found a $FILE_SHARE_PATH directory; will use it for file sharing."
|
||||||
|
else
|
||||||
|
echo "Creating the $FILE_SHARE_PATH directory and granting read/write permissions to all users..."
|
||||||
|
sudo mkdir -p "$FILE_SHARE_PATH"
|
||||||
|
sudo chown -R "$USER:$USER" "$FILE_SHARE_PATH"
|
||||||
|
chmod -Rv 775 "$FILE_SHARE_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Installing dependencies..."
|
||||||
|
sudo apt-get update || true
|
||||||
|
sudo apt-get install samba --no-install-recommends --assume-yes </dev/null
|
||||||
|
echo ""
|
||||||
|
echo "Modifying $SAMBA_CONFIG_PATH/smb.conf ..."
|
||||||
|
if [[ `sudo grep -c "server min protocol = NT1" $SAMBA_CONFIG_PATH/smb.conf` -eq 0 ]]; then
|
||||||
|
# Allow Windows XP clients and earlier to connect to the server
|
||||||
|
sudo sed -i 's/\[global\]/\[global\]\nserver min protocol = NT1/' "$SAMBA_CONFIG_PATH/smb.conf"
|
||||||
|
echo "server min prototol = NT1"
|
||||||
|
fi
|
||||||
|
if [[ `sudo grep -c "\[Pi File Server\]" $SAMBA_CONFIG_PATH/smb.conf` -eq 0 ]]; then
|
||||||
|
# Define a shared directory with full read/write privileges, while aggressively hiding dot files
|
||||||
|
echo -e '\n[Pi File Server]\npath = '"$FILE_SHARE_PATH"'\nbrowseable = yes\nwriteable = yes\nhide dot files = yes\nveto files = /.*/' | sudo tee -a "$SAMBA_CONFIG_PATH/smb.conf"
|
||||||
|
fi
|
||||||
|
|
||||||
|
sudo systemctl restart smbd
|
||||||
|
|
||||||
|
echo "Please create a Samba password for user $USER"
|
||||||
|
sudo smbpasswd -a "$USER"
|
||||||
|
}
|
||||||
|
|
||||||
# updates configuration files and installs packages needed for the OLED screen script
|
# updates configuration files and installs packages needed for the OLED screen script
|
||||||
function installRaScsiScreen() {
|
function installRaScsiScreen() {
|
||||||
if [[ -f "$SECRET_FILE" && -z "$TOKEN" ]] ; then
|
if [[ -f "$SECRET_FILE" && -z "$TOKEN" ]] ; then
|
||||||
|
@ -1182,6 +1188,8 @@ function runChoice() {
|
||||||
stopOldWebInterface
|
stopOldWebInterface
|
||||||
updateRaScsiGit
|
updateRaScsiGit
|
||||||
installPackages
|
installPackages
|
||||||
|
installHfdisk
|
||||||
|
fetchHardDiskDrivers
|
||||||
stopRaScsiScreen
|
stopRaScsiScreen
|
||||||
stopRaScsi
|
stopRaScsi
|
||||||
compileRaScsi
|
compileRaScsi
|
||||||
|
@ -1254,16 +1262,19 @@ function runChoice() {
|
||||||
echo "Installing / Updating RaSCSI OLED Screen - Complete!"
|
echo "Installing / Updating RaSCSI OLED Screen - Complete!"
|
||||||
;;
|
;;
|
||||||
4)
|
4)
|
||||||
echo "Creating an HFS formatted 600 MiB drive image with LIDO driver"
|
echo "Installing / Updating RaSCSI Control Board UI"
|
||||||
createDrive600M
|
echo "This script will make the following changes to your system:"
|
||||||
echo "Creating an HFS formatted 600 MiB drive image with LIDO driver - Complete!"
|
echo "- Install additional packages with apt-get"
|
||||||
|
echo "- Add and modify systemd services"
|
||||||
|
echo "- Stop and disable the RaSCSI OLED service if it is running"
|
||||||
|
echo "- Modify the Raspberry Pi boot configuration (may require a reboot)"
|
||||||
|
sudoCheck
|
||||||
|
preparePythonCommon
|
||||||
|
installRaScsiCtrlBoard
|
||||||
|
showRaScsiCtrlBoardStatus
|
||||||
|
echo "Installing / Updating RaSCSI Control Board UI - Complete!"
|
||||||
;;
|
;;
|
||||||
5)
|
5)
|
||||||
echo "Creating an HFS formatted drive image with LIDO driver"
|
|
||||||
createDriveCustom
|
|
||||||
echo "Creating an HFS formatted drive image with LIDO driver - Complete!"
|
|
||||||
;;
|
|
||||||
6)
|
|
||||||
echo "Configuring wired network bridge"
|
echo "Configuring wired network bridge"
|
||||||
echo "This script will make the following changes to your system:"
|
echo "This script will make the following changes to your system:"
|
||||||
echo "- Create a virtual network bridge interface in /etc/network/interfaces.d"
|
echo "- Create a virtual network bridge interface in /etc/network/interfaces.d"
|
||||||
|
@ -1273,7 +1284,7 @@ function runChoice() {
|
||||||
setupWiredNetworking
|
setupWiredNetworking
|
||||||
echo "Configuring wired network bridge - Complete!"
|
echo "Configuring wired network bridge - Complete!"
|
||||||
;;
|
;;
|
||||||
7)
|
6)
|
||||||
echo "Configuring wifi network bridge"
|
echo "Configuring wifi network bridge"
|
||||||
echo "This script will make the following changes to your system:"
|
echo "This script will make the following changes to your system:"
|
||||||
echo "- Install additional packages with apt-get"
|
echo "- Install additional packages with apt-get"
|
||||||
|
@ -1284,10 +1295,22 @@ function runChoice() {
|
||||||
setupWirelessNetworking
|
setupWirelessNetworking
|
||||||
echo "Configuring wifi network bridge - Complete!"
|
echo "Configuring wifi network bridge - Complete!"
|
||||||
;;
|
;;
|
||||||
8)
|
7)
|
||||||
|
echo "Installing AppleShare File Server"
|
||||||
installNetatalk
|
installNetatalk
|
||||||
echo "Installing AppleShare File Server - Complete!"
|
echo "Installing AppleShare File Server - Complete!"
|
||||||
;;
|
;;
|
||||||
|
8)
|
||||||
|
echo "Installing SMB File Server"
|
||||||
|
echo "This script will make the following changes to your system:"
|
||||||
|
echo " - Install packages with apt-get"
|
||||||
|
echo " - Enable Samba systemd services"
|
||||||
|
echo " - Create a directory in the current user's home directory where shared files will be stored"
|
||||||
|
echo " - Create a Samba user for the current user"
|
||||||
|
sudoCheck
|
||||||
|
installSamba
|
||||||
|
echo "Installing SMB File Server - Complete!"
|
||||||
|
;;
|
||||||
9)
|
9)
|
||||||
echo "Installing Web Proxy Server"
|
echo "Installing Web Proxy Server"
|
||||||
echo "This script will make the following changes to your system:"
|
echo "This script will make the following changes to your system:"
|
||||||
|
@ -1327,6 +1350,8 @@ function runChoice() {
|
||||||
createCfgDir
|
createCfgDir
|
||||||
updateRaScsiGit
|
updateRaScsiGit
|
||||||
installPackages
|
installPackages
|
||||||
|
installHfdisk
|
||||||
|
fetchHardDiskDrivers
|
||||||
preparePythonCommon
|
preparePythonCommon
|
||||||
cachePipPackages
|
cachePipPackages
|
||||||
installRaScsiWebInterface
|
installRaScsiWebInterface
|
||||||
|
@ -1352,19 +1377,6 @@ function runChoice() {
|
||||||
echo "Enabling or disabling Web Interface authentication - Complete!"
|
echo "Enabling or disabling Web Interface authentication - Complete!"
|
||||||
;;
|
;;
|
||||||
14)
|
14)
|
||||||
echo "Installing / Updating RaSCSI Control Board UI"
|
|
||||||
echo "This script will make the following changes to your system:"
|
|
||||||
echo "- Install additional packages with apt-get"
|
|
||||||
echo "- Add and modify systemd services"
|
|
||||||
echo "- Stop and disable the RaSCSI OLED service if it is running"
|
|
||||||
echo "- Modify the Raspberry Pi boot configuration (may require a reboot)"
|
|
||||||
sudoCheck
|
|
||||||
preparePythonCommon
|
|
||||||
installRaScsiCtrlBoard
|
|
||||||
showRaScsiCtrlBoardStatus
|
|
||||||
echo "Installing / Updating RaSCSI Control Board UI - Complete!"
|
|
||||||
;;
|
|
||||||
15)
|
|
||||||
shareImagesWithNetatalk
|
shareImagesWithNetatalk
|
||||||
echo "Configuring AppleShare File Server - Complete!"
|
echo "Configuring AppleShare File Server - Complete!"
|
||||||
;;
|
;;
|
||||||
|
@ -1382,7 +1394,7 @@ function readChoice() {
|
||||||
choice=-1
|
choice=-1
|
||||||
|
|
||||||
until [ $choice -ge "0" ] && [ $choice -le "15" ]; do
|
until [ $choice -ge "0" ] && [ $choice -le "15" ]; do
|
||||||
echo -n "Enter your choice (0-13) or CTRL-C to exit: "
|
echo -n "Enter your choice (0-14) or CTRL-C to exit: "
|
||||||
read -r choice
|
read -r choice
|
||||||
done
|
done
|
||||||
|
|
||||||
|
@ -1394,27 +1406,24 @@ function showMenu() {
|
||||||
echo ""
|
echo ""
|
||||||
echo "Choose among the following options:"
|
echo "Choose among the following options:"
|
||||||
echo "INSTALL/UPDATE RASCSI (${CONNECT_TYPE-FULLSPEC} version)"
|
echo "INSTALL/UPDATE RASCSI (${CONNECT_TYPE-FULLSPEC} version)"
|
||||||
echo " 1) install or update RaSCSI Service + Web Interface"
|
echo " 1) Install or update RaSCSI Service + Web Interface"
|
||||||
echo " 2) install or update RaSCSI Service"
|
echo " 2) Install or update RaSCSI Service"
|
||||||
echo " 3) install or update RaSCSI OLED Screen (requires hardware)"
|
echo " 3) Install or update RaSCSI OLED Screen (requires hardware)"
|
||||||
echo "CREATE HFS FORMATTED (MAC) IMAGE WITH LIDO DRIVERS"
|
echo " 4) Install or update RaSCSI Control Board UI (requires hardware)"
|
||||||
echo "** For the Mac Plus, it's better to create an image through the Web Interface **"
|
|
||||||
echo " 4) 600 MiB drive (suggested size)"
|
|
||||||
echo " 5) custom drive size (up to 4000 MiB)"
|
|
||||||
echo "NETWORK BRIDGE ASSISTANT"
|
echo "NETWORK BRIDGE ASSISTANT"
|
||||||
echo " 6) configure network bridge for Ethernet (DHCP)"
|
echo " 5) Configure network bridge for Ethernet (DHCP)"
|
||||||
echo " 7) configure network bridge for WiFi (static IP + NAT)"
|
echo " 6) Configure network bridge for WiFi (static IP + NAT)"
|
||||||
echo "INSTALL COMPANION APPS"
|
echo "INSTALL COMPANION APPS"
|
||||||
echo " 8) install AppleShare File Server (Netatalk)"
|
echo " 7) Install AppleShare File Server (Netatalk)"
|
||||||
echo " 9) install Web Proxy Server (Macproxy)"
|
echo " 8) Install SMB File Server (Samba)"
|
||||||
|
echo " 9) Install Web Proxy Server (Macproxy)"
|
||||||
echo "ADVANCED OPTIONS"
|
echo "ADVANCED OPTIONS"
|
||||||
echo " 10) compile and install RaSCSI stand-alone"
|
echo " 10) Compile and install RaSCSI stand-alone"
|
||||||
echo " 11) configure the RaSCSI Web Interface stand-alone"
|
echo " 11) Configure the RaSCSI Web Interface stand-alone"
|
||||||
echo " 12) enable or disable RaSCSI back-end authentication"
|
echo " 12) Enable or disable RaSCSI back-end authentication"
|
||||||
echo " 13) enable or disable RaSCSI Web Interface authentication"
|
echo " 13) Enable or disable RaSCSI Web Interface authentication"
|
||||||
echo "EXPERIMENTAL FEATURES"
|
echo "EXPERIMENTAL FEATURES"
|
||||||
echo " 14) install or update RaSCSI Control Board UI (requires hardware)"
|
echo " 14) Share the images dir over AppleShare (requires Netatalk)"
|
||||||
echo " 15) share the images dir over AppleShare (requires Netatalk)"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# parse arguments passed to the script
|
# parse arguments passed to the script
|
||||||
|
@ -1430,8 +1439,8 @@ while [ "$1" != "" ]; do
|
||||||
CONNECT_TYPE=$VALUE
|
CONNECT_TYPE=$VALUE
|
||||||
;;
|
;;
|
||||||
-r | --run_choice)
|
-r | --run_choice)
|
||||||
if ! [[ $VALUE =~ ^[1-9][0-9]?$ && $VALUE -ge 1 && $VALUE -le 15 ]]; then
|
if ! [[ $VALUE =~ ^[1-9][0-9]?$ && $VALUE -ge 1 && $VALUE -le 14 ]]; then
|
||||||
echo "ERROR: The run choice parameter must have a numeric value between 1 and 15"
|
echo "ERROR: The run choice parameter must have a numeric value between 1 and 14"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
RUN_CHOICE=$VALUE
|
RUN_CHOICE=$VALUE
|
||||||
|
|
BIN
lido-driver.img
BIN
lido-driver.img
Binary file not shown.
|
@ -8,11 +8,12 @@ from os import path, walk
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from pathlib import PurePath, Path
|
from pathlib import PurePath, Path
|
||||||
from zipfile import ZipFile, is_zipfile
|
from zipfile import ZipFile, is_zipfile
|
||||||
from subprocess import run, CalledProcessError
|
from subprocess import run, Popen, PIPE, CalledProcessError, TimeoutExpired
|
||||||
from json import dump, load
|
from json import dump, load
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
from tempfile import TemporaryDirectory
|
from tempfile import TemporaryDirectory
|
||||||
|
from re import search
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
@ -366,6 +367,222 @@ class FileCmds:
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
def partition_disk(self, file_name, volume_name, disk_format):
|
||||||
|
"""
|
||||||
|
Creates a partition table on an image file.
|
||||||
|
Takes (str) file_name, (str) volume_name, (str) disk_format as arguments.
|
||||||
|
disk_format is either HFS or FAT
|
||||||
|
Returns (dict) with (bool) status, (str) msg
|
||||||
|
"""
|
||||||
|
server_info = self.ractl.get_server_info()
|
||||||
|
full_file_path = Path(server_info["image_dir"]) / file_name
|
||||||
|
|
||||||
|
# Inject hfdisk commands to create Drive with correct partitions
|
||||||
|
# https://www.codesrc.com/mediawiki/index.php/HFSFromScratch
|
||||||
|
# i initialize partition map
|
||||||
|
# continue with default first block
|
||||||
|
# C Create 1st partition with type specified next)
|
||||||
|
# continue with default
|
||||||
|
# 32 32 blocks (required for HFS+)
|
||||||
|
# Driver_Partition Partition Name
|
||||||
|
# Apple_Driver Partition Type (available types: Apple_Driver,
|
||||||
|
# Apple_Driver43, Apple_Free, Apple_HFS...)
|
||||||
|
# C Create 2nd partition with type specified next
|
||||||
|
# continue with default first block
|
||||||
|
# continue with default block size (rest of the disk)
|
||||||
|
# ${volumeName} Partition name provided by user
|
||||||
|
# Apple_HFS Partition Type
|
||||||
|
# w Write partition map to disk
|
||||||
|
# y Confirm partition table
|
||||||
|
# p Print partition map
|
||||||
|
if disk_format == "HFS":
|
||||||
|
partitioning_tool = "hfdisk"
|
||||||
|
commands = [
|
||||||
|
"i",
|
||||||
|
"",
|
||||||
|
"C",
|
||||||
|
"",
|
||||||
|
"32",
|
||||||
|
"Driver_Partition",
|
||||||
|
"Apple_Driver",
|
||||||
|
"C",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
volume_name,
|
||||||
|
"Apple_HFS",
|
||||||
|
"w",
|
||||||
|
"y",
|
||||||
|
"p",
|
||||||
|
]
|
||||||
|
# Create a DOS label, primary partition, W95 FAT type
|
||||||
|
elif disk_format == "FAT":
|
||||||
|
partitioning_tool = "fdisk"
|
||||||
|
commands = [
|
||||||
|
"o",
|
||||||
|
"n",
|
||||||
|
"p",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"",
|
||||||
|
"t",
|
||||||
|
"b",
|
||||||
|
"w",
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
process = Popen(
|
||||||
|
[partitioning_tool, str(full_file_path)],
|
||||||
|
stdin=PIPE,
|
||||||
|
stdout=PIPE,
|
||||||
|
)
|
||||||
|
for command in commands:
|
||||||
|
process.stdin.write(bytes(command + "\n", "utf-8"))
|
||||||
|
process.stdin.flush()
|
||||||
|
try:
|
||||||
|
outs, errs = process.communicate(timeout=15)
|
||||||
|
if outs:
|
||||||
|
logging.info(str(outs, "utf-8"))
|
||||||
|
if errs:
|
||||||
|
logging.error(str(errs, "utf-8"))
|
||||||
|
if process.returncode:
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": errs}
|
||||||
|
except TimeoutExpired:
|
||||||
|
process.kill()
|
||||||
|
outs, errs = process.communicate()
|
||||||
|
if outs:
|
||||||
|
logging.info(str(outs, "utf-8"))
|
||||||
|
if errs:
|
||||||
|
logging.error(str(errs, "utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": errs}
|
||||||
|
|
||||||
|
except (OSError, IOError) as error:
|
||||||
|
logging.error(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||||
|
|
||||||
|
return {"status": True, "msg": ""}
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
def format_hfs(self, file_name, volume_name, driver_path):
|
||||||
|
"""
|
||||||
|
Initializes an HFS file system and injects a hard disk driver
|
||||||
|
Takes (str) file_name, (str) volume_name and (Path) driver_path as arguments.
|
||||||
|
Returns (dict) with (bool) status, (str) msg
|
||||||
|
"""
|
||||||
|
server_info = self.ractl.get_server_info()
|
||||||
|
full_file_path = Path(server_info["image_dir"]) / file_name
|
||||||
|
|
||||||
|
try:
|
||||||
|
run(
|
||||||
|
[
|
||||||
|
"dd",
|
||||||
|
f"if={driver_path}",
|
||||||
|
f"of={full_file_path}",
|
||||||
|
"seek=64",
|
||||||
|
"count=32",
|
||||||
|
"bs=512",
|
||||||
|
"conv=notrunc",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
except (FileNotFoundError, CalledProcessError) as error:
|
||||||
|
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||||
|
|
||||||
|
try:
|
||||||
|
process = run(
|
||||||
|
[
|
||||||
|
"hformat",
|
||||||
|
"-l",
|
||||||
|
volume_name,
|
||||||
|
str(full_file_path),
|
||||||
|
"1",
|
||||||
|
],
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
logging.info(process.stdout.decode("utf-8"))
|
||||||
|
except (FileNotFoundError, CalledProcessError) as error:
|
||||||
|
logging.error(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||||
|
|
||||||
|
return {"status": True, "msg": ""}
|
||||||
|
|
||||||
|
|
||||||
|
# noinspection PyMethodMayBeStatic
|
||||||
|
def format_fat(self, file_name, volume_name, fat_size):
|
||||||
|
"""
|
||||||
|
Initializes a FAT file system
|
||||||
|
Takes (str) file_name, (str) volume_name and (str) FAT size (12|16|32) as arguments.
|
||||||
|
Returns (dict) with (bool) status, (str) msg
|
||||||
|
"""
|
||||||
|
server_info = self.ractl.get_server_info()
|
||||||
|
full_file_path = Path(server_info["image_dir"]) / file_name
|
||||||
|
loopback_device = ""
|
||||||
|
|
||||||
|
try:
|
||||||
|
process = run(
|
||||||
|
["kpartx", "-av", str(full_file_path)],
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
logging.info(process.stdout.decode("utf-8"))
|
||||||
|
if process.returncode == 0:
|
||||||
|
loopback_device = search(r"(loop\d\D\d)", process.stdout.decode("utf-8")).group(1)
|
||||||
|
else:
|
||||||
|
logging.info(process.stdout.decode("utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||||
|
except (FileNotFoundError, CalledProcessError) as error:
|
||||||
|
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||||
|
|
||||||
|
args = [
|
||||||
|
"mkfs.fat",
|
||||||
|
"-v",
|
||||||
|
"-F",
|
||||||
|
fat_size,
|
||||||
|
"-n",
|
||||||
|
volume_name,
|
||||||
|
"/dev/mapper/" + loopback_device,
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
process = run(
|
||||||
|
args,
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
logging.info(process.stdout.decode("utf-8"))
|
||||||
|
except (FileNotFoundError, CalledProcessError) as error:
|
||||||
|
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||||
|
|
||||||
|
try:
|
||||||
|
process = run(
|
||||||
|
["kpartx", "-dv", str(full_file_path)],
|
||||||
|
capture_output=True,
|
||||||
|
check=True,
|
||||||
|
)
|
||||||
|
logging.info(process.stdout.decode("utf-8"))
|
||||||
|
if process.returncode:
|
||||||
|
logging.info(process.stderr.decode("utf-8"))
|
||||||
|
logging.warning("Failed to delete loopback device. You may have to do it manually")
|
||||||
|
except (FileNotFoundError, CalledProcessError) as error:
|
||||||
|
logging.warning(SHELL_ERROR, " ".join(error.cmd), error.stderr.decode("utf-8"))
|
||||||
|
self.delete_file(Path(file_name))
|
||||||
|
return {"status": False, "msg": error.stderr.decode("utf-8")}
|
||||||
|
|
||||||
|
return {"status": True, "msg": ""}
|
||||||
|
|
||||||
|
|
||||||
def download_file_to_iso(self, url, *iso_args):
|
def download_file_to_iso(self, url, *iso_args):
|
||||||
"""
|
"""
|
||||||
Takes (str) url and one or more (str) *iso_args
|
Takes (str) url and one or more (str) *iso_args
|
||||||
|
@ -446,9 +663,12 @@ class FileCmds:
|
||||||
headers={"User-Agent": "Mozilla/5.0"},
|
headers={"User-Agent": "Mozilla/5.0"},
|
||||||
) as req:
|
) as req:
|
||||||
req.raise_for_status()
|
req.raise_for_status()
|
||||||
with open(f"{save_dir}/{file_name}", "wb") as download:
|
try:
|
||||||
for chunk in req.iter_content(chunk_size=8192):
|
with open(f"{save_dir}/{file_name}", "wb") as download:
|
||||||
download.write(chunk)
|
for chunk in req.iter_content(chunk_size=8192):
|
||||||
|
download.write(chunk)
|
||||||
|
except FileNotFoundError as error:
|
||||||
|
return {"status": False, "msg": str(error)}
|
||||||
except requests.exceptions.RequestException as error:
|
except requests.exceptions.RequestException as error:
|
||||||
logging.warning("Request failed: %s", str(error))
|
logging.warning("Request failed: %s", str(error))
|
||||||
return {"status": False, "msg": str(error)}
|
return {"status": False, "msg": str(error)}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import rascsi.common_settings
|
||||||
WEB_DIR = getcwd()
|
WEB_DIR = getcwd()
|
||||||
HOME_DIR = "/".join(WEB_DIR.split("/")[0:3])
|
HOME_DIR = "/".join(WEB_DIR.split("/")[0:3])
|
||||||
|
|
||||||
AFP_DIR = f"{HOME_DIR}/afpshare"
|
FILE_SERVER_DIR = f"{HOME_DIR}/shared_files"
|
||||||
|
|
||||||
MAX_FILE_SIZE = getenv("MAX_FILE_SIZE", str(1024 * 1024 * 1024 * 4)) # 4gb
|
MAX_FILE_SIZE = getenv("MAX_FILE_SIZE", str(1024 * 1024 * 1024 * 4)) # 4gb
|
||||||
|
|
||||||
|
|
|
@ -439,7 +439,7 @@
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ _("The largest file size accepted in this form is %(max_file_size)s MiB. Use other file transfer means for larger files.", max_file_size=max_file_size) }}</li>
|
<li>{{ _("The largest file size accepted in this form is %(max_file_size)s MiB. Use other file transfer means for larger files.", max_file_size=max_file_size) }}</li>
|
||||||
<li>{{ _("File uploads will progress only if you stay on this page. If you navigate away before the transfer is completed, you will end up with an incomplete file.") }}</li>
|
<li>{{ _("File uploads will progress only if you stay on this page. If you navigate away before the transfer is completed, you will end up with an incomplete file.") }}</li>
|
||||||
<li>{{ _("Install <a href=\"%(url)s\" target=\"_blank\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
|
<li>{{ _("Install Netatalk or Samba to use the File Server.") }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -447,8 +447,8 @@
|
||||||
<p>
|
<p>
|
||||||
<label for="upload_destination">{{ _("Target directory:") }}</label>
|
<label for="upload_destination">{{ _("Target directory:") }}</label>
|
||||||
<select name="destination" id="upload_destination">
|
<select name="destination" id="upload_destination">
|
||||||
<option value="images">Images - {{ env["image_dir"] }}</option>
|
<option value="images">{{ _("Disk Images") }} - {{ env["image_dir"] }}</option>
|
||||||
<option value="afp">AppleShare - {{ AFP_DIR }}</option>
|
<option value="file_server">{{ _("File Server") }} - {{ FILE_SERVER_DIR }}</option>
|
||||||
</select>
|
</select>
|
||||||
</p>
|
</p>
|
||||||
</form>
|
</form>
|
||||||
|
@ -489,15 +489,15 @@
|
||||||
{{ _("Download File from the Web") }}
|
{{ _("Download File from the Web") }}
|
||||||
</summary>
|
</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ _("Install <a href=\"%(url)s\" target=\"_blank\">Netatalk</a> to use the AFP File Server.", url="https://github.com/akuker/RASCSI/wiki/AFP-File-Sharing") }}</li>
|
<li>{{ _("Install Netatalk or Samba to use the File Server.") }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<form action="/files/download_url" method="post">
|
<form action="/files/download_url" method="post">
|
||||||
<label for="download_destination">{{ _("Target directory:") }}</label>
|
<label for="download_destination">{{ _("Target directory:") }}</label>
|
||||||
<select name="destination" id="download_destination">
|
<select name="destination" id="download_destination">
|
||||||
<option value="images">Images - {{ env["image_dir"] }}</option>
|
<option value="images">{{ _("Disk Images") }} - {{ env["image_dir"] }}</option>
|
||||||
<option value="afp">AppleShare - {{ AFP_DIR }}</option>
|
<option value="file_server">{{ _("File Server") }} - {{ FILE_SERVER_DIR }}</option>
|
||||||
</select>
|
</select>
|
||||||
<label for="download_url">{{ _("URL:") }}</label>
|
<label for="download_url">{{ _("URL:") }}</label>
|
||||||
<input name="url" id="download_url" required="" type="url">
|
<input name="url" id="download_url" required="" type="url">
|
||||||
|
@ -560,6 +560,7 @@
|
||||||
</summary>
|
</summary>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types#image-types") }}</li>
|
<li>{{ _("Please refer to <a href=\"%(url)s\" target=\"_blank\">wiki documentation</a> to learn more about the supported image file types.", url="https://github.com/akuker/RASCSI/wiki/Supported-Device-Types#image-types") }}</li>
|
||||||
|
<li>{{ _("It is not recommended to use the Lido hard disk driver with the Macintosh Plus.") }}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
@ -587,6 +588,24 @@
|
||||||
</option>
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
<label for="drive_format">{{ _("Format as:") }}</label>
|
||||||
|
<select name="drive_format" id="drive_format">
|
||||||
|
<option value="">
|
||||||
|
{{ _("None") }}
|
||||||
|
</option>
|
||||||
|
<option value="Lido 7.56">
|
||||||
|
HFS + Lido
|
||||||
|
</option>
|
||||||
|
<option value="SpeedTools 3.6">
|
||||||
|
HFS + SpeedTools
|
||||||
|
</option>
|
||||||
|
<option value="FAT16">
|
||||||
|
FAT16
|
||||||
|
</option>
|
||||||
|
<option value="FAT32">
|
||||||
|
FAT32
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
<input type="submit" value="{{ _("Create") }}">
|
<input type="submit" value="{{ _("Create") }}">
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|
|
@ -59,7 +59,7 @@ from web_utils import (
|
||||||
)
|
)
|
||||||
from settings import (
|
from settings import (
|
||||||
WEB_DIR,
|
WEB_DIR,
|
||||||
AFP_DIR,
|
FILE_SERVER_DIR,
|
||||||
MAX_FILE_SIZE,
|
MAX_FILE_SIZE,
|
||||||
DEFAULT_CONFIG,
|
DEFAULT_CONFIG,
|
||||||
DRIVE_PROPERTIES_FILE,
|
DRIVE_PROPERTIES_FILE,
|
||||||
|
@ -251,7 +251,7 @@ def index():
|
||||||
drive_properties=format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"]),
|
drive_properties=format_drive_properties(APP.config["RASCSI_DRIVE_PROPERTIES"]),
|
||||||
RESERVATIONS=RESERVATIONS,
|
RESERVATIONS=RESERVATIONS,
|
||||||
CFG_DIR=CFG_DIR,
|
CFG_DIR=CFG_DIR,
|
||||||
AFP_DIR=AFP_DIR,
|
FILE_SERVER_DIR=FILE_SERVER_DIR,
|
||||||
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
|
PROPERTIES_SUFFIX=PROPERTIES_SUFFIX,
|
||||||
ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES,
|
ARCHIVE_FILE_SUFFIXES=ARCHIVE_FILE_SUFFIXES,
|
||||||
CONFIG_FILE_SUFFIX=CONFIG_FILE_SUFFIX,
|
CONFIG_FILE_SUFFIX=CONFIG_FILE_SUFFIX,
|
||||||
|
@ -864,8 +864,8 @@ def download_file():
|
||||||
"""
|
"""
|
||||||
destination = request.form.get("destination")
|
destination = request.form.get("destination")
|
||||||
url = request.form.get("url")
|
url = request.form.get("url")
|
||||||
if destination == "afp":
|
if destination == "file_server":
|
||||||
destination_dir = AFP_DIR
|
destination_dir = FILE_SERVER_DIR
|
||||||
else:
|
else:
|
||||||
server_info = ractl_cmd.get_server_info()
|
server_info = ractl_cmd.get_server_info()
|
||||||
destination_dir = server_info["image_dir"]
|
destination_dir = server_info["image_dir"]
|
||||||
|
@ -895,8 +895,8 @@ def upload_file():
|
||||||
return make_response(auth["msg"], 403)
|
return make_response(auth["msg"], 403)
|
||||||
|
|
||||||
destination = request.form.get("destination")
|
destination = request.form.get("destination")
|
||||||
if destination == "afp":
|
if destination == "file_server":
|
||||||
destination_dir = AFP_DIR
|
destination_dir = FILE_SERVER_DIR
|
||||||
else:
|
else:
|
||||||
server_info = ractl_cmd.get_server_info()
|
server_info = ractl_cmd.get_server_info()
|
||||||
destination_dir = server_info["image_dir"]
|
destination_dir = server_info["image_dir"]
|
||||||
|
@ -913,6 +913,7 @@ def create_file():
|
||||||
size = (int(request.form.get("size")) * 1024 * 1024)
|
size = (int(request.form.get("size")) * 1024 * 1024)
|
||||||
file_type = request.form.get("type")
|
file_type = request.form.get("type")
|
||||||
drive_name = request.form.get("drive_name")
|
drive_name = request.form.get("drive_name")
|
||||||
|
drive_format = request.form.get("drive_format")
|
||||||
|
|
||||||
safe_path = is_safe_path(file_name)
|
safe_path = is_safe_path(file_name)
|
||||||
if not safe_path["status"]:
|
if not safe_path["status"]:
|
||||||
|
@ -922,6 +923,68 @@ def create_file():
|
||||||
if not process["status"]:
|
if not process["status"]:
|
||||||
return response(error=True, message=process["msg"])
|
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
|
# Creating the drive properties file, if one is chosen
|
||||||
if drive_name:
|
if drive_name:
|
||||||
properties = get_properties_by_drive_name(
|
properties = get_properties_by_drive_name(
|
||||||
|
@ -937,15 +1000,20 @@ def create_file():
|
||||||
return response(
|
return response(
|
||||||
status_code=201,
|
status_code=201,
|
||||||
message=_(
|
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,
|
file_name=full_file_name,
|
||||||
|
drive_format=message_postfix,
|
||||||
),
|
),
|
||||||
image=full_file_name,
|
image=full_file_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
return response(
|
return response(
|
||||||
status_code=201,
|
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,
|
image=full_file_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,62 @@ def test_create_file_with_properties(http_client, list_files, delete_file):
|
||||||
delete_file(file_name)
|
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"])
|
# route("/files/rename", methods=["POST"])
|
||||||
def test_rename_file(http_client, create_test_image, list_files, delete_file):
|
def test_rename_file(http_client, create_test_image, list_files, delete_file):
|
||||||
original_file = create_test_image(auto_delete=False)
|
original_file = create_test_image(auto_delete=False)
|
||||||
|
|
|
@ -23,7 +23,7 @@ def env(pytestconfig):
|
||||||
"home_dir": home_dir,
|
"home_dir": home_dir,
|
||||||
"cfg_dir": f"{home_dir}/.config/rascsi",
|
"cfg_dir": f"{home_dir}/.config/rascsi",
|
||||||
"images_dir": f"{home_dir}/images",
|
"images_dir": f"{home_dir}/images",
|
||||||
"afp_dir": f"{home_dir}/afpshare",
|
"file_server_dir": f"{home_dir}/shared_files",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue