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.c500
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);
}