aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2008-10-09 15:41:02 +0200
committerMax Kellermann <max@duempel.org>2008-10-09 15:41:02 +0200
commit2bb7bcc7b6a12abd47c9cf56255045f5e78dba84 (patch)
treec60b826e2989177e604de883753d71c3eca4fb41
parent96c681e2db9f8f707f80061360c9b6b680fe72e3 (diff)
downloadmpd-2bb7bcc7b6a12abd47c9cf56255045f5e78dba84.tar.gz
mpd-2bb7bcc7b6a12abd47c9cf56255045f5e78dba84.tar.xz
mpd-2bb7bcc7b6a12abd47c9cf56255045f5e78dba84.zip
update: locked delete after update error
When a directory failed to update, it was removed from the database, without freeing all children and songs (memory leak), and without locking (race condition). Introduce the functions clear_directory() and delete_directory(), which do both.
-rw-r--r--src/update.c41
1 files changed, 40 insertions, 1 deletions
diff --git a/src/update.c b/src/update.c
index 55740bfc4..36f866325 100644
--- a/src/update.c
+++ b/src/update.c
@@ -81,6 +81,45 @@ delete_song(struct directory *dir, struct song *del)
song_free(del);
}
+static int
+delete_each_song(struct song *song, mpd_unused void *data)
+{
+ struct directory *directory = data;
+ assert(song->parent == directory);
+ delete_song(directory, song);
+ return 0;
+}
+
+/**
+ * Recursively remove all sub directories and songs from a directory,
+ * leaving an empty directory.
+ */
+static void
+clear_directory(struct directory *directory)
+{
+ int i;
+
+ for (i = directory->children.nr; --i >= 0;)
+ clear_directory(directory->children.base[i]);
+ dirvec_clear(&directory->children);
+
+ songvec_for_each(&directory->songs, delete_each_song, directory);
+}
+
+/**
+ * Recursively free a directory and all its contents.
+ */
+static void
+delete_directory(struct directory *directory)
+{
+ assert(directory->parent != NULL);
+
+ clear_directory(directory);
+
+ dirvec_delete(&directory->parent->children, directory);
+ directory_free(directory);
+}
+
struct delete_data {
char *tmp;
struct directory *dir;
@@ -374,7 +413,7 @@ static enum update_return updatePath(const char *utf8path)
/* if updateDirectory fails, means we should delete it */
else {
LOG("removing directory: %s\n", path);
- dirvec_delete(&parentDirectory->children, directory);
+ delete_directory(directory);
ret = UPDATE_RETURN_UPDATED;
/* don't return, path maybe a song now */
}