doc: Remove superfluous comment already described in footnotes.
[mpd-mk.git] / src / update.c
blob6778a5989d13cc5d87615f42874d98a1cc5781cf
1 /*
2 * Copyright (C) 2003-2009 The Music Player Daemon Project
3 * http://www.musicpd.org
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "update.h"
21 #include "database.h"
22 #include "directory.h"
23 #include "song.h"
24 #include "uri.h"
25 #include "mapper.h"
26 #include "path.h"
27 #include "decoder_list.h"
28 #include "archive_list.h"
29 #include "playlist.h"
30 #include "event_pipe.h"
31 #include "notify.h"
32 #include "update.h"
33 #include "idle.h"
34 #include "conf.h"
35 #include "stats.h"
36 #include "main.h"
37 #include "config.h"
39 #ifdef ENABLE_SQLITE
40 #include "sticker.h"
41 #include "song_sticker.h"
42 #endif
44 #include <glib.h>
46 #include <assert.h>
47 #include <sys/types.h>
48 #include <sys/stat.h>
49 #include <unistd.h>
50 #include <dirent.h>
51 #include <string.h>
52 #include <stdlib.h>
53 #include <errno.h>
55 #include "decoder_plugin.h"
57 #undef G_LOG_DOMAIN
58 #define G_LOG_DOMAIN "update"
60 static enum update_progress {
61 UPDATE_PROGRESS_IDLE = 0,
62 UPDATE_PROGRESS_RUNNING = 1,
63 UPDATE_PROGRESS_DONE = 2
64 } progress;
66 static bool modified;
68 /* make this dynamic?, or maybe this is big enough... */
69 static char *update_paths[32];
70 static size_t update_paths_nr;
72 static GThread *update_thr;
74 static const unsigned update_task_id_max = 1 << 15;
76 static unsigned update_task_id;
78 static struct song *delete;
80 /** used by the main thread to notify the update thread */
81 static struct notify update_notify;
83 #ifndef WIN32
85 enum {
86 DEFAULT_FOLLOW_INSIDE_SYMLINKS = true,
87 DEFAULT_FOLLOW_OUTSIDE_SYMLINKS = true,
90 static bool follow_inside_symlinks;
91 static bool follow_outside_symlinks;
93 #endif
95 unsigned
96 isUpdatingDB(void)
98 return (progress != UPDATE_PROGRESS_IDLE) ? update_task_id : 0;
101 static void
102 directory_set_stat(struct directory *dir, const struct stat *st)
104 dir->inode = st->st_ino;
105 dir->device = st->st_dev;
106 dir->stat = 1;
109 static void
110 delete_song(struct directory *dir, struct song *del)
112 /* first, prevent traversers in main task from getting this */
113 songvec_delete(&dir->songs, del);
115 /* now take it out of the playlist (in the main_task) */
116 assert(!delete);
117 delete = del;
118 event_pipe_emit(PIPE_EVENT_DELETE);
120 do {
121 notify_wait(&update_notify);
122 } while (delete != NULL);
124 /* finally, all possible references gone, free it */
125 song_free(del);
128 static int
129 delete_each_song(struct song *song, G_GNUC_UNUSED void *data)
131 struct directory *directory = data;
132 assert(song->parent == directory);
133 delete_song(directory, song);
134 return 0;
137 static void
138 delete_directory(struct directory *directory);
141 * Recursively remove all sub directories and songs from a directory,
142 * leaving an empty directory.
144 static void
145 clear_directory(struct directory *directory)
147 int i;
149 for (i = directory->children.nr; --i >= 0;)
150 delete_directory(directory->children.base[i]);
152 assert(directory->children.nr == 0);
154 songvec_for_each(&directory->songs, delete_each_song, directory);
158 * Recursively free a directory and all its contents.
160 static void
161 delete_directory(struct directory *directory)
163 assert(directory->parent != NULL);
165 clear_directory(directory);
167 dirvec_delete(&directory->parent->children, directory);
168 directory_free(directory);
171 static void
172 delete_name_in(struct directory *parent, const char *name)
174 struct directory *directory = directory_get_child(parent, name);
175 struct song *song = songvec_find(&parent->songs, name);
177 if (directory != NULL) {
178 delete_directory(directory);
179 modified = true;
182 if (song != NULL) {
183 delete_song(parent, song);
184 modified = true;
188 /* passed to songvec_for_each */
189 static int
190 delete_song_if_removed(struct song *song, void *_data)
192 struct directory *dir = _data;
193 char *path;
194 struct stat st;
196 if ((path = map_song_fs(song)) == NULL ||
197 stat(path, &st) < 0 || !S_ISREG(st.st_mode)) {
198 delete_song(dir, song);
199 modified = true;
202 g_free(path);
203 return 0;
206 static bool
207 directory_exists(const struct directory *directory)
209 char *path_fs;
210 GFileTest test;
211 bool exists;
213 path_fs = map_directory_fs(directory);
214 if (path_fs == NULL)
215 /* invalid path: cannot exist */
216 return false;
218 test = directory->device == DEVICE_INARCHIVE ||
219 directory->device == DEVICE_CONTAINER
220 ? G_FILE_TEST_IS_REGULAR
221 : G_FILE_TEST_IS_DIR;
223 exists = g_file_test(path_fs, test);
224 g_free(path_fs);
226 return exists;
229 static void
230 removeDeletedFromDirectory(struct directory *directory)
232 int i;
233 struct dirvec *dv = &directory->children;
235 for (i = dv->nr; --i >= 0; ) {
236 if (directory_exists(dv->base[i]))
237 continue;
239 g_debug("removing directory: %s", dv->base[i]->path);
240 delete_directory(dv->base[i]);
241 modified = true;
244 songvec_for_each(&directory->songs, delete_song_if_removed, directory);
247 static int
248 stat_directory(const struct directory *directory, struct stat *st)
250 char *path_fs;
251 int ret;
253 path_fs = map_directory_fs(directory);
254 if (path_fs == NULL)
255 return -1;
256 ret = stat(path_fs, st);
257 g_free(path_fs);
258 return ret;
261 static int
262 stat_directory_child(const struct directory *parent, const char *name,
263 struct stat *st)
265 char *path_fs;
266 int ret;
268 path_fs = map_directory_child_fs(parent, name);
269 if (path_fs == NULL)
270 return -1;
272 ret = stat(path_fs, st);
273 g_free(path_fs);
274 return ret;
277 static int
278 statDirectory(struct directory *dir)
280 struct stat st;
282 if (stat_directory(dir, &st) < 0)
283 return -1;
285 directory_set_stat(dir, &st);
287 return 0;
290 static int
291 inodeFoundInParent(struct directory *parent, ino_t inode, dev_t device)
293 while (parent) {
294 if (!parent->stat && statDirectory(parent) < 0)
295 return -1;
296 if (parent->inode == inode && parent->device == device) {
297 g_debug("recursive directory found");
298 return 1;
300 parent = parent->parent;
303 return 0;
306 static struct directory *
307 make_subdir(struct directory *parent, const char *name)
309 struct directory *directory;
311 directory = directory_get_child(parent, name);
312 if (directory == NULL) {
313 char *path;
315 if (directory_is_root(parent))
316 path = NULL;
317 else
318 name = path = g_strconcat(directory_get_path(parent),
319 "/", name, NULL);
321 directory = directory_new_child(parent, name);
322 g_free(path);
325 return directory;
328 #ifdef ENABLE_ARCHIVE
329 static void
330 update_archive_tree(struct directory *directory, char *name)
332 struct directory *subdir;
333 struct song *song;
334 char *tmp;
336 tmp = strchr(name, '/');
337 if (tmp) {
338 *tmp = 0;
339 //add dir is not there already
340 if ((subdir = dirvec_find(&directory->children, name)) == NULL) {
341 //create new directory
342 subdir = make_subdir(directory, name);
343 subdir->device = DEVICE_INARCHIVE;
345 //create directories first
346 update_archive_tree(subdir, tmp+1);
347 } else {
348 if (strlen(name) == 0) {
349 g_warning("archive returned directory only");
350 return;
352 //add file
353 song = songvec_find(&directory->songs, name);
354 if (song == NULL) {
355 song = song_file_load(name, directory);
356 if (song != NULL) {
357 songvec_add(&directory->songs, song);
358 modified = true;
359 g_message("added %s/%s",
360 directory_get_path(directory), name);
367 * Updates the file listing from an archive file.
369 * @param parent the parent directory the archive file resides in
370 * @param name the UTF-8 encoded base name of the archive file
371 * @param st stat() information on the archive file
372 * @param plugin the archive plugin which fits this archive type
374 static void
375 update_archive_file(struct directory *parent, const char *name,
376 const struct stat *st,
377 const struct archive_plugin *plugin)
379 char *path_fs;
380 struct archive_file *file;
381 struct directory *directory;
382 char *filepath;
384 directory = dirvec_find(&parent->children, name);
385 if (directory != NULL && directory->mtime == st->st_mtime)
386 /* MPD has already scanned the archive, and it hasn't
387 changed since - don't consider updating it */
388 return;
390 path_fs = map_directory_child_fs(parent, name);
392 /* open archive */
393 file = plugin->open(path_fs);
394 if (file == NULL) {
395 g_warning("unable to open archive %s", path_fs);
396 g_free(path_fs);
397 return;
400 g_debug("archive %s opened", path_fs);
401 g_free(path_fs);
403 if (directory == NULL) {
404 g_debug("creating archive directory: %s", name);
405 directory = make_subdir(parent, name);
406 /* mark this directory as archive (we use device for
407 this) */
408 directory->device = DEVICE_INARCHIVE;
411 directory->mtime = st->st_mtime;
413 plugin->scan_reset(file);
415 while ((filepath = plugin->scan_next(file)) != NULL) {
416 /* split name into directory and file */
417 g_debug("adding archive file: %s", filepath);
418 update_archive_tree(directory, filepath);
421 plugin->close(file);
423 #endif
425 static bool
426 update_container_file( struct directory* directory,
427 const char* name,
428 const struct stat* st,
429 const struct decoder_plugin* plugin)
431 char* vtrack = NULL;
432 unsigned int tnum = 0;
433 char* pathname = map_directory_child_fs(directory, name);
434 struct directory* contdir = dirvec_find(&directory->children, name);
436 // directory exists already
437 if (contdir != NULL)
439 // modification time not eq. file mod. time
440 if (contdir->mtime != st->st_mtime)
442 g_message("removing container file: %s", pathname);
444 delete_directory(contdir);
445 contdir = NULL;
447 modified = true;
449 else {
450 g_free(pathname);
451 return true;
455 contdir = make_subdir(directory, name);
456 contdir->mtime = st->st_mtime;
457 contdir->device = DEVICE_CONTAINER;
459 while ((vtrack = plugin->container_scan(pathname, ++tnum)) != NULL)
461 struct song* song = song_file_new(vtrack, contdir);
462 if (song == NULL)
463 return true;
465 // shouldn't be necessary but it's there..
466 song->mtime = st->st_mtime;
468 song->tag = plugin->tag_dup(map_directory_child_fs(contdir, vtrack));
470 songvec_add(&contdir->songs, song);
471 song = NULL;
473 modified = true;
475 g_free(vtrack);
478 g_free(pathname);
480 if (tnum == 1)
482 delete_directory(contdir);
483 return false;
485 else
486 return true;
489 static void
490 update_regular_file(struct directory *directory,
491 const char *name, const struct stat *st)
493 const char *suffix = uri_get_suffix(name);
494 const struct decoder_plugin* plugin;
495 #ifdef ENABLE_ARCHIVE
496 const struct archive_plugin *archive;
497 #endif
498 if (suffix == NULL)
499 return;
501 if ((plugin = decoder_plugin_from_suffix(suffix, false)) != NULL)
503 struct song* song = songvec_find(&directory->songs, name);
505 if (plugin->container_scan != NULL)
507 if (update_container_file(directory, name, st, plugin))
509 if (song != NULL)
510 delete_song(directory, song);
512 return;
516 if (song == NULL) {
517 song = song_file_load(name, directory);
518 if (song == NULL)
519 return;
521 songvec_add(&directory->songs, song);
522 modified = true;
523 g_message("added %s/%s",
524 directory_get_path(directory), name);
525 } else if (st->st_mtime != song->mtime) {
526 g_message("updating %s/%s",
527 directory_get_path(directory), name);
528 if (!song_file_update(song))
529 delete_song(directory, song);
530 modified = true;
532 #ifdef ENABLE_ARCHIVE
533 } else if ((archive = archive_plugin_from_suffix(suffix))) {
534 update_archive_file(directory, name, st, archive);
535 #endif
539 static bool
540 updateDirectory(struct directory *directory, const struct stat *st);
542 static void
543 updateInDirectory(struct directory *directory,
544 const char *name, const struct stat *st)
546 assert(strchr(name, '/') == NULL);
548 if (S_ISREG(st->st_mode)) {
549 update_regular_file(directory, name, st);
550 } else if (S_ISDIR(st->st_mode)) {
551 struct directory *subdir;
552 bool ret;
554 if (inodeFoundInParent(directory, st->st_ino, st->st_dev))
555 return;
557 subdir = make_subdir(directory, name);
558 assert(directory == subdir->parent);
560 ret = updateDirectory(subdir, st);
561 if (!ret)
562 delete_directory(subdir);
563 } else {
564 g_debug("update: %s is not a directory, archive or music", name);
568 /* we don't look at "." / ".." nor files with newlines in their name */
569 static bool skip_path(const char *path)
571 return (path[0] == '.' && path[1] == 0) ||
572 (path[0] == '.' && path[1] == '.' && path[2] == 0) ||
573 strchr(path, '\n') != NULL;
576 static bool
577 skip_symlink(const struct directory *directory, const char *utf8_name)
579 #ifndef WIN32
580 char buffer[MPD_PATH_MAX];
581 char *path_fs;
582 const char *p;
583 ssize_t ret;
585 path_fs = map_directory_child_fs(directory, utf8_name);
586 if (path_fs == NULL)
587 return true;
589 ret = readlink(path_fs, buffer, sizeof(buffer));
590 g_free(path_fs);
591 if (ret < 0)
592 /* don't skip if this is not a symlink */
593 return errno != EINVAL;
595 if (!follow_inside_symlinks && !follow_outside_symlinks) {
596 /* ignore all symlinks */
597 return true;
598 } else if (follow_inside_symlinks && follow_outside_symlinks) {
599 /* consider all symlinks */
600 return false;
603 if (buffer[0] == '/')
604 return !follow_outside_symlinks;
606 p = buffer;
607 while (*p == '.') {
608 if (p[1] == '.' && p[2] == '/') {
609 /* "../" moves to parent directory */
610 directory = directory->parent;
611 if (directory == NULL) {
612 /* we have moved outside the music
613 directory - skip this symlink
614 if such symlinks are not allowed */
615 return !follow_outside_symlinks;
617 p += 3;
618 } else if (p[1] == '/')
619 /* eliminate "./" */
620 p += 2;
621 else
622 break;
625 /* we are still in the music directory, so this symlink points
626 to a song which is already in the database - skip according
627 to the follow_inside_symlinks param*/
628 return !follow_inside_symlinks;
629 #else
630 /* no symlink checking on WIN32 */
632 (void)directory;
633 (void)utf8_name;
635 return false;
636 #endif
639 static bool
640 updateDirectory(struct directory *directory, const struct stat *st)
642 DIR *dir;
643 struct dirent *ent;
644 char *path_fs;
646 assert(S_ISDIR(st->st_mode));
648 directory_set_stat(directory, st);
650 path_fs = map_directory_fs(directory);
651 if (path_fs == NULL)
652 return false;
654 dir = opendir(path_fs);
655 if (!dir) {
656 g_warning("Failed to open directory %s: %s",
657 path_fs, g_strerror(errno));
658 g_free(path_fs);
659 return false;
662 g_free(path_fs);
664 removeDeletedFromDirectory(directory);
666 while ((ent = readdir(dir))) {
667 char *utf8;
668 struct stat st2;
670 if (skip_path(ent->d_name))
671 continue;
673 utf8 = fs_charset_to_utf8(ent->d_name);
674 if (utf8 == NULL || skip_symlink(directory, utf8)) {
675 g_free(utf8);
676 continue;
679 if (stat_directory_child(directory, utf8, &st2) == 0)
680 updateInDirectory(directory, utf8, &st2);
681 else
682 delete_name_in(directory, utf8);
684 g_free(utf8);
687 closedir(dir);
689 directory->mtime = st->st_mtime;
691 return true;
694 static struct directory *
695 directory_make_child_checked(struct directory *parent, const char *path)
697 struct directory *directory;
698 char *base;
699 struct stat st;
700 struct song *conflicting;
702 directory = directory_get_child(parent, path);
703 if (directory != NULL)
704 return directory;
706 base = g_path_get_basename(path);
708 if (stat_directory_child(parent, base, &st) < 0 ||
709 inodeFoundInParent(parent, st.st_ino, st.st_dev)) {
710 g_free(base);
711 return NULL;
714 /* if we're adding directory paths, make sure to delete filenames
715 with potentially the same name */
716 conflicting = songvec_find(&parent->songs, base);
717 if (conflicting)
718 delete_song(parent, conflicting);
720 g_free(base);
722 directory = directory_new_child(parent, path);
723 directory_set_stat(directory, &st);
724 return directory;
727 static struct directory *
728 addParentPathToDB(const char *utf8path)
730 struct directory *directory = db_get_root();
731 char *duplicated = g_strdup(utf8path);
732 char *slash = duplicated;
734 while ((slash = strchr(slash, '/')) != NULL) {
735 *slash = 0;
737 directory = directory_make_child_checked(directory,
738 duplicated);
739 if (directory == NULL || slash == NULL)
740 break;
742 *slash++ = '/';
745 g_free(duplicated);
746 return directory;
749 static void
750 updatePath(const char *path)
752 struct directory *parent;
753 char *name;
754 struct stat st;
756 parent = addParentPathToDB(path);
757 if (parent == NULL)
758 return;
760 name = g_path_get_basename(path);
762 if (stat_directory_child(parent, name, &st) == 0)
763 updateInDirectory(parent, name, &st);
764 else
765 delete_name_in(parent, name);
767 g_free(name);
770 static void * update_task(void *_path)
772 if (_path != NULL && !isRootDirectory(_path)) {
773 updatePath((char *)_path);
774 } else {
775 struct directory *directory = db_get_root();
776 struct stat st;
778 if (stat_directory(directory, &st) == 0)
779 updateDirectory(directory, &st);
782 g_free(_path);
784 if (modified || !db_exists())
785 db_save();
787 progress = UPDATE_PROGRESS_DONE;
788 event_pipe_emit(PIPE_EVENT_UPDATE);
789 return NULL;
792 static void spawn_update_task(char *path)
794 GError *e = NULL;
796 assert(g_thread_self() == main_task);
798 progress = UPDATE_PROGRESS_RUNNING;
799 modified = false;
800 if (!(update_thr = g_thread_create(update_task, path, TRUE, &e)))
801 g_error("Failed to spawn update task: %s", e->message);
802 if (++update_task_id > update_task_id_max)
803 update_task_id = 1;
804 g_debug("spawned thread for update job id %i", update_task_id);
807 unsigned
808 directory_update_init(char *path)
810 assert(g_thread_self() == main_task);
812 if (!mapper_has_music_directory())
813 return 0;
815 if (progress != UPDATE_PROGRESS_IDLE) {
816 unsigned next_task_id;
818 if (update_paths_nr == G_N_ELEMENTS(update_paths)) {
819 g_free(path);
820 return 0;
823 assert(update_paths_nr < G_N_ELEMENTS(update_paths));
824 update_paths[update_paths_nr++] = path;
825 next_task_id = update_task_id + update_paths_nr;
827 return next_task_id > update_task_id_max ? 1 : next_task_id;
829 spawn_update_task(path);
831 idle_add(IDLE_UPDATE);
833 return update_task_id;
837 * Safely delete a song from the database. This must be done in the
838 * main task, to be sure that there is no pointer left to it.
840 static void song_delete_event(void)
842 char *uri;
844 assert(progress == UPDATE_PROGRESS_RUNNING);
845 assert(delete != NULL);
847 uri = song_get_uri(delete);
848 g_debug("removing: %s", uri);
849 g_free(uri);
851 #ifdef ENABLE_SQLITE
852 /* if the song has a sticker, delete it */
853 if (sticker_enabled())
854 sticker_song_delete(delete);
855 #endif
857 playlist_delete_song(&g_playlist, delete);
858 delete = NULL;
860 notify_signal(&update_notify);
864 * Called in the main thread after the database update is finished.
866 static void update_finished_event(void)
868 assert(progress == UPDATE_PROGRESS_DONE);
870 g_thread_join(update_thr);
872 idle_add(IDLE_UPDATE);
874 if (modified) {
875 /* send "idle" events */
876 playlist_increment_version_all(&g_playlist);
877 idle_add(IDLE_DATABASE);
880 if (update_paths_nr) {
881 /* schedule the next path */
882 char *path = update_paths[0];
883 memmove(&update_paths[0], &update_paths[1],
884 --update_paths_nr * sizeof(char *));
885 spawn_update_task(path);
886 } else {
887 progress = UPDATE_PROGRESS_IDLE;
889 stats_update();
893 void update_global_init(void)
895 #ifndef WIN32
896 follow_inside_symlinks =
897 config_get_bool(CONF_FOLLOW_INSIDE_SYMLINKS,
898 DEFAULT_FOLLOW_INSIDE_SYMLINKS);
900 follow_outside_symlinks =
901 config_get_bool(CONF_FOLLOW_OUTSIDE_SYMLINKS,
902 DEFAULT_FOLLOW_OUTSIDE_SYMLINKS);
903 #endif
905 notify_init(&update_notify);
907 event_pipe_register(PIPE_EVENT_DELETE, song_delete_event);
908 event_pipe_register(PIPE_EVENT_UPDATE, update_finished_event);
911 void update_global_finish(void)
913 notify_deinit(&update_notify);