diff options
author | Eric Wong <normalperson@yhbt.net> | 2008-10-13 21:52:29 -0700 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2008-10-13 21:52:29 -0700 |
commit | e5c604feac962f654ba2cb7739ef33b9242cb072 (patch) | |
tree | 29ee04894067cef8b6d1a040e2996306b9bde3eb | |
parent | 89e5feacfe1d74583b1c6cbf218841368b1a20bd (diff) | |
download | mpd-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.c | 32 |
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; } |