notmuch_filenames_t *db_files = NULL;
notmuch_filenames_t *db_subdirs = NULL;
struct stat st;
- notmuch_bool_t is_maildir;
+ notmuch_bool_t is_maildir, new_directory;
if (stat (path, &st)) {
fprintf (stderr, "Error reading directory %s: %s\n",
return NOTMUCH_STATUS_FILE_ERROR;
}
- if (! S_ISDIR (st.st_mode)) {
- fprintf (stderr, "Error: %s is not a directory.\n", path);
- return NOTMUCH_STATUS_FILE_ERROR;
- }
+ /* This is not an error since we may have recursed based on a
+ * symlink to a regular file, not a directory, and we don't know
+ * that until this stat. */
+ if (! S_ISDIR (st.st_mode))
+ return NOTMUCH_STATUS_SUCCESS;
fs_mtime = st.st_mtime;
directory = notmuch_database_get_directory (notmuch, path);
db_mtime = notmuch_directory_get_mtime (directory);
+ if (db_mtime == 0) {
+ new_directory = TRUE;
+ db_files = NULL;
+ db_subdirs = NULL;
+ } else {
+ new_directory = FALSE;
+ db_files = notmuch_directory_get_child_files (directory);
+ db_subdirs = notmuch_directory_get_child_directories (directory);
+ }
+
/* If the database knows about this directory, then we sort based
* on strcmp to match the database sorting. Otherwise, we can do
* inode-based sorting for faster filesystem operation. */
num_fs_entries = scandir (path, &fs_entries, 0,
- db_mtime ?
- dirent_sort_strcmp_name : dirent_sort_inode);
+ new_directory ?
+ dirent_sort_inode : dirent_sort_strcmp_name);
if (num_fs_entries == -1) {
fprintf (stderr, "Error opening directory %s: %s\n",
entry = fs_entries[i];
- if (entry->d_type != DT_DIR)
+ if (entry->d_type != DT_DIR && entry->d_type != DT_LNK)
continue;
/* Ignore special directories to avoid infinite recursion.
goto DONE;
/* Pass 2: Scan for new files, removed files, and removed directories. */
- db_files = notmuch_directory_get_child_files (directory);
- db_subdirs = notmuch_directory_get_child_directories (directory);
-
for (i = 0; i < num_fs_entries; i++)
{
if (interrupted)
notmuch_filenames_advance (db_subdirs);
}
- if (entry->d_type != DT_REG)
+ /* If we're looking at a symlink, we only want to add it if it
+ * links to a regular file, (and not to a directory, say). */
+ if (entry->d_type == DT_LNK) {
+ int err;
+
+ next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
+ err = stat (next, &st);
+ talloc_free (next);
+ next = NULL;
+
+ /* Don't emit an error for a link pointing nowhere, since
+ * the directory-traversal pass will have already done
+ * that. */
+ if (err)
+ continue;
+
+ if (! S_ISREG (st.st_mode))
+ continue;
+ } else if (entry->d_type != DT_REG) {
continue;
+ }
/* Don't add a file that we've added before. */
if (notmuch_filenames_has_more (db_files) &&
next);
break;
/* Fatal issues. Don't process anymore. */
- case NOTMUCH_STATUS_READONLY_DATABASE:
+ case NOTMUCH_STATUS_READ_ONLY_DATABASE:
case NOTMUCH_STATUS_XAPIAN_EXCEPTION:
case NOTMUCH_STATUS_OUT_OF_MEMORY:
fprintf (stderr, "Error: %s. Halting processing.\n",
next = NULL;
}
+ /* Now that we've walked the whole filesystem list, anything left
+ * over in the database lists has been deleted. */
+ while (notmuch_filenames_has_more (db_files))
+ {
+ char *absolute = talloc_asprintf (state->removed_files,
+ "%s/%s", path,
+ notmuch_filenames_get (db_files));
+
+ _filename_list_add (state->removed_files, absolute);
+
+ notmuch_filenames_advance (db_files);
+ }
+
+ while (notmuch_filenames_has_more (db_subdirs))
+ {
+ char *absolute = talloc_asprintf (state->removed_directories,
+ "%s/%s", path,
+ notmuch_filenames_get (db_subdirs));
+
+ _filename_list_add (state->removed_directories, absolute);
+
+ notmuch_filenames_advance (db_subdirs);
+ }
+
if (! interrupted) {
status = notmuch_directory_set_mtime (directory, fs_mtime);
if (status && ret == NOTMUCH_STATUS_SUCCESS)
struct sigaction action;
struct itimerval timerval;
notmuch_bool_t timer_is_active = FALSE;
+ struct stat st;
if (state->output_is_a_tty && ! debugger_is_active () && ! state->verbose) {
/* Setup our handler for SIGALRM */
timer_is_active = TRUE;
}
+ if (stat (path, &st)) {
+ fprintf (stderr, "Error reading directory %s: %s\n",
+ path, strerror (errno));
+ return NOTMUCH_STATUS_FILE_ERROR;
+ }
+
+ if (! S_ISDIR (st.st_mode)) {
+ fprintf (stderr, "Error: %s is not a directory.\n", path);
+ return NOTMUCH_STATUS_FILE_ERROR;
+ }
+
status = add_files_recursive (notmuch, path, state);
if (timer_is_active) {