aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2008-10-13 21:52:29 -0700
committerEric Wong <normalperson@yhbt.net>2008-10-13 21:52:29 -0700
commite5c604feac962f654ba2cb7739ef33b9242cb072 (patch)
tree29ee04894067cef8b6d1a040e2996306b9bde3eb
parent89e5feacfe1d74583b1c6cbf218841368b1a20bd (diff)
downloadmpd-e5c604feac962f654ba2cb7739ef33b9242cb072.tar.gz
mpd-e5c604feac962f654ba2cb7739ef33b9242cb072.tar.xz
mpd-e5c604feac962f654ba2cb7739ef33b9242cb072.zip
directory: fix directory_free() braindamage
Recursively calling directory_walk leads to double-free()s causing nasty assertion errors and segfaults when directories are deleted or at exit. This is now Valgrind clean.
-rw-r--r--src/directory.c32
1 files changed, 19 insertions, 13 deletions
diff --git a/src/directory.c b/src/directory.c
index 40be57855..fe9e83405 100644
--- a/src/directory.c
+++ b/src/directory.c
@@ -44,27 +44,29 @@ struct directory * directory_new(const char *path, struct directory * parent)
static int free_each_song(struct mpd_song *song, mpd_unused void *arg)
{
+ songvec_delete(&song->parent->songs, song);
song_free(song);
return 0;
}
-static int free_each_dir(struct directory *dir, void *arg)
+static int free_each_dir(struct directory *dir, mpd_unused void *arg)
{
- if (arg != dir)
- directory_free(dir);
+ if (dir != &music_root) {
+ assert(dir->parent);
+ dirvec_delete(&dir->parent->children, dir);
+ }
+ assert(!dir->songs.nr);
+ assert(!dir->songs.base);
+ assert(!dir->children.nr);
+ assert(!dir->children.base);
+ if (dir != &music_root)
+ free(dir);
return 0;
}
void directory_free(struct directory *dir)
{
- directory_walk(dir, free_each_song, free_each_dir, dir);
- dirvec_destroy(&dir->children);
- songvec_destroy(&dir->songs);
- if (dir != &music_root) {
- assert(dir->parent);
- dirvec_delete(&dir->parent->children, dir);
- free(dir);
- }
+ directory_walk(dir, free_each_song, free_each_dir, NULL);
}
static int dir_pruner(struct directory *dir, void *_dv)
@@ -133,8 +135,9 @@ int directory_walk(struct directory *dir,
{
int err = 0;
- if (forEachDir && (err = forEachDir(dir, data)) < 0)
- return err;
+ if (forEachDir && forEachDir != free_each_dir)
+ if ((err = forEachDir(dir, data)) < 0)
+ return err;
if (forEachSong) {
err = songvec_for_each(&dir->songs, forEachSong, data);
@@ -150,5 +153,8 @@ int directory_walk(struct directory *dir,
dw_arg.data = data;
err = dirvec_for_each(&dir->children, dirwalk_x, &dw_arg);
}
+ if (forEachDir == free_each_dir)
+ if ((err = forEachDir(dir, data)) < 0)
+ return err;
return err;
}