├── subprojects ├── apa │ ├── irx │ ├── src │ ├── include │ ├── _init.c │ └── meson.build ├── pfs │ ├── irx │ ├── src │ ├── include │ ├── _init.c │ └── meson.build ├── iomanX │ ├── src │ ├── include │ └── meson.build ├── fakeps2sdk │ ├── include │ │ ├── irx.h │ │ ├── intrman.h │ │ ├── loadcore.h │ │ ├── dev9.h │ │ ├── types.h │ │ ├── thsemap.h │ │ ├── thbase.h │ │ ├── sysclib.h │ │ ├── io_common.h │ │ ├── atad.h │ │ ├── iox_stat.h │ │ └── hdd-ioctl.h │ ├── meson.build │ ├── thsemap.c │ └── atad.c └── hdlfs │ ├── _init.c │ ├── irx_imports.h │ ├── meson.build │ ├── README.txt │ ├── Research.txt │ ├── hdlfs.h │ └── main.c ├── src ├── shell.h ├── startup.c ├── util.h ├── hl.h ├── pfsd_client.h ├── util.c ├── pfslib_compat.h ├── ps2kinst.c ├── pfslib_compat.c ├── pfsd_common.h ├── iomanX_port.h ├── hl.c ├── host_adapter.c ├── pfsd_client.c └── pfs2tar.c ├── .gitmodules ├── dokany.sh ├── .editorconfig ├── .gitignore ├── meson_options.txt ├── .clang-format ├── test └── pfsshell_test.tcl ├── README.md ├── meson.build ├── COPYING.AFLv2 ├── .github └── workflows │ └── ci.yml └── COPYING /subprojects/apa/irx: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/hdd/apa/src -------------------------------------------------------------------------------- /subprojects/pfs/irx: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/hdd/pfs/src -------------------------------------------------------------------------------- /subprojects/apa/src: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/hdd/libapa/src -------------------------------------------------------------------------------- /subprojects/pfs/src: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/hdd/libpfs/src -------------------------------------------------------------------------------- /subprojects/apa/include: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/hdd/libapa/include -------------------------------------------------------------------------------- /subprojects/iomanX/src: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/system/iomanx/src -------------------------------------------------------------------------------- /subprojects/pfs/include: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/hdd/libpfs/include -------------------------------------------------------------------------------- /subprojects/iomanX/include: -------------------------------------------------------------------------------- 1 | ../../external/ps2sdk/iop/system/iomanx/include -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/irx.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "types.h" 4 | -------------------------------------------------------------------------------- /src/shell.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int shell(FILE *in, FILE *out, FILE *err); 6 | -------------------------------------------------------------------------------- /src/startup.c: -------------------------------------------------------------------------------- 1 | #include "shell.h" 2 | 3 | 4 | int main(void) 5 | { 6 | return (shell(stdin, stdout, stderr)); 7 | } 8 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/intrman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define CpuSuspendIntr(x) 0 4 | #define CpuResumeIntr(x) 0 5 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/loadcore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define FlushIcache() 0 6 | #define RegisterLibraryEntries(x) 0 7 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | int parse_line(char *line, /* modified */ 6 | char *(*tokens)[], 7 | size_t *count); 8 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/dev9.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define dev9Shutdown() \ 4 | do { \ 5 | } while (0) 6 | 7 | #define dev9RegisterShutdownCb(x, y) 0 8 | -------------------------------------------------------------------------------- /subprojects/apa/_init.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int apa_start(int argc, char **argv); 4 | 5 | int _init_apa(int argc, char *argv[]) 6 | { 7 | return apa_start(argc, argv); 8 | } 9 | -------------------------------------------------------------------------------- /subprojects/pfs/_init.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int pfs_start(int argc, char *argv[]); 4 | 5 | int _init_pfs(int argc, char *argv[]) 6 | { 7 | return pfs_start(argc, argv); 8 | } 9 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/types.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define MODULE_RESIDENT_END 0 7 | #define MODULE_NO_RESIDENT_END 1 8 | 9 | #define IRX_ID(x, y, z) 10 | #define IRX_VER(x, y) 0 11 | -------------------------------------------------------------------------------- /subprojects/hdlfs/_init.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int hdlfs_start(int argc, char *argv[]); 4 | 5 | int _init_hdlfs(int argc, char *argv[]) 6 | { 7 | char *args[] = 8 | { 9 | NULL}; 10 | int result = hdlfs_start(0, args); 11 | return (result); 12 | } 13 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/thsemap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct 4 | { 5 | u32 attr; 6 | u32 option; 7 | int initial; 8 | int max; 9 | } iop_sema_t; 10 | 11 | int CreateSema(iop_sema_t *sema); 12 | int DeleteSema(int semid); 13 | int WaitSema(int semid); 14 | int SignalSema(int semid); 15 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/meson_toolchains"] 2 | path = external/meson_toolchains 3 | url = https://github.com/uyjulian/meson_toolchains.git 4 | [submodule "external/dokany"] 5 | path = external/dokany 6 | url = https://github.com/dokan-dev/dokany 7 | [submodule "external/ps2sdk"] 8 | path = external/ps2sdk 9 | url = https://github.com/ps2dev/ps2sdk.git 10 | -------------------------------------------------------------------------------- /dokany.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | rm -rf external/dokany/dokan_fuse/build 3 | mkdir external/dokany/dokan_fuse/build 4 | cmake -G 'MinGW Makefiles' -Sexternal/dokany/dokan_fuse -Bexternal/dokany/dokan_fuse/build -DCMAKE_SHARED_LINKER_FLAGS="-static -static-libgcc -static-libstdc++" -DCMAKE_INSTALL_PREFIX='./build' -DCMAKE_POLICY_VERSION_MINIMUM=3.5 5 | mingw32-make -C external/dokany/dokan_fuse/build 6 | cp -f external/dokany/dokan_fuse/build/libdokanfuse*.dll build/ 7 | -------------------------------------------------------------------------------- /subprojects/hdlfs/irx_imports.h: -------------------------------------------------------------------------------- 1 | /* 2 | * irx_imports.h - Defines all IRX imports. 3 | */ 4 | 5 | #ifndef IOP_IRX_IMPORTS_H 6 | #define IOP_IRX_IMPORTS_H 7 | 8 | #include 9 | 10 | /* Please keep these in alphabetical order! */ 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #endif /* IOP_IRX_IMPORTS_H */ 20 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/meson.build: -------------------------------------------------------------------------------- 1 | project('fakeps2sdk', 'c') 2 | 3 | libfakeps2sdk_inc = [ 4 | include_directories('include') 5 | ] 6 | libfakeps2sdk_src = [ 7 | 'atad.c', 8 | 'thsemap.c', 9 | ] 10 | libfakeps2sdk = static_library('fakeps2sdk', sources: libfakeps2sdk_src, include_directories: libfakeps2sdk_inc) 11 | 12 | libfakeps2sdk_dep = declare_dependency(include_directories: libfakeps2sdk_inc, link_with: libfakeps2sdk) 13 | libfakeps2sdk_noinclude_dep = declare_dependency(link_with: libfakeps2sdk) 14 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig: http://EditorConfig.org 2 | 3 | # Top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | charset = utf-8 12 | 13 | # 4 space indentation 14 | [*.{c,h,js,css,html}] 15 | indent_style = space 16 | indent_size = 4 17 | 18 | # 2 space indentation 19 | [*.{json,xml,yaml,yml}] 20 | indent_style = space 21 | indent_size = 2 22 | 23 | # Tab indentation 24 | [Makefile*] 25 | indent_style = tab 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE! Please use 'git ls-files -i --exclude-standard -c' 3 | # command after changing this file, to see if there are 4 | # any tracked files which get ignored after the change. 5 | # 6 | # Normal rules 7 | # 8 | .* 9 | *.o 10 | *.a 11 | *.diff 12 | *.elf 13 | *.ELF 14 | *.erl 15 | *.exe 16 | *.irx 17 | *.map 18 | *.o 19 | *.patch 20 | *.rej 21 | *.s 22 | *.zip 23 | *.ZIP 24 | *.a 25 | 26 | # 27 | # files that we don't want to ignore 28 | # 29 | !.git* 30 | !.editorconfig 31 | !.clang-format 32 | 33 | # 34 | # Generated source files 35 | # 36 | *.IMG 37 | pfsshell 38 | build-mingw 39 | build 40 | -------------------------------------------------------------------------------- /subprojects/iomanX/meson.build: -------------------------------------------------------------------------------- 1 | project('iomanX', 'c') 2 | 3 | libfakeps2sdk_proj = subproject('fakeps2sdk') 4 | libfakeps2sdk_dep = libfakeps2sdk_proj.get_variable('libfakeps2sdk_dep') 5 | 6 | libiomanX_inc = [ 7 | include_directories('.'), 8 | include_directories('include'), 9 | ] 10 | 11 | libiomanX_src = [ 12 | 'src/ioman_sbv.c', 13 | 'src/iomanX.c', 14 | ] 15 | libiomanX = static_library( 16 | 'iomanX', 17 | sources: libiomanX_src, 18 | include_directories: libiomanX_inc, 19 | dependencies: libfakeps2sdk_dep 20 | ) 21 | 22 | libiomanX_dep = declare_dependency(include_directories: libiomanX_inc, link_with: libiomanX) 23 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/thbase.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define TH_C 0x02000000 6 | 7 | typedef struct _iop_thread 8 | { 9 | u32 attr; 10 | u32 option; 11 | void (*thread)(void *); 12 | u32 stacksize; 13 | u32 priority; 14 | } iop_thread_t; 15 | 16 | typedef struct _iop_sys_clock 17 | { 18 | u32 lo, hi; 19 | } iop_sys_clock_t; 20 | 21 | int CreateThread(iop_thread_t *thread); 22 | int StartThread(int thid, void *arg); 23 | 24 | int CheckThreadStack(void); 25 | 26 | int GetSystemTime(iop_sys_clock_t *sys_clock); 27 | 28 | void SysClock2USec(iop_sys_clock_t *sys_clock, u32 *sec, u32 *usec); 29 | -------------------------------------------------------------------------------- /meson_options.txt: -------------------------------------------------------------------------------- 1 | 2 | option( 3 | 'enable_pfsfuse', 4 | type: 'boolean', 5 | value: false, 6 | description: 'Enable building of pfsfuse executable. Requires FUSE to be installed' 7 | ) 8 | 9 | option( 10 | 'enable_pfs2tar', 11 | type: 'boolean', 12 | value: true, 13 | description: 'Enable building of pfs2tar executable.' 14 | ) 15 | 16 | option( 17 | 'enable_pfsd', 18 | type: 'boolean', 19 | value: false, 20 | description: 'Enable building of pfsd executable. Requires SysV and Linux specific APIs.' 21 | ) 22 | 23 | option( 24 | 'enable_ps2kinst', 25 | type: 'boolean', 26 | value: false, 27 | description: 'Enable building of ps2kinst executable. Requires SysV and Linux specific APIs.' 28 | ) 29 | -------------------------------------------------------------------------------- /subprojects/hdlfs/meson.build: -------------------------------------------------------------------------------- 1 | project('hdlfs', 'c') 2 | 3 | libfakeps2sdk_proj = subproject('fakeps2sdk') 4 | libfakeps2sdk_dep = libfakeps2sdk_proj.get_variable('libfakeps2sdk_dep') 5 | 6 | libiomanX_proj = subproject('iomanX') 7 | libiomanX_dep = libiomanX_proj.get_variable('libiomanX_dep') 8 | 9 | libhdlfs_inc = [ 10 | include_directories('.') 11 | ] 12 | libhdlfs_src = [ 13 | '_init.c', 14 | 'main.c' 15 | ] 16 | libapa_deps = [ 17 | libfakeps2sdk_dep, 18 | libiomanX_dep, 19 | ] 20 | libhdlfs = static_library( 21 | 'hdlfs', 22 | sources: libhdlfs_src, 23 | include_directories: libhdlfs_inc, 24 | dependencies: libapa_deps 25 | ) 26 | 27 | libhdlfs_dep = declare_dependency(include_directories: libhdlfs_inc, link_with: libhdlfs) 28 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/sysclib.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define index strchr 9 | 10 | typedef uint8_t u8; 11 | typedef uint16_t u16; 12 | 13 | typedef volatile uint8_t vu8; 14 | typedef volatile uint16_t vu16; 15 | 16 | typedef uint32_t u32; 17 | typedef uint64_t u64; 18 | 19 | typedef volatile uint32_t vu32; 20 | typedef volatile uint64_t vu64; 21 | 22 | typedef int8_t s8; 23 | typedef int16_t s16; 24 | 25 | typedef volatile int8_t vs8; 26 | typedef volatile int16_t vs16; 27 | 28 | typedef int32_t s32; 29 | typedef int64_t s64; 30 | 31 | typedef volatile int32_t vs32; 32 | typedef volatile int64_t vs64; 33 | 34 | typedef uintptr_t uiptr; 35 | typedef intptr_t siptr; 36 | 37 | typedef volatile uintptr_t vuiptr; 38 | typedef volatile intptr_t vsiptr; 39 | -------------------------------------------------------------------------------- /src/hl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* copy file, host to PFS */ 4 | int copyto(const char *mount_point, const char *dest, const char *src); 5 | 6 | /* copy file, PFS to host */ 7 | int copyfrom(const char *mount_point, const char *src, const char *dest); 8 | 9 | /* list files at PFS */ 10 | int list_dir_objects(int dh, int lsmode); 11 | int ls(const char *mount_point, const char *path); 12 | 13 | /* list HDD partitions */ 14 | int lspart(int lsmode); 15 | 16 | /* create PFS onto an existing partition */ 17 | int mkpfs(const char *mount_point); 18 | 19 | /* create partition of any type and format it as PFS if type=0x0100; 20 | * so far the only sizes supported are powers of 2 */ 21 | int mkpart(const char *mount_point, long size_in_mb, int format); 22 | 23 | /* initialize PS2 HDD with APA partitioning and create common partitions 24 | * (__mbr, __common, __net, etc.) */ 25 | int initialize(void); 26 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/io_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define FIO_O_RDONLY 0x0001 4 | #define FIO_O_WRONLY 0x0002 5 | #define FIO_O_RDWR 0x0003 6 | #define FIO_O_DIROPEN 0x0008 // Internal use for dopen 7 | #define FIO_O_NBLOCK 0x0010 8 | #define FIO_O_APPEND 0x0100 9 | #define FIO_O_CREAT 0x0200 10 | #define FIO_O_TRUNC 0x0400 11 | #define FIO_O_EXCL 0x0800 12 | #define FIO_O_NOWAIT 0x8000 13 | 14 | #define FIO_MT_RDWR 0x00 15 | #define FIO_MT_RDONLY 0x01 16 | 17 | #define FIO_SEEK_SET 0 18 | #define FIO_SEEK_CUR 1 19 | #define FIO_SEEK_END 2 20 | 21 | typedef struct 22 | { 23 | unsigned int mode; 24 | unsigned int attr; 25 | unsigned int size; 26 | unsigned char ctime[8]; 27 | unsigned char atime[8]; 28 | unsigned char mtime[8]; 29 | unsigned int hisize; 30 | } io_stat_t; 31 | 32 | typedef struct 33 | { 34 | io_stat_t stat; 35 | char name[256]; 36 | void *privdata; 37 | } io_dirent_t; 38 | -------------------------------------------------------------------------------- /subprojects/apa/meson.build: -------------------------------------------------------------------------------- 1 | project('apa', 'c') 2 | 3 | libfakeps2sdk_proj = subproject('fakeps2sdk') 4 | libfakeps2sdk_dep = libfakeps2sdk_proj.get_variable('libfakeps2sdk_dep') 5 | 6 | libiomanX_proj = subproject('iomanX') 7 | libiomanX_dep = libiomanX_proj.get_variable('libiomanX_dep') 8 | 9 | libapa_inc = [ 10 | include_directories('.'), 11 | include_directories('include'), 12 | include_directories('irx'), 13 | ] 14 | libapa_src = [ 15 | '_init.c', 16 | 'irx/hdd_fio.c', 17 | 'irx/hdd_blkio.c', 18 | 'irx/hdd.c', 19 | 'src/apa.c', 20 | 'src/cache.c', 21 | 'src/free.c', 22 | 'src/journal.c', 23 | 'src/misc.c', 24 | 'src/password.c', 25 | ] 26 | libapa_deps = [ 27 | libfakeps2sdk_dep, 28 | libiomanX_dep, 29 | ] 30 | libapa_c_args = [ 31 | '-DAPA_POSIX_VER', 32 | '-DAPA_USE_ATAD', 33 | ] 34 | libapa = static_library( 35 | 'apa', 36 | sources: libapa_src, 37 | include_directories: libapa_inc, 38 | dependencies: libapa_deps, 39 | c_args: libapa_c_args 40 | ) 41 | 42 | libapa_dep = declare_dependency(include_directories: libapa_inc, link_with: libapa) 43 | -------------------------------------------------------------------------------- /subprojects/pfs/meson.build: -------------------------------------------------------------------------------- 1 | project('pfs', 'c') 2 | 3 | libfakeps2sdk_proj = subproject('fakeps2sdk') 4 | libfakeps2sdk_dep = libfakeps2sdk_proj.get_variable('libfakeps2sdk_dep') 5 | 6 | libiomanX_proj = subproject('iomanX') 7 | libiomanX_dep = libiomanX_proj.get_variable('libiomanX_dep') 8 | 9 | libpfs_inc = [ 10 | include_directories('.'), 11 | include_directories('include'), 12 | include_directories('irx') 13 | ] 14 | libpfs_src = [ 15 | '_init.c', 16 | 'irx/pfs.c', 17 | 'irx/pfs_fio.c', 18 | 'irx/pfs_fioctl.c', 19 | 'src/bitmap.c', 20 | 'src/block.c', 21 | 'src/blockWrite.c', 22 | 'src/cache.c', 23 | 'src/dir.c', 24 | 'src/inode.c', 25 | 'src/journal.c', 26 | 'src/misc.c', 27 | 'src/super.c', 28 | 'src/superWrite.c', 29 | ] 30 | libpfs_deps = [ 31 | libfakeps2sdk_dep, 32 | libiomanX_dep, 33 | ] 34 | libpfs_c_args = [ 35 | '-DPFS_POSIX_VER', 36 | ] 37 | libpfs = static_library( 38 | 'pfs', 39 | sources: libpfs_src, 40 | include_directories: libpfs_inc, 41 | dependencies: libpfs_deps, 42 | c_args: libpfs_c_args 43 | ) 44 | 45 | libpfs_dep = declare_dependency(include_directories: libpfs_inc, link_with: libpfs) 46 | -------------------------------------------------------------------------------- /subprojects/hdlfs/README.txt: -------------------------------------------------------------------------------- 1 | How this driver is designed to be used for creating game partitions: 2 | -------------------------------------------------------------------- 3 | 4 | 1. A partition is created with the accompanying modified PS2HDD module. If the partition is not large enough to hold the entire game, create sufficient sub-partitions to store the entire game. 5 | Note: The size of the sub-partitions is expected to be of the same size as the main partition, with the exception of the last sub partition. All partitions are expected to be of the largest allowable size, except for the last partition (Which may be smaller). 6 | 2. mount the created partition with HDLFS. 7 | HDLFS shall allocate a handle for future I/O access to the partition. 8 | 3. Format the partition, specifying the game's data. 9 | HDLFS shall initialize the HDL Game information area (Sector offset 0x800 in the partition extended attribute area) with the specified parameters. 10 | 4. Open the mount point and begin writing to the partition. 11 | HDLFS will automatically determine the partition to write data to. 12 | 5. Close the mount point. 13 | 6. Unmount the mount point. Failing to do so might result in data loss. 14 | HDLFS might update game installation data and will close the mounted volume at this point. 15 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/atad.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | /* These are used with the dir parameter of ata_device_sector_io(). */ 6 | #define ATA_DIR_READ 0 7 | #define ATA_DIR_WRITE 1 8 | 9 | typedef struct _ata_devinfo 10 | { 11 | u32 exists; /* Was successfully probed. */ 12 | u32 has_packet; /* Supports the PACKET command set. */ 13 | u32 total_sectors; /* Total number of user sectors. */ 14 | u32 security_status; /* Word 0x100 of the identify info. */ 15 | } ata_devinfo_t; 16 | 17 | ata_devinfo_t *ata_get_devinfo(int device); 18 | int ata_device_sector_io(int device, void *buf, u32 lba, u32 nsectors, int dir); 19 | 20 | #define sceAtaInit ata_get_devinfo 21 | #define sceAtaDmaTransfer ata_device_sector_io 22 | 23 | #define ata_device_sce_sec_unlock(x, y) 0 24 | #define sceAtaSecurityUnLock(x, y) 0 25 | #define ata_device_idle(x, y) 0 26 | #define sceAtaIdle(x, y) 0 27 | #define ata_device_idle_immediate(x) 0 28 | #define sceAtaIdleImmediate(x) 0 29 | #define ata_device_sce_identify_drive(x, y) -1 30 | #define sceAtaGetSceId(x, y) -1 31 | #define ata_device_smart_get_status(x) 0 32 | #define sceAtaSmartReturnStatus(x) 0 33 | #define ata_device_smart_save_attr(x) 0 34 | #define sceAtaSmartSaveAttr(x) 0 35 | #define ata_device_flush_cache(x) 0 36 | #define sceAtaFlushCache(x) 0 37 | -------------------------------------------------------------------------------- /subprojects/hdlfs/Research.txt: -------------------------------------------------------------------------------- 1 | An APA partition can have a parent and child partition(s). All APA partitions (Regardless of whether they are child or parent partitions), have a 2-sector area reserved for storing the APA partition header. 2 | 3 | There is also a reserved area at the beginning of each parent partition, known as the attribute area. It contains information about the contents of the partition and boot information. It also contains resources like icons for a Sony dashboard to use for display. 4 | Parent partitions have a 4MB (0x2000 512-byte sectors) attribute area, which includes it's APA partition header area (2 sectors long). 5 | 6 | File READ/WRITE I/O operations to the APA device is used for reading and writing to the attribute area of the parent partition. 7 | Child partitions do not have such an area, and hence file READ/WRITE I/O operations should not be performed with child partitions. 8 | 9 | When data is written to the attribute area via the READ/WRITE I/O operation functions of the APA driver, data is written to sector 8 onwards. 10 | 11 | IOCTL2 calls to the APA device is used for reading and writing data to the partition. As parent partitions have a 4MB attribute area and an APA header at the beginning of it, therefore, data transfers should begin at logical sector 0x2000. 12 | As for child partitions, they still have the 2-sector APA header. Therefore, data transfers should begin from logical sector 2 onwards. 13 | 14 | HDLoader has the game's information stored at sector 0x800 to 0x801 of the parent partition's attribute area, when accessed via the APA driver's file I/O operation functions. 15 | (Therefore, their actual offsets are 0x808 and 0x809 from the start of the partition, respectively) 16 | -------------------------------------------------------------------------------- /subprojects/hdlfs/hdlfs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define __start hdlfs_start 4 | 5 | #define HDL_FS_MAGIC 0x1337 6 | #define APA_FLAG_SUB 0x0001 7 | #define HDL_INFO_MAGIC 0xDEADFEED 8 | #define HDL_GAME_DATA_OFFSET 0x100000 9 | 10 | #define HDLFS_GAME_TITLE_LEN 160 11 | #define HDLFS_STARTUP_PTH_LEN 60 12 | 13 | typedef struct 14 | { 15 | unsigned int part_offset; // In CD-ROM (2048-byte) sectors 16 | unsigned int data_start; // In 512-byte HDD sectors 17 | unsigned int part_size; // In bytes 18 | } part_specs_t; 19 | 20 | typedef struct // size = 1024 bytes 21 | { 22 | unsigned int magic; 23 | unsigned short int reserved; 24 | unsigned short int version; 25 | char gamename[HDLFS_GAME_TITLE_LEN]; 26 | unsigned char hdl_compat_flags; 27 | unsigned char ops2l_compat_flags; 28 | unsigned char dma_type; 29 | unsigned char dma_mode; 30 | char startup[HDLFS_STARTUP_PTH_LEN]; /* NOTE: The startup file name here must be without the ";1" suffix. */ 31 | unsigned int layer1_start; 32 | unsigned int discType; 33 | int num_partitions; 34 | part_specs_t part_specs[65]; 35 | } hdl_game_info; 36 | 37 | enum HDLFS_DEVCTL_codes { 38 | HDLFS_DEVCTL_GET_STARTUP_PATH = 0x1000, 39 | HDLFS_DEVCTL_GET_TITLE, 40 | HDLFS_DEVCTL_SET_TITLE 41 | }; 42 | 43 | struct HDLFS_FormatArgs 44 | { 45 | unsigned char CompatFlags, DiscType; 46 | unsigned char TRType, TRMode; 47 | unsigned int NumSectors, Layer1Start; 48 | char GameTitle[HDLFS_GAME_TITLE_LEN]; // In UTF-8 49 | char StartupPath[HDLFS_STARTUP_PTH_LEN]; /* NOTE: The startup file name here must be without the ";1" suffix. */ 50 | }; 51 | -------------------------------------------------------------------------------- /src/pfsd_client.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pfsd_common.h" 4 | #include "pfslib_compat.h" 5 | #include 6 | 7 | extern int scepfsdInit(void); 8 | extern void scepfsdExit(void); 9 | extern int scepfsdOpen(const char *name, int flags, int mode); 10 | extern int scepfsdLseek(int fd, int offset, int whence); 11 | extern int64_t scepfsdLseek64(int fd, int64_t offset, int whence); 12 | extern int scepfsdRead(int fd, void *ptr, size_t size); 13 | extern int scepfsdWrite(int fd, void *ptr, size_t size); 14 | extern int scepfsdClose(int fd); 15 | extern int scepfsdDopen(const char *name); 16 | extern int scepfsdDread(int fd, iox_dirent_t *iox_dirent); 17 | extern int scepfsdDclose(int fd); 18 | extern int scepfsdChstat(const char *name, iox_stat_t *stat, int mask); 19 | extern int scepfsdGetstat(const char *name, iox_stat_t *stat); 20 | extern int scepfsdMkdir(const char *name, int mode); 21 | extern int scepfsdRmdir(const char *name); 22 | extern int scepfsdChdir(const char *name); 23 | extern int scepfsdRemove(const char *name); 24 | extern int scepfsdRename(const char *old, const char *new_); 25 | extern int scepfsdIoctl(int fd, int cmd, void *arg); 26 | extern int scepfsdIoctl2(int fd, int command, void *arg, size_t arglen, void *buf, size_t buflen); 27 | extern int scepfsdSymlink(const char *old, const char *new_); 28 | extern int scepfsdReadlink(const char *name, char *buf, size_t buflen); 29 | extern int scepfsdSync(const char *dev, int flag); 30 | extern int scepfsdFormat(const char *dev, const char *blockdev, void *arg, size_t arglen); 31 | extern int scepfsdMount(const char *fsname, const char *devname, int flag, void *arg, size_t arglen); 32 | extern int scepfsdUmount(const char *fsname); 33 | extern int scepfsdDevctl(const char *name, int cmd, const void *arg, size_t arglen, void *buf, size_t buflen); 34 | extern int scepfsdSetReadAhead(int enabled); 35 | extern int scepfsdGetReadAhead(void); 36 | extern int scepfsdGetMountPoint(const char *in_oldmap, char *out_newmap); 37 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | AccessModifierOffset: -4 4 | AlignAfterOpenBracket: Align 5 | AlignConsecutiveAssignments: false 6 | AlignConsecutiveBitFields: AcrossEmptyLinesAndComments 7 | AlignConsecutiveDeclarations: false 8 | AlignConsecutiveMacros: AcrossComments 9 | AlignEscapedNewlines: Left 10 | AlignOperands: true 11 | AlignTrailingComments: true 12 | AllowAllParametersOfDeclarationOnNextLine: true 13 | AllowShortBlocksOnASingleLine: false 14 | AllowShortCaseLabelsOnASingleLine: false 15 | AllowShortFunctionsOnASingleLine: All 16 | AllowShortIfStatementsOnASingleLine: false 17 | AllowShortLoopsOnASingleLine: false 18 | AlwaysBreakAfterDefinitionReturnType: None 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakBeforeMultilineStrings: false 21 | AlwaysBreakTemplateDeclarations: true 22 | BinPackArguments: true 23 | BinPackParameters: true 24 | BraceWrapping: 25 | AfterClass: true 26 | AfterControlStatement: false 27 | AfterEnum: false 28 | AfterFunction: true 29 | AfterNamespace: true 30 | AfterObjCDeclaration: false 31 | AfterStruct: true 32 | AfterUnion: true 33 | BeforeCatch: false 34 | BeforeElse: false 35 | IndentBraces: false 36 | BreakBeforeBinaryOperators: None 37 | BreakBeforeBraces: Custom 38 | BreakBeforeTernaryOperators: false 39 | BreakConstructorInitializersBeforeComma: true 40 | ColumnLimit: 0 41 | CommentPragmas: '^ (IWYU pragma:|NOLINT)' 42 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 43 | ConstructorInitializerIndentWidth: 4 44 | ContinuationIndentWidth: 4 45 | Cpp11BracedListStyle: true 46 | DerivePointerAlignment: false 47 | DisableFormat: false 48 | ForEachMacros: [] 49 | IndentCaseLabels: true 50 | IndentWidth: 4 51 | IndentWrappedFunctionNames: false 52 | KeepEmptyLinesAtTheStartOfBlocks: true 53 | MacroBlockBegin: '' 54 | MacroBlockEnd: '' 55 | MaxEmptyLinesToKeep: 3 56 | NamespaceIndentation: None 57 | ObjCBlockIndentWidth: 2 58 | ObjCSpaceAfterProperty: false 59 | ObjCSpaceBeforeProtocolList: true 60 | PenaltyBreakBeforeFirstCallParameter: 19 61 | PenaltyBreakComment: 300 62 | PenaltyBreakFirstLessLess: 120 63 | PenaltyBreakString: 1000 64 | PenaltyExcessCharacter: 1000000 65 | PenaltyReturnTypeOnItsOwnLine: 60 66 | PointerAlignment: Right 67 | ReflowComments: true 68 | SortIncludes: false 69 | SpaceAfterCStyleCast: false 70 | SpaceBeforeAssignmentOperators: true 71 | SpaceBeforeParens: ControlStatements 72 | SpaceInEmptyParentheses: false 73 | SpacesBeforeTrailingComments: 1 74 | SpacesInAngles: false 75 | SpacesInContainerLiterals: true 76 | SpacesInCStyleCastParentheses: false 77 | SpacesInParentheses: false 78 | SpacesInSquareBrackets: false 79 | Standard: Cpp11 80 | TabWidth: 4 81 | UseTab: Never 82 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/iox_stat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Flags for chstat 'statmask' 4 | #define FIO_CST_MODE 0x0001 5 | #define FIO_CST_ATTR 0x0002 6 | #define FIO_CST_SIZE 0x0004 7 | #define FIO_CST_CT 0x0008 8 | #define FIO_CST_AT 0x0010 9 | #define FIO_CST_MT 0x0020 10 | #define FIO_CST_PRVT 0x0040 11 | 12 | // File mode flags 13 | /** Format mask */ 14 | #define FIO_S_IFMT 0xF000 15 | /** Symbolic link */ 16 | #define FIO_S_IFLNK 0x4000 17 | /** Regular file */ 18 | #define FIO_S_IFREG 0x2000 19 | /** Directory */ 20 | #define FIO_S_IFDIR 0x1000 21 | 22 | // Access rights 23 | /** SUID */ 24 | #define FIO_S_ISUID 0x0800 25 | /** SGID */ 26 | #define FIO_S_ISGID 0x0400 27 | /** Sticky bit */ 28 | #define FIO_S_ISVTX 0x0200 29 | 30 | /** User access rights mask */ 31 | #define FIO_S_IRWXU 0x01C0 32 | /** read */ 33 | #define FIO_S_IRUSR 0x0100 34 | /** write */ 35 | #define FIO_S_IWUSR 0x0080 36 | /** execute */ 37 | #define FIO_S_IXUSR 0x0040 38 | 39 | /** Group access rights mask */ 40 | #define FIO_S_IRWXG 0x0038 41 | /** read */ 42 | #define FIO_S_IRGRP 0x0020 43 | /** write */ 44 | #define FIO_S_IWGRP 0x0010 45 | /** execute */ 46 | #define FIO_S_IXGRP 0x0008 47 | 48 | /** Others access rights mask */ 49 | #define FIO_S_IRWXO 0x0007 50 | /** read */ 51 | #define FIO_S_IROTH 0x0004 52 | /** write */ 53 | #define FIO_S_IWOTH 0x0002 54 | /** execute */ 55 | #define FIO_S_IXOTH 0x0001 56 | 57 | // File mode checking macros 58 | #define FIO_S_ISLNK(m) (((m) & FIO_S_IFMT) == FIO_S_IFLNK) 59 | #define FIO_S_ISREG(m) (((m) & FIO_S_IFMT) == FIO_S_IFREG) 60 | #define FIO_S_ISDIR(m) (((m) & FIO_S_IFMT) == FIO_S_IFDIR) 61 | 62 | /* File attributes that are retrieved using the getstat and dread calls, and 63 | set using chstat. */ 64 | 65 | /* The following structures are only supported by iomanX. */ 66 | 67 | typedef struct 68 | { 69 | unsigned int mode; 70 | unsigned int attr; 71 | unsigned int size; 72 | unsigned char ctime[8]; 73 | unsigned char atime[8]; 74 | unsigned char mtime[8]; 75 | unsigned int hisize; 76 | /** Number of subs (main) / subpart number (sub) */ 77 | unsigned int private_0; 78 | unsigned int private_1; 79 | unsigned int private_2; 80 | unsigned int private_3; 81 | unsigned int private_4; 82 | /** Sector start. */ 83 | unsigned int private_5; 84 | } iox_stat_t __attribute__((aligned(64))); 85 | 86 | typedef struct 87 | { 88 | iox_stat_t stat; 89 | char name[256]; 90 | void *privdata; 91 | } iox_dirent_t __attribute__((aligned(64))); 92 | 93 | /* The following defines are only supported by ioman. */ 94 | 95 | // File mode flags (for mode in io_stat_t) 96 | /** Format mask */ 97 | #define FIO_SO_IFMT 0x0038 98 | /** Symbolic link */ 99 | #define FIO_SO_IFLNK 0x0008 100 | /** Regular file */ 101 | #define FIO_SO_IFREG 0x0010 102 | /** Directory */ 103 | #define FIO_SO_IFDIR 0x0020 104 | 105 | /** read */ 106 | #define FIO_SO_IROTH 0x0004 107 | /** write */ 108 | #define FIO_SO_IWOTH 0x0002 109 | /** execute */ 110 | #define FIO_SO_IXOTH 0x0001 111 | 112 | // File mode checking macros 113 | #define FIO_SO_ISLNK(m) (((m) & FIO_SO_IFMT) == FIO_SO_IFLNK) 114 | #define FIO_SO_ISREG(m) (((m) & FIO_SO_IFMT) == FIO_SO_IFREG) 115 | #define FIO_SO_ISDIR(m) (((m) & FIO_SO_IFMT) == FIO_SO_IFDIR) 116 | -------------------------------------------------------------------------------- /test/pfsshell_test.tcl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env expect 2 | 3 | # Author : Bignaux Ronan 4 | # KISS test with interactive programs 5 | 6 | log_user 0 7 | set timeout 5 8 | set count 0 9 | 10 | proc init_process {} { 11 | 12 | global spawn_id 13 | 14 | if { [file exists test.img] == 1} { 15 | file delete test.img 16 | } 17 | exec truncate -s 10G test.img 18 | 19 | spawn ../build/pfsshell 20 | match_max 100000 21 | expect -exact "pfsshell for POSIX systems\r 22 | https://github.com/ps2homebrew/pfsshell\r 23 | \r 24 | This program uses pfs, apa, iomanX, \r 25 | code from ps2sdk (https://github.com/ps2dev/ps2sdk)\r 26 | \r 27 | Type \"help\" for a list of commands.\r 28 | \r 29 | > " 30 | } 31 | 32 | proc run_cmd { name cmdparam arg expect } { 33 | 34 | 35 | send -- "$cmdparam\r" 36 | set fmt1 "\[%d]\t%-16s" 37 | global count 38 | incr count 39 | puts -nonewline [format $fmt1 $count $name] 40 | expect { 41 | $arg $expect { 42 | puts "\[\033\[01;32mpassed\033\[0m]" 43 | } 44 | timeout { 45 | puts "\[\033\[01;31mtimeout\033\[0m]" 46 | exit 1 47 | } 48 | -re "(.*)# " { 49 | puts "\[\033\[01;31mfailed\033\[0m]" 50 | # exit 1 51 | } 52 | #eof { 53 | # puts "eof" 54 | #} 55 | } 56 | 57 | } 58 | 59 | puts "Performing test :" 60 | 61 | init_process 62 | 63 | run_cmd "device" "device test.img" "-re" "(.*)# " 64 | # TODO: check more from device output 65 | run_cmd "initialize" "initialize yes" "-exact" " 66 | pfs: Format: log.number = 8224, log.count = 16\r 67 | pfs: Format sub: sub = 0, sector start = 8208, sector end = 8211\r 68 | pfs: Format: log.number = 8224, log.count = 16\r 69 | pfs: Format sub: sub = 0, sector start = 8208, sector end = 8215\r 70 | pfs: Format: log.number = 8224, log.count = 16\r 71 | pfs: Format sub: sub = 0, sector start = 8208, sector end = 8223\r 72 | pfs: Format: log.number = 8240, log.count = 16\r 73 | pfs: Format sub: sub = 0, sector start = 8208, sector end = 8239\r 74 | # " 75 | 76 | run_cmd "ls partitions" "ls" "-exact" " 77 | __mbr\r 78 | __net/\r 79 | __system/\r 80 | __sysconf/\r 81 | __common/\r 82 | # " 83 | 84 | run_cmd "mkpart" "mkpart PP.TEST 128M PFS" "-exact" "# " 85 | run_cmd "mkpart" "ls" "-exact" " 86 | __mbr\r 87 | __net/\r 88 | __system/\r 89 | __sysconf/\r 90 | __common/\r 91 | PP.TEST/\r 92 | # " 93 | 94 | run_cmd "mount" "mount __net" "-exact" "__net:/# " 95 | run_cmd "pwd" "pwd" "-exact" "\r 96 | /\r 97 | __net:/# " 98 | 99 | run_cmd "put" "put pfsshell_test.tcl" "-exact" "__net:/# " 100 | run_cmd "rename" "rename pfsshell_test.tcl pfsshell.md" "-exact" "__net:/# " 101 | run_cmd "mkdir" "mkdir directory" "-exact" "__net:/# " 102 | run_cmd "ls files" "ls" "-exact" " 103 | ./\r 104 | ../\r 105 | pfsshell.md\r 106 | directory/\r 107 | __net:/# " 108 | run_cmd "cd" "cd directory" "-exact" "__net:/directory# " 109 | run_cmd "cd" "cd .." "-exact" "__net:/# " 110 | run_cmd "get" "get pfsshell.md" "-exact" "__net:/# " 111 | run_cmd "rm" "rm pfsshell.md" "-exact" "__net:/# " 112 | run_cmd "rmdir" "rmdir directory" "-exact" "__net:/# " 113 | run_cmd "umount" "umount" "-exact" "# " 114 | run_cmd "rmpart" "rmpart __net" "-exact" "# " 115 | run_cmd "rmpart" "ls" "-exact" " 116 | __mbr\r 117 | __empty%\r 118 | __system/\r 119 | __sysconf/\r 120 | __common/\r 121 | PP.TEST/\r 122 | # " 123 | 124 | run_cmd "exit" "exit" eof "" 125 | file delete pfsshell.md 126 | 127 | #exec rm test.img 128 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | 4 | int parse_line(char *line, /* modified */ 5 | char *(*tokens)[], 6 | size_t *count) 7 | { 8 | enum { 9 | s_token_pending, /* waiting for command to begin */ 10 | s_parse_plain_token, /* parsing plain token, such as foo... */ 11 | s_parse_quoted_token, /* parsing quoted token, such as "foo... */ 12 | s_parse_aposd_token /* parsing quoted token, such as 'foo... */ 13 | } state = s_token_pending; 14 | 15 | *count = 0; 16 | char *start = line; 17 | int term = 0; 18 | while (!term) { 19 | const char ch = *line; 20 | switch (state) { 21 | case s_token_pending: 22 | switch (ch) { 23 | case ' ': 24 | case '\t': 25 | case '\r': 26 | case '\n': 27 | case '\0': 28 | break; /* skip white-space */ 29 | case '\"': 30 | state = s_parse_quoted_token; 31 | start = line + 1; 32 | break; 33 | case '\'': 34 | state = s_parse_aposd_token; 35 | start = line + 1; 36 | break; 37 | default: 38 | state = s_parse_plain_token; 39 | start = line; 40 | break; 41 | } 42 | break; 43 | 44 | case s_parse_plain_token: 45 | switch (ch) { 46 | case ' ': 47 | case '\t': 48 | case '\r': 49 | case '\n': 50 | case '\0': 51 | /* end of plain token */ 52 | *line = '\0'; 53 | (*tokens)[(*count)++] = start; 54 | state = s_token_pending; 55 | break; 56 | case '\'': 57 | case '\"': 58 | return (-1); /* quotes are not allowed in the middle of token */ 59 | } 60 | break; 61 | 62 | case s_parse_quoted_token: 63 | switch (ch) { 64 | case '\0': 65 | return (-1); /* missing closing quotes */ 66 | case '\"': 67 | /* end of quoted token */ 68 | *line = '\0'; 69 | (*tokens)[(*count)++] = start; 70 | state = s_token_pending; 71 | break; 72 | } 73 | break; 74 | 75 | case s_parse_aposd_token: 76 | switch (ch) { 77 | case '\0': 78 | return (-1); /* missing closing quotes */ 79 | case '\'': 80 | /* end of quoted token */ 81 | *line = '\0'; 82 | (*tokens)[(*count)++] = start; 83 | state = s_token_pending; 84 | break; 85 | } 86 | break; 87 | } 88 | 89 | ++line; 90 | term = (ch == '\0' || ch == '\r' || ch == '\n'); 91 | } 92 | if (state != s_token_pending) 93 | return (-1); 94 | return (0); 95 | } 96 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/thsemap.c: -------------------------------------------------------------------------------- 1 | 2 | #include "types.h" 3 | #include "thsemap.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define SEMA_COUNT 100 14 | static sem_t *sem[SEMA_COUNT] = {NULL}; 15 | 16 | #if defined(__MACH__) 17 | #define USE_NAMED_SEMAPHORES 18 | #endif 19 | 20 | int CreateSema(iop_sema_t *sema) 21 | { 22 | size_t i = 0; 23 | while (i < SEMA_COUNT && sem[i] != NULL) { 24 | ++i; 25 | } 26 | if (i < SEMA_COUNT) { 27 | #ifdef USE_NAMED_SEMAPHORES 28 | char sema_name_buf[NAME_MAX - 4]; 29 | snprintf(sema_name_buf, sizeof(sema_name_buf), "fakeps2sdk%x_%i", getpid(), i); 30 | sem[i] = sem_open((const char *)sema_name_buf, O_CREAT, O_RDWR, 1); 31 | #else 32 | sem[i] = (sem_t *)malloc(sizeof(sem_t)); 33 | #endif 34 | if (sem[i] != NULL) { 35 | #ifdef USE_NAMED_SEMAPHORES 36 | sem_unlink((const char *)sema_name_buf); 37 | return i; 38 | #else 39 | int retv = sem_init(sem[i], 0, 1); 40 | if (retv != -1) { 41 | return i; 42 | } else { 43 | printf("sem_init failed with %d\n", errno); 44 | free(sem[i]); 45 | sem[i] = NULL; 46 | return -1; 47 | } 48 | #endif 49 | } else { 50 | printf("sem_open failed with %d\n", errno); 51 | return -1; 52 | } 53 | } else { 54 | printf("too many semaphores allocated\n"); 55 | return -1; 56 | } 57 | } 58 | 59 | int DeleteSema(int semid) 60 | { 61 | if (semid >= 0 && semid < SEMA_COUNT) { 62 | if (sem[semid] != NULL) { 63 | #ifdef USE_NAMED_SEMAPHORES 64 | int retv = sem_close(sem[semid]); 65 | #else 66 | int retv = sem_destroy(sem[semid]); 67 | #endif 68 | if (retv == 0) { 69 | #ifndef USE_NAMED_SEMAPHORES 70 | free(sem[semid]); 71 | #endif 72 | sem[semid] = NULL; 73 | return 0; 74 | } else { 75 | printf("sem_close failed with %d\n", errno); 76 | return -1; 77 | } 78 | } else { 79 | printf("DeleteSema: already freed: %d\n", semid); 80 | return -1; 81 | } 82 | } else { 83 | printf("DeleteSema: invalid index: %d\n", semid); 84 | return -1; 85 | } 86 | } 87 | 88 | int WaitSema(int semid) 89 | { 90 | if (semid >= 0 && semid < SEMA_COUNT) { 91 | if (sem[semid] != NULL) { 92 | return (sem_wait(sem[semid])); 93 | } else { 94 | printf("WaitSema: already freed: %d\n", semid); 95 | return (-1); 96 | } 97 | } else { 98 | printf("WaitSema: invalid index: %d\n", semid); 99 | return (-1); 100 | } 101 | } 102 | 103 | int SignalSema(int semid) 104 | { 105 | if (semid >= 0 && semid < SEMA_COUNT) { 106 | if (sem[semid] != NULL) { 107 | return (sem_post(sem[semid])); 108 | } else { 109 | printf("SignalSema: already freed: %d\n", semid); 110 | return (-1); 111 | } 112 | } else { 113 | printf("SignalSema: invalid index: %d\n", semid); 114 | return (-1); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/pfslib_compat.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "iomanX_port.h" 3 | 4 | extern int scepfsInit(void); 5 | extern void scepfsExit(void); 6 | extern int scepfsSetReadAhead(int enabled); 7 | extern int scepfsGetReadAhead(void); 8 | 9 | 10 | static inline int scepfsOpen(const char *name, int flags, int mode) 11 | { 12 | return iomanX_open(name, flags, mode); 13 | } 14 | 15 | static inline int scepfsLseek(int fd, int offset, int whence) 16 | { 17 | return iomanX_lseek(fd, offset, whence); 18 | } 19 | 20 | static inline int64_t scepfsLseek64(int fd, int64_t offset, int whence) 21 | { 22 | return iomanX_lseek64(fd, offset, whence); 23 | } 24 | 25 | static inline int scepfsRead(int fd, void *ptr, size_t size) 26 | { 27 | return iomanX_read(fd, ptr, size); 28 | } 29 | 30 | static inline int scepfsWrite(int fd, void *ptr, size_t size) 31 | { 32 | return iomanX_write(fd, ptr, size); 33 | } 34 | 35 | static inline int scepfsClose(int fd) 36 | { 37 | return iomanX_close(fd); 38 | } 39 | 40 | static inline int scepfsDopen(const char *name) 41 | { 42 | return iomanX_dopen(name); 43 | } 44 | 45 | static inline int scepfsDread(int fd, iox_dirent_t *iox_dirent) 46 | { 47 | return iomanX_dread(fd, iox_dirent); 48 | } 49 | 50 | static inline int scepfsDclose(int fd) 51 | { 52 | return iomanX_close(fd); 53 | } 54 | 55 | static inline int scepfsChstat(const char *name, iox_stat_t *stat, int mask) 56 | { 57 | return iomanX_chstat(name, stat, mask); 58 | } 59 | 60 | static inline int scepfsGetstat(const char *name, iox_stat_t *stat) 61 | { 62 | return iomanX_getstat(name, stat); 63 | } 64 | 65 | static inline int scepfsMkdir(const char *name, int mode) 66 | { 67 | return iomanX_mkdir(name, mode); 68 | } 69 | 70 | static inline int scepfsRmdir(const char *name) 71 | { 72 | return iomanX_rmdir(name); 73 | } 74 | 75 | static inline int scepfsChdir(const char *name) 76 | { 77 | return iomanX_chdir(name); 78 | } 79 | 80 | static inline int scepfsRemove(const char *name) 81 | { 82 | return iomanX_remove(name); 83 | } 84 | 85 | static inline int scepfsRename(const char *old, const char *new_) 86 | { 87 | return iomanX_rename(old, new_); 88 | } 89 | 90 | static inline int scepfsIoctl(int fd, int cmd, void *arg) 91 | { 92 | return iomanX_ioctl(fd, cmd, arg); 93 | } 94 | 95 | static inline int scepfsIoctl2(int fd, int command, void *arg, size_t arglen, void *buf, size_t buflen) 96 | { 97 | return iomanX_ioctl2(fd, command, arg, arglen, buf, buflen); 98 | } 99 | 100 | static inline int scepfsSymlink(const char *old, const char *new_) 101 | { 102 | return iomanX_symlink(old, new_); 103 | } 104 | 105 | static inline int scepfsReadlink(const char *name, char *buf, size_t buflen) 106 | { 107 | return iomanX_readlink(name, buf, buflen); 108 | } 109 | 110 | static inline int scepfsSync(const char *dev, int flag) 111 | { 112 | return iomanX_sync(dev, flag); 113 | } 114 | 115 | static inline int scepfsFormat(const char *dev, const char *blockdev, void *arg, size_t arglen) 116 | { 117 | return iomanX_format(dev, blockdev, arg, arglen); 118 | } 119 | 120 | static inline int scepfsMount(const char *fsname, const char *devname, int flag, void *arg, size_t arglen) 121 | { 122 | return iomanX_mount(fsname, devname, flag, arg, arglen); 123 | } 124 | 125 | static inline int scepfsUmount(const char *fsname) 126 | { 127 | return iomanX_umount(fsname); 128 | } 129 | 130 | static inline int scepfsDevctl(const char *name, int cmd, void *arg, size_t arglen, void *buf, size_t buflen) 131 | { 132 | return iomanX_devctl(name, cmd, arg, arglen, buf, buflen); 133 | } 134 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/include/hdd-ioctl.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // The types here would be included in tamtypes.h, but we restructured the file layout 4 | #include 5 | 6 | // apa 7 | 8 | // Partition format/types (as returned via the mode field for getstat/dread) 9 | #define APA_TYPE_FREE 0x0000 10 | #define APA_TYPE_MBR 0x0001 // Master Boot Record 11 | #define APA_TYPE_EXT2SWAP 0x0082 12 | #define APA_TYPE_EXT2 0x0083 13 | #define APA_TYPE_REISER 0x0088 14 | #define APA_TYPE_PFS 0x0100 15 | #define APA_TYPE_CFS 0x0101 16 | #define APA_TYPE_HDL 0x1337 17 | 18 | #define APA_IDMAX 32 19 | #define APA_MAXSUB 64 // Maximum # of sub-partitions 20 | #define APA_PASSMAX 8 21 | #define APA_FLAG_SUB 0x0001 // Sub-partition status for partitions (attr field) 22 | 23 | // 24 | // IOCTL2 commands 25 | // 26 | #define HIOCADDSUB 0x6801 27 | #define HIOCDELSUB 0x6802 28 | #define HIOCNSUB 0x6803 29 | #define HIOCFLUSH 0x6804 30 | 31 | // Arbitrarily-named commands 32 | #define HIOCTRANSFER 0x6832 // Used by PFS.IRX to read/write data 33 | #define HIOCGETSIZE 0x6833 // For main(0)/subs(1+) 34 | #define HIOCSETPARTERROR 0x6834 // Set (sector of a partition) that has an error 35 | #define HIOCGETPARTERROR 0x6835 // Get (sector of a partition) that has an error 36 | 37 | // HDLFS addition 38 | #define HIOCGETPARTSTART 0x6836 // Get the sector number of the first sector of the partition. 39 | 40 | // I/O direction 41 | #define APA_IO_MODE_READ 0x00 42 | #define APA_IO_MODE_WRITE 0x01 43 | 44 | // structs for IOCTL2 commands 45 | typedef struct 46 | { 47 | u32 sub; // main(0)/subs(1+) to read/write 48 | u32 sector; 49 | u32 size; // in sectors 50 | u32 mode; // ATAD_MODE_READ/ATAD_MODE_WRITE..... 51 | void *buffer; 52 | } hddIoctl2Transfer_t; 53 | 54 | // 55 | // DEVCTL commands 56 | // 57 | // 'H' set 58 | #define HDIOC_MAXSECTOR 0x4801 // Maximum partition size (in sectors) 59 | #define HDIOC_TOTALSECTOR 0x4802 // Capacity of the disk (in sectors) 60 | #define HDIOC_IDLE 0x4803 61 | #define HDIOC_FLUSH 0x4804 62 | #define HDIOC_SWAPTMP 0x4805 63 | #define HDIOC_DEV9OFF 0x4806 64 | #define HDIOC_STATUS 0x4807 65 | #define HDIOC_FORMATVER 0x4808 66 | #define HDIOC_SMARTSTAT 0x4809 67 | #define HDIOC_FREESECTOR 0x480A // Returns the approximate amount of free space 68 | #define HDIOC_IDLEIMM 0x480B 69 | 70 | // 'h' command set 71 | // Arbitrarily-named commands 72 | #define HDIOC_GETTIME 0x6832 73 | #define HDIOC_SETOSDMBR 0x6833 // arg = hddSetOsdMBR_t 74 | #define HDIOC_GETSECTORERROR 0x6834 75 | #define HDIOC_GETERRORPARTNAME 0x6835 // bufp = namebuffer[0x20] 76 | #define HDIOC_READSECTOR 0x6836 // arg = hddAtaTransfer_t 77 | #define HDIOC_WRITESECTOR 0x6837 // arg = hddAtaTransfer_t 78 | #define HDIOC_SCEIDENTIFY 0x6838 // bufp = buffer for atadSceIdentifyDrive 79 | 80 | // structs for DEVCTL commands 81 | 82 | typedef struct 83 | { 84 | u32 lba; 85 | u32 size; 86 | u8 data[0]; 87 | } hddAtaTransfer_t; 88 | 89 | typedef struct 90 | { 91 | u32 start; 92 | u32 size; 93 | } hddSetOsdMBR_t; 94 | 95 | // pfs 96 | 97 | // IOCTL2 commands 98 | // Command set 'p' 99 | #define PIOCALLOC 0x7001 100 | #define PIOCFREE 0x7002 101 | #define PIOCATTRADD 0x7003 102 | #define PIOCATTRDEL 0x7004 103 | #define PIOCATTRLOOKUP 0x7005 104 | #define PIOCATTRREAD 0x7006 105 | #define PIOCINVINODE 0x7032 // Only available in OSD version. Arbitrarily named. 106 | 107 | // DEVCTL commands 108 | // Command set 'P' 109 | #define PDIOC_ZONESZ 0x5001 110 | #define PDIOC_ZONEFREE 0x5002 111 | #define PDIOC_CLOSEALL 0x5003 112 | #define PDIOC_GETFSCKSTAT 0x5004 113 | #define PDIOC_CLRFSCKSTAT 0x5005 114 | 115 | // Arbitrarily-named commands 116 | #define PDIOC_SETUID 0x5032 117 | #define PDIOC_SETGID 0x5033 118 | #define PDIOC_SHOWBITMAP 0xFF 119 | 120 | // I/O direction 121 | #define PFS_IO_MODE_READ 0x00 122 | #define PFS_IO_MODE_WRITE 0x01 123 | -------------------------------------------------------------------------------- /src/ps2kinst.c: -------------------------------------------------------------------------------- 1 | 2 | #include "pfsd_client.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static int install_kernel(const char *kernel); 10 | 11 | static int usage(int es) 12 | { 13 | fprintf( 14 | stderr, 15 | "usage: ps2kinst [options] kernel_file\n" 16 | "install linux kernel in system area.\n" 17 | "\n" 18 | " --help display this help and exit\n"); 19 | return es; 20 | } 21 | 22 | static int show_help; 23 | 24 | static const struct option long_options[] = { 25 | { 26 | "help", 27 | 0, 28 | &show_help, 29 | 1, 30 | }, 31 | { 32 | NULL, 33 | 0, 34 | NULL, 35 | 0, 36 | }, 37 | }; 38 | 39 | int main(int ac, char **av) 40 | { 41 | for (;;) { 42 | switch (getopt_long(ac, av, "h", long_options, NULL)) { 43 | case -1: 44 | if (optind >= ac) 45 | return usage(1); 46 | if (show_help) 47 | return usage(0); 48 | return install_kernel(av[optind]); 49 | case 0: 50 | continue; 51 | case 'h': 52 | show_help = 1; 53 | break; 54 | default: 55 | return usage(1); 56 | } 57 | } 58 | } 59 | 60 | static char buf[0x8000]; 61 | static int rfd; 62 | 63 | static int install_kernel_PFSlib(const char *kernel) 64 | { 65 | int r; 66 | int wfd; 67 | 68 | if (scepfsInit()) { 69 | fprintf(stderr, "PFSlib init failed.\n"); 70 | return -2; 71 | } 72 | r = scepfsMount("pfs0:", "hdd0:__system", 0, NULL, 0); 73 | if (r < 0) { 74 | fprintf(stderr, "Mount failed.\n"); 75 | return -3; 76 | } 77 | r = scepfsMkdir("pfs0:/p2lboot", 511); 78 | if (r < 0 && r != -17) { 79 | fprintf(stderr, "Mkdir failed.\n"); 80 | return -4; 81 | } 82 | r = scepfsChdir("pfs0:/p2lboot"); 83 | if (r < 0) { 84 | fprintf(stderr, "Chdir failed.\n"); 85 | return -5; 86 | } 87 | wfd = scepfsOpen("pfs0:vmlinux", 1538, 511); 88 | if (wfd < 0) { 89 | fprintf(stderr, "Create failed.\n"); 90 | return -6; 91 | } 92 | for (;;) { 93 | r = read(rfd, buf, sizeof(buf)); 94 | if (r <= 0) 95 | break; 96 | if (scepfsWrite(wfd, buf, r) != r) { 97 | fprintf(stderr, "Write failed.\n"); 98 | return -7; 99 | } 100 | } 101 | if (r < 0) { 102 | fprintf(stderr, "Read failed.\n"); 103 | return -8; 104 | } 105 | close(rfd); 106 | scepfsClose(wfd); 107 | scepfsUmount("pfs0:"); 108 | scepfsExit(); 109 | fprintf(stderr, "Installation Succeed.\n"); 110 | return 0; 111 | } 112 | 113 | static int install_kernel(const char *kernel) 114 | { 115 | int r; 116 | int wfd; 117 | 118 | rfd = open(kernel, O_RDONLY); 119 | if (rfd < 0) { 120 | fprintf(stderr, "can't open kernel_file.\n"); 121 | return -1; 122 | } 123 | if (scepfsdInit()) { 124 | fprintf(stderr, "use PFSlib instead\n"); 125 | return install_kernel_PFSlib(kernel); 126 | } 127 | r = scepfsdMount("pfs0:", "hdd0:__system", 0, NULL, 0); 128 | if (r < 0) { 129 | fprintf(stderr, "Mount failed.\n"); 130 | return -3; 131 | } 132 | r = scepfsdMkdir("pfs0:/p2lboot", 511); 133 | if (r < 0 && r != -17) { 134 | fprintf(stderr, "Mkdir failed.\n"); 135 | return -4; 136 | } 137 | r = scepfsdChdir("pfs0:/p2lboot"); 138 | if (r < 0) { 139 | fprintf(stderr, "Chdir failed.\n"); 140 | return -5; 141 | } 142 | wfd = scepfsdOpen("pfs0:vmlinux", 1538, 511); 143 | if (wfd < 0) { 144 | fprintf(stderr, "Create failed.\n"); 145 | return -6; 146 | } 147 | for (;;) { 148 | r = read(rfd, buf, sizeof(buf)); 149 | if (r <= 0) 150 | break; 151 | if (scepfsdWrite(wfd, buf, r) != r) { 152 | fprintf(stderr, "Write failed.\n"); 153 | return -7; 154 | } 155 | } 156 | if (r < 0) { 157 | fprintf(stderr, "Read failed.\n"); 158 | return -8; 159 | } 160 | close(rfd); 161 | scepfsdClose(wfd); 162 | scepfsdUmount("pfs0:"); 163 | scepfsdExit(); 164 | fprintf(stderr, "Installation Succeed.\n"); 165 | return 0; 166 | } 167 | -------------------------------------------------------------------------------- /subprojects/fakeps2sdk/atad.c: -------------------------------------------------------------------------------- 1 | 2 | #define _FILE_OFFSET_BITS 64 3 | 4 | #ifdef _WIN32 5 | #define WIN32_LEAN_AND_MEAN 6 | #include 7 | #include 8 | #include 9 | #endif 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #ifdef __APPLE__ 20 | #include 21 | #endif 22 | 23 | typedef uint32_t u32; 24 | typedef uint64_t u64; 25 | 26 | #ifndef O_BINARY 27 | #define O_BINARY 0 28 | #endif 29 | 30 | /* These are used with the dir parameter of ata_device_sector_io(). */ 31 | #define ATA_DIR_READ 0 32 | #define ATA_DIR_WRITE 1 33 | 34 | typedef struct _ata_devinfo 35 | { 36 | u32 exists; /* Was successfully probed. */ 37 | u32 has_packet; /* Supports the PACKET command set. */ 38 | u32 total_sectors; /* Total number of user sectors. */ 39 | u32 security_status; /* Word 0x100 of the identify info. */ 40 | } ata_devinfo_t; 41 | 42 | static int handle = -1; 43 | 44 | static u32 hdd_length = 0; /* in sectors */ 45 | 46 | int set_atad_device_handle(int fd) 47 | { 48 | #ifdef _WIN32 49 | HANDLE win_handle = (HANDLE)_get_osfhandle(fd); 50 | #endif 51 | #ifdef __APPLE__ 52 | u64 size = 0, sector_count = 0; 53 | u32 sector_size = 0; 54 | if (ioctl(fd, DKIOCGETBLOCKCOUNT, §or_count) == 0) { 55 | ioctl(fd, DKIOCGETBLOCKSIZE, §or_size); 56 | if (sector_size != 512) 57 | size = ((sector_count * sector_size) - 511) / 512; 58 | else 59 | size = sector_count; 60 | if ((int64_t)size >= 0) 61 | hdd_length = size; 62 | else 63 | return 1; 64 | } else if (errno == ENOTTY) { 65 | /* Not a device. Fall back to lseek */ 66 | #endif 67 | #ifdef _WIN32 68 | DISK_GEOMETRY geo; 69 | DWORD len = 0; 70 | u64 size = 0, size_in_bytes = 0; 71 | if (win_handle != INVALID_HANDLE_VALUE && DeviceIoControl(win_handle, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geo, sizeof(DISK_GEOMETRY), &len, NULL)) { 72 | size_in_bytes = (geo.Cylinders.QuadPart * geo.TracksPerCylinder * geo.SectorsPerTrack * geo.BytesPerSector); 73 | size = ((size_in_bytes)-511) / 512; 74 | if ((int64_t)size >= 0) { 75 | hdd_length = size; 76 | } else 77 | return 1; 78 | } else { 79 | #endif 80 | off_t size = lseek(fd, 0, SEEK_END); 81 | if (size != (off_t)-1) 82 | hdd_length = (size - 511) / 512; 83 | else 84 | return 1; 85 | #ifdef _WIN32 86 | } 87 | #endif 88 | #ifdef __APPLE__ 89 | } 90 | #endif 91 | handle = fd; 92 | return 0; 93 | } 94 | 95 | void set_atad_device_path(const char *path) 96 | { 97 | int fd; 98 | fd = open(path, O_RDWR | O_BINARY); 99 | if (fd == -1 || set_atad_device_handle(fd)) { 100 | perror(path), exit(1); 101 | } 102 | } 103 | 104 | void atad_close(void) 105 | { 106 | if (handle != -1) 107 | close(handle), handle = -1; 108 | } 109 | 110 | ata_devinfo_t *ata_get_devinfo(int device) 111 | { 112 | static ata_devinfo_t info; 113 | if (device == 0 && handle != -1) { 114 | info.exists = 1; 115 | info.has_packet = 0; 116 | info.total_sectors = hdd_length; 117 | info.security_status = 0; 118 | } else { 119 | info.exists = 0; 120 | info.has_packet = 0; 121 | info.total_sectors = 0; 122 | info.security_status = 0; 123 | } 124 | return (&info); 125 | } 126 | 127 | int ata_device_sector_io(int device, void *buf, u32 lba, u32 nsectors, int dir) 128 | { 129 | if (handle == -1) { 130 | printf("atadDmaTransfer: device fd not set\n"); 131 | return (-1); 132 | } 133 | 134 | if (device != 0) { 135 | printf("atadDmaTransfer: invalid device %d\n", device); 136 | return (-1); 137 | } 138 | 139 | off_t pos = lseek(handle, (off_t)lba * 512, SEEK_SET); 140 | if (pos == (off_t)-1) { 141 | printf("lseek: atad device fd %d: %s\n", handle, strerror(errno)); 142 | return (-1); 143 | } 144 | 145 | ssize_t len; 146 | if (dir == ATA_DIR_WRITE) 147 | len = write(handle, buf, nsectors * 512); 148 | else 149 | len = read(handle, buf, nsectors * 512); 150 | if (len == nsectors * 512) 151 | return (0); /* success */ 152 | else { 153 | printf("read/write: atad device fd %d: %s\n", handle, strerror(errno)); 154 | return (-1); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /src/pfslib_compat.c: -------------------------------------------------------------------------------- 1 | 2 | #ifndef _LARGEFILE64_SOURCE 3 | #define _LARGEFILE64_SOURCE 4 | #endif 5 | 6 | #include "pfslib_compat.h" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | static int hddFd[2]; 15 | static int g_hdd_argc = 5; 16 | static int g_pfs_argc = 7; 17 | static char *g_hdd_argv[] = { 18 | NULL, 19 | "-o", 20 | "32", 21 | "-n", 22 | "8", 23 | NULL, 24 | }; 25 | static char *g_pfs_argv[] = { 26 | NULL, 27 | "-m", 28 | "32", 29 | "-n", 30 | "72", 31 | "-o", 32 | "32", 33 | NULL, 34 | }; 35 | 36 | #define hdd_main _init_apa 37 | #define pfs_main _init_pfs 38 | 39 | /* where (image of) PS2 HDD is; in fake_sdk/atad.c */ 40 | extern int set_atad_device_handle(int fd); 41 | 42 | int scepfsInit(void) 43 | { 44 | int blkdev_count; 45 | int i; 46 | char dev_buf[16]; 47 | 48 | blkdev_count = 0; 49 | for (i = 0; i < (sizeof(hddFd) / sizeof(hddFd[0])); i += 1) { 50 | sprintf(dev_buf, "/dev/hd%c", 'a' + i); 51 | hddFd[i] = open(dev_buf, O_LARGEFILE | O_RDWR); 52 | if (hddFd[i] < 0) { 53 | hddFd[i] = -1; 54 | continue; 55 | } 56 | if (flock(hddFd[i], LOCK_EX)) { 57 | close(hddFd[i]); 58 | hddFd[i] = -1; 59 | continue; 60 | } 61 | #ifndef WIP_WIP_FIXME 62 | if (i == 0) { 63 | if (set_atad_device_handle(hddFd[i])) { 64 | flock(hddFd[i], LOCK_UN); 65 | close(hddFd[i]); 66 | continue; 67 | } 68 | } 69 | #endif 70 | blkdev_count += 1; 71 | } 72 | return !blkdev_count ? -1 : (hdd_main(g_hdd_argc, g_hdd_argv) ? -2 : (pfs_main(g_pfs_argc, g_pfs_argv) ? -3 : 0)); 73 | } 74 | 75 | void scepfsExit(void) 76 | { 77 | int i; 78 | 79 | #ifdef WIP_WIP_FIXME 80 | Flush1024Buffer(); 81 | #endif 82 | for (i = 0; i < (sizeof(hddFd) / sizeof(hddFd[0])); i += 1) { 83 | if (hddFd[i] >= 0) { 84 | flock(hddFd[i], LOCK_UN); 85 | close(hddFd[i]); 86 | } 87 | } 88 | } 89 | 90 | #ifdef WIP_WIP_FIXME 91 | static char Buf1024[0x200]; 92 | static int Buf1024dev; 93 | static int Buf1024lba; 94 | static int fDirty1024; 95 | 96 | int rawDiskAccess(int device, void *buf, unsigned int lba, int nsectors, int dir) 97 | { 98 | return ((lseek64(hddFd[device], lba << 9, SEEK_SET) < 0) || ((dir ? write(hddFd[device], buf, nsectors << 9) : read(hddFd[device], buf, nsectors << 9)) != nsectors << 9)) ? -1 : 0; 99 | } 100 | 101 | void Flush1024Buffer(void) 102 | { 103 | if (fDirty1024) { 104 | fDirty1024 = 0; 105 | rawDiskAccess(Buf1024dev, &Buf1024[0], Buf1024lba, 1, 1); 106 | } 107 | } 108 | 109 | int DiskAccess(int device, char *buf, unsigned int lba, int nsectors, int dir) 110 | { 111 | char *buf_tmp; 112 | int lba_tmp; 113 | int nsectors_tmp; 114 | 115 | buf_tmp = buf; 116 | lba_tmp = lba; 117 | nsectors_tmp = nsectors; 118 | if (fDirty1024 == 1) { 119 | if (dir && device == Buf1024dev && Buf1024lba + 1 == lba) { 120 | char buf_stk[1024]; 121 | 122 | lba_tmp = Buf1024lba + 2; 123 | nsectors_tmp = nsectors - 1; 124 | memcpy(&buf_stk[0], &Buf1024[0], sizeof(Buf1024)); 125 | memcpy(&buf_stk[512], buf, 512); 126 | rawDiskAccess(device, &buf_stk[0], Buf1024lba, 2, 1); 127 | fDirty1024 = 0; 128 | buf_tmp = &buf[512]; 129 | if (!nsectors_tmp) 130 | return 0; 131 | } else 132 | Flush1024Buffer(); 133 | } 134 | if (dir != 1 || ((lba_tmp + nsectors_tmp - 1) & 1)) 135 | return rawDiskAccess(device, buf_tmp, lba_tmp, nsectors_tmp, dir); 136 | Buf1024lba = lba_tmp + nsectors_tmp - 1; 137 | Buf1024dev = device; 138 | memcpy(&Buf1024[0], &buf_tmp[512 * nsectors_tmp], 512); 139 | nsectors_tmp -= 1; 140 | fDirty1024 = 1; 141 | return nsectors_tmp ? rawDiskAccess(device, buf_tmp, lba_tmp, nsectors_tmp, dir) : 0; 142 | } 143 | 144 | int DiskGetNumberOfSectors(int device) 145 | { 146 | int ret_sz; 147 | 148 | if (hddFd[device] < 0) 149 | return 0; 150 | ioctl(hddFd[device], BLKGETSIZE, &ret_sz); 151 | return ret_sz; 152 | } 153 | 154 | void DiskSync(int device) 155 | { 156 | Flush1024Buffer(); 157 | fsync(hddFd[device]); 158 | } 159 | #endif 160 | 161 | int scepfsSetReadAhead(int enabled) 162 | { 163 | int raget_res; 164 | int ret_ra; 165 | 166 | scepfsGetReadAhead(); 167 | raget_res = ioctl(hddFd[0], BLKRASET, enabled); 168 | if (raget_res) { 169 | printf("BLKRASET failed. errno%d\n", errno); 170 | return raget_res; 171 | } 172 | ioctl(hddFd[0], BLKRAGET, &ret_ra); 173 | printf("read ahead setting set to %d\n", ret_ra); 174 | return 0; 175 | } 176 | 177 | int scepfsGetReadAhead(void) 178 | { 179 | int ret_ra; 180 | 181 | ioctl(hddFd[0], BLKRAGET, &ret_ra); 182 | printf("current read ahead setting = %d\n", ret_ra); 183 | return ret_ra; 184 | } 185 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PFS (PlayStation File System) shell for POSIX-based systems 2 | 3 | This tool allows you to browse and transfer files to and from PFS filesystems 4 | using the command line. 5 | This tool is useful for transferring configuration and media files used by 6 | programs such as Open PS2 Loader and SMS. 7 | 8 | ## Quickstart 9 | 10 | Binaries for Win32 are available here: 11 | To start the program, provide the path to it on the command line: 12 | 13 | /path/to/pfsshell 14 | 15 | You will get a prompt similar to the following: 16 | 17 | > 18 | 19 | To get a list of commands, type in the following: 20 | 21 | > help 22 | 23 | To select a device which can be a disk image or block device, type in the following: 24 | 25 | > device /path/to/device 26 | 27 | On the Windows systems, the block devices can be used by using the UNC path. To get the UNC path, use this command: 28 | 29 | ```cmd 30 | wmic diskdrive get Caption,DeviceID,InterfaceType 31 | ``` 32 | 33 | Once a device is selected, the prompt will change to the following: 34 | 35 | # 36 | 37 | To mount a partition (for example, the `+OPL` partition), type in the following: 38 | 39 | # mount +OPL 40 | 41 | The prompt will change to the following: 42 | 43 | +OPL:/# 44 | 45 | To get a list of files or partitions, type in the following: 46 | 47 | +OPL:/# ls 48 | or 49 | +OPL:/# ls -l 50 | 51 | To transfer a file from the current directory of the PFS partition to the current directory of the process, type in the following: 52 | 53 | +OPL:/# get example.txt 54 | 55 | To transfer a file from the current directory of the process to the current directory of the PFS partition, type in the following: 56 | 57 | +OPL:/# put example.txt 58 | 59 | Once you are finished looking around in the partition, type in the following: 60 | 61 | +OPL:/# umount 62 | 63 | Once you are finished with the program, type in the following: 64 | 65 | # exit 66 | 67 | ## List of commands 68 | 69 | Commands marked with DESTRUCTIVE could potentially wipe or remove important information. Remember to make backups if using these commands. 70 | 71 | **Device Selection & Initialization** 72 | 73 | lcd [path] - print/change the local working directory 74 | 75 | device - select the PS2 HDD device 76 | 77 | initialize - blank and create a partition on a PS2 HDD device (DESTRUCTIVE) 78 | 79 | **Partition Editing** 80 | 81 | ls - list partitions (only when no partition mounted) 82 | 83 | mkpart - create a new partition (IMPORTANT size must be power of 2) 84 | 85 | mkfs - blank and create PFS on a new partition (DESTRUCTIVE) 86 | 87 | mount - mount a partition 88 | 89 | unmount - unmount the active partition 90 | 91 | rmpart - remove a partition (DESTRUCTIVE) 92 | 93 | **File & Folder Editing** 94 | 95 | pwd - print the current PS2 HDD directory 96 | 97 | ls - list folders/files (only when partition mounted) 98 | 99 | cd - change directory 100 | 101 | mkdir - create a new directory 102 | 103 | rmdir - delete an existing directory 104 | 105 | pwd - print the current PS2 HDD directory 106 | 107 | get - copy a file from PS2 HDD to the working directory 108 | 109 | put - copy a file from the working directory to the PS2 HDD 110 | 111 | rm - delete a file 112 | 113 | rename - rename a file or directory 114 | 115 | 116 | ## pfsfuse 117 | 118 | `pfsfuse` provides an ability to mount partition into host filesystem (like network folder). 119 | `pfsfuse` supports physically connected drives, raw drive images as regular files, and an NBD network server from OPL. 120 | Your system has to support fuse. For example, in the Linux `fuse` package should be installed, on the MacOS `macfuse` can be used. 121 | The Unix users can use the following command for mounting the partition: 122 | 123 | ```sh 124 | mkdir -p mountpoint 125 | pfsfuse --partition=+OPL /path/to/device mountpoint/ 126 | df -h mountpoint/ # will show information about free space 127 | ``` 128 | 129 | `mountpoint` is accessible like a normal folder. Once you are finished looking around in the partition, type in the following: 130 | 131 | umount mountpoint 132 | 133 | Note: for full access without root, use the argument `-o allow_other` when mounting. 134 | 135 | ### pfsfuse-win32 ### 136 | 137 | Windows port uses [Dokan FUSE wrapper](https://github.com/dokan-dev/dokany) implementation as a base. Before using `pfsfuse` on Windows you should install Dokany. [Installation instructions](https://github.com/dokan-dev/dokany/wiki/Installation). After successful installation, you can use `pfsfuse` by pasting command in command line (CMD) or PowerShell with elevated privileges (run as administrator): 138 | ```sh 139 | pfsfuse.exe --partition=+OPL \\.\PHYSICALDRIVE2 M -o volname=+OPL 140 | or 141 | pfsfuse.exe --partition=+OPL D:\ps2image.bin M -o volname=+OPL 142 | ``` 143 | 144 | Where `M` - is drive letter (please choose unused driver letter). `-o volname=+OPL` - will be volume name in File Explorer. 145 | For unmounting, please locate dokanctl.exe and launch the following command in the elevated command prompt: 146 | ```sh 147 | dokanctl.exe /u M 148 | ``` 149 | Where `M` - is mounted point drive letter. 150 | 151 | ## NBD server support 152 | 153 | The latest Open PS2 Loader revisions have a built-in NBD server. `pfsshell/pfsfuse` have full support for an NBD block device, once an NBD server is mounted in the host filesystem. 154 | 155 | ## Building 156 | 157 | This project can be built by using the Meson build system. For more information 158 | about the system, please visit the following location: 159 | 160 | ## Original project 161 | 162 | The original project was created by Wizard of Oz. More information about the original project can be found at the following location: 163 | 164 | 165 | ## License 166 | 167 | This project as a whole is licensed under the GNU General Public License GPL 168 | version 2. Please read the `COPYING` file for more information. 169 | The APA, PFS, and iomanX libraries are licensed under The Academic Free 170 | License version 2. Please read the `COPYING.AFLv2` file for more information. 171 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project('pfsshell', 'c', default_options: ['optimization=0', 'debug=true', 'warning_level=0'], version: 'v1.1.1') 2 | 3 | add_global_arguments(['-Wno-unused-value', '-Wno-format', '-Wno-format-security', '-Wno-unused-function', '-Wno-unused-variable'], language : 'c') 4 | 5 | cc = meson.get_compiler('c') 6 | host_system = host_machine.system() 7 | 8 | libfakeps2sdk_proj = subproject('fakeps2sdk') 9 | libfakeps2sdk_noinclude_dep = libfakeps2sdk_proj.get_variable('libfakeps2sdk_noinclude_dep') 10 | 11 | libapa_proj = subproject('apa') 12 | libapa_dep = libapa_proj.get_variable('libapa_dep') 13 | 14 | libpfs_proj = subproject('pfs') 15 | libpfs_dep = libpfs_proj.get_variable('libpfs_dep') 16 | 17 | libiomanX_proj = subproject('iomanX') 18 | libiomanX_dep = libiomanX_proj.get_variable('libiomanX_dep') 19 | 20 | libhdlfs_proj = subproject('hdlfs') 21 | libhdlfs_dep = libhdlfs_proj.get_variable('libhdlfs_dep') 22 | 23 | pfsshell_src = [ 24 | 'src/startup.c', 25 | 'src/hl.c', 26 | 'src/host_adapter.c', 27 | 'src/util.c', 28 | 'src/shell.c', 29 | ] 30 | pfsshell_cflags = [] 31 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 32 | pfsshell_cflags += ['-DNEED_GETLINE'] 33 | endif 34 | if host_system == 'linux' 35 | pfsshell_cflags += ['-pthread'] 36 | endif 37 | pfsshell_ldflags = [] 38 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 39 | pfsshell_ldflags += ['-static'] 40 | endif 41 | if host_system == 'linux' 42 | pfsshell_ldflags += ['-lpthread'] 43 | endif 44 | pfsshell_dependson = [ 45 | libfakeps2sdk_noinclude_dep, 46 | libapa_dep, 47 | libpfs_dep, 48 | libiomanX_dep, 49 | libhdlfs_dep, 50 | ] 51 | pfsshell = executable('pfsshell', sources: pfsshell_src, c_args: pfsshell_cflags, link_args: pfsshell_ldflags, dependencies: pfsshell_dependson, install: true) 52 | libps2hdd = shared_library('ps2hdd', sources: pfsshell_src, c_args: pfsshell_cflags, link_args: pfsshell_ldflags, dependencies: pfsshell_dependson, install: true) 53 | 54 | if get_option('enable_pfsfuse') == true 55 | 56 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 57 | #Build libdokanfuse using CMake 58 | run_command('./dokany.sh') 59 | libfuse_include = include_directories(['external/dokany/dokan_fuse/include']) 60 | libfuse_dep = cc.find_library('libdokanfuse2.dll', 61 | dirs : [meson.current_source_dir() +'/external/dokany/dokan_fuse/build']) 62 | else 63 | libfuse_include = [''] 64 | libfuse_dep = dependency('fuse') 65 | endif 66 | 67 | pfsfuse_src = [ 68 | 'src/iomanx_adapter.c', 69 | ] 70 | pfsfuse_cflags = [] 71 | if host_system == 'linux' 72 | pfsfuse_cflags += ['-pthread'] 73 | endif 74 | pfsfuse_cflags += ['-D_FILE_OFFSET_BITS=64'] 75 | pfsfuse_ldflags = [] 76 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 77 | pfsfuse_ldflags += ['-static'] 78 | endif 79 | if host_system == 'linux' 80 | pfsfuse_ldflags += ['-lpthread'] 81 | endif 82 | pfsfuse_dependson = [ 83 | libfakeps2sdk_noinclude_dep, 84 | libapa_dep, 85 | libpfs_dep, 86 | libiomanX_dep, 87 | libfuse_dep, 88 | ] 89 | pfsfuse = executable('pfsfuse', sources: pfsfuse_src, include_directories : libfuse_include, c_args: pfsfuse_cflags, link_args: pfsfuse_ldflags, dependencies: pfsfuse_dependson, install: true) 90 | endif 91 | 92 | if get_option('enable_pfs2tar') == true 93 | pfs2tar_src = [ 94 | 'src/pfs2tar.c', 95 | ] 96 | pfs2tar_cflags = [] 97 | if host_system == 'linux' 98 | pfs2tar_cflags += ['-pthread', '-D_FILE_OFFSET_BITS=64'] 99 | elif host_system == 'darwin' 100 | # macOS-specific adjustments 101 | # Threads are part of libc, no need for -pthread 102 | # No need for -D_FILE_OFFSET_BITS=64 103 | endif 104 | 105 | pfs2tar_ldflags = [] 106 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 107 | pfs2tar_ldflags += ['-static'] 108 | elif host_system == 'linux' 109 | pfs2tar_ldflags += ['-lpthread'] 110 | endif 111 | 112 | pfs2tar_dependson = [ 113 | libfakeps2sdk_noinclude_dep, 114 | libapa_dep, 115 | libpfs_dep, 116 | libiomanX_dep, 117 | ] 118 | 119 | pfs2tar = executable( 120 | 'pfs2tar', 121 | sources: pfs2tar_src, 122 | c_args: pfs2tar_cflags, 123 | link_args: pfs2tar_ldflags, 124 | dependencies: pfs2tar_dependson, 125 | install: true 126 | ) 127 | endif 128 | 129 | if get_option('enable_pfsd') == true 130 | pfsd_src = [ 131 | 'src/pfsd.c', 132 | 'src/pfslib_compat.c', 133 | ] 134 | pfsd_cflags = [] 135 | if host_system == 'linux' 136 | pfsd_cflags += ['-pthread'] 137 | endif 138 | pfsd_cflags += ['-D_FILE_OFFSET_BITS=64'] 139 | pfsd_ldflags = [] 140 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 141 | pfsd_ldflags += ['-static'] 142 | endif 143 | if host_system == 'linux' 144 | pfsd_ldflags += ['-lpthread'] 145 | endif 146 | pfsd_dependson = [ 147 | libfakeps2sdk_noinclude_dep, 148 | libapa_dep, 149 | libpfs_dep, 150 | libiomanX_dep, 151 | ] 152 | pfsd = executable('pfsd', sources: pfsd_src, c_args: pfsd_cflags, link_args: pfsd_ldflags, dependencies: pfsd_dependson, install: true) 153 | endif 154 | 155 | if get_option('enable_ps2kinst') == true 156 | ps2kinst_src = [ 157 | 'src/pfsd_client.c', 158 | 'src/pfslib_compat.c', 159 | 'src/ps2kinst.c', 160 | ] 161 | ps2kinst_cflags = [] 162 | if host_system == 'linux' 163 | ps2kinst_cflags += ['-pthread'] 164 | endif 165 | ps2kinst_cflags += ['-D_FILE_OFFSET_BITS=64'] 166 | ps2kinst_ldflags = [] 167 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 168 | ps2kinst_ldflags += ['-static'] 169 | endif 170 | if host_system == 'linux' 171 | ps2kinst_ldflags += ['-lpthread'] 172 | endif 173 | ps2kinst_dependson = [ 174 | libfakeps2sdk_noinclude_dep, 175 | libapa_dep, 176 | libpfs_dep, 177 | libiomanX_dep, 178 | ] 179 | ps2kinst = executable('ps2kinst', sources: ps2kinst_src, c_args: ps2kinst_cflags, link_args: ps2kinst_ldflags, dependencies: ps2kinst_dependson, install: true) 180 | endif 181 | 182 | if host_system == 'windows' and cc.get_id() != 'msvc' and cc.get_id() != 'clang-cl' 183 | archive = custom_target( 184 | 'archive', 185 | output: meson.project_name() + '-' + meson.project_version() + '-win32.7z', 186 | input: [pfsshell, libps2hdd, pfs2tar, 'README.md', 'COPYING', 'COPYING.AFLv2'], 187 | build_by_default: true, 188 | command: [find_program('7z'), 'a', '@OUTPUT@', '@INPUT@'] 189 | ) 190 | endif 191 | -------------------------------------------------------------------------------- /src/pfsd_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #define PFSD_PATH "/tmp/pfsd" 6 | #define PFSD_SHARED_KEY 0x7C724E1D 7 | 8 | typedef struct pfsd_rpc_open_struct_ 9 | { 10 | char m_name[1024]; 11 | int m_flags; 12 | int m_mode; 13 | } pfsd_rpc_open_struct_t; 14 | 15 | typedef struct pfsd_rpc_lseek_struct_ 16 | { 17 | int m_fd; 18 | int m_offset; 19 | int m_whence; 20 | } pfsd_rpc_lseek_struct_t; 21 | 22 | typedef struct pfsd_rpc_lseek64_struct_ 23 | { 24 | int m_fd; 25 | int m_pad1; 26 | int64_t m_offset; 27 | int m_whence; 28 | int m_pad2; 29 | } pfsd_rpc_lseek64_struct_t; 30 | 31 | typedef struct pfsd_rpc_read_struct_ 32 | { 33 | int m_fd; 34 | int m_ptr_useshmptr; 35 | int m_size; 36 | } pfsd_rpc_read_struct_t; 37 | 38 | typedef struct pfsd_rpc_write_struct_ 39 | { 40 | int m_fd; 41 | int m_ptr_useshmptr; 42 | int m_size; 43 | } pfsd_rpc_write_struct_t; 44 | 45 | typedef struct pfsd_rpc_close_struct_ 46 | { 47 | int m_fd; 48 | } pfsd_rpc_close_struct_t; 49 | 50 | typedef struct pfsd_rpc_dopen_struct_ 51 | { 52 | char m_name[1024]; 53 | } pfsd_rpc_dopen_struct_t; 54 | 55 | typedef struct pfsd_rpc_dread_struct_ 56 | { 57 | int m_fd; 58 | int m_iox_dirent_useshmptr; 59 | } pfsd_rpc_dread_struct_t; 60 | 61 | typedef struct pfsd_rpc_dclose_struct_ 62 | { 63 | int m_fd; 64 | } pfsd_rpc_dclose_struct_t; 65 | 66 | typedef struct pfsd_rpc_chstat_struct_ 67 | { 68 | char m_name[1024]; 69 | int m_stat_useshmptr; 70 | int m_mask; 71 | } pfsd_rpc_chstat_struct_t; 72 | 73 | typedef struct pfsd_rpc_getstat_struct_ 74 | { 75 | char m_name[1024]; 76 | int m_stat_useshmptr; 77 | } pfsd_rpc_getstat_struct_t; 78 | 79 | typedef struct pfsd_rpc_mkdir_struct_ 80 | { 81 | char m_name[1024]; 82 | int m_mode; 83 | } pfsd_rpc_mkdir_struct_t; 84 | 85 | typedef struct pfsd_rpc_rmdir_struct_ 86 | { 87 | char m_name[1024]; 88 | } pfsd_rpc_rmdir_struct_t; 89 | 90 | typedef struct pfsd_rpc_chdir_struct_ 91 | { 92 | char m_name[1024]; 93 | } pfsd_rpc_chdir_struct_t; 94 | 95 | typedef struct pfsd_rpc_remove_struct_ 96 | { 97 | char m_name[1024]; 98 | } pfsd_rpc_remove_struct_t; 99 | 100 | typedef struct pfsd_rpc_rename_struct_ 101 | { 102 | char m_old[1024]; 103 | char m_new[1024]; 104 | } pfsd_rpc_rename_struct_t; 105 | 106 | typedef struct pfsd_rpc_ioctl_struct_ 107 | { 108 | int m_fd; 109 | int m_cmd; 110 | int m_arg_useshmptr; 111 | } pfsd_rpc_ioctl_struct_t; 112 | 113 | typedef struct pfsd_rpc_ioctl2_struct_ 114 | { 115 | int m_fd; 116 | int m_cmd; 117 | int m_arg_useshmptr; 118 | int m_arglen; 119 | int m_buf_useshmptr; 120 | int m_buflen; 121 | } pfsd_rpc_ioctl2_struct_t; 122 | 123 | typedef struct pfsd_rpc_symlink_struct_ 124 | { 125 | char m_old[1024]; 126 | char m_new[1024]; 127 | } pfsd_rpc_symlink_struct_t; 128 | 129 | typedef struct pfsd_rpc_readlink_struct_ 130 | { 131 | char m_name[1024]; 132 | int m_buf_useshmptr; 133 | int m_buflen; 134 | } pfsd_rpc_readlink_struct_t; 135 | 136 | typedef struct pfsd_rpc_sync_struct_ 137 | { 138 | char m_dev[1024]; 139 | int m_flag; 140 | } pfsd_rpc_sync_struct_t; 141 | 142 | typedef struct pfsd_rpc_format_struct_ 143 | { 144 | char m_dev[1024]; 145 | char m_blockdev[1024]; 146 | int m_arg_useshmptr; 147 | int m_arglen; 148 | } pfsd_rpc_format_struct_t; 149 | 150 | typedef struct pfsd_rpc_mount_struct_ 151 | { 152 | char m_fsname[1024]; 153 | char m_devname[1024]; 154 | int m_flag; 155 | int m_arg_useshmptr; 156 | int m_arglen; 157 | } pfsd_rpc_mount_struct_t; 158 | 159 | typedef struct pfsd_rpc_umount_struct_ 160 | { 161 | char m_fsname[1024]; 162 | } pfsd_rpc_umount_struct_t; 163 | 164 | typedef struct pfsd_rpc_devctl_struct_ 165 | { 166 | char m_name[1024]; 167 | int m_cmd; 168 | int m_arg_useshmptr; 169 | int m_arglen; 170 | int m_buf_useshmptr; 171 | int m_buflen; 172 | } pfsd_rpc_devctl_struct_t; 173 | 174 | typedef struct pfsd_rpc_setreadahead_struct_ 175 | { 176 | int m_enabled; 177 | } pfsd_rpc_setreadahead_struct_t; 178 | 179 | typedef struct pfsd_rpc_getmountpoint_struct_ 180 | { 181 | char m_path[1024]; 182 | } pfsd_rpc_getmountpoint_struct_t; 183 | 184 | typedef struct pfsd_rpc_ioctl2_hioctransfer_struct_ 185 | { 186 | int m_fd; 187 | unsigned int m_sub; 188 | unsigned int m_sector; 189 | unsigned int m_size; 190 | unsigned int m_mode; 191 | int m_buffer_useshmptr; 192 | } pfsd_rpc_ioctl2_hioctransfer_struct_t; 193 | 194 | typedef struct hddIoctl2Transfer_ 195 | { 196 | unsigned int sub; 197 | unsigned int sector; 198 | unsigned int size; 199 | unsigned int mode; 200 | void *buffer; 201 | } hddIoctl2Transfer_t; 202 | 203 | typedef struct pfsd_rpc_common_struct_ 204 | { 205 | int m_unused; 206 | int m_command; 207 | union pfsd_rpc_common_struct_opu_ 208 | { 209 | pfsd_rpc_open_struct_t m_opi_open; 210 | pfsd_rpc_lseek_struct_t m_opi_lseek; 211 | pfsd_rpc_lseek64_struct_t m_opi_lseek64; 212 | pfsd_rpc_read_struct_t m_opi_read; 213 | pfsd_rpc_write_struct_t m_opi_write; 214 | pfsd_rpc_close_struct_t m_opi_close; 215 | pfsd_rpc_dopen_struct_t m_opi_dopen; 216 | pfsd_rpc_dread_struct_t m_opi_dread; 217 | pfsd_rpc_dclose_struct_t m_opi_dclose; 218 | pfsd_rpc_chstat_struct_t m_opi_chstat; 219 | pfsd_rpc_getstat_struct_t m_opi_getstat; 220 | pfsd_rpc_mkdir_struct_t m_opi_mkdir; 221 | pfsd_rpc_rmdir_struct_t m_opi_rmdir; 222 | pfsd_rpc_chdir_struct_t m_opi_chdir; 223 | pfsd_rpc_remove_struct_t m_opi_remove; 224 | pfsd_rpc_rename_struct_t m_opi_rename; 225 | pfsd_rpc_ioctl_struct_t m_opi_ioctl; 226 | pfsd_rpc_ioctl2_struct_t m_opi_ioctl2; 227 | pfsd_rpc_symlink_struct_t m_opi_symlink; 228 | pfsd_rpc_readlink_struct_t m_opi_readlink; 229 | pfsd_rpc_sync_struct_t m_opi_sync; 230 | pfsd_rpc_format_struct_t m_opi_format; 231 | pfsd_rpc_mount_struct_t m_opi_mount; 232 | pfsd_rpc_umount_struct_t m_opi_umount; 233 | pfsd_rpc_devctl_struct_t m_opi_devctl; 234 | pfsd_rpc_setreadahead_struct_t m_opi_setreadahead; 235 | pfsd_rpc_getmountpoint_struct_t m_opi_getmountpoint; 236 | pfsd_rpc_ioctl2_hioctransfer_struct_t m_opi_ioctl2_hioctransfer; 237 | char m_payload[0]; 238 | } m_opu; 239 | } pfsd_rpc_common_struct_t; 240 | 241 | enum pfsd_req_enum_ { 242 | PFSD_REQ_OPEN = 0, 243 | PFSD_REQ_LSEEK = 1, 244 | PFSD_REQ_LSEEK64 = 2, 245 | PFSD_REQ_READ = 3, 246 | PFSD_REQ_WRITE = 4, 247 | PFSD_REQ_CLOSE = 5, 248 | PFSD_REQ_DOPEN = 6, 249 | PFSD_REQ_DREAD = 7, 250 | PFSD_REQ_DCLOSE = 8, 251 | PFSD_REQ_CHSTAT = 9, 252 | PFSD_REQ_GETSTAT = 10, 253 | PFSD_REQ_MKDIR = 11, 254 | PFSD_REQ_RMDIR = 12, 255 | PFSD_REQ_CHDIR = 13, 256 | PFSD_REQ_REMOVE = 14, 257 | PFSD_REQ_RENAME = 15, 258 | PFSD_REQ_IOCTL = 16, 259 | PFSD_REQ_IOCTL2 = 17, 260 | PFSD_REQ_SYMLINK = 18, 261 | PFSD_REQ_READLINK = 19, 262 | PFSD_REQ_SYNC = 20, 263 | PFSD_REQ_FORMAT = 21, 264 | PFSD_REQ_MOUNT = 22, 265 | PFSD_REQ_UMOUNT = 23, 266 | PFSD_REQ_DEVCTL = 24, 267 | PFSD_REQ_SETREADAHEAD = 25, 268 | PFSD_REQ_GETREADAHEAD = 26, 269 | PFSD_REQ_GETMOUNTPOINT = 27, 270 | PFSD_REQ_IOCTL2_HIOCTRANSFER = 28, 271 | }; 272 | -------------------------------------------------------------------------------- /COPYING.AFLv2: -------------------------------------------------------------------------------- 1 | 2 | The Academic Free License 3 | v. 2.0 4 | 5 | This Academic Free License (the "License") applies to any original work 6 | of authorship (the "Original Work") whose owner (the "Licensor") has 7 | placed the following notice immediately following the copyright notice 8 | for the Original Work: 9 | 10 | *Licensed under the Academic Free License version 2.0* 11 | 12 | 1) *Grant of Copyright License.* Licensor hereby grants You a 13 | world-wide, royalty-free, non-exclusive, perpetual, sublicenseable 14 | license to do the following: 15 | 16 | a) to reproduce the Original Work in copies; 17 | 18 | b) to prepare derivative works ("Derivative Works") based upon the 19 | Original Work; 20 | 21 | c) to distribute copies of the Original Work and Derivative Works to 22 | the public; 23 | 24 | d) to perform the Original Work publicly; and 25 | 26 | e) to display the Original Work publicly. 27 | 28 | 2) *Grant of Patent License.* Licensor hereby grants You a world-wide, 29 | royalty-free, non-exclusive, perpetual, sublicenseable license, under 30 | patent claims owned or controlled by the Licensor that are embodied in 31 | the Original Work as furnished by the Licensor, to make, use, sell and 32 | offer for sale the Original Work and Derivative Works. 33 | 34 | 3) *Grant of Source Code License.* The term "Source Code" means the 35 | preferred form of the Original Work for making modifications to it and 36 | all available documentation describing how to modify the Original Work. 37 | Licensor hereby agrees to provide a machine-readable copy of the Source 38 | Code of the Original Work along with each copy of the Original Work that 39 | Licensor distributes. Licensor reserves the right to satisfy this 40 | obligation by placing a machine-readable copy of the Source Code in an 41 | information repository reasonably calculated to permit inexpensive and 42 | convenient access by You for as long as Licensor continues to distribute 43 | the Original Work, and by publishing the address of that information 44 | repository in a notice immediately following the copyright notice that 45 | applies to the Original Work. 46 | 47 | 4) *Exclusions From License Grant. *Neither the names of Licensor, nor 48 | the names of any contributors to the Original Work, nor any of their 49 | trademarks or service marks, may be used to endorse or promote products 50 | derived from this Original Work without express prior written permission 51 | of the Licensor. Nothing in this License shall be deemed to grant any 52 | rights to trademarks, copyrights, patents, trade secrets or any other 53 | intellectual property of Licensor except as expressly stated herein. No 54 | patent license is granted to make, use, sell or offer to sell 55 | embodiments of any patent claims other than the licensed claims defined 56 | in Section 2. No right is granted to the trademarks of Licensor even if 57 | such marks are included in the Original Work. Nothing in this License 58 | shall be interpreted to prohibit Licensor from licensing under different 59 | terms from this License any Original Work that Licensor otherwise would 60 | have a right to license. 61 | 62 | 5) This section intentionally omitted. 63 | 64 | 6) *Attribution Rights.* You must retain, in the Source Code of any 65 | Derivative Works that You create, all copyright, patent or trademark 66 | notices from the Source Code of the Original Work, as well as any 67 | notices of licensing and any descriptive text identified therein as an 68 | "Attribution Notice." You must cause the Source Code for any Derivative 69 | Works that You create to carry a prominent Attribution Notice reasonably 70 | calculated to inform recipients that You have modified the Original Work. 71 | 72 | 7) *Warranty of Provenance and Disclaimer of Warranty.* Licensor 73 | warrants that the copyright in and to the Original Work and the patent 74 | rights granted herein by Licensor are owned by the Licensor or are 75 | sublicensed to You under the terms of this License with the permission 76 | of the contributor(s) of those copyrights and patent rights. Except as 77 | expressly stated in the immediately proceeding sentence, the Original 78 | Work is provided under this License on an "AS IS" BASIS and WITHOUT 79 | WARRANTY, either express or implied, including, without limitation, the 80 | warranties of NON-INFRINGEMENT, MERCHANTABILITY or FITNESS FOR A 81 | PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY OF THE ORIGINAL 82 | WORK IS WITH YOU. This DISCLAIMER OF WARRANTY constitutes an essential 83 | part of this License. No license to Original Work is granted hereunder 84 | except under this disclaimer. 85 | 86 | 8) *Limitation of Liability.* Under no circumstances and under no legal 87 | theory, whether in tort (including negligence), contract, or otherwise, 88 | shall the Licensor be liable to any person for any direct, indirect, 89 | special, incidental, or consequential damages of any character arising 90 | as a result of this License or the use of the Original Work including, 91 | without limitation, damages for loss of goodwill, work stoppage, 92 | computer failure or malfunction, or any and all other commercial damages 93 | or losses. This limitation of liability shall not apply to liability for 94 | death or personal injury resulting from Licensor's negligence to the 95 | extent applicable law prohibits such limitation. Some jurisdictions do 96 | not allow the exclusion or limitation of incidental or consequential 97 | damages, so this exclusion and limitation may not apply to You. 98 | 99 | 9) *Acceptance and Termination.* If You distribute copies of the 100 | Original Work or a Derivative Work, You must make a reasonable effort 101 | under the circumstances to obtain the express assent of recipients to 102 | the terms of this License. Nothing else but this License (or another 103 | written agreement between Licensor and You) grants You permission to 104 | create Derivative Works based upon the Original Work or to exercise any 105 | of the rights granted in Section 1 herein, and any attempt to do so 106 | except under the terms of this License (or another written agreement 107 | between Licensor and You) is expressly prohibited by U.S. copyright law, 108 | the equivalent laws of other countries, and by international treaty. 109 | Therefore, by exercising any of the rights granted to You in Section 1 110 | herein, You indicate Your acceptance of this License and all of its 111 | terms and conditions. 112 | 113 | 10) *Termination for Patent Action.* This License shall terminate 114 | automatically and You may no longer exercise any of the rights granted 115 | to You by this License as of the date You commence an action, including 116 | a cross-claim or counterclaim, for patent infringement (i) against 117 | Licensor with respect to a patent applicable to software or (ii) against 118 | any entity with respect to a patent applicable to the Original Work (but 119 | excluding combinations of the Original Work with other software or 120 | hardware). 121 | 122 | 11) *Jurisdiction, Venue and Governing Law.* Any action or suit relating 123 | to this License may be brought only in the courts of a jurisdiction 124 | wherein the Licensor resides or in which Licensor conducts its primary 125 | business, and under the laws of that jurisdiction excluding its 126 | conflict-of-law provisions. The application of the United Nations 127 | Convention on Contracts for the International Sale of Goods is expressly 128 | excluded. Any use of the Original Work outside the scope of this License 129 | or after its termination shall be subject to the requirements and 130 | penalties of the U.S. Copyright Act, 17 U.S.C. � 101 et seq., the 131 | equivalent laws of other countries, and international treaty. This 132 | section shall survive the termination of this License. 133 | 134 | 12) *Attorneys Fees.* In any action to enforce the terms of this License 135 | or seeking damages relating thereto, the prevailing party shall be 136 | entitled to recover its costs and expenses, including, without 137 | limitation, reasonable attorneys' fees and costs incurred in connection 138 | with such action, including any appeal of such action. This section 139 | shall survive the termination of this License. 140 | 141 | 13) *Miscellaneous.* This License represents the complete agreement 142 | concerning the subject matter hereof. If any provision of this License 143 | is held to be unenforceable, such provision shall be reformed only to 144 | the extent necessary to make it enforceable. 145 | 146 | 14) *Definition of "You" in This License.* "You" throughout this 147 | License, whether in upper or lower case, means an individual or a legal 148 | entity exercising rights under, and complying with all of the terms of, 149 | this License. For legal entities, "You" includes any entity that 150 | controls, is controlled by, or is under common control with you. For 151 | purposes of this definition, "control" means (i) the power, direct or 152 | indirect, to cause the direction or management of such entity, whether 153 | by contract or otherwise, or (ii) ownership of fifty percent (50%) or 154 | more of the outstanding shares, or (iii) beneficial ownership of such 155 | entity. 156 | 157 | 15) *Right to Use.* You may use the Original Work in all ways not 158 | otherwise restricted or conditioned by this License or by law, and 159 | Licensor promises not to interfere with or be responsible for such uses 160 | by You. 161 | 162 | This license is Copyright (C) 2003 Lawrence E. Rosen. All rights 163 | reserved. Permission is hereby granted to copy and distribute this 164 | license without modification. This license may not be modified without 165 | the express written permission of its copyright owner. 166 | 167 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Continuous Integration", 3 | "on": { 4 | "push": { 5 | "paths": [ 6 | "**.c", 7 | "**.h", 8 | "**.cpp", 9 | "**.hpp", 10 | "**.build", 11 | "**.in", 12 | "**.yml" 13 | ] 14 | }, 15 | "pull_request": { 16 | "paths": [ 17 | "**.c", 18 | "**.h", 19 | "**.cpp", 20 | "**.hpp", 21 | "**.build", 22 | "**.in", 23 | "**.yml" 24 | ] 25 | } 26 | }, 27 | "env": { 28 | "CMAKE_GENERATOR": "Ninja", 29 | "HOMEBREW_NO_ANALYTICS": "1", 30 | "HOMEBREW_NO_AUTO_UPDATE": "1", 31 | "HOMEBREW_NO_INSTALL_UPGRADE": "1", 32 | "HOMEBREW_NO_INSTALL_CLEANUP": "1", 33 | "DEBIAN_FRONTEND": "noninteractive", 34 | "TZ": "Japan" 35 | }, 36 | "jobs": { 37 | "build-ubuntu": { 38 | "runs-on": "ubuntu-latest", 39 | "timeout-minutes": 20, 40 | "container": { 41 | "image": "ubuntu:20.04", 42 | "options": "--user 0" 43 | }, 44 | "steps": [ 45 | { 46 | "name": "Install build dependancies", 47 | "run": "apt-get -y update\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install build-essential expect pkg-config fuse libfuse-dev git time p7zip-full\n" 48 | }, 49 | { 50 | "uses": "actions/checkout@v3", 51 | "with": { 52 | "submodules": "recursive" 53 | } 54 | }, 55 | { 56 | "uses": "actions/setup-python@v3", 57 | "with": { 58 | "python-version": "3.13.3" 59 | } 60 | }, 61 | { 62 | "uses": "actions/cache@v3", 63 | "with": { 64 | "path": "~/.cache/pip", 65 | "key": "${{ runner.os }}-pip", 66 | "restore-keys": "${{ runner.os }}-pip\n" 67 | } 68 | }, 69 | { 70 | "run": "python -m pip install --upgrade meson ninja" 71 | }, 72 | { 73 | "name": "Build pfsshell", 74 | "run": "meson setup build/ -Denable_pfs2tar=true\nmeson compile -C build\n" 75 | }, 76 | { 77 | "name": "Run Test", 78 | "run": "cd test\ntime ./pfsshell_test.tcl\n" 79 | }, 80 | { 81 | "uses": "actions/upload-artifact@v4", 82 | "with": { 83 | "name": "pfsshell-ubuntu", 84 | "path": "build/pfsshell\nbuild/pfs2tar\n" 85 | } 86 | }, 87 | { 88 | "name": "Build pfsfuse", 89 | "run": "rm -rf build/\nmeson setup build/ -Denable_pfsfuse=true\nmeson compile -C build\n" 90 | }, 91 | { 92 | "uses": "actions/upload-artifact@v4", 93 | "with": { 94 | "name": "pfsfuse-ubuntu", 95 | "path": "build/pfsfuse" 96 | } 97 | } 98 | ] 99 | }, 100 | "build-win32": { 101 | "runs-on": "ubuntu-latest", 102 | "timeout-minutes": 20, 103 | "container": { 104 | "image": "ubuntu:20.04", 105 | "options": "--user 0" 106 | }, 107 | "steps": [ 108 | { 109 | "run": "apt-get -y update\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install build-essential mingw-w64 git p7zip-full\nupdate-alternatives --set i686-w64-mingw32-gcc /usr/bin/i686-w64-mingw32-gcc-posix\nupdate-alternatives --set i686-w64-mingw32-g++ /usr/bin/i686-w64-mingw32-g++-posix\n" 110 | }, 111 | { 112 | "uses": "actions/checkout@v3", 113 | "with": { 114 | "submodules": "recursive" 115 | } 116 | }, 117 | { 118 | "uses": "actions/setup-python@v3", 119 | "with": { 120 | "python-version": "3.13.3" 121 | } 122 | }, 123 | { 124 | "uses": "actions/cache@v3", 125 | "with": { 126 | "path": "~/.cache/pip", 127 | "key": "${{ runner.os }}-pip", 128 | "restore-keys": "${{ runner.os }}-pip\n" 129 | } 130 | }, 131 | { 132 | "run": "python -m pip install --upgrade meson ninja" 133 | }, 134 | { 135 | "run": "meson setup build/ -Denable_pfs2tar=true --cross-file ./external/meson_toolchains/mingw32_meson.ini\nmeson compile -C build archive\n" 136 | }, 137 | { 138 | "uses": "actions/upload-artifact@v4", 139 | "with": { 140 | "name": "pfsshell-win32", 141 | "path": "build/*.7z" 142 | } 143 | } 144 | ] 145 | }, 146 | "build-pfsfuse-win32": { 147 | "runs-on": "windows-latest", 148 | "timeout-minutes": 20, 149 | "defaults": { 150 | "run": { 151 | "shell": "msys2 {0}" 152 | } 153 | }, 154 | "steps": [ 155 | { 156 | "name": "Install MSYS2 packages", 157 | "uses": "msys2/setup-msys2@v2", 158 | "with": { 159 | "msystem": "MINGW32", 160 | "install": "base-devel git make texinfo patch binutils mpc-devel p7zip mingw-w64-i686-gcc\nmingw-w64-i686-cmake mingw-w64-i686-make mingw-w64-i686-meson\n", 161 | "update": true, 162 | "shell": "msys2 {0}" 163 | } 164 | }, 165 | { 166 | "uses": "actions/checkout@v3", 167 | "with": { 168 | "submodules": "recursive" 169 | } 170 | }, 171 | { 172 | "name": "Enable git symlinks", 173 | "run": "git config core.symlinks true\ngit reset --hard\n" 174 | }, 175 | { 176 | "name": "Build pfsfuse", 177 | "run": "meson setup build/ -Denable_pfsfuse=true\nmeson compile -C build\ncp -f external/dokany/license.lgpl.txt build/\n" 178 | }, 179 | { 180 | "uses": "actions/upload-artifact@v4", 181 | "with": { 182 | "name": "pfsfuse-win32", 183 | "path": "build/pfsfuse.exe\nbuild/libdokanfuse*.dll\nbuild/license.lgpl.txt\n" 184 | } 185 | } 186 | ] 187 | }, 188 | "build-macos": { 189 | "runs-on": "macos-latest", 190 | "timeout-minutes": 20, 191 | "steps": [ 192 | { 193 | "uses": "actions/checkout@v3", 194 | "with": { 195 | "submodules": "recursive" 196 | } 197 | }, 198 | { 199 | "uses": "actions/setup-python@v3", 200 | "with": { 201 | "python-version": "3.x" 202 | } 203 | }, 204 | { 205 | "uses": "actions/cache@v3", 206 | "with": { 207 | "path": "~/.cache/pip", 208 | "key": "${{ runner.os }}-pip", 209 | "restore-keys": "${{ runner.os }}-pip\n" 210 | } 211 | }, 212 | { 213 | "run": "python -m pip install --upgrade meson ninja" 214 | }, 215 | { 216 | "run": "brew install macfuse coreutils" 217 | }, 218 | { 219 | "run": "meson setup build/ -Denable_pfs2tar=true\nmeson compile -C build\n" 220 | }, 221 | { 222 | "name": "Run Test", 223 | "run": "cd test\ntime ./pfsshell_test.tcl\n" 224 | }, 225 | { 226 | "uses": "actions/upload-artifact@v4", 227 | "with": { 228 | "name": "pfsshell-macos", 229 | "path": "build/pfsshell\nbuild/pfs2tar\n" 230 | } 231 | }, 232 | { 233 | "run": "rm -rf build/\nmeson setup build/ -Denable_pfsfuse=true\nmeson compile -C build\n" 234 | }, 235 | { 236 | "uses": "actions/upload-artifact@v4", 237 | "with": { 238 | "name": "pfsfuse-macos", 239 | "path": "build/pfsfuse" 240 | } 241 | } 242 | ] 243 | }, 244 | "create-release": { 245 | "needs": [ 246 | "build-ubuntu", 247 | "build-win32", 248 | "build-macos", 249 | "build-pfsfuse-win32" 250 | ], 251 | "runs-on": "ubuntu-latest", 252 | "timeout-minutes": 20, 253 | "container": { 254 | "image": "ubuntu:20.04", 255 | "options": "--user 0" 256 | }, 257 | "if": "startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/master'", 258 | "steps": [ 259 | { 260 | "name": "Download all artifact", 261 | "uses": "actions/download-artifact@v4" 262 | }, 263 | { 264 | "name": "Install build dependancies", 265 | "run": "apt-get -y update\napt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install p7zip-full\n" 266 | }, 267 | { 268 | "name": "Prepare artifacts for release", 269 | "run": "7z a -tzip pfsshell-ubuntu.zip pfsshell-ubuntu/*\n7z a -tzip pfsshell-macos.zip pfsshell-macos/*\n7z a -tzip pfsfuse-ubuntu.zip pfsfuse-ubuntu/*\n7z a -tzip pfsfuse-macos.zip pfsfuse-macos/*\n7z a -tzip pfsfuse-win32.zip pfsfuse-win32/*\n" 270 | }, 271 | { 272 | "name": "Create prerelease", 273 | "if": "github.ref == 'refs/heads/master'", 274 | "uses": "marvinpinto/action-automatic-releases@latest", 275 | "with": { 276 | "repo_token": "${{ secrets.GITHUB_TOKEN }}", 277 | "prerelease": true, 278 | "automatic_release_tag": "latest", 279 | "title": "Latest development builds", 280 | "files": "pfsshell-win32/*\npfsfuse-win32.zip\npfsshell-ubuntu.zip\npfsshell-macos.zip\npfsfuse-ubuntu.zip\npfsfuse-macos.zip\n" 281 | } 282 | }, 283 | { 284 | "name": "Create release", 285 | "if": "startsWith(github.ref, 'refs/tags/v')", 286 | "uses": "marvinpinto/action-automatic-releases@latest", 287 | "with": { 288 | "repo_token": "${{ secrets.GITHUB_TOKEN }}", 289 | "prerelease": "${{ contains(github.ref, '-rc') }}", 290 | "files": "pfsshell-win32/*\npfsfuse-win32.zip\npfsshell-ubuntu.zip\npfsshell-macos.zip\npfsfuse-ubuntu.zip\npfsfuse-macos.zip\n" 291 | } 292 | } 293 | ] 294 | } 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/iomanX_port.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | /* 6 | * most constants were prefixed with `IOMANX_' because 7 | * of name clashes against OS' APIs; 8 | * 9 | * that header is absolutely sufficient if you wish to do iomanX I/O; 10 | * 11 | * including ps2sdk headers could possibly lead to hard to track 12 | * problems and data loss, because of different values 13 | * for common constants, such as O_RDONLY, etc. 14 | * (imagine a call, such as open ("foo", O_RDONLY) which OS reads as 15 | * open ("foo", O_WRONLY | O_CREAT) ) 16 | */ 17 | 18 | /* those three would return 0 on success */ 19 | extern int _init_apa(int argc, char *argv[]); 20 | extern int _init_pfs(int argc, char *argv[]); 21 | extern int _init_hdlfs(int argc, char *argv[]); 22 | 23 | #define FIO_O_RDONLY 0x0001 24 | #define FIO_O_WRONLY 0x0002 25 | #define FIO_O_RDWR 0x0003 26 | #define FIO_O_DIROPEN 0x0008 // Internal use for dopen 27 | #define FIO_O_NBLOCK 0x0010 28 | #define FIO_O_APPEND 0x0100 29 | #define FIO_O_CREAT 0x0200 30 | #define FIO_O_TRUNC 0x0400 31 | #define FIO_O_EXCL 0x0800 32 | #define FIO_O_NOWAIT 0x8000 33 | 34 | // Access flags for filesystem mount 35 | #define FIO_MT_RDWR 0x00 36 | #define FIO_MT_RDONLY 0x01 37 | 38 | #define FIO_SEEK_SET 0 39 | #define FIO_SEEK_CUR 1 40 | #define FIO_SEEK_END 2 41 | 42 | /* Device drivers. */ 43 | 44 | /* Device types. */ 45 | #define IOP_DT_CHAR 0x01 46 | #define IOP_DT_CONS 0x02 47 | #define IOP_DT_BLOCK 0x04 48 | #define IOP_DT_RAW 0x08 49 | #define IOP_DT_FS 0x10 50 | #ifndef IOMAN_NO_EXTENDED 51 | #define IOP_DT_FSEXT 0x10000000 /* Supports calls after chstat(). */ 52 | #endif 53 | 54 | // Flags for chstat 'statmask' 55 | #define FIO_CST_MODE 0x0001 56 | #define FIO_CST_ATTR 0x0002 57 | #define FIO_CST_SIZE 0x0004 58 | #define FIO_CST_CT 0x0008 59 | #define FIO_CST_AT 0x0010 60 | #define FIO_CST_MT 0x0020 61 | #define FIO_CST_PRVT 0x0040 62 | 63 | // File mode flags 64 | #define FIO_S_IFMT 0xF000 // Format mask 65 | #define FIO_S_IFLNK 0x4000 // Symbolic link 66 | #define FIO_S_IFREG 0x2000 // Regular file 67 | #define FIO_S_IFDIR 0x1000 // Directory 68 | 69 | // Access rights 70 | #define FIO_S_ISUID 0x0800 // SUID 71 | #define FIO_S_ISGID 0x0400 // SGID 72 | #define FIO_S_ISVTX 0x0200 // Sticky bit 73 | 74 | #define FIO_S_IRWXU 0x01C0 // User access rights mask 75 | #define FIO_S_IRUSR 0x0100 // read 76 | #define FIO_S_IWUSR 0x0080 // write 77 | #define FIO_S_IXUSR 0x0040 // execute 78 | 79 | #define FIO_S_IRWXG 0x0038 // Group access rights mask 80 | #define FIO_S_IRGRP 0x0020 // read 81 | #define FIO_S_IWGRP 0x0010 // write 82 | #define FIO_S_IXGRP 0x0008 // execute 83 | 84 | #define FIO_S_IRWXO 0x0007 // Others access rights mask 85 | #define FIO_S_IROTH 0x0004 // read 86 | #define FIO_S_IWOTH 0x0002 // write 87 | #define FIO_S_IXOTH 0x0001 // execute 88 | 89 | // File mode checking macros 90 | #define FIO_S_ISLNK(m) (((m) & FIO_S_IFMT) == FIO_S_IFLNK) 91 | #define FIO_S_ISREG(m) (((m) & FIO_S_IFMT) == FIO_S_IFREG) 92 | #define FIO_S_ISDIR(m) (((m) & FIO_S_IFMT) == FIO_S_IFDIR) 93 | 94 | /* File attributes that are retrieved using the getstat and dread calls, and 95 | set using chstat. */ 96 | 97 | // 98 | // HDD IOCTL2 commands 99 | // 100 | #define HIOCADDSUB 0x6801 101 | #define HIOCDELSUB 0x6802 102 | #define HIOCNSUB 0x6803 103 | #define HIOCFLUSH 0x6804 104 | 105 | // DEVCTL commands 106 | #define HDIOC_TOTALSECTOR 0x4802 107 | #define HDIOC_FREESECTOR 0x480A 108 | 109 | // Arbitrarily-named commands 110 | #define HIOCTRANSFER 0x6832 // Used by PFS.IRX to read/write data 111 | #define HIOCGETSIZE 0x6833 // For main(0)/subs(1+) 112 | #define HIOCSETPARTERROR 0x6834 // Set (sector of a partition) that has an error 113 | #define HIOCGETPARTERROR 0x6835 // Get (sector of a partition) that has an error 114 | 115 | // pfs 116 | 117 | // IOCTL2 commands 118 | // Command set 'p' 119 | #define PIOCALLOC 0x7001 120 | #define PIOCFREE 0x7002 121 | #define PIOCATTRADD 0x7003 122 | #define PIOCATTRDEL 0x7004 123 | #define PIOCATTRLOOKUP 0x7005 124 | #define PIOCATTRREAD 0x7006 125 | #define PIOCINVINODE 0x7032 // Only available in OSD version. Arbitrarily named. 126 | 127 | // DEVCTL commands 128 | // Command set 'P' 129 | #define PDIOC_ZONESZ 0x5001 130 | #define PDIOC_ZONEFREE 0x5002 131 | #define PDIOC_CLOSEALL 0x5003 132 | #define PDIOC_GETFSCKSTAT 0x5004 133 | #define PDIOC_CLRFSCKSTAT 0x5005 134 | 135 | // Arbitrarily-named commands 136 | #define PDIOC_SETUID 0x5032 137 | #define PDIOC_SETGID 0x5033 138 | #define PDIOC_SHOWBITMAP 0xFF 139 | 140 | // I/O direction 141 | #define PFS_IO_MODE_READ 0x00 142 | #define PFS_IO_MODE_WRITE 0x01 143 | 144 | /* ps2_hdd.h: Date/time descriptor used in on-disk partition header */ 145 | typedef struct ps2fs_datetime_type 146 | { 147 | uint8_t unused; 148 | uint8_t sec; 149 | uint8_t min; 150 | uint8_t hour; 151 | uint8_t day; 152 | uint8_t month; 153 | uint16_t year; 154 | } ps2fs_datetime_t; 155 | 156 | /* The following structures are only supported by iomanX. */ 157 | #ifndef iomanX_struct 158 | #define iomanX_struct 159 | typedef struct 160 | { 161 | /*00*/ unsigned int mode; 162 | /*04*/ unsigned int attr; 163 | /*08*/ unsigned int size; 164 | /*0c*/ unsigned char ctime[8]; 165 | /*14*/ unsigned char atime[8]; 166 | /*1c*/ unsigned char mtime[8]; 167 | /*24*/ unsigned int hisize; 168 | /*28*/ unsigned int private_0; // Number of subs (main) / subpart number (sub) 169 | /*2c*/ unsigned int private_1; // partition size low part 170 | // cppcheck-suppress unusedStructMember 171 | /*30*/ unsigned int private_2; // partition size high part 172 | /*34*/ unsigned int private_3; 173 | /*38*/ unsigned int private_4; 174 | // cppcheck-suppress unusedStructMember 175 | /*3c*/ unsigned int private_5; // Sector start 176 | } iox_stat_t; 177 | 178 | typedef struct 179 | { 180 | iox_stat_t stat; 181 | char name[256]; 182 | unsigned int unknown; 183 | } iox_dirent_t; 184 | #endif 185 | 186 | /* File objects passed to driver operations. */ 187 | typedef struct _iop_file 188 | { 189 | int32_t mode; /* File open mode. */ 190 | int32_t unit; /* HW device unit number. */ 191 | struct _iop_device *device; /* Device driver. */ 192 | void *privdata; /* The device driver can use this however it 193 | wants. */ 194 | } iop_file_t; 195 | 196 | typedef struct _iop_device 197 | { 198 | const char *name; 199 | uint32_t type; 200 | uint32_t version; /* Not so sure about this one. */ 201 | const char *desc; 202 | struct _iop_device_ops *ops; 203 | } iop_device_t; 204 | 205 | #ifdef lseek 206 | #undef lseek 207 | #endif 208 | 209 | typedef struct _iop_device_ops 210 | { 211 | int (*init)(iop_device_t *); 212 | int (*deinit)(iop_device_t *); 213 | int (*format)(iop_file_t *, const char *, const char *, void *, int); 214 | int (*open)(iop_file_t *, const char *, int, int); 215 | int (*close)(iop_file_t *); 216 | int (*read)(iop_file_t *, void *, int); 217 | int (*write)(iop_file_t *, void *, int); 218 | int (*lseek)(iop_file_t *, int, int); 219 | int (*ioctl)(iop_file_t *, int, void *); 220 | int (*remove)(iop_file_t *, const char *); 221 | int (*mkdir)(iop_file_t *, const char *, int); 222 | int (*rmdir)(iop_file_t *, const char *); 223 | int (*dopen)(iop_file_t *, const char *); 224 | int (*dclose)(iop_file_t *); 225 | int (*dread)(iop_file_t *, iox_dirent_t *); 226 | int (*getstat)(iop_file_t *, const char *, iox_stat_t *); 227 | int (*chstat)(iop_file_t *, const char *, iox_stat_t *, unsigned int); 228 | 229 | #ifndef IOMAN_NO_EXTENDED 230 | /* Extended ops start here. */ 231 | int (*rename)(iop_file_t *, const char *, const char *); 232 | int (*chdir)(iop_file_t *, const char *); 233 | int (*sync)(iop_file_t *, const char *, int); 234 | int (*mount)(iop_file_t *, const char *, const char *, int, void *, int); 235 | int (*umount)(iop_file_t *, const char *); 236 | int64_t (*lseek64)(iop_file_t *, int64_t, int); 237 | int (*devctl)(iop_file_t *, const char *, int, void *, unsigned int, void *, unsigned int); 238 | int (*symlink)(iop_file_t *, const char *, const char *); 239 | int (*readlink)(iop_file_t *, const char *, char *, unsigned int); 240 | int (*ioctl2)(iop_file_t *, int, void *, unsigned int, void *, unsigned int); 241 | #endif /* IOMAN_NO_EXTENDED */ 242 | } iop_device_ops_t; 243 | 244 | /* open() takes an optional mode argument. */ 245 | int iomanX_open(const char *name, int flags, ...); 246 | int iomanX_close(int fd); 247 | int iomanX_read(int fd, void *ptr, int size); 248 | int iomanX_write(int fd, void *ptr, int size); 249 | int iomanX_lseek(int fd, int offset, int mode); 250 | 251 | int iomanX_ioctl(int fd, int cmd, void *param); 252 | int iomanX_remove(const char *name); 253 | 254 | int iomanX_mkdir(const char *path, int mode); 255 | int iomanX_rmdir(const char *path); 256 | int iomanX_dopen(const char *path); 257 | int iomanX_dclose(int fd); 258 | int iomanX_dread(int fd, iox_dirent_t *buf); 259 | 260 | int iomanX_getstat(const char *name, iox_stat_t *stat); 261 | int iomanX_chstat(const char *name, iox_stat_t *stat, unsigned int statmask); 262 | 263 | /** This can take take more than one form. */ 264 | int iomanX_format(const char *dev, const char *blockdev, void *arg, int arglen); 265 | 266 | /* The newer calls - these are NOT supported by the older IOMAN. */ 267 | int iomanX_rename(const char *old, const char *new); 268 | int iomanX_chdir(const char *name); 269 | int iomanX_sync(const char *dev, int flag); 270 | int iomanX_mount(const char *fsname, const char *devname, int flag, void *arg, int arglen); 271 | int iomanX_umount(const char *fsname); 272 | int64_t iomanX_lseek64(int fd, int64_t offset, int whence); 273 | int iomanX_devctl(const char *name, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen); 274 | int iomanX_symlink(const char *old, const char *new); 275 | int iomanX_readlink(const char *path, char *buf, unsigned int buflen); 276 | int iomanX_ioctl2(int fd, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen); 277 | 278 | int iomanX_AddDrv(iop_device_t *device); 279 | int iomanX_DelDrv(const char *name); 280 | 281 | // const char* strerror (int err); 282 | -------------------------------------------------------------------------------- /src/hl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #ifndef O_BINARY 7 | #define O_BINARY 0 8 | #endif 9 | 10 | #include "iomanX_port.h" 11 | 12 | 13 | /* copy file, host to PFS */ 14 | int copyto(const char *mount_point, const char *dest, const char *src) 15 | { 16 | int retval = 0; 17 | int in_file = open(src, O_RDONLY | O_BINARY); 18 | if (in_file != -1) { 19 | int result = iomanX_mount("pfs0:", mount_point, 0, NULL, 0); 20 | if (result >= 0) { /* mount successful */ 21 | char dest_path[256]; 22 | strcpy(dest_path, "pfs0:"); 23 | strcat(dest_path, dest); 24 | 25 | int fh = iomanX_open(dest_path, 26 | FIO_O_WRONLY | FIO_O_CREAT, 0666); 27 | if (fh >= 0) { 28 | char buf[4096 * 16]; 29 | int len; 30 | while ((len = read(in_file, buf, sizeof(buf))) > 0) { 31 | result = iomanX_write(fh, buf, len); 32 | if (result < 0) { 33 | printf("%s: write failed with %d\n", dest_path, result); 34 | retval = -1; 35 | break; 36 | } 37 | } 38 | if (len < 0) 39 | perror(src); 40 | result = iomanX_close(fh); 41 | if (result < 0) 42 | printf("close: failed with %d\n", result), retval = -1; 43 | } else 44 | printf("%s: create failed with %d\n", dest_path, fh), retval = -1; 45 | 46 | result = iomanX_umount("pfs0:"); 47 | if (result < 0) 48 | printf("pfs0: umount failed with %d\n", result), retval = -1; 49 | } else 50 | printf("pfs0: mount of \"%s\" failed with %d\n", 51 | mount_point, result), 52 | retval = -1; 53 | close(in_file); 54 | } else 55 | perror(src), retval = -1; 56 | return (retval); 57 | } 58 | 59 | 60 | /* copy file, PFS to host */ 61 | int copyfrom(const char *mount_point, const char *src, const char *dest) 62 | { 63 | int retval = 0; 64 | int out_file = open(dest, O_CREAT | O_WRONLY | O_BINARY, 0664); 65 | if (out_file != -1) { 66 | int result = iomanX_mount("pfs0:", mount_point, 0, NULL, 0); 67 | if (result >= 0) { /* mount successful */ 68 | char src_path[256]; 69 | strcpy(src_path, "pfs0:"); 70 | strcat(src_path, src); 71 | 72 | int fh = iomanX_open(src_path, FIO_O_RDONLY); 73 | if (fh >= 0) { 74 | char buf[4096 * 16]; 75 | int len; 76 | while ((len = iomanX_read(fh, buf, sizeof(buf))) > 0) { 77 | result = write(out_file, buf, len); 78 | if (result == -1) { 79 | perror(dest); 80 | retval = -1; 81 | break; 82 | } 83 | } 84 | if (len < 0) 85 | printf("%s: read failed with %d\n", 86 | src_path, len), 87 | retval = -1; 88 | 89 | result = iomanX_close(fh); 90 | if (result < 0) 91 | printf("close: failed with %d\n", result), retval = -1; 92 | } else 93 | printf("%s: open failed with %d\n", src_path, fh), retval = -1; 94 | 95 | result = iomanX_umount("pfs0:"); 96 | if (result < 0) 97 | printf("pfs0: umount failed with %d\n", result), retval = -1; 98 | } else 99 | printf("pfs0: mount of \"%s\" failed with %d\n", 100 | mount_point, result), 101 | retval = -1; 102 | if (close(out_file) == -1) 103 | perror(dest), retval = -1; 104 | } else 105 | perror(dest), retval = -1; 106 | return (retval); 107 | } 108 | 109 | 110 | int list_dir_objects(int dh, int lsmode) 111 | { 112 | int result; 113 | iox_dirent_t dirent; 114 | char end_symbol[2]; 115 | end_symbol[1] = '\0'; 116 | while ((result = iomanX_dread(dh, &dirent)) && result != -1) { 117 | char mode[10 + 1] = {'\0'}; /* unix-style */ 118 | const int m = dirent.stat.mode; 119 | switch (m & FIO_S_IFMT) { /* item type */ 120 | case FIO_S_IFLNK: 121 | mode[0] = 'l'; 122 | end_symbol[0] = '@'; 123 | break; 124 | case FIO_S_IFREG: 125 | mode[0] = '-'; 126 | end_symbol[0] = '\0'; 127 | break; 128 | case FIO_S_IFDIR: 129 | mode[0] = 'd'; 130 | end_symbol[0] = '/'; 131 | break; 132 | default: 133 | mode[0] = '?'; 134 | break; 135 | } 136 | mode[1] = m & FIO_S_IRUSR ? 'r' : '-'; 137 | mode[2] = m & FIO_S_IWUSR ? 'w' : '-'; 138 | mode[3] = m & FIO_S_IXUSR ? 'x' : '-'; 139 | mode[4] = m & FIO_S_IRGRP ? 'r' : '-'; 140 | mode[5] = m & FIO_S_IWGRP ? 'w' : '-'; 141 | mode[6] = m & FIO_S_IXGRP ? 'x' : '-'; 142 | mode[7] = m & FIO_S_IROTH ? 'r' : '-'; 143 | mode[8] = m & FIO_S_IWOTH ? 'w' : '-'; 144 | mode[9] = m & FIO_S_IXOTH ? 'x' : '-'; 145 | mode[10] = '\0'; /* not really necessary */ 146 | 147 | const ps2fs_datetime_t *mtime = 148 | (ps2fs_datetime_t *)dirent.stat.mtime; 149 | char mod_time[16 + 1]; /* yyyy-mm-dd hh:mi */ 150 | sprintf(mod_time, "%04d-%02d-%02d %02d:%02d", 151 | mtime->year, mtime->month, mtime->day, 152 | mtime->hour, mtime->min); 153 | if (lsmode == 0) 154 | printf("%s%s\n", 155 | dirent.name, end_symbol); 156 | else if (lsmode == 1) 157 | printf("%s %10u %s %s%s\n", 158 | mode, dirent.stat.size, mod_time, dirent.name, end_symbol); 159 | } 160 | return (result); 161 | } 162 | 163 | 164 | /* list HDD partitions */ 165 | int lspart(int lsmode) 166 | { 167 | const char *dir_path = "hdd0:"; 168 | char end_symbol[2]; 169 | end_symbol[1] = '\0'; 170 | int retval = 0; 171 | int dh = iomanX_dopen(dir_path); 172 | if (dh >= 0) { /* dopen successful */ 173 | #if 0 174 | printf("Partitions of %s, dh = %d\n", dir_path, dh); 175 | #endif 176 | int result; 177 | iox_dirent_t dirent; 178 | if (lsmode == 1) 179 | printf("Start (sector) Code Slice Size Timestamp Name\n"); 180 | while ((result = iomanX_dread(dh, &dirent)) && result != -1) { 181 | 182 | // Equal to, but avoids overflows of: size * 512 / 1024 / 1024; 183 | uint32_t size = dirent.stat.size / 2048; 184 | uint64_t totalsize = (dirent.stat.private_1 + dirent.stat.private_2 * 0x100000000ULL) / 2048; 185 | double sizeInGiB = totalsize / 1024.0; 186 | 187 | if (dirent.stat.mode == 0x0000) /* empty partition */ 188 | end_symbol[0] = '%'; 189 | else if (dirent.stat.attr == 1) /* sub-partition */ 190 | end_symbol[0] = '@'; 191 | else if (dirent.stat.mode == 0x0100) 192 | end_symbol[0] = '/'; 193 | else if (dirent.stat.mode == 0x1337) 194 | end_symbol[0] = '*'; 195 | else 196 | end_symbol[0] = '\0'; 197 | 198 | const ps2fs_datetime_t *mtime = 199 | (ps2fs_datetime_t *)dirent.stat.mtime; 200 | char mod_time[16 + 1]; /* yyyy-mm-dd hh:mi */ 201 | sprintf(mod_time, "%04d-%02d-%02d %02d:%02d", 202 | mtime->year, mtime->month, mtime->day, 203 | mtime->hour, mtime->min); 204 | if (lsmode == 0) { 205 | if (dirent.stat.attr == 0 && dirent.stat.mode != 0x0000) 206 | printf("%7.2f GiB %s%s\n", sizeInGiB, dirent.name, end_symbol); 207 | } else if (lsmode == 1) { 208 | printf("%#10x ", dirent.stat.private_5); 209 | if (dirent.stat.private_5 % 0x200000 == 0) 210 | printf("%4u GiB", dirent.stat.private_5 / 0x200000); 211 | else 212 | printf(" "); 213 | 214 | printf(" %04X", dirent.stat.mode); 215 | if (size < 1024) 216 | printf("%5u MiB", size); 217 | else 218 | printf("%5u GiB", size / 1024); 219 | 220 | if (dirent.stat.attr == 0 && dirent.stat.mode != 0x0000) { 221 | if (sizeInGiB == (int)sizeInGiB) 222 | printf(" %4u GiB", (unsigned int)sizeInGiB); 223 | else 224 | printf(" %7.2f GiB", sizeInGiB); 225 | } else { 226 | printf(" "); 227 | } 228 | printf(" %s %s%s\n", mod_time, dirent.name, end_symbol); 229 | } 230 | } 231 | 232 | result = iomanX_close(dh); 233 | if (result < 0) 234 | printf("dclose: failed with %d\n", result), retval = -1; 235 | } else 236 | printf("dopen: \"%s\" failed with %d\n", 237 | dir_path, dh), 238 | retval = dh; 239 | return (retval); 240 | } 241 | 242 | 243 | /* list files at PFS */ 244 | int ls(const char *mount_point, const char *path) 245 | { 246 | int retval = 0; 247 | int result = iomanX_mount("pfs0:", mount_point, 0, NULL, 0); 248 | if (result >= 0) { /* mount successful */ 249 | char dir_path[256]; 250 | strcpy(dir_path, "pfs0:"); 251 | strcat(dir_path, path); 252 | int dh = iomanX_dopen(dir_path); 253 | if (dh >= 0) { /* dopen successful */ 254 | #if 0 255 | printf ("Directory of %s%s\n", mount_point, path); 256 | #endif 257 | list_dir_objects(dh, 1); 258 | 259 | result = iomanX_close(dh); 260 | if (result < 0) 261 | printf("dclose: failed with %d\n", result), retval = -1; 262 | } else 263 | printf("dopen: \"%s\" failed with %d\n", 264 | dir_path, dh), 265 | retval = -1; 266 | 267 | result = iomanX_umount("pfs0:"); 268 | if (result < 0) 269 | printf("pfs0: umount failed with %d\n", result), retval = -1; 270 | } else 271 | printf("pfs0: mount of \"%s\" failed with %d\n", 272 | mount_point, result), 273 | retval = -1; 274 | return (retval); 275 | } 276 | 277 | 278 | /* create PFS onto an existing partition */ 279 | int mkpfs(const char *mount_point) 280 | { 281 | #define PFS_ZONE_SIZE 8192 282 | #define PFS_FRAGMENT 0x00000000 283 | int format_arg[] = {PFS_ZONE_SIZE, 0x2d66, PFS_FRAGMENT}; 284 | 285 | char tmp[256]; 286 | strcpy(tmp, "hdd0:"); 287 | strcat(tmp, mount_point); 288 | return (iomanX_format("pfs:", tmp, 289 | (void *)&format_arg, sizeof(format_arg))); 290 | } 291 | 292 | 293 | /* create partition of any type and format it as PFS if type=0x0100 */ 294 | int mkpart(const char *mount_point, long size_in_mb, int format) 295 | { 296 | char tmp[256]; 297 | if (size_in_mb >= 1024) 298 | sprintf(tmp, "%s,%ldG", mount_point, size_in_mb / 1024); 299 | else 300 | sprintf(tmp, "%s,%ldM", mount_point, size_in_mb); 301 | int result = iomanX_open(tmp, FIO_O_RDWR | FIO_O_CREAT, 0); 302 | if (result >= 0) { 303 | iomanX_close(result), result = 0; 304 | 305 | if (format) 306 | result = mkpfs(mount_point); 307 | } 308 | return (result); 309 | } 310 | 311 | 312 | /* initialize PS2 HDD with APA partitioning and create common partitions 313 | * (__mbr, __common, __net, etc.); __mbr partition is not PFS-formatted */ 314 | int initialize(void) 315 | { 316 | int result = iomanX_format("hdd0:", NULL, NULL, 0); 317 | if (result >= 0) { 318 | result = mkpfs("__net"); 319 | mkpfs("__system"); 320 | mkpfs("__sysconf"); 321 | mkpfs("__common"); 322 | } 323 | return (result); 324 | } 325 | -------------------------------------------------------------------------------- /src/host_adapter.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "iomanX_port.h" 10 | 11 | static int host_translator_op_init(iop_device_t *f) 12 | { 13 | (void)f; 14 | 15 | return -48; 16 | } 17 | 18 | static int host_translator_op_exit(iop_device_t *f) 19 | { 20 | (void)f; 21 | 22 | return -48; 23 | } 24 | 25 | static int host_translator_op_format(iop_file_t *f, const char *dev, const char *blockdev, void *arg, int arglen) 26 | { 27 | (void)f; 28 | (void)dev; 29 | (void)blockdev; 30 | (void)arg; 31 | (void)arglen; 32 | 33 | return -48; 34 | } 35 | 36 | static int host_translator_op_open(iop_file_t *f, const char *name, int flags, int mode) 37 | { 38 | int translated_flags = 0; 39 | if ((flags & FIO_O_RDWR) != 0) { 40 | flags |= O_RDWR; 41 | } else if ((flags & FIO_O_RDONLY) != 0) { 42 | flags |= O_RDONLY; 43 | } else if ((flags & FIO_O_WRONLY) != 0) { 44 | flags |= O_WRONLY; 45 | } 46 | #ifndef _WIN32 47 | if ((flags & FIO_O_NBLOCK) != 0) { 48 | flags |= O_NONBLOCK; 49 | } 50 | #endif 51 | if ((flags & FIO_O_APPEND) != 0) { 52 | flags |= O_APPEND; 53 | } 54 | flags |= O_CREAT; 55 | if ((flags & FIO_O_TRUNC) != 0) { 56 | flags |= O_TRUNC; 57 | } 58 | if ((flags & FIO_O_EXCL) != 0) { 59 | flags |= O_EXCL; 60 | } 61 | 62 | int fh = open(name, flags, mode); 63 | if (fh < 0) { 64 | // TODO: translate errno 65 | return -errno; 66 | } 67 | f->privdata = (void *)(uintptr_t)fh; 68 | return 0; 69 | } 70 | 71 | static int host_translator_op_close(iop_file_t *f) 72 | { 73 | int res = close((int)(uintptr_t)f->privdata); 74 | if (res < 0) { 75 | // TODO: translate errno 76 | return -errno; 77 | } 78 | 79 | return 0; 80 | } 81 | 82 | static int host_translator_op_read(iop_file_t *f, void *ptr, int size) 83 | { 84 | int res = read((int)(uintptr_t)f->privdata, ptr, size); 85 | if (res < 0) { 86 | // TODO: translate errno 87 | return -errno; 88 | } 89 | 90 | return res; 91 | } 92 | 93 | static int host_translator_op_write(iop_file_t *f, void *ptr, int size) 94 | { 95 | int res = write((int)(uintptr_t)f->privdata, (const void *)ptr, size); 96 | if (res < 0) { 97 | // TODO: translate errno 98 | return -errno; 99 | } 100 | 101 | return res; 102 | } 103 | 104 | static int host_translator_op_lseek(iop_file_t *f, int offset, int mode) 105 | { 106 | // TODO: translate mode 107 | int res = lseek((int)(uintptr_t)f->privdata, offset, mode); 108 | if (res < 0) { 109 | // TODO: translate errno 110 | return -errno; 111 | } 112 | 113 | return res; 114 | } 115 | 116 | static int host_translator_op_ioctl(iop_file_t *f, int cmd, void *param) 117 | { 118 | (void)f; 119 | (void)cmd; 120 | (void)param; 121 | 122 | // Intentionally not handled. 123 | 124 | return -48; 125 | } 126 | 127 | static int host_translator_op_remove(iop_file_t *f, const char *name) 128 | { 129 | (void)f; 130 | 131 | // TODO: should we also handle directories? 132 | int res = unlink(name); 133 | if (res < 0) { 134 | // TODO: translate errno 135 | return -errno; 136 | } 137 | 138 | return 0; 139 | } 140 | 141 | static int host_translator_op_mkdir(iop_file_t *f, const char *path, int mode) 142 | { 143 | (void)f; 144 | 145 | #ifndef _WIN32 146 | int res = mkdir(path, mode); 147 | if (res < 0) { 148 | // TODO: translate errno 149 | return -errno; 150 | } 151 | #endif 152 | 153 | return 0; 154 | } 155 | 156 | static int host_translator_op_rmdir(iop_file_t *f, const char *path) 157 | { 158 | (void)f; 159 | 160 | int res = rmdir(path); 161 | if (res < 0) { 162 | // TODO: translate errno 163 | return -errno; 164 | } 165 | 166 | return 0; 167 | } 168 | 169 | static int host_translator_op_dopen(iop_file_t *f, const char *path) 170 | { 171 | DIR *res = opendir(path); 172 | if (res == NULL) { 173 | // TODO: translate errno 174 | return -errno; 175 | } 176 | f->privdata = (void *)res; 177 | 178 | return 0; 179 | } 180 | 181 | static int host_translator_op_dclose(iop_file_t *f) 182 | { 183 | if (f->privdata == NULL) { 184 | return -EBADF; 185 | } 186 | 187 | int res = closedir((DIR *)f->privdata); 188 | if (res < 0) { 189 | // TODO: translate errno 190 | return -errno; 191 | } 192 | 193 | return 0; 194 | } 195 | 196 | static int host_translator_op_dread(iop_file_t *f, iox_dirent_t *buf) 197 | { 198 | if (f->privdata == NULL) { 199 | return -EBADF; 200 | } 201 | 202 | errno = 0; 203 | struct dirent *d = readdir((DIR *)f->privdata); 204 | if (d == NULL) { 205 | if (errno != 0) { 206 | return -errno; 207 | } else { 208 | return -1; 209 | } 210 | } 211 | 212 | memset(buf, 0, sizeof(*buf)); 213 | int name_len = strlen(d->d_name); 214 | if (name_len > sizeof(buf->name)) { 215 | name_len = sizeof(buf->name); 216 | } 217 | memcpy(buf->name, d->d_name, name_len); 218 | 219 | // TODO: fill in the rest of the iox_dirent_t structure 220 | 221 | return 0; 222 | } 223 | 224 | static void convert_mode_to_posix(mode_t *posix_mode, const unsigned int *iomanx_mode) 225 | { 226 | *posix_mode = 0; 227 | if (FIO_S_ISDIR(*iomanx_mode)) { 228 | *posix_mode |= S_IFDIR; 229 | } 230 | if (FIO_S_ISREG(*iomanx_mode)) { 231 | *posix_mode |= S_IFREG; 232 | } 233 | #ifndef _WIN32 234 | if (FIO_S_ISLNK(*iomanx_mode)) { 235 | *posix_mode |= S_IFLNK; 236 | } 237 | #endif 238 | #if 0 239 | if (*iomanx_mode & FIO_S_IRUSR) { 240 | *posix_mode |= S_IRUSR; 241 | } 242 | if (*iomanx_mode & FIO_S_IWUSR) { 243 | *posix_mode |= S_IWUSR; 244 | } 245 | if (*iomanx_mode & FIO_S_IXUSR) { 246 | *posix_mode |= S_IXUSR; 247 | } 248 | if (*iomanx_mode & FIO_S_IRGRP) { 249 | *posix_mode |= S_IRGRP; 250 | } 251 | if (*iomanx_mode & FIO_S_IWGRP) { 252 | *posix_mode |= S_IWGRP; 253 | } 254 | if (*iomanx_mode & FIO_S_IXGRP) { 255 | *posix_mode |= S_IXGRP; 256 | } 257 | if (*iomanx_mode & FIO_S_IROTH) { 258 | *posix_mode |= S_IROTH; 259 | } 260 | if (*iomanx_mode & FIO_S_IWOTH) { 261 | *posix_mode |= S_IWOTH; 262 | } 263 | if (*iomanx_mode & FIO_S_IXOTH) { 264 | *posix_mode |= S_IXOTH; 265 | } 266 | #else 267 | if (*iomanx_mode & (FIO_S_IRUSR | FIO_S_IRGRP | FIO_S_IROTH)) { 268 | *posix_mode |= S_IRUSR | S_IRGRP | S_IROTH; 269 | } 270 | if (*iomanx_mode & (FIO_S_IWUSR | FIO_S_IWGRP | FIO_S_IWOTH)) { 271 | *posix_mode |= S_IWUSR | S_IWGRP | S_IWOTH; 272 | } 273 | if (*iomanx_mode & (FIO_S_IXUSR | FIO_S_IXGRP | FIO_S_IXOTH)) { 274 | *posix_mode |= S_IXUSR | S_IXGRP | S_IXOTH; 275 | } 276 | #endif 277 | #ifndef _WIN32 278 | if (*iomanx_mode & FIO_S_ISUID) { 279 | *posix_mode |= S_ISUID; 280 | } 281 | if (*iomanx_mode & FIO_S_ISGID) { 282 | *posix_mode |= S_ISGID; 283 | } 284 | if (*iomanx_mode & FIO_S_ISVTX) { 285 | *posix_mode |= S_ISVTX; 286 | } 287 | #endif 288 | } 289 | 290 | static void convert_mode_to_iomanx(unsigned int *iomanx_mode, const mode_t *posix_mode) 291 | { 292 | *iomanx_mode = 0; 293 | if (S_ISDIR(*posix_mode)) { 294 | *iomanx_mode |= FIO_S_IFDIR; 295 | } 296 | if (S_ISREG(*posix_mode)) { 297 | *iomanx_mode |= FIO_S_IFREG; 298 | } 299 | #ifndef _WIN32 300 | if (S_ISLNK(*posix_mode)) { 301 | *iomanx_mode |= FIO_S_IFLNK; 302 | } 303 | #endif 304 | #if 0 305 | if (*posix_mode & S_IRUSR) { 306 | *iomanx_mode |= FIO_S_IRUSR; 307 | } 308 | if (*posix_mode & S_IWUSR) { 309 | *iomanx_mode |= FIO_S_IWUSR; 310 | } 311 | if (*posix_mode & S_IXUSR) { 312 | *iomanx_mode |= FIO_S_IXUSR; 313 | } 314 | if (*posix_mode & S_IRGRP) { 315 | *iomanx_mode |= FIO_S_IRGRP; 316 | } 317 | if (*posix_mode & S_IWGRP) { 318 | *iomanx_mode |= FIO_S_IWGRP; 319 | } 320 | if (*posix_mode & S_IXGRP) { 321 | *iomanx_mode |= FIO_S_IXGRP; 322 | } 323 | if (*posix_mode & S_IROTH) { 324 | *iomanx_mode |= FIO_S_IROTH; 325 | } 326 | if (*posix_mode & S_IWOTH) { 327 | *iomanx_mode |= FIO_S_IWOTH; 328 | } 329 | if (*posix_mode & S_IXOTH) { 330 | *iomanx_mode |= FIO_S_IXOTH; 331 | } 332 | #else 333 | if (*posix_mode & (S_IRUSR | S_IRGRP | S_IROTH)) { 334 | *iomanx_mode |= FIO_S_IRUSR | FIO_S_IRGRP | FIO_S_IROTH; 335 | } 336 | if (*posix_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) { 337 | *iomanx_mode |= FIO_S_IWUSR | FIO_S_IWGRP | FIO_S_IWOTH; 338 | } 339 | if (*posix_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) { 340 | *iomanx_mode |= FIO_S_IXUSR | FIO_S_IXGRP | FIO_S_IXOTH; 341 | } 342 | #endif 343 | #ifndef _WIN32 344 | if (*posix_mode & S_ISUID) { 345 | *iomanx_mode |= FIO_S_ISUID; 346 | } 347 | if (*posix_mode & S_ISGID) { 348 | *iomanx_mode |= FIO_S_ISGID; 349 | } 350 | if (*posix_mode & S_ISVTX) { 351 | *iomanx_mode |= FIO_S_ISVTX; 352 | } 353 | #endif 354 | } 355 | 356 | static void convert_time_to_iomanx(unsigned char *iomanx_time, const time_t *posix_time) 357 | { 358 | struct tm timeinfo; 359 | time_t rawtime = *posix_time; 360 | // convert JST->UTC 361 | rawtime -= (9 * 60 * 60); 362 | #ifndef _WIN32 363 | gmtime_r(&rawtime, &timeinfo); 364 | #else 365 | gmtime_s(&timeinfo, &rawtime); 366 | #endif 367 | iomanx_time[0] = 0; 368 | iomanx_time[1] = timeinfo.tm_sec; 369 | iomanx_time[2] = timeinfo.tm_min; 370 | iomanx_time[3] = timeinfo.tm_hour; 371 | iomanx_time[4] = timeinfo.tm_mday; 372 | iomanx_time[5] = timeinfo.tm_mon + 1; // month 1 (January) is 0 373 | iomanx_time[6] = (timeinfo.tm_year + 1900) & 0xff; // year 1900 is 0 374 | iomanx_time[7] = ((timeinfo.tm_year + 1900) >> 8) & 0xff; 375 | } 376 | 377 | static void convert_stat_to_iomanx(iox_stat_t *iomanx_stat, const struct stat *posix_stat) 378 | { 379 | memset(iomanx_stat, 0, sizeof(*iomanx_stat)); 380 | iomanx_stat->size = posix_stat->st_size & 0xffffffff; 381 | if (sizeof(posix_stat->st_size) > 4) { 382 | iomanx_stat->hisize = ((off_t)posix_stat->st_size >> 32) & 0xffffffff; 383 | } 384 | convert_mode_to_iomanx(&(iomanx_stat->mode), &(posix_stat->st_mode)); 385 | #if 0 386 | posix_stat->st_attr = iomanx_stat->attr; 387 | #endif 388 | #ifndef _WIN32 389 | convert_time_to_iomanx(iomanx_stat->ctime, &(posix_stat->st_ctime)); 390 | convert_time_to_iomanx(iomanx_stat->atime, &(posix_stat->st_atime)); 391 | convert_time_to_iomanx(iomanx_stat->mtime, &(posix_stat->st_mtime)); 392 | #else 393 | #if 0 394 | convert_time_to_iomanx(iomanx_stat->ctime, &(posix_stat->st_ctim)); 395 | convert_time_to_iomanx(iomanx_stat->atime, &(posix_stat->st_atim)); 396 | convert_time_to_iomanx(iomanx_stat->mtime, &(posix_stat->st_mtim)); 397 | #endif 398 | #endif 399 | #if 0 400 | posix_stat->st_uid = iomanx_stat->private_0; 401 | posix_stat->st_gid = iomanx_stat->private_1; 402 | #endif 403 | } 404 | 405 | static int host_translator_op_getstat(iop_file_t *f, const char *name, iox_stat_t *iomanx_stat) 406 | { 407 | struct stat posix_stat; 408 | 409 | (void)f; 410 | 411 | int res = stat(name, &posix_stat); 412 | if (res < 0) { 413 | // TODO: translate errno 414 | return -errno; 415 | } 416 | 417 | convert_stat_to_iomanx(iomanx_stat, &posix_stat); 418 | 419 | return 0; 420 | } 421 | 422 | static int host_translator_op_chstat(iop_file_t *f, const char *name, iox_stat_t *iomanx_stat, unsigned int statmask) 423 | { 424 | (void)f; 425 | 426 | if (iomanx_stat == NULL) { 427 | return -EBADF; 428 | } 429 | 430 | if ((statmask & FIO_CST_MODE) != 0) { 431 | mode_t posix_mode = 0; 432 | convert_mode_to_posix(&posix_mode, &(iomanx_stat->mode)); 433 | int res = chmod(name, posix_mode); 434 | if (res < 0) { 435 | // TODO: translate errno 436 | return -errno; 437 | } 438 | } 439 | 440 | if ((statmask & FIO_CST_AT) != 0) { 441 | // TODO: get stat from stat, then set time using utimes 442 | } 443 | 444 | if ((statmask & FIO_CST_MT) != 0) { 445 | // TODO: get stat from stat, then set time using utimes 446 | } 447 | 448 | return 0; 449 | } 450 | 451 | static int host_translator_op_rename(iop_file_t *f, const char *old, const char *new_1) 452 | { 453 | (void)f; 454 | 455 | int res = rename(old, new_1); 456 | if (res < 0) { 457 | // TODO: translate errno 458 | return -errno; 459 | } 460 | 461 | return 0; 462 | } 463 | 464 | static int host_translator_op_chdir(iop_file_t *f, const char *name) 465 | { 466 | (void)f; 467 | 468 | int res = chdir(name); 469 | if (res < 0) { 470 | // TODO: translate errno 471 | return -errno; 472 | } 473 | 474 | return 0; 475 | } 476 | 477 | static int host_translator_op_sync(iop_file_t *f, const char *dev, int flag) 478 | { 479 | (void)f; 480 | (void)dev; 481 | (void)flag; 482 | 483 | #ifndef _WIN32 484 | sync(); 485 | #endif 486 | 487 | return 0; 488 | } 489 | 490 | static int host_translator_op_mount(iop_file_t *f, const char *fsname, const char *devname, int flag, void *arg, int arglen) 491 | { 492 | (void)f; 493 | (void)fsname; 494 | (void)devname; 495 | (void)flag; 496 | (void)arg; 497 | (void)arglen; 498 | 499 | // Intentionally not handled. 500 | 501 | return -48; 502 | } 503 | 504 | static int host_translator_op_umount(iop_file_t *f, const char *fsname) 505 | { 506 | (void)f; 507 | (void)fsname; 508 | 509 | // Intentionally not handled. 510 | 511 | return -48; 512 | } 513 | 514 | static int64_t host_translator_op_lseek64(iop_file_t *f, int64_t offset, int whence) 515 | { 516 | // TODO: translate whence 517 | int res = lseek((int)(uintptr_t)f->privdata, offset, whence); 518 | if (res < 0) { 519 | // TODO: translate errno 520 | return -errno; 521 | } 522 | 523 | return 0; 524 | } 525 | 526 | static int host_translator_op_devctl(iop_file_t *f, const char *name, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) 527 | { 528 | (void)f; 529 | (void)name; 530 | (void)cmd; 531 | (void)arg; 532 | (void)arglen; 533 | (void)buf; 534 | (void)buflen; 535 | 536 | // Intentionally not handled. 537 | 538 | return -48; 539 | } 540 | 541 | static int host_translator_op_symlink(iop_file_t *f, const char *old, const char *new_1) 542 | { 543 | (void)f; 544 | 545 | #ifndef _WIN32 546 | int res = symlink(old, new_1); 547 | if (res < 0) { 548 | // TODO: translate errno 549 | return -errno; 550 | } 551 | #endif 552 | 553 | return 0; 554 | } 555 | 556 | static int host_translator_op_readlink(iop_file_t *f, const char *path, char *buf, unsigned int buflen) 557 | { 558 | #ifndef _WIN32 559 | int res = readlink(path, buf, buflen); 560 | if (res < 0) { 561 | // TODO: translate errno 562 | return -errno; 563 | } 564 | #endif 565 | 566 | return 0; 567 | } 568 | 569 | static int host_translator_op_ioctl2(iop_file_t *f, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) 570 | { 571 | (void)f; 572 | (void)cmd; 573 | (void)arg; 574 | (void)arglen; 575 | (void)buf; 576 | (void)buflen; 577 | 578 | // Intentionally not handled. 579 | 580 | return -48; 581 | } 582 | 583 | static iop_device_ops_t host_translator_ops = { 584 | &host_translator_op_init, 585 | &host_translator_op_exit, 586 | &host_translator_op_format, 587 | &host_translator_op_open, 588 | &host_translator_op_close, 589 | &host_translator_op_read, 590 | &host_translator_op_write, 591 | &host_translator_op_lseek, 592 | &host_translator_op_ioctl, 593 | &host_translator_op_remove, 594 | &host_translator_op_mkdir, 595 | &host_translator_op_rmdir, 596 | &host_translator_op_dopen, 597 | &host_translator_op_dclose, 598 | &host_translator_op_dread, 599 | &host_translator_op_getstat, 600 | &host_translator_op_chstat, 601 | &host_translator_op_rename, 602 | &host_translator_op_chdir, 603 | &host_translator_op_sync, 604 | &host_translator_op_mount, 605 | &host_translator_op_umount, 606 | &host_translator_op_lseek64, 607 | &host_translator_op_devctl, 608 | &host_translator_op_symlink, 609 | &host_translator_op_readlink, 610 | &host_translator_op_ioctl2, 611 | }; 612 | 613 | static iop_device_t host_translator_fio_dev = { 614 | "host", 615 | (IOP_DT_FS | IOP_DT_FSEXT), 616 | 1, 617 | "Host adapter filesystem", 618 | &host_translator_ops, 619 | }; 620 | 621 | int host_adapter_init(void) 622 | { 623 | iomanX_AddDrv(&host_translator_fio_dev); 624 | return 0; 625 | } 626 | -------------------------------------------------------------------------------- /src/pfsd_client.c: -------------------------------------------------------------------------------- 1 | 2 | #include "pfsd_client.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | static int SemID; 13 | static int ShmID; 14 | static void *ShmPtr; 15 | static int Cfd; 16 | static size_t SharedMemorySize; 17 | 18 | int Connect(void) 19 | { 20 | struct sockaddr_un sa; 21 | 22 | Cfd = socket(AF_UNIX, SOCK_STREAM, PF_UNSPEC); 23 | if (Cfd < 0) { 24 | perror("socket"); 25 | return -1; 26 | } 27 | memset(&sa, 0, sizeof(sa)); 28 | sa.sun_family = AF_UNIX; 29 | if (bind(Cfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 30 | perror("bind"); 31 | return -1; 32 | } 33 | memset(&sa, 0, sizeof(sa)); 34 | sa.sun_family = AF_UNIX; 35 | strcpy(sa.sun_path, PFSD_PATH); 36 | if (connect(Cfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) { 37 | perror("connect"); 38 | return -1; 39 | } 40 | return 0; 41 | } 42 | 43 | size_t GetShmSize(void) 44 | { 45 | struct shmid_ds shminfo; 46 | 47 | shmctl(ShmID, IPC_STAT, &shminfo); 48 | return shminfo.shm_segsz; 49 | } 50 | 51 | int StartUp(void) 52 | { 53 | if (Connect() < 0) { 54 | printf("Can't connect to pfsd.\n"); 55 | return -1; 56 | } 57 | SemID = semget(PFSD_SHARED_KEY, 1, 0); 58 | if (SemID < 0) { 59 | perror("semget"); 60 | return -2; 61 | } 62 | ShmID = shmget(PFSD_SHARED_KEY, 0, 0); 63 | if (ShmID < 0) { 64 | perror("shmget"); 65 | return -3; 66 | } 67 | ShmPtr = shmat(ShmID, NULL, 0); 68 | SharedMemorySize = GetShmSize(); 69 | if ((int)SharedMemorySize <= 0) { 70 | fprintf(stderr, "GetShmSize error.\n"); 71 | return -5; 72 | } 73 | return 0; 74 | } 75 | 76 | void CleanUp(void) 77 | { 78 | shmdt(ShmPtr); 79 | close(Cfd); 80 | } 81 | 82 | void RequireLock(void) 83 | { 84 | struct sembuf sema_obj[1]; 85 | 86 | sema_obj[0].sem_num = 0; 87 | sema_obj[0].sem_op = -1; 88 | sema_obj[0].sem_flg = SEM_UNDO; 89 | if (semop(SemID, &sema_obj[0], sizeof(sema_obj) / sizeof(sema_obj[0])) < 0) 90 | perror("semop require lock"); 91 | } 92 | 93 | void ReleaseLock(void) 94 | { 95 | struct sembuf sema_obj[1]; 96 | 97 | sema_obj[0].sem_num = 0; 98 | sema_obj[0].sem_op = 1; 99 | sema_obj[0].sem_flg = SEM_UNDO; 100 | if (semop(SemID, &sema_obj[0], sizeof(sema_obj) / sizeof(sema_obj[0])) < 0) 101 | perror("semop release lock"); 102 | } 103 | 104 | void SendRequest(int rpc_cmd, const void *rpc_buf, size_t rpc_buf_size) 105 | { 106 | size_t buf_size; 107 | pfsd_rpc_common_struct_t *rpc_buf_p; 108 | 109 | buf_size = rpc_buf_size + 8; 110 | rpc_buf_p = malloc(buf_size); 111 | rpc_buf_p->m_command = rpc_cmd; 112 | memcpy(rpc_buf_p->m_opu.m_payload, rpc_buf, rpc_buf_size); 113 | if (write(Cfd, rpc_buf_p, buf_size) < 0) 114 | perror("Send msg failed.\n"); 115 | free(rpc_buf_p); 116 | } 117 | 118 | int ReceiveReply(void) 119 | { 120 | int rpcret; 121 | 122 | if (read(Cfd, &rpcret, sizeof(rpcret)) <= 0) 123 | perror("Receive msg failed.\n"); 124 | return rpcret; 125 | } 126 | 127 | int ReceiveReplyLong(void) 128 | { 129 | int rpcret; 130 | 131 | if (read(Cfd, &rpcret, sizeof(rpcret)) <= 0) 132 | perror("Receive msg failed.\n"); 133 | return rpcret; 134 | } 135 | 136 | int64_t ReceiveReplyLongLong(void) 137 | { 138 | int64_t rpcret64; 139 | 140 | if (read(Cfd, &rpcret64, sizeof(rpcret64)) <= 0) 141 | perror("Receive msg failed.\n"); 142 | return rpcret64; 143 | } 144 | 145 | int scepfsdInit(void) 146 | { 147 | return StartUp(); 148 | } 149 | 150 | void scepfsdExit(void) 151 | { 152 | CleanUp(); 153 | } 154 | 155 | int scepfsdOpen(const char *name, int flags, int mode) 156 | { 157 | int ret; 158 | pfsd_rpc_open_struct_t rpc_struct; 159 | 160 | strcpy(rpc_struct.m_name, name); 161 | rpc_struct.m_flags = flags; 162 | rpc_struct.m_mode = mode; 163 | RequireLock(); 164 | SendRequest(PFSD_REQ_OPEN, &rpc_struct, sizeof(rpc_struct)); 165 | ret = ReceiveReply(); 166 | ReleaseLock(); 167 | return ret; 168 | } 169 | 170 | int scepfsdLseek(int fd, int offset, int whence) 171 | { 172 | int ret; 173 | pfsd_rpc_lseek_struct_t rpc_struct; 174 | 175 | rpc_struct.m_fd = fd; 176 | rpc_struct.m_offset = offset; 177 | rpc_struct.m_whence = whence; 178 | RequireLock(); 179 | SendRequest(PFSD_REQ_LSEEK, &rpc_struct, sizeof(rpc_struct)); 180 | ret = ReceiveReplyLong(); 181 | ReleaseLock(); 182 | return ret; 183 | } 184 | 185 | int64_t scepfsdLseek64(int fd, int64_t offset, int whence) 186 | { 187 | int64_t ret; 188 | pfsd_rpc_lseek64_struct_t rpc_struct; 189 | 190 | rpc_struct.m_fd = fd; 191 | rpc_struct.m_offset = offset; 192 | rpc_struct.m_whence = whence; 193 | RequireLock(); 194 | SendRequest(PFSD_REQ_LSEEK64, &rpc_struct, sizeof(rpc_struct)); 195 | ret = ReceiveReplyLongLong(); 196 | ReleaseLock(); 197 | return ret; 198 | } 199 | 200 | int scepfsdRead(int fd, void *ptr, size_t size) 201 | { 202 | int chunked_size; 203 | int result; 204 | int ret; 205 | pfsd_rpc_read_struct_t rpc_struct; 206 | 207 | for (chunked_size = 0; SharedMemorySize < (size - chunked_size); chunked_size += result) { 208 | result = scepfsdRead(fd, (char *)ptr + chunked_size, SharedMemorySize); 209 | if (result < 0) 210 | return result; 211 | if (result < (int)SharedMemorySize) 212 | return result + chunked_size; 213 | } 214 | rpc_struct.m_fd = fd; 215 | rpc_struct.m_ptr_useshmptr = !!ptr; 216 | rpc_struct.m_size = size - chunked_size; 217 | RequireLock(); 218 | SendRequest(PFSD_REQ_READ, &rpc_struct, sizeof(rpc_struct)); 219 | ret = ReceiveReply(); 220 | if (ptr) 221 | memcpy(ptr, ShmPtr, rpc_struct.m_size); 222 | ReleaseLock(); 223 | return ret + chunked_size; 224 | } 225 | 226 | int scepfsdWrite(int fd, void *ptr, size_t size) 227 | { 228 | int chunked_size; 229 | int result; 230 | int ret; 231 | pfsd_rpc_write_struct_t rpc_struct; 232 | 233 | for (chunked_size = 0; SharedMemorySize < (size - chunked_size); chunked_size += result) { 234 | result = scepfsdWrite(fd, (char *)ptr + chunked_size, SharedMemorySize); 235 | if (result < 0) 236 | return result; 237 | if (result < (int)SharedMemorySize) 238 | return result + chunked_size; 239 | } 240 | rpc_struct.m_fd = fd; 241 | rpc_struct.m_ptr_useshmptr = !!ptr; 242 | rpc_struct.m_size = size - chunked_size; 243 | RequireLock(); 244 | if (ptr) 245 | memcpy(ShmPtr, ptr, rpc_struct.m_size); 246 | SendRequest(PFSD_REQ_WRITE, &rpc_struct, sizeof(rpc_struct)); 247 | ret = ReceiveReply(); 248 | ReleaseLock(); 249 | return ret + chunked_size; 250 | } 251 | 252 | int scepfsdClose(int fd) 253 | { 254 | int ret; 255 | pfsd_rpc_close_struct_t rpc_struct; 256 | 257 | rpc_struct.m_fd = fd; 258 | RequireLock(); 259 | SendRequest(PFSD_REQ_CLOSE, &rpc_struct, sizeof(rpc_struct)); 260 | ret = ReceiveReply(); 261 | ReleaseLock(); 262 | return ret; 263 | } 264 | 265 | int scepfsdDopen(const char *name) 266 | { 267 | int ret; 268 | pfsd_rpc_dopen_struct_t rpc_struct; 269 | 270 | strcpy(rpc_struct.m_name, name); 271 | RequireLock(); 272 | SendRequest(PFSD_REQ_DOPEN, &rpc_struct, sizeof(rpc_struct)); 273 | ret = ReceiveReply(); 274 | ReleaseLock(); 275 | return ret; 276 | } 277 | 278 | int scepfsdDread(int fd, iox_dirent_t *iox_dirent) 279 | { 280 | int ret; 281 | pfsd_rpc_dread_struct_t rpc_struct; 282 | 283 | rpc_struct.m_fd = fd; 284 | rpc_struct.m_iox_dirent_useshmptr = !!iox_dirent; 285 | RequireLock(); 286 | SendRequest(PFSD_REQ_DREAD, &rpc_struct, sizeof(rpc_struct)); 287 | ret = ReceiveReply(); 288 | memcpy(iox_dirent, ShmPtr, sizeof(iox_dirent_t)); 289 | ReleaseLock(); 290 | return ret; 291 | } 292 | 293 | int scepfsdDclose(int fd) 294 | { 295 | int ret; 296 | pfsd_rpc_dclose_struct_t rpc_struct; 297 | 298 | rpc_struct.m_fd = fd; 299 | RequireLock(); 300 | SendRequest(PFSD_REQ_DCLOSE, &rpc_struct, sizeof(rpc_struct)); 301 | ret = ReceiveReply(); 302 | ReleaseLock(); 303 | return ret; 304 | } 305 | 306 | int scepfsdChstat(const char *name, iox_stat_t *stat, int mask) 307 | { 308 | int ret; 309 | pfsd_rpc_chstat_struct_t rpc_struct; 310 | 311 | strcpy(rpc_struct.m_name, name); 312 | rpc_struct.m_stat_useshmptr = !!stat; 313 | rpc_struct.m_mask = mask; 314 | RequireLock(); 315 | if (stat) 316 | memcpy(ShmPtr, stat, sizeof(iox_stat_t)); 317 | SendRequest(PFSD_REQ_CHSTAT, &rpc_struct, sizeof(rpc_struct)); 318 | ret = ReceiveReply(); 319 | ReleaseLock(); 320 | return ret; 321 | } 322 | 323 | int scepfsdGetstat(const char *name, iox_stat_t *stat) 324 | { 325 | int ret; 326 | pfsd_rpc_getstat_struct_t rpc_struct; 327 | 328 | strcpy(rpc_struct.m_name, name); 329 | rpc_struct.m_stat_useshmptr = !!stat; 330 | RequireLock(); 331 | SendRequest(PFSD_REQ_GETSTAT, &rpc_struct, sizeof(rpc_struct)); 332 | ret = ReceiveReply(); 333 | if (stat) 334 | memcpy(stat, ShmPtr, sizeof(iox_stat_t)); 335 | ReleaseLock(); 336 | return ret; 337 | } 338 | 339 | int scepfsdMkdir(const char *name, int mode) 340 | { 341 | int ret; 342 | pfsd_rpc_mkdir_struct_t rpc_struct; 343 | 344 | strcpy(rpc_struct.m_name, name); 345 | rpc_struct.m_mode = mode; 346 | RequireLock(); 347 | SendRequest(PFSD_REQ_MKDIR, &rpc_struct, sizeof(rpc_struct)); 348 | ret = ReceiveReply(); 349 | ReleaseLock(); 350 | return ret; 351 | } 352 | 353 | int scepfsdRmdir(const char *name) 354 | { 355 | int ret; 356 | pfsd_rpc_rmdir_struct_t rpc_struct; 357 | 358 | strcpy(rpc_struct.m_name, name); 359 | RequireLock(); 360 | SendRequest(PFSD_REQ_RMDIR, &rpc_struct, sizeof(rpc_struct)); 361 | ret = ReceiveReply(); 362 | ReleaseLock(); 363 | return ret; 364 | } 365 | 366 | int scepfsdChdir(const char *name) 367 | { 368 | int ret; 369 | pfsd_rpc_chdir_struct_t rpc_struct; 370 | 371 | strcpy(rpc_struct.m_name, name); 372 | RequireLock(); 373 | SendRequest(PFSD_REQ_CHDIR, &rpc_struct, sizeof(rpc_struct)); 374 | ret = ReceiveReply(); 375 | ReleaseLock(); 376 | return ret; 377 | } 378 | 379 | int scepfsdRemove(const char *name) 380 | { 381 | int ret; 382 | pfsd_rpc_remove_struct_t rpc_struct; 383 | 384 | strcpy(rpc_struct.m_name, name); 385 | RequireLock(); 386 | SendRequest(PFSD_REQ_REMOVE, &rpc_struct, sizeof(rpc_struct)); 387 | ret = ReceiveReply(); 388 | ReleaseLock(); 389 | return ret; 390 | } 391 | 392 | int scepfsdRename(const char *old, const char *new_) 393 | { 394 | int ret; 395 | pfsd_rpc_rename_struct_t rpc_struct; 396 | 397 | strcpy(rpc_struct.m_old, old); 398 | strcpy(rpc_struct.m_new, new_); 399 | RequireLock(); 400 | SendRequest(PFSD_REQ_RENAME, &rpc_struct, sizeof(rpc_struct)); 401 | ret = ReceiveReply(); 402 | ReleaseLock(); 403 | return ret; 404 | } 405 | 406 | int scepfsdIoctl(int fd, int cmd, void *arg) 407 | { 408 | int ret; 409 | pfsd_rpc_ioctl_struct_t rpc_struct; 410 | 411 | rpc_struct.m_fd = fd; 412 | rpc_struct.m_cmd = cmd; 413 | rpc_struct.m_arg_useshmptr = !!arg; 414 | RequireLock(); 415 | if (arg) 416 | memcpy(ShmPtr, arg, 1024); 417 | SendRequest(PFSD_REQ_IOCTL, &rpc_struct, sizeof(rpc_struct)); 418 | ret = ReceiveReply(); 419 | ReleaseLock(); 420 | return ret; 421 | } 422 | 423 | int scepfsdIoctl2(int fd, int command, void *arg, size_t arglen, void *buf, size_t buflen) 424 | { 425 | int ret; 426 | 427 | if (command == 0x6832) { 428 | pfsd_rpc_ioctl2_hioctransfer_struct_t rpc_struct; 429 | hddIoctl2Transfer_t *xfer; 430 | 431 | xfer = arg; 432 | rpc_struct.m_fd = fd; 433 | rpc_struct.m_size = xfer->size; 434 | rpc_struct.m_buffer_useshmptr = !!xfer->buffer; 435 | rpc_struct.m_sub = xfer->sub; 436 | rpc_struct.m_mode = xfer->mode; 437 | rpc_struct.m_sector = xfer->sector; 438 | if (!xfer->buffer || rpc_struct.m_size > 0x100) 439 | return -22; 440 | RequireLock(); 441 | memcpy(ShmPtr, xfer->buffer, rpc_struct.m_size << 9); 442 | SendRequest(PFSD_REQ_IOCTL2_HIOCTRANSFER, &rpc_struct, sizeof(rpc_struct)); 443 | ret = ReceiveReply(); 444 | memcpy(xfer->buffer, ShmPtr, rpc_struct.m_size << 9); 445 | } else { 446 | pfsd_rpc_ioctl2_struct_t rpc_struct; 447 | 448 | rpc_struct.m_fd = fd; 449 | rpc_struct.m_cmd = command; 450 | rpc_struct.m_arg_useshmptr = !!arg; 451 | rpc_struct.m_arglen = arglen; 452 | rpc_struct.m_buf_useshmptr = !!buf; 453 | rpc_struct.m_buflen = buflen; 454 | RequireLock(); 455 | if (arg && arglen) 456 | memcpy(ShmPtr, arg, arglen); 457 | SendRequest(PFSD_REQ_IOCTL2, &rpc_struct, sizeof(rpc_struct)); 458 | ret = ReceiveReply(); 459 | if (buf && buflen) 460 | memcpy(buf, ShmPtr, buflen); 461 | } 462 | ReleaseLock(); 463 | return ret; 464 | } 465 | 466 | int scepfsdSymlink(const char *old, const char *new_) 467 | { 468 | int ret; 469 | pfsd_rpc_symlink_struct_t rpc_struct; 470 | 471 | strcpy(rpc_struct.m_old, old); 472 | strcpy(rpc_struct.m_new, new_); 473 | RequireLock(); 474 | SendRequest(PFSD_REQ_SYMLINK, &rpc_struct, sizeof(rpc_struct)); 475 | ret = ReceiveReply(); 476 | ReleaseLock(); 477 | return ret; 478 | } 479 | 480 | int scepfsdReadlink(const char *name, char *buf, size_t buflen) 481 | { 482 | int ret; 483 | pfsd_rpc_readlink_struct_t rpc_struct; 484 | 485 | strcpy(rpc_struct.m_name, name); 486 | rpc_struct.m_buf_useshmptr = !!buf; 487 | rpc_struct.m_buflen = buflen; 488 | RequireLock(); 489 | SendRequest(PFSD_REQ_READLINK, &rpc_struct, sizeof(rpc_struct)); 490 | ret = ReceiveReply(); 491 | if (buf && buflen) 492 | memcpy(buf, ShmPtr, buflen); 493 | ReleaseLock(); 494 | return ret; 495 | } 496 | 497 | int scepfsdSync(const char *dev, int flag) 498 | { 499 | int ret; 500 | pfsd_rpc_sync_struct_t rpc_struct; 501 | 502 | strcpy(rpc_struct.m_dev, dev); 503 | rpc_struct.m_flag = flag; 504 | RequireLock(); 505 | SendRequest(PFSD_REQ_SYNC, &rpc_struct, sizeof(rpc_struct)); 506 | ret = ReceiveReply(); 507 | ReleaseLock(); 508 | return ret; 509 | } 510 | 511 | int scepfsdFormat(const char *dev, const char *blockdev, void *arg, size_t arglen) 512 | { 513 | int ret; 514 | pfsd_rpc_format_struct_t rpc_struct; 515 | 516 | strcpy(rpc_struct.m_dev, dev); 517 | strcpy(rpc_struct.m_blockdev, blockdev); 518 | rpc_struct.m_arg_useshmptr = !!arg; 519 | rpc_struct.m_arglen = arglen; 520 | RequireLock(); 521 | if (arg && arglen) 522 | memcpy(ShmPtr, arg, arglen); 523 | SendRequest(PFSD_REQ_FORMAT, &rpc_struct, sizeof(rpc_struct)); 524 | ret = ReceiveReply(); 525 | ReleaseLock(); 526 | return ret; 527 | } 528 | 529 | int scepfsdMount(const char *fsname, const char *devname, int flag, void *arg, size_t arglen) 530 | { 531 | int ret; 532 | pfsd_rpc_mount_struct_t rpc_struct; 533 | 534 | strcpy(rpc_struct.m_fsname, fsname); 535 | strcpy(rpc_struct.m_devname, devname); 536 | rpc_struct.m_flag = flag; 537 | rpc_struct.m_arg_useshmptr = !!arg; 538 | rpc_struct.m_arglen = arglen; 539 | RequireLock(); 540 | if (arg && arglen) 541 | memcpy(ShmPtr, arg, arglen); 542 | SendRequest(PFSD_REQ_MOUNT, &rpc_struct, sizeof(rpc_struct)); 543 | ret = ReceiveReply(); 544 | ReleaseLock(); 545 | return ret; 546 | } 547 | 548 | int scepfsdUmount(const char *fsname) 549 | { 550 | int ret; 551 | pfsd_rpc_umount_struct_t rpc_struct; 552 | 553 | strcpy(rpc_struct.m_fsname, fsname); 554 | RequireLock(); 555 | SendRequest(PFSD_REQ_UMOUNT, &rpc_struct, sizeof(rpc_struct)); 556 | ret = ReceiveReply(); 557 | ReleaseLock(); 558 | return ret; 559 | } 560 | 561 | int scepfsdDevctl(const char *name, int cmd, const void *arg, size_t arglen, void *buf, size_t buflen) 562 | { 563 | int ret; 564 | pfsd_rpc_devctl_struct_t rpc_struct; 565 | 566 | strcpy(rpc_struct.m_name, name); 567 | rpc_struct.m_cmd = cmd; 568 | rpc_struct.m_arg_useshmptr = !!arg; 569 | rpc_struct.m_arglen = arglen; 570 | rpc_struct.m_buf_useshmptr = !!buf; 571 | rpc_struct.m_buflen = buflen; 572 | RequireLock(); 573 | if (arg && arglen) 574 | memcpy(ShmPtr, arg, arglen); 575 | SendRequest(PFSD_REQ_DEVCTL, &rpc_struct, sizeof(rpc_struct)); 576 | ret = ReceiveReply(); 577 | if (buf && buflen) 578 | memcpy(buf, ShmPtr, buflen); 579 | ReleaseLock(); 580 | return ret; 581 | } 582 | 583 | int scepfsdSetReadAhead(int enabled) 584 | { 585 | int ret; 586 | pfsd_rpc_setreadahead_struct_t rpc_struct; 587 | 588 | rpc_struct.m_enabled = enabled; 589 | RequireLock(); 590 | SendRequest(PFSD_REQ_SETREADAHEAD, &rpc_struct, sizeof(rpc_struct)); 591 | ret = ReceiveReply(); 592 | ReleaseLock(); 593 | return ret; 594 | } 595 | 596 | int scepfsdGetReadAhead(void) 597 | { 598 | int ret; 599 | 600 | RequireLock(); 601 | SendRequest(PFSD_REQ_GETREADAHEAD, NULL, 0); 602 | ret = ReceiveReply(); 603 | ReleaseLock(); 604 | return ret; 605 | } 606 | 607 | int scepfsdGetMountPoint(const char *in_oldmap, char *out_newmap) 608 | { 609 | int ret; 610 | pfsd_rpc_getmountpoint_struct_t rpc_struct; 611 | 612 | strcpy(rpc_struct.m_path, in_oldmap); 613 | RequireLock(); 614 | SendRequest(PFSD_REQ_GETMOUNTPOINT, &rpc_struct, sizeof(rpc_struct)); 615 | ret = ReceiveReply(); 616 | if (out_newmap) 617 | strcpy(out_newmap, (const char *)ShmPtr); 618 | ReleaseLock(); 619 | return ret; 620 | } 621 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Library General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) year name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | -------------------------------------------------------------------------------- /subprojects/hdlfs/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "hdlfs.h" 15 | 16 | #define MODNAME "hdl_filesystem_driver" 17 | IRX_ID(MODNAME, 0x01, 0x02); 18 | 19 | // #define DEBUG 20 | 21 | #ifdef DEBUG 22 | #define DEBUG_PRINTF(args...) printf(args) 23 | #else 24 | #define DEBUG_PRINTF(args...) 25 | #endif 26 | 27 | /* APA IOCTL2 commands */ 28 | // Special HDD.IRX IOCTL2 command for supporting HDLFS 29 | #if 0 30 | #define HIOCGETPARTSTART 0x00006836 // Get the sector number of the first sector of the partition. 31 | #endif 32 | 33 | struct HDLFS_FileDescriptor 34 | { 35 | int MountFD; 36 | int SemaID; 37 | unsigned short int NumPartitions; 38 | unsigned short int CurrentPartNum; 39 | unsigned int RelativeSectorNumber; /* The current sector number, relative to the start of the current partition being accessed. */ 40 | unsigned int offset; /* The current offset of the game in HDD sectors. */ 41 | unsigned int size; /* The total size of all linked partitions in HDD sectors. */ 42 | 43 | part_specs_t part_specs[65]; 44 | }; 45 | 46 | #define MAX_AVAILABLE_FDs 2 47 | 48 | struct HDLFS_FileDescriptor FD_List[MAX_AVAILABLE_FDs]; 49 | 50 | static int unmount(unsigned int unit); 51 | 52 | static int hdlfs_init(iomanX_iop_device_t *fd) 53 | { 54 | unsigned int i; 55 | iop_sema_t sema; 56 | 57 | memset(FD_List, 0, sizeof(FD_List)); 58 | for (i = 0; i < MAX_AVAILABLE_FDs; i++) { 59 | FD_List[i].MountFD = -1; 60 | sema.attr = sema.option = 0; 61 | sema.initial = sema.max = 1; 62 | FD_List[i].SemaID = CreateSema(&sema); 63 | } 64 | 65 | return 0; 66 | } 67 | 68 | static int hdlfs_deinit(iomanX_iop_device_t *fd) 69 | { 70 | unsigned int i; 71 | 72 | for (i = 0; i < MAX_AVAILABLE_FDs; i++) { 73 | if (FD_List[i].MountFD >= 0) { 74 | unmount(i); 75 | } 76 | 77 | DeleteSema(FD_List[i].SemaID); 78 | } 79 | 80 | return 0; 81 | } 82 | 83 | static int hdlfs_format(iomanX_iop_file_t *fd, const char *device, const char *blockdev, void *args, int arglen) 84 | { 85 | int PartitionFD, result; 86 | unsigned int i, MaxSectorsPerPart, NumReservedSectors, NumberOfSectors, SectorNumber, SectorsInPart, SectorsRemaining; 87 | hdl_game_info HDLFilesystemData; 88 | 89 | MaxSectorsPerPart = iomanX_devctl(blockdev, HDIOC_MAXSECTOR, NULL, 0, NULL, 0); 90 | result = 0; 91 | if ((PartitionFD = iomanX_open(blockdev, FIO_O_WRONLY, 0644)) >= 0) { 92 | iomanX_lseek(PartitionFD, HDL_GAME_DATA_OFFSET, SEEK_SET); 93 | 94 | memset(&HDLFilesystemData, 0, sizeof(hdl_game_info)); 95 | HDLFilesystemData.magic = HDL_INFO_MAGIC; 96 | HDLFilesystemData.version = 1; 97 | 98 | /* Parse arguments. */ 99 | memcpy(HDLFilesystemData.gamename, ((struct HDLFS_FormatArgs *)args)->GameTitle, sizeof(HDLFilesystemData.gamename)); 100 | HDLFilesystemData.hdl_compat_flags = 0; 101 | HDLFilesystemData.ops2l_compat_flags = ((struct HDLFS_FormatArgs *)args)->CompatFlags; 102 | HDLFilesystemData.dma_type = ((struct HDLFS_FormatArgs *)args)->TRType; 103 | HDLFilesystemData.dma_mode = ((struct HDLFS_FormatArgs *)args)->TRMode; 104 | strncpy(HDLFilesystemData.startup, ((struct HDLFS_FormatArgs *)args)->StartupPath, sizeof(HDLFilesystemData.startup)); 105 | NumberOfSectors = ((struct HDLFS_FormatArgs *)args)->NumSectors; 106 | HDLFilesystemData.layer1_start = ((struct HDLFS_FormatArgs *)args)->Layer1Start; 107 | HDLFilesystemData.discType = ((struct HDLFS_FormatArgs *)args)->DiscType; 108 | 109 | /* Set up partition data in the HDLoader game filesystem structure. */ 110 | HDLFilesystemData.num_partitions = iomanX_ioctl2(PartitionFD, HIOCNSUB, NULL, 0, NULL, 0) + 1; 111 | for (i = 0, SectorNumber = 0; i < HDLFilesystemData.num_partitions && result >= 0; i++) { 112 | if ((result = iomanX_ioctl2(PartitionFD, HIOCGETPARTSTART, &i, sizeof(i), NULL, 0)) >= 0) { 113 | NumReservedSectors = (i == 0) ? 0x2000 : 4; /* Main partitions have a 4MB reserved area, while sub-partitions have 2 reserved sectors. 114 | However, if a sub-partition has 2 reserved sectors, it means that there will be some leftover sectors at the end of the disk 115 | (The number of sectors will be indivisible by 4 - as 2048=512x4). 116 | 117 | Therefore, round up the number of reserved sectors for sub-partitions to 4. 118 | */ 119 | DEBUG_PRINTF("HDLFS format - part: %u, start: 0x%08lx\n", i, SectorNumber); 120 | HDLFilesystemData.part_specs[i].part_offset = SectorNumber; 121 | HDLFilesystemData.part_specs[i].data_start = result + NumReservedSectors; 122 | SectorsInPart = (iomanX_ioctl2(PartitionFD, HIOCGETSIZE, &i, sizeof(i), NULL, 0) - NumReservedSectors) / 4; 123 | SectorsRemaining = NumberOfSectors - SectorNumber; 124 | SectorsInPart = SectorsInPart > SectorsRemaining ? SectorsRemaining : SectorsInPart; 125 | // Sanity check: Ensure that the partition size doesn't hit exactly 4GB or larger, or the size field will overflow. 126 | if (SectorsInPart >= 0x200000) { 127 | DEBUG_PRINTF("HDLFS: Error: Partition slice %d is too big for HDLFS.\n", ); 128 | result = -EINVAL; 129 | break; 130 | } 131 | 132 | HDLFilesystemData.part_specs[i].part_size = SectorsInPart * 2048; 133 | SectorNumber += SectorsInPart; 134 | } else { 135 | DEBUG_PRINTF("HDLFS: Error: IOCTL2_GETSTART fail for partition number %u: %d\n", i, result); 136 | } 137 | } 138 | 139 | if (result >= 0) { 140 | /* Sanity check: Can the partition actually hold the number of sectors it should hold? */ 141 | if (SectorNumber < NumberOfSectors) { 142 | DEBUG_PRINTF("HDLFS - format failed: Partition is too small to contain game.\n"); 143 | result = -ENOMEM; 144 | } else { 145 | result = (iomanX_write(PartitionFD, &HDLFilesystemData, sizeof(hdl_game_info)) != sizeof(hdl_game_info)) ? -EIO : 0; 146 | } 147 | } 148 | iomanX_close(PartitionFD); 149 | } else 150 | result = PartitionFD; 151 | 152 | return result; 153 | } 154 | 155 | static int hdlfs_open(iomanX_iop_file_t *fd, const char *path, int flags, int mode) 156 | { 157 | int result; 158 | 159 | if (fd->unit < MAX_AVAILABLE_FDs && FD_List[fd->unit].MountFD >= 0) { 160 | fd->privdata = &FD_List[fd->unit]; 161 | result = fd->unit; 162 | } else 163 | result = -ENODEV; 164 | 165 | return result; 166 | } 167 | 168 | static int hdlfs_close(iomanX_iop_file_t *fd) 169 | { 170 | return 0; 171 | } 172 | 173 | static int hdlfs_io_internal(iomanX_iop_file_t *fd, void *buffer, int size, int mode) 174 | { 175 | hddIoctl2Transfer_t CmdData; 176 | struct HDLFS_FileDescriptor *hdlfs_fd; 177 | unsigned int SectorsToRead, SectorsRemaining, ReservedSectors, SectorsRemainingInPartition; 178 | int result; 179 | 180 | if ((size % 512 != 0) || (buffer == NULL)) { 181 | DEBUG_PRINTF("HDLFS: Invalid arguments for I/O! buffer: %p size: %d\n", buffer, size); 182 | return -EINVAL; 183 | } 184 | 185 | hdlfs_fd = (struct HDLFS_FileDescriptor *)fd->privdata; 186 | 187 | WaitSema(hdlfs_fd->SemaID); 188 | 189 | SectorsRemaining = size / 512; 190 | result = 0; 191 | while (SectorsRemaining > 0) { 192 | /* 193 | What is done here: 194 | 1. Determine which partition the data should be read/written from/to. 195 | 2. Determine the number of sectors that can be read from the partition. 196 | 3. Determine the first sector within the partition, at which data should be read/written from/to. 197 | */ 198 | 199 | SectorsRemainingInPartition = hdlfs_fd->part_specs[hdlfs_fd->CurrentPartNum].part_size / 512 - hdlfs_fd->RelativeSectorNumber; 200 | /* If there are no more sectors in the current partition, move on. */ 201 | if (SectorsRemainingInPartition < 1) { 202 | hdlfs_fd->CurrentPartNum++; 203 | hdlfs_fd->RelativeSectorNumber = 0; 204 | SectorsRemainingInPartition = hdlfs_fd->part_specs[hdlfs_fd->CurrentPartNum].part_size / 512; 205 | } 206 | 207 | ReservedSectors = hdlfs_fd->CurrentPartNum == 0 ? 0x2000 : 4; 208 | SectorsToRead = SectorsRemaining > SectorsRemainingInPartition ? SectorsRemainingInPartition : SectorsRemaining; 209 | 210 | /* If the calling program passes bogus arguments, the device driver called via iomanX_ioctl2() should bail out. */ 211 | CmdData.sub = hdlfs_fd->CurrentPartNum; 212 | CmdData.sector = ReservedSectors + hdlfs_fd->RelativeSectorNumber; 213 | CmdData.size = SectorsToRead; 214 | CmdData.mode = mode; 215 | CmdData.buffer = buffer; 216 | 217 | if ((result = iomanX_ioctl2(hdlfs_fd->MountFD, HIOCTRANSFER, &CmdData, 0, NULL, 0)) < 0) { 218 | DEBUG_PRINTF("HDLFS: I/O error occurred at partition %u, sector 0x%08lx, num sectors: %u, code: %d\n", hdlfs_fd->CurrentPartNum, hdlfs_fd->RelativeSectorNumber, SectorsToRead, result); 219 | break; 220 | } 221 | 222 | hdlfs_fd->offset += SectorsToRead; 223 | SectorsRemaining -= SectorsToRead; 224 | buffer = (u8 *)buffer + SectorsToRead * 512; 225 | hdlfs_fd->RelativeSectorNumber += SectorsToRead; 226 | } 227 | 228 | SignalSema(hdlfs_fd->SemaID); 229 | 230 | return ((result < 0) ? result : size); 231 | } 232 | 233 | static int hdlfs_read(iomanX_iop_file_t *fd, void *buffer, int size) 234 | { 235 | return ((fd->mode & FIO_O_RDONLY) ? hdlfs_io_internal(fd, buffer, size, ATA_DIR_READ) : -EINVAL); 236 | } 237 | 238 | static int hdlfs_write(iomanX_iop_file_t *fd, void *buffer, int size) 239 | { 240 | return ((fd->mode & FIO_O_WRONLY) ? hdlfs_io_internal(fd, buffer, size, ATA_DIR_WRITE) : -EROFS); 241 | } 242 | 243 | static int hdlfs_lseek(iomanX_iop_file_t *fd, int offset, int whence) 244 | { 245 | unsigned int i; 246 | struct HDLFS_FileDescriptor *hdl_fd; 247 | int result; 248 | 249 | if (fd->unit < MAX_AVAILABLE_FDs && FD_List[fd->unit].MountFD >= 0) { 250 | hdl_fd = (struct HDLFS_FileDescriptor *)fd->privdata; 251 | 252 | WaitSema(hdl_fd->SemaID); 253 | 254 | result = -EINVAL; 255 | for (i = 0; i < hdl_fd->NumPartitions; i++) { 256 | if (hdl_fd->part_specs[i].part_offset <= offset && offset < hdl_fd->part_specs[i].part_offset + hdl_fd->part_specs[i].part_size / 2048) { 257 | switch (whence) { 258 | case SEEK_SET: 259 | hdl_fd->offset = offset * 4; 260 | break; 261 | case SEEK_CUR: 262 | hdl_fd->offset += offset * 4; 263 | break; 264 | case SEEK_END: 265 | hdl_fd->offset = hdl_fd->size - offset * 4; 266 | } 267 | 268 | hdl_fd->RelativeSectorNumber = hdl_fd->offset - hdl_fd->part_specs[i].part_offset * 4; 269 | hdl_fd->CurrentPartNum = i; 270 | result = hdl_fd->offset / 4; 271 | break; 272 | } 273 | } 274 | 275 | SignalSema(hdl_fd->SemaID); 276 | } else 277 | result = -ENODEV; 278 | 279 | return result; 280 | } 281 | 282 | static int hdlfs_dopen(iomanX_iop_file_t *fd, const char *path) 283 | { 284 | return 0; 285 | } 286 | 287 | static int hdlfs_dclose(iomanX_iop_file_t *fd) 288 | { 289 | return 0; 290 | } 291 | 292 | static int hdlfs_getstat_filler(int unit, const char *path, iox_stat_t *stat, char *GameTitleOut, unsigned int MaxTitleLen, char *StartupPathOut, unsigned int MaxStartupPathLen) 293 | { 294 | int PartFD, result; 295 | hdl_game_info HDLGameInfo; 296 | unsigned int i; 297 | u32 GameSize; 298 | 299 | if (unit < MAX_AVAILABLE_FDs && FD_List[unit].MountFD >= 0) { 300 | PartFD = FD_List[unit].MountFD; 301 | 302 | WaitSema(FD_List[unit].SemaID); 303 | 304 | iomanX_lseek(PartFD, HDL_GAME_DATA_OFFSET, SEEK_SET); 305 | if ((result = iomanX_read(PartFD, &HDLGameInfo, 1024)) == 1024) { 306 | if (stat != NULL) { 307 | GameSize = 0; 308 | for (i = 0; i < HDLGameInfo.num_partitions; i++) 309 | GameSize += HDLGameInfo.part_specs[i].part_size / 2048; 310 | 311 | memset(stat, 0, sizeof(iox_stat_t)); 312 | stat->attr = ((unsigned int)HDLGameInfo.dma_mode << 24) | ((unsigned int)HDLGameInfo.dma_type << 16) | ((unsigned int)HDLGameInfo.ops2l_compat_flags << 8) | HDLGameInfo.hdl_compat_flags; 313 | stat->size = GameSize; 314 | stat->private_0 = (HDLGameInfo.discType << 16) | HDLGameInfo.num_partitions; 315 | stat->private_1 = HDLGameInfo.layer1_start; 316 | stat->private_5 = 0x2000; /* The relative LBA of the start of the game's disc image in the first partition. */ 317 | } 318 | 319 | if (GameTitleOut != NULL) { 320 | memcpy(GameTitleOut, HDLGameInfo.gamename, (MaxTitleLen > sizeof(HDLGameInfo.gamename) ? sizeof(HDLGameInfo.gamename) : MaxTitleLen) - 1); 321 | GameTitleOut[MaxTitleLen - 1] = '\0'; 322 | } 323 | if (StartupPathOut != NULL) { 324 | strncpy(StartupPathOut, HDLGameInfo.startup, MaxStartupPathLen - 1); 325 | StartupPathOut[MaxStartupPathLen - 1] = '\0'; 326 | } 327 | 328 | result = 0; 329 | } else 330 | result = -EIO; 331 | 332 | SignalSema(FD_List[unit].SemaID); 333 | } else 334 | result = -ENODEV; 335 | 336 | return result; 337 | } 338 | 339 | static int hdlfs_dread(iomanX_iop_file_t *fd, iox_dirent_t *dirent) 340 | { 341 | return 0; 342 | } 343 | 344 | static int hdlfs_getstat(iomanX_iop_file_t *fd, const char *path, iox_stat_t *stat) 345 | { 346 | return hdlfs_getstat_filler(fd->unit, path, stat, NULL, 0, NULL, 0); 347 | } 348 | 349 | static int hdlfs_chstat(iomanX_iop_file_t *fd, const char *path, iox_stat_t *stat, unsigned int flags) 350 | { 351 | int PartFD, result; 352 | hdl_game_info HDLGameInfo; 353 | 354 | if (fd->unit < MAX_AVAILABLE_FDs && FD_List[fd->unit].MountFD >= 0) { 355 | PartFD = FD_List[fd->unit].MountFD; 356 | 357 | WaitSema(FD_List[fd->unit].SemaID); 358 | 359 | iomanX_lseek(PartFD, HDL_GAME_DATA_OFFSET, SEEK_SET); 360 | if ((result = iomanX_read(PartFD, &HDLGameInfo, 1024)) == 1024) { 361 | result = 0; 362 | if (!(flags & (FIO_CST_MODE | FIO_CST_SIZE | FIO_CST_CT | FIO_CST_AT | FIO_CST_MT))) { 363 | if (flags & FIO_CST_ATTR) { 364 | HDLGameInfo.dma_mode = stat->attr >> 24; 365 | HDLGameInfo.dma_type = (unsigned char)(stat->attr >> 16); 366 | HDLGameInfo.ops2l_compat_flags = (unsigned char)(stat->attr >> 8); 367 | HDLGameInfo.hdl_compat_flags = (unsigned char)stat->attr; 368 | } 369 | if (flags & FIO_CST_PRVT) { 370 | HDLGameInfo.discType = stat->private_0 >> 16; 371 | HDLGameInfo.layer1_start = stat->private_1; 372 | } 373 | 374 | iomanX_lseek(PartFD, HDL_GAME_DATA_OFFSET, SEEK_SET); 375 | if ((result = iomanX_write(PartFD, &HDLGameInfo, 1024)) != 1024) { 376 | result = -EIO; 377 | } 378 | } else 379 | result = -EINVAL; /* The contents of the mode, size and all time fields cannot be changed. */ 380 | } else 381 | result = -EIO; 382 | 383 | SignalSema(FD_List[fd->unit].SemaID); 384 | } else 385 | result = -ENODEV; 386 | 387 | return result; 388 | } 389 | 390 | /* Here, mount the specified partition/device. */ 391 | static int hdlfs_mount(iomanX_iop_file_t *fd, const char *mountpoint, const char *blockdev, int flags, void *arg, int arglen) 392 | { 393 | iox_stat_t stat; 394 | hdl_game_info HDLGameInfo; 395 | int result; 396 | struct HDLFS_FileDescriptor *MountDescriptor; 397 | unsigned int part; 398 | 399 | if (fd->unit < MAX_AVAILABLE_FDs) { 400 | if ((result = iomanX_getstat(blockdev, &stat)) >= 0) { 401 | if (stat.mode == HDL_FS_MAGIC) { 402 | MountDescriptor = &FD_List[fd->unit]; 403 | 404 | WaitSema(MountDescriptor->SemaID); 405 | 406 | if ((MountDescriptor->MountFD = iomanX_open(blockdev, flags == FIO_MT_RDWR ? FIO_O_RDWR : FIO_O_RDONLY, 0644)) >= 0) { 407 | iomanX_lseek(MountDescriptor->MountFD, HDL_GAME_DATA_OFFSET, SEEK_SET); 408 | if ((result = iomanX_read(MountDescriptor->MountFD, &HDLGameInfo, sizeof(HDLGameInfo))) == sizeof(HDLGameInfo)) { 409 | /* Calculate the size of all partitions combined. */ 410 | MountDescriptor->size = 0; 411 | MountDescriptor->NumPartitions = iomanX_ioctl2(MountDescriptor->MountFD, HIOCNSUB, NULL, 0, NULL, 0) + 1; 412 | for (part = 0; part < MountDescriptor->NumPartitions; part++) 413 | MountDescriptor->size += iomanX_ioctl2(MountDescriptor->MountFD, HIOCGETSIZE, &part, sizeof(part), NULL, 0); 414 | 415 | MountDescriptor->offset = MountDescriptor->CurrentPartNum = MountDescriptor->RelativeSectorNumber = 0; 416 | memcpy(MountDescriptor->part_specs, HDLGameInfo.part_specs, sizeof(MountDescriptor->part_specs)); 417 | result = fd->unit; 418 | } 419 | } else { 420 | result = MountDescriptor->MountFD; 421 | DEBUG_PRINTF("Failed to open partition: %s, result: %d\n", path, result); 422 | } 423 | 424 | SignalSema(MountDescriptor->SemaID); 425 | } else 426 | result = -EMFILE; 427 | } 428 | } else 429 | result = -ENODEV; 430 | 431 | return result; 432 | } 433 | 434 | static int unmount(unsigned int unit) 435 | { 436 | int result; 437 | 438 | if (unit < MAX_AVAILABLE_FDs && FD_List[unit].MountFD >= 0) { 439 | WaitSema(FD_List[unit].SemaID); 440 | 441 | iomanX_ioctl2(FD_List[unit].MountFD, HIOCFLUSH, NULL, 0, NULL, 0); 442 | iomanX_close(FD_List[unit].MountFD); 443 | FD_List[unit].MountFD = -1; 444 | 445 | SignalSema(FD_List[unit].SemaID); 446 | 447 | result = 0; 448 | } else 449 | result = -ENODEV; 450 | 451 | return result; 452 | } 453 | 454 | static int hdlfs_umount(iomanX_iop_file_t *fd, const char *mountpoint) 455 | { 456 | return unmount(fd->unit); 457 | } 458 | 459 | static inline int hdlfs_UpdateGameTitle(int unit, const char *NewName, unsigned int len) 460 | { 461 | int result, PartFD; 462 | hdl_game_info HDLGameInfo; 463 | 464 | if (len < sizeof(HDLGameInfo.gamename)) { 465 | PartFD = FD_List[unit].MountFD; 466 | 467 | iomanX_lseek(PartFD, HDL_GAME_DATA_OFFSET, SEEK_SET); 468 | if ((result = iomanX_read(PartFD, &HDLGameInfo, sizeof(hdl_game_info))) == sizeof(hdl_game_info)) { 469 | memcpy(HDLGameInfo.gamename, NewName, len); 470 | memset(&HDLGameInfo.gamename[len], 0, sizeof(HDLGameInfo.gamename) - len); 471 | iomanX_lseek(PartFD, HDL_GAME_DATA_OFFSET, SEEK_SET); 472 | result = (iomanX_write(PartFD, &HDLGameInfo, sizeof(hdl_game_info)) != sizeof(hdl_game_info)) ? -EIO : 0; 473 | } else 474 | result = -EIO; 475 | } else 476 | result = -EINVAL; 477 | 478 | return result; 479 | } 480 | 481 | static int hdlfs_devctl(iomanX_iop_file_t *fd, const char *path, int cmd, void *arg, unsigned int arglen, void *buf, unsigned int buflen) 482 | { 483 | int result; 484 | 485 | if (fd->unit < MAX_AVAILABLE_FDs && FD_List[fd->unit].MountFD >= 0) { 486 | switch (cmd) { 487 | case HDLFS_DEVCTL_GET_STARTUP_PATH: 488 | result = hdlfs_getstat_filler(fd->unit, path, NULL, NULL, 0, buf, buflen); 489 | break; 490 | case HDLFS_DEVCTL_GET_TITLE: 491 | result = hdlfs_getstat_filler(fd->unit, path, NULL, buf, buflen, NULL, 0); 492 | break; 493 | case HDLFS_DEVCTL_SET_TITLE: 494 | result = hdlfs_UpdateGameTitle(fd->unit, arg, arglen); 495 | break; 496 | default: 497 | result = -EINVAL; 498 | } 499 | } else 500 | result = -ENODEV; 501 | 502 | return result; 503 | } 504 | 505 | static int hdlfs_NulldevFunction(void) 506 | { 507 | return -EIO; 508 | } 509 | 510 | /* Device driver I/O functions */ 511 | static iomanX_iop_device_ops_t hdlfs_functarray = { 512 | &hdlfs_init, /* INIT */ 513 | &hdlfs_deinit, /* DEINIT */ 514 | &hdlfs_format, /* FORMAT */ 515 | &hdlfs_open, /* OPEN */ 516 | &hdlfs_close, /* CLOSE */ 517 | &hdlfs_read, /* READ */ 518 | &hdlfs_write, /* WRITE */ 519 | &hdlfs_lseek, /* LSEEK */ 520 | (void *)&hdlfs_NulldevFunction, /* IOCTL */ 521 | (void *)&hdlfs_NulldevFunction, /* REMOVE */ 522 | (void *)&hdlfs_NulldevFunction, /* MKDIR */ 523 | (void *)&hdlfs_NulldevFunction, /* RMDIR */ 524 | &hdlfs_dopen, /* DOPEN */ 525 | &hdlfs_dclose, /* DCLOSE */ 526 | &hdlfs_dread, /* DREAD */ 527 | &hdlfs_getstat, /* GETSTAT */ 528 | &hdlfs_chstat, /* CHSTAT */ 529 | (void *)&hdlfs_NulldevFunction, /* RENAME */ 530 | (void *)&hdlfs_NulldevFunction, /* CHDIR */ 531 | (void *)&hdlfs_NulldevFunction, /* SYNC */ 532 | &hdlfs_mount, /* MOUNT */ 533 | &hdlfs_umount, /* UMOUNT */ 534 | (void *)&hdlfs_NulldevFunction, /* LSEEK64 */ 535 | &hdlfs_devctl, /* DEVCTL */ 536 | (void *)&hdlfs_NulldevFunction, /* SYMLINK */ 537 | (void *)&hdlfs_NulldevFunction, /* READLINK */ 538 | (void *)&hdlfs_NulldevFunction /* IOCTL2 */ 539 | }; 540 | 541 | static const char hdlfs_dev_name[] = "hdl"; 542 | 543 | static iomanX_iop_device_t hdlfs_dev = { 544 | hdlfs_dev_name, /* Device name */ 545 | IOP_DT_FS | IOP_DT_FSEXT, /* Device type flag */ 546 | 1, /* Version */ 547 | "HDLoader filesystem driver", /* Description. */ 548 | &hdlfs_functarray /* Device driver function pointer array */ 549 | }; 550 | 551 | /* Entry point */ 552 | int __start(int argc, char **argv) 553 | { 554 | iomanX_DelDrv(hdlfs_dev_name); 555 | iomanX_AddDrv(&hdlfs_dev); 556 | 557 | return MODULE_RESIDENT_END; 558 | } 559 | -------------------------------------------------------------------------------- /src/pfs2tar.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #ifdef _WIN32 11 | #define timegm _mkgmtime 12 | #endif 13 | 14 | #include "iomanX_port.h" 15 | 16 | #define IOMANX_PATH_MAX 256 17 | #define IOMANX_MOUNT_POINT "pfs0:" 18 | 19 | static bool overwrite_mode = false; 20 | 21 | /* Mostly based on musl libc's nftw implementation 35e9831156efc1b54e1a91917ba0f787d5df3353 */ 22 | 23 | typedef struct path_info 24 | { 25 | int orig_len; 26 | const char *path_prefix; 27 | } path_info_t; 28 | 29 | typedef int (*wrapped_ftw_callback)(path_info_t *pi, const char *path, const iox_stat_t *st); 30 | 31 | static int do_wrapped_ftw(path_info_t *pi, char *path, wrapped_ftw_callback fn) 32 | { 33 | size_t l = strlen(path), j = l && path[l - 1] == '/' ? l - 1 : l; 34 | iox_stat_t st; 35 | int r; 36 | 37 | if (iomanX_getstat(path, &st) < 0) { 38 | return -1; 39 | } 40 | 41 | if ((r = fn(pi, path, &st))) { 42 | return r; 43 | } 44 | 45 | if (FIO_S_ISDIR(st.mode)) { 46 | int d = iomanX_dopen(path); 47 | if (d >= 0) { 48 | int result; 49 | iox_dirent_t de; 50 | 51 | while ((result = iomanX_dread(d, &de)) && result != -1) { 52 | if (de.name[0] == '.' && (!de.name[1] || (de.name[1] == '.' && !de.name[2]))) 53 | continue; 54 | if (strlen(de.name) >= IOMANX_PATH_MAX - l) { 55 | iomanX_close(d); 56 | return -1; 57 | } 58 | path[j] = '/'; 59 | strcpy(path + j + 1, de.name); 60 | printf("\r\033[K %s", path); 61 | fflush(stdout); 62 | if ((r = do_wrapped_ftw(pi, path, fn))) { 63 | iomanX_close(d); 64 | return r; 65 | } 66 | } 67 | printf("\r\033[K"); 68 | iomanX_close(d); 69 | } else { 70 | return -1; 71 | } 72 | } 73 | 74 | path[l] = 0; 75 | 76 | return 0; 77 | } 78 | 79 | static int wrapped_ftw(const char *path_prefix, const char *path, wrapped_ftw_callback fn) 80 | { 81 | int r, cs; 82 | size_t l; 83 | path_info_t pi; 84 | char pathbuf[IOMANX_PATH_MAX + 1]; 85 | 86 | l = strlen(path); 87 | if (l > IOMANX_PATH_MAX) { 88 | return -1; 89 | } 90 | memcpy(pathbuf, path, l + 1); 91 | 92 | pi.orig_len = l; 93 | pi.path_prefix = path_prefix; 94 | 95 | r = do_wrapped_ftw(&pi, pathbuf, fn); 96 | return r; 97 | } 98 | 99 | /* Mostly based on sltar code */ 100 | 101 | static time_t convert_iox_stat_time_to_posix(const unsigned char *iomanx_time) 102 | { 103 | struct tm timeinfo; 104 | timeinfo.tm_sec = iomanx_time[1]; 105 | timeinfo.tm_min = iomanx_time[2]; 106 | timeinfo.tm_hour = iomanx_time[3]; 107 | timeinfo.tm_mday = iomanx_time[4]; 108 | timeinfo.tm_mon = iomanx_time[5] - 1; // month 1 (January) is 0 109 | timeinfo.tm_year = (iomanx_time[6] | (iomanx_time[7] << 8)) - 1900; // year 1900 is 0 110 | time_t rawtime = timegm(&timeinfo); 111 | // convert UTC->JST 112 | rawtime += (9 * 60 * 60); 113 | return rawtime; 114 | } 115 | 116 | static unsigned int convert_mode_to_posix(unsigned int iomanx_mode) 117 | { 118 | unsigned int posix_mode = 0; 119 | if (FIO_S_ISDIR(iomanx_mode)) { 120 | posix_mode |= /* S_IFDIR */ 0040000; 121 | } 122 | if (FIO_S_ISREG(iomanx_mode)) { 123 | posix_mode |= /* S_IFREG */ 0100000; 124 | } 125 | if (FIO_S_ISLNK(iomanx_mode)) { 126 | posix_mode |= /* S_IFLNK */ 0120000; 127 | } 128 | if (iomanx_mode & FIO_S_IRUSR) { 129 | posix_mode |= /* S_IRUSR */ 0400; 130 | } 131 | if (iomanx_mode & FIO_S_IWUSR) { 132 | posix_mode |= /* S_IWUSR */ 0200; 133 | } 134 | if (iomanx_mode & FIO_S_IXUSR) { 135 | posix_mode |= /* S_IXUSR */ 0100; 136 | } 137 | if (iomanx_mode & FIO_S_IRGRP) { 138 | posix_mode |= /* S_IRGRP */ 0040; 139 | } 140 | if (iomanx_mode & FIO_S_IWGRP) { 141 | posix_mode |= /* S_IWGRP */ 0020; 142 | } 143 | if (iomanx_mode & FIO_S_IXGRP) { 144 | posix_mode |= /* S_IXGRP */ 0010; 145 | } 146 | if (iomanx_mode & FIO_S_IROTH) { 147 | posix_mode |= /* S_IROTH */ 0004; 148 | } 149 | if (iomanx_mode & FIO_S_IWOTH) { 150 | posix_mode |= /* S_IWOTH */ 0002; 151 | } 152 | if (iomanx_mode & FIO_S_IXOTH) { 153 | posix_mode |= /* S_IXOTH */ 0001; 154 | } 155 | if (iomanx_mode & FIO_S_ISUID) { 156 | posix_mode |= /* S_ISUID */ 04000; 157 | } 158 | if (iomanx_mode & FIO_S_ISGID) { 159 | posix_mode |= /* S_ISGID */ 02000; 160 | } 161 | if (iomanx_mode & FIO_S_ISVTX) { 162 | posix_mode |= /* S_ISVTX */ 01000; 163 | } 164 | return posix_mode; 165 | } 166 | 167 | enum TarHeader { 168 | NAME = 0, 169 | MODE = 100, 170 | SIZE = 124, 171 | MTIME = 136, 172 | CHK = 148, 173 | TYPE = 156, 174 | LINK = 157, 175 | MAGIC = 257, 176 | VERS = 263, 177 | NAME2 = 345, 178 | END = 512 179 | }; 180 | 181 | // Converts a POSIX time_t value to an 8-byte iomanX timestamp format 182 | static void convert_posix_time_to_iox_time(time_t posix_time, unsigned char *iomanx_time) 183 | { 184 | struct tm *tm_time; 185 | 186 | // Convert UTC to JST (revert +9h used in the forward function) 187 | posix_time -= 9 * 60 * 60; 188 | 189 | // Convert time_t to tm struct in UTC 190 | tm_time = gmtime(&posix_time); 191 | 192 | // Encode into iomanX format 193 | iomanx_time[0] = 0; // unused 194 | iomanx_time[1] = tm_time->tm_sec; 195 | iomanx_time[2] = tm_time->tm_min; 196 | iomanx_time[3] = tm_time->tm_hour; 197 | iomanx_time[4] = tm_time->tm_mday; 198 | iomanx_time[5] = tm_time->tm_mon + 1; // tm_mon is 0-based 199 | iomanx_time[6] = tm_time->tm_year + 1900; 200 | iomanx_time[7] = (tm_time->tm_year + 1900) >> 8; 201 | } 202 | 203 | static unsigned int convert_posix_mode_to_iomanx(unsigned int posix_mode) 204 | { 205 | unsigned int iomanx_mode = 0; 206 | 207 | if ((posix_mode & 0170000) == 0040000) 208 | iomanx_mode |= FIO_S_IFDIR; 209 | if ((posix_mode & 0170000) == 0100000) 210 | iomanx_mode |= FIO_S_IFREG; 211 | if ((posix_mode & 0170000) == 0120000) 212 | iomanx_mode |= FIO_S_IFLNK; 213 | 214 | if (posix_mode & 0400) 215 | iomanx_mode |= FIO_S_IRUSR; 216 | if (posix_mode & 0200) 217 | iomanx_mode |= FIO_S_IWUSR; 218 | if (posix_mode & 0100) 219 | iomanx_mode |= FIO_S_IXUSR; 220 | 221 | if (posix_mode & 0040) 222 | iomanx_mode |= FIO_S_IRGRP; 223 | if (posix_mode & 0020) 224 | iomanx_mode |= FIO_S_IWGRP; 225 | if (posix_mode & 0010) 226 | iomanx_mode |= FIO_S_IXGRP; 227 | 228 | if (posix_mode & 0004) 229 | iomanx_mode |= FIO_S_IROTH; 230 | if (posix_mode & 0002) 231 | iomanx_mode |= FIO_S_IWOTH; 232 | if (posix_mode & 0001) 233 | iomanx_mode |= FIO_S_IXOTH; 234 | 235 | if (posix_mode & 04000) 236 | iomanx_mode |= FIO_S_ISUID; 237 | if (posix_mode & 02000) 238 | iomanx_mode |= FIO_S_ISGID; 239 | if (posix_mode & 01000) 240 | iomanx_mode |= FIO_S_ISVTX; 241 | 242 | return iomanx_mode; 243 | } 244 | 245 | 246 | static void tar_checksum(const char b[END], char *chk) 247 | { 248 | unsigned sum = 0, i; 249 | for (i = 0; i < END; i++) 250 | sum += (i >= CHK && i < CHK + 8) ? ' ' : b[i]; 251 | snprintf(chk, 8, "%.7o", sum); 252 | } 253 | 254 | static FILE *tarfile_handle = NULL; 255 | 256 | static int tar_c_file(path_info_t *pi, const char *in_path, const iox_stat_t *st) 257 | { 258 | int l = END; 259 | char b[END] = {0}; 260 | int f = -1; 261 | int pathlen; 262 | char path[512]; 263 | 264 | /* TODO: pax header for longer filenames and larger files */ 265 | /* https://pubs.opengroup.org/onlinepubs/9699919799/utilities/pax.html#tag_20_92_13 */ 266 | 267 | if ((FIO_S_ISREG(st->mode)) && (st->hisize != 0)) { 268 | /* The file is over 4GB, which we don't support (currently) */ 269 | printf("(!) %s: too large file. Skipping.\n", in_path); // Print message for large file 270 | return 0; 271 | } 272 | 273 | snprintf(path, sizeof(path), "%s%s", pi->path_prefix, in_path + pi->orig_len); 274 | 275 | pathlen = strlen(path); 276 | 277 | if (pathlen == 0) { 278 | /* We don't need to archive the root */ 279 | return 0; 280 | } 281 | 282 | memset(b + SIZE, '0', 11); 283 | memcpy(b + MAGIC, "ustar\x00", 6); 284 | memcpy(b + VERS, "00", 2); 285 | if (pathlen > 100) { 286 | char *path_separate = strchr(path, '/'); 287 | 288 | if (path_separate == NULL) { 289 | /* Path is too long */ 290 | printf("(!) %s: path is too long. Skipping.\n", in_path); // Print message for long path 291 | return 0; 292 | } 293 | 294 | while ((path_separate - path) < sizeof(path)) { 295 | if (*path_separate == '\x00') { 296 | break; 297 | } 298 | char *new_path_separate = strchr(path_separate + 1, '/'); 299 | if (new_path_separate == NULL) { 300 | break; 301 | } 302 | if ((new_path_separate - path) >= 155) { 303 | break; 304 | } 305 | path_separate = new_path_separate; 306 | } 307 | 308 | if ((path_separate - path) >= 155) { 309 | /* Path is too long */ 310 | return 0; 311 | } 312 | 313 | { 314 | int prefix_pathlen = path_separate - path; 315 | 316 | memcpy(b + NAME2, path, prefix_pathlen); 317 | memcpy(b + NAME, path + prefix_pathlen, pathlen - prefix_pathlen); 318 | } 319 | 320 | } else { 321 | memcpy(b + NAME, path, pathlen); 322 | } 323 | snprintf(b + MODE, 8, "%.7o", (unsigned int)(convert_mode_to_posix(st->mode))); 324 | snprintf(b + MTIME, 12, "%.11o", (unsigned int)(convert_iox_stat_time_to_posix(st->mtime))); 325 | 326 | if (FIO_S_ISREG(st->mode)) { 327 | b[TYPE] = '0'; 328 | snprintf(b + SIZE, 12, "%.11o", (unsigned int)(st->size)); 329 | f = iomanX_open(in_path, FIO_O_RDONLY); 330 | } else if (FIO_S_ISDIR(st->mode)) { 331 | b[TYPE] = '5'; 332 | } else if (FIO_S_ISLNK(st->mode)) { 333 | b[TYPE] = '2'; 334 | iomanX_readlink(in_path, b + LINK, 100); 335 | } 336 | tar_checksum(b, b + CHK); 337 | do { 338 | if (l < END) { 339 | memset(b + l, 0, END - l); 340 | } 341 | fwrite(b, END, 1, tarfile_handle); 342 | } while ((f >= 0) && ((l = iomanX_read(f, b, END)) > 0)); 343 | iomanX_close(f); 344 | return 0; 345 | } 346 | 347 | static int tar_part(const char *arg) 348 | { 349 | int retval = 0; 350 | int dh = iomanX_dopen("hdd0:"); 351 | if (dh >= 0) { 352 | int result; 353 | iox_dirent_t de; 354 | while ((result = iomanX_dread(dh, &de)) && result != -1) { 355 | if (de.stat.mode == 0x0100 && de.stat.attr != 1) { 356 | printf("%s%s\n", "hdd0:", de.name); 357 | if (arg == NULL || !strcmp(de.name, arg)) { 358 | char mount_point[256]; 359 | char prefix_path[256]; 360 | 361 | snprintf(mount_point, sizeof(mount_point), "hdd0:%s", de.name); 362 | 363 | result = iomanX_mount(IOMANX_MOUNT_POINT, mount_point, FIO_MT_RDONLY, NULL, 0); 364 | if (result < 0) { 365 | fprintf(stderr, "(!) %s: %s.\n", mount_point, strerror(-result)); 366 | continue; 367 | } 368 | 369 | snprintf(prefix_path, sizeof(prefix_path), "%s/", de.name); 370 | wrapped_ftw(prefix_path, IOMANX_MOUNT_POINT "/", tar_c_file); 371 | 372 | iomanX_umount(IOMANX_MOUNT_POINT); 373 | } 374 | printf("\n"); 375 | } 376 | } 377 | 378 | result = iomanX_close(dh); 379 | if (result < 0) { 380 | printf("(!) dclose: failed with %d\n", result); 381 | retval = -1; 382 | } 383 | } else { 384 | printf("(!) dopen: failed with %d\n", dh); 385 | retval = dh; 386 | } 387 | return retval; 388 | } 389 | 390 | static void ensure_parent_dirs_exist(const char *path) 391 | { 392 | char tmp[IOMANX_PATH_MAX]; 393 | strncpy(tmp, path, sizeof(tmp)); 394 | tmp[sizeof(tmp) - 1] = '\0'; 395 | 396 | for (char *p = tmp + strlen(IOMANX_MOUNT_POINT) + 1; *p; p++) { 397 | if (*p == '/') { 398 | *p = '\0'; 399 | iomanX_mkdir(tmp, 0777); // Ignore errors 400 | *p = '/'; 401 | } 402 | } 403 | } 404 | 405 | static int restore_from_tar(const char *pfs_mount_path, const char *prefix_path) 406 | { 407 | char header[512]; 408 | 409 | size_t prefix_len = strlen(prefix_path); 410 | fseek(tarfile_handle, 0, SEEK_SET); 411 | 412 | while (fread(header, 1, 512, tarfile_handle) == 512) { 413 | if (header[0] == '\0') 414 | break; 415 | 416 | // Name 417 | char name[256] = {0}; 418 | strncpy(name, header + NAME, 100); 419 | if (name[0] == '\0') 420 | continue; 421 | 422 | 423 | // Type and size 424 | char size_str[13] = {0}; 425 | strncpy(size_str, header + SIZE, 12); 426 | unsigned int size = strtol(size_str, NULL, 8); 427 | char type = header[TYPE]; 428 | 429 | // Parse mtime from tar header 430 | char mtime_str[13] = {0}; 431 | strncpy(mtime_str, header + MTIME, 12); 432 | time_t mtime = strtol(mtime_str, NULL, 8); 433 | // Backup mode from tar header (octal format) 434 | char mode_str[8] = {0}; 435 | strncpy(mode_str, header + MODE, 7); 436 | unsigned int posix_mode = strtol(mode_str, NULL, 8); 437 | unsigned int iomanx_mode = convert_posix_mode_to_iomanx(posix_mode); 438 | 439 | // Check if file exists and skip if needed 440 | if (strncmp(name, prefix_path, prefix_len) != 0) { 441 | fseek(tarfile_handle, ((size + 511) / 512) * 512, SEEK_CUR); 442 | continue; 443 | } 444 | const char *rel_path = name + prefix_len; 445 | 446 | if (*rel_path == '\0') { 447 | fseek(tarfile_handle, ((size + 511) / 512) * 512, SEEK_CUR); 448 | continue; 449 | } 450 | // Full path = pfs0:/ + relative path 451 | char full_path[512]; 452 | snprintf(full_path, sizeof(full_path), "%s%s", pfs_mount_path, rel_path); 453 | 454 | if (!overwrite_mode) { 455 | iox_stat_t st; 456 | if (iomanX_getstat(full_path, &st) == 0) { 457 | printf("Skipping existing file: %s\n", name); 458 | fseek(tarfile_handle, ((size + 511) / 512) * 512, SEEK_CUR); 459 | continue; 460 | } 461 | } 462 | if (type == '5') { 463 | // Create directories 464 | ensure_parent_dirs_exist(full_path); 465 | 466 | iomanX_mkdir(full_path, 0777); 467 | 468 | // Convert to iomanX format 469 | iox_stat_t st_time = {0}; 470 | convert_posix_time_to_iox_time(mtime, st_time.mtime); 471 | memcpy(st_time.atime, st_time.mtime, sizeof(st_time.mtime)); 472 | memcpy(st_time.ctime, st_time.mtime, sizeof(st_time.mtime)); 473 | 474 | iox_stat_t st = {0}; 475 | st.mode = iomanx_mode; 476 | iomanX_chstat(full_path, &st, FIO_CST_MODE); 477 | // Set timestamps 478 | iomanX_chstat(full_path, &st_time, FIO_CST_MT | FIO_CST_AT | FIO_CST_CT); 479 | } else if (type == '2') { 480 | char link_target[100 + 1] = {0}; 481 | strncpy(link_target, header + LINK, 100); 482 | 483 | ensure_parent_dirs_exist(full_path); 484 | 485 | // Create symlink 486 | int res = iomanX_symlink(link_target, full_path); 487 | if (res < 0) { 488 | fprintf(stderr, "(!) Failed to create symlink: %s -> %s (%d)\n", full_path, link_target, res); 489 | continue; 490 | } 491 | printf("Restoring symlink: %s -> %s\n", full_path, link_target); 492 | } else if (type == '0' || type == '\0') { 493 | // Create directories 494 | ensure_parent_dirs_exist(full_path); 495 | 496 | int fd = iomanX_open(full_path, FIO_O_WRONLY | FIO_O_CREAT | FIO_O_TRUNC, 0666); 497 | 498 | if (fd < 0) { 499 | fprintf(stderr, "(!) Failed to create file: %s\n", full_path); 500 | fseek(tarfile_handle, ((size + 511) / 512) * 512, SEEK_CUR); 501 | continue; 502 | } 503 | 504 | // Read and write file content 505 | unsigned int remaining = size; 506 | while (remaining > 0) { 507 | char buf[512]; 508 | int chunk = remaining > 512 ? 512 : remaining; 509 | fread(buf, 1, chunk, tarfile_handle); 510 | iomanX_write(fd, buf, chunk); 511 | remaining -= chunk; 512 | } 513 | iomanX_close(fd); 514 | 515 | // Convert to iomanX format 516 | iox_stat_t st_time = {0}; 517 | convert_posix_time_to_iox_time(mtime, st_time.mtime); 518 | memcpy(st_time.atime, st_time.mtime, sizeof(st_time.mtime)); 519 | memcpy(st_time.ctime, st_time.mtime, sizeof(st_time.mtime)); 520 | iox_stat_t st = {0}; 521 | st.mode = iomanx_mode; 522 | iomanX_chstat(full_path, &st, FIO_CST_MODE); 523 | // Set timestamps 524 | iomanX_chstat(full_path, &st_time, FIO_CST_MT | FIO_CST_AT | FIO_CST_CT); 525 | printf("Restoring file: %s (type: %c, size: %u, mask: %o)\n", name, type, size, posix_mode); 526 | 527 | // Skip padding 528 | if (size % 512 != 0) 529 | fseek(tarfile_handle, 512 - (size % 512), SEEK_CUR); 530 | } else { 531 | // Unknown type 532 | fseek(tarfile_handle, ((size + 511) / 512) * 512, SEEK_CUR); 533 | } 534 | } 535 | 536 | return 0; 537 | } 538 | 539 | static int part_tar(const char *arg) 540 | { 541 | int retval = 0; 542 | int dh = iomanX_dopen("hdd0:"); 543 | if (dh >= 0) { 544 | int result; 545 | iox_dirent_t de; 546 | while ((result = iomanX_dread(dh, &de)) && result != -1) { 547 | if (de.stat.mode == 0x0100 && de.stat.attr != 1) { 548 | printf("(%s) %s\n", "hdd0:", de.name); 549 | if (arg == NULL || !strcmp(de.name, arg)) { 550 | char mount_point[256]; 551 | char prefix_path[256]; 552 | 553 | snprintf(mount_point, sizeof(mount_point), "hdd0:%s", de.name); 554 | 555 | result = iomanX_mount(IOMANX_MOUNT_POINT, mount_point, FIO_MT_RDWR, NULL, 0); 556 | if (result < 0) { 557 | fprintf(stderr, "(!) %s: %s.\n", mount_point, strerror(-result)); 558 | continue; 559 | } 560 | 561 | snprintf(prefix_path, sizeof(prefix_path), "%s/", de.name); 562 | restore_from_tar(IOMANX_MOUNT_POINT "/", prefix_path); 563 | 564 | iomanX_umount(IOMANX_MOUNT_POINT); 565 | } 566 | } 567 | } 568 | 569 | result = iomanX_close(dh); 570 | if (result < 0) { 571 | printf("(!) dclose: failed with %d\n", result); 572 | retval = -1; 573 | } 574 | } else { 575 | printf("(!) dopen: failed with %d\n", dh); 576 | retval = dh; 577 | } 578 | return retval; 579 | } 580 | 581 | static void show_help(const char *progname) 582 | { 583 | printf("usage: %s --backup [--partition ] []\n", progname); 584 | printf("usage: %s --restore --partition [--overwrite]\n", progname); 585 | } 586 | 587 | /* where (image of) PS2 HDD is; in fake_sdk/atad.c */ 588 | extern void set_atad_device_path(const char *path); 589 | 590 | extern void atad_close(void); /* fake_sdk/atad.c */ 591 | 592 | int main(int argc, char *argv[]) 593 | { 594 | int result; 595 | bool backup_mode = false; 596 | 597 | if (argc < 3) { 598 | show_help(argv[0]); 599 | return 1; 600 | } 601 | 602 | if (strcmp(argv[1], "--backup") == 0) 603 | backup_mode = true; 604 | else if (strcmp(argv[1], "--restore") == 0) 605 | backup_mode = false; 606 | else { 607 | show_help(argv[0]); 608 | return 1; 609 | } 610 | 611 | for (int i = 1; i < argc; ++i) { 612 | if (strcmp(argv[i], "--overwrite") == 0) { 613 | overwrite_mode = true; 614 | } 615 | } 616 | 617 | // Get the input HDD image path 618 | const char *hdd_path = argv[2]; 619 | const char *last_slash = strrchr(hdd_path, '/'); 620 | const char *filename = last_slash ? last_slash + 1 : hdd_path; 621 | 622 | // Find the last dot for extension 623 | const char *last_dot = strrchr(filename, '.'); 624 | size_t base_len = last_dot ? (size_t)(last_dot - filename) : strlen(filename); 625 | char tar_filename[1024]; 626 | const char *partition_name = NULL; 627 | if (argc >= 4) { 628 | if (strcmp(argv[3], "--partition") == 0) { 629 | if (argc < 5) { 630 | fprintf(stderr, "(!) Missing partition name.\n"); 631 | show_help(argv[0]); 632 | return 1; 633 | } 634 | partition_name = argv[4]; 635 | if (argc > 5) 636 | snprintf(tar_filename, sizeof(tar_filename), "%s", argv[5]); 637 | else 638 | snprintf(tar_filename, sizeof(tar_filename), "%s_%.*s.tar", partition_name, (int)base_len, filename); 639 | } else { 640 | snprintf(tar_filename, sizeof(tar_filename), "%s", argv[3]); 641 | } 642 | } else 643 | snprintf(tar_filename, sizeof(tar_filename), "%.*s.tar", (int)base_len, filename); 644 | 645 | // Check if tar file already exists for backup mode 646 | if (backup_mode) { 647 | FILE *test_file = fopen(tar_filename, "rb"); 648 | if (test_file != NULL) { 649 | fclose(test_file); 650 | fprintf(stderr, "(!) %s already exists. Aborting.\n", tar_filename); 651 | return 1; 652 | } 653 | } 654 | 655 | set_atad_device_path(argv[2]); 656 | static const char *apa_args[] = 657 | { 658 | "ps2hdd.irx", 659 | "-o", "2", 660 | NULL}; 661 | 662 | /* mandatory */ 663 | result = _init_apa(3, (char **)apa_args); 664 | if (result < 0) { 665 | fprintf(stderr, "(!) init_apa: failed with %d (%s)\n", result, 666 | strerror(-result)); 667 | return 1; 668 | } 669 | 670 | static const char *pfs_args[] = 671 | { 672 | "pfs.irx", 673 | "-m", "1", 674 | "-o", "32", 675 | "-n", "127", 676 | NULL}; 677 | 678 | /* mandatory */ 679 | result = _init_pfs(7, (char **)pfs_args); 680 | if (result < 0) { 681 | fprintf(stderr, "(!) init_pfs: failed with %d (%s)\n", result, 682 | strerror(-result)); 683 | return 1; 684 | } 685 | 686 | if (backup_mode) 687 | tarfile_handle = fopen(tar_filename, "wb"); 688 | else 689 | tarfile_handle = fopen(tar_filename, "rb"); 690 | if (tarfile_handle == NULL) { 691 | fprintf(stderr, "(!) %s: %s.\n", tar_filename, strerror(errno)); 692 | return 1; 693 | } 694 | if (backup_mode) { 695 | printf("Backing up from %s to %s\n", hdd_path, tar_filename); 696 | tar_part(partition_name); 697 | char zero_block[512] = {0}; 698 | fwrite(zero_block, 1, 512, tarfile_handle); 699 | fwrite(zero_block, 1, 512, tarfile_handle); 700 | } else { 701 | printf("Restoring from %s to %s\n", tar_filename, hdd_path); 702 | part_tar(partition_name); 703 | } 704 | 705 | fclose(tarfile_handle); 706 | 707 | // Check if the tar file is empty (no files dumped) 708 | FILE *check_file = fopen(tar_filename, "rb"); 709 | if (check_file != NULL) { 710 | fseek(check_file, 0, SEEK_END); 711 | long size = ftell(check_file); 712 | fclose(check_file); 713 | if (size <= 1024) { // If the file is less than 1KB, consider it empty 714 | remove(tar_filename); 715 | printf("Tar file empty, tar file removed: %s\n", tar_filename); 716 | } 717 | } 718 | 719 | atad_close(); 720 | 721 | return 0; 722 | } 723 | --------------------------------------------------------------------------------