2 * Copyright (C) 2003-2010 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 "config.h" /* must be first for large file support */
21 #include "update_internal.h"
24 #include "directory.h"
29 #include "decoder_list.h"
30 #include "decoder_plugin.h"
34 #include "archive_list.h"
35 #include "archive_plugin.h"
41 #include <sys/types.h>
49 static bool walk_discard
;
55 DEFAULT_FOLLOW_INSIDE_SYMLINKS
= true,
56 DEFAULT_FOLLOW_OUTSIDE_SYMLINKS
= true,
59 static bool follow_inside_symlinks
;
60 static bool follow_outside_symlinks
;
65 update_walk_global_init(void)
68 follow_inside_symlinks
=
69 config_get_bool(CONF_FOLLOW_INSIDE_SYMLINKS
,
70 DEFAULT_FOLLOW_INSIDE_SYMLINKS
);
72 follow_outside_symlinks
=
73 config_get_bool(CONF_FOLLOW_OUTSIDE_SYMLINKS
,
74 DEFAULT_FOLLOW_OUTSIDE_SYMLINKS
);
79 update_walk_global_finish(void)
84 directory_set_stat(struct directory
*dir
, const struct stat
*st
)
86 dir
->inode
= st
->st_ino
;
87 dir
->device
= st
->st_dev
;
92 delete_song(struct directory
*dir
, struct song
*del
)
94 /* first, prevent traversers in main task from getting this */
95 songvec_delete(&dir
->songs
, del
);
97 /* now take it out of the playlist (in the main_task) */
98 update_remove_song(del
);
100 /* finally, all possible references gone, free it */
105 delete_each_song(struct song
*song
, G_GNUC_UNUSED
void *data
)
107 struct directory
*directory
= data
;
108 assert(song
->parent
== directory
);
109 delete_song(directory
, song
);
114 delete_directory(struct directory
*directory
);
117 * Recursively remove all sub directories and songs from a directory,
118 * leaving an empty directory.
121 clear_directory(struct directory
*directory
)
125 for (i
= directory
->children
.nr
; --i
>= 0;)
126 delete_directory(directory
->children
.base
[i
]);
128 assert(directory
->children
.nr
== 0);
130 songvec_for_each(&directory
->songs
, delete_each_song
, directory
);
134 * Recursively free a directory and all its contents.
137 delete_directory(struct directory
*directory
)
139 assert(directory
->parent
!= NULL
);
141 clear_directory(directory
);
143 dirvec_delete(&directory
->parent
->children
, directory
);
144 directory_free(directory
);
148 delete_name_in(struct directory
*parent
, const char *name
)
150 struct directory
*directory
= directory_get_child(parent
, name
);
151 struct song
*song
= songvec_find(&parent
->songs
, name
);
153 if (directory
!= NULL
) {
154 delete_directory(directory
);
159 delete_song(parent
, song
);
164 /* passed to songvec_for_each */
166 delete_song_if_excluded(struct song
*song
, void *_data
)
168 GSList
*exclude_list
= _data
;
171 assert(song
->parent
!= NULL
);
173 name_fs
= utf8_to_fs_charset(song
->uri
);
174 if (exclude_list_check(exclude_list
, name_fs
)) {
175 delete_song(song
->parent
, song
);
184 remove_excluded_from_directory(struct directory
*directory
,
185 GSList
*exclude_list
)
188 struct dirvec
*dv
= &directory
->children
;
190 for (i
= dv
->nr
; --i
>= 0; ) {
191 struct directory
*child
= dv
->base
[i
];
192 char *name_fs
= utf8_to_fs_charset(directory_get_name(child
));
194 if (exclude_list_check(exclude_list
, name_fs
)) {
195 delete_directory(child
);
202 songvec_for_each(&directory
->songs
,
203 delete_song_if_excluded
, exclude_list
);
206 /* passed to songvec_for_each */
208 delete_song_if_removed(struct song
*song
, void *_data
)
210 struct directory
*dir
= _data
;
214 if ((path
= map_song_fs(song
)) == NULL
||
215 stat(path
, &st
) < 0 || !S_ISREG(st
.st_mode
)) {
216 delete_song(dir
, song
);
225 directory_exists(const struct directory
*directory
)
231 path_fs
= map_directory_fs(directory
);
233 /* invalid path: cannot exist */
236 test
= directory
->device
== DEVICE_INARCHIVE
||
237 directory
->device
== DEVICE_CONTAINER
238 ? G_FILE_TEST_IS_REGULAR
239 : G_FILE_TEST_IS_DIR
;
241 exists
= g_file_test(path_fs
, test
);
248 removeDeletedFromDirectory(struct directory
*directory
)
251 struct dirvec
*dv
= &directory
->children
;
253 for (i
= dv
->nr
; --i
>= 0; ) {
254 if (directory_exists(dv
->base
[i
]))
257 g_debug("removing directory: %s", dv
->base
[i
]->path
);
258 delete_directory(dv
->base
[i
]);
262 songvec_for_each(&directory
->songs
, delete_song_if_removed
, directory
);
266 stat_directory(const struct directory
*directory
, struct stat
*st
)
271 path_fs
= map_directory_fs(directory
);
274 ret
= stat(path_fs
, st
);
280 stat_directory_child(const struct directory
*parent
, const char *name
,
286 path_fs
= map_directory_child_fs(parent
, name
);
290 ret
= stat(path_fs
, st
);
296 statDirectory(struct directory
*dir
)
300 if (stat_directory(dir
, &st
) < 0)
303 directory_set_stat(dir
, &st
);
309 inodeFoundInParent(struct directory
*parent
, ino_t inode
, dev_t device
)
312 if (!parent
->stat
&& statDirectory(parent
) < 0)
314 if (parent
->inode
== inode
&& parent
->device
== device
) {
315 g_debug("recursive directory found");
318 parent
= parent
->parent
;
324 static struct directory
*
325 make_subdir(struct directory
*parent
, const char *name
)
327 struct directory
*directory
;
329 directory
= directory_get_child(parent
, name
);
330 if (directory
== NULL
) {
333 if (directory_is_root(parent
))
336 name
= path
= g_strconcat(directory_get_path(parent
),
339 directory
= directory_new_child(parent
, name
);
346 #ifdef ENABLE_ARCHIVE
348 update_archive_tree(struct directory
*directory
, char *name
)
350 struct directory
*subdir
;
354 tmp
= strchr(name
, '/');
357 //add dir is not there already
358 if ((subdir
= dirvec_find(&directory
->children
, name
)) == NULL
) {
359 //create new directory
360 subdir
= make_subdir(directory
, name
);
361 subdir
->device
= DEVICE_INARCHIVE
;
363 //create directories first
364 update_archive_tree(subdir
, tmp
+1);
366 if (strlen(name
) == 0) {
367 g_warning("archive returned directory only");
371 song
= songvec_find(&directory
->songs
, name
);
373 song
= song_file_load(name
, directory
);
375 songvec_add(&directory
->songs
, song
);
377 g_message("added %s/%s",
378 directory_get_path(directory
), name
);
385 * Updates the file listing from an archive file.
387 * @param parent the parent directory the archive file resides in
388 * @param name the UTF-8 encoded base name of the archive file
389 * @param st stat() information on the archive file
390 * @param plugin the archive plugin which fits this archive type
393 update_archive_file(struct directory
*parent
, const char *name
,
394 const struct stat
*st
,
395 const struct archive_plugin
*plugin
)
397 GError
*error
= NULL
;
399 struct archive_file
*file
;
400 struct directory
*directory
;
403 directory
= dirvec_find(&parent
->children
, name
);
404 if (directory
!= NULL
&& directory
->mtime
== st
->st_mtime
&&
406 /* MPD has already scanned the archive, and it hasn't
407 changed since - don't consider updating it */
410 path_fs
= map_directory_child_fs(parent
, name
);
413 file
= archive_file_open(plugin
, path_fs
, &error
);
416 g_warning("%s", error
->message
);
421 g_debug("archive %s opened", path_fs
);
424 if (directory
== NULL
) {
425 g_debug("creating archive directory: %s", name
);
426 directory
= make_subdir(parent
, name
);
427 /* mark this directory as archive (we use device for
429 directory
->device
= DEVICE_INARCHIVE
;
432 directory
->mtime
= st
->st_mtime
;
434 archive_file_scan_reset(file
);
436 while ((filepath
= archive_file_scan_next(file
)) != NULL
) {
437 /* split name into directory and file */
438 g_debug("adding archive file: %s", filepath
);
439 update_archive_tree(directory
, filepath
);
442 archive_file_close(file
);
447 update_container_file( struct directory
* directory
,
449 const struct stat
* st
,
450 const struct decoder_plugin
* plugin
)
453 unsigned int tnum
= 0;
454 char* pathname
= map_directory_child_fs(directory
, name
);
455 struct directory
* contdir
= dirvec_find(&directory
->children
, name
);
457 // directory exists already
460 // modification time not eq. file mod. time
461 if (contdir
->mtime
!= st
->st_mtime
|| walk_discard
)
463 g_message("removing container file: %s", pathname
);
465 delete_directory(contdir
);
476 contdir
= make_subdir(directory
, name
);
477 contdir
->mtime
= st
->st_mtime
;
478 contdir
->device
= DEVICE_CONTAINER
;
480 while ((vtrack
= plugin
->container_scan(pathname
, ++tnum
)) != NULL
)
482 struct song
* song
= song_file_new(vtrack
, contdir
);
485 // shouldn't be necessary but it's there..
486 song
->mtime
= st
->st_mtime
;
488 child_path_fs
= map_directory_child_fs(contdir
, vtrack
);
490 song
->tag
= plugin
->tag_dup(child_path_fs
);
491 g_free(child_path_fs
);
493 songvec_add(&contdir
->songs
, song
);
497 g_message("added %s/%s",
498 directory_get_path(directory
), vtrack
);
506 delete_directory(contdir
);
514 update_regular_file(struct directory
*directory
,
515 const char *name
, const struct stat
*st
)
517 const char *suffix
= uri_get_suffix(name
);
518 const struct decoder_plugin
* plugin
;
519 #ifdef ENABLE_ARCHIVE
520 const struct archive_plugin
*archive
;
525 if ((plugin
= decoder_plugin_from_suffix(suffix
, false)) != NULL
)
527 struct song
* song
= songvec_find(&directory
->songs
, name
);
529 if (!(song
!= NULL
&& st
->st_mtime
== song
->mtime
&&
531 plugin
->container_scan
!= NULL
)
533 if (update_container_file(directory
, name
, st
, plugin
))
536 delete_song(directory
, song
);
543 song
= song_file_load(name
, directory
);
545 g_debug("ignoring unrecognized file %s/%s",
546 directory_get_path(directory
), name
);
550 songvec_add(&directory
->songs
, song
);
552 g_message("added %s/%s",
553 directory_get_path(directory
), name
);
554 } else if (st
->st_mtime
!= song
->mtime
|| walk_discard
) {
555 g_message("updating %s/%s",
556 directory_get_path(directory
), name
);
557 if (!song_file_update(song
)) {
558 g_debug("deleting unrecognized file %s/%s",
559 directory_get_path(directory
), name
);
560 delete_song(directory
, song
);
565 #ifdef ENABLE_ARCHIVE
566 } else if ((archive
= archive_plugin_from_suffix(suffix
))) {
567 update_archive_file(directory
, name
, st
, archive
);
573 updateDirectory(struct directory
*directory
, const struct stat
*st
);
576 updateInDirectory(struct directory
*directory
,
577 const char *name
, const struct stat
*st
)
579 assert(strchr(name
, '/') == NULL
);
581 if (S_ISREG(st
->st_mode
)) {
582 update_regular_file(directory
, name
, st
);
583 } else if (S_ISDIR(st
->st_mode
)) {
584 struct directory
*subdir
;
587 if (inodeFoundInParent(directory
, st
->st_ino
, st
->st_dev
))
590 subdir
= make_subdir(directory
, name
);
591 assert(directory
== subdir
->parent
);
593 ret
= updateDirectory(subdir
, st
);
595 delete_directory(subdir
);
597 g_debug("update: %s is not a directory, archive or music", name
);
601 /* we don't look at "." / ".." nor files with newlines in their name */
602 static bool skip_path(const char *path
)
604 return (path
[0] == '.' && path
[1] == 0) ||
605 (path
[0] == '.' && path
[1] == '.' && path
[2] == 0) ||
606 strchr(path
, '\n') != NULL
;
610 skip_symlink(const struct directory
*directory
, const char *utf8_name
)
613 char buffer
[MPD_PATH_MAX
];
618 path_fs
= map_directory_child_fs(directory
, utf8_name
);
622 ret
= readlink(path_fs
, buffer
, sizeof(buffer
));
625 /* don't skip if this is not a symlink */
626 return errno
!= EINVAL
;
628 if (!follow_inside_symlinks
&& !follow_outside_symlinks
) {
629 /* ignore all symlinks */
631 } else if (follow_inside_symlinks
&& follow_outside_symlinks
) {
632 /* consider all symlinks */
636 if (buffer
[0] == '/')
637 return !follow_outside_symlinks
;
641 if (p
[1] == '.' && G_IS_DIR_SEPARATOR(p
[2])) {
642 /* "../" moves to parent directory */
643 directory
= directory
->parent
;
644 if (directory
== NULL
) {
645 /* we have moved outside the music
646 directory - skip this symlink
647 if such symlinks are not allowed */
648 return !follow_outside_symlinks
;
651 } else if (G_IS_DIR_SEPARATOR(p
[1]))
658 /* we are still in the music directory, so this symlink points
659 to a song which is already in the database - skip according
660 to the follow_inside_symlinks param*/
661 return !follow_inside_symlinks
;
663 /* no symlink checking on WIN32 */
673 updateDirectory(struct directory
*directory
, const struct stat
*st
)
677 char *path_fs
, *exclude_path_fs
;
678 GSList
*exclude_list
;
680 assert(S_ISDIR(st
->st_mode
));
682 directory_set_stat(directory
, st
);
684 path_fs
= map_directory_fs(directory
);
688 dir
= opendir(path_fs
);
690 g_warning("Failed to open directory %s: %s",
691 path_fs
, g_strerror(errno
));
696 exclude_path_fs
= g_build_filename(path_fs
, ".mpdignore", NULL
);
697 exclude_list
= exclude_list_load(exclude_path_fs
);
698 g_free(exclude_path_fs
);
702 if (exclude_list
!= NULL
)
703 remove_excluded_from_directory(directory
, exclude_list
);
705 removeDeletedFromDirectory(directory
);
707 while ((ent
= readdir(dir
))) {
711 if (skip_path(ent
->d_name
) ||
712 exclude_list_check(exclude_list
, ent
->d_name
))
715 utf8
= fs_charset_to_utf8(ent
->d_name
);
719 if (skip_symlink(directory
, utf8
)) {
720 delete_name_in(directory
, utf8
);
725 if (stat_directory_child(directory
, utf8
, &st2
) == 0)
726 updateInDirectory(directory
, utf8
, &st2
);
728 delete_name_in(directory
, utf8
);
733 exclude_list_free(exclude_list
);
737 directory
->mtime
= st
->st_mtime
;
742 static struct directory
*
743 directory_make_child_checked(struct directory
*parent
, const char *path
)
745 struct directory
*directory
;
748 struct song
*conflicting
;
750 directory
= directory_get_child(parent
, path
);
751 if (directory
!= NULL
)
754 base
= g_path_get_basename(path
);
756 if (stat_directory_child(parent
, base
, &st
) < 0 ||
757 inodeFoundInParent(parent
, st
.st_ino
, st
.st_dev
)) {
762 /* if we're adding directory paths, make sure to delete filenames
763 with potentially the same name */
764 conflicting
= songvec_find(&parent
->songs
, base
);
766 delete_song(parent
, conflicting
);
770 directory
= directory_new_child(parent
, path
);
771 directory_set_stat(directory
, &st
);
775 static struct directory
*
776 addParentPathToDB(const char *utf8path
)
778 struct directory
*directory
= db_get_root();
779 char *duplicated
= g_strdup(utf8path
);
780 char *slash
= duplicated
;
782 while ((slash
= strchr(slash
, '/')) != NULL
) {
785 directory
= directory_make_child_checked(directory
,
787 if (directory
== NULL
|| slash
== NULL
)
798 updatePath(const char *path
)
800 struct directory
*parent
;
804 parent
= addParentPathToDB(path
);
808 name
= g_path_get_basename(path
);
810 if (stat_directory_child(parent
, name
, &st
) == 0)
811 updateInDirectory(parent
, name
, &st
);
813 delete_name_in(parent
, name
);
819 update_walk(const char *path
, bool discard
)
821 walk_discard
= discard
;
824 if (path
!= NULL
&& !isRootDirectory(path
)) {
827 struct directory
*directory
= db_get_root();
830 if (stat_directory(directory
, &st
) == 0)
831 updateDirectory(directory
, &st
);