summaryrefslogtreecommitdiffstats
path: root/apps/playlist.c
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2023-01-16 18:06:26 +0000
committerAidan MacDonald <amachronic@protonmail.com>2023-01-22 13:47:50 +0000
commitc307d98e3fbe5e867cd6e6e3c61f4937021c6b4c (patch)
tree3ae7d5b34a6c6f72a2dd50737dfe278db934446a /apps/playlist.c
parent32f365bf3cdcaadb22087041bfc44574737b557d (diff)
downloadrockbox-c307d98e3f.tar.gz
rockbox-c307d98e3f.zip
playlist: pin dircache fileref buffer during background scanning
dircache_search() can yield, which would lead to memory corruption if the playlist dcfrefs buffer is moved at that point. Prevent this from happening by storing the buflib handle and pinning the buffer while scanning the dircache. Change-Id: I28b122de283953dd6d54c1d00598759f5bdcbe93
Diffstat (limited to 'apps/playlist.c')
-rw-r--r--apps/playlist.c136
1 files changed, 67 insertions, 69 deletions
diff --git a/apps/playlist.c b/apps/playlist.c
index b56959fefc..7bdcafa69a 100644
--- a/apps/playlist.c
+++ b/apps/playlist.c
@@ -170,6 +170,25 @@ struct directory_search_context {
static struct playlist_info current_playlist;
+static void dc_init_filerefs(struct playlist_info *playlist,
+ int start, int count)
+{
+#ifdef HAVE_DIRCACHE
+ if (!playlist->dcfrefs_handle)
+ return;
+
+ struct dircache_fileref *dcfrefs = core_get_data(playlist->dcfrefs_handle);
+ int end = start + count;
+
+ for (int i = start; i < end; i++)
+ dircache_fileref_init(&dcfrefs[i]);
+#else
+ (void)playlist;
+ (void)start;
+ (void)count;
+#endif
+}
+
#ifdef HAVE_DIRCACHE
#define PLAYLIST_LOAD_POINTERS 1
#define PLAYLIST_CLEAN_POINTERS 2
@@ -179,9 +198,6 @@ static struct event_queue playlist_queue SHAREDBSS_ATTR;
static struct queue_sender_list playlist_queue_sender_list SHAREDBSS_ATTR;
static long playlist_stack[(DEFAULT_STACK_SIZE + 0x800)/sizeof(long)];
static const char dc_thread_playlist_name[] = "playlist cachectrl";
-
-static void dc_copy_filerefs(struct dircache_fileref*,
- const struct dircache_fileref*, int);
#endif
#if defined(PLAYLIST_DEBUG_MUTEX)
@@ -882,9 +898,7 @@ static int add_indices_to_playlist(struct playlist_info* playlist,
/* Store a new entry */
playlist->indices[ playlist->amount ] = i+count;
- #ifdef HAVE_DIRCACHE
- dc_copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
- #endif
+ dc_init_filerefs(playlist, playlist->amount, 1);
playlist->amount++;
}
}
@@ -1191,13 +1205,14 @@ static int get_track_filename(struct playlist_info* playlist, int index, int see
buf_length = MAX_PATH+1;
#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
+ if (playlist->dcfrefs_handle)
{
- max = dircache_get_fileref_path(&playlist->dcfrefs[index],
+ struct dircache_fileref *dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle);
+ max = dircache_get_fileref_path(&dcfrefs[index],
tmp_buf, sizeof(tmp_buf));
- NOTEF("%s [in DCache]: 0x%x %s", __func__,
- playlist->dcfrefs[index], tmp_buf);
+ NOTEF("%s [in DCache]: 0x%x %s", __func__, dcfrefs[index], tmp_buf);
+ core_put_data_pinned(dcfrefs);
}
#endif /* HAVE_DIRCACHE */
@@ -1425,14 +1440,20 @@ static int add_track_to_playlist_unlocked(struct playlist_info* playlist,
if (queue)
flags |= PLAYLIST_QUEUED;
+#ifdef HAVE_DIRCACHE
+ struct dircache_fileref *dcfrefs = NULL;
+ if (playlist->dcfrefs_handle)
+ dcfrefs = core_get_data(playlist->dcfrefs_handle);
+#else
+ int *dcfrefs = NULL;
+#endif
+
/* shift indices so that track can be added */
for (i=playlist->amount; i>insert_position; i--)
{
playlist->indices[i] = playlist->indices[i-1];
-#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
- playlist->dcfrefs[i] = playlist->dcfrefs[i-1];
-#endif
+ if (dcfrefs)
+ dcfrefs[i] = dcfrefs[i-1];
}
/* update stored indices if needed */
@@ -1463,10 +1484,7 @@ static int add_track_to_playlist_unlocked(struct playlist_info* playlist,
}
playlist->indices[insert_position] = flags | seek_pos;
-
-#ifdef HAVE_DIRCACHE
- dc_copy_filerefs(&playlist->dcfrefs[insert_position], NULL, 1);
-#endif
+ dc_init_filerefs(playlist, insert_position, 1);
playlist->amount++;
playlist->num_inserted_tracks++;
@@ -1534,14 +1552,20 @@ static int remove_track_from_playlist(struct playlist_info* playlist,
inserted = playlist->indices[position] & PLAYLIST_INSERT_TYPE_MASK;
+#ifdef HAVE_DIRCACHE
+ struct dircache_fileref *dcfrefs = NULL;
+ if (playlist->dcfrefs_handle)
+ dcfrefs = core_get_data(playlist->dcfrefs_handle);
+#else
+ int *dcfrefs = NULL;
+#endif
+
/* shift indices now that track has been removed */
for (i=position; i<playlist->amount; i++)
{
playlist->indices[i] = playlist->indices[i+1];
-#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
- playlist->dcfrefs[i] = playlist->dcfrefs[i+1];
-#endif
+ if (dcfrefs)
+ dcfrefs[i] = dcfrefs[i+1];
}
playlist->amount--;
@@ -1630,11 +1654,12 @@ static int randomise_playlist(struct playlist_info* playlist,
playlist->indices[candidate] = playlist->indices[count];
playlist->indices[count] = indextmp;
#ifdef HAVE_DIRCACHE
- if (playlist->dcfrefs)
+ if (playlist->dcfrefs_handle)
{
- struct dircache_fileref dcftmp = playlist->dcfrefs[candidate];
- playlist->dcfrefs[candidate] = playlist->dcfrefs[count];
- playlist->dcfrefs[count] = dcftmp;
+ struct dircache_fileref *dcfrefs = core_get_data(playlist->dcfrefs_handle);
+ struct dircache_fileref dcftmp = dcfrefs[candidate];
+ dcfrefs[candidate] = dcfrefs[count];
+ dcfrefs[count] = dcftmp;
}
#endif
}
@@ -1707,7 +1732,7 @@ static int sort_playlist_unlocked(struct playlist_info* playlist,
/** We need to re-check the song names from disk because qsort can't
* sort two arrays at once :/
* FIXME: Please implement a better way to do this. */
- dc_copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
+ dc_init_filerefs(playlist, 0, playlist->max_playlist_size);
dc_load_playlist_pointers();
#endif
@@ -1859,24 +1884,6 @@ static int get_next_index(const struct playlist_info* playlist, int steps,
}
#ifdef HAVE_DIRCACHE
-
-static void dc_copy_filerefs(struct dircache_fileref *dcfto,
- const struct dircache_fileref *dcffrom,
- int count)
-{
- if (!dcfto)
- return;
-
- if (dcffrom)
- memmove(dcfto, dcffrom, count * sizeof (*dcfto));
- else
- {
- /* just initialize the destination */
- for (int i = 0; i < count; i++, dcfto++)
- dircache_fileref_init(dcfto);
- }
-}
-
static void dc_flush_playlist_callback(void)
{
struct playlist_info *playlist;
@@ -1901,6 +1908,7 @@ static void dc_thread_playlist(void)
static char tmp[MAX_PATH+1];
struct playlist_info *playlist;
+ struct dircache_fileref *dcfrefs;
int index;
int seek;
bool control_file;
@@ -1939,7 +1947,7 @@ static void dc_thread_playlist(void)
register_storage_idle_func(dc_flush_playlist_callback);
}
- if (!playlist->dcfrefs || playlist->amount <= 0)
+ if (!playlist->dcfrefs_handle || playlist->amount <= 0)
break ;
/* Check if previously loaded pointers are intact. */
@@ -1953,12 +1961,13 @@ static void dc_thread_playlist(void)
break ;
trigger_cpu_boost();
+ dcfrefs = core_get_data_pinned(playlist->dcfrefs_handle);
for (index = 0; index < playlist->amount
&& queue_empty(&playlist_queue); index++)
{
/* Process only pointers that are superficially stale. */
- if (dircache_search(DCS_FILEREF, &playlist->dcfrefs[index], NULL) > 0)
+ if (dircache_search(DCS_FILEREF, &dcfrefs[index], NULL) > 0)
continue ;
control_file = playlist->indices[index] & PLAYLIST_INSERT_TYPE_MASK;
@@ -1973,12 +1982,13 @@ static void dc_thread_playlist(void)
/* Obtain the dircache file entry cookie. */
dircache_search(DCS_CACHED_PATH | DCS_UPDATE_FILEREF,
- &playlist->dcfrefs[index], tmp);
+ &dcfrefs[index], tmp);
/* And be on background so user doesn't notice any delays. */
yield();
}
+ core_put_data_pinned(dcfrefs);
cancel_cpu_boost();
if (index == playlist->amount)
@@ -2042,10 +2052,6 @@ static int move_callback(int handle, void* current, void* new)
struct playlist_info* playlist = &current_playlist;
if (current == playlist->indices)
playlist->indices = new;
-#ifdef HAVE_DIRCACHE
- else if (current == playlist->dcfrefs)
- playlist->dcfrefs = new;
-#endif /* HAVE_DIRCACHE */
return BUFLIB_CB_OK;
}
@@ -2083,10 +2089,10 @@ void playlist_init(void)
initalize_new_playlist(playlist, true);
#ifdef HAVE_DIRCACHE
- handle = core_alloc_ex(
- playlist->max_playlist_size * sizeof(*playlist->dcfrefs), &ops);
- playlist->dcfrefs = core_get_data(handle);
- dc_copy_filerefs(playlist->dcfrefs, NULL, playlist->max_playlist_size);
+ playlist->dcfrefs_handle = core_alloc(
+ playlist->max_playlist_size * sizeof(struct dircache_fileref));
+ dc_init_filerefs(playlist, 0, playlist->max_playlist_size);
+
unsigned int playlist_thread_id =
create_thread(dc_thread_playlist, playlist_stack, sizeof(playlist_stack),
0, dc_thread_playlist_name IF_PRIO(, PRIORITY_BACKGROUND)
@@ -2149,10 +2155,7 @@ int playlist_add(const char *filename)
chunk_put_data(&playlist->name_chunk_buffer, namebuf, indice);
playlist->indices[playlist->amount] = indice;
-
-#ifdef HAVE_DIRCACHE
- dc_copy_filerefs(&playlist->dcfrefs[playlist->amount], NULL, 1);
-#endif
+ dc_init_filerefs(playlist, playlist->amount, 1);
playlist->amount++;
@@ -2217,15 +2220,16 @@ int playlist_create_ex(struct playlist_info* playlist,
playlist->max_playlist_size = num_indices;
playlist->indices = index_buffer;
#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = (void *)&playlist->indices[num_indices];
+ playlist->dcfrefs_handle = 0;
#endif
}
else
{
+ /* FIXME not sure if it's safe to share index buffers */
playlist->max_playlist_size = current_playlist.max_playlist_size;
playlist->indices = current_playlist.indices;
#ifdef HAVE_DIRCACHE
- playlist->dcfrefs = current_playlist.dcfrefs;
+ playlist->dcfrefs_handle = 0;
#endif
}
@@ -2524,9 +2528,6 @@ size_t playlist_get_required_bufsz(struct playlist_info* playlist,
playlist = &current_playlist;
size_t unit_size = sizeof (*playlist->indices);
- #ifdef HAVE_DIRCACHE
- unit_size += sizeof (*playlist->dcfrefs);
- #endif
if (include_namebuf)
namebuf = AVERAGE_FILENAME_LENGTH * global_settings.max_files_in_dir;
@@ -3909,10 +3910,7 @@ int playlist_set_current(struct playlist_info* playlist)
{
memcpy((void*)current_playlist.indices, (void*)playlist->indices,
playlist->max_playlist_size*sizeof(*playlist->indices));
-#ifdef HAVE_DIRCACHE
- dc_copy_filerefs(current_playlist.dcfrefs, playlist->dcfrefs,
- playlist->max_playlist_size);
-#endif
+ dc_init_filerefs(&current_playlist, 0, current_playlist.max_playlist_size);
}
current_playlist.first_index = playlist->first_index;