Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/Cross-libc-vfs-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ jobs:
ASAN_OPTIONS=${{ matrix.asan_opts }} ./vfs_bulk_read_test
./vfs_seek_contract_test
./vfs_mapped_ptr_test
./vfs_v5_metadata_test
}
# Mapping on (what the platform actually ships), mapping off
# with the descriptor path forced (what the consoles have),
Expand Down Expand Up @@ -158,6 +159,9 @@ jobs:
make MMAP=0
./vfs_bulk_read_test.exe
./vfs_seek_contract_test.exe
# Win32 branches of the v5 metadata ops: attributes,
# SetFileTime, CopyFileW, find-data dirent_stat.
./vfs_v5_metadata_test.exe
# Exercises the Win32 CreateFileMapping/MapViewOfFile
# backend, which no other job in the tree reaches.
./vfs_mapped_ptr_test.exe
20 changes: 20 additions & 0 deletions .github/workflows/Linux-libretro-common-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ jobs:
vfs_large_file_test
vfs_seek_contract_test
vfs_bulk_read_test
vfs_v5_metadata_test
vfs_hybrid_test
filestream_rbuf_fault_test
cdrom_cuesheet_overflow_test
Expand Down Expand Up @@ -819,3 +820,22 @@ jobs:
UBSAN_OPTIONS=print_stacktrace=1 timeout 120 \
./vfs_mapped_ptr_test
echo "[pass] vfs_mapped_ptr_test (MMAP=0, ASan)"

- name: Run vfs_v5_metadata_test against a copy_file_range that ignores len
shell: bash
working-directory: libretro-common/samples/file/vfs
run: |
set -eu
# A sandboxed kernel (gVisor on some hosted runners) answers
# copy_file_range by copying to EOF regardless of len. The
# VFS must account the bytes, retire the kernel path for the
# process, and finish the copy in place on the portable path.
# FAKE_CFR=1 is the to-EOF case, FAKE_CFR=2 a partial overshoot
# that leaves the copy part-way and has to be resumed.
for mode in 1 2; do
make clean >/dev/null
make vfs_v5_metadata_test FAKE_CFR=$mode SANITIZER=address,undefined
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 timeout 120 \
./vfs_v5_metadata_test
echo "[pass] vfs_v5_metadata_test (FAKE_CFR=$mode, ASan)"
done
49 changes: 49 additions & 0 deletions libretro-common/file/file_path_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@
static retro_vfs_stat_t path_stat32_cb = retro_vfs_stat_impl;
static retro_vfs_stat_64_t path_stat64_cb = retro_vfs_stat_64_impl;
static retro_vfs_mkdir_t path_mkdir_cb = retro_vfs_mkdir_impl;
/* VFS API v5. NULL when a frontend older than v5 is in use, so the
* wrappers report failure instead of touching the local file system
* behind a foreign frontend's back. */
static retro_vfs_set_readonly_t path_set_readonly_cb = retro_vfs_set_readonly_impl;
static retro_vfs_get_mtime_t path_get_mtime_cb = retro_vfs_get_mtime_impl;
static retro_vfs_set_mtime_t path_set_mtime_cb = retro_vfs_set_mtime_impl;

void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
{
Expand All @@ -80,6 +86,9 @@ void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
path_stat32_cb = retro_vfs_stat_impl;
path_stat64_cb = retro_vfs_stat_64_impl;
path_mkdir_cb = retro_vfs_mkdir_impl;
path_set_readonly_cb = retro_vfs_set_readonly_impl;
path_get_mtime_cb = retro_vfs_get_mtime_impl;
path_set_mtime_cb = retro_vfs_set_mtime_impl;

if (vfs_info->required_interface_version < PATH_REQUIRED_VFS_VERSION || !vfs_iface)
return;
Expand All @@ -91,6 +100,46 @@ void path_vfs_init(const struct retro_vfs_interface_info* vfs_info)
path_stat64_cb = vfs_iface->stat_64;
else
path_stat64_cb = NULL;

/* Members a v5 frontend left NULL stay NULL: the wrappers then
* report "unavailable" rather than dereferencing them. */
path_set_readonly_cb = NULL;
path_get_mtime_cb = NULL;
path_set_mtime_cb = NULL;
if (vfs_info->required_interface_version >= METADATA_REQUIRED_VFS_VERSION)
{
path_set_readonly_cb = vfs_iface->set_readonly;
path_get_mtime_cb = vfs_iface->get_mtime;
path_set_mtime_cb = vfs_iface->set_mtime;
}
}

bool path_is_readonly(const char *path)
{
if (path_stat64_cb)
return (path_stat64_cb(path, NULL) & RETRO_VFS_STAT_IS_READONLY) != 0;
return (path_stat32_cb(path, NULL) & RETRO_VFS_STAT_IS_READONLY) != 0;
}

bool path_set_readonly(const char *path, bool readonly)
{
if (!path_set_readonly_cb)
return false;
return path_set_readonly_cb(path, readonly ? 1 : 0) == 0;
}

bool path_get_mtime(const char *path, int64_t *mtime)
{
if (!path_get_mtime_cb || !mtime)
return false;
return path_get_mtime_cb(path, mtime) == 0;
}

bool path_set_mtime(const char *path, int64_t mtime)
{
if (!path_set_mtime_cb)
return false;
return path_set_mtime_cb(path, mtime) == 0;
}

int path_stat(const char *path)
Expand Down
26 changes: 26 additions & 0 deletions libretro-common/file/retro_dirent.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ static retro_vfs_readdir_t dirent_readdir_cb = NULL;
static retro_vfs_dirent_get_name_t dirent_dirent_get_name_cb = NULL;
static retro_vfs_dirent_is_dir_t dirent_dirent_is_dir_cb = NULL;
static retro_vfs_closedir_t dirent_closedir_cb = NULL;
static retro_vfs_dirent_stat_t dirent_dirent_stat_cb = NULL;
/* Set when a frontend older than VFS API v5 owns the directory
* handles: the local _impl cannot be used on a foreign handle, so
* retro_dirent_stat() reports "unavailable" instead. */
static bool dirent_stat_unavailable = false;

void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info)
{
Expand All @@ -47,6 +52,8 @@ void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info)
dirent_dirent_get_name_cb = NULL;
dirent_dirent_is_dir_cb = NULL;
dirent_closedir_cb = NULL;
dirent_dirent_stat_cb = NULL;
dirent_stat_unavailable = false;

vfs_iface = vfs_info->iface;

Expand All @@ -60,6 +67,14 @@ void dirent_vfs_init(const struct retro_vfs_interface_info* vfs_info)
dirent_dirent_get_name_cb = vfs_iface->dirent_get_name;
dirent_dirent_is_dir_cb = vfs_iface->dirent_is_dir;
dirent_closedir_cb = vfs_iface->closedir;

/* A frontend that negotiated v5 but left the member NULL is treated
* like an older one: the local _impl must not touch its handles. */
if (vfs_info->required_interface_version >= DIRENT_STAT_REQUIRED_VFS_VERSION
&& vfs_iface->dirent_stat)
dirent_dirent_stat_cb = vfs_iface->dirent_stat;
else
dirent_stat_unavailable = true;
}

struct RDIR *retro_opendir_include_hidden(
Expand Down Expand Up @@ -113,6 +128,17 @@ bool retro_dirent_is_dir(struct RDIR *rdir, const char *unused)
return retro_vfs_dirent_is_dir_impl((struct retro_vfs_dir_handle *)rdir);
}

int retro_dirent_stat(struct RDIR *rdir, int64_t *size, int64_t *mtime)
{
if (!rdir)
return 0;
if (dirent_dirent_stat_cb)
return dirent_dirent_stat_cb((struct retro_vfs_dir_handle *)rdir, size, mtime);
if (dirent_stat_unavailable)
return 0;
return retro_vfs_dirent_stat_impl((struct retro_vfs_dir_handle *)rdir, size, mtime);
}

void retro_closedir(struct RDIR *rdir)
{
if (dirent_closedir_cb)
Expand Down
43 changes: 43 additions & 0 deletions libretro-common/include/file/file_path.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ RETRO_BEGIN_DECLS

#define PATH_REQUIRED_VFS_VERSION 3
#define STAT64_REQUIRED_VFS_VERSION 4
#define METADATA_REQUIRED_VFS_VERSION 5

void path_vfs_init(const struct retro_vfs_interface_info* vfs_info);

Expand Down Expand Up @@ -696,6 +697,48 @@ bool path_is_valid(const char *path);
**/
bool path_set_private(const char *path);

/**
* path_is_readonly:
* @path : path
*
* Whether the current user cannot write to @path. Reads the
* RETRO_VFS_STAT_IS_READONLY flag, which frontends older than
* VFS API v5 never set, so the answer there is always false.
*
* @return true if @path exists and is read-only.
**/
bool path_is_readonly(const char *path);

/**
* path_set_readonly:
* @path : path
* @readonly : true to make read-only, false to make writable
*
* POSIX: toggles the write bits. Windows: FILE_ATTRIBUTE_READONLY.
*
* @return true on success, false if unsupported on this platform,
* the file system, or the negotiated VFS version (< 5).
**/
bool path_set_readonly(const char *path, bool readonly);

/**
* path_get_mtime:
* @path : path
* @mtime : receives seconds since 1970-01-01T00:00:00Z
*
* @return true on success, false if unavailable.
**/
bool path_get_mtime(const char *path, int64_t *mtime);

/**
* path_set_mtime:
* @path : path
* @mtime : seconds since 1970-01-01T00:00:00Z
*
* @return true on success, false if unsupported or it failed.
**/
bool path_set_mtime(const char *path, int64_t mtime);

int64_t path_get_size(const char *path);

bool is_path_accessible_using_standard_io(const char *path);
Expand Down
Loading
Loading