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"
2015-03-12 20:02:02 +01:00
2021-04-24 20:43:09 +02:00
# include "Emu/Cell/PPUModule.h"
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"
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-05-08 00:53:04 +02:00
# include "Emu/RSX/Overlays/overlay_utils.h" // for ascii8_to_utf16
2016-04-27 00:27:24 +02:00
# include "Utilities/StrUtil.h"
2016-06-02 17:16:01 +02:00
2022-05-08 00:53:04 +02:00
# include <charconv>
2022-07-04 15:02:17 +02:00
# include <span>
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
2020-10-01 21:07:38 +02:00
lv2_fs_mount_point g_mp_sys_dev_root ;
2020-10-03 18:05:09 +02:00
lv2_fs_mount_point g_mp_sys_no_device ;
2020-10-02 13:37:58 +02:00
lv2_fs_mount_point g_mp_sys_dev_hdd0 { " /dev_hdd0 " } ;
2021-03-16 17:37:00 +01:00
lv2_fs_mount_point g_mp_sys_dev_hdd1 { " /dev_hdd1 " , 512 , 32768 , lv2_mp_flag : : no_uid_gid + lv2_mp_flag : : cache } ;
2020-10-02 13:37:58 +02:00
lv2_fs_mount_point g_mp_sys_dev_usb { " " , 512 , 4096 , lv2_mp_flag : : no_uid_gid } ;
lv2_fs_mount_point g_mp_sys_dev_bdvd { " " , 2048 , 65536 , lv2_mp_flag : : read_only + lv2_mp_flag : : no_uid_gid } ;
2022-06-24 17:58:52 +02:00
lv2_fs_mount_point g_mp_sys_dev_dvd { " " , 2048 , 32768 , lv2_mp_flag : : read_only + lv2_mp_flag : : no_uid_gid } ;
2020-10-02 13:37:58 +02:00
lv2_fs_mount_point g_mp_sys_host_root { " " , 512 , 512 , lv2_mp_flag : : strict_get_block_size + lv2_mp_flag : : no_uid_gid } ;
lv2_fs_mount_point g_mp_sys_dev_flash { " " , 512 , 8192 , lv2_mp_flag : : read_only + lv2_mp_flag : : no_uid_gid } ;
2021-04-17 16:22:17 +02:00
lv2_fs_mount_point g_mp_sys_dev_flash2 { " " , 512 , 8192 , lv2_mp_flag : : no_uid_gid } ; // TODO confirm
lv2_fs_mount_point g_mp_sys_dev_flash3 { " " , 512 , 8192 , lv2_mp_flag : : read_only + lv2_mp_flag : : no_uid_gid } ; // TODO confirm
2016-06-02 17:16:01 +02: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 ;
2021-11-14 00:15:27 +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 ) ;
fmt : : append ( out , u8 " Directory, “%s”, Entries: %u/%u " , dir . name . data ( ) , std : : min < u64 > ( dir . pos , dir . entries . size ( ) ) , dir . entries . size ( ) ) ;
}
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 ;
}
2020-01-04 21:12:46 +01:00
lv2_fs_mount_point * lv2_fs_object : : get_mp ( std : : string_view filename )
2016-06-02 17:16:01 +02:00
{
2020-10-01 21:07:38 +02:00
std : : string_view mp_name , vpath = filename ;
2020-01-04 21:12:46 +01:00
2020-12-18 08:39:54 +01:00
for ( usz depth = 0 ; ; )
2020-01-04 21:12:46 +01:00
{
2020-10-01 21:07:38 +02:00
// Skip one or more '/'
const auto pos = vpath . find_first_not_of ( ' / ' ) ;
2020-01-04 21:12:46 +01:00
2020-10-01 21:07:38 +02:00
if ( pos = = 0 )
{
// Relative path (TODO)
2020-10-03 18:05:09 +02:00
return & g_mp_sys_no_device ;
2020-10-01 21:07:38 +02:00
}
if ( pos = = umax )
{
break ;
}
// Get fragment name
const auto name = vpath . substr ( pos , vpath . find_first_of ( ' / ' , pos ) - pos ) ;
vpath . remove_prefix ( name . size ( ) + pos ) ;
// Process special directories
if ( name = = " . " sv )
{
// Keep current
continue ;
}
if ( name = = " .. " sv )
{
// Root parent is root
if ( depth = = 0 )
{
continue ;
}
depth - - ;
continue ;
}
if ( depth + + = = 0 )
{
// Save mountpoint name
mp_name = name ;
}
}
if ( ! mp_name . empty ( ) )
{
if ( mp_name = = " dev_hdd0 " sv )
return & g_mp_sys_dev_hdd0 ;
2020-01-04 21:12:46 +01:00
if ( mp_name = = " dev_hdd1 " sv )
return & g_mp_sys_dev_hdd1 ;
2020-02-17 22:43:23 +01:00
if ( mp_name . starts_with ( " dev_usb " sv ) )
2020-01-04 21:12:46 +01:00
return & g_mp_sys_dev_usb ;
if ( mp_name = = " dev_bdvd " sv )
return & g_mp_sys_dev_bdvd ;
2022-06-24 17:58:52 +02:00
if ( mp_name = = " dev_ps2disc " sv )
return & g_mp_sys_dev_dvd ;
2020-10-01 21:07:38 +02:00
if ( mp_name = = " app_home " sv & & filename . data ( ) ! = Emu . argv [ 0 ] . data ( ) )
return lv2_fs_object : : get_mp ( Emu . argv [ 0 ] ) ;
2020-01-04 21:12:46 +01:00
if ( mp_name = = " host_root " sv )
return & g_mp_sys_host_root ;
2020-01-18 21:41:58 +01:00
if ( mp_name = = " dev_flash " sv )
return & g_mp_sys_dev_flash ;
2021-04-17 16:22:17 +02:00
if ( mp_name = = " dev_flash2 " sv )
return & g_mp_sys_dev_flash2 ;
if ( mp_name = = " dev_flash3 " sv )
return & g_mp_sys_dev_flash3 ;
2020-10-01 21:07:38 +02:00
// Default
return & g_mp_sys_dev_hdd0 ;
2020-01-04 21:12:46 +01:00
}
2020-10-01 21:07:38 +02:00
// Default fallback
return & g_mp_sys_dev_root ;
2016-06-02 17:16:01 +02:00
}
2022-07-04 15:02:17 +02:00
lv2_fs_object : : lv2_fs_object ( utils : : serial & ar , bool )
: name ( ar )
, mp ( get_mp ( name . data ( ) ) )
{
}
2020-02-27 16:24:47 +01:00
u64 lv2_file : : op_read ( const fs : : file & file , vm : : ptr < void > buf , u64 size )
2016-06-02 17:16:01 +02:00
{
// Copy data from intermediate buffer (avoid passing vm pointer to a native API)
2020-01-19 02:25:50 +01:00
uchar local_buf [ 65536 ] ;
u64 result = 0 ;
while ( result < size )
{
const u64 block = std : : min < u64 > ( size - result , sizeof ( local_buf ) ) ;
const u64 nread = file . read ( + local_buf , block ) ;
std : : memcpy ( static_cast < uchar * > ( buf . get_ptr ( ) ) + result , local_buf , nread ) ;
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)
2020-01-19 02:25:50 +01:00
uchar local_buf [ 65536 ] ;
u64 result = 0 ;
while ( result < size )
{
const u64 block = std : : min < u64 > ( size - result , sizeof ( local_buf ) ) ;
2020-01-21 13:52:13 +01:00
std : : memcpy ( local_buf , static_cast < const uchar * > ( buf . get_ptr ( ) ) + result , block ) ;
2020-01-19 02:25:50 +01:00
const u64 nwrite = file . write ( + local_buf , block ) ;
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 )
{
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 ;
}
const std : : string retrieve_real = ar ;
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 ;
if ( ar . operator bool ( ) ) // see lv2_file::save in_mem
{
std : : vector < u8 > buf = ar ;
const fs : : stat_t stat = ar ;
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
if ( ! ( mp - > flags & lv2_mp_flag : : read_only ) & & flags & CELL_FS_O_ACCMODE )
{
// 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 = [ & ] ( )
{
if ( mp - > flags & lv2_mp_flag : : read_only )
{
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
2022-08-25 09:53:35 +02:00
fs : : stat_t test_s = test . stat ( ) ;
fs : : stat_t file_s = file . stat ( ) ;
// They don't matter for comparison and only create problems with encrypted files
test_s . is_writable = file_s . is_writable ;
test_s . size = file_s . size ;
return test_s ! = file_s ;
2022-07-04 15:02:17 +02:00
} ( ) ;
2022-08-25 09:53:35 +02:00
if ( in_mem )
{
sys_fs . error ( " Saving \' %s \' LV2 file descriptor in memory! (exists=%s, type=%s, flags=0x%x) " , name . data ( ) , fs : : is_file ( real_path ) , type , flags ) ;
}
2022-07-04 15:02:17 +02:00
ar ( in_mem ) ;
if ( in_mem )
{
ar ( file . to_vector < u8 > ( ) ) ;
ar ( file . stat ( ) ) ;
}
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
{
const std : : shared_ptr < lv2_file > m_file ;
const u64 m_off ;
u64 m_pos ;
explicit file_view ( const std : : shared_ptr < lv2_file > & _file , u64 offset )
: m_file ( _file )
, m_off ( offset )
, m_pos ( 0 )
{
}
~ file_view ( ) override
{
}
fs : : stat_t stat ( ) override
{
return m_file - > file . stat ( ) ;
}
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
{
const u64 old_pos = m_file - > file . pos ( ) ;
2021-01-12 11:01:06 +01:00
m_file - > file . seek ( m_off + m_pos ) ;
2017-03-04 12:54:53 +01:00
const u64 result = m_file - > file . read ( buffer , size ) ;
2020-12-09 08:47:45 +01:00
ensure ( old_pos = = m_file - > file . seek ( old_pos ) ) ;
2017-03-04 12:54:53 +01:00
m_pos + = result ;
return result ;
}
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
{
2017-03-10 10:06:36 +01:00
return m_file - > file . size ( ) ;
2017-03-04 12:54:53 +01:00
}
} ;
fs : : file lv2_file : : make_view ( const std : : shared_ptr < lv2_file > & _file , u64 offset )
{
fs : : file result ;
result . reset ( std : : make_unique < lv2_file : : file_view > ( _file , offset ) ) ;
return result ;
}
2021-11-12 18:32:35 +01:00
std : : pair < CellError , std : : string_view > translate_to_sv ( vm : : cptr < char > ptr )
{
const u32 addr = ptr . addr ( ) ;
if ( ! vm : : check_addr ( addr , vm : : page_readable ) )
{
return { CELL_EFAULT , { } } ;
}
const usz remained_page_memory = ( ~ addr % 4096 ) + 1 ;
constexpr usz max_length = CELL_FS_MAX_FS_PATH_LENGTH + 1 ;
const usz target_memory_span_size = std : : min < usz > ( max_length , vm : : check_addr ( addr + 4096 , vm : : page_readable ) ? max_length : remained_page_memory ) ;
std : : string_view path { ptr . get_ptr ( ) , target_memory_span_size } ;
path = path . substr ( 0 , path . find_first_of ( ' \0 ' ) ) ;
if ( path . size ( ) = = max_length )
{
return { CELL_ENAMETOOLONG , { } } ;
}
if ( path . size ( ) = = target_memory_span_size )
{
// Null character lookup has ended whilst pointing at invalid memory
return { CELL_EFAULT , path } ;
}
if ( ! path . starts_with ( " / " sv ) )
{
return { CELL_ENOENT , path } ;
}
return { { } , path } ;
}
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 ;
}
const auto file = idm : : get < lv2_fs_object > ( * arg3 ) ;
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 ;
}
2021-03-05 20:05:37 +01: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_point * 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
2020-01-11 02:48:42 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
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
}
2020-01-04 22:55:15 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
// 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
{
2020-01-04 22:55:15 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
// 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 ) ;
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
file = std : : move ( edata_file - > edata_file ) ;
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
2021-06-25 21:54:13 +02:00
const auto mp = lv2_fs_object : : get_mp ( 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 } ;
}
lv2_file_type type = lv2_file_type : : regular ;
if ( size = = 8 )
{
// see lv2_file::open_raw
switch ( * static_cast < const be_t < u64 > * > ( arg ) )
{
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 ) ;
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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 ) ;
}
return { error , path } ;
2017-03-07 01:59:05 +01:00
}
2017-08-08 21:13:48 +02:00
2021-11-14 00:15:27 +01:00
if ( const u32 id = idm : : import < lv2_fs_object , lv2_file > ( [ & ppath = ppath , & file = file , mode , flags , & real = real , & type = type ] ( ) - > std : : shared_ptr < lv2_file >
2020-04-04 16:11:21 +02:00
{
2021-11-14 00:15:27 +01:00
std : : 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
result = std : : make_shared < lv2_file > ( ppath , std : : move ( file ) , mode , flags , real , type ) ;
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
2017-01-26 02:12:50 +01:00
const auto file = idm : : get < 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 ;
}
* nread = 0 ;
return CELL_OK ;
}
2018-09-03 21:28:33 +02:00
std : : lock_guard 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 ) ;
* nread = read_bytes ;
if ( ! read_bytes & & file - > file . pos ( ) < file - > file . size ( ) )
{
// 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 ;
}
2017-01-26 02:12:50 +01:00
const auto file = idm : : get < 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 ;
}
* 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 ( ) ) ;
}
2020-01-04 22:55:15 +01:00
if ( file - > mp - > flags & lv2_mp_flag : : read_only )
{
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 ) ;
2021-03-12 15:25:03 +01:00
const auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
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
}
2021-03-16 17:37:00 +01:00
if ( ! ( file - > mp - > flags & ( lv2_mp_flag : : read_only + 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 ) ;
if ( auto ct = idm : : get < lv2_memory_container > ( file - > ct_id ) )
{
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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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
2020-01-04 22:55:15 +01:00
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
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 ( ) )
{
return { CELL_ENOENT , path } ;
}
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
lv2_obj : : sleep ( ppu ) ;
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
2017-01-26 02:12:50 +01:00
const auto directory = idm : : get < 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 ( ) ;
2018-09-15 16:04:35 +02:00
if ( auto * info = directory - > dir_read ( ) )
2015-03-12 20:02:02 +01:00
{
2018-09-15 16:04:35 +02:00
dir - > d_type = info - > is_directory ? CELL_FS_TYPE_DIRECTORY : CELL_FS_TYPE_REGULAR ;
2020-12-18 08:39:54 +01:00
dir - > 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 ( dir - > d_name , info - > name ) ;
2015-03-12 20:02:02 +01:00
* nread = sizeof ( CellFsDirent ) ;
}
else
{
* nread = 0 ;
}
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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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 ) ;
2020-01-04 22:55:15 +01:00
const auto mp = lv2_fs_object : : get_mp ( 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
{
2017-08-08 21:13:48 +02:00
return { 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 { } ;
2015-04-25 21:15:53 +02:00
if ( ! fs : : 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
{
if ( fs : : stat ( fmt : : format ( " %s.%u " , local_path , i ) , info ) & & ! info . is_directory )
{
total_size + = info . size ;
}
else
{
break ;
}
}
// Use attributes from the first fragment (consistently with sys_fs_open+fstat)
2020-01-05 15:31:34 +01:00
if ( fs : : 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 ;
}
return { CELL_ENOENT , path } ;
}
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 ( ) ;
2015-04-24 23:38:11 +02:00
sb - > mode = info . is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666 ;
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
2020-01-11 02:48:42 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
// Remove write permissions
sb - > mode & = ~ 0222 ;
}
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
2017-01-26 02:12:50 +01:00
const auto file = idm : : get < 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 ;
}
2022-09-18 20:19:34 +02:00
const fs : : stat_t info = file - > file . stat ( ) ;
lock . unlock ( ) ;
ppu . check_state ( ) ;
2015-04-19 19:57:04 +02:00
2015-04-24 23:38:11 +02:00
sb - > mode = info . is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666 ;
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
2020-01-11 02:48:42 +01:00
if ( file - > mp - > flags & lv2_mp_flag : : read_only )
{
// Remove write permissions
sb - > mode & = ~ 0222 ;
}
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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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
2020-10-02 13:37:58 +02:00
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
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
}
2020-01-04 22:55:15 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
return { CELL_EROFS , path } ;
}
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 )
{
2017-09-18 22:04:11 +02:00
case fs : : error : : noent : return { CELL_ENOENT , path } ;
2017-09-08 16:17:03 +02:00
case fs : : error : : exist : return { CELL_EEXIST , path } ;
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
2021-11-12 18:32:35 +01:00
const auto [ from_error , vfrom ] = translate_to_sv ( from ) ;
if ( from_error )
{
return { from_error , vfrom } ;
}
2017-09-18 23:03:26 +02:00
2021-11-12 18:32:35 +01:00
const auto [ to_error , vto ] = translate_to_sv ( to ) ;
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 ) ;
2020-10-02 13:37:58 +02:00
const auto mp = lv2_fs_object : : get_mp ( vfrom ) ;
const auto mp_to = lv2_fs_object : : get_mp ( vto ) ;
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 ;
}
if ( mp - > flags & lv2_mp_flag : : read_only )
{
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
2020-09-11 13:06:46 +02:00
if ( ! vfs : : host : : rename ( local_from , local_to , 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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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
2020-10-02 13:37:58 +02:00
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
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
}
2020-01-04 22:55:15 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
return { CELL_EROFS , path } ;
}
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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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
2020-10-02 13:37:58 +02:00
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
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 } ;
}
2020-01-04 22:55:15 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
return { CELL_EROFS , path } ;
}
std : : lock_guard lock ( mp - > mutex ) ;
2020-10-02 13:37:58 +02:00
// Provide default mp root or use parent directory if not available (such as host_root)
if ( ! vfs : : host : : unlink ( local_path , vfs : : get ( mp - > root . empty ( ) ? vpath . substr ( 0 , vpath . find_last_of ( ' / ' ) ) : mp - > root ) ) )
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 } ;
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 ;
}
2017-01-26 02:12:50 +01:00
const auto file = idm : : get < 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 ;
}
2020-01-04 22:55:15 +01:00
if ( op = = 0x8000000b & & file - > mp - > flags & lv2_mp_flag : : read_only )
{
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 ( ) ) ;
}
2018-09-03 21:28:33 +02:00
std : : lock_guard lock ( file - > mp - > mutex ) ;
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 ;
}
2016-06-02 17:16:01 +02:00
const u64 old_pos = file - > file . pos ( ) ;
2021-01-12 11:01:06 +01:00
file - > file . seek ( arg - > offset ) ;
2016-06-02 17:16:01 +02:00
2017-04-26 14:48:50 +02:00
arg - > out_size = op = = 0x8000000a
2016-06-02 17:16:01 +02:00
? file - > op_read ( arg - > buf , arg - > size )
: file - > op_write ( arg - > buf , arg - > size ) ;
2020-12-09 08:47:45 +01:00
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 ;
}
const auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
if ( ! file )
{
return CELL_EBADF ;
}
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 )
{
return CELL_EBADF ;
}
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 ( ) )
{
return CELL_EFSSPECIFIC ;
}
fs : : file stream ;
stream . reset ( std : : move ( sdata_file ) ) ;
2020-04-04 19:06:36 +02:00
if ( const u32 id = idm : : import < lv2_fs_object , lv2_file > ( [ & file = * file , & stream = stream ] ( ) - > std : : 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
{
return nullptr ;
}
2021-09-21 10:45:32 +02:00
return std : : 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 ) ;
2020-01-10 23:09:30 +01:00
const auto mp = lv2_fs_object : : get_mp ( " /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 ;
}
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 ) ;
2021-12-02 16:41:53 +01:00
// TODO populate arg-> unk1+2
arg - > out_code = CELL_OK ;
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 ;
}
auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
if ( ! file )
{
return CELL_EBADF ;
}
if ( auto ct = idm : : get < lv2_memory_container > ( file - > ct_id ) )
{
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
}
2021-12-02 16:41:53 +01:00
case 0xc0000015 : // USB Vid/Pid lookup - Used by arcade games on dev_usbXXX
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 ) ;
if ( arg - > size ! = 0x20u )
{
sys_fs . error ( " sys_fs_fcntl(0xc0000015): invalid size (0x%x) " , arg - > size ) ;
break ;
}
if ( arg - > _x4 ! = 0x10u | | arg - > _x8 ! = 0x18u )
{
sys_fs . error ( " sys_fs_fcntl(0xc0000015): invalid args (0x%x, 0x%x) " , arg - > _x4 , arg - > _x8 ) ;
break ;
}
std : : string_view vpath { arg - > name . get_ptr ( ) , arg - > name_size } ;
2022-05-08 00:53:04 +02:00
// Trim trailing '\0'
if ( auto trim_pos = vpath . find ( ' \0 ' ) ; trim_pos ! = vpath . npos )
{
vpath . remove_suffix ( vpath . size ( ) - trim_pos ) ;
}
2021-12-02 16:41:53 +01:00
if ( ! vpath . starts_with ( " /dev_usb " sv ) )
{
arg - > out_code = CELL_ENOTSUP ;
break ;
}
2022-05-08 00:53:04 +02:00
const cfg : : device_info device = g_cfg_vfs . get_device ( g_cfg_vfs . dev_usb , vpath ) ;
2021-12-02 16:41:53 +01:00
2022-08-23 16:53:50 +02:00
if ( device . path . empty ( ) | | device . vid . empty ( ) | | device . pid . empty ( ) )
2022-05-08 00:53:04 +02:00
{
arg - > out_code = CELL_ENOTSUP ;
break ;
}
u16 vid { } ;
{
auto [ ptr , err ] = std : : from_chars ( device . vid . data ( ) , device . vid . data ( ) + device . vid . size ( ) , vid , 16 ) ;
if ( err ! = std : : errc ( ) )
{
fmt : : throw_exception ( " Failed to read hex string: %s " , std : : make_error_code ( err ) . message ( ) ) ;
}
}
u16 pid { } ;
{
auto [ ptr , err ] = std : : from_chars ( device . pid . data ( ) , device . pid . data ( ) + device . pid . size ( ) , pid , 16 ) ;
if ( err ! = std : : errc ( ) )
{
fmt : : throw_exception ( " Failed to read hex string: %s " , std : : make_error_code ( err ) . message ( ) ) ;
}
}
arg - > vendorID = vid ;
arg - > productID = pid ;
2021-12-02 16:41:53 +01:00
arg - > out_code = CELL_OK ;
2022-05-08 00:53:04 +02:00
sys_fs . trace ( " sys_fs_fcntl(0xc0000015): found device '%s' (vid=0x%x, pid=0x%x) " , vpath , arg - > vendorID , arg - > productID ) ;
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
}
2021-12-02 16:41:53 +01:00
case 0xc000001c : // USB Vid/Pid/Serial lookup
{
const auto arg = vm : : static_ptr_cast < lv2_file_c000001c > ( _arg ) ;
if ( arg - > size ! = 0x60u )
{
sys_fs . error ( " sys_fs_fcntl(0xc000001c): invalid size (0x%x) " , arg - > size ) ;
break ;
}
if ( arg - > _x4 ! = 0x10u | | arg - > _x8 ! = 0x18u )
{
sys_fs . error ( " sys_fs_fcntl(0xc000001c): invalid args (0x%x, 0x%x) " , arg - > _x4 , arg - > _x8 ) ;
break ;
}
std : : string_view vpath { arg - > name . get_ptr ( ) , arg - > name_size } ;
2022-05-08 00:53:04 +02:00
// Trim trailing '\0'
if ( auto trim_pos = vpath . find ( ' \0 ' ) ; trim_pos ! = vpath . npos )
{
vpath . remove_suffix ( vpath . size ( ) - trim_pos ) ;
}
2021-12-02 16:41:53 +01:00
if ( ! vpath . starts_with ( " /dev_usb " sv ) )
{
arg - > out_code = CELL_ENOTSUP ;
break ;
}
2022-05-08 00:53:04 +02:00
const cfg : : device_info device = g_cfg_vfs . get_device ( g_cfg_vfs . dev_usb , vpath ) ;
2022-08-23 16:53:50 +02:00
if ( device . path . empty ( ) | | device . vid . empty ( ) | | device . pid . empty ( ) )
2022-05-08 00:53:04 +02:00
{
arg - > out_code = CELL_ENOTSUP ;
break ;
}
u16 vid { } ;
{
auto [ ptr , err ] = std : : from_chars ( device . vid . data ( ) , device . vid . data ( ) + device . vid . size ( ) , vid , 16 ) ;
if ( err ! = std : : errc ( ) )
{
fmt : : throw_exception ( " Failed to read hex string: %s " , std : : make_error_code ( err ) . message ( ) ) ;
}
}
u16 pid { } ;
{
auto [ ptr , err ] = std : : from_chars ( device . pid . data ( ) , device . pid . data ( ) + device . pid . size ( ) , pid , 16 ) ;
if ( err ! = std : : errc ( ) )
{
fmt : : throw_exception ( " Failed to read hex string: %s " , std : : make_error_code ( err ) . message ( ) ) ;
}
}
arg - > vendorID = vid ;
arg - > productID = pid ;
// Serial needs to be encoded to utf-16 BE
const std : : u16string serial = ascii8_to_utf16 ( device . serial ) ;
ensure ( ( serial . size ( ) * sizeof ( u16 ) ) < = sizeof ( arg - > serial ) ) ;
std : : memset ( arg - > serial , 0 , sizeof ( arg - > serial ) ) ;
const auto write_byteswapped = [ ] ( const void * src , void * dst ) - > void
{
* static_cast < u16 * > ( dst ) = * static_cast < const be_t < u16 > * > ( src ) ;
} ;
for ( size_t i = 0 ; i < serial . size ( ) ; i + + )
{
write_byteswapped ( & serial [ i ] , & arg - > serial [ i * 2 ] ) ;
}
2021-12-02 16:41:53 +01:00
arg - > out_code = CELL_OK ;
2022-05-08 00:53:04 +02:00
sys_fs . trace ( " sys_fs_fcntl(0xc000001c): found device '%s' (vid=0x%x, pid=0x%x, serial=%s) " , vpath , arg - > vendorID , arg - > productID , device . serial ) ;
2021-12-02 16:41:53 +01:00
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 ;
}
const auto directory = idm : : get < lv2_fs_object , lv2_dir > ( fd ) ;
if ( ! directory )
{
return CELL_EBADF ;
}
2020-01-03 21:29:39 +01:00
arg - > _size = 0 ; // This write is not really useful for cellFs but do it anyways
// NOTE: This function is actually capable of reading only one entry at a time
if ( arg - > max )
2017-04-22 14:29:20 +02:00
{
2020-01-03 21:29:39 +01:00
std : : memset ( arg - > ptr . get_ptr ( ) , 0 , arg - > max * arg - > ptr . size ( ) ) ;
2018-09-15 16:04:35 +02:00
if ( auto * info = directory - > dir_read ( ) )
2017-04-22 14:29:20 +02:00
{
2020-01-03 21:29:39 +01:00
auto & entry = arg - > ptr [ arg - > _size + + ] ;
2017-04-22 14:29:20 +02:00
2018-09-15 16:04:35 +02:00
entry . attribute . mode = info - > is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666 ;
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
2020-01-11 02:48:42 +01:00
if ( directory - > mp - > flags & lv2_mp_flag : : read_only )
{
// Remove write permissions
entry . attribute . mode & = ~ 0222 ;
}
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
}
}
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 ;
}
if ( const u32 id = idm : : import < lv2_fs_object , lv2_file > ( [ & ] ( ) - > std : : shared_ptr < lv2_file >
{
if ( ! g_fxo - > get < loaded_npdrm_keys > ( ) . npdrm_fds . try_inc ( 16 ) )
{
return nullptr ;
}
return std : : make_shared < lv2_file > ( result . ppath , std : : move ( result . file ) , 0 , 0 , std : : move ( result . real_path ) , lv2_file_type : : sdata ) ;
} ) )
{
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
2017-01-26 02:12:50 +01:00
const auto file = idm : : get < 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 )
{
case fs : : error : : inval : return CELL_EINVAL ;
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 ) ;
const auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
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 ) ;
const auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
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
2017-01-26 02:12:50 +01:00
const auto file = idm : : get < 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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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 } ;
}
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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
2020-10-02 13:37:58 +02:00
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
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
}
2020-01-04 22:55:15 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
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
{
2017-08-08 21:13:48 +02:00
case fs : : error : : noent : return { 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
2017-01-26 02:12:50 +01:00
const auto file = idm : : get < 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
2020-01-04 22:55:15 +01:00
if ( file - > mp - > flags & lv2_mp_flag : : read_only )
{
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 ) ;
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 } ;
}
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
if ( mp - > flags & lv2_mp_flag : : strict_get_block_size )
{
// TODO:
return { CELL_ENOTSUP , path } ;
}
2022-09-18 20:19:34 +02:00
ppu . check_state ( ) ;
2020-01-10 23:10:50 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
// TODO: check /dev_bdvd
* total_free = 0 ;
* avail_free = 0 ;
return CELL_OK ;
2017-09-18 23:03:26 +02:00
}
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
{
2020-09-18 21:52:04 +02:00
* avail_free = ( 1u < < 31 ) - mp - > sector_size ; // 2GB (TODO: Should be the total size)
}
else //if (mp == &g_mp_sys_dev_hdd0)
{
* avail_free = ( 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
2020-09-18 21:52:04 +02:00
* total_free = * avail_free + fs : : get_dir_size ( local_path , mp - > sector_size ) ;
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
2021-11-12 18:32:35 +01:00
const auto [ path_error , vpath ] = translate_to_sv ( 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
2020-10-02 13:37:58 +02:00
const auto mp = lv2_fs_object : : get_mp ( vpath ) ;
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
}
2020-01-04 22:55:15 +01:00
if ( mp - > flags & lv2_mp_flag : : read_only )
{
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 )
{
2017-08-08 21:13:48 +02:00
case fs : : error : : noent : return { 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 ) ;
const auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
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 ) ;
const auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
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 ;
}
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 ) ;
const auto file = idm : : get < lv2_fs_object , lv2_file > ( fd ) ;
if ( ! file )
{
return CELL_EBADF ;
}
2020-01-11 01:44:52 +01:00
// Unlock unconditionally
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
{
sys_fs . todo ( " sys_fs_get_mount_info_size(len=*0x%x) " , len ) ;
2021-07-17 19:19:13 +02:00
if ( ! len )
{
return CELL_EFAULT ;
}
* len = 0x8 ;
2020-02-07 10:31:33 +01:00
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_get_mount_info ( ppu_thread & , vm : : ptr < CellFsMountInfo > info , u32 len , vm : : ptr < u64 > out_len )
2020-02-07 10:31:33 +01:00
{
sys_fs . todo ( " sys_fs_get_mount_info(info=*0x%x, len=0x%x, out_len=*0x%x) " , info , len , out_len ) ;
2021-07-17 19:19:13 +02:00
if ( ! out_len )
{
return CELL_EFAULT ;
}
// TODO there is a case where 'something' happens if !info or len == 0
if ( ! info | | len = = 0 )
{
sys_fs . todo ( " sys_fs_get_mount_info special case TODO " ) ;
}
const u32 max_len = std : : min < u32 > ( len , 8 ) ;
* out_len = max_len ;
struct mount_info
{
std : : string_view path , filesystem , dev_name ;
be_t < u32 > unk1 = 0 , unk2 = 0 , unk3 = 0 , unk4 = 0 , unk5 = 0 ;
} ;
static constexpr std : : array < mount_info , 8 > data
{
mount_info { . path = " / " , . filesystem = " CELL_FS_ADMINFS " , . dev_name = " CELL_FS_ADMINFS: " , . unk5 = 0x10000000 } ,
mount_info { . path = " /app_home " , . filesystem = " CELL_FS_DUMMY " , . dev_name = " CELL_FS_DUMMY: " } ,
mount_info { . path = " /host_root " , . filesystem = " CELL_FS_DUMMY " , . dev_name = " CELL_FS_DUMMY: " } ,
mount_info { . path = " /dev_flash " , . filesystem = " CELL_FS_FAT " , . dev_name = " CELL_FS_IOS:BUILTIN_FLSH1: " , . unk5 = 0x10000000 } ,
mount_info { . path = " /dev_flash2 " , . filesystem = " CELL_FS_FAT " , . dev_name = " CELL_FS_IOS:BUILTIN_FLSH2: " } ,
mount_info { . path = " /dev_flash3 " , . filesystem = " CELL_FS_FAT " , . dev_name = " CELL_FS_IOS:BUILTIN_FLSH3: " } ,
mount_info { . path = " /dev_hdd0 " , . filesystem = " CELL_FS_UFS " , . dev_name = " CELL_FS_UTILITY:HDD0: " } ,
mount_info { . path = " /dev_bdvd " , . filesystem = " CELL_FS_ISO9660 " , . dev_name = " CELL_FS_IOS:PATA0_BDVD_DRIVE " } ,
} ;
for ( u32 i = 0 ; i < max_len ; info + + , i + + )
{
strcpy_trunc ( info - > mount_path , data [ i ] . path ) ;
strcpy_trunc ( info - > filesystem , data [ i ] . filesystem ) ;
strcpy_trunc ( info - > dev_name , data [ i ] . dev_name ) ;
std : : memcpy ( & info - > unk1 , & data [ i ] . unk1 , sizeof ( be_t < u32 > ) * 5 ) ;
}
2020-02-07 10:31:33 +01:00
return CELL_OK ;
}
2021-03-05 20:05:37 +01:00
error_code sys_fs_mount ( ppu_thread & , vm : : cptr < char > dev_name , vm : : cptr < char > file_system , vm : : cptr < char > path , s32 unk1 , s32 prot , s32 unk3 , vm : : cptr < char > str1 , u32 str_len )
2020-02-07 10:31:33 +01:00
{
sys_fs . todo ( " sys_fs_mount(dev_name=%s, file_system=%s, path=%s, unk1=0x%x, prot=0x%x, unk3=0x%x, str1=%s, str_len=%d) " , dev_name , file_system , path , unk1 , prot , unk3 , str1 , str_len ) ;
return CELL_OK ;
}