scan: don't wait on an SMB server on the main thread

From the menu, the main thread stat'd an smb:// content folder up to
three times before its scan task ran: for the playlist name, for
whether it is a file and for the scan's folder flag. Manual Scan, Scan
Directory, Scan File and a lone Refresh Playlist each did, and against
a server that does not answer each stat waits out a connect with the
menu frozen.

The manual scan settings now keep what the caller knows the content
folder to be. The menu's folder pick, Scan File and Scan Directory say
whether it is a folder or a file, a refresh says its saved folder is a
folder, and --scan and the chain's refreshes take it from the worker's
read of the path. For an smb:// path of known kind those three places
use the kind instead of a stat: the menu paths stat it none at all on
the main thread, and a --scan or refresh of a reachable share is not
stat'd again when its scan is pushed. The kind is kept with the
settings a scan chain saves and puts back. A local path, and an
smb:// one of unknown kind, stats as before.
This commit is contained in:
davey authored and libretroadmin committed 2026-11-06 05:15:14 +01:00
1 parent 6ef3af70f8
commit c4fb538a89
7 files changed
+481 -22

No files matched your search

+59 -11
View File
@@ -94,6 +94,7 @@ static char scan_file_exts_custom[PATH_MAX_LENGTH];
static char scan_dat_file_path[PATH_MAX_LENGTH];
/* A folder or a file */
static char scan_content_dir[PATH_MAX_LENGTH];
static enum manual_content_scan_path_kind scan_content_kind;
/* Number of bytes held back when populating
* manual_content_scan_task_config_t::system_name, so that the file
* extensions appended to it downstream (".lpl", ".rdb") still fit in
@@ -400,6 +401,7 @@ void manual_content_scan_reset_settings(void)
scan_file_exts_custom[0] = '\0';
scan_dat_file_path[0] = '\0';
scan_content_dir[0] = '\0';
scan_content_kind = MANUAL_CONTENT_SCAN_PATH_UNKNOWN;
scan_system_name_content_dir[0] = '\0';
scan_system_name_database[0] = '\0';
scan_system_name_custom[0] = '\0';
@@ -409,6 +411,7 @@ void manual_content_scan_reset_settings(void)
struct manual_content_scan_saved
{
scan_settings_t settings;
enum manual_content_scan_path_kind content_kind;
char core_path[PATH_MAX_LENGTH];
char file_exts_core[PATH_MAX_LENGTH];
char file_exts_custom[PATH_MAX_LENGTH];
@@ -427,6 +430,7 @@ manual_content_scan_saved_t *manual_content_scan_save_settings(void)
if (!sv)
return NULL;
sv->settings = scan_settings;
sv->content_kind = scan_content_kind;
memcpy(sv->core_path, scan_core_path, sizeof(sv->core_path));
memcpy(sv->file_exts_core, scan_file_exts_core, sizeof(sv->file_exts_core));
memcpy(sv->file_exts_custom, scan_file_exts_custom, sizeof(sv->file_exts_custom));
@@ -444,6 +448,7 @@ void manual_content_scan_restore_settings(manual_content_scan_saved_t *sv)
if (!sv)
return;
scan_settings = sv->settings;
scan_content_kind = sv->content_kind;
memcpy(scan_core_path, sv->core_path, sizeof(scan_core_path));
memcpy(scan_file_exts_core, sv->file_exts_core, sizeof(scan_file_exts_core));
memcpy(scan_file_exts_custom, sv->file_exts_custom, sizeof(scan_file_exts_custom));
@@ -456,16 +461,21 @@ void manual_content_scan_restore_settings(manual_content_scan_saved_t *sv)
free(sv);
}
static enum manual_content_scan_path_kind manual_content_scan_fs_kind(
const manual_content_scan_fs_t *fs)
{
if (!fs || !(fs->content_stat & RETRO_VFS_STAT_IS_VALID))
return MANUAL_CONTENT_SCAN_PATH_UNKNOWN;
return (fs->content_stat & RETRO_VFS_STAT_IS_DIRECTORY)
? MANUAL_CONTENT_SCAN_PATH_FOLDER : MANUAL_CONTENT_SCAN_PATH_FILE;
}
/* Sets content directory for next manual scan
* operation.
* Returns true if content directory is valid. */
bool manual_content_scan_set_menu_content_dir(const char *content_dir)
{
return manual_content_scan_set_menu_content_dir_fs(content_dir, NULL);
}
bool manual_content_scan_set_menu_content_dir_fs(const char *content_dir,
const manual_content_scan_fs_t *fs)
static bool manual_content_scan_set_content_dir(const char *content_dir,
const manual_content_scan_fs_t *fs,
enum manual_content_scan_path_kind kind)
{
size_t _len;
int path_stats;
@@ -525,9 +535,12 @@ bool manual_content_scan_set_menu_content_dir_fs(const char *content_dir,
if (!dir_name || !*dir_name)
goto error;
scan_content_kind = kind;
/* A file takes the name of the folder that holds it,
* as a scan of that folder would */
path_stats = fs ? fs->content_stat : path_stat(scan_content_dir);
path_stats = fs ? fs->content_stat
: manual_content_scan_content_stat(scan_content_dir);
if ( (path_stats & RETRO_VFS_STAT_IS_VALID)
&& !(path_stats & RETRO_VFS_STAT_IS_DIRECTORY)
&& fill_pathname_parent_dir_name(scan_system_name_content_dir,
@@ -547,10 +560,42 @@ error:
* content directory and associated 'directory'
* system name */
scan_content_dir[0] = '\0';
scan_content_kind = MANUAL_CONTENT_SCAN_PATH_UNKNOWN;
scan_system_name_content_dir[0] = '\0';
return false;
}
bool manual_content_scan_set_menu_content_dir(const char *content_dir)
{
return manual_content_scan_set_content_dir(content_dir, NULL,
MANUAL_CONTENT_SCAN_PATH_UNKNOWN);
}
bool manual_content_scan_set_menu_content_dir_fs(const char *content_dir,
const manual_content_scan_fs_t *fs)
{
return manual_content_scan_set_content_dir(content_dir, fs,
manual_content_scan_fs_kind(fs));
}
bool manual_content_scan_set_menu_content_dir_kind(const char *content_dir,
enum manual_content_scan_path_kind kind)
{
return manual_content_scan_set_content_dir(content_dir, NULL, kind);
}
int manual_content_scan_content_stat(const char *path)
{
#ifdef HAVE_SMBCLIENT
if ( scan_content_kind != MANUAL_CONTENT_SCAN_PATH_UNKNOWN
&& string_starts_with_size(path, "smb://", STRLEN_CONST("smb://")))
return scan_content_kind == MANUAL_CONTENT_SCAN_PATH_FOLDER
? RETRO_VFS_STAT_IS_VALID | RETRO_VFS_STAT_IS_DIRECTORY
: RETRO_VFS_STAT_IS_VALID;
#endif
return path_stat(path);
}
bool manual_content_scan_set_menu_scan_method(
enum manual_content_scan_method method)
{
@@ -854,8 +899,10 @@ enum manual_content_scan_playlist_refresh_status
if (!*system_name)
return MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_SYSTEM_NAME;
/* Set content directory */
if (!manual_content_scan_set_menu_content_dir_fs(content_dir, fs))
/* Set content directory: a file scan saves none */
if (!manual_content_scan_set_content_dir(content_dir, fs,
fs ? manual_content_scan_fs_kind(fs)
: MANUAL_CONTENT_SCAN_PATH_FOLDER))
return MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_INVALID_CONTENT_DIR;
if (!manual_content_scan_set_menu_scan_use_db((enum manual_content_scan_db_usage)db_usage))
@@ -1974,7 +2021,8 @@ bool manual_content_scan_get_task_config(
if (!*scan_content_dir)
return false;
task_config->content_is_file = !path_is_directory(scan_content_dir);
task_config->content_is_file = !(manual_content_scan_content_stat(
scan_content_dir) & RETRO_VFS_STAT_IS_DIRECTORY);
strlcpy(
task_config->content_dir,
+17
View File
@@ -54,6 +54,14 @@ enum manual_content_scan_system_name_type
MANUAL_CONTENT_SCAN_SYSTEM_NAME_DATABASE
};
/* What the caller knows a content dir to be */
enum manual_content_scan_path_kind
{
MANUAL_CONTENT_SCAN_PATH_UNKNOWN = 0,
MANUAL_CONTENT_SCAN_PATH_FOLDER,
MANUAL_CONTENT_SCAN_PATH_FILE
};
/* Defines types of matching content to databases */
enum manual_content_scan_db_usage
{
@@ -301,6 +309,15 @@ void manual_content_scan_fs_free(manual_content_scan_fs_t *fs);
bool manual_content_scan_set_menu_content_dir_fs(const char *content_dir,
const manual_content_scan_fs_t *fs);
/* As manual_content_scan_set_menu_content_dir(), of @kind: an smb://
* path of known kind is not stat'd, as that can wait on the server. */
bool manual_content_scan_set_menu_content_dir_kind(const char *content_dir,
enum manual_content_scan_path_kind kind);
/* path_stat() of @path, the content directory last set, or for an
* smb:// one of known kind what its kind says. */
int manual_content_scan_content_stat(const char *path);
bool manual_content_scan_set_menu_scan_method(
enum manual_content_scan_method method);
+13 -4
View File
@@ -4585,8 +4585,8 @@ static int action_ok_path_scan_directory(const char *path,
}
#endif
/* Also called by the Apple file pickers opened from Content Location. */
void action_ok_manual_content_scan_set_dir(const char *content_dir)
static void action_ok_manual_content_scan_set_dir_kind(
const char *content_dir, enum manual_content_scan_path_kind kind)
{
#ifdef HAVE_COCOATOUCH
/* For iOS, set the path using realpath because the path name
@@ -4604,7 +4604,7 @@ void action_ok_manual_content_scan_set_dir(const char *content_dir)
filebrowser_clear_type();
/* Update manual content scan settings */
manual_content_scan_set_menu_content_dir(content_dir);
manual_content_scan_set_menu_content_dir_kind(content_dir, kind);
#ifdef HAVE_COCOATOUCH
free(real_dir);
#endif
@@ -4613,6 +4613,13 @@ void action_ok_manual_content_scan_set_dir(const char *content_dir)
menu_entries_flush_stack(MENU_ENUM_LABEL_DEFERRED_MANUAL_CONTENT_SCAN_LIST_STR, 0);
}
/* Also called by the Apple file pickers opened from Content Location. */
void action_ok_manual_content_scan_set_dir(const char *content_dir)
{
action_ok_manual_content_scan_set_dir_kind(content_dir,
MANUAL_CONTENT_SCAN_PATH_UNKNOWN);
}
/* Whether Content Location's list of places or its browser, or a
* folder entered from it, is the current menu. Back leaves the browser
* type set; a folder entered keeps the browser's label. */
@@ -4669,7 +4676,9 @@ static int action_ok_path_manual_scan_directory(const char *path,
return 0;
}
action_ok_manual_content_scan_set_dir(content_dir);
action_ok_manual_content_scan_set_dir_kind(content_dir,
type == FILE_TYPE_MANUAL_SCAN_DIRECTORY
? MANUAL_CONTENT_SCAN_PATH_FOLDER : MANUAL_CONTENT_SCAN_PATH_FILE);
return 0;
}
+2 -2
View File
@@ -69,7 +69,7 @@ int action_scan_file(const char *path,
fill_pathname_join_special(fullpath, menu_path, path, sizeof(fullpath));
task_push_dbscan(fullpath, handle_dbscan_finished, NULL);
task_push_dbscan_known(fullpath, false, handle_dbscan_finished, NULL);
return 0;
}
@@ -96,7 +96,7 @@ int action_scan_directory(const char *path,
else
strlcpy(fullpath, menu_path, sizeof(fullpath));
task_push_dbscan(fullpath, handle_dbscan_finished, NULL);
task_push_dbscan_known(fullpath, true, handle_dbscan_finished, NULL);
return 0;
}
+365 -2
View File
@@ -312,9 +312,13 @@ int retro_vfs_file_error_smb(libretro_vfs_implementation_file *stream)
return 1;
}
/* How often a folder was opened */
static retro_atomic_int_t smb_stub_opendirs;
smb_dir_handle *retro_vfs_opendir_smb(const char *path, bool include_hidden)
{
(void)path; (void)include_hidden;
retro_atomic_fetch_add_int(&smb_stub_opendirs, 1);
return NULL;
}
@@ -330,10 +334,26 @@ int retro_vfs_closedir_smb(smb_dir_handle *dh)
return -1;
}
/* Set: the share answers, names ending .bin are files and the rest
* folders. How often a stat was asked on the main thread, and that
* count when a scan was pushed (-1: none was). */
static bool smb_stub_live;
static retro_atomic_int_t smb_stub_stats_main;
static retro_atomic_int_t smb_stats_at_push;
int retro_vfs_stat_smb(const char *path, int64_t *size)
{
(void)path; (void)size;
return 0;
size_t n = strlen(path);
if (size)
*size = 0;
if (task_is_on_main_thread())
retro_atomic_fetch_add_int(&smb_stub_stats_main, 1);
if (!smb_stub_live)
return 0;
if (n > 4 && !strcmp(path + n - 4, ".bin"))
return RETRO_VFS_STAT_IS_VALID;
return RETRO_VFS_STAT_IS_VALID | RETRO_VFS_STAT_IS_DIRECTORY;
}
/* What smb_url_check() and smb_url_failure() answer, and how often
@@ -1081,6 +1101,12 @@ bool __wrap_task_queue_push(retro_task_t *task)
{
int n;
#ifdef HAVE_SMBCLIENT
/* Only a scan has a title */
if (task->title)
retro_atomic_store_release_int(&smb_stats_at_push,
retro_atomic_load_acquire_int(&smb_stub_stats_main));
#endif
if (!(task_get_flags(task) & RETRO_TASK_FLG_PARALLEL))
{
if ( prep_cancel_at && !task->title
@@ -10025,6 +10051,341 @@ done:
sizeof(config_get_ptr()->paths.directory_playlist));
free(saved_pl);
}
enum smb_kind_push
{
SMB_KIND_DBSCAN = 0, /* task_push_dbscan(), kind unknown */
SMB_KIND_FOLDER, /* task_push_dbscan_known(), a folder */
SMB_KIND_FILE, /* task_push_dbscan_known(), a file */
SMB_KIND_AS_SET /* the settings as set */
};
static void smb_kind_reset(void)
{
retro_atomic_store_release_int(&smb_stub_stats_main, 0);
retro_atomic_store_release_int(&smb_stats_at_push, -1);
retro_atomic_store_release_int(&smb_stub_opendirs, 0);
}
/* Scans @path as run_scan() does, pushed as @how says. Returns the
* main thread's smb:// stats before the push (since smb_kind_reset()
* for SMB_KIND_AS_SET), or -1 if none was. */
static int smb_kind_scan(const char *path, enum smb_kind_push how)
{
time_t started;
bool pushed;
if (how != SMB_KIND_AS_SET)
smb_kind_reset();
loop_active = true;
scan_completed = false;
scan_result.failed = true;
switch (how)
{
case SMB_KIND_DBSCAN:
pushed = task_push_dbscan(path, scan_cb, &scan_result);
break;
case SMB_KIND_FOLDER:
case SMB_KIND_FILE:
pushed = task_push_dbscan_known(path, how == SMB_KIND_FOLDER,
scan_cb, &scan_result);
break;
default:
pushed = task_push_manual_content_scan(false, scan_cb,
&scan_result);
break;
}
if (!pushed)
return -1;
started = time(NULL);
while (loop_active)
{
task_queue_check();
if (difftime(time(NULL), started) > SCAN_TIMEOUT_SECONDS)
break;
retro_sleep(1);
}
return retro_atomic_load_acquire_int(&smb_stats_at_push);
}
/* A chain run of @items; the main thread's smb:// stats before its
* scan was pushed. */
static int smb_kind_chain(task_dbscan_item_t *items, const char *out,
const char *err, bool *ran)
{
smb_kind_reset();
*ran = refresh_run(items, 1, out, err, true);
return retro_atomic_load_acquire_int(&smb_stats_at_push);
}
/* An smb:// path of known kind is not stat'd on the main thread
* before its scan is pushed: Scan Directory and Scan File, the menu's
* folder pick, a lone refresh, and the chain's --scan and refresh
* items, whose path or load task stats it off the main thread
* instead. Unknown kind, and a local path whatever its kind, are
* stat'd. A single smb:// file keeps its folder's playlist name, its
* single-file database scan, saves no settings and ignores
* Overwrite, as when the stat told it. */
static void smb_kind_lane(const char *root, bool threaded)
{
const char *dir = "smb://host/share/roms";
const char *file = "smb://host/share/roms/01_alpha.bin";
const char *q = threaded ? "(threaded)" : "(regular)";
char pl[512], lpl[1024], ref[1024], loc[512], p[1024];
char out[512], err[512], line[1024], what[96], detail[160];
manual_content_scan_task_config_t *cfg = (manual_content_scan_task_config_t*)
calloc(2, sizeof(*cfg));
task_dbscan_result_t res;
task_dbscan_item_t *items;
playlist_config_t *pcfg = (playlist_config_t*)calloc(1, sizeof(*pcfg));
playlist_t *plst;
int n, n2, task_n;
bool ok, ok2, ran, as_folder, as_file, off_main = false;
char *saved_pl = strdup(
config_get_ptr()->paths.directory_playlist);
#ifdef HAVE_THREADS
off_main = threaded;
#endif
/* The path or load task's one stat */
task_n = off_main ? 0 : 1;
if (!cfg || !pcfg)
goto fixture;
sprintf(pl, "%s/smb_kind_pl", root);
sprintf(loc, "%s/smb_kind_local", root);
sprintf(lpl, "%s/roms.lpl", pl);
sprintf(ref, "%s/smb_kind_ref.lpl", root);
sprintf(out, "%s/smb_kind.out", root);
sprintf(err, "%s/smb_kind.err", root);
path_mkdir(pl);
path_mkdir(loc);
delete_playlists(pl);
sprintf(p, "%s/02_beta.bin", loc);
if (!write_content(p, 0x52A3ED00u, 1024))
goto fixture;
strlcpy(config_get_ptr()->paths.directory_playlist, pl,
sizeof(config_get_ptr()->paths.directory_playlist));
use_queue(threaded);
smb_stub_check = SMB_URL_OK;
smb_stub_failure = SMB_FAILURE_UNKNOWN;
smb_stub_detail = "";
smb_stub_live = true;
/* Unknown kind: set_menu_content_dir, get_task_config and the
* directory flag stat it. */
manual_content_scan_reset_settings();
n = smb_kind_scan(dir, SMB_KIND_DBSCAN);
n2 = smb_kind_scan(file, SMB_KIND_DBSCAN);
sprintf(what, "smb kind %s: unknown kind stats the path", q);
sprintf(detail, "folder %d, file %d", n, n2);
check(n == 3 && n2 == 3, what, detail);
/* Scan Directory, Scan File. Only a folder push opens the
* folder. */
n = smb_kind_scan(dir, SMB_KIND_FOLDER);
as_folder = retro_atomic_load_acquire_int(&smb_stub_opendirs) == 1
&& manual_content_scan_get_task_config(&cfg[0], pl)
&& !cfg[0].content_is_file;
n2 = smb_kind_scan(file, SMB_KIND_FILE);
as_file = !retro_atomic_load_acquire_int(&smb_stub_opendirs);
sprintf(what, "smb kind %s: Scan Directory and Scan File", q);
sprintf(detail, "folder %d, as a folder %d, file %d, as a file %d",
n, (int)as_folder, n2, (int)as_file);
check(n == 0 && as_folder && n2 == 0 && as_file, what, detail);
/* The menu's folder pick */
smb_kind_reset();
ok = manual_content_scan_set_menu_content_dir_kind(dir,
MANUAL_CONTENT_SCAN_PATH_FOLDER);
as_folder = ok && manual_content_scan_get_task_config(&cfg[0], pl)
&& !cfg[0].content_is_file;
n = ok ? smb_kind_scan(dir, SMB_KIND_AS_SET) : -1;
as_folder = as_folder
&& retro_atomic_load_acquire_int(&smb_stub_opendirs) == 1;
smb_kind_reset();
ok2 = manual_content_scan_set_menu_content_dir_kind(file,
MANUAL_CONTENT_SCAN_PATH_FILE);
n2 = ok2 ? smb_kind_scan(file, SMB_KIND_AS_SET) : -1;
as_file = ok2 && !retro_atomic_load_acquire_int(&smb_stub_opendirs);
sprintf(what, "smb kind %s: the menu's folder pick", q);
sprintf(detail, "folder %d, as a folder %d, file %d, as a file %d",
n, (int)as_folder, n2, (int)as_file);
check(n == 0 && as_folder && n2 == 0 && as_file, what, detail);
/* A lone refresh: the saved folder is a folder. */
delete_playlists(pl);
n = -1;
if (!refresh_write(lpl, dir, NULL, "smb://host/share/roms/00_old.bin"))
goto fixture;
playlist_config_set_path(pcfg, lpl);
pcfg->capacity = COLLECTION_SIZE;
if ((plst = playlist_init(pcfg)))
{
manual_content_scan_reset_settings();
smb_kind_reset();
if (manual_content_scan_set_menu_from_playlist(plst,
config_get_ptr()->paths.path_content_database, false)
== MANUAL_CONTENT_SCAN_PLAYLIST_REFRESH_OK)
n = smb_kind_scan(dir, SMB_KIND_AS_SET);
playlist_free(plst);
}
sprintf(what, "smb kind %s: a lone refresh", q);
sprintf(detail, "%d", n);
check(n == 0, what, detail);
/* The chain: --scan of a folder and of a file with
* --scan-overwrite, --scan-refresh, refresh-all. */
delete_playlists(pl);
if (!(items = (task_dbscan_item_t*)calloc(1, sizeof(*items))))
goto fixture;
chain_item(&items[0], dir, NULL);
if ((items[0].overrides = manual_content_scan_overrides_new()))
items[0].overrides->flags |= MANUAL_CONTENT_SCAN_OVR_OVERWRITE;
n = smb_kind_chain(items, out, err, &ran);
sprintf(what, "smb kind %s: --scan of a folder", q);
sprintf(detail, "%d (want %d), %u items", n, task_n, chain_seen);
check( ran && n == task_n && chain_seen == 1
&& !file_contains(err, "single file")
&& !file_contains(err, "not found"), what, detail);
if (!(items = (task_dbscan_item_t*)calloc(1, sizeof(*items))))
goto fixture;
chain_item(&items[0], file, NULL);
if ((items[0].overrides = manual_content_scan_overrides_new()))
items[0].overrides->flags |= MANUAL_CONTENT_SCAN_OVR_OVERWRITE;
n = smb_kind_chain(items, out, err, &ran);
sprintf(line, "%s: --scan-overwrite ignored for a single file", file);
sprintf(what, "smb kind %s: --scan of a file", q);
sprintf(detail, "%d (want %d), %u items", n, task_n, chain_seen);
check( ran && n == task_n && chain_seen == 1
&& file_contains(err, line), what, detail);
delete_playlists(pl);
if (!refresh_write(lpl, dir, NULL, "smb://host/share/roms/00_old.bin")
|| !(items = (task_dbscan_item_t*)calloc(1, sizeof(*items))))
goto fixture;
refresh_item(&items[0], TASK_DBSCAN_ITEM_REFRESH, "roms");
n = smb_kind_chain(items, out, err, &ran);
ok = ran;
if (!(items = (task_dbscan_item_t*)calloc(1, sizeof(*items))))
goto fixture;
refresh_item(&items[0], TASK_DBSCAN_ITEM_REFRESH_ALL, NULL);
n2 = smb_kind_chain(items, out, err, &ran);
sprintf(what, "smb kind %s: --scan-refresh and refresh-all", q);
sprintf(detail, "%d, %d (want %d)", n, n2, task_n);
check(ok && ran && n == task_n && n2 == task_n, what, detail);
/* A picked file is one still once a chain has put the settings
* back. */
manual_content_scan_reset_settings();
manual_content_scan_set_menu_scan_method(
MANUAL_CONTENT_SCAN_METHOD_CUSTOM);
manual_content_scan_set_menu_scan_use_db(MANUAL_CONTENT_SCAN_USE_DB_NONE);
ok = manual_content_scan_set_menu_content_dir_kind(file,
MANUAL_CONTENT_SCAN_PATH_FILE);
delete_playlists(pl);
if (!refresh_write(lpl, dir, NULL, "smb://host/share/roms/00_old.bin")
|| !(items = (task_dbscan_item_t*)calloc(1, sizeof(*items))))
goto fixture;
refresh_item(&items[0], TASK_DBSCAN_ITEM_REFRESH_ALL, NULL);
smb_kind_chain(items, out, err, &ran);
smb_kind_reset();
ok2 = ok && ran && manual_content_scan_get_task_config(&cfg[0], pl);
n = retro_atomic_load_acquire_int(&smb_stub_stats_main);
sprintf(what, "smb kind %s: a picked file after refresh-all", q);
sprintf(detail, "%s, file %d, %d stats",
ok2 ? path_basename(cfg[0].content_dir) : "-",
ok2 && cfg[0].content_is_file, n);
check( ok2 && n == 0 && cfg[0].content_is_file
&& !strcmp(cfg[0].content_dir, file), what, detail);
/* One file: the folder's playlist name, no Overwrite, from the
* kind as from the stat; a local path stats whatever its kind. */
manual_content_scan_reset_settings();
manual_content_scan_set_menu_scan_method(
MANUAL_CONTENT_SCAN_METHOD_CUSTOM);
manual_content_scan_set_menu_scan_use_db(MANUAL_CONTENT_SCAN_USE_DB_NONE);
*manual_content_scan_get_overwrite_playlist_ptr() = true;
ok = manual_content_scan_set_menu_content_dir(file)
&& manual_content_scan_get_task_config(&cfg[0], pl);
smb_kind_reset();
ok2 = manual_content_scan_set_menu_content_dir_kind(file,
MANUAL_CONTENT_SCAN_PATH_FILE)
&& manual_content_scan_get_task_config(&cfg[1], pl);
n = retro_atomic_load_acquire_int(&smb_stub_stats_main);
sprintf(what, "smb kind %s: one file's playlist and Overwrite", q);
sprintf(detail, "%s, %s, overwrite %d, %d stats",
ok ? path_basename(cfg[0].playlist_file) : "-",
ok2 ? path_basename(cfg[1].playlist_file) : "-",
(int)cfg[1].overwrite_playlist, n);
check( ok && ok2 && n == 0
&& cfg[0].content_is_file && cfg[1].content_is_file
&& !cfg[0].overwrite_playlist && !cfg[1].overwrite_playlist
&& !strcmp(cfg[0].playlist_file, cfg[1].playlist_file)
&& !strcmp(path_basename(cfg[1].playlist_file), "roms.lpl"),
what, detail);
sprintf(p, "%s/02_beta.bin", loc);
ok = manual_content_scan_set_menu_content_dir_kind(p,
MANUAL_CONTENT_SCAN_PATH_FOLDER)
&& manual_content_scan_get_task_config(&cfg[0], pl);
sprintf(what, "smb kind %s: a local file read as a folder", q);
check( ok && cfg[0].content_is_file
&& !strcmp(path_basename(cfg[0].playlist_file),
"smb_kind_local.lpl"), what,
ok ? path_basename(cfg[0].playlist_file) : "-");
/* With no database: one entry added to the folder's playlist,
* no settings saved, Overwrite and Validate not applied. */
*manual_content_scan_get_validate_entries_ptr() = true;
delete_playlists(pl);
if (!refresh_write(lpl, NULL, NULL, "smb://host/share/roms/00_old.bin"))
goto fixture;
n = smb_kind_scan(file, SMB_KIND_DBSCAN);
ok = !filestream_rename(lpl, ref);
if (!refresh_write(lpl, NULL, NULL, "smb://host/share/roms/00_old.bin"))
goto fixture;
n2 = smb_kind_scan(file, SMB_KIND_FILE);
sprintf(what, "smb kind %s: one file with no database", q);
sprintf(detail, "%d, %d stats, same %d, new %d, old %d", n, n2,
files_equal(ref, lpl), file_contains(lpl, "01_alpha.bin"),
file_contains(lpl, "00_old.bin"));
check( ok && n == 3 && n2 == 0 && files_equal(ref, lpl)
&& file_contains(lpl, "01_alpha.bin")
&& file_contains(lpl, "00_old.bin")
&& !scan_record(lpl, line, p, sizeof(p), &ran), what, detail);
filestream_delete(ref);
/* With the database: the single-file scan, not a folder's. */
manual_content_scan_reset_settings();
delete_playlists(pl);
smb_kind_scan(file, SMB_KIND_DBSCAN);
res = scan_result;
smb_kind_scan(file, SMB_KIND_FILE);
sprintf(what, "smb kind %s: one file with the database", q);
sprintf(detail, "added %u/%u, unmatched %u/%u, errors %u/%u, "
"failed %d/%d", res.added, scan_result.added, res.unmatched,
scan_result.unmatched, res.errors, scan_result.errors,
(int)res.failed, (int)scan_result.failed);
check( res.added == scan_result.added
&& res.unmatched == scan_result.unmatched
&& res.errors == scan_result.errors
&& res.failed == scan_result.failed, what, detail);
printf(" info smb kind %s: one file with the database: %s\n", q,
detail);
goto done;
fixture:
check(0, "fixture", "could not write the smb kind lane");
done:
smb_stub_live = false;
delete_playlists(pl);
manual_content_scan_reset_settings();
if (saved_pl)
strlcpy(config_get_ptr()->paths.directory_playlist, saved_pl,
sizeof(config_get_ptr()->paths.directory_playlist));
free(saved_pl);
free(pcfg);
free(cfg);
}
#endif
/* Refresh items over a playlist folder: a refresh-all takes every
@@ -11774,6 +12135,8 @@ int main(int argc, char **argv)
smb_reason_lane(root, true);
smb_refresh_lane(root, crc_a, sz_a, false);
smb_refresh_lane(root, crc_a, sz_a, true);
smb_kind_lane(root, false);
smb_kind_lane(root, true);
#endif
{
char reader_db[512];
+20 -3
View File
@@ -4218,15 +4218,31 @@ static bool manual_scan_end_flush_tick(
}
#ifdef HAVE_LIBRETRODB
bool task_push_dbscan(const char *fullpath, retro_task_callback_t cb,
static bool task_push_dbscan_kind(const char *fullpath,
enum manual_content_scan_path_kind kind, retro_task_callback_t cb,
void *user_data)
{
if (task_dbscan_chain_refuse())
return false;
manual_content_scan_set_menu_content_dir(fullpath);
manual_content_scan_set_menu_content_dir_kind(fullpath, kind);
return task_push_manual_content_scan(false, cb, user_data);
}
bool task_push_dbscan(const char *fullpath, retro_task_callback_t cb,
void *user_data)
{
return task_push_dbscan_kind(fullpath, MANUAL_CONTENT_SCAN_PATH_UNKNOWN,
cb, user_data);
}
bool task_push_dbscan_known(const char *fullpath, bool is_dir,
retro_task_callback_t cb, void *user_data)
{
return task_push_dbscan_kind(fullpath, is_dir
? MANUAL_CONTENT_SCAN_PATH_FOLDER : MANUAL_CONTENT_SCAN_PATH_FILE,
cb, user_data);
}
#endif
/* Frees task handle + all constituent objects */
@@ -5933,7 +5949,8 @@ static bool task_manual_content_scan_push(
if (manual_scan->task_config->db_selection == MANUAL_CONTENT_SCAN_SELECT_DB_AUTO_FIRST_MATCH)
manual_scan->flags |= DB_HANDLE_FLAG_USE_FIRST_MATCH_ONLY;
if (path_is_directory(manual_scan->task_config->content_dir))
if (manual_content_scan_content_stat(manual_scan->task_config->content_dir)
& RETRO_VFS_STAT_IS_DIRECTORY)
manual_scan->flags |= DB_HANDLE_FLAG_IS_DIRECTORY;
#endif
+5
View File
@@ -364,6 +364,11 @@ typedef struct
* settings. @cb gets @user_data. False while a chain runs. */
bool task_push_dbscan(const char *fullpath, retro_task_callback_t cb,
void *user_data);
/* As task_push_dbscan(), for a @fullpath known to be a folder
* (@is_dir) or a file. */
bool task_push_dbscan_known(const char *fullpath, bool is_dir,
retro_task_callback_t cb, void *user_data);
#endif
struct manual_content_scan_overrides;