diff options
Diffstat (limited to 'src/update_walk.c')
-rw-r--r-- | src/update_walk.c | 500 |
1 files changed, 166 insertions, 334 deletions
diff --git a/src/update_walk.c b/src/update_walk.c index 5d2f778ff..9ca9115bd 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 @@ -19,10 +19,14 @@ #include "config.h" /* must be first for large file support */ #include "update_internal.h" +#include "update_io.h" +#include "update_db.h" #include "database.h" +#include "db_lock.h" #include "exclude.h" #include "directory.h" #include "song.h" +#include "playlist_vector.h" #include "uri.h" #include "mapper.h" #include "path.h" @@ -30,6 +34,8 @@ #include "decoder_plugin.h" #include "playlist_list.h" #include "conf.h" +#include "tag.h" +#include "tag_handler.h" #ifdef ENABLE_ARCHIVE #include "archive_list.h" @@ -90,108 +96,13 @@ directory_set_stat(struct directory *dir, const struct stat *st) } static void -delete_song(struct directory *dir, struct song *del) -{ - /* first, prevent traversers in main task from getting this */ - songvec_delete(&dir->songs, del); - - /* 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; -} - -static void -delete_directory(struct directory *directory); - -/** - * 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;) - delete_directory(directory->children.base[i]); - - assert(directory->children.nr == 0); - - 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); -} - -static void -delete_name_in(struct directory *parent, const char *name) -{ - struct directory *directory = directory_get_child(parent, name); - struct song *song = songvec_find(&parent->songs, name); - - if (directory != NULL) { - delete_directory(directory); - modified = true; - } - - 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); - - 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; -} - -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,128 +113,61 @@ 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; -} - -static bool -directory_exists(const struct directory *directory) -{ - char *path_fs; - GFileTest test; - bool exists; - - path_fs = map_directory_fs(directory); - if (path_fs == NULL) - /* invalid path: cannot exist */ - return false; - - test = directory->device == DEVICE_INARCHIVE || - directory->device == DEVICE_CONTAINER - ? G_FILE_TEST_IS_REGULAR - : G_FILE_TEST_IS_DIR; - - exists = g_file_test(path_fs, test); - g_free(path_fs); - - return exists; -} - -static bool -directory_child_is_regular(const struct directory *directory, - const char *name_utf8) -{ - char *path_fs = map_directory_child_fs(directory, name_utf8); - if (path_fs == NULL) - return false; - - struct stat st; - bool is_regular = stat(path_fs, &st) == 0 && S_ISREG(st.st_mode); - g_free(path_fs); - - return is_regular; + db_unlock(); } 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); - - for (const struct playlist_metadata *pm = directory->playlists.head; - pm != NULL;) { - const struct playlist_metadata *next = pm->next; + 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(); - if (!directory_child_is_regular(directory, pm->name)) - playlist_vector_remove(&directory->playlists, pm->name); + modified = true; + } - pm = next; + g_free(path); } -} - -static int -stat_directory(const struct directory *directory, struct stat *st) -{ - char *path_fs; - int ret; - path_fs = map_directory_fs(directory); - if (path_fs == NULL) - return -1; - ret = stat(path_fs, st); - if (ret < 0) - g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno)); - - g_free(path_fs); - return ret; -} - -static int -stat_directory_child(const struct directory *parent, const char *name, - struct stat *st) -{ - char *path_fs; - int ret; - - path_fs = map_directory_child_fs(parent, name); - if (path_fs == NULL) - return -1; - - ret = stat(path_fs, st); - if (ret < 0) - g_warning("Failed to stat %s: %s", path_fs, g_strerror(errno)); - - g_free(path_fs); - return ret; + struct playlist_metadata *pm, *np; + directory_for_each_playlist_safe(pm, np, directory) { + if (!directory_child_is_regular(directory, pm->name)) { + db_lock(); + playlist_vector_remove(&directory->playlists, pm->name); + db_unlock(); + } + } } #ifndef G_OS_WIN32 @@ -363,45 +207,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 +230,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 +264,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 +289,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 +320,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 +338,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) { @@ -529,10 +359,12 @@ update_container_file( struct directory* directory, child_path_fs = map_directory_child_fs(contdir, vtrack); - song->tag = plugin->tag_dup(child_path_fs); + song->tag = tag_new(); + decoder_plugin_scan_file(plugin, child_path_fs, + &add_tag_handler, song->tag); g_free(child_path_fs); - songvec_add(&contdir->songs, song); + directory_add_song(contdir, song); modified = true; @@ -545,37 +377,76 @@ update_container_file( struct directory* directory, if (tnum == 1) { + db_lock(); delete_directory(contdir); + db_unlock(); return false; } else return true; } -/** - * Checks if the given permissions on the mapped file are given. - */ -static bool -directory_child_access(const struct directory *directory, - const char *name, int mode) -{ -#ifdef WIN32 - /* access() is useless on WIN32 */ - (void)directory; - (void)name; - (void)mode; - return true; -#else - char *path = map_directory_child_fs(directory, name); - if (path == NULL) - /* something went wrong, but that isn't a permission - problem */ - return true; +static void +update_song_file(struct directory *directory, + const char *name, const struct stat *st, + const struct decoder_plugin *plugin) +{ + 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) { + db_lock(); + delete_song(directory, song); + db_unlock(); + } - bool success = access(path, mode) == 0 || errno != EACCES; - g_free(path); - return success; -#endif + return; + } + + if (!(song != NULL && st->st_mtime == song->mtime && + !walk_discard) && + plugin->container_scan != NULL && + update_container_file(directory, name, st, plugin)) { + if (song != NULL) { + db_lock(); + delete_song(directory, song); + db_unlock(); + } + + return; + } + + if (song == NULL) { + g_debug("reading %s/%s", + directory_get_path(directory), name); + song = song_file_load(name, directory); + if (song == NULL) { + g_debug("ignoring unrecognized file %s/%s", + directory_get_path(directory), name); + return; + } + + directory_add_song(directory, song); + modified = true; + g_message("added %s/%s", + directory_get_path(directory), name); + } else if (st->st_mtime != song->mtime || walk_discard) { + g_message("updating %s/%s", + directory_get_path(directory), name); + 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; + } } static void @@ -592,63 +463,18 @@ update_regular_file(struct directory *directory, if ((plugin = decoder_plugin_from_suffix(suffix, false)) != NULL) { - struct song* song = songvec_find(&directory->songs, name); - - if (!directory_child_access(directory, name, R_OK)) { - g_warning("no read permissions on %s/%s", - directory_get_path(directory), name); - if (song != NULL) - delete_song(directory, song); - return; - } - - if (!(song != NULL && st->st_mtime == song->mtime && - !walk_discard) && - plugin->container_scan != NULL) - { - if (update_container_file(directory, name, st, plugin)) - { - if (song != NULL) - delete_song(directory, song); - - return; - } - } - - if (song == NULL) { - g_debug("reading %s/%s", - directory_get_path(directory), name); - song = song_file_load(name, directory); - if (song == NULL) { - g_debug("ignoring unrecognized file %s/%s", - directory_get_path(directory), name); - return; - } - - songvec_add(&directory->songs, song); - modified = true; - g_message("added %s/%s", - directory_get_path(directory), name); - } else if (st->st_mtime != song->mtime || walk_discard) { - g_message("updating %s/%s", - directory_get_path(directory), name); - if (!song_file_update(song)) { - g_debug("deleting unrecognized file %s/%s", - directory_get_path(directory), name); - delete_song(directory, song); - } - - modified = true; - } + update_song_file(directory, name, st, plugin); #ifdef ENABLE_ARCHIVE } else if ((archive = archive_plugin_from_suffix(suffix))) { update_archive_file(directory, name, st, archive); #endif } else if (playlist_suffix_supported(suffix)) { + db_lock(); if (playlist_vector_update_or_add(&directory->playlists, name, st->st_mtime)) modified = true; + db_unlock(); } } @@ -670,12 +496,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); } @@ -806,7 +638,7 @@ updateDirectory(struct directory *directory, const struct stat *st) continue; if (skip_symlink(directory, utf8)) { - delete_name_in(directory, utf8); + modified |= delete_name_in(directory, utf8); g_free(utf8); continue; } @@ -814,7 +646,7 @@ updateDirectory(struct directory *directory, const struct stat *st) if (stat_directory_child(directory, utf8, &st2) == 0) updateInDirectory(directory, utf8, &st2); else - delete_name_in(directory, utf8); + modified |= delete_name_in(directory, utf8); g_free(utf8); } @@ -829,37 +661,34 @@ 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 (skip_symlink(parent, path)) + if (skip_symlink(parent, name_utf8)) 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; } @@ -869,17 +698,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); @@ -903,7 +735,7 @@ updatePath(const char *path) stat_directory_child(parent, name, &st) == 0) updateInDirectory(parent, name, &st); else - delete_name_in(parent, name); + modified |= delete_name_in(parent, name); g_free(name); } |