2020-12-05 13:08:24 +01:00
# include "stdafx.h"
2019-03-08 17:07:14 +01:00
# include "sys_sync.h"
2015-03-12 20:02:02 +01:00
# include "sys_fs.h"
2022-06-11 14:12:42 +02:00
# include "sys_memory.h"
2025-02-11 03:00:37 +01:00
# include "util/asm.hpp"
2015-03-12 20:02:02 +01:00
2017-03-07 01:59:05 +01:00
# include "Emu/Cell/PPUThread.h"
# include "Crypto/unedat.h"
2020-10-30 21:26:22 +01:00
# include "Emu/System.h"
2023-04-26 19:21:26 +02:00
# include "Emu/system_config.h"
2016-06-02 17:16:01 +02:00
# include "Emu/VFS.h"
2022-05-08 00:53:04 +02:00
# include "Emu/vfs_config.h"
2017-02-04 16:09:02 +01:00
# include "Emu/IdManager.h"
2022-10-27 22:53:12 +02:00
# include "Emu/system_utils.hpp"
# include "Emu/Cell/lv2/sys_process.h"
2016-06-02 17:16:01 +02:00
2023-06-25 04:09:08 +02:00
# include <filesystem>
2022-07-04 15:02:17 +02:00
# include <span>
2023-10-02 17:23:21 +02:00
# include <shared_mutex>
2022-05-08 00:53:04 +02:00
2018-08-25 14:39:00 +02:00
LOG_CHANNEL ( sys_fs ) ;
2015-03-12 20:02:02 +01:00
2022-11-13 15:15:46 +01:00
lv2_fs_mount_point g_mp_sys_dev_usb { " /dev_usb " , " CELL_FS_FAT " , " CELL_FS_IOS:USB_MASS_STORAGE " , 512 , 0x100 , 4096 , lv2_mp_flag : : no_uid_gid } ;
2022-12-02 05:40:39 +01:00
lv2_fs_mount_point g_mp_sys_dev_dvd { " /dev_ps2disc " , " CELL_FS_ISO9660 " , " CELL_FS_IOS:PATA1_BDVD_DRIVE " , 2048 , 0x100 , 32768 , lv2_mp_flag : : read_only + lv2_mp_flag : : no_uid_gid , & g_mp_sys_dev_usb } ;
2024-12-04 15:27:55 +01:00
lv2_fs_mount_point g_mp_sys_dev_bdvd { " /dev_bdvd " , " CELL_FS_ISO9660 " , " CELL_FS_IOS:PATA0_BDVD_DRIVE " , 2048 , 0x4D955 , 2048 , lv2_mp_flag : : read_only + lv2_mp_flag : : no_uid_gid , & g_mp_sys_dev_dvd } ;
2022-11-18 16:18:16 +01:00
lv2_fs_mount_point g_mp_sys_dev_hdd1 { " /dev_hdd1 " , " CELL_FS_FAT " , " CELL_FS_UTILITY:HDD1 " , 512 , 0x3FFFF8 , 32768 , lv2_mp_flag : : no_uid_gid + lv2_mp_flag : : cache , & g_mp_sys_dev_bdvd } ;
lv2_fs_mount_point g_mp_sys_dev_hdd0 { " /dev_hdd0 " , " CELL_FS_UFS " , " CELL_FS_UTILITY:HDD0 " , 512 , 0x24FAEA98 , 4096 , { } , & g_mp_sys_dev_hdd1 } ;
2023-05-25 08:31:17 +02:00
lv2_fs_mount_point g_mp_sys_dev_flash3 { " /dev_flash3 " , " CELL_FS_FAT " , " CELL_FS_IOS:BUILTIN_FLSH3 " , 512 , 0x400 , 8192 , lv2_mp_flag : : no_uid_gid , & g_mp_sys_dev_hdd0 } ; // TODO confirm
2022-11-18 16:18:16 +01:00
lv2_fs_mount_point g_mp_sys_dev_flash2 { " /dev_flash2 " , " CELL_FS_FAT " , " CELL_FS_IOS:BUILTIN_FLSH2 " , 512 , 0x8000 , 8192 , lv2_mp_flag : : no_uid_gid , & g_mp_sys_dev_flash3 } ; // TODO confirm
2023-05-25 08:31:17 +02:00
lv2_fs_mount_point g_mp_sys_dev_flash { " /dev_flash " , " CELL_FS_FAT " , " CELL_FS_IOS:BUILTIN_FLSH1 " , 512 , 0x63E00 , 8192 , lv2_mp_flag : : no_uid_gid , & g_mp_sys_dev_flash2 } ;
2022-11-18 16:18:16 +01:00
lv2_fs_mount_point g_mp_sys_host_root { " /host_root " , " CELL_FS_DUMMYFS " , " CELL_FS_DUMMY:/ " , 512 , 0x100 , 512 , lv2_mp_flag : : strict_get_block_size + lv2_mp_flag : : no_uid_gid , & g_mp_sys_dev_flash } ;
lv2_fs_mount_point g_mp_sys_app_home { " /app_home " , " CELL_FS_DUMMYFS " , " CELL_FS_DUMMY: " , 512 , 0x100 , 512 , lv2_mp_flag : : strict_get_block_size + lv2_mp_flag : : no_uid_gid , & g_mp_sys_host_root } ;
2022-12-02 05:40:39 +01:00
lv2_fs_mount_point g_mp_sys_dev_root { " / " , " CELL_FS_ADMINFS " , " CELL_FS_ADMINFS: " , 512 , 0x100 , 512 , lv2_mp_flag : : read_only + lv2_mp_flag : : strict_get_block_size + lv2_mp_flag : : no_uid_gid , & g_mp_sys_app_home } ;
2022-12-03 15:42:51 +01:00
lv2_fs_mount_point g_mp_sys_no_device { } ;
2023-05-25 08:31:17 +02:00
lv2_fs_mount_info g_mi_sys_not_found { } ; // wrapper for &g_mp_sys_no_device
2022-11-18 16:18:16 +01:00
2021-09-21 11:49:59 +02:00
template < >
void fmt_class_string < lv2_file_type > : : format ( std : : string & out , u64 arg )
{
format_enum ( out , arg , [ ] ( lv2_file_type type )
{
switch ( type )
{
case lv2_file_type : : regular : return " Regular file " ;
case lv2_file_type : : sdata : return " SDATA " ;
case lv2_file_type : : edata : return " EDATA " ;
}
return unknown ;
} ) ;
}
2021-06-24 15:47:14 +02:00
template < >
void fmt_class_string < lv2_file > : : format ( std : : string & out , u64 arg )
{
const auto & file = get_object ( arg ) ;
auto get_size = [ ] ( u64 size ) - > std : : string
{
if ( size = = umax )
{
return " N/A " ;
}
2021-11-14 00:15:27 +01:00
std : : string size_str ;
2021-06-24 15:47:14 +02:00
switch ( std : : bit_width ( size ) / 10 * 10 )
{
2021-11-14 00:15:27 +01:00
case 0 : fmt : : append ( size_str , " %u " , size ) ; break ;
case 10 : fmt : : append ( size_str , " %gKB " , size / 1024. ) ; break ;
case 20 : fmt : : append ( size_str , " %gMB " , size / ( 1024. * 1024 ) ) ; break ;
2021-06-24 15:47:14 +02:00
default :
2021-11-14 00:15:27 +01:00
case 30 : fmt : : append ( size_str , " %gGB " , size / ( 1024. * 1024 * 1024 ) ) ; break ;
2021-06-24 15:47:14 +02:00
}
2022-06-24 17:58:52 +02:00
2021-06-24 15:47:14 +02:00
return size_str ;
} ;
const usz pos = file . file ? file . file . pos ( ) : umax ;
const usz size = file . file ? file . file . size ( ) : umax ;
2024-12-23 02:06:49 +01:00
fmt : : append ( out , u8 " %s, '%s', Mode: 0x%x, Flags: 0x%x, Pos/Size: %s/%s (0x%x/0x%x) " , file . type , file . name . data ( ) , file . mode , file . flags , get_size ( pos ) , get_size ( size ) , pos , size ) ;
2021-06-24 15:47:14 +02:00
}
template < >
void fmt_class_string < lv2_dir > : : format ( std : : string & out , u64 arg )
{
const auto & dir = get_object ( arg ) ;
2024-12-23 02:06:49 +01:00
fmt : : append ( out , u8 " Directory, '%s', Entries: %u/%u " , dir . name . data ( ) , std : : min < u64 > ( dir . pos , dir . entries . size ( ) ) , dir . entries . size ( ) ) ;
2021-06-24 15:47:14 +02:00
}
2024-02-08 06:49:46 +01:00
bool has_fs_write_rights ( std : : string_view vpath )
{
// VSH has access to everything
if ( g_ps3_process_info . has_root_perm ( ) )
return true ;
const auto norm_vpath = lv2_fs_object : : get_normalized_path ( vpath ) ;
const auto parent_dir = fs : : get_parent_dir_view ( norm_vpath ) ;
// This is not exhaustive, PS3 has a unix filesystem with rights for each directory and files
// This is mostly meant to protect against games doing insane things(ie NPUB30003 => NPUB30008)
if ( parent_dir = = " /dev_hdd0 " | | parent_dir = = " /dev_hdd0/game " )
return false ;
return true ;
}
2020-03-06 13:47:24 +01:00
bool verify_mself ( const fs : : file & mself_file )
2017-03-07 01:59:05 +01:00
{
FsMselfHeader mself_header ;
if ( ! mself_file . read < FsMselfHeader > ( mself_header ) )
{
sys_fs . error ( " verify_mself: Didn't read expected bytes for header. " ) ;
return false ;
}
2020-02-19 16:26:41 +01:00
if ( mself_header . m_magic ! = 0x4D534600u )
2017-03-07 01:59:05 +01:00
{
sys_fs . error ( " verify_mself: Header magic is incorrect. " ) ;
return false ;
}
2020-02-19 16:26:41 +01:00
if ( mself_header . m_format_version ! = 1u )
2017-03-07 01:59:05 +01:00
{
sys_fs . error ( " verify_mself: Unexpected header format version. " ) ;
return false ;
}
// sanity check
if ( mself_header . m_entry_size ! = sizeof ( FsMselfEntry ) )
{
sys_fs . error ( " verify_mself: Unexpected header entry size. " ) ;
return false ;
}
2017-12-23 20:18:55 +01:00
mself_file . seek ( 0 ) ;
2017-03-07 01:59:05 +01:00
return true ;
}
2023-05-01 15:08:09 +02:00
lv2_fs_mount_info_map : : lv2_fs_mount_info_map ( )
{
for ( auto mp = & g_mp_sys_dev_root ; mp ; mp = mp - > next ) // Scan and keep track of pre-mounted devices
{
if ( mp = = & g_mp_sys_dev_usb )
{
for ( int i = 0 ; i < 8 ; i + + )
{
if ( ! vfs : : get ( fmt : : format ( " %s%03d " , mp - > root , i ) ) . empty ( ) )
{
2023-05-25 08:31:17 +02:00
add ( fmt : : format ( " %s%03d " , mp - > root , i ) , mp , fmt : : format ( " %s%03d " , mp - > device , i ) , mp - > file_system , false ) ;
2023-05-01 15:08:09 +02:00
}
}
}
else if ( mp = = & g_mp_sys_dev_root | | ! vfs : : get ( mp - > root ) . empty ( ) )
{
2023-05-25 08:31:17 +02:00
add ( std : : string ( mp - > root ) , mp , mp - > device , mp - > file_system , mp = = & g_mp_sys_dev_flash ) ; // /dev_flash is mounted in read only mode initially
2023-05-01 15:08:09 +02:00
}
}
}
lv2_fs_mount_info_map : : ~ lv2_fs_mount_info_map ( )
{
for ( const auto & [ path , info ] : map )
2023-06-05 22:31:58 +02:00
vfs_unmount ( path , false ) ; // Do not remove the value from the map we are iterating over.
2023-05-01 15:08:09 +02:00
}
bool lv2_fs_mount_info_map : : remove ( std : : string_view path )
{
if ( const auto iterator = map . find ( path ) ; iterator ! = map . end ( ) )
{
map . erase ( iterator ) ;
return true ;
}
return false ;
}
2023-12-27 04:59:41 +01:00
const lv2_fs_mount_info & lv2_fs_mount_info_map : : lookup ( std : : string_view path , bool no_cell_fs_path , std : : string * mount_path ) const
2023-05-01 15:08:09 +02:00
{
2023-05-25 08:31:17 +02:00
if ( path . starts_with ( " / " sv ) )
{
constexpr std : : string_view cell_fs_path = " CELL_FS_PATH: " sv ;
2023-06-25 04:09:08 +02:00
const std : : string normalized_path = lv2_fs_object : : get_normalized_path ( path ) ;
std : : string_view parent_dir ;
2023-05-25 08:31:17 +02:00
u32 parent_level = 0 ;
do
{
2023-06-25 04:09:08 +02:00
parent_dir = fs : : get_parent_dir_view ( normalized_path , parent_level + + ) ;
if ( const auto iterator = map . find ( parent_dir ) ; iterator ! = map . end ( ) )
2023-05-25 08:31:17 +02:00
{
if ( iterator - > second = = & g_mp_sys_dev_root & & parent_level > 1 )
break ;
if ( no_cell_fs_path & & iterator - > second . device . starts_with ( cell_fs_path ) )
2023-12-27 04:59:41 +01:00
return lookup ( iterator - > second . device . substr ( cell_fs_path . size ( ) ) , no_cell_fs_path , mount_path ) ; // Recursively look up the parent mount info
if ( mount_path )
* mount_path = iterator - > first ;
2023-05-25 08:31:17 +02:00
return iterator - > second ;
}
2023-06-25 04:09:08 +02:00
} while ( parent_dir . length ( ) > 1 ) ; // Exit the loop when parent_dir == "/" or empty
2023-05-25 08:31:17 +02:00
}
return g_mi_sys_not_found ;
2023-05-01 15:08:09 +02:00
}
u64 lv2_fs_mount_info_map : : get_all ( CellFsMountInfo * info , u64 len ) const
{
if ( ! info )
return map . size ( ) ;
u64 count = 0 ;
2024-01-08 00:52:28 +01:00
for ( const auto & [ path , mount_info ] : map )
2023-05-01 15:08:09 +02:00
{
if ( count > = len )
2024-01-08 00:52:28 +01:00
break ;
2023-05-01 15:08:09 +02:00
2024-01-08 00:52:28 +01:00
strcpy_trunc ( info [ count ] . mount_path , path ) ;
strcpy_trunc ( info [ count ] . filesystem , mount_info . file_system ) ;
strcpy_trunc ( info [ count ] . dev_name , mount_info . device ) ;
if ( mount_info . read_only )
info [ count ] . unk [ 4 ] | = 0x10000000 ;
2023-05-01 15:08:09 +02:00
2024-01-08 00:52:28 +01:00
count + + ;
2023-05-01 15:08:09 +02:00
}
return count ;
}
2024-01-08 00:52:28 +01:00
bool lv2_fs_mount_info_map : : is_device_mounted ( std : : string_view device_name ) const
{
return std : : any_of ( map . begin ( ) , map . end ( ) , [ & ] ( const decltype ( map ) : : value_type & info ) { return info . second . device = = device_name ; } ) ;
}
2023-06-05 22:31:58 +02:00
bool lv2_fs_mount_info_map : : vfs_unmount ( std : : string_view vpath , bool remove_from_map )
2016-06-02 17:16:01 +02:00
{
2023-05-25 08:31:17 +02:00
const std : : string local_path = vfs : : get ( vpath ) ;
2020-01-04 21:12:46 +01:00
2023-05-25 08:31:17 +02:00
if ( local_path . empty ( ) )
return false ;
2020-01-04 21:12:46 +01:00
2023-05-25 08:31:17 +02:00
if ( fs : : is_file ( local_path ) )
{
if ( fs : : remove_file ( local_path ) )
2020-10-01 21:07:38 +02:00
{
2023-05-25 08:31:17 +02:00
sys_fs . notice ( " Removed simplefs file \" %s \" " , local_path ) ;
2020-10-01 21:07:38 +02:00
}
2023-05-25 08:31:17 +02:00
else
2020-10-01 21:07:38 +02:00
{
2023-05-25 08:31:17 +02:00
sys_fs . error ( " Failed to remove simplefs file \" %s \" " , local_path ) ;
2020-10-01 21:07:38 +02:00
}
2023-05-25 08:31:17 +02:00
}
2020-10-01 21:07:38 +02:00
2023-06-05 22:31:58 +02:00
const bool result = vfs : : unmount ( vpath ) ;
2020-10-01 21:07:38 +02:00
2023-06-05 22:31:58 +02:00
if ( result & & remove_from_map )
2023-05-25 08:31:17 +02:00
g_fxo - > get < lv2_fs_mount_info_map > ( ) . remove ( vpath ) ;
2020-10-01 21:07:38 +02:00
2023-05-25 08:31:17 +02:00
return result ;
}
2020-10-01 21:07:38 +02:00
2023-06-25 04:09:08 +02:00
std : : string lv2_fs_object : : get_normalized_path ( std : : string_view path )
2023-05-25 08:31:17 +02:00
{
2023-07-20 00:25:32 +02:00
std : : string normalized_path = std : : filesystem : : path ( path ) . lexically_normal ( ) . string ( ) ;
2020-10-01 21:07:38 +02:00
2023-06-25 04:09:08 +02:00
# ifdef _WIN32
std : : replace ( normalized_path . begin ( ) , normalized_path . end ( ) , ' \\ ' , ' / ' ) ;
# endif
if ( normalized_path . ends_with ( ' / ' ) )
normalized_path . pop_back ( ) ;
return normalized_path . empty ( ) ? " / " : normalized_path ;
}
2024-01-08 00:52:28 +01:00
std : : string lv2_fs_object : : get_device_root ( std : : string_view filename )
2023-06-25 04:09:08 +02:00
{
2024-01-08 00:52:28 +01:00
std : : string path = get_normalized_path ( filename ) ; // Prevent getting fooled by ".." trick such as "/dev_usb000/../dev_flash"
2023-06-25 04:09:08 +02:00
if ( const auto first = path . find_first_not_of ( " / " sv ) ; first ! = umax )
{
if ( const auto pos = path . substr ( first ) . find_first_of ( " / " sv ) ; pos ! = umax )
path = path . substr ( 0 , first + pos ) ;
2024-01-08 00:52:28 +01:00
path = path . substr ( std : : max < std : : make_signed_t < usz > > ( 0 , first - 1 ) ) ; // Remove duplicate leading '/' while keeping only one
2023-06-25 04:09:08 +02:00
}
else
{
path = path . substr ( 0 , 1 ) ;
2020-10-01 21:07:38 +02:00
}
2023-06-25 04:09:08 +02:00
return path ;
2022-10-27 22:53:12 +02:00
}
2022-12-08 05:46:09 +01:00
lv2_fs_mount_point * lv2_fs_object : : get_mp ( std : : string_view filename , std : : string * vfs_path )
2022-10-27 22:53:12 +02:00
{
2023-04-30 18:07:27 +02:00
constexpr std : : string_view cell_fs_path = " CELL_FS_PATH: " sv ;
const bool is_cell_fs_path = filename . starts_with ( cell_fs_path ) ;
2022-10-27 22:53:12 +02:00
2023-04-30 18:07:27 +02:00
if ( is_cell_fs_path )
filename . remove_prefix ( cell_fs_path . size ( ) ) ;
2023-06-25 04:09:08 +02:00
const bool is_path = filename . starts_with ( " / " sv ) ;
2024-01-08 00:52:28 +01:00
std : : string mp_name = is_path ? get_device_root ( filename ) : std : : string ( filename ) ;
2023-04-30 18:07:27 +02:00
const auto check_mp = [ & ] ( )
2022-12-03 15:42:51 +01:00
{
2022-12-08 05:46:09 +01:00
for ( auto mp = & g_mp_sys_dev_root ; mp ; mp = mp - > next )
2022-12-03 15:42:51 +01:00
{
2023-04-30 18:07:27 +02:00
const auto & device_alias_check = ! is_path & & (
2023-05-01 15:08:09 +02:00
( mp = = & g_mp_sys_dev_hdd0 & & mp_name = = " CELL_FS_IOS:PATA0_HDD_DRIVE " sv ) | |
( mp = = & g_mp_sys_dev_hdd1 & & mp_name = = " CELL_FS_IOS:PATA1_HDD_DRIVE " sv ) | |
( mp = = & g_mp_sys_dev_flash2 & & mp_name = = " CELL_FS_IOS:BUILTIN_FLASH " sv ) ) ; // TODO confirm
2022-12-08 05:46:09 +01:00
if ( mp = = & g_mp_sys_dev_usb )
2022-12-03 15:42:51 +01:00
{
2023-12-27 04:59:41 +01:00
if ( mp_name . starts_with ( is_path ? mp - > root : mp - > device ) )
2022-12-03 15:42:51 +01:00
{
2023-12-27 04:59:41 +01:00
if ( ! is_path )
mp_name = fmt : : format ( " %s%s " , mp - > root , mp_name . substr ( mp - > device . size ( ) ) ) ;
return mp ;
2022-12-03 15:42:51 +01:00
}
}
2023-12-27 04:59:41 +01:00
else if ( ( is_path ? mp - > root : mp - > device ) = = mp_name | | device_alias_check )
2022-12-08 05:46:09 +01:00
{
2023-04-30 18:07:27 +02:00
if ( ! is_path )
2023-12-27 04:59:41 +01:00
mp_name = mp - > root ;
2023-04-30 18:07:27 +02:00
return mp ;
2022-12-08 05:46:09 +01:00
}
2022-12-03 15:42:51 +01:00
}
2023-04-30 18:07:27 +02:00
return & g_mp_sys_no_device ; // Default fallback
} ;
const auto result = check_mp ( ) ;
2020-01-04 21:12:46 +01:00
2022-12-08 05:46:09 +01:00
if ( vfs_path )
{
2023-05-01 15:08:09 +02:00
if ( is_cell_fs_path )
* vfs_path = vfs : : get ( filename ) ;
else if ( result = = & g_mp_sys_dev_hdd0 )
2022-12-08 05:46:09 +01:00
* vfs_path = g_cfg_vfs . get ( g_cfg_vfs . dev_hdd0 , rpcs3 : : utils : : get_emu_dir ( ) ) ;
else if ( result = = & g_mp_sys_dev_hdd1 )
* vfs_path = g_cfg_vfs . get ( g_cfg_vfs . dev_hdd1 , rpcs3 : : utils : : get_emu_dir ( ) ) ;
else if ( result = = & g_mp_sys_dev_usb )
2023-12-27 04:59:41 +01:00
* vfs_path = g_cfg_vfs . get_device ( g_cfg_vfs . dev_usb , mp_name , rpcs3 : : utils : : get_emu_dir ( ) ) . path ;
2022-12-08 05:46:09 +01:00
else if ( result = = & g_mp_sys_dev_bdvd )
* vfs_path = g_cfg_vfs . get ( g_cfg_vfs . dev_bdvd , rpcs3 : : utils : : get_emu_dir ( ) ) ;
else if ( result = = & g_mp_sys_dev_dvd )
2023-04-30 18:07:27 +02:00
* vfs_path = g_cfg_vfs . get ( g_cfg_vfs . dev_bdvd , rpcs3 : : utils : : get_emu_dir ( ) ) ; // For compatibility
2022-12-08 05:46:09 +01:00
else if ( result = = & g_mp_sys_app_home )
* vfs_path = g_cfg_vfs . get ( g_cfg_vfs . app_home , rpcs3 : : utils : : get_emu_dir ( ) ) ;
2023-04-30 18:07:27 +02:00
else if ( result = = & g_mp_sys_host_root & & g_cfg . vfs . host_root )
* vfs_path = " / " ;
2022-12-08 05:46:09 +01:00
else if ( result = = & g_mp_sys_dev_flash )
* vfs_path = g_cfg_vfs . get_dev_flash ( ) ;
else if ( result = = & g_mp_sys_dev_flash2 )
* vfs_path = g_cfg_vfs . get_dev_flash2 ( ) ;
else if ( result = = & g_mp_sys_dev_flash3 )
* vfs_path = g_cfg_vfs . get_dev_flash3 ( ) ;
else
* vfs_path = { } ;
2023-03-18 02:48:07 +01:00
2023-05-01 15:08:09 +02:00
if ( is_path & & ! is_cell_fs_path & & ! vfs_path - > empty ( ) )
2023-12-27 04:59:41 +01:00
vfs_path - > append ( filename . substr ( mp_name . size ( ) ) ) ;
2022-12-08 05:46:09 +01:00
}
2022-10-27 22:53:12 +02:00
2022-12-08 05:46:09 +01:00
return result ;
2022-10-27 22:53:12 +02:00
}
2023-05-25 08:31:17 +02:00
lv2_fs_object : : lv2_fs_object ( std : : string_view filename )
: name ( get_name ( filename ) )
, mp ( g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( name . data ( ) ) )
2022-11-13 15:15:46 +01:00
{
}
2022-07-04 15:02:17 +02:00
lv2_fs_object : : lv2_fs_object ( utils : : serial & ar , bool )
: name ( ar )
2023-05-25 08:31:17 +02:00
, mp ( g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( name . data ( ) ) )
2022-07-04 15:02:17 +02:00
{
}
2023-10-02 17:23:21 +02:00
u64 lv2_file : : op_read ( const fs : : file & file , vm : : ptr < void > buf , u64 size , u64 opt_pos )
2016-06-02 17:16:01 +02:00
{
2024-01-17 16:08:01 +01:00
if ( u64 region = buf . addr ( ) > > 28 , region_end = ( buf . addr ( ) & 0xfff'ffff ) + ( size & 0xfff'ffff ) ; region = = region_end & & ( ( region > > 28 ) = = 0 | | region > = 0xC ) )
{
// Optimize reads from safe memory
return ( opt_pos = = umax ? file . read ( buf . get_ptr ( ) , size ) : file . read_at ( opt_pos , buf . get_ptr ( ) , size ) ) ;
}
2016-06-02 17:16:01 +02:00
// Copy data from intermediate buffer (avoid passing vm pointer to a native API)
2023-03-06 16:55:28 +01:00
std : : vector < uchar > local_buf ( std : : min < u64 > ( size , 65536 ) ) ;
2020-01-19 02:25:50 +01:00
u64 result = 0 ;
while ( result < size )
{
2023-03-06 16:55:28 +01:00
const u64 block = std : : min < u64 > ( size - result , local_buf . size ( ) ) ;
2023-10-02 17:23:21 +02:00
const u64 nread = ( opt_pos = = umax ? file . read ( local_buf . data ( ) , block ) : file . read_at ( opt_pos + result , local_buf . data ( ) , block ) ) ;
2020-01-19 02:25:50 +01:00
2023-03-06 16:55:28 +01:00
std : : memcpy ( static_cast < uchar * > ( buf . get_ptr ( ) ) + result , local_buf . data ( ) , nread ) ;
2020-01-19 02:25:50 +01:00
result + = nread ;
if ( nread < block )
{
break ;
}
}
2016-06-02 17:16:01 +02:00
return result ;
}
2020-02-27 16:24:47 +01:00
u64 lv2_file : : op_write ( const fs : : file & file , vm : : cptr < void > buf , u64 size )
2016-06-02 17:16:01 +02:00
{
// Copy data to intermediate buffer (avoid passing vm pointer to a native API)
2023-03-06 16:55:28 +01:00
std : : vector < uchar > local_buf ( std : : min < u64 > ( size , 65536 ) ) ;
2020-01-19 02:25:50 +01:00
u64 result = 0 ;
while ( result < size )
{
2023-03-06 16:55:28 +01:00
const u64 block = std : : min < u64 > ( size - result , local_buf . size ( ) ) ;
std : : memcpy ( local_buf . data ( ) , static_cast < const uchar * > ( buf . get_ptr ( ) ) + result , block ) ;
const u64 nwrite = file . write ( + local_buf . data ( ) , block ) ;
2020-01-19 02:25:50 +01:00
result + = nwrite ;
if ( nwrite < block )
{
break ;
}
}
return result ;
2016-06-02 17:16:01 +02:00
}
2022-07-04 15:02:17 +02:00
lv2_file : : lv2_file ( utils : : serial & ar )
: lv2_fs_object ( ar , false )
, mode ( ar )
, flags ( ar )
, type ( ar )
{
2024-02-02 20:08:17 +01:00
[[maybe_unused]] const s32 version = GET_SERIALIZATION_VERSION ( lv2_fs ) ;
2022-07-04 15:02:17 +02:00
ar ( lock ) ;
be_t < u64 > arg = 0 ;
u64 size = 0 ;
switch ( type )
{
case lv2_file_type : : regular : break ;
case lv2_file_type : : sdata : arg = 0x18000000010 , size = 8 ; break ; // TODO: Fix
case lv2_file_type : : edata : arg = 0x2 , size = 8 ; break ;
}
2024-02-02 20:08:17 +01:00
const std : : string retrieve_real = ar . pop < std : : string > ( ) ;
if ( type = = lv2_file_type : : edata & & version > = 2 )
{
ar ( g_fxo - > get < loaded_npdrm_keys > ( ) . one_time_key ) ;
}
2022-07-04 15:02:17 +02:00
open_result_t res = lv2_file : : open ( retrieve_real , flags & CELL_FS_O_ACCMODE , mode , size ? & arg : nullptr , size ) ;
file = std : : move ( res . file ) ;
real_path = std : : move ( res . real_path ) ;
g_fxo - > get < loaded_npdrm_keys > ( ) . npdrm_fds . raw ( ) + = type ! = lv2_file_type : : regular ;
2024-02-02 20:08:17 +01:00
g_fxo - > get < loaded_npdrm_keys > ( ) . one_time_key = { } ;
2022-07-04 15:02:17 +02:00
2023-11-15 20:07:42 +01:00
if ( ar . pop < bool > ( ) ) // see lv2_file::save in_mem
2022-07-04 15:02:17 +02:00
{
const fs : : stat_t stat = ar ;
2023-10-03 15:03:07 +02:00
std : : vector < u8 > buf ( stat . size ) ;
ar ( std : : span < u8 > ( buf . data ( ) , buf . size ( ) ) ) ;
2022-07-04 15:02:17 +02:00
file = fs : : make_stream < std : : vector < u8 > > ( std : : move ( buf ) , stat ) ;
}
if ( ! file )
{
2022-07-07 16:28:00 +02:00
sys_fs . error ( " Failed to load \' %s \' file for savestates (res=%s, vpath= \' %s \' , real-path= \' %s \' , type=%s, flags=0x%x) " , name . data ( ) , res . error , retrieve_real , real_path , type , flags ) ;
2022-07-04 15:02:17 +02:00
ar . pos + = sizeof ( u64 ) ;
ensure ( ! ! g_cfg . savestate . state_inspection_mode ) ;
return ;
}
2022-07-07 16:28:00 +02:00
else
{
sys_fs . success ( " Loaded file descriptor \' %s \' file for savestates (vpath= \' %s \' , type=%s, flags=0x%x, id=%d) " , name . data ( ) , retrieve_real , type , flags , idm : : last_id ( ) ) ;
}
2022-07-04 15:02:17 +02:00
file . seek ( ar ) ;
}
void lv2_file : : save ( utils : : serial & ar )
{
USING_SERIALIZATION_VERSION ( lv2_fs ) ;
2022-07-17 20:04:12 +02:00
ar ( name , mode , flags , type , lock , ensure ( vfs : : retrieve ( real_path ) , FN ( ! x . empty ( ) ) ) ) ;
2022-07-04 15:02:17 +02:00
2024-02-02 20:08:17 +01:00
if ( type = = lv2_file_type : : edata )
{
auto file_ptr = file . release ( ) ;
ar ( static_cast < EDATADecrypter * > ( file_ptr . get ( ) ) - > get_key ( ) ) ;
file . reset ( std : : move ( file_ptr ) ) ;
}
2023-05-25 08:31:17 +02:00
if ( ! mp . read_only & & flags & CELL_FS_O_ACCMODE )
2022-07-04 15:02:17 +02:00
{
// Ensure accurate timestamps and content on disk
file . sync ( ) ;
}
// UNIX allows deletion of files while descriptors are still opened
2022-08-25 09:53:35 +02:00
// descriptors shall keep the data in memory in this case
2022-07-04 15:02:17 +02:00
const bool in_mem = [ & ] ( )
{
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2022-07-04 15:02:17 +02:00
{
return false ;
}
fs : : file test { real_path } ;
2022-08-25 09:53:35 +02:00
if ( ! test )
{
if ( fs : : is_file ( real_path + " .66600 " ) )
{
// May be a split-files descriptor, don't even bother
return false ;
}
return true ;
}
2022-07-04 15:02:17 +02:00
2023-07-17 06:28:27 +02:00
fs : : file_id test_s = test . get_id ( ) ;
fs : : file_id file_s = file . get_id ( ) ;
2022-08-25 09:53:35 +02:00
2023-10-02 13:35:19 +02:00
return ! test_s . is_coherent_with ( file_s ) ;
2022-07-04 15:02:17 +02:00
} ( ) ;
ar ( in_mem ) ;
if ( in_mem )
{
2023-10-02 13:35:19 +02:00
fs : : stat_t stats = file . get_stat ( ) ;
sys_fs . error ( " Saving \' %s \' LV2 file descriptor in memory! (exists=%s, type=%s, flags=0x%x, size=0x%x) " , name . data ( ) , fs : : is_file ( real_path ) , type , flags , stats . size ) ;
2023-10-03 15:03:07 +02:00
const usz patch_stats_pos = ar . seek_end ( ) ;
ar ( stats ) ;
2023-10-02 13:35:19 +02:00
const usz old_end = ar . pad_from_end ( stats . size ) ;
if ( usz read_size = file . read_at ( 0 , & ar . data [ old_end ] , stats . size ) ; read_size ! = stats . size )
{
ensure ( read_size < stats . size ) ;
sys_fs . error ( " Read less than expected! (new-size=0x%x) " , read_size ) ;
stats . size = read_size ;
ar . data . resize ( old_end + stats . size ) ;
2023-10-03 15:03:07 +02:00
write_to_ptr < fs : : stat_t > ( & ar . data [ patch_stats_pos ] , stats ) ;
2023-10-02 13:35:19 +02:00
}
2022-07-04 15:02:17 +02:00
}
ar ( file . pos ( ) ) ;
}
lv2_dir : : lv2_dir ( utils : : serial & ar )
: lv2_fs_object ( ar , false )
, entries ( [ & ]
{
std : : vector < fs : : dir_entry > entries ;
u64 size = 0 ;
ar . deserialize_vle ( size ) ;
entries . resize ( size ) ;
for ( auto & entry : entries )
{
ar ( entry . name , static_cast < fs : : stat_t & > ( entry ) ) ;
}
return entries ;
} ( ) )
, pos ( ar )
{
}
void lv2_dir : : save ( utils : : serial & ar )
{
USING_SERIALIZATION_VERSION ( lv2_fs ) ;
ar ( name ) ;
ar . serialize_vle ( entries . size ( ) ) ;
for ( auto & entry : entries )
{
ar ( entry . name , static_cast < const fs : : stat_t & > ( entry ) ) ;
}
ar ( pos ) ;
}
loaded_npdrm_keys : : loaded_npdrm_keys ( utils : : serial & ar )
{
save ( ar ) ;
}
void loaded_npdrm_keys : : save ( utils : : serial & ar )
{
ar ( dec_keys_pos ) ;
ar ( std : : span ( dec_keys , std : : min < usz > ( std : : size ( dec_keys ) , dec_keys_pos ) ) ) ;
}
2017-03-04 12:54:53 +01:00
struct lv2_file : : file_view : fs : : file_base
{
2024-12-22 19:59:48 +01:00
const shared_ptr < lv2_file > m_file ;
2017-03-04 12:54:53 +01:00
const u64 m_off ;
u64 m_pos ;
2024-12-22 19:59:48 +01:00
explicit file_view ( const shared_ptr < lv2_file > & _file , u64 offset )
2017-03-04 12:54:53 +01:00
: m_file ( _file )
, m_off ( offset )
, m_pos ( 0 )
{
}
~ file_view ( ) override
{
}
2023-07-11 20:40:30 +02:00
fs : : stat_t get_stat ( ) override
2017-03-04 12:54:53 +01:00
{
2024-02-15 13:05:45 +01:00
fs : : stat_t stat = m_file - > file . get_stat ( ) ;
// TODO: Check this on realhw
//stat.size = utils::sub_saturate<u64>(stat.size, m_off);
stat . is_writable = false ;
return stat ;
2017-03-04 12:54:53 +01:00
}
2021-03-05 20:05:37 +01:00
bool trunc ( u64 ) override
2017-03-04 12:54:53 +01:00
{
return false ;
}
u64 read ( void * buffer , u64 size ) override
{
2024-02-15 19:03:50 +01:00
const u64 result = file_view : : read_at ( m_pos , buffer , size ) ;
2017-03-04 12:54:53 +01:00
m_pos + = result ;
return result ;
}
2022-12-24 15:15:29 +01:00
u64 read_at ( u64 offset , void * buffer , u64 size ) override
{
2024-02-14 13:00:10 +01:00
return m_file - > file . read_at ( m_off + offset , buffer , size ) ;
2022-12-24 15:15:29 +01:00
}
2021-03-05 20:05:37 +01:00
u64 write ( const void * , u64 ) override
2017-03-04 12:54:53 +01:00
{
return 0 ;
}
u64 seek ( s64 offset , fs : : seek_mode whence ) override
{
2017-03-23 19:32:59 +01:00
const s64 new_pos =
whence = = fs : : seek_set ? offset :
whence = = fs : : seek_cur ? offset + m_pos :
2020-03-07 09:52:54 +01:00
whence = = fs : : seek_end ? offset + size ( ) : - 1 ;
2017-03-23 19:32:59 +01:00
if ( new_pos < 0 )
{
fs : : g_tls_error = fs : : error : : inval ;
return - 1 ;
}
m_pos = new_pos ;
return m_pos ;
2017-03-04 12:54:53 +01:00
}
u64 size ( ) override
{
2024-02-15 13:05:45 +01:00
return utils : : sub_saturate < u64 > ( m_file - > file . size ( ) , m_off ) ;
2017-03-04 12:54:53 +01:00
}
2021-09-21 15:17:45 +02:00
fs : : file_id get_id ( ) override
{
fs : : file_id id = m_file - > file . get_id ( ) ;
be_t < u64 > off = m_off ;
const auto ptr = reinterpret_cast < u8 * > ( & off ) ;
id . data . insert ( id . data . end ( ) , ptr , ptr + sizeof ( off ) ) ;
id . type . insert ( 0 , " lv2_file::file_view: " sv ) ;
return id ;
}
2017-03-04 12:54:53 +01:00
} ;
2024-12-22 19:59:48 +01:00
fs : : file lv2_file : : make_view ( const shared_ptr < lv2_file > & _file , u64 offset )
2017-03-04 12:54:53 +01:00
{
fs : : file result ;
result . reset ( std : : make_unique < lv2_file : : file_view > ( _file , offset ) ) ;
return result ;
}
2023-12-11 07:45:38 +01:00
std : : pair < CellError , std : : string > translate_to_str ( vm : : cptr < char > ptr , bool is_path = true )
2021-11-12 18:32:35 +01:00
{
constexpr usz max_length = CELL_FS_MAX_FS_PATH_LENGTH + 1 ;
2023-12-11 07:45:38 +01:00
std : : string path ;
2021-11-12 18:32:35 +01:00
2023-12-11 07:45:38 +01:00
if ( ! vm : : read_string ( ptr . addr ( ) , max_length , path , true ) )
2021-11-12 18:32:35 +01:00
{
2023-12-11 07:45:38 +01:00
// Null character lookup has ended whilst pointing at invalid memory
return { CELL_EFAULT , std : : move ( path ) } ;
2021-11-12 18:32:35 +01:00
}
2023-12-11 07:45:38 +01:00
if ( path . size ( ) = = max_length )
2021-11-12 18:32:35 +01:00
{
2023-12-11 07:45:38 +01:00
return { CELL_ENAMETOOLONG , { } } ;
2021-11-12 18:32:35 +01:00
}
2022-10-31 14:59:02 +01:00
if ( is_path & & ! path . starts_with ( " / " sv ) )
2021-11-12 18:32:35 +01:00
{
2023-12-11 07:45:38 +01:00
return { CELL_ENOENT , std : : move ( path ) } ;
2021-11-12 18:32:35 +01:00
}
2023-12-11 07:45:38 +01:00
return { { } , std : : move ( path ) } ;
2021-11-12 18:32:35 +01:00
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_test ( ppu_thread & , u32 arg1 , u32 arg2 , vm : : ptr < u32 > arg3 , u32 arg4 , vm : : ptr < char > buf , u32 buf_size )
2015-03-15 01:41:08 +01:00
{
2017-04-24 18:49:53 +02:00
sys_fs . trace ( " sys_fs_test(arg1=0x%x, arg2=0x%x, arg3=*0x%x, arg4=0x%x, buf=*0x%x, buf_size=0x%x) " , arg1 , arg2 , arg3 , arg4 , buf , buf_size ) ;
2015-03-15 01:41:08 +01:00
2017-04-24 18:49:53 +02:00
if ( arg1 ! = 6 | | arg2 ! = 0 | | arg4 ! = sizeof ( u32 ) )
{
sys_fs . todo ( " sys_fs_test: unknown arguments (arg1=0x%x, arg2=0x%x, arg3=*0x%x, arg4=0x%x) " , arg1 , arg2 , arg3 , arg4 ) ;
}
if ( ! arg3 )
{
return CELL_EFAULT ;
}
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object > ( * arg3 ) ;
2017-04-24 18:49:53 +02:00
if ( ! file )
{
return CELL_EBADF ;
}
for ( u32 i = 0 ; i < buf_size ; i + + )
{
if ( ! ( buf [ i ] = file - > name [ i ] ) )
{
return CELL_OK ;
}
}
buf [ buf_size - 1 ] = 0 ;
2015-03-15 01:41:08 +01:00
return CELL_OK ;
}
2023-05-25 08:31:17 +02:00
lv2_file : : open_raw_result_t lv2_file : : open_raw ( const std : : string & local_path , s32 flags , s32 /*mode*/ , lv2_file_type type , const lv2_fs_mount_info & mp )
2015-03-12 20:02:02 +01:00
{
2015-03-13 16:06:27 +01:00
// TODO: other checks for path
2018-09-11 18:02:19 +02:00
if ( fs : : is_dir ( local_path ) )
2015-03-13 16:06:27 +01:00
{
2020-09-11 13:06:46 +02:00
return { CELL_EISDIR } ;
2015-03-13 16:06:27 +01:00
}
2015-03-12 20:02:02 +01:00
2016-08-07 21:01:27 +02:00
bs_t < fs : : open_mode > open_mode { } ;
2015-03-13 21:43:11 +01:00
2015-04-19 15:19:24 +02:00
switch ( flags & CELL_FS_O_ACCMODE )
2015-03-13 21:43:11 +01:00
{
2016-04-14 00:23:53 +02:00
case CELL_FS_O_RDONLY : open_mode + = fs : : read ; break ;
case CELL_FS_O_WRONLY : open_mode + = fs : : write ; break ;
case CELL_FS_O_RDWR : open_mode + = fs : : read + fs : : write ; break ;
2021-04-09 21:12:47 +02:00
default : break ;
2015-03-13 21:43:11 +01:00
}
2015-04-19 15:19:24 +02:00
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-11 02:48:42 +01:00
{
2021-06-18 16:10:58 +02:00
if ( ( flags & CELL_FS_O_ACCMODE ) ! = CELL_FS_O_RDONLY & & fs : : is_file ( local_path ) )
2020-01-11 02:48:42 +01:00
{
2020-09-11 13:06:46 +02:00
return { CELL_EPERM } ;
2020-01-11 02:48:42 +01:00
}
}
2015-04-19 15:19:24 +02:00
if ( flags & CELL_FS_O_CREAT )
2015-03-13 21:43:11 +01:00
{
2016-04-14 00:23:53 +02:00
open_mode + = fs : : create ;
2018-11-23 17:38:24 +01:00
if ( flags & CELL_FS_O_EXCL )
{
open_mode + = fs : : excl ;
}
2015-03-13 21:43:11 +01:00
}
2015-04-19 15:19:24 +02:00
if ( flags & CELL_FS_O_TRUNC )
2015-03-13 21:43:11 +01:00
{
2016-04-14 00:23:53 +02:00
open_mode + = fs : : trunc ;
2015-03-13 21:43:11 +01:00
}
2015-03-12 20:02:02 +01:00
2017-03-07 01:59:05 +01:00
if ( flags & CELL_FS_O_MSELF )
{
open_mode = fs : : read ;
// mself can be mself or mself | rdonly
if ( flags & ~ ( CELL_FS_O_MSELF | CELL_FS_O_RDONLY ) )
{
open_mode = { } ;
}
}
2018-02-14 18:06:46 +01:00
if ( flags & CELL_FS_O_UNK )
2015-03-13 16:06:27 +01:00
{
2020-03-06 13:47:24 +01:00
sys_fs . warning ( " lv2_file::open() called with CELL_FS_O_UNK flag enabled. FLAGS: %#o " , flags ) ;
2018-02-14 18:06:46 +01:00
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
// Deactivate mutating flags on read-only FS
open_mode = fs : : read ;
}
2018-02-14 18:06:46 +01:00
// Tests have shown that invalid combinations get resolved internally (without exceptions), but that would complicate code with minimal accuracy gains.
// For example, no games are known to try and call TRUNCATE | APPEND | RW, or APPEND | READ, which currently would cause an exception.
if ( flags & ~ ( CELL_FS_O_UNK | CELL_FS_O_ACCMODE | CELL_FS_O_CREAT | CELL_FS_O_TRUNC | CELL_FS_O_APPEND | CELL_FS_O_EXCL | CELL_FS_O_MSELF ) )
2018-03-21 20:02:36 +01:00
{
2016-04-14 00:23:53 +02:00
open_mode = { } ; // error
2015-03-13 16:06:27 +01:00
}
2015-03-12 20:02:02 +01:00
2015-04-19 15:19:24 +02:00
if ( ( flags & CELL_FS_O_ACCMODE ) = = CELL_FS_O_ACCMODE )
2015-03-13 16:06:27 +01:00
{
2016-04-14 00:23:53 +02:00
open_mode = { } ; // error
2015-03-12 20:02:02 +01:00
}
2015-03-13 21:43:11 +01:00
2018-09-02 19:22:35 +02:00
if ( ! open_mode )
2015-03-12 20:02:02 +01:00
{
2020-12-09 16:04:52 +01:00
fmt : : throw_exception ( " lv2_file::open_raw(): Invalid or unimplemented flags: %#o " , flags ) ;
2015-03-13 16:06:27 +01:00
}
2015-04-19 15:19:24 +02:00
2020-09-11 13:06:46 +02:00
std : : lock_guard lock ( mp - > mutex ) ;
fs : : file file ( local_path , open_mode ) ;
2015-03-12 20:02:02 +01:00
2018-08-13 00:06:34 +02:00
if ( ! file & & open_mode = = fs : : read & & fs : : g_tls_error = = fs : : error : : noent )
{
// Try to gather split file (TODO)
std : : vector < fs : : file > fragments ;
for ( u32 i = 66600 ; i < = 66699 ; i + + )
{
if ( fs : : file fragment { fmt : : format ( " %s.%u " , local_path , i ) } )
{
fragments . emplace_back ( std : : move ( fragment ) ) ;
}
else
{
break ;
}
}
if ( ! fragments . empty ( ) )
{
file = fs : : make_gather ( std : : move ( fragments ) ) ;
}
}
2016-04-14 00:23:53 +02:00
if ( ! file )
2015-03-12 20:02:02 +01:00
{
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
// Failed to create file on read-only FS (file doesn't exist)
2021-06-18 16:10:58 +02:00
if ( flags & CELL_FS_O_ACCMODE & & flags & CELL_FS_O_CREAT )
2020-01-04 22:55:15 +01:00
{
2021-06-18 16:10:58 +02:00
return { CELL_EPERM } ;
2020-01-04 22:55:15 +01:00
}
}
2018-09-02 19:22:35 +02:00
if ( open_mode & fs : : excl & & fs : : g_tls_error = = fs : : error : : exist )
2015-04-20 17:53:31 +02:00
{
2020-03-06 13:47:24 +01:00
return { CELL_EEXIST } ;
2015-04-20 17:53:31 +02:00
}
2017-09-08 16:17:03 +02:00
switch ( auto error = fs : : g_tls_error )
{
2020-09-11 13:06:46 +02:00
case fs : : error : : noent : return { CELL_ENOENT } ;
2020-03-06 13:47:24 +01:00
default : sys_fs . error ( " lv2_file::open(): unknown error %s " , error ) ;
2017-09-08 16:17:03 +02:00
}
2020-09-11 13:06:46 +02:00
return { CELL_EIO } ;
2015-03-12 20:02:02 +01:00
}
2020-03-06 13:47:24 +01:00
if ( flags & CELL_FS_O_MSELF & & ! verify_mself ( file ) )
2017-08-08 21:13:48 +02:00
{
2020-09-11 13:06:46 +02:00
return { CELL_ENOTMSELF } ;
2017-08-08 21:13:48 +02:00
}
2020-09-11 13:06:46 +02:00
if ( type > = lv2_file_type : : sdata )
2017-03-07 01:59:05 +01:00
{
2017-12-16 01:03:49 +01:00
// check for sdata
2020-09-11 13:06:46 +02:00
switch ( type )
2020-03-06 13:47:24 +01:00
{
2020-09-11 13:06:46 +02:00
case lv2_file_type : : sdata :
2017-03-07 01:59:05 +01:00
{
2017-03-10 10:06:36 +01:00
// check if the file has the NPD header, or else assume its not encrypted
u32 magic ;
file . read < u32 > ( magic ) ;
file . seek ( 0 ) ;
if ( magic = = " NPD \0 " _u32 )
2017-03-07 01:59:05 +01:00
{
2017-03-10 10:06:36 +01:00
auto sdata_file = std : : make_unique < EDATADecrypter > ( std : : move ( file ) ) ;
if ( ! sdata_file - > ReadHeader ( ) )
{
2020-09-11 13:06:46 +02:00
return { CELL_EFSSPECIFIC } ;
2017-03-10 10:06:36 +01:00
}
file . reset ( std : : move ( sdata_file ) ) ;
}
2020-03-06 13:47:24 +01:00
break ;
2017-03-10 10:06:36 +01:00
}
2017-12-16 01:03:49 +01:00
// edata
2020-09-11 13:06:46 +02:00
case lv2_file_type : : edata :
2017-03-10 10:06:36 +01:00
{
// check if the file has the NPD header, or else assume its not encrypted
u32 magic ;
file . read < u32 > ( magic ) ;
file . seek ( 0 ) ;
if ( magic = = " NPD \0 " _u32 )
{
2021-03-02 12:59:19 +01:00
auto & edatkeys = g_fxo - > get < loaded_npdrm_keys > ( ) ;
2021-10-15 11:11:16 +02:00
const u64 init_pos = edatkeys . dec_keys_pos ;
const auto & dec_keys = edatkeys . dec_keys ;
const u64 max_i = std : : min < u64 > ( std : : size ( dec_keys ) , init_pos ) ;
2024-02-02 20:08:17 +01:00
if ( edatkeys . one_time_key )
{
auto edata_file = std : : make_unique < EDATADecrypter > ( std : : move ( file ) , edatkeys . one_time_key ) ;
edatkeys . one_time_key = { } ;
if ( ! edata_file - > ReadHeader ( ) )
{
// Read failure
return { CELL_EFSSPECIFIC } ;
}
file . reset ( std : : move ( edata_file ) ) ;
break ;
}
2021-10-15 11:11:16 +02:00
for ( u64 i = 0 ; ; i + + )
2017-03-10 10:06:36 +01:00
{
2021-10-15 11:11:16 +02:00
if ( i = = max_i )
{
// Run out of keys to try
return { CELL_EFSSPECIFIC } ;
}
// Try all registered keys
auto edata_file = std : : make_unique < EDATADecrypter > ( std : : move ( file ) , dec_keys [ ( init_pos - i - 1 ) % std : : size ( dec_keys ) ] . load ( ) ) ;
if ( ! edata_file - > ReadHeader ( ) )
{
// Prepare file for the next iteration
2024-02-02 21:28:44 +01:00
file = std : : move ( edata_file - > m_edata_file ) ;
2021-10-15 11:11:16 +02:00
continue ;
}
file . reset ( std : : move ( edata_file ) ) ;
break ;
2017-03-10 10:06:36 +01:00
2021-10-15 11:11:16 +02:00
}
2017-03-07 01:59:05 +01:00
}
2020-03-06 13:47:24 +01:00
break ;
2017-03-07 01:59:05 +01:00
}
2020-03-06 13:47:24 +01:00
default : break ;
}
}
2020-09-11 13:06:46 +02:00
return { . error = { } , . file = std : : move ( file ) } ;
}
lv2_file : : open_result_t lv2_file : : open ( std : : string_view vpath , s32 flags , s32 mode , const void * arg , u64 size )
{
if ( vpath . empty ( ) )
{
return { CELL_ENOENT } ;
}
std : : string path ;
2022-06-06 22:06:42 +02:00
std : : string local_path = vfs : : get ( vpath , nullptr , & path ) ;
2020-09-11 13:06:46 +02:00
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-09-11 13:06:46 +02:00
2020-10-12 20:23:17 +02:00
if ( mp = = & g_mp_sys_dev_root )
2020-09-11 13:06:46 +02:00
{
return { CELL_EISDIR , path } ;
}
if ( local_path . empty ( ) )
{
return { CELL_ENOTMOUNTED , path } ;
}
2024-02-08 06:49:46 +01:00
if ( flags & CELL_FS_O_CREAT & & ! has_fs_write_rights ( vpath ) & & ! fs : : is_dir ( local_path ) )
{
return { CELL_EACCES } ;
}
2020-09-11 13:06:46 +02:00
lv2_file_type type = lv2_file_type : : regular ;
if ( size = = 8 )
{
// see lv2_file::open_raw
2022-09-29 11:04:38 +02:00
switch ( * static_cast < const be_t < u64 , 1 > * > ( arg ) )
2020-09-11 13:06:46 +02:00
{
case 0x18000000010 : type = lv2_file_type : : sdata ; break ;
case 0x2 : type = lv2_file_type : : edata ; break ;
default :
break ;
}
}
auto [ error , file ] = open_raw ( local_path , flags , mode , type , mp ) ;
return { . error = error , . ppath = std : : move ( path ) , . real_path = std : : move ( local_path ) , . file = std : : move ( file ) , . type = type } ;
2020-03-06 13:47:24 +01:00
}
error_code sys_fs_open ( ppu_thread & ppu , vm : : cptr < char > path , s32 flags , vm : : ptr < u32 > fd , s32 mode , vm : : cptr < void > arg , u64 size )
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2020-03-06 13:47:24 +01:00
lv2_obj : : sleep ( ppu ) ;
sys_fs . warning ( " sys_fs_open(path=%s, flags=%#o, fd=*0x%x, mode=%#o, arg=*0x%x, size=0x%llx) " , path , flags , fd , mode , arg , size ) ;
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2020-03-06 13:47:24 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
auto [ error , ppath , real , file , type ] = lv2_file : : open ( vpath , flags , mode , arg . get_ptr ( ) , size ) ;
2020-03-06 13:47:24 +01:00
if ( error )
{
if ( error = = CELL_EEXIST )
{
return not_an_error ( CELL_EEXIST ) ;
}
2023-05-25 08:31:17 +02:00
return { g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) = = & g_mp_sys_dev_hdd1 ? sys_fs . warning : sys_fs . error , error , path } ;
2017-03-07 01:59:05 +01:00
}
2017-08-08 21:13:48 +02:00
2024-12-22 19:59:48 +01:00
if ( const u32 id = idm : : import < lv2_fs_object , lv2_file > ( [ & ppath = ppath , & file = file , mode , flags , & real = real , & type = type ] ( ) - > shared_ptr < lv2_file >
2020-04-04 16:11:21 +02:00
{
2024-12-22 19:59:48 +01:00
shared_ptr < lv2_file > result ;
2020-04-04 16:11:21 +02:00
2021-11-14 00:15:27 +01:00
if ( type > = lv2_file_type : : sdata & & ! g_fxo - > get < loaded_npdrm_keys > ( ) . npdrm_fds . try_inc ( 16 ) )
2020-04-04 16:11:21 +02:00
{
2021-11-14 00:15:27 +01:00
return result ;
2020-04-04 16:11:21 +02:00
}
2021-11-14 00:15:27 +01:00
2024-12-22 19:59:48 +01:00
result = stx : : make_shared < lv2_file > ( ppath , std : : move ( file ) , mode , flags , real , type ) ;
2021-11-14 00:15:27 +01:00
sys_fs . warning ( " sys_fs_open(): fd=%u, %s " , idm : : last_id ( ) , * result ) ;
return result ;
} ) )
2015-08-11 18:14:53 +02:00
{
2022-09-18 20:19:34 +02:00
ppu . check_state ( ) ;
2017-02-04 16:09:02 +01:00
* fd = id ;
return CELL_OK ;
2015-07-28 23:34:22 +02:00
}
2017-02-04 16:09:02 +01:00
// Out of file descriptors
2017-08-08 21:13:48 +02:00
return { CELL_EMFILE , path } ;
2015-03-12 20:02:02 +01:00
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_read ( ppu_thread & ppu , u32 fd , vm : : ptr < void > buf , u64 nbytes , vm : : ptr < u64 > nread )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-04-09 11:45:45 +02:00
sys_fs . trace ( " sys_fs_read(fd=%d, buf=*0x%x, nbytes=0x%llx, nread=*0x%x) " , fd , buf , nbytes , nread ) ;
2020-01-18 13:01:30 +01:00
if ( ! nread )
{
return CELL_EFAULT ;
}
2016-04-09 11:45:45 +02:00
if ( ! buf )
{
2020-01-18 13:01:30 +01:00
nread . try_write ( 0 ) ;
2016-04-09 11:45:45 +02:00
return CELL_EFAULT ;
}
2015-03-12 20:02:02 +01:00
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2015-03-12 20:02:02 +01:00
2021-09-21 11:07:01 +02:00
if ( ! file | | ( nbytes & & file - > flags & CELL_FS_O_WRONLY ) )
2015-03-13 16:06:27 +01:00
{
2020-01-18 13:01:30 +01:00
nread . try_write ( 0 ) ; // nread writing is allowed to fail, error code is unchanged
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-13 16:06:27 +01:00
}
2015-03-12 20:02:02 +01:00
2021-09-21 11:07:01 +02:00
if ( ! nbytes )
{
// Whole function is skipped, only EBADF and EBUSY are checked
if ( file - > lock = = 1 )
{
nread . try_write ( 0 ) ;
return CELL_EBUSY ;
}
2022-10-13 17:18:24 +02:00
ppu . check_state ( ) ;
2021-09-21 11:07:01 +02:00
* nread = 0 ;
return CELL_OK ;
}
2022-10-13 17:18:24 +02:00
std : : unique_lock lock ( file - > mp - > mutex ) ;
2015-03-16 01:21:40 +01:00
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2019-11-09 12:35:41 +01:00
if ( file - > lock = = 2 )
{
2020-01-18 13:01:30 +01:00
nread . try_write ( 0 ) ;
2019-11-09 12:35:41 +01:00
return CELL_EIO ;
}
2021-10-15 11:11:16 +02:00
const u64 read_bytes = file - > op_read ( buf , nbytes ) ;
2022-10-13 17:18:24 +02:00
const bool failure = ! read_bytes & & file - > file . pos ( ) < file - > file . size ( ) ;
lock . unlock ( ) ;
ppu . check_state ( ) ;
2021-10-15 11:11:16 +02:00
* nread = read_bytes ;
2022-10-13 17:18:24 +02:00
if ( failure )
2021-10-15 11:11:16 +02:00
{
// EDATA corruption perhaps
return CELL_EFSSPECIFIC ;
}
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_write ( ppu_thread & ppu , u32 fd , vm : : cptr < void > buf , u64 nbytes , vm : : ptr < u64 > nwrite )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-01-12 22:57:16 +01:00
sys_fs . trace ( " sys_fs_write(fd=%d, buf=*0x%x, nbytes=0x%llx, nwrite=*0x%x) " , fd , buf , nbytes , nwrite ) ;
2015-03-12 20:02:02 +01:00
2020-01-18 13:01:30 +01:00
if ( ! nwrite )
{
return CELL_EFAULT ;
}
if ( ! buf )
{
nwrite . try_write ( 0 ) ;
return CELL_EFAULT ;
}
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2015-03-12 20:02:02 +01:00
2021-09-21 11:07:01 +02:00
if ( ! file | | ( nbytes & & ! ( file - > flags & CELL_FS_O_ACCMODE ) ) )
2015-03-13 16:06:27 +01:00
{
2020-01-18 13:01:30 +01:00
nwrite . try_write ( 0 ) ; // nwrite writing is allowed to fail, error code is unchanged
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-13 16:06:27 +01:00
}
2015-03-12 20:02:02 +01:00
2021-09-21 11:07:01 +02:00
if ( ! nbytes )
{
// Whole function is skipped, only EBADF and EBUSY are checked
if ( file - > lock = = 1 )
{
nwrite . try_write ( 0 ) ;
return CELL_EBUSY ;
}
2022-10-13 17:18:24 +02:00
ppu . check_state ( ) ;
2021-09-21 11:07:01 +02:00
* nwrite = 0 ;
return CELL_OK ;
}
2021-09-21 11:49:59 +02:00
if ( file - > type ! = lv2_file_type : : regular )
{
sys_fs . error ( " %s type: Writing %u bytes to FD=%d (path=%s) " , file - > type , nbytes , file - > name . data ( ) ) ;
}
2023-05-25 08:31:17 +02:00
if ( file - > mp . read_only )
2020-01-04 22:55:15 +01:00
{
2020-01-18 13:01:30 +01:00
nwrite . try_write ( 0 ) ;
2020-01-04 22:55:15 +01:00
return CELL_EROFS ;
}
2022-09-18 20:19:34 +02:00
std : : unique_lock lock ( file - > mp - > mutex ) ;
2015-03-13 23:05:48 +01:00
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2017-04-24 22:30:15 +02:00
if ( file - > lock )
{
2019-11-09 12:35:41 +01:00
if ( file - > lock = = 2 )
{
2020-01-18 13:01:30 +01:00
nwrite . try_write ( 0 ) ;
2019-11-09 12:35:41 +01:00
return CELL_EIO ;
}
2020-01-18 13:01:30 +01:00
nwrite . try_write ( 0 ) ;
2017-04-24 22:30:15 +02:00
return CELL_EBUSY ;
}
2020-01-05 16:14:07 +01:00
if ( file - > flags & CELL_FS_O_APPEND )
{
file - > file . seek ( 0 , fs : : seek_end ) ;
}
2022-09-18 20:19:34 +02:00
const u64 written = file - > op_write ( buf , nbytes ) ;
lock . unlock ( ) ;
ppu . check_state ( ) ;
2015-03-12 20:02:02 +01:00
2022-09-18 20:19:34 +02:00
* nwrite = written ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_close ( ppu_thread & ppu , u32 fd )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2021-03-12 15:25:03 +01:00
if ( ! file )
{
2021-09-06 13:52:56 +02:00
return { CELL_EBADF , fd } ;
2021-03-12 15:25:03 +01:00
}
2022-01-24 09:13:54 +01:00
std : : string FD_state_log ;
if ( sys_fs . warning )
{
FD_state_log = fmt : : format ( " sys_fs_close(fd=%u) " , fd ) ;
}
2021-03-12 15:25:03 +01:00
{
std : : lock_guard lock ( file - > mp - > mutex ) ;
if ( ! file - > file )
{
2022-01-24 09:13:54 +01:00
sys_fs . warning ( " %s " , FD_state_log ) ;
2021-09-06 13:52:56 +02:00
return { CELL_EBADF , fd } ;
2021-03-12 15:25:03 +01:00
}
2023-05-25 08:31:17 +02:00
if ( ! ( file - > mp . read_only & & file - > mp - > flags & lv2_mp_flag : : cache ) & & file - > flags & CELL_FS_O_ACCMODE )
2021-03-12 15:25:03 +01:00
{
// Special: Ensure temporary directory for gamedata writes will remain on disk before final gamedata commitment
file - > file . sync ( ) ; // For cellGameContentPermit atomicity
}
2022-01-24 09:13:54 +01:00
if ( ! FD_state_log . empty ( ) )
{
sys_fs . warning ( " %s: %s " , FD_state_log , * file ) ;
}
2022-06-11 14:12:42 +02:00
// Free memory associated with fd if any
if ( file - > ct_id & & file - > ct_used )
{
auto & default_container = g_fxo - > get < default_sys_fs_container > ( ) ;
std : : lock_guard lock ( default_container . mutex ) ;
2024-12-22 19:59:48 +01:00
if ( auto ct = idm : : get_unlocked < lv2_memory_container > ( file - > ct_id ) )
2022-06-11 14:12:42 +02:00
{
ct - > free ( file - > ct_used ) ;
if ( default_container . id = = file - > ct_id )
{
default_container . used - = file - > ct_used ;
}
}
}
2021-03-12 15:25:03 +01:00
// Ensure Host file handle won't be kept open after this syscall
file - > file . close ( ) ;
}
2022-01-24 09:13:54 +01:00
ensure ( idm : : withdraw < lv2_fs_object , lv2_file > ( fd , [ & ] ( lv2_file & _file ) - > CellError
2020-04-04 16:11:21 +02:00
{
2021-03-12 15:25:03 +01:00
if ( _file . type > = lv2_file_type : : sdata )
2020-04-04 16:11:21 +02:00
{
2021-03-02 12:59:19 +01:00
g_fxo - > get < loaded_npdrm_keys > ( ) . npdrm_fds - - ;
2020-04-04 16:11:21 +02:00
}
2021-03-12 15:25:03 +01:00
return { } ;
2022-01-24 09:13:54 +01:00
} ) ) ;
2021-09-06 13:52:56 +02:00
2020-01-16 20:10:08 +01:00
if ( file - > lock = = 1 )
2017-04-24 22:30:15 +02:00
{
2021-09-06 13:52:56 +02:00
return { CELL_EBUSY , fd } ;
2017-04-24 22:30:15 +02:00
}
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_opendir ( ppu_thread & ppu , vm : : cptr < char > path , vm : : ptr < u32 > fd )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-11 01:29:59 +02:00
sys_fs . warning ( " sys_fs_opendir(path=%s, fd=*0x%x) " , path , fd ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2018-03-21 20:02:36 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2018-03-21 20:02:36 +01:00
2020-01-04 18:10:37 +01:00
std : : string processed_path ;
2018-09-15 16:04:35 +02:00
std : : vector < std : : string > ext ;
2020-01-04 18:10:37 +01:00
const std : : string local_path = vfs : : get ( vpath , & ext , & processed_path ) ;
processed_path + = " / " ;
2018-09-11 18:02:19 +02:00
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-01-04 22:55:15 +01:00
2018-09-15 16:04:35 +02:00
if ( local_path . empty ( ) & & ext . empty ( ) )
2016-06-02 17:16:01 +02:00
{
2017-08-08 21:13:48 +02:00
return { CELL_ENOTMOUNTED , path } ;
2016-06-02 17:16:01 +02:00
}
// TODO: other checks for path
if ( fs : : is_file ( local_path ) )
{
2017-08-08 21:13:48 +02:00
return { CELL_ENOTDIR , path } ;
2016-06-02 17:16:01 +02:00
}
2022-09-18 20:19:34 +02:00
std : : unique_lock lock ( mp - > mutex ) ;
2020-09-11 13:06:46 +02:00
2021-04-09 21:12:47 +02:00
const fs : : dir dir ( local_path ) ;
2015-03-13 16:06:27 +01:00
2016-04-14 00:23:53 +02:00
if ( ! dir )
2015-03-12 20:02:02 +01:00
{
2021-04-09 21:12:47 +02:00
switch ( const auto error = fs : : g_tls_error )
2017-09-08 16:17:03 +02:00
{
2018-09-15 16:04:35 +02:00
case fs : : error : : noent :
{
if ( ext . empty ( ) )
{
2022-11-26 11:01:55 +01:00
return { mp = = & g_mp_sys_dev_hdd1 ? sys_fs . warning : sys_fs . error , CELL_ENOENT , path } ;
2018-09-15 16:04:35 +02:00
}
break ;
}
default :
{
sys_fs . error ( " sys_fs_opendir(): unknown error %s " , error ) ;
return { CELL_EIO , path } ;
2017-09-08 16:17:03 +02:00
}
2018-09-15 16:04:35 +02:00
}
}
2017-09-08 16:17:03 +02:00
2018-09-15 16:04:35 +02:00
// Build directory as a vector of entries
std : : vector < fs : : dir_entry > data ;
if ( dir )
{
// Add real directories
while ( dir . read ( data . emplace_back ( ) ) )
{
// Preprocess entries
data . back ( ) . name = vfs : : unescape ( data . back ( ) . name ) ;
2021-02-21 20:55:07 +01:00
if ( ! data . back ( ) . is_directory & & data . back ( ) . name = = " . " sv )
{
// Files hidden from emulation
data . resize ( data . size ( ) - 1 ) ;
continue ;
}
2018-09-15 16:04:35 +02:00
// Add additional entries for split file candidates (while ends with .66600)
2020-02-17 22:43:23 +01:00
while ( data . back ( ) . name . ends_with ( " .66600 " ) )
2018-09-15 16:04:35 +02:00
{
data . emplace_back ( data . back ( ) ) . name . resize ( data . back ( ) . name . size ( ) - 6 ) ;
}
}
data . resize ( data . size ( ) - 1 ) ;
}
else
{
2022-09-13 15:08:55 +02:00
data . emplace_back ( ) . name + = ' . ' ;
2018-09-15 16:04:35 +02:00
data . back ( ) . is_directory = true ;
data . emplace_back ( ) . name = " .. " ;
data . back ( ) . is_directory = true ;
2015-03-12 20:02:02 +01:00
}
2018-09-15 16:04:35 +02:00
// Add mount points (TODO)
for ( auto & & ex : ext )
{
data . emplace_back ( ) . name = std : : move ( ex ) ;
data . back ( ) . is_directory = true ;
}
// Sort files, keeping . and ..
2022-06-30 19:56:34 +02:00
std : : stable_sort ( data . begin ( ) + 2 , data . end ( ) , FN ( x . name < y . name ) ) ;
2018-09-15 16:04:35 +02:00
// Remove duplicates
2022-06-30 19:56:34 +02:00
data . erase ( std : : unique ( data . begin ( ) , data . end ( ) , FN ( x . name = = y . name ) ) , data . end ( ) ) ;
2018-09-15 16:04:35 +02:00
2020-01-04 21:12:46 +01:00
if ( const u32 id = idm : : make < lv2_fs_object , lv2_dir > ( processed_path , std : : move ( data ) ) )
2015-08-11 18:14:53 +02:00
{
2022-09-18 20:19:34 +02:00
lock . unlock ( ) ;
ppu . check_state ( ) ;
2017-02-04 16:09:02 +01:00
* fd = id ;
return CELL_OK ;
2015-07-28 23:34:22 +02:00
}
2017-02-04 16:09:02 +01:00
// Out of file descriptors
return CELL_EMFILE ;
2015-03-12 20:02:02 +01:00
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_readdir ( ppu_thread & ppu , u32 fd , vm : : ptr < CellFsDirent > dir , vm : : ptr < u64 > nread )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
2016-01-12 22:57:16 +01:00
sys_fs . warning ( " sys_fs_readdir(fd=%d, dir=*0x%x, nread=*0x%x) " , fd , dir , nread ) ;
2015-03-12 20:02:02 +01:00
2023-03-11 07:55:22 +01:00
if ( ! dir | | ! nread )
{
return CELL_EFAULT ;
}
2024-12-22 19:59:48 +01:00
const auto directory = idm : : get_unlocked < lv2_fs_object , lv2_dir > ( fd ) ;
2015-03-13 16:06:27 +01:00
2015-04-12 03:36:25 +02:00
if ( ! directory )
2015-03-13 16:06:27 +01:00
{
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-13 16:06:27 +01:00
}
2015-03-12 20:02:02 +01:00
2022-09-18 20:19:34 +02:00
ppu . check_state ( ) ;
2023-03-11 07:55:22 +01:00
auto * info = directory - > dir_read ( ) ;
u64 nread_to_write = 0 ;
if ( info )
2015-03-12 20:02:02 +01:00
{
2023-03-11 07:55:22 +01:00
nread_to_write = sizeof ( CellFsDirent ) ;
2015-03-12 20:02:02 +01:00
}
else
{
2023-03-11 07:55:22 +01:00
// It does actually write polling the last entry. Seems consistent across HDD0 and HDD1 (TODO: check more partitions)
info = & directory - > entries . back ( ) ;
nread_to_write = 0 ;
2015-03-12 20:02:02 +01:00
}
2023-03-11 07:55:22 +01:00
CellFsDirent dir_write { } ;
dir_write . d_type = info - > is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR ;
dir_write . d_namlen = u8 ( std : : min < usz > ( info - > name . size ( ) , CELL_FS_MAX_FS_FILE_NAME_LENGTH ) ) ;
strcpy_trunc ( dir_write . d_name , info - > name ) ;
// TODO: Check more partitions (HDD1 is known to differ in actual filesystem implementation)
if ( directory - > mp ! = & g_mp_sys_dev_hdd1 & & nread_to_write = = 0 )
{
// First 3 bytes are being set to 0 here
dir_write . d_type = 0 ;
dir_write . d_namlen = 0 ;
dir_write . d_name [ 0 ] = ' \0 ' ;
}
* dir = dir_write ;
// Write after dir
* nread = nread_to_write ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_closedir ( ppu_thread & ppu , u32 fd )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-18 12:27:20 +02:00
sys_fs . warning ( " sys_fs_closedir(fd=%d) " , fd ) ;
2015-03-13 16:06:27 +01:00
2019-04-10 16:32:48 +02:00
if ( ! idm : : remove < lv2_fs_object , lv2_dir > ( fd ) )
2015-03-13 16:06:27 +01:00
{
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-13 16:06:27 +01:00
}
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_stat ( ppu_thread & ppu , vm : : cptr < char > path , vm : : ptr < CellFsStat > sb )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-11 01:29:59 +02:00
sys_fs . warning ( " sys_fs_stat(path=%s, sb=*0x%x) " , path , sb ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2017-12-16 01:03:49 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2018-03-21 20:02:36 +01:00
2018-09-11 18:02:19 +02:00
const std : : string local_path = vfs : : get ( vpath ) ;
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-01-03 21:29:39 +01:00
2020-10-02 13:37:58 +02:00
if ( mp = = & g_mp_sys_dev_root )
2018-09-11 18:02:19 +02:00
{
2020-08-29 20:29:19 +02:00
sb - > mode = CELL_FS_S_IFDIR | 0711 ;
sb - > uid = - 1 ;
sb - > gid = - 1 ;
sb - > atime = - 1 ;
sb - > mtime = - 1 ;
sb - > ctime = - 1 ;
sb - > size = 258 ;
sb - > blksize = 512 ;
2018-09-11 18:02:19 +02:00
return CELL_OK ;
}
2015-03-12 20:02:02 +01:00
2016-04-14 00:23:53 +02:00
if ( local_path . empty ( ) )
2015-04-21 17:16:29 +02:00
{
2023-12-27 04:59:41 +01:00
// This syscall can be used by games and VSH to test the presence of dev_usb000 ~ dev_usb127
// Thus there is no need to fuss about CELL_ENOTMOUNTED in this case
return { sys_fs . warning , CELL_ENOTMOUNTED , path } ;
2015-04-21 17:16:29 +02:00
}
2015-03-12 20:02:02 +01:00
2022-09-18 20:19:34 +02:00
std : : unique_lock lock ( mp - > mutex ) ;
2020-01-04 22:55:15 +01:00
2017-09-18 23:03:26 +02:00
fs : : stat_t info { } ;
2023-07-18 23:30:36 +02:00
if ( ! fs : : get_stat ( local_path , info ) )
2015-03-12 20:02:02 +01:00
{
2017-09-08 16:17:03 +02:00
switch ( auto error = fs : : g_tls_error )
{
2018-08-26 23:38:21 +02:00
case fs : : error : : noent :
{
// Try to analyse split file (TODO)
u64 total_size = 0 ;
2017-09-08 16:17:03 +02:00
2020-01-05 15:31:34 +01:00
for ( u32 i = 66601 ; i < = 66699 ; i + + )
2018-08-26 23:38:21 +02:00
{
2023-07-18 23:30:36 +02:00
if ( fs : : get_stat ( fmt : : format ( " %s.%u " , local_path , i ) , info ) & & ! info . is_directory )
2018-08-26 23:38:21 +02:00
{
total_size + = info . size ;
}
else
{
break ;
}
}
// Use attributes from the first fragment (consistently with sys_fs_open+fstat)
2023-07-18 23:30:36 +02:00
if ( fs : : get_stat ( local_path + " .66600 " , info ) & & ! info . is_directory )
2018-08-26 23:38:21 +02:00
{
// Success
2020-01-05 15:31:34 +01:00
info . size + = total_size ;
2018-08-26 23:38:21 +02:00
break ;
}
2022-11-26 11:01:55 +01:00
return { mp = = & g_mp_sys_dev_hdd1 ? sys_fs . warning : sys_fs . error , CELL_ENOENT , path } ;
2018-08-26 23:38:21 +02:00
}
default :
{
sys_fs . error ( " sys_fs_stat(): unknown error %s " , error ) ;
return { CELL_EIO , path } ;
}
}
2015-03-12 20:02:02 +01:00
}
2022-09-18 20:19:34 +02:00
lock . unlock ( ) ;
ppu . check_state ( ) ;
2022-10-13 16:56:40 +02:00
s32 mode = info . is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666 ;
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2022-10-13 16:56:40 +02:00
{
// Remove write permissions
mode & = ~ 0222 ;
}
sb - > mode = mode ;
2020-01-04 21:12:46 +01:00
sb - > uid = mp - > flags & lv2_mp_flag : : no_uid_gid ? - 1 : 0 ;
sb - > gid = mp - > flags & lv2_mp_flag : : no_uid_gid ? - 1 : 0 ;
2015-04-19 18:02:35 +02:00
sb - > atime = info . atime ;
sb - > mtime = info . mtime ;
sb - > ctime = info . ctime ;
2020-08-29 20:29:19 +02:00
sb - > size = info . is_directory ? mp - > block_size : info . size ;
2020-01-07 20:14:01 +01:00
sb - > blksize = mp - > block_size ;
2015-03-12 20:02:02 +01:00
2015-04-19 18:02:35 +02:00
return CELL_OK ;
2015-03-12 20:02:02 +01:00
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_fstat ( ppu_thread & ppu , u32 fd , vm : : ptr < CellFsStat > sb )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-01-12 22:57:16 +01:00
sys_fs . warning ( " sys_fs_fstat(fd=%d, sb=*0x%x) " , fd , sb ) ;
2015-03-12 20:02:02 +01:00
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2015-03-16 17:20:02 +01:00
2015-04-12 03:36:25 +02:00
if ( ! file )
2015-03-16 17:20:02 +01:00
{
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-16 17:20:02 +01:00
}
2015-03-12 20:02:02 +01:00
2022-09-18 20:19:34 +02:00
std : : unique_lock lock ( file - > mp - > mutex ) ;
2015-04-19 19:57:04 +02:00
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2019-11-09 12:35:41 +01:00
if ( file - > lock = = 2 )
{
return CELL_EIO ;
}
2023-07-11 20:40:30 +02:00
const fs : : stat_t info = file - > file . get_stat ( ) ;
2022-09-18 20:19:34 +02:00
lock . unlock ( ) ;
ppu . check_state ( ) ;
2015-04-19 19:57:04 +02:00
2022-10-13 16:56:40 +02:00
s32 mode = info . is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666 ;
2023-05-25 08:31:17 +02:00
if ( file - > mp . read_only )
2022-10-13 16:56:40 +02:00
{
// Remove write permissions
mode & = ~ 0222 ;
}
sb - > mode = mode ;
2020-01-04 21:12:46 +01:00
sb - > uid = file - > mp - > flags & lv2_mp_flag : : no_uid_gid ? - 1 : 0 ;
sb - > gid = file - > mp - > flags & lv2_mp_flag : : no_uid_gid ? - 1 : 0 ;
2015-04-19 19:57:04 +02:00
sb - > atime = info . atime ;
sb - > mtime = info . mtime ;
2015-04-21 20:18:15 +02:00
sb - > ctime = info . ctime ; // ctime may be incorrect
2015-04-19 19:57:04 +02:00
sb - > size = info . size ;
2020-01-07 20:14:01 +01:00
sb - > blksize = file - > mp - > block_size ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_link ( ppu_thread & , vm : : cptr < char > from , vm : : cptr < char > to )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_link(from=%s, to=%s) " , from , to ) ;
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_mkdir ( ppu_thread & ppu , vm : : cptr < char > path , s32 mode )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-11 01:29:59 +02:00
sys_fs . warning ( " sys_fs_mkdir(path=%s, mode=%#o) " , path , mode ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2018-03-21 20:02:36 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2018-03-21 20:02:36 +01:00
2018-09-11 18:02:19 +02:00
const std : : string local_path = vfs : : get ( vpath ) ;
2017-09-18 23:03:26 +02:00
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-10-02 13:37:58 +02:00
if ( mp = = & g_mp_sys_dev_root )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_EEXIST , path } ;
2017-09-18 23:03:26 +02:00
}
2018-09-11 18:02:19 +02:00
if ( local_path . empty ( ) )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_ENOTMOUNTED , path } ;
2017-09-18 23:03:26 +02:00
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
return { CELL_EROFS , path } ;
}
2024-02-08 06:49:46 +01:00
if ( ! fs : : exists ( local_path ) & & ! has_fs_write_rights ( path . get_ptr ( ) ) )
{
return { CELL_EACCES , path } ;
}
2020-01-04 22:55:15 +01:00
std : : lock_guard lock ( mp - > mutex ) ;
2018-11-11 10:54:32 +01:00
if ( ! fs : : create_dir ( local_path ) )
2015-03-16 17:20:02 +01:00
{
2017-09-08 16:17:03 +02:00
switch ( auto error = fs : : g_tls_error )
{
2022-11-26 11:01:55 +01:00
case fs : : error : : noent :
{
return { mp = = & g_mp_sys_dev_hdd1 ? sys_fs . warning : sys_fs . error , CELL_ENOENT , path } ;
}
case fs : : error : : exist :
{
return { sys_fs . warning , CELL_EEXIST , path } ;
}
2017-09-08 16:17:03 +02:00
default : sys_fs . error ( " sys_fs_mkdir(): unknown error %s " , error ) ;
}
2015-03-12 20:02:02 +01:00
2017-08-08 21:13:48 +02:00
return { CELL_EIO , path } ; // ???
2015-03-16 17:20:02 +01:00
}
2015-03-12 20:02:02 +01:00
2016-08-11 01:29:59 +02:00
sys_fs . notice ( " sys_fs_mkdir(): directory %s created " , path ) ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_rename ( ppu_thread & ppu , vm : : cptr < char > from , vm : : cptr < char > to )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-11 01:29:59 +02:00
sys_fs . warning ( " sys_fs_rename(from=%s, to=%s) " , from , to ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ from_error , vfrom ] = translate_to_str ( from ) ;
2021-11-12 18:32:35 +01:00
if ( from_error )
{
return { from_error , vfrom } ;
}
2017-09-18 23:03:26 +02:00
2023-12-11 07:45:38 +01:00
const auto [ to_error , vto ] = translate_to_str ( to ) ;
2021-11-12 18:32:35 +01:00
if ( to_error )
{
return { to_error , vto } ;
}
const std : : string local_from = vfs : : get ( vfrom ) ;
2018-09-11 18:02:19 +02:00
const std : : string local_to = vfs : : get ( vto ) ;
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vfrom ) ;
const auto & mp_to = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vto ) ;
2020-10-02 13:37:58 +02:00
if ( mp = = & g_mp_sys_dev_root | | mp_to = = & g_mp_sys_dev_root )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return CELL_EPERM ;
2017-09-18 23:03:26 +02:00
}
2017-12-16 01:03:49 +01:00
2018-09-11 18:02:19 +02:00
if ( local_from . empty ( ) | | local_to . empty ( ) )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return CELL_ENOTMOUNTED ;
2017-09-18 23:03:26 +02:00
}
2020-10-02 13:37:58 +02:00
if ( mp ! = mp_to )
2020-01-04 22:55:15 +01:00
{
return CELL_EXDEV ;
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
return CELL_EROFS ;
}
2020-09-11 13:06:46 +02:00
// Done in vfs::host::rename
//std::lock_guard lock(mp->mutex);
2020-01-04 22:55:15 +01:00
2023-05-25 08:31:17 +02:00
if ( ! vfs : : host : : rename ( local_from , local_to , mp . mp , false ) )
2015-03-12 20:02:02 +01:00
{
2017-08-30 16:14:30 +02:00
switch ( auto error = fs : : g_tls_error )
{
case fs : : error : : noent : return { CELL_ENOENT , from } ;
case fs : : error : : exist : return { CELL_EEXIST , to } ;
default : sys_fs . error ( " sys_fs_rename(): unknown error %s " , error ) ;
}
return { CELL_EIO , from } ; // ???
2015-03-12 20:02:02 +01:00
}
2016-08-11 01:29:59 +02:00
sys_fs . notice ( " sys_fs_rename(): %s renamed to %s " , from , to ) ;
2015-08-08 15:17:28 +02:00
return CELL_OK ;
2015-03-12 20:02:02 +01:00
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_rmdir ( ppu_thread & ppu , vm : : cptr < char > path )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-11 01:29:59 +02:00
sys_fs . warning ( " sys_fs_rmdir(path=%s) " , path ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2018-03-21 20:02:36 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2018-03-21 20:02:36 +01:00
2018-09-11 18:02:19 +02:00
const std : : string local_path = vfs : : get ( vpath ) ;
2017-09-18 23:03:26 +02:00
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-10-02 13:37:58 +02:00
if ( mp = = & g_mp_sys_dev_root )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_EPERM , path } ;
2017-09-18 23:03:26 +02:00
}
2018-09-11 18:02:19 +02:00
if ( local_path . empty ( ) )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_ENOTMOUNTED , path } ;
2017-09-18 23:03:26 +02:00
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
return { CELL_EROFS , path } ;
}
2024-02-08 06:49:46 +01:00
if ( fs : : is_dir ( local_path ) & & ! has_fs_write_rights ( path . get_ptr ( ) ) )
{
return { CELL_EACCES } ;
}
2020-01-04 22:55:15 +01:00
std : : lock_guard lock ( mp - > mutex ) ;
2017-09-18 23:03:26 +02:00
if ( ! fs : : remove_dir ( local_path ) )
2015-03-16 17:20:02 +01:00
{
2016-05-13 15:55:34 +02:00
switch ( auto error = fs : : g_tls_error )
2016-04-14 00:23:53 +02:00
{
2017-08-08 21:13:48 +02:00
case fs : : error : : noent : return { CELL_ENOENT , path } ;
2017-09-12 19:05:38 +02:00
case fs : : error : : notempty : return { CELL_ENOTEMPTY , path } ;
2016-07-21 00:00:31 +02:00
default : sys_fs . error ( " sys_fs_rmdir(): unknown error %s " , error ) ;
2016-04-14 00:23:53 +02:00
}
2015-03-12 20:02:02 +01:00
2017-08-08 21:13:48 +02:00
return { CELL_EIO , path } ; // ???
2015-04-20 17:53:31 +02:00
}
2016-08-11 01:29:59 +02:00
sys_fs . notice ( " sys_fs_rmdir(): directory %s removed " , path ) ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_unlink ( ppu_thread & ppu , vm : : cptr < char > path )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-11 01:29:59 +02:00
sys_fs . warning ( " sys_fs_unlink(path=%s) " , path ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2018-03-21 20:02:36 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2017-12-16 01:03:49 +01:00
2018-09-11 18:02:19 +02:00
const std : : string local_path = vfs : : get ( vpath ) ;
2017-09-18 23:03:26 +02:00
2023-12-27 04:59:41 +01:00
std : : string mount_path = fs : : get_parent_dir ( vpath ) ; // Use its parent directory as fallback
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath , true , & mount_path ) ;
2019-09-25 03:57:56 +02:00
2020-10-02 13:37:58 +02:00
if ( mp = = & g_mp_sys_dev_root )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_EISDIR , path } ;
2017-09-18 23:03:26 +02:00
}
2018-09-11 18:02:19 +02:00
if ( local_path . empty ( ) )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_ENOTMOUNTED , path } ;
2017-09-18 23:03:26 +02:00
}
2019-04-12 11:24:36 +02:00
if ( fs : : is_dir ( local_path ) )
{
return { CELL_EISDIR , path } ;
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
return { CELL_EROFS , path } ;
}
std : : lock_guard lock ( mp - > mutex ) ;
2023-12-27 04:59:41 +01:00
if ( ! vfs : : host : : unlink ( local_path , vfs : : get ( mount_path ) ) )
2015-03-16 17:20:02 +01:00
{
2016-05-13 15:55:34 +02:00
switch ( auto error = fs : : g_tls_error )
2016-04-14 00:23:53 +02:00
{
2022-11-26 11:01:55 +01:00
case fs : : error : : noent :
{
return { mp = = & g_mp_sys_dev_hdd1 ? sys_fs . warning : sys_fs . error , CELL_ENOENT , path } ;
}
2016-07-21 00:00:31 +02:00
default : sys_fs . error ( " sys_fs_unlink(): unknown error %s " , error ) ;
2016-04-14 00:23:53 +02:00
}
2015-03-12 20:02:02 +01:00
2017-08-08 21:13:48 +02:00
return { CELL_EIO , path } ; // ???
2015-04-20 17:53:31 +02:00
}
2016-08-11 01:29:59 +02:00
sys_fs . notice ( " sys_fs_unlink(): file %s deleted " , path ) ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_access ( ppu_thread & , vm : : cptr < char > path , s32 mode )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_access(path=%s, mode=%#o) " , path , mode ) ;
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_fcntl ( ppu_thread & ppu , u32 fd , u32 op , vm : : ptr < void > _arg , u32 _size )
2015-03-15 01:41:08 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2016-06-02 17:16:01 +02:00
sys_fs . trace ( " sys_fs_fcntl(fd=%d, op=0x%x, arg=*0x%x, size=0x%x) " , fd , op , _arg , _size ) ;
switch ( op )
{
2021-07-17 19:19:13 +02:00
case 0x80000004 : // Unknown
{
if ( _size > 4 )
{
return CELL_EINVAL ;
}
const auto arg = vm : : static_ptr_cast < u32 > ( _arg ) ;
* arg = 0 ;
break ;
}
2017-04-26 14:48:50 +02:00
case 0x80000006 : // cellFsAllocateFileAreaByFdWithInitialData
{
break ;
}
case 0x80000007 : // cellFsAllocateFileAreaByFdWithoutZeroFill
{
break ;
}
case 0x80000008 : // cellFsChangeFileSizeByFdWithoutAllocation
{
break ;
}
case 0x8000000a : // cellFsReadWithOffset
case 0x8000000b : // cellFsWriteWithOffset
2016-06-02 17:16:01 +02:00
{
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-06-02 17:16:01 +02:00
const auto arg = vm : : static_ptr_cast < lv2_file_op_rw > ( _arg ) ;
if ( _size < arg . size ( ) )
{
return CELL_EINVAL ;
}
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2016-06-02 17:16:01 +02:00
if ( ! file )
{
return CELL_EBADF ;
}
2017-04-26 14:48:50 +02:00
if ( op = = 0x8000000a & & file - > flags & CELL_FS_O_WRONLY )
2016-06-02 17:16:01 +02:00
{
return CELL_EBADF ;
}
2017-04-26 14:48:50 +02:00
if ( op = = 0x8000000b & & ! ( file - > flags & CELL_FS_O_ACCMODE ) )
2016-06-02 17:16:01 +02:00
{
return CELL_EBADF ;
}
2020-01-05 16:14:07 +01:00
if ( op = = 0x8000000b & & file - > flags & CELL_FS_O_APPEND )
{
return CELL_EBADF ;
}
2023-05-25 08:31:17 +02:00
if ( op = = 0x8000000b & & file - > mp . read_only )
2020-01-04 22:55:15 +01:00
{
return CELL_EROFS ;
}
2021-09-21 11:49:59 +02:00
if ( op = = 0x8000000b & & file - > type ! = lv2_file_type : : regular & & arg - > size )
{
sys_fs . error ( " %s type: Writing %u bytes to FD=%d (path=%s) " , file - > type , arg - > size , file - > name . data ( ) ) ;
}
2023-10-02 17:23:21 +02:00
std : : unique_lock wlock ( file - > mp - > mutex , std : : defer_lock ) ;
std : : shared_lock rlock ( file - > mp - > mutex , std : : defer_lock ) ;
if ( op = = 0x8000000b )
{
// Writer lock
wlock . lock ( ) ;
}
else
{
// Reader lock (not needing exclusivity in this special case because the state should not change)
rlock . lock ( ) ;
}
2016-06-02 17:16:01 +02:00
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2020-01-18 21:23:16 +01:00
if ( file - > lock = = 2 )
2017-04-24 22:30:15 +02:00
{
2020-01-18 21:23:16 +01:00
return CELL_EIO ;
}
2019-11-09 12:35:41 +01:00
2020-01-18 21:23:16 +01:00
if ( op = = 0x8000000b & & file - > lock )
{
2017-04-24 22:30:15 +02:00
return CELL_EBUSY ;
}
2023-10-02 17:23:21 +02:00
u64 old_pos = umax ;
const u64 op_pos = arg - > offset ;
if ( op = = 0x8000000b )
{
old_pos = file - > file . pos ( ) ;
file - > file . seek ( op_pos ) ;
}
2016-06-02 17:16:01 +02:00
2017-04-26 14:48:50 +02:00
arg - > out_size = op = = 0x8000000a
2023-10-02 17:23:21 +02:00
? file - > op_read ( arg - > buf , arg - > size , op_pos )
2016-06-02 17:16:01 +02:00
: file - > op_write ( arg - > buf , arg - > size ) ;
2023-10-02 17:23:21 +02:00
if ( op = = 0x8000000b )
{
ensure ( old_pos = = file - > file . seek ( old_pos ) ) ;
}
2016-06-02 17:16:01 +02:00
2021-10-15 11:11:16 +02:00
// TODO: EDATA corruption detection
2016-06-02 17:16:01 +02:00
arg - > out_code = CELL_OK ;
2017-04-26 14:48:50 +02:00
return CELL_OK ;
2016-06-02 17:16:01 +02:00
}
2017-03-04 12:54:53 +01:00
case 0x80000009 : // cellFsSdataOpenByFd
{
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2017-03-04 12:54:53 +01:00
const auto arg = vm : : static_ptr_cast < lv2_file_op_09 > ( _arg ) ;
if ( _size < arg . size ( ) )
{
return CELL_EINVAL ;
}
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2017-03-04 12:54:53 +01:00
if ( ! file )
{
2024-02-13 21:23:57 +01:00
return { CELL_EBADF , " fd=%u " , fd } ;
2017-03-04 12:54:53 +01:00
}
2024-02-13 21:23:57 +01:00
sys_fs . warning ( " sys_fs_fcntl(0x80000009): fd=%d, arg->offset=0x%x, size=0x%x (file: %s) " , fd , arg - > offset , _size , * file ) ;
2020-09-11 13:06:46 +02:00
std : : lock_guard lock ( file - > mp - > mutex ) ;
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
2024-02-13 21:23:57 +01:00
return { CELL_EBADF , " fd=%u " , fd } ;
2021-03-12 15:25:03 +01:00
}
2017-03-10 10:06:36 +01:00
auto sdata_file = std : : make_unique < EDATADecrypter > ( lv2_file : : make_view ( file , arg - > offset ) ) ;
2017-03-07 01:59:05 +01:00
if ( ! sdata_file - > ReadHeader ( ) )
{
2024-02-13 21:23:57 +01:00
return { CELL_EFSSPECIFIC , " fd=%u " , fd } ;
2017-03-07 01:59:05 +01:00
}
fs : : file stream ;
stream . reset ( std : : move ( sdata_file ) ) ;
2024-12-22 19:59:48 +01:00
if ( const u32 id = idm : : import < lv2_fs_object , lv2_file > ( [ & file = * file , & stream = stream ] ( ) - > shared_ptr < lv2_file >
2020-04-04 16:11:21 +02:00
{
2021-03-02 12:59:19 +01:00
if ( ! g_fxo - > get < loaded_npdrm_keys > ( ) . npdrm_fds . try_inc ( 16 ) )
2020-04-04 16:11:21 +02:00
{
2024-12-22 19:59:48 +01:00
return null_ptr ;
2020-04-04 16:11:21 +02:00
}
2024-12-22 19:59:48 +01:00
return stx : : make_shared < lv2_file > ( file , std : : move ( stream ) , file . mode , CELL_FS_O_RDONLY , file . real_path , lv2_file_type : : sdata ) ;
2020-04-04 16:11:21 +02:00
} ) )
2017-03-04 12:54:53 +01:00
{
arg - > out_code = CELL_OK ;
arg - > out_fd = id ;
return CELL_OK ;
}
// Out of file descriptors
return CELL_EMFILE ;
}
2017-04-25 02:13:45 +02:00
case 0xc0000002 : // cellFsGetFreeSize (TODO)
{
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2017-04-25 02:13:45 +02:00
const auto arg = vm : : static_ptr_cast < lv2_file_c0000002 > ( _arg ) ;
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( " /dev_hdd0 " ) ;
2017-09-18 23:03:26 +02:00
2020-01-07 20:14:01 +01:00
arg - > out_block_size = mp - > block_size ;
2020-09-18 21:34:42 +02:00
arg - > out_block_count = ( 40ull * 1024 * 1024 * 1024 - 1 ) / mp - > block_size ; // Read explanation in cellHddGameCheck
2017-04-25 02:13:45 +02:00
return CELL_OK ;
}
2024-01-08 00:52:28 +01:00
case 0xc0000003 : // cellFsUtilitySetFakeSize
{
break ;
}
case 0xc0000004 : // cellFsUtilityGetFakeSize
{
break ;
}
2017-04-22 15:01:24 +02:00
case 0xc0000006 : // Unknown
{
const auto arg = vm : : static_ptr_cast < lv2_file_c0000006 > ( _arg ) ;
2020-02-19 16:26:41 +01:00
if ( arg - > size ! = 0x20u )
2020-01-18 23:20:31 +01:00
{
sys_fs . error ( " sys_fs_fcntl(0xc0000006): invalid size (0x%x) " , arg - > size ) ;
break ;
}
2020-02-19 16:26:41 +01:00
if ( arg - > _x4 ! = 0x10u | | arg - > _x8 ! = 0x18u )
2020-01-18 23:20:31 +01:00
{
sys_fs . error ( " sys_fs_fcntl(0xc0000006): invalid args (0x%x, 0x%x) " , arg - > _x4 , arg - > _x8 ) ;
break ;
}
// Load mountpoint (doesn't support multiple // at the start)
std : : string_view vpath { arg - > name . get_ptr ( ) , arg - > name_size } ;
sys_fs . notice ( " sys_fs_fcntl(0xc0000006): %s " , vpath ) ;
// Check only mountpoint
vpath = vpath . substr ( 0 , vpath . find_first_of ( " / " , 1 ) ) ;
// Some mountpoints seem to be handled specially
if ( false )
{
// TODO: /dev_hdd1, /dev_usb000, /dev_flash
//arg->out_code = CELL_OK;
//arg->out_id = 0x1b5;
}
2020-01-09 01:57:44 +01:00
2020-01-18 23:20:31 +01:00
arg - > out_code = CELL_ENOTSUP ;
arg - > out_id = 0 ;
2017-04-22 15:01:24 +02:00
return CELL_OK ;
}
2017-04-26 14:48:50 +02:00
case 0xc0000007 : // cellFsArcadeHddSerialNumber
{
2022-06-11 14:12:42 +02:00
const auto arg = vm : : static_ptr_cast < lv2_file_c0000007 > ( _arg ) ;
2023-05-02 17:57:39 +02:00
2023-07-26 11:36:44 +02:00
arg - > out_code = CELL_OK ;
2023-05-02 17:57:39 +02:00
2023-07-26 11:36:44 +02:00
if ( const auto size = arg - > model_size ; size > 0 )
strcpy_trunc ( std : : span ( arg - > model . get_ptr ( ) , size ) ,
fmt : : format ( " %-*s " , size - 1 , g_cfg . sys . hdd_model . to_string ( ) ) ) ; // Example: "TOSHIBA MK3265GSX H "
2023-05-02 17:57:39 +02:00
2023-07-26 11:36:44 +02:00
if ( const auto size = arg - > serial_size ; size > 0 )
strcpy_trunc ( std : : span ( arg - > serial . get_ptr ( ) , size ) ,
fmt : : format ( " %*s " , size - 1 , g_cfg . sys . hdd_serial . to_string ( ) ) ) ; // Example: " 0A1B2C3D4"
else
return CELL_EFAULT ; // CELL_EFAULT is returned only when arg->serial_size == 0
2023-05-02 17:57:39 +02:00
2021-12-02 16:41:53 +01:00
return CELL_OK ;
2017-04-26 14:48:50 +02:00
}
case 0xc0000008 : // cellFsSetDefaultContainer, cellFsSetIoBuffer, cellFsSetIoBufferFromDefaultContainer
{
2022-06-11 14:12:42 +02:00
// Allocates memory from a container/default container to a specific fd or default IO processing
const auto arg = vm : : static_ptr_cast < lv2_file_c0000008 > ( _arg ) ;
auto & default_container = g_fxo - > get < default_sys_fs_container > ( ) ;
std : : lock_guard def_container_lock ( default_container . mutex ) ;
if ( fd = = 0xFFFFFFFF )
{
// No check on container is done when setting default container
default_container . id = arg - > size ? : : narrow < u32 > ( arg - > container_id ) : 0u ;
default_container . cap = arg - > size ;
default_container . used = 0 ;
arg - > out_code = CELL_OK ;
return CELL_OK ;
}
2024-12-22 19:59:48 +01:00
auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2022-06-11 14:12:42 +02:00
if ( ! file )
{
return CELL_EBADF ;
}
2024-12-22 19:59:48 +01:00
if ( auto ct = idm : : get_unlocked < lv2_memory_container > ( file - > ct_id ) )
2022-06-11 14:12:42 +02:00
{
ct - > free ( file - > ct_used ) ;
if ( default_container . id = = file - > ct_id )
{
default_container . used - = file - > ct_used ;
}
}
file - > ct_id = 0 ;
file - > ct_used = 0 ;
// Aligns on lower bound
u32 actual_size = arg - > size - ( arg - > size % ( ( arg - > page_type & CELL_FS_IO_BUFFER_PAGE_SIZE_64KB ) ? 0x10000 : 0x100000 ) ) ;
if ( ! actual_size )
{
arg - > out_code = CELL_OK ;
return CELL_OK ;
}
u32 new_container_id = arg - > container_id = = 0xFFFFFFFF ? default_container . id : : : narrow < u32 > ( arg - > container_id ) ;
if ( default_container . id = = new_container_id & & ( default_container . used + actual_size ) > default_container . cap )
{
return CELL_ENOMEM ;
}
const auto ct = idm : : get < lv2_memory_container > ( new_container_id , [ & ] ( lv2_memory_container & ct ) - > CellError
{
if ( ! ct . take ( actual_size ) )
{
return CELL_ENOMEM ;
}
return { } ;
} ) ;
if ( ! ct )
{
return CELL_ESRCH ;
}
if ( ct . ret )
{
return ct . ret ;
}
if ( default_container . id = = new_container_id )
{
default_container . used + = actual_size ;
}
file - > ct_id = new_container_id ;
file - > ct_used = actual_size ;
arg - > out_code = CELL_OK ;
return CELL_OK ;
2017-04-26 14:48:50 +02:00
}
2023-05-01 15:08:38 +02:00
case 0xc0000015 : // USB Vid/Pid query
case 0xc000001c : // USB Vid/Pid/Serial query
2017-04-26 14:48:50 +02:00
{
2021-12-02 16:41:53 +01:00
const auto arg = vm : : static_ptr_cast < lv2_file_c0000015 > ( _arg ) ;
2023-05-01 15:08:38 +02:00
const bool with_serial = op = = 0xc000001c ;
2021-12-02 16:41:53 +01:00
2023-05-01 15:08:38 +02:00
if ( arg - > size ! = ( with_serial ? sizeof ( lv2_file_c000001c ) : sizeof ( lv2_file_c0000015 ) ) )
2021-12-02 16:41:53 +01:00
{
2023-05-01 15:08:38 +02:00
sys_fs . error ( " sys_fs_fcntl(0x%08x): invalid size (0x%x) " , op , arg - > size ) ;
2021-12-02 16:41:53 +01:00
break ;
}
if ( arg - > _x4 ! = 0x10u | | arg - > _x8 ! = 0x18u )
{
2023-05-01 15:08:38 +02:00
sys_fs . error ( " sys_fs_fcntl(0x%08x): invalid args (0x%x, 0x%x) " , op , arg - > _x4 , arg - > _x8 ) ;
2021-12-02 16:41:53 +01:00
break ;
}
2023-07-26 11:36:57 +02:00
std : : string_view vpath { arg - > path . get_ptr ( ) , arg - > path_size } ;
if ( vpath . size ( ) = = 0 )
return CELL_ENOMEM ;
2022-05-08 00:53:04 +02:00
// Trim trailing '\0'
2023-05-01 15:08:09 +02:00
if ( const auto trim_pos = vpath . find ( ' \0 ' ) ; trim_pos ! = umax )
2022-05-08 00:53:04 +02:00
vpath . remove_suffix ( vpath . size ( ) - trim_pos ) ;
2023-07-26 11:36:57 +02:00
arg - > out_code = CELL_ENOTMOUNTED ; // arg->out_code is set to CELL_ENOTMOUNTED on real hardware when the device doesn't exist or when the device isn't USB
2023-05-01 15:08:09 +02:00
2023-07-26 11:36:57 +02:00
if ( ! vfs : : get ( vpath ) . empty ( ) )
2023-05-01 15:08:09 +02:00
{
2023-07-26 11:36:57 +02:00
if ( const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath , true ) ; mp = = & g_mp_sys_dev_usb )
{
const cfg : : device_info device = g_cfg_vfs . get_device ( g_cfg_vfs . dev_usb , fmt : : format ( " %s%s " , mp - > root , mp . device . substr ( mp - > device . size ( ) ) ) ) ;
const auto usb_ids = device . get_usb_ids ( ) ;
std : : tie ( arg - > vendorID , arg - > productID ) = usb_ids ;
2023-05-01 15:08:09 +02:00
2023-07-26 11:36:57 +02:00
if ( with_serial )
{
const auto arg_c000001c = vm : : static_ptr_cast < lv2_file_c000001c > ( _arg ) ;
const std : : u16string serial = utf8_to_utf16 ( device . serial ) ; // Serial needs to be encoded to utf-16 BE
std : : copy_n ( serial . begin ( ) , std : : min ( serial . size ( ) , sizeof ( arg_c000001c - > serial ) / sizeof ( u16 ) ) , arg_c000001c - > serial ) ;
}
2021-12-02 16:41:53 +01:00
2023-07-26 11:36:57 +02:00
arg - > out_code = CELL_OK ;
sys_fs . trace ( " sys_fs_fcntl(0x%08x): found device \" %s \" (vid=0x%04x, pid=0x%04x, serial= \" %s \" ) " , op , mp . device , usb_ids . first , usb_ids . second , device . serial ) ;
}
2023-05-01 15:08:38 +02:00
}
2021-12-02 16:41:53 +01:00
return CELL_OK ;
2017-04-26 14:48:50 +02:00
}
case 0xc0000016 : // ps2disc_8160A811
{
break ;
}
case 0xc000001a : // cellFsSetDiscReadRetrySetting, 5731DF45
{
2022-06-11 14:12:42 +02:00
[[maybe_unused]] const auto arg = vm : : static_ptr_cast < lv2_file_c000001a > ( _arg ) ;
return CELL_OK ;
2017-04-26 14:48:50 +02:00
}
case 0xc0000021 : // 9FDBBA89
{
break ;
}
case 0xe0000000 : // Unknown (cellFsGetBlockSize)
{
break ;
}
2017-05-07 00:08:44 +02:00
case 0xe0000001 : // Unknown (cellFsStat)
2017-04-26 14:48:50 +02:00
{
break ;
}
case 0xe0000003 : // Unknown
{
break ;
}
case 0xe0000004 : // Unknown
{
break ;
}
case 0xe0000005 : // Unknown (cellFsMkdir)
{
break ;
}
case 0xe0000006 : // Unknown
{
break ;
}
case 0xe0000007 : // Unknown
{
break ;
}
case 0xe0000008 : // Unknown (cellFsAclRead)
{
break ;
}
case 0xe0000009 : // Unknown (cellFsAccess)
{
break ;
}
case 0xe000000a : // Unknown (E3D28395)
{
break ;
}
case 0xe000000b : // Unknown (cellFsRename, FF29F478)
{
break ;
}
case 0xe000000c : // Unknown (cellFsTruncate)
{
break ;
}
case 0xe000000d : // Unknown (cellFsUtime)
{
break ;
}
case 0xe000000e : // Unknown (cellFsAclWrite)
{
break ;
}
case 0xe000000f : // Unknown (cellFsChmod)
{
break ;
}
case 0xe0000010 : // Unknown (cellFsChown)
{
break ;
}
case 0xe0000011 : // Unknown
{
break ;
}
2017-04-22 14:29:20 +02:00
case 0xe0000012 : // cellFsGetDirectoryEntries
{
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2017-04-22 14:29:20 +02:00
const auto arg = vm : : static_ptr_cast < lv2_file_op_dir : : dir_info > ( _arg ) ;
if ( _size < arg . size ( ) )
{
return CELL_EINVAL ;
}
2024-12-22 19:59:48 +01:00
const auto directory = idm : : get_unlocked < lv2_fs_object , lv2_dir > ( fd ) ;
2017-04-22 14:29:20 +02:00
if ( ! directory )
{
return CELL_EBADF ;
}
2022-10-13 17:18:24 +02:00
ppu . check_state ( ) ;
u32 read_count = 0 ;
2020-01-03 21:29:39 +01:00
// NOTE: This function is actually capable of reading only one entry at a time
2022-10-13 17:18:24 +02:00
if ( const u32 max = arg - > max )
2017-04-22 14:29:20 +02:00
{
2022-10-13 17:18:24 +02:00
const auto arg_ptr = + arg - > ptr ;
2020-01-03 21:29:39 +01:00
2018-09-15 16:04:35 +02:00
if ( auto * info = directory - > dir_read ( ) )
2017-04-22 14:29:20 +02:00
{
2022-10-13 17:18:24 +02:00
auto & entry = arg_ptr [ read_count + + ] ;
s32 mode = info - > is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666 ;
2017-04-22 14:29:20 +02:00
2023-05-25 08:31:17 +02:00
if ( directory - > mp . read_only )
2022-10-13 17:18:24 +02:00
{
// Remove write permissions
mode & = ~ 0222 ;
}
entry . attribute . mode = mode ;
2020-01-04 21:12:46 +01:00
entry . attribute . uid = directory - > mp - > flags & lv2_mp_flag : : no_uid_gid ? - 1 : 0 ;
entry . attribute . gid = directory - > mp - > flags & lv2_mp_flag : : no_uid_gid ? - 1 : 0 ;
2018-09-15 16:04:35 +02:00
entry . attribute . atime = info - > atime ;
entry . attribute . mtime = info - > mtime ;
entry . attribute . ctime = info - > ctime ;
entry . attribute . size = info - > size ;
2020-01-07 20:14:01 +01:00
entry . attribute . blksize = directory - > mp - > block_size ;
2017-04-22 14:29:20 +02:00
2018-09-15 16:04:35 +02:00
entry . entry_name . d_type = info - > is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR ;
2020-12-18 08:39:54 +01:00
entry . entry_name . d_namlen = u8 ( std : : min < usz > ( info - > name . size ( ) , CELL_FS_MAX_FS_FILE_NAME_LENGTH ) ) ;
2018-09-15 16:04:35 +02:00
strcpy_trunc ( entry . entry_name . d_name , info - > name ) ;
2017-04-22 14:29:20 +02:00
}
2022-10-13 17:18:24 +02:00
2022-12-24 15:15:29 +01:00
// Apparently all this function does to additional buffer elements is to zeroize them
2022-10-13 17:18:24 +02:00
std : : memset ( arg_ptr . get_ptr ( ) + read_count , 0 , ( max - read_count ) * arg - > ptr . size ( ) ) ;
2017-04-22 14:29:20 +02:00
}
2022-10-13 17:18:24 +02:00
arg - > _size = read_count ;
2017-04-22 14:29:20 +02:00
arg - > _code = CELL_OK ;
return CELL_OK ;
}
2017-04-26 14:48:50 +02:00
case 0xe0000015 : // Unknown
{
break ;
}
case 0xe0000016 : // cellFsAllocateFileAreaWithInitialData
2016-06-02 17:16:01 +02:00
{
2017-04-26 14:48:50 +02:00
break ;
}
case 0xe0000017 : // cellFsAllocateFileAreaWithoutZeroFill
{
2017-05-07 00:08:44 +02:00
const auto arg = vm : : static_ptr_cast < lv2_file_e0000017 > ( _arg ) ;
2020-02-19 16:26:41 +01:00
if ( _size < arg - > size | | arg - > _x4 ! = 0x10u | | arg - > _x8 ! = 0x20u )
2017-05-07 00:08:44 +02:00
{
return CELL_EINVAL ;
}
2019-06-08 18:34:55 +02:00
arg - > out_code = sys_fs_truncate ( ppu , arg - > file_path , arg - > file_size ) ;
2017-05-07 00:08:44 +02:00
return CELL_OK ;
2017-04-26 14:48:50 +02:00
}
case 0xe0000018 : // cellFsChangeFileSizeWithoutAllocation
{
break ;
}
case 0xe0000019 : // Unknown
{
break ;
}
case 0xe000001b : // Unknown
{
break ;
}
case 0xe000001d : // Unknown
{
break ;
}
case 0xe000001e : // Unknown
{
break ;
}
case 0xe000001f : // Unknown
{
break ;
}
case 0xe0000020 : // Unknown
{
break ;
}
2021-04-24 20:43:09 +02:00
case 0xe0000025 : // cellFsSdataOpenWithVersion
2017-04-26 14:48:50 +02:00
{
2021-04-24 20:43:09 +02:00
const auto arg = vm : : static_ptr_cast < lv2_file_e0000025 > ( _arg ) ;
if ( arg - > size ! = 0x30u )
{
sys_fs . error ( " sys_fs_fcntl(0xe0000025): invalid size (0x%x) " , arg - > size ) ;
break ;
}
if ( arg - > _x4 ! = 0x10u | | arg - > _x8 ! = 0x28u )
{
sys_fs . error ( " sys_fs_fcntl(0xe0000025): invalid args (0x%x, 0x%x) " , arg - > _x4 , arg - > _x8 ) ;
break ;
}
std : : string_view vpath { arg - > name . get_ptr ( ) , arg - > name_size } ;
vpath = vpath . substr ( 0 , vpath . find_first_of ( ' \0 ' ) ) ;
sys_fs . notice ( " sys_fs_fcntl(0xe0000025): %s " , vpath ) ;
be_t < u64 > sdata_identifier = 0x18000000010 ;
lv2_file : : open_result_t result = lv2_file : : open ( vpath , 0 , 0 , & sdata_identifier , 8 ) ;
if ( result . error )
{
return result . error ;
}
2024-12-22 19:59:48 +01:00
if ( const u32 id = idm : : import < lv2_fs_object , lv2_file > ( [ & ] ( ) - > shared_ptr < lv2_file >
2021-04-24 20:43:09 +02:00
{
if ( ! g_fxo - > get < loaded_npdrm_keys > ( ) . npdrm_fds . try_inc ( 16 ) )
{
2024-12-22 19:59:48 +01:00
return null_ptr ;
2021-04-24 20:43:09 +02:00
}
2024-12-22 19:59:48 +01:00
return stx : : make_shared < lv2_file > ( result . ppath , std : : move ( result . file ) , 0 , 0 , std : : move ( result . real_path ) , lv2_file_type : : sdata ) ;
2021-04-24 20:43:09 +02:00
} ) )
{
arg - > out_code = CELL_OK ;
arg - > fd = id ;
return CELL_OK ;
}
// Out of file descriptors
return CELL_EMFILE ;
2016-06-02 17:16:01 +02:00
}
}
2015-03-15 01:41:08 +01:00
2020-03-08 20:00:05 +01:00
sys_fs . error ( " sys_fs_fcntl(): Unknown operation 0x%08x (fd=%d, arg=*0x%x, size=0x%x) " , op , fd , _arg , _size ) ;
2015-03-15 01:41:08 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_lseek ( ppu_thread & ppu , u32 fd , s64 offset , s32 whence , vm : : ptr < u64 > pos )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-01-12 22:57:16 +01:00
sys_fs . trace ( " sys_fs_lseek(fd=%d, offset=0x%llx, whence=0x%x, pos=*0x%x) " , fd , offset , whence , pos ) ;
2015-03-12 20:02:02 +01:00
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2015-03-16 17:20:02 +01:00
2015-04-12 03:36:25 +02:00
if ( ! file )
2015-03-16 17:20:02 +01:00
{
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-16 17:20:02 +01:00
}
2015-03-12 20:02:02 +01:00
2022-09-18 20:19:34 +02:00
std : : unique_lock lock ( file - > mp - > mutex ) ;
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2020-01-03 21:32:17 +01:00
if ( whence + 0u > = 3 )
{
return { CELL_EINVAL , whence } ;
}
2017-03-23 19:32:59 +01:00
const u64 result = file - > file . seek ( offset , static_cast < fs : : seek_mode > ( whence ) ) ;
2020-02-19 18:03:59 +01:00
if ( result = = umax )
2017-03-23 19:32:59 +01:00
{
switch ( auto error = fs : : g_tls_error )
{
2024-02-15 18:58:25 +01:00
case fs : : error : : inval : return { CELL_EINVAL , " fd=%u, offset=0x%x, whence=%d " , fd , offset , whence } ;
2017-03-23 19:32:59 +01:00
default : sys_fs . error ( " sys_fs_lseek(): unknown error %s " , error ) ;
}
return CELL_EIO ; // ???
}
2015-03-16 17:20:02 +01:00
2022-09-18 20:19:34 +02:00
lock . unlock ( ) ;
ppu . check_state ( ) ;
2017-03-23 19:32:59 +01:00
* pos = result ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-03-08 17:07:14 +01:00
error_code sys_fs_fdatasync ( ppu_thread & ppu , u32 fd )
2017-04-24 17:43:27 +02:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2017-04-24 17:43:27 +02:00
sys_fs . trace ( " sys_fs_fdadasync(fd=%d) " , fd ) ;
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2017-04-24 17:43:27 +02:00
2017-05-01 20:34:50 +02:00
if ( ! file | | ! ( file - > flags & CELL_FS_O_ACCMODE ) )
2017-04-24 17:43:27 +02:00
{
return CELL_EBADF ;
}
2020-01-04 22:55:15 +01:00
std : : lock_guard lock ( file - > mp - > mutex ) ;
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2017-04-24 17:43:27 +02:00
file - > file . sync ( ) ;
return CELL_OK ;
}
2019-03-08 17:07:14 +01:00
error_code sys_fs_fsync ( ppu_thread & ppu , u32 fd )
2017-04-24 17:43:27 +02:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2017-04-24 17:43:27 +02:00
sys_fs . trace ( " sys_fs_fsync(fd=%d) " , fd ) ;
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2017-04-24 17:43:27 +02:00
2017-05-01 20:34:50 +02:00
if ( ! file | | ! ( file - > flags & CELL_FS_O_ACCMODE ) )
2017-04-24 17:43:27 +02:00
{
return CELL_EBADF ;
}
2020-01-04 22:55:15 +01:00
std : : lock_guard lock ( file - > mp - > mutex ) ;
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2017-04-24 17:43:27 +02:00
file - > file . sync ( ) ;
return CELL_OK ;
}
2020-01-07 19:54:21 +01:00
error_code sys_fs_fget_block_size ( ppu_thread & ppu , u32 fd , vm : : ptr < u64 > sector_size , vm : : ptr < u64 > block_size , vm : : ptr < u64 > arg4 , vm : : ptr < s32 > out_flags )
2015-03-12 20:02:02 +01:00
{
2021-03-05 20:05:37 +01:00
ppu . state + = cpu_flag : : wait ;
2020-01-07 19:54:21 +01:00
sys_fs . warning ( " sys_fs_fget_block_size(fd=%d, sector_size=*0x%x, block_size=*0x%x, arg4=*0x%x, out_flags=*0x%x) " , fd , sector_size , block_size , arg4 , out_flags ) ;
2015-03-12 20:02:02 +01:00
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2015-03-16 17:20:02 +01:00
2015-04-12 03:36:25 +02:00
if ( ! file )
2015-03-16 17:20:02 +01:00
{
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-16 17:20:02 +01:00
}
2015-03-12 20:02:02 +01:00
2022-07-04 15:02:17 +02:00
static_cast < void > ( ppu . test_stopped ( ) ) ;
2021-03-05 20:05:37 +01:00
2017-04-26 19:31:39 +02:00
// TODO
2020-01-07 20:14:01 +01:00
* sector_size = file - > mp - > sector_size ;
* block_size = file - > mp - > block_size ;
* arg4 = file - > mp - > sector_size ;
2020-01-07 19:54:21 +01:00
* out_flags = file - > flags ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_get_block_size ( ppu_thread & ppu , vm : : cptr < char > path , vm : : ptr < u64 > sector_size , vm : : ptr < u64 > block_size , vm : : ptr < u64 > arg4 )
2015-03-12 20:02:02 +01:00
{
2021-03-05 20:05:37 +01:00
ppu . state + = cpu_flag : : wait ;
2020-01-09 01:26:03 +01:00
sys_fs . warning ( " sys_fs_get_block_size(path=%s, sector_size=*0x%x, block_size=*0x%x, arg4=*0x%x) " , path , sector_size , block_size , arg4 ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2020-01-07 20:14:01 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2020-01-07 20:14:01 +01:00
const std : : string local_path = vfs : : get ( vpath ) ;
2020-02-19 18:03:59 +01:00
if ( vpath . find_first_not_of ( ' / ' ) = = umax )
2020-01-07 20:14:01 +01:00
{
return { CELL_EISDIR , path } ;
}
if ( local_path . empty ( ) )
{
return { CELL_ENOTMOUNTED , path } ;
}
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-01-07 20:14:01 +01:00
2020-01-10 01:17:35 +01:00
// It appears that /dev_hdd0 mount point is special in this function
if ( mp ! = & g_mp_sys_dev_hdd0 & & ( mp - > flags & lv2_mp_flag : : strict_get_block_size ? ! fs : : is_file ( local_path ) : ! fs : : exists ( local_path ) ) )
2020-01-07 20:14:01 +01:00
{
switch ( auto error = fs : : g_tls_error )
{
2020-01-10 01:17:35 +01:00
case fs : : error : : exist : return { CELL_EISDIR , path } ;
2020-01-07 20:14:01 +01:00
case fs : : error : : noent : return { CELL_ENOENT , path } ;
default : sys_fs . error ( " sys_fs_get_block_size(): unknown error %s " , error ) ;
}
return { CELL_EIO , path } ; // ???
}
2022-07-04 15:02:17 +02:00
static_cast < void > ( ppu . test_stopped ( ) ) ;
2021-03-05 20:05:37 +01:00
2017-04-26 19:31:39 +02:00
// TODO
2020-01-07 20:14:01 +01:00
* sector_size = mp - > sector_size ;
* block_size = mp - > block_size ;
* arg4 = mp - > sector_size ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_truncate ( ppu_thread & ppu , vm : : cptr < char > path , u64 size )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-08-11 01:29:59 +02:00
sys_fs . warning ( " sys_fs_truncate(path=%s, size=0x%llx) " , path , size ) ;
2015-03-12 20:02:02 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2018-03-21 20:02:36 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2018-03-21 20:02:36 +01:00
2018-09-11 18:02:19 +02:00
const std : : string local_path = vfs : : get ( vpath ) ;
2017-09-18 23:03:26 +02:00
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-10-02 13:37:58 +02:00
if ( mp = = & g_mp_sys_dev_root )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_EISDIR , path } ;
2017-09-18 23:03:26 +02:00
}
2018-09-11 18:02:19 +02:00
if ( local_path . empty ( ) )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_ENOTMOUNTED , path } ;
2017-09-18 23:03:26 +02:00
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
return { CELL_EROFS , path } ;
}
std : : lock_guard lock ( mp - > mutex ) ;
2017-09-18 23:03:26 +02:00
if ( ! fs : : truncate_file ( local_path , size ) )
2015-03-12 20:02:02 +01:00
{
2016-05-13 15:55:34 +02:00
switch ( auto error = fs : : g_tls_error )
2016-04-14 00:23:53 +02:00
{
2022-11-26 11:01:55 +01:00
case fs : : error : : noent :
{
return { mp = = & g_mp_sys_dev_hdd1 ? sys_fs . warning : sys_fs . error , CELL_ENOENT , path } ;
}
2016-07-21 00:00:31 +02:00
default : sys_fs . error ( " sys_fs_truncate(): unknown error %s " , error ) ;
2016-04-14 00:23:53 +02:00
}
2015-03-12 20:02:02 +01:00
2017-08-08 21:13:48 +02:00
return { CELL_EIO , path } ; // ???
2015-03-12 20:02:02 +01:00
}
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_ftruncate ( ppu_thread & ppu , u32 fd , u64 size )
2015-03-12 20:02:02 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2016-01-12 22:57:16 +01:00
sys_fs . warning ( " sys_fs_ftruncate(fd=%d, size=0x%llx) " , fd , size ) ;
2015-03-12 20:02:02 +01:00
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2015-03-16 17:20:02 +01:00
2015-04-19 19:14:16 +02:00
if ( ! file | | ! ( file - > flags & CELL_FS_O_ACCMODE ) )
2015-03-16 17:20:02 +01:00
{
2016-06-02 17:16:01 +02:00
return CELL_EBADF ;
2015-03-16 17:20:02 +01:00
}
2015-03-12 20:02:02 +01:00
2023-05-25 08:31:17 +02:00
if ( file - > mp . read_only )
2020-01-04 22:55:15 +01:00
{
return CELL_EROFS ;
}
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( file - > mp - > mutex ) ;
2015-04-12 03:36:25 +02:00
2021-03-12 15:25:03 +01:00
if ( ! file - > file )
{
return CELL_EBADF ;
}
2019-11-09 12:35:41 +01:00
if ( file - > lock = = 2 )
{
return CELL_EIO ;
}
2017-09-04 01:09:22 +02:00
if ( file - > lock )
{
return CELL_EBUSY ;
}
2020-01-05 16:14:07 +01:00
if ( ! file - > file . trunc ( size ) )
2015-04-19 19:14:16 +02:00
{
2016-05-13 15:55:34 +02:00
switch ( auto error = fs : : g_tls_error )
2016-04-14 00:23:53 +02:00
{
2016-05-13 15:55:34 +02:00
case fs : : error : : ok :
2016-07-21 00:00:31 +02:00
default : sys_fs . error ( " sys_fs_ftruncate(): unknown error %s " , error ) ;
2016-04-14 00:23:53 +02:00
}
2015-04-19 19:14:16 +02:00
2016-06-02 17:16:01 +02:00
return CELL_EIO ; // ???
2015-04-19 19:14:16 +02:00
}
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_symbolic_link ( ppu_thread & , vm : : cptr < char > target , vm : : cptr < char > linkpath )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_symbolic_link(target=%s, linkpath=%s) " , target , linkpath ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_chmod ( ppu_thread & , vm : : cptr < char > path , s32 mode )
2015-03-12 20:02:02 +01:00
{
2017-05-10 16:43:04 +02:00
sys_fs . todo ( " sys_fs_chmod(path=%s, mode=%#o) " , path , mode ) ;
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2022-10-21 12:16:30 +02:00
if ( path_error )
{
return { path_error , vpath } ;
}
const std : : string local_path = vfs : : get ( vpath ) ;
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
if ( local_path . empty ( ) )
{
return { CELL_ENOTMOUNTED , path } ;
}
if ( mp - > flags & lv2_mp_flag : : read_only )
{
return { CELL_EROFS , path } ;
}
std : : unique_lock lock ( mp - > mutex ) ;
fs : : stat_t info { } ;
if ( ! fs : : get_stat ( local_path , info ) )
{
switch ( auto error = fs : : g_tls_error )
{
case fs : : error : : noent :
{
// Try to locate split files
for ( u32 i = 66601 ; i < = 66699 ; i + + )
{
if ( ! fs : : get_stat ( fmt : : format ( " %s.%u " , local_path , i ) , info ) & & ! info . is_directory )
{
break ;
}
}
if ( fs : : get_stat ( local_path + " .66600 " , info ) & & ! info . is_directory )
{
break ;
}
return { CELL_ENOENT , path } ;
}
default :
{
sys_fs . error ( " sys_fs_chmod(): unknown error %s " , error ) ;
return { CELL_EIO , path } ;
}
}
}
2017-05-10 16:43:04 +02:00
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_chown ( ppu_thread & , vm : : cptr < char > path , s32 uid , s32 gid )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_chown(path=%s, uid=%d, gid=%d) " , path , uid , gid ) ;
2015-03-12 20:02:02 +01:00
return CELL_OK ;
}
2017-01-26 13:01:21 +01:00
2019-06-08 18:34:55 +02:00
error_code sys_fs_disk_free ( ppu_thread & ppu , vm : : cptr < char > path , vm : : ptr < u64 > total_free , vm : : ptr < u64 > avail_free )
2017-06-25 11:31:50 +02:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2017-06-25 11:31:50 +02:00
sys_fs . warning ( " sys_fs_disk_free(path=%s total_free=*0x%x avail_free=*0x%x) " , path , total_free , avail_free ) ;
2018-03-21 20:02:36 +01:00
if ( ! path )
return CELL_EFAULT ;
if ( ! path [ 0 ] )
return CELL_EINVAL ;
2018-09-11 18:02:19 +02:00
const std : : string_view vpath = path . get_ptr ( ) ;
2017-09-18 23:03:26 +02:00
2020-01-10 23:10:50 +01:00
if ( vpath = = " / " sv )
2017-09-18 23:03:26 +02:00
{
2020-01-10 23:10:50 +01:00
return CELL_ENOTSUP ;
}
// It seems max length is 31, and multiple / at the start aren't supported
2020-01-15 23:10:48 +01:00
if ( vpath . size ( ) > CELL_FS_MAX_MP_LENGTH )
2020-01-10 23:10:50 +01:00
{
return { CELL_ENAMETOOLONG , path } ;
2017-09-18 23:03:26 +02:00
}
2020-01-10 23:10:50 +01:00
if ( vpath . find_first_not_of ( ' / ' ) ! = 1 )
{
return { CELL_EINVAL , path } ;
}
// Get only device path
const std : : string local_path = vfs : : get ( vpath . substr ( 0 , vpath . find_first_of ( ' / ' , 1 ) ) ) ;
2018-09-11 18:02:19 +02:00
if ( local_path . empty ( ) )
2017-09-18 23:03:26 +02:00
{
2020-01-10 23:10:50 +01:00
return { CELL_EINVAL , path } ;
}
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-01-10 23:10:50 +01:00
if ( mp - > flags & lv2_mp_flag : : strict_get_block_size )
{
// TODO:
return { CELL_ENOTSUP , path } ;
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-10 23:10:50 +01:00
{
// TODO: check /dev_bdvd
2022-10-13 16:56:40 +02:00
ppu . check_state ( ) ;
2020-01-10 23:10:50 +01:00
* total_free = 0 ;
* avail_free = 0 ;
return CELL_OK ;
2017-09-18 23:03:26 +02:00
}
2022-10-13 16:56:40 +02:00
u64 available = 0 ;
2020-09-18 21:52:04 +02:00
// avail_free is the only value used by cellFsGetFreeSize
if ( mp = = & g_mp_sys_dev_hdd1 )
2017-06-25 11:31:50 +02:00
{
2022-10-13 16:56:40 +02:00
available = ( 1u < < 31 ) - mp - > sector_size ; // 2GB (TODO: Should be the total size)
2020-09-18 21:52:04 +02:00
}
else //if (mp == &g_mp_sys_dev_hdd0)
{
2022-10-13 16:56:40 +02:00
available = ( 40ull * 1024 * 1024 * 1024 - mp - > sector_size ) ; // Read explanation in cellHddGameCheck
2017-06-25 11:31:50 +02:00
}
2020-09-18 21:52:04 +02:00
// HACK: Hopefully nothing uses this value or once at max because its hacked here:
2020-10-01 21:07:38 +02:00
// The total size can change based on the size of the directory
2022-10-13 16:56:40 +02:00
const u64 total = available + fs : : get_dir_size ( local_path , mp - > sector_size ) ;
ppu . check_state ( ) ;
* total_free = total ;
* avail_free = available ;
2017-06-25 11:31:50 +02:00
return CELL_OK ;
}
2019-06-08 18:34:55 +02:00
error_code sys_fs_utime ( ppu_thread & ppu , vm : : cptr < char > path , vm : : cptr < CellFsUtimbuf > timep )
2017-01-26 13:01:21 +01:00
{
2020-10-30 18:14:32 +01:00
ppu . state + = cpu_flag : : wait ;
2019-06-08 18:34:55 +02:00
lv2_obj : : sleep ( ppu ) ;
2017-01-26 13:01:21 +01:00
sys_fs . warning ( " sys_fs_utime(path=%s, timep=*0x%x) " , path , timep ) ;
2017-11-20 17:06:17 +01:00
sys_fs . warning ( " ** actime=%u, modtime=%u " , timep - > actime , timep - > modtime ) ;
2017-01-26 13:01:21 +01:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2018-03-21 20:02:36 +01:00
2021-11-12 18:32:35 +01:00
if ( path_error )
{
return { path_error , vpath } ;
}
2018-03-21 20:02:36 +01:00
2018-09-11 18:02:19 +02:00
const std : : string local_path = vfs : : get ( vpath ) ;
2017-09-18 23:03:26 +02:00
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
2020-10-02 13:37:58 +02:00
if ( mp = = & g_mp_sys_dev_root )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_EISDIR , path } ;
2017-09-18 23:03:26 +02:00
}
2018-09-11 18:02:19 +02:00
if ( local_path . empty ( ) )
2017-09-18 23:03:26 +02:00
{
2018-09-11 18:02:19 +02:00
return { CELL_ENOTMOUNTED , path } ;
2017-09-18 23:03:26 +02:00
}
2023-05-25 08:31:17 +02:00
if ( mp . read_only )
2020-01-04 22:55:15 +01:00
{
return { CELL_EROFS , path } ;
}
std : : lock_guard lock ( mp - > mutex ) ;
2017-09-18 23:03:26 +02:00
if ( ! fs : : utime ( local_path , timep - > actime , timep - > modtime ) )
2017-01-26 13:01:21 +01:00
{
switch ( auto error = fs : : g_tls_error )
{
2022-11-26 11:01:55 +01:00
case fs : : error : : noent :
{
return { mp = = & g_mp_sys_dev_hdd1 ? sys_fs . warning : sys_fs . error , CELL_ENOENT , path } ;
}
2017-01-26 13:01:21 +01:00
default : sys_fs . error ( " sys_fs_utime(): unknown error %s " , error ) ;
}
2017-08-08 21:13:48 +02:00
return { CELL_EIO , path } ; // ???
2017-01-26 13:01:21 +01:00
}
return CELL_OK ;
}
2017-04-24 22:30:15 +02:00
2021-03-05 20:05:37 +01:00
error_code sys_fs_acl_read ( ppu_thread & , vm : : cptr < char > path , vm : : ptr < void > ptr )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_acl_read(path=%s, ptr=*0x%x) " , path , ptr ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_acl_write ( ppu_thread & , vm : : cptr < char > path , vm : : ptr < void > ptr )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_acl_write(path=%s, ptr=*0x%x) " , path , ptr ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_lsn_get_cda_size ( ppu_thread & , u32 fd , vm : : ptr < u64 > ptr )
2017-04-26 19:26:29 +02:00
{
sys_fs . warning ( " sys_fs_lsn_get_cda_size(fd=%d, ptr=*0x%x) " , fd , ptr ) ;
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2017-04-26 19:26:29 +02:00
if ( ! file )
{
return CELL_EBADF ;
}
// TODO
* ptr = 0 ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_lsn_get_cda ( ppu_thread & , u32 fd , vm : : ptr < void > arg2 , u64 arg3 , vm : : ptr < u64 > arg4 )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_lsn_get_cda(fd=%d, arg2=*0x%x, arg3=0x%x, arg4=*0x%x) " , fd , arg2 , arg3 , arg4 ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_lsn_lock ( ppu_thread & , u32 fd )
2017-04-24 22:30:15 +02:00
{
sys_fs . trace ( " sys_fs_lsn_lock(fd=%d) " , fd ) ;
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2017-04-24 22:30:15 +02:00
if ( ! file )
{
return CELL_EBADF ;
}
2020-01-11 01:44:52 +01:00
// TODO: seems to do nothing on /dev_hdd0 or /host_root
if ( file - > mp = = & g_mp_sys_dev_hdd0 | | file - > mp - > flags & lv2_mp_flag : : strict_get_block_size )
{
return CELL_OK ;
}
2021-09-27 18:25:20 +02:00
std : : lock_guard lock ( file - > mp - > mutex ) ;
2020-01-11 01:44:52 +01:00
file - > lock . compare_and_swap ( 0 , 1 ) ;
2017-04-24 22:30:15 +02:00
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_lsn_unlock ( ppu_thread & , u32 fd )
2017-04-24 22:30:15 +02:00
{
sys_fs . trace ( " sys_fs_lsn_unlock(fd=%d) " , fd ) ;
2024-12-22 19:59:48 +01:00
const auto file = idm : : get_unlocked < lv2_fs_object , lv2_file > ( fd ) ;
2017-04-24 22:30:15 +02:00
if ( ! file )
{
return CELL_EBADF ;
}
2021-09-27 18:25:20 +02:00
// See sys_fs_lsn_lock
if ( file - > mp = = & g_mp_sys_dev_hdd0 | | file - > mp - > flags & lv2_mp_flag : : strict_get_block_size )
{
return CELL_OK ;
}
2020-01-11 01:44:52 +01:00
// Unlock unconditionally
2021-09-27 18:25:20 +02:00
std : : lock_guard lock ( file - > mp - > mutex ) ;
2020-01-11 01:44:52 +01:00
file - > lock . compare_and_swap ( 1 , 0 ) ;
2017-04-24 22:30:15 +02:00
return CELL_OK ;
}
2017-05-10 16:43:04 +02:00
2021-03-05 20:05:37 +01:00
error_code sys_fs_lsn_read ( ppu_thread & , u32 fd , vm : : cptr < void > ptr , u64 size )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_lsn_read(fd=%d, ptr=*0x%x, size=0x%x) " , fd , ptr , size ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_lsn_write ( ppu_thread & , u32 fd , vm : : cptr < void > ptr , u64 size )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_lsn_write(fd=%d, ptr=*0x%x, size=0x%x) " , fd , ptr , size ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_mapped_allocate ( ppu_thread & , u32 fd , u64 size , vm : : pptr < void > out_ptr )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_mapped_allocate(fd=%d, arg2=0x%x, out_ptr=**0x%x) " , fd , size , out_ptr ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_mapped_free ( ppu_thread & , u32 fd , vm : : ptr < void > ptr )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_mapped_free(fd=%d, ptr=0x%#x) " , fd , ptr ) ;
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_truncate2 ( ppu_thread & , u32 fd , u64 size )
2017-05-10 16:43:04 +02:00
{
sys_fs . todo ( " sys_fs_truncate2(fd=%d, size=0x%x) " , fd , size ) ;
return CELL_OK ;
}
2020-02-07 10:31:33 +01:00
2021-03-05 20:05:37 +01:00
error_code sys_fs_get_mount_info_size ( ppu_thread & , vm : : ptr < u64 > len )
2020-02-07 10:31:33 +01:00
{
2023-07-10 10:30:28 +02:00
sys_fs . warning ( " sys_fs_get_mount_info_size(len=*0x%x) " , len ) ;
2020-02-07 10:31:33 +01:00
2021-07-17 19:19:13 +02:00
if ( ! len )
{
return CELL_EFAULT ;
}
2023-05-01 15:08:09 +02:00
* len = g_fxo - > get < lv2_fs_mount_info_map > ( ) . get_all ( ) ;
2021-07-17 19:19:13 +02:00
2020-02-07 10:31:33 +01:00
return CELL_OK ;
}
2022-11-18 16:18:16 +01:00
error_code sys_fs_get_mount_info ( ppu_thread & , vm : : ptr < CellFsMountInfo > info , u64 len , vm : : ptr < u64 > out_len )
2020-02-07 10:31:33 +01:00
{
2023-07-10 10:30:28 +02:00
sys_fs . warning ( " sys_fs_get_mount_info(info=*0x%x, len=0x%x, out_len=*0x%x) " , info , len , out_len ) ;
2020-02-07 10:31:33 +01:00
2022-11-13 15:15:46 +01:00
if ( ! info | | ! out_len )
2021-07-17 19:19:13 +02:00
{
return CELL_EFAULT ;
}
2023-05-01 15:08:09 +02:00
* out_len = g_fxo - > get < lv2_fs_mount_info_map > ( ) . get_all ( info . get_ptr ( ) , len ) ;
2021-07-17 19:19:13 +02:00
2020-02-07 10:31:33 +01:00
return CELL_OK ;
}
2023-03-06 16:55:10 +01:00
error_code sys_fs_newfs ( ppu_thread & ppu , vm : : cptr < char > dev_name , vm : : cptr < char > file_system , s32 unk1 , vm : : cptr < char > str1 )
2022-11-01 17:49:50 +01:00
{
2023-03-06 16:55:10 +01:00
ppu . state + = cpu_flag : : wait ;
2022-11-01 17:49:50 +01:00
2023-07-10 10:30:28 +02:00
sys_fs . warning ( " sys_fs_newfs(dev_name=%s, file_system=%s, unk1=0x%x, str1=%s) " , dev_name , file_system , unk1 , str1 ) ;
2022-11-01 17:49:50 +01:00
2023-12-11 07:45:38 +01:00
const auto [ dev_error , device_name ] = translate_to_str ( dev_name , false ) ;
2022-12-02 05:40:39 +01:00
if ( dev_error )
{
2023-03-06 16:55:10 +01:00
return { dev_error , device_name } ;
2022-12-02 05:40:39 +01:00
}
2022-12-08 05:46:09 +01:00
std : : string vfs_path ;
2023-04-30 18:07:27 +02:00
const auto mp = lv2_fs_object : : get_mp ( device_name , & vfs_path ) ;
2023-05-25 08:31:17 +02:00
std : : unique_lock lock ( mp - > mutex , std : : defer_lock ) ;
2022-12-02 05:40:39 +01:00
2023-04-30 18:07:27 +02:00
if ( ! g_ps3_process_info . has_root_perm ( ) & & mp ! = & g_mp_sys_dev_usb )
2023-05-25 08:31:17 +02:00
return { CELL_EPERM , device_name } ;
2023-03-06 16:55:10 +01:00
2023-04-30 18:07:27 +02:00
if ( mp = = & g_mp_sys_no_device )
return { CELL_ENXIO , device_name } ;
2024-01-08 00:52:28 +01:00
if ( g_fxo - > get < lv2_fs_mount_info_map > ( ) . is_device_mounted ( device_name ) | | ! lock . try_lock ( ) )
2023-05-25 08:31:17 +02:00
return { CELL_EBUSY , device_name } ;
if ( vfs_path . empty ( ) )
2023-04-30 18:07:27 +02:00
return { CELL_ENOTSUP , device_name } ;
2023-03-06 16:55:10 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
2023-04-30 18:07:27 +02:00
return { CELL_EROFS , device_name } ;
2022-12-02 05:40:39 +01:00
if ( mp = = & g_mp_sys_dev_hdd1 )
{
const std : : string_view appname = g_ps3_process_info . get_cellos_appname ( ) ;
2023-03-06 16:55:10 +01:00
vfs_path = fmt : : format ( " %s/caches/%s " , vfs_path , appname . substr ( 0 , appname . find_last_of ( ' . ' ) ) ) ;
2022-12-02 05:40:39 +01:00
}
2023-03-06 16:55:10 +01:00
if ( ! fs : : remove_all ( vfs_path , false ) )
{
2023-04-30 18:07:27 +02:00
sys_fs . error ( " sys_fs_newfs(): Failed to clear \" %s \" at \" %s \" " , device_name , vfs_path ) ;
return { CELL_EIO , vfs_path } ;
2023-03-06 16:55:10 +01:00
}
2022-12-02 05:40:39 +01:00
2023-04-30 18:07:27 +02:00
sys_fs . success ( " sys_fs_newfs(): Successfully cleared \" %s \" at \" %s \" " , device_name , vfs_path ) ;
2022-11-01 17:49:50 +01:00
return CELL_OK ;
}
2024-06-10 05:13:45 +02:00
error_code sys_fs_mount ( ppu_thread & ppu , vm : : cptr < char > dev_name , vm : : cptr < char > file_system , vm : : cptr < char > path , s32 unk1 , s32 prot , s32 unk2 , vm : : cptr < char > str1 , u32 str_len )
2020-02-07 10:31:33 +01:00
{
2023-03-06 16:55:10 +01:00
ppu . state + = cpu_flag : : wait ;
2024-06-10 05:13:45 +02:00
sys_fs . warning ( " sys_fs_mount(dev_name=%s, file_system=%s, path=%s, unk1=0x%x, prot=%d, unk3=0x%x, str1=%s, str_len=%d) " , dev_name , file_system , path , unk1 , prot , unk2 , str1 , str_len ) ;
2022-10-27 22:53:12 +02:00
2023-12-11 07:45:38 +01:00
const auto [ dev_error , device_name ] = translate_to_str ( dev_name , false ) ;
2023-03-06 16:55:10 +01:00
if ( dev_error )
{
return { dev_error , device_name } ;
}
2023-12-11 07:45:38 +01:00
const auto [ fs_error , filesystem ] = translate_to_str ( file_system , false ) ;
2023-03-06 16:55:10 +01:00
if ( fs_error )
2022-10-27 22:53:12 +02:00
{
2023-03-06 16:55:10 +01:00
return { fs_error , filesystem } ;
2022-10-27 22:53:12 +02:00
}
2023-12-11 07:45:38 +01:00
const auto [ path_error , path_sv ] = translate_to_str ( path ) ;
2022-10-27 22:53:12 +02:00
if ( path_error )
{
2023-05-25 08:31:17 +02:00
return { path_error , path_sv } ;
2022-10-27 22:53:12 +02:00
}
2023-06-25 04:09:08 +02:00
const std : : string vpath = lv2_fs_object : : get_normalized_path ( path_sv ) ;
2023-03-06 16:55:10 +01:00
std : : string vfs_path ;
2023-05-25 08:31:17 +02:00
const auto mp = lv2_fs_object : : get_mp ( device_name , & vfs_path ) ;
std : : unique_lock lock ( mp - > mutex , std : : defer_lock ) ;
2023-03-06 16:55:10 +01:00
2023-05-25 08:31:17 +02:00
if ( ! g_ps3_process_info . has_root_perm ( ) & & mp ! = & g_mp_sys_dev_usb )
return { CELL_EPERM , device_name } ;
2023-03-06 16:55:10 +01:00
2023-05-25 08:31:17 +02:00
if ( mp = = & g_mp_sys_no_device )
2023-04-30 18:07:27 +02:00
return { CELL_ENXIO , device_name } ;
2024-01-08 00:52:28 +01:00
if ( g_fxo - > get < lv2_fs_mount_info_map > ( ) . is_device_mounted ( device_name ) | | ! lock . try_lock ( ) )
2023-05-25 08:31:17 +02:00
return { CELL_EBUSY , device_name } ;
if ( vfs_path . empty ( ) )
2023-04-30 18:07:27 +02:00
return { CELL_ENOTSUP , device_name } ;
2023-05-25 08:31:17 +02:00
if ( vpath . find_first_not_of ( ' / ' ) = = umax | | ! vfs : : get ( vpath ) . empty ( ) )
2023-03-06 16:55:10 +01:00
return { CELL_EEXIST , vpath } ;
2023-05-25 08:31:17 +02:00
if ( mp = = & g_mp_sys_dev_hdd1 )
2022-10-31 14:59:02 +01:00
{
2023-03-06 16:55:10 +01:00
const std : : string_view appname = g_ps3_process_info . get_cellos_appname ( ) ;
vfs_path = fmt : : format ( " %s/caches/%s " , vfs_path , appname . substr ( 0 , appname . find_last_of ( ' . ' ) ) ) ;
2022-10-31 14:59:02 +01:00
}
2023-03-06 16:55:10 +01:00
if ( ! vfs_path . ends_with ( ' / ' ) )
vfs_path + = ' / ' ;
2022-10-27 22:53:12 +02:00
2023-03-06 16:55:10 +01:00
if ( ! fs : : is_dir ( vfs_path ) & & ! fs : : create_dir ( vfs_path ) )
2022-10-31 14:59:02 +01:00
{
2023-03-06 16:55:10 +01:00
sys_fs . error ( " Failed to create directory \" %s \" " , vfs_path ) ;
2023-04-30 18:07:27 +02:00
return { CELL_EIO , vfs_path } ;
2023-03-06 16:55:10 +01:00
}
2023-05-25 08:31:17 +02:00
const bool is_simplefs = filesystem = = " CELL_FS_SIMPLEFS " sv ;
2023-03-06 16:55:10 +01:00
if ( is_simplefs )
{
2023-04-30 18:07:27 +02:00
vfs_path + = " simplefs.tmp " ;
if ( fs : : file simplefs_file ; simplefs_file . open ( vfs_path , fs : : create + fs : : read + fs : : write + fs : : trunc + fs : : lock ) )
2022-10-31 14:59:02 +01:00
{
2023-05-25 08:31:17 +02:00
const u64 file_size = mp - > sector_size ; // One sector's size is enough for VSH's simplefs check
2023-04-30 18:07:27 +02:00
simplefs_file . trunc ( file_size ) ;
2023-05-01 15:08:09 +02:00
sys_fs . notice ( " Created a simplefs file at \" %s \" " , vfs_path ) ;
2022-10-31 14:59:02 +01:00
}
2023-03-06 16:55:10 +01:00
else
2022-10-31 14:59:02 +01:00
{
2023-04-30 18:07:27 +02:00
sys_fs . error ( " Failed to create simplefs file \" %s \" " , vfs_path ) ;
return { CELL_EIO , vfs_path } ;
2022-10-31 14:59:02 +01:00
}
2022-10-27 22:53:12 +02:00
}
2023-03-06 16:55:10 +01:00
if ( ! vfs : : mount ( vpath , vfs_path , ! is_simplefs ) )
2023-04-30 18:07:27 +02:00
{
if ( is_simplefs )
{
if ( fs : : remove_file ( vfs_path ) )
{
sys_fs . notice ( " Removed simplefs file \" %s \" " , vfs_path ) ;
}
else
{
sys_fs . error ( " Failed to remove simplefs file \" %s \" " , vfs_path ) ;
}
}
2022-10-27 22:53:12 +02:00
return CELL_EIO ;
2023-04-30 18:07:27 +02:00
}
2022-10-27 22:53:12 +02:00
2023-05-25 08:31:17 +02:00
g_fxo - > get < lv2_fs_mount_info_map > ( ) . add ( vpath , mp , device_name , filesystem , prot ) ;
2023-05-01 15:08:09 +02:00
2022-10-27 22:53:12 +02:00
return CELL_OK ;
}
2024-01-08 00:52:28 +01:00
error_code sys_fs_unmount ( ppu_thread & ppu , vm : : cptr < char > path , s32 unk1 , s32 force )
2022-10-27 22:53:12 +02:00
{
2023-03-06 16:55:10 +01:00
ppu . state + = cpu_flag : : wait ;
2022-10-27 22:53:12 +02:00
2024-01-08 00:52:28 +01:00
sys_fs . warning ( " sys_fs_unmount(path=%s, unk1=0x%x, force=%d) " , path , unk1 , force ) ;
2022-10-27 22:53:12 +02:00
2023-12-11 07:45:38 +01:00
const auto [ path_error , vpath ] = translate_to_str ( path ) ;
2022-10-27 22:53:12 +02:00
if ( path_error )
{
2023-03-06 16:55:10 +01:00
return { path_error , vpath } ;
2022-10-27 22:53:12 +02:00
}
2023-05-25 08:31:17 +02:00
const auto & mp = g_fxo - > get < lv2_fs_mount_info_map > ( ) . lookup ( vpath ) ;
std : : unique_lock lock ( mp - > mutex , std : : defer_lock ) ;
2022-10-27 22:53:12 +02:00
2023-04-30 18:07:27 +02:00
if ( ! g_ps3_process_info . has_root_perm ( ) & & mp ! = & g_mp_sys_dev_usb )
2023-05-25 08:31:17 +02:00
return { CELL_EPERM , vpath } ;
2023-04-30 18:07:27 +02:00
2023-05-25 08:31:17 +02:00
if ( mp = = & g_mp_sys_no_device )
2023-06-25 04:09:29 +02:00
return { CELL_EINVAL , vpath } ;
2020-02-07 10:31:33 +01:00
2024-01-08 00:52:28 +01:00
if ( mp = = & g_mp_sys_dev_root | | ( ! lock . try_lock ( ) & & ! force ) )
2023-05-25 08:31:17 +02:00
return { CELL_EBUSY , vpath } ;
if ( ! lv2_fs_mount_info_map : : vfs_unmount ( vpath ) )
return { CELL_EIO , vpath } ;
2023-05-01 15:08:09 +02:00
2020-02-07 10:31:33 +01:00
return CELL_OK ;
}