Improve and fix bugs with saving&loading configuration files for rascsi-web (#218)

* Translate code commends into English, removing redundant ones

* - Translated all remaining Japanese code comments in src/raspberrypi/ to English, with the exception of cfilesystem.cpp|h
- Removed some redundant comments where the context is obvious from the
  code
  - Fixed a few typos and mistakes

* - Store only file path and name to configuration csv
- Strip known non-file path strings when reading configuration csv (backwards compatibility)
- Validate SCSI ID before attempting to attach a device

* Add comment and TODO

* Partial translation of cfilesystem.h

* Move csv read/write logic into file_cmd.py

* Load default.csv on rascsi-web startup

* Add rudimentary error handling to config loading/saving

* Implement a delete configuration csv file feature. Also rename the delete_image method to delete_file and made it take the full file patch as argument to be consistent with other file operation methods.

* Catch the exception when attempting to exclude SCSI id that is already in use from a list of valid SCSI ids

* Fix error handling when failing to open a csv file for read or write
This commit is contained in:
Daniel Markstedt 2021-08-29 23:47:36 -07:00 committed by GitHub
parent 8fc8531b5b
commit 436e54d83c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 304 additions and 244 deletions

View File

@ -16,330 +16,321 @@
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// ステータスコード定義 // Status code definitions
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
#define FS_INVALIDFUNC 0xFFFFFFFF ///< 無効なファンクションコードを実行した #define FS_INVALIDFUNC 0xFFFFFFFF ///< Executed an invalid function
#define FS_FILENOTFND 0xFFFFFFFE ///< 指定したファイルが見つからない #define FS_FILENOTFND 0xFFFFFFFE ///< The selected file can not be found
#define FS_DIRNOTFND 0xFFFFFFFD ///< 指定したディレクトリが見つからない #define FS_DIRNOTFND 0xFFFFFFFD ///< The selected directory can not be found
#define FS_OVEROPENED 0xFFFFFFFC ///< オープンしているファイルが多すぎる #define FS_OVEROPENED 0xFFFFFFFC ///< There are too many files open
#define FS_CANTACCESS 0xFFFFFFFB ///< ディレクトリやボリュームラベルはアクセス不可 #define FS_CANTACCESS 0xFFFFFFFB ///< Can not access the direcory or volume
#define FS_NOTOPENED 0xFFFFFFFA ///< 指定したハンドルはオープンされていない #define FS_NOTOPENED 0xFFFFFFFA ///< The selected handle is not opened
#define FS_INVALIDMEM 0xFFFFFFF9 ///< メモリ管理領域が破壊された #define FS_INVALIDMEM 0xFFFFFFF9 ///< Memory management has been destroyed
#define FS_OUTOFMEM 0xFFFFFFF8 ///< 実行に必要なメモリがない #define FS_OUTOFMEM 0xFFFFFFF8 ///< Insufficient memory for execution
#define FS_INVALIDPTR 0xFFFFFFF7 ///< 無効なメモリ管理ポインタを指定した #define FS_INVALIDPTR 0xFFFFFFF7 ///< Selected an invalid memory management pointer
#define FS_INVALIDENV 0xFFFFFFF6 ///< 不正な環境を指定した #define FS_INVALIDENV 0xFFFFFFF6 ///< Selected an invalid environment
#define FS_ILLEGALFMT 0xFFFFFFF5 ///< 実行ファイルのフォーマットが異常 #define FS_ILLEGALFMT 0xFFFFFFF5 ///< The exeucted file is in an invalid format
#define FS_ILLEGALMOD 0xFFFFFFF4 ///< オープンのアクセスモードが異常 #define FS_ILLEGALMOD 0xFFFFFFF4 ///< Invalid open access mode
#define FS_INVALIDPATH 0xFFFFFFF3 ///< ファイル名の指定に誤りがある #define FS_INVALIDPATH 0xFFFFFFF3 ///< Mistake in selected file name
#define FS_INVALIDPRM 0xFFFFFFF2 ///< 無効なパラメータでコールした #define FS_INVALIDPRM 0xFFFFFFF2 ///< Called with an invalid parameter
#define FS_INVALIDDRV 0xFFFFFFF1 ///< ドライブ指定に誤りがある #define FS_INVALIDDRV 0xFFFFFFF1 ///< Mistake in selected drive
#define FS_DELCURDIR 0xFFFFFFF0 ///< カレントディレクトリは削除できない #define FS_DELCURDIR 0xFFFFFFF0 ///< Unable to delete the current directory
#define FS_NOTIOCTRL 0xFFFFFFEF ///< IOCTRLできないデバイス #define FS_NOTIOCTRL 0xFFFFFFEF ///< Unable to use IOCTRL with the device
#define FS_LASTFILE 0xFFFFFFEE ///< これ以上ファイルが見つからない #define FS_LASTFILE 0xFFFFFFEE ///< Can not find any more files
#define FS_CANTWRITE 0xFFFFFFED ///< 指定のファイルは書き込みできない #define FS_CANTWRITE 0xFFFFFFED ///< Selected file can not be written
#define FS_DIRALREADY 0xFFFFFFEC ///< 指定のディレクトリは既に登録されている #define FS_DIRALREADY 0xFFFFFFEC ///< Selected directory is already registered
#define FS_CANTDELETE 0xFFFFFFEB ///< ファイルがあるので削除できない #define FS_CANTDELETE 0xFFFFFFEB ///< Can not delete because of a file
#define FS_CANTRENAME 0xFFFFFFEA ///< ファイルがあるのでリネームできない #define FS_CANTRENAME 0xFFFFFFEA ///< Can not rename because of a file
#define FS_DISKFULL 0xFFFFFFE9 ///< ディスクが一杯でファイルが作れない #define FS_DISKFULL 0xFFFFFFE9 ///< Can not create a file because the disk is full
#define FS_DIRFULL 0xFFFFFFE8 ///< ディレクトリが一杯でファイルが作れない #define FS_DIRFULL 0xFFFFFFE8 ///< Can not create a file because the directory is full
#define FS_CANTSEEK 0xFFFFFFE7 ///< 指定の位置にはシークできない #define FS_CANTSEEK 0xFFFFFFE7 ///< Can not seek in the selected location
#define FS_SUPERVISOR 0xFFFFFFE6 ///< スーパーバイザ状態でスーパバイザ指定した #define FS_SUPERVISOR 0xFFFFFFE6 ///< Selected supervisor in supervisor mode
#define FS_THREADNAME 0xFFFFFFE5 ///< 同じスレッド名が存在する #define FS_THREADNAME 0xFFFFFFE5 ///< A thread with this name already exists
#define FS_BUFWRITE 0xFFFFFFE4 ///< プロセス間通信のバッファが書込み禁止 #define FS_BUFWRITE 0xFFFFFFE4 ///< Writing to inter-process communication buffers is disallowed
#define FS_BACKGROUND 0xFFFFFFE3 ///< バックグラウンドプロセスを起動できない #define FS_BACKGROUND 0xFFFFFFE3 ///< Unable to start a background process
#define FS_OUTOFLOCK 0xFFFFFFE0 ///< ロック領域が足りない #define FS_OUTOFLOCK 0xFFFFFFE0 ///< Insufficient lock space
#define FS_LOCKED 0xFFFFFFDF ///< ロックされていてアクセスできない #define FS_LOCKED 0xFFFFFFDF ///< Can not access because it is locked
#define FS_DRIVEOPENED 0xFFFFFFDE ///< 指定のドライブはハンドラがオープンされている #define FS_DRIVEOPENED 0xFFFFFFDE ///< Selected drive has an open handler
#define FS_LINKOVER 0xFFFFFFDD ///< シンボリックリンクネストが16回を超えた #define FS_LINKOVER 0xFFFFFFDD ///< The symbolic link is nested over 16 times
#define FS_FILEEXIST 0xFFFFFFB0 ///< ファイルが存在する #define FS_FILEEXIST 0xFFFFFFB0 ///< The file exists
#define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< メディアが入っていない #define FS_FATAL_MEDIAOFFLINE 0xFFFFFFA3 ///< No media inserted
#define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< 書き込み禁止違反 #define FS_FATAL_WRITEPROTECT 0xFFFFFFA2 ///< Write protected
#define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< 不正なコマンド番号 #define FS_FATAL_INVALIDCOMMAND 0xFFFFFFA1 ///< Invalid command number
#define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< 不正なユニット番号 #define FS_FATAL_INVALIDUNIT 0xFFFFFFA0 ///< Invalid unit number
#define HUMAN68K_PATH_MAX 96 ///< Human68kのパス最大長 #define HUMAN68K_PATH_MAX 96 ///< Longest path allowed in Human68k
//=========================================================================== //===========================================================================
// //
/// Human68k 名前空間 /// Human68k name space
// //
//=========================================================================== //===========================================================================
namespace Human68k { namespace Human68k {
/// ファイル属性ビット /// File attribute bit
enum attribute_t { enum attribute_t {
AT_READONLY = 0x01, ///< 読み込み専用属性 AT_READONLY = 0x01, ///< Read only attribute
AT_HIDDEN = 0x02, ///< 隠し属性 AT_HIDDEN = 0x02, ///< Hidden attribute
AT_SYSTEM = 0x04, ///< システム属性 AT_SYSTEM = 0x04, ///< System attribute
AT_VOLUME = 0x08, ///< ボリュームラベル属性 AT_VOLUME = 0x08, ///< Volume label attribute
AT_DIRECTORY = 0x10, ///< ディレクトリ属性 AT_DIRECTORY = 0x10, ///< Directory attribute
AT_ARCHIVE = 0x20, ///< アーカイブ属性 AT_ARCHIVE = 0x20, ///< Archive attribute
AT_ALL = 0xFF, ///< 全ての属性ビットが1 AT_ALL = 0xFF, ///< All attribute bits are 1
}; };
/// ファイルオープンモード /// File open modes
enum open_t { enum open_t {
OP_READ = 0, ///< 読み込み OP_READ = 0, ///< Read
OP_WRITE = 1, ///< 書き込み OP_WRITE = 1, ///< Write
OP_FULL = 2, ///< 読み書き OP_FULL = 2, ///< Read/Write
OP_MASK = 0x0F, ///< 判定用マスク OP_MASK = 0x0F, ///< Decision mask
OP_SHARE_NONE = 0x10, ///< 共有禁止 OP_SHARE_NONE = 0x10, ///< Sharing forbidden
OP_SHARE_READ = 0x20, ///< 読み込み共有 OP_SHARE_READ = 0x20, ///< Read sharing
OP_SHARE_WRITE = 0x30, ///< 書き込み共有 OP_SHARE_WRITE = 0x30, ///< Write sharing
OP_SHARE_FULL = 0x40, ///< 読み書き共有 OP_SHARE_FULL = 0x40, ///< Read/Write sharing
OP_SHARE_MASK = 0x70, ///< 共有判定用マスク OP_SHARE_MASK = 0x70, ///< Sharing decision mask
OP_SPECIAL = 0x100, ///< 辞書アクセス OP_SPECIAL = 0x100, ///< Dictionary access
}; };
/// シーク種類 /// Seek types
enum seek_t { enum seek_t {
SK_BEGIN = 0, ///< ファイル先頭から SK_BEGIN = 0, ///< From the beginning of a file
SK_CURRENT = 1, ///< 現在位置から SK_CURRENT = 1, ///< From the current location
SK_END = 2, ///< ファイル末尾から SK_END = 2, ///< From the end of the file
}; };
/// メディアバイト /// Media byte
enum media_t { enum media_t {
MEDIA_2DD_10 = 0xE0, ///< 2DD/10セクタ MEDIA_2DD_10 = 0xE0, ///< 2DD/10 sector
MEDIA_1D_9 = 0xE5, ///< 1D/9セクタ MEDIA_1D_9 = 0xE5, ///< 1D/9 sector
MEDIA_2D_9 = 0xE6, ///< 2D/9セクタ MEDIA_2D_9 = 0xE6, ///< 2D/9 sector
MEDIA_1D_8 = 0xE7, ///< 1D/8セクタ MEDIA_1D_8 = 0xE7, ///< 1D/8 sector
MEDIA_2D_8 = 0xE8, ///< 2D/8セクタ MEDIA_2D_8 = 0xE8, ///< 2D/8 sector
MEDIA_2HT = 0xEA, ///< 2HT MEDIA_2HT = 0xEA, ///< 2HT
MEDIA_2HS = 0xEB, ///< 2HS MEDIA_2HS = 0xEB, ///< 2HS
MEDIA_2HDE = 0xEC, ///< 2DDE MEDIA_2HDE = 0xEC, ///< 2DDE
MEDIA_1DD_9 = 0xEE, ///< 1DD/9セクタ MEDIA_1DD_9 = 0xEE, ///< 1DD/9 sector
MEDIA_1DD_8 = 0xEF, ///< 1DD/8セクタ MEDIA_1DD_8 = 0xEF, ///< 1DD/8 sector
MEDIA_MANUAL = 0xF1, ///< リモートドライブ (手動イジェクト) MEDIA_MANUAL = 0xF1, ///< Remote drive (manual eject)
MEDIA_REMOVABLE = 0xF2, ///< リモートドライブ (リムーバブル) MEDIA_REMOVABLE = 0xF2, ///< Remote drive (removable)
MEDIA_REMOTE = 0xF3, ///< リモートドライブ MEDIA_REMOTE = 0xF3, ///< Remote drive
MEDIA_DAT = 0xF4, ///< SCSI-DAT MEDIA_DAT = 0xF4, ///< SCSI-DAT
MEDIA_CDROM = 0xF5, ///< SCSI-CDROM MEDIA_CDROM = 0xF5, ///< SCSI-CDROM
MEDIA_MO = 0xF6, ///< SCSI-MO MEDIA_MO = 0xF6, ///< SCSI-MO
MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD MEDIA_SCSI_HD = 0xF7, ///< SCSI-HD
MEDIA_SASI_HD = 0xF8, ///< SASI-HD MEDIA_SASI_HD = 0xF8, ///< SASI-HD
MEDIA_RAMDISK = 0xF9, ///< RAMディスク MEDIA_RAMDISK = 0xF9, ///< RAM disk
MEDIA_2HQ = 0xFA, ///< 2HQ MEDIA_2HQ = 0xFA, ///< 2HQ
MEDIA_2DD_8 = 0xFB, ///< 2DD/8セクタ MEDIA_2DD_8 = 0xFB, ///< 2DD/8 sector
MEDIA_2DD_9 = 0xFC, ///< 2DD/9セクタ MEDIA_2DD_9 = 0xFC, ///< 2DD/9 sector
MEDIA_2HC = 0xFD, ///< 2HC MEDIA_2HC = 0xFD, ///< 2HC
MEDIA_2HD = 0xFE, ///< 2HD MEDIA_2HD = 0xFE, ///< 2HD
}; };
/// namests構造体
struct namests_t { struct namests_t {
BYTE wildcard; ///< ワイルドカード文字数 BYTE wildcard; ///< Wildcard array
BYTE drive; ///< ドライブ番号 BYTE drive; ///< Drive number
BYTE path[65]; ///< パス(サブディレクトリ+/) BYTE path[65]; ///< Path (subdirectory +/)
BYTE name[8]; ///< ファイル名 (PADDING 0x20) BYTE name[8]; ///< File name (PADDING 0x20)
BYTE ext[3]; ///< 拡張子 (PADDING 0x20) BYTE ext[3]; ///< Extension (PADDING 0x20)
BYTE add[10]; ///< ファイル名追加 (PADDING 0x00) BYTE add[10]; ///< File name addition (PADDING 0x00)
// 文字列取得
void GetCopyPath(BYTE* szPath) const; void GetCopyPath(BYTE* szPath) const;
///< パス名取得
void GetCopyFilename(BYTE* szFilename) const; void GetCopyFilename(BYTE* szFilename) const;
///< ファイル名取得
}; };
/// files構造体
struct files_t { struct files_t {
BYTE fatr; ///< + 0 検索する属性 読込専用 BYTE fatr; ///< + 0 search attribute; read-only
// BYTE drive; ///< + 1 ドライブ番号 読込専用 // BYTE drive; ///< + 1 drive number; read-only
DWORD sector; ///< + 2 ディレクトリのセクタ DOS _FILES先頭アドレスで代用 DWORD sector; ///< + 2 directory sector; DOS _FILES first address substitute
// WORD cluster; ///< + 6 ディレクトリのクラスタ 詳細不明 (未使用) // WORD cluster; ///< + 6 directory cluster; details unknown (unused)
WORD offset; ///< + 8 ディレクトリエントリ 書込専用 WORD offset; ///< + 8 directory entry; write-only
// BYTE name[8]; ///< +10 作業用ファイル名 読込専用 (未使用) // BYTE name[8]; ///< +10 working file name; write-only (unused)
// BYTE ext[3]; ///< +18 作業用拡張子 読込専用 (未使用) // BYTE ext[3]; ///< +18 working extension; write-only (unused)
BYTE attr; ///< +21 ファイル属性 書込専用 BYTE attr; ///< +21 file attribute; write-only
WORD time; ///< +22 最終変更時刻 書込専用 WORD time; ///< +22 last change time of day; write-only
WORD date; ///< +24 最終変更月日 書込専用 WORD date; ///< +24 last change date; write-only
DWORD size; ///< +26 ファイルサイズ 書込専用 DWORD size; ///< +26 file size; write-only
BYTE full[23]; ///< +30 フルファイル名 書込専用 BYTE full[23]; ///< +30 full name; write-only
}; };
/// FCB構造体
struct fcb_t { struct fcb_t {
// BYTE pad00[6]; ///< + 0+ 5 (未使用) // BYTE pad00[6]; ///< + 0~+ 5 (unused)
DWORD fileptr; ///< + 6+ 9 ファイルポインタ DWORD fileptr; ///< + 6~+ 9 file pointer
// BYTE pad01[4]; ///< +10+13 (未使用) // BYTE pad01[4]; ///< +10~+13 (unused)
WORD mode; ///< +14+15 オープンモード WORD mode; ///< +14~+15 open mode
// BYTE pad02[16]; ///< +16+31 (未使用) // BYTE pad02[16]; ///< +16~+31 (unused)
// DWORD zero; ///< +32+35 オープンのとき0が書き込まれている (未使用) // DWORD zero; ///< +32~+35 zeros are written when opened (unused)
// BYTE name[8]; ///< +36+43 ファイル名 (PADDING 0x20) (未使用) // BYTE name[8]; ///< +36~+43 file name (PADDING 0x20) (unused)
// BYTE ext[3]; ///< +44+46 拡張子 (PADDING 0x20) (未使用) // BYTE ext[3]; ///< +44~+46 extension (PADDING 0x20) (unused)
BYTE attr; ///< +47 ファイル属性 BYTE attr; ///< +47 file attribute
// BYTE add[10]; ///< +48+57 ファイル名追加 (PADDING 0x00) (未使用) // BYTE add[10]; ///< +48~+57 file name addition (PADDING 0x00) (unused)
WORD time; ///< +58+59 最終変更時刻 WORD time; ///< +58~+59 last change time of day
WORD date; ///< +60+61 最終変更月日 WORD date; ///< +60~+61 last change date
// WORD cluster; ///< +62+63 クラスタ番号 (未使用) // WORD cluster; ///< +62~+63 cluster number (unused)
DWORD size; ///< +64+67 ファイルサイズ DWORD size; ///< +64~+67 file size
// BYTE pad03[28]; ///< +68+95 FATキャッシュ (未使用) // BYTE pad03[28]; ///< +68~+95 FAT cache (unused)
}; };
/// capacity構造体
struct capacity_t { struct capacity_t {
WORD freearea; ///< + 0 使用可能なクラスタ数 WORD freearea; ///< + 0 Number of available clusters
WORD clusters; ///< + 2 総クラスタ数 WORD clusters; ///< + 2 Total number of clusters
WORD sectors; ///< + 4 クラスタあたりのセクタ数 WORD sectors; ///< + 4 Number of sectors per cluster
WORD bytes; ///< + 6 セクタ当たりのバイト数 WORD bytes; ///< + 6 Number of bytes per sector
}; };
/// ctrldrive構造体
struct ctrldrive_t { struct ctrldrive_t {
BYTE status; ///< +13 状態 BYTE status; ///< +13 status
BYTE pad[3]; ///< Padding BYTE pad[3]; ///< Padding
}; };
/// DPB構造体
struct dpb_t { struct dpb_t {
WORD sector_size; ///< + 0 1セクタ当りのバイト数 WORD sector_size; ///< + 0 Number of bytes in one sector
BYTE cluster_size; ///< + 2 1クラスタ当りのセクタ数-1 BYTE cluster_size; ///< + 2 Number sectors in one cluster -1
BYTE shift; ///< + 3 クラスタ→セクタのシフト数 BYTE shift; ///< + 3 Number of cluster→sector shifts
WORD fat_sector; ///< + 4 FATの先頭セクタ番号 WORD fat_sector; ///< + 4 FAT first sector number
BYTE fat_max; ///< + 6 FAT領域の個数 BYTE fat_max; ///< + 6 FAT storage quantity
BYTE fat_size; ///< + 7 FATの占めるセクタ数(複写分を除く) BYTE fat_size; ///< + 7 FAT controlled sector number (excluding duplicates)
WORD file_max; ///< + 8 ルートディレクトリに入るファイルの個数 WORD file_max; ///< + 8 Number of files in the root directory
WORD data_sector; ///< +10 データ領域の先頭セクタ番号 WORD data_sector; ///< +10 First sector number of data storage
WORD cluster_max; ///< +12 総クラスタ数+1 WORD cluster_max; ///< +12 Total number of clusters +1
WORD root_sector; ///< +14 ルートディレクトリの先頭セクタ番号 WORD root_sector; ///< +14 First sector number of root directory
// DWORD driverentry; ///< +16 デバイスドライバへのポインタ (未使用) // DWORD driverentry; ///< +16 Device driver pointer (unused)
BYTE media; ///< +20 メディア識別子 BYTE media; ///< +20 Media identifier
// BYTE flag; ///< +21 DPB使用フラグ (未使用) // BYTE flag; ///< +21 Flag used by DPB (unused)
}; };
/// ディレクトリエントリ構造体 /// Directory entry struct
struct dirent_t { struct dirent_t {
BYTE name[8]; ///< + 0 ファイル名 (PADDING 0x20) BYTE name[8]; ///< + 0 File name (PADDING 0x20)
BYTE ext[3]; ///< + 8 拡張子 (PADDING 0x20) BYTE ext[3]; ///< + 8 Extension (PADDING 0x20)
BYTE attr; ///< +11 ファイル属性 BYTE attr; ///< +11 File attribute
BYTE add[10]; ///< +12 ファイル名追加 (PADDING 0x00) BYTE add[10]; ///< +12 File name addition (PADDING 0x00)
WORD time; ///< +22 最終変更時刻 WORD time; ///< +22 Last change time of day
WORD date; ///< +24 最終変更月日 WORD date; ///< +24 Last change date
WORD cluster; ///< +26 クラスタ番号 WORD cluster; ///< +26 Cluster number
DWORD size; ///< +28 ファイルサイズ DWORD size; ///< +28 File size
}; };
/// IOCTRLパラメータ共用体 /// IOCTRL parameter union
union ioctrl_t { union ioctrl_t {
BYTE buffer[8]; ///< バイト単位でのアクセス BYTE buffer[8]; ///< Access in byte units
DWORD param; ///< パラメータ(先頭4バイト) DWORD param; ///< Parameter (First 4 bytes)
WORD media; ///< メディアバイト(先頭2バイト) WORD media; ///< Media byte (First 2 bytes)
}; };
/// コマンドライン引数構造体 /// Command line parameter struct
/** /**
HUMAN68K_PATH_MAX以上のサイズにする The driver itself is included in the beginning of the argument,
so setting to a length longer than HUMAN68K_PATH_MAX
*/ */
struct argument_t { struct argument_t {
BYTE buf[256]; ///< コマンドライン引数 BYTE buf[256]; ///< Command line argument
}; };
} }
/// FILES用バッファ個数 /// Number of FILES buffers
/** /**
Human68kの複数のプロセスがマルチタスクで同時に Under normal circumstances it's enough with just a few buffers,
but Human68k multitasking may lead to multiple threads working
deeply in the system, which is why this value is set this high.
20 Default is 20 buffers.
*/ */
#define XM6_HOST_FILES_MAX 20 #define XM6_HOST_FILES_MAX 20
/// FCB用バッファ個数 /// Number of FCB buffers
/** /**
This decides how many files can be opened at the same time.
100 Default is 100 files.
*/ */
#define XM6_HOST_FCB_MAX 100 #define XM6_HOST_FCB_MAX 100
/// 仮想セクタ/クラスタ 最大個数 /// Max number of virtual clusters and sectors
/** /**
Number of virtual sectors used for accessing the first sector of a file entity.
lzdsysによるアクセスを行なうスレッドの数より多めに確保する Allocating a generous amount to exceed the number of threads lzdsys uses for access.
10 Default is 10 sectors.
*/ */
#define XM6_HOST_PSEUDO_CLUSTER_MAX 10 #define XM6_HOST_PSEUDO_CLUSTER_MAX 10
/// ディレクトリエントリ キャッシュ個数 /// Number of caches for directory entries
/** /**
Human68k Human68k carries out a large number of checks of directory entries when doing an operation
inside a subdirectory. This specifies the number of caches used to speed up this operation.
Cache is allocated per drive. The more you add the faster it gets, but use too many
OS側に負担がかかるので注意 and the host OS gets under a heavy load, so be careful.
16 Default is 16.
*/ */
#define XM6_HOST_DIRENTRY_CACHE_MAX 16 #define XM6_HOST_DIRENTRY_CACHE_MAX 16
/// 1ディレクトリに収納できるエントリの最大数 /// Max number of entries that can be stored per directory
/** /**
When a large number of files are stored in a directory, a larger amount of data than
contemporanous applications can handle will be returned. This may lead to errors such as
partial data being recognized, performance dropping significantly, or OOM crashes.
To guard against this, an upper limit is defined here. In the case of a particular
2560 file manager, the upper limit is 2560 files. This is one good example to use as reference.
6(FATのルートディレクトリでの上限値) Default is around 60000 entries. (Upper limit of the FAT root directory)
*/ */
#define XM6_HOST_DIRENTRY_FILE_MAX 65535 #define XM6_HOST_DIRENTRY_FILE_MAX 65535
/// ファイル名の重複除外パターンの最大数 /// Max number of patterns for file name deduplication
/** /**
Human68k側のファイル名は The file names on the Human68k side are automatically created based on the file system on
Human68k側のファイル名よりもホスト側のファイル名の名 the host side. However, Human68k have stricter file name length restrictions than the host has.
Because of this, there is a risk that file name duplication will occur. When this happens,
Human68k側からファイル名を区別できるようにするためWindrvXM WindrvXM will use a certain renaming heuristic to generate alternate file names to resolve
the duplication. Theoretically, there are over 60 million (36^5) unique file names that
6(365) can be generated by this method. However, in reality any more than a few hundred
deduplications will take excessive processing time. So here an upper limit to deduplication
is set in order to maintain system performance. If a system is operated with common sense,
you should only need a few dozen deduplication patterns, so this value can be kept low
to further improce performance. In the case deduplication is not carried out, multiple files
with the same name will be created. When trying to access such files,
only the first entry will ever be accessed.
36 Default is 36 patterns.
*/ */
#define XM6_HOST_FILENAME_PATTERN_MAX 36 #define XM6_HOST_FILENAME_PATTERN_MAX 36
/// ファイル名重複防止マーク /// Duplicate file identification mark
/** /**
Human68k側ファイル名の名称の区別をつけるときに A symbol used to distinguish between host and Human68k files.
使 Do not use a command shell escape character, or similar protected symbol.
@ Default is '@'.
*/ */
#define XM6_HOST_FILENAME_MARK '@' #define XM6_HOST_FILENAME_MARK '@'
/// WINDRV動作フラグ /// WINDRV operational flags
/** /**
0OSのごみ箱機能を利用する場合は1にする Normally set to 0. When put in the OS trash can for deletion, it is set to 1.
Other values are reserved for future use.
Can be used for future extentions such as internal operational flags or mock media byte.
*/ */
enum { enum {
WINDRV_OPT_REMOVE = 0x00000001, ///< Bit 0: ファイル削除処理 0:直接 1:ごみ箱 WINDRV_OPT_REMOVE = 0x00000001, ///< Bit 0: File delete process 0:Directly 1:Trash can
WINDRV_OPT_ALPHABET = 0x00000020, ///< Bit 5: ファイル名比較 Alphabet区別 0:なし 1:あり 0:-C 1:+C WINDRV_OPT_ALPHABET = 0x00000020, ///< Bit 5: File name comparison; Alphabet distinction 0:No 1:Yes 0:-C 1:+C
WINDRV_OPT_COMPARE_LENGTH = 0x00000040, ///< Bit 6: ファイル名比較 文字数(未実装) 0:18+3 1:8+3 0:+T 1:-T WINDRV_OPT_COMPARE_LENGTH = 0x00000040, ///< Bit 6: File name comparison; String length (unimplemented) 0:18+3 1:8+3 0:+T 1:-T
WINDRV_OPT_CONVERT_LENGTH = 0x00000080, ///< Bit 7: ファイル名変換 文字数 0:18+3 1:8+3 0:-A 1:+A WINDRV_OPT_CONVERT_LENGTH = 0x00000080, ///< Bit 7: File name conversion; String length 0:18+3 1:8+3 0:-A 1:+A
WINDRV_OPT_CONVERT_SPACE = 0x00000100, ///< Bit 8: ファイル名変換 スペース 0:なし 1:'_' WINDRV_OPT_CONVERT_SPACE = 0x00000100, ///< Bit 8: File name conversion; Space 0:No 1:'_'
WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, ///< Bit 9: ファイル名変換 無効な文字 0:なし 1:'_' WINDRV_OPT_CONVERT_BADCHAR = 0x00000200, ///< Bit 9: File name conversion; Invalid char 0:No 1:'_'
WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, ///< Bit10: ファイル名変換 中間のハイフン 0:なし 1:'_' WINDRV_OPT_CONVERT_HYPHENS = 0x00000400, ///< Bit10: File name conversion; Middle hyphen 0:No 1:'_'
WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, ///< Bit11: ファイル名変換 先頭のハイフン 0:なし 1:'_' WINDRV_OPT_CONVERT_HYPHEN = 0x00000800, ///< Bit11: File name conversion; Initial hyphen 0:No 1:'_'
WINDRV_OPT_CONVERT_PERIODS = 0x00001000, ///< Bit12: ファイル名変換 中間のピリオド 0:なし 1:'_' WINDRV_OPT_CONVERT_PERIODS = 0x00001000, ///< Bit12: File name conversion; Middle period 0:No 1:'_'
WINDRV_OPT_CONVERT_PERIOD = 0x00002000, ///< Bit13: ファイル名変換 先頭のピリオド 0:なし 1:'_' WINDRV_OPT_CONVERT_PERIOD = 0x00002000, ///< Bit13: File name conversion; Initial period 0:No 1:'_'
WINDRV_OPT_REDUCED_SPACE = 0x00010000, ///< Bit16: ファイル名短縮 スペース 0:なし 1:短縮 WINDRV_OPT_REDUCED_SPACE = 0x00010000, ///< Bit16: File name reduction; Space 0:No 1:Reduced
WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, ///< Bit17: ファイル名短縮 無効な文字 0:なし 1:短縮 WINDRV_OPT_REDUCED_BADCHAR = 0x00020000, ///< Bit17: File name reduction; Invalid char 0:No 1:Reduced
WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, ///< Bit18: ファイル名短縮 中間のハイフン 0:なし 1:短縮 WINDRV_OPT_REDUCED_HYPHENS = 0x00040000, ///< Bit18: File name reduction Middle hyphen 0:No 1:Reduced
WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, ///< Bit19: ファイル名短縮 先頭のハイフン 0:なし 1:短縮 WINDRV_OPT_REDUCED_HYPHEN = 0x00080000, ///< Bit19: File name reduction Initial hyphen 0:No 1:Reduced
WINDRV_OPT_REDUCED_PERIODS = 0x00100000, ///< Bit20: ファイル名短縮 中間のピリオド 0:なし 1:短縮 WINDRV_OPT_REDUCED_PERIODS = 0x00100000, ///< Bit20: File name reduction Middle period 0:No 1:Reduced
WINDRV_OPT_REDUCED_PERIOD = 0x00200000, ///< Bit21: ファイル名短縮 先頭のピリオド 0:なし 1:短縮 WINDRV_OPT_REDUCED_PERIOD = 0x00200000, ///< Bit21: File name reduction Initial period 0:No 1:Reduced
// Bit2430 ファイル重複防止マーク 0:自動 1127:文字 // Bit2430 Duplicate file identification mark 0:Automatic 1127:Chars
}; };
/// ファイルシステム動作フラグ /// ファイルシステム動作フラグ

View File

@ -2,8 +2,15 @@ import fnmatch
import os import os
import subprocess import subprocess
import time import time
import io
import re
import sys
from ractl_cmds import attach_image from ractl_cmds import (
attach_image,
detach_all,
list_devices,
)
from settings import * from settings import *
@ -19,10 +26,9 @@ def create_new_image(file_name, type, size):
) )
def delete_image(file_name): def delete_file(file_name):
full_path = base_dir + file_name if os.path.exists(file_name):
if os.path.exists(full_path): os.remove(file_name)
os.remove(full_path)
return True return True
else: else:
return False return False
@ -71,3 +77,49 @@ def download_image(url):
full_path = base_dir + file_name full_path = base_dir + file_name
urllib.request.urlretrieve(url, full_path) urllib.request.urlretrieve(url, full_path)
def write_config_csv(file_name):
import csv
# This method takes the output of 'rasctl -l' and parses it into csv format:
# 0: ID
# 1: Unit Number (unused in rascsi-web)
# 2: Device Type
# 3: Device Status (includes the path to a loaded image file)
# TODO: Remove the dependence on rasctl; e.g. when implementing protobuf for rascsi-web
try:
with open(file_name, "w") as csv_file:
writer = csv.writer(csv_file)
for device in list_devices():
if device["type"] != "-":
device_info = list (device.values())
# Match a *nix file path inside column 3, cutting out the last chunk that starts with a space
filesearch = re.search("(^(/[^/ ]*)+)(\s.*)*$", device_info[3])
if filesearch is None:
device_info[3] = ""
else:
device_info[3] = filesearch.group(1)
writer.writerow(device_info)
return True
except:
print ("Could not open file for writing: ", file_name)
return False
def read_config_csv(file_name):
detach_all()
import csv
try:
with open(file_name) as csv_file:
config_reader = csv.reader(csv_file)
#TODO: Remove hard-coded string sanitation (e.g. after implementing protobuf)
exclude_list = ("X68000 HOST BRIDGE", "DaynaPort SCSI/Link", " (WRITEPROTECT)", "NO MEDIA")
for row in config_reader:
image_name = row[3]
for e in exclude_list:
image_name = image_name.replace(e, "")
attach_image(row[0], image_name, row[2])
return True
except:
print ("Could not access file: ", file_name)
return False

View File

@ -1,6 +1,7 @@
import fnmatch import fnmatch
import subprocess import subprocess
import re import re
import logging
from settings import * from settings import *
@ -54,9 +55,12 @@ def get_valid_scsi_ids(devices):
valid_list = list(range(8)) valid_list = list(range(8))
for id in invalid_list: for id in invalid_list:
valid_list.remove(id) try:
valid_list.remove(id)
except:
logging.warning("Reserved SCSI id " + str(id) + " is in use.")
valid_list.reverse() valid_list.reverse()
return valid_list return valid_list

View File

@ -21,14 +21,19 @@
{% block content %} {% block content %}
<h2>Current RaSCSI Configuration</h2> <h2>Current RaSCSI Configuration</h2>
<p>The <em>default</em> configuration will be loaded when the Web UI starts up.</p>
<p>
<form action="/config/load" method="post"> <form action="/config/load" method="post">
<select name="name" > <select name="name" >
{% for config in config_files %} {% for config in config_files %}
<option value="{{config}}">{{config.replace(".csv", '')}}</option> <option value="{{config}}">{{config.replace(".csv", '')}}</option>
{% endfor %} {% endfor %}
</select> </select>
<input type="submit" value="Load" /> <input type="submit" name="load" value="Load" />
<input type="submit" name="delete" value="Delete" />
</form> </form>
</p>
<p>
<form action="/config/save" method="post"> <form action="/config/save" method="post">
<input name="name" placeholder="default"> <input name="name" placeholder="default">
<input type="submit" value="Save" /> <input type="submit" value="Save" />
@ -36,6 +41,7 @@
<form action="/scsi/detach_all" method="post" onsubmit="return confirm('Detach all SCSI Devices?')"> <form action="/scsi/detach_all" method="post" onsubmit="return confirm('Detach all SCSI Devices?')">
<input type="submit" value="Detach All" /> <input type="submit" value="Detach All" />
</form> </form>
</p>
<table cellpadding="3" border="black"> <table cellpadding="3" border="black">
<tbody> <tbody>

View File

@ -6,9 +6,11 @@ from flask import Flask, render_template, request, flash, url_for, redirect, sen
from file_cmds import ( from file_cmds import (
create_new_image, create_new_image,
download_file_to_iso, download_file_to_iso,
delete_image, delete_file,
unzip_file, unzip_file,
download_image, download_image,
write_config_csv,
read_config_csv,
) )
from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service from pi_cmds import shutdown_pi, reboot_pi, running_version, rascsi_service
from ractl_cmds import ( from ractl_cmds import (
@ -54,30 +56,28 @@ def index():
def config_save(): def config_save():
file_name = request.form.get("name") or "default" file_name = request.form.get("name") or "default"
file_name = f"{base_dir}{file_name}.csv" file_name = f"{base_dir}{file_name}.csv"
import csv
with open(file_name, "w") as csv_file: write_config_csv(file_name)
writer = csv.writer(csv_file)
for device in list_devices():
if device["type"] != "-":
writer.writerow(device.values())
flash(f"Saved config to {file_name}!") flash(f"Saved config to {file_name}!")
return redirect(url_for("index")) return redirect(url_for("index"))
@app.route("/config/load", methods=["POST"]) @app.route("/config/load", methods=["POST"])
def config_load(): def config_load():
file_name = request.form.get("name") or "default.csv" file_name = request.form.get("name")
file_name = f"{base_dir}{file_name}" file_name = f"{base_dir}{file_name}"
detach_all()
import csv
with open(file_name) as csv_file: if "load" in request.form:
config_reader = csv.reader(csv_file) if read_config_csv(file_name):
for row in config_reader: flash(f"Loaded config from {file_name}!")
image_name = row[3].replace("(WRITEPROTECT)", "") else:
attach_image(row[0], image_name, row[2]) flash(f"Failed to load {file_name}!", "error")
flash(f"Loaded config from {file_name}!") elif "delete" in request.form:
if delete_file(file_name):
flash(f"Deleted config {file_name}!")
else:
flash(f"Failed to delete {file_name}!", "error")
return redirect(url_for("index")) return redirect(url_for("index"))
@ -144,6 +144,11 @@ def attach():
flash(f"Unknown file type. Valid files are: {', '.join(valid_file_suffix)}", "error") flash(f"Unknown file type. Valid files are: {', '.join(valid_file_suffix)}", "error")
return redirect(url_for("index")) return redirect(url_for("index"))
# Validate the SCSI ID
if re.match("[0-7]", str(scsi_id)) == None:
flash(f"Invalid SCSI ID. Should be a number between 0-7", "error")
return redirect(url_for("index"))
process = attach_image(scsi_id, file_name, image_type) process = attach_image(scsi_id, file_name, image_type)
if process.returncode == 0: if process.returncode == 0:
flash(f"Attached {file_name} to SCSI id {scsi_id}!") flash(f"Attached {file_name} to SCSI id {scsi_id}!")
@ -283,7 +288,7 @@ def download():
@app.route("/files/delete", methods=["POST"]) @app.route("/files/delete", methods=["POST"])
def delete(): def delete():
image = request.form.get("image") image = request.form.get("image")
if delete_image(image): if delete_file(base_dir + image):
flash("File " + image + " deleted") flash("File " + image + " deleted")
return redirect(url_for("index")) return redirect(url_for("index"))
else: else:
@ -309,6 +314,8 @@ if __name__ == "__main__":
app.config["UPLOAD_FOLDER"] = base_dir app.config["UPLOAD_FOLDER"] = base_dir
os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True) os.makedirs(app.config["UPLOAD_FOLDER"], exist_ok=True)
app.config["MAX_CONTENT_LENGTH"] = MAX_FILE_SIZE app.config["MAX_CONTENT_LENGTH"] = MAX_FILE_SIZE
read_config_csv(f"{base_dir}default.csv")
import bjoern import bjoern
print("Serving rascsi-web...") print("Serving rascsi-web...")