aboutsummaryrefslogtreecommitdiffstats
path: root/src/update_walk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/update_walk.c')
-rw-r--r--src/update_walk.c253
1 files changed, 127 insertions, 126 deletions
diff --git a/src/update_walk.c b/src/update_walk.c
index 03fa78456..8b2df6ba7 100644
--- a/src/update_walk.c
+++ b/src/update_walk.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2003-2010 The Music Player Daemon Project
+ * Copyright (C) 2003-2011 The Music Player Daemon Project
* http://www.musicpd.org
*
* This program is free software; you can redistribute it and/or modify
@@ -20,6 +20,7 @@
#include "config.h" /* must be first for large file support */
#include "update_internal.h"
#include "database.h"
+#include "db_lock.h"
#include "exclude.h"
#include "directory.h"
#include "song.h"
@@ -89,26 +90,26 @@ directory_set_stat(struct directory *dir, const struct stat *st)
dir->have_stat = true;
}
+/**
+ * Caller must lock the #db_mutex.
+ */
static void
delete_song(struct directory *dir, struct song *del)
{
+ assert(del->parent == dir);
+
/* first, prevent traversers in main task from getting this */
- songvec_delete(&dir->songs, del);
+ directory_remove_song(dir, del);
+
+ db_unlock(); /* temporary unlock, because update_remove_song() blocks */
/* now take it out of the playlist (in the main_task) */
update_remove_song(del);
/* finally, all possible references gone, free it */
song_free(del);
-}
-static int
-delete_each_song(struct song *song, G_GNUC_UNUSED void *data)
-{
- struct directory *directory = data;
- assert(song->parent == directory);
- delete_song(directory, song);
- return 0;
+ db_lock();
}
static void
@@ -117,22 +118,27 @@ delete_directory(struct directory *directory);
/**
* Recursively remove all sub directories and songs from a directory,
* leaving an empty directory.
+ *
+ * Caller must lock the #db_mutex.
*/
static void
clear_directory(struct directory *directory)
{
- int i;
-
- for (i = directory->children.nr; --i >= 0;)
- delete_directory(directory->children.base[i]);
+ struct directory *child, *n;
+ directory_for_each_child_safe(child, n, directory)
+ delete_directory(child);
- assert(directory->children.nr == 0);
-
- songvec_for_each(&directory->songs, delete_each_song, directory);
+ struct song *song, *ns;
+ directory_for_each_song_safe(song, ns, directory) {
+ assert(song->parent == directory);
+ delete_song(directory, song);
+ }
}
/**
* Recursively free a directory and all its contents.
+ *
+ * Caller must lock the #db_mutex.
*/
static void
delete_directory(struct directory *directory)
@@ -141,57 +147,39 @@ delete_directory(struct directory *directory)
clear_directory(directory);
- dirvec_delete(&directory->parent->children, directory);
- directory_free(directory);
+ directory_delete(directory);
}
static void
delete_name_in(struct directory *parent, const char *name)
{
+ db_lock();
struct directory *directory = directory_get_child(parent, name);
- struct song *song = songvec_find(&parent->songs, name);
if (directory != NULL) {
delete_directory(directory);
modified = true;
}
+ struct song *song = directory_get_song(parent, name);
if (song != NULL) {
delete_song(parent, song);
modified = true;
}
- playlist_vector_remove(&parent->playlists, name);
-}
-
-/* passed to songvec_for_each */
-static int
-delete_song_if_excluded(struct song *song, void *_data)
-{
- GSList *exclude_list = _data;
- char *name_fs;
-
- assert(song->parent != NULL);
+ db_unlock();
- name_fs = utf8_to_fs_charset(song->uri);
- if (exclude_list_check(exclude_list, name_fs)) {
- delete_song(song->parent, song);
- modified = true;
- }
-
- g_free(name_fs);
- return 0;
+ playlist_vector_remove(&parent->playlists, name);
}
static void
remove_excluded_from_directory(struct directory *directory,
GSList *exclude_list)
{
- int i;
- struct dirvec *dv = &directory->children;
+ db_lock();
- for (i = dv->nr; --i >= 0; ) {
- struct directory *child = dv->base[i];
+ struct directory *child, *n;
+ directory_for_each_child_safe(child, n, directory) {
char *name_fs = utf8_to_fs_charset(directory_get_name(child));
if (exclude_list_check(exclude_list, name_fs)) {
@@ -202,26 +190,20 @@ remove_excluded_from_directory(struct directory *directory,
g_free(name_fs);
}
- songvec_for_each(&directory->songs,
- delete_song_if_excluded, exclude_list);
-}
+ struct song *song, *ns;
+ directory_for_each_song_safe(song, ns, directory) {
+ assert(song->parent == directory);
-/* passed to songvec_for_each */
-static int
-delete_song_if_removed(struct song *song, void *_data)
-{
- struct directory *dir = _data;
- char *path;
- struct stat st;
+ char *name_fs = utf8_to_fs_charset(song->uri);
+ if (exclude_list_check(exclude_list, name_fs)) {
+ delete_song(directory, song);
+ modified = true;
+ }
- if ((path = map_song_fs(song)) == NULL ||
- stat(path, &st) < 0 || !S_ISREG(st.st_mode)) {
- delete_song(dir, song);
- modified = true;
+ g_free(name_fs);
}
- g_free(path);
- return 0;
+ db_unlock();
}
static bool
@@ -265,19 +247,33 @@ directory_child_is_regular(const struct directory *directory,
static void
removeDeletedFromDirectory(struct directory *directory)
{
- int i;
- struct dirvec *dv = &directory->children;
-
- for (i = dv->nr; --i >= 0; ) {
- if (directory_exists(dv->base[i]))
+ struct directory *child, *n;
+ directory_for_each_child_safe(child, n, directory) {
+ if (directory_exists(child))
continue;
- g_debug("removing directory: %s", dv->base[i]->path);
- delete_directory(dv->base[i]);
+ db_lock();
+ delete_directory(child);
+ db_unlock();
+
modified = true;
}
- songvec_for_each(&directory->songs, delete_song_if_removed, directory);
+ struct song *song, *ns;
+ directory_for_each_song_safe(song, ns, directory) {
+ char *path;
+ struct stat st;
+ if ((path = map_song_fs(song)) == NULL ||
+ stat(path, &st) < 0 || !S_ISREG(st.st_mode)) {
+ db_lock();
+ delete_song(directory, song);
+ db_unlock();
+
+ modified = true;
+ }
+
+ g_free(path);
+ }
for (const struct playlist_metadata *pm = directory->playlists.head;
pm != NULL;) {
@@ -363,45 +359,21 @@ inodeFoundInParent(struct directory *parent, ino_t inode, dev_t device)
return 0;
}
-static struct directory *
-make_subdir(struct directory *parent, const char *name)
-{
- struct directory *directory;
-
- directory = directory_get_child(parent, name);
- if (directory == NULL) {
- char *path;
-
- if (directory_is_root(parent))
- path = NULL;
- else
- name = path = g_strconcat(directory_get_path(parent),
- "/", name, NULL);
-
- directory = directory_new_child(parent, name);
- g_free(path);
- }
-
- return directory;
-}
-
#ifdef ENABLE_ARCHIVE
static void
update_archive_tree(struct directory *directory, char *name)
{
struct directory *subdir;
- struct song *song;
char *tmp;
tmp = strchr(name, '/');
if (tmp) {
*tmp = 0;
//add dir is not there already
- if ((subdir = dirvec_find(&directory->children, name)) == NULL) {
- //create new directory
- subdir = make_subdir(directory, name);
- subdir->device = DEVICE_INARCHIVE;
- }
+ db_lock();
+ subdir = directory_make_child(directory, name);
+ subdir->device = DEVICE_INARCHIVE;
+ db_unlock();
//create directories first
update_archive_tree(subdir, tmp+1);
} else {
@@ -410,11 +382,13 @@ update_archive_tree(struct directory *directory, char *name)
return;
}
//add file
- song = songvec_find(&directory->songs, name);
+ db_lock();
+ struct song *song = directory_get_song(directory, name);
+ db_unlock();
if (song == NULL) {
song = song_file_load(name, directory);
if (song != NULL) {
- songvec_add(&directory->songs, song);
+ directory_add_song(directory, song);
modified = true;
g_message("added %s/%s",
directory_get_path(directory), name);
@@ -442,7 +416,9 @@ update_archive_file(struct directory *parent, const char *name,
struct directory *directory;
char *filepath;
- directory = dirvec_find(&parent->children, name);
+ db_lock();
+ directory = directory_get_child(parent, name);
+ db_unlock();
if (directory != NULL && directory->mtime == st->st_mtime &&
!walk_discard)
/* MPD has already scanned the archive, and it hasn't
@@ -465,10 +441,12 @@ update_archive_file(struct directory *parent, const char *name,
if (directory == NULL) {
g_debug("creating archive directory: %s", name);
- directory = make_subdir(parent, name);
+ db_lock();
+ directory = directory_new_child(parent, name);
/* mark this directory as archive (we use device for
this) */
directory->device = DEVICE_INARCHIVE;
+ db_unlock();
}
directory->mtime = st->st_mtime;
@@ -494,7 +472,9 @@ update_container_file( struct directory* directory,
char* vtrack = NULL;
unsigned int tnum = 0;
char* pathname = map_directory_child_fs(directory, name);
- struct directory* contdir = dirvec_find(&directory->children, name);
+
+ db_lock();
+ struct directory *contdir = directory_get_child(directory, name);
// directory exists already
if (contdir != NULL)
@@ -510,14 +490,16 @@ update_container_file( struct directory* directory,
modified = true;
}
else {
+ db_unlock();
g_free(pathname);
return true;
}
}
- contdir = make_subdir(directory, name);
+ contdir = directory_make_child(directory, name);
contdir->mtime = st->st_mtime;
contdir->device = DEVICE_CONTAINER;
+ db_unlock();
while ((vtrack = plugin->container_scan(pathname, ++tnum)) != NULL)
{
@@ -532,7 +514,7 @@ update_container_file( struct directory* directory,
song->tag = plugin->tag_dup(child_path_fs);
g_free(child_path_fs);
- songvec_add(&contdir->songs, song);
+ directory_add_song(contdir, song);
modified = true;
@@ -545,7 +527,9 @@ update_container_file( struct directory* directory,
if (tnum == 1)
{
+ db_lock();
delete_directory(contdir);
+ db_unlock();
return false;
}
else
@@ -592,13 +576,19 @@ update_regular_file(struct directory *directory,
if ((plugin = decoder_plugin_from_suffix(suffix, false)) != NULL)
{
- struct song* song = songvec_find(&directory->songs, name);
+ db_lock();
+ struct song *song = directory_get_song(directory, name);
+ db_unlock();
if (!directory_child_access(directory, name, R_OK)) {
g_warning("no read permissions on %s/%s",
directory_get_path(directory), name);
- if (song != NULL)
+ if (song != NULL) {
+ db_lock();
delete_song(directory, song);
+ db_unlock();
+ }
+
return;
}
@@ -608,8 +598,11 @@ update_regular_file(struct directory *directory,
{
if (update_container_file(directory, name, st, plugin))
{
- if (song != NULL)
+ if (song != NULL) {
+ db_lock();
delete_song(directory, song);
+ db_unlock();
+ }
return;
}
@@ -625,7 +618,7 @@ update_regular_file(struct directory *directory,
return;
}
- songvec_add(&directory->songs, song);
+ directory_add_song(directory, song);
modified = true;
g_message("added %s/%s",
directory_get_path(directory), name);
@@ -635,7 +628,9 @@ update_regular_file(struct directory *directory,
if (!song_file_update(song)) {
g_debug("deleting unrecognized file %s/%s",
directory_get_path(directory), name);
+ db_lock();
delete_song(directory, song);
+ db_unlock();
}
modified = true;
@@ -670,12 +665,18 @@ updateInDirectory(struct directory *directory,
if (inodeFoundInParent(directory, st->st_ino, st->st_dev))
return;
- subdir = make_subdir(directory, name);
+ db_lock();
+ subdir = directory_make_child(directory, name);
+ db_unlock();
+
assert(directory == subdir->parent);
ret = updateDirectory(subdir, st);
- if (!ret)
+ if (!ret) {
+ db_lock();
delete_directory(subdir);
+ db_unlock();
+ }
} else {
g_debug("update: %s is not a directory, archive or music", name);
}
@@ -829,34 +830,31 @@ updateDirectory(struct directory *directory, const struct stat *st)
}
static struct directory *
-directory_make_child_checked(struct directory *parent, const char *path)
+directory_make_child_checked(struct directory *parent, const char *name_utf8)
{
struct directory *directory;
- char *base;
struct stat st;
- struct song *conflicting;
- directory = directory_get_child(parent, path);
+ db_lock();
+ directory = directory_get_child(parent, name_utf8);
+ db_unlock();
if (directory != NULL)
return directory;
- base = g_path_get_basename(path);
-
- if (stat_directory_child(parent, base, &st) < 0 ||
- inodeFoundInParent(parent, st.st_ino, st.st_dev)) {
- g_free(base);
+ if (stat_directory_child(parent, name_utf8, &st) < 0 ||
+ inodeFoundInParent(parent, st.st_ino, st.st_dev))
return NULL;
- }
/* if we're adding directory paths, make sure to delete filenames
with potentially the same name */
- conflicting = songvec_find(&parent->songs, base);
+ db_lock();
+ struct song *conflicting = directory_get_song(parent, name_utf8);
if (conflicting)
delete_song(parent, conflicting);
- g_free(base);
+ directory = directory_new_child(parent, name_utf8);
+ db_unlock();
- directory = directory_new_child(parent, path);
directory_set_stat(directory, &st);
return directory;
}
@@ -866,17 +864,20 @@ addParentPathToDB(const char *utf8path)
{
struct directory *directory = db_get_root();
char *duplicated = g_strdup(utf8path);
- char *slash = duplicated;
+ char *name_utf8 = duplicated, *slash;
- while ((slash = strchr(slash, '/')) != NULL) {
+ while ((slash = strchr(name_utf8, '/')) != NULL) {
*slash = 0;
+ if (*name_utf8 == 0)
+ continue;
+
directory = directory_make_child_checked(directory,
- duplicated);
- if (directory == NULL || slash == NULL)
+ name_utf8);
+ if (directory == NULL)
break;
- *slash++ = '/';
+ name_utf8 = slash + 1;
}
g_free(duplicated);