diff --git a/cmdline/search.c b/cmdline/search.c
new file mode 100644
index 00000000..945be6f6
--- /dev/null
+++ b/cmdline/search.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2014 Andrea Mazzoleni
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "portable.h"
+
+#include "search.h"
+
+/****************************************************************************/
+/* search */
+
+static void search_file(struct snapraid_state* state, const char* path, data_off_t size, int64_t mtime_sec, int mtime_nsec)
+{
+ struct snapraid_search_file* file;
+ tommy_uint32_t file_hash;
+
+ file = malloc_nofail(sizeof(struct snapraid_search_file));
+ file->path = strdup_nofail(path);
+ file->size = size;
+ file->mtime_sec = mtime_sec;
+ file->mtime_nsec = mtime_nsec;
+
+ file_hash = file_stamp_hash(file->size, file->mtime_sec, file->mtime_nsec);
+
+ tommy_hashdyn_insert(&state->searchset, &file->node, file, file_hash);
+}
+
+void search_file_free(struct snapraid_search_file* file)
+{
+ free(file->path);
+ free(file);
+}
+
+struct search_file_compare_arg {
+ const struct snapraid_state* state;
+ const struct snapraid_block* block;
+ const struct snapraid_file* file;
+ unsigned char* buffer;
+ data_off_t offset;
+ unsigned read_size;
+ int prevhash;
+};
+
+int search_file_compare(const void* void_arg, const void* void_data)
+{
+ const struct search_file_compare_arg* arg = void_arg;
+ const struct snapraid_search_file* file = void_data;
+ const struct snapraid_state* state = arg->state;
+ unsigned char buffer_hash[HASH_SIZE];
+ const char* path = file->path;
+ int f;
+ ssize_t ret;
+
+ /* compare file info */
+ if (arg->file->size != file->size)
+ return -1;
+
+ if (arg->file->mtime_sec != file->mtime_sec)
+ return -1;
+
+ if (arg->file->mtime_nsec != file->mtime_nsec)
+ return -1;
+
+ /* read the block and compare the hash */
+ f = open(path, O_RDONLY | O_BINARY);
+ if (f == -1) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error opening file '%s'. %s.\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+
+ if (lseek(f, arg->offset, SEEK_SET) != arg->offset) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error seeking file '%s'. %s.\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = read(f, arg->buffer, arg->read_size);
+ if (ret < 0 || (unsigned)ret != arg->read_size) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error reading file '%s'. %s.\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+
+ ret = close(f);
+ if (ret != 0) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error closing file '%s'. %s.\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+
+ /* compute the hash */
+ if (arg->prevhash)
+ memhash(state->prevhash, state->prevhashseed, buffer_hash, arg->buffer, arg->read_size);
+ else
+ memhash(state->hash, state->hashseed, buffer_hash, arg->buffer, arg->read_size);
+
+ /* check if the hash is matching */
+ if (memcmp(buffer_hash, arg->block->hash, HASH_SIZE) != 0)
+ return -1;
+
+ if (arg->read_size != state->block_size) {
+ /* fill the remaining with 0 */
+ memset(arg->buffer + arg->read_size, 0, state->block_size - arg->read_size);
+ }
+
+ return 0;
+}
+
+int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_block* missing_block, unsigned char* buffer)
+{
+ struct snapraid_search_file* file;
+ tommy_uint32_t file_hash;
+ struct search_file_compare_arg arg;
+
+ arg.state = state;
+ arg.block = missing_block;
+ arg.file = block_file_get(missing_block);
+ arg.buffer = buffer;
+ arg.offset = state->block_size * (data_off_t)block_file_pos(missing_block);
+ arg.read_size = block_file_size(missing_block, state->block_size);
+ arg.prevhash = prevhash;
+
+ file_hash = file_stamp_hash(arg.file->size, arg.file->mtime_sec, arg.file->mtime_nsec);
+
+ /* search in the hashtable, and also check if the data matches the hash */
+ file = tommy_hashdyn_search(&state->searchset, search_file_compare, &arg, file_hash);
+ if (!file)
+ return -1;
+
+ /* if found, buffer is already set with data */
+ return 0;
+}
+
+static void search_dir(struct snapraid_state* state, const char* dir)
+{
+ DIR* d;
+
+ d = opendir(dir);
+ if (!d) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error opening directory '%s'. %s.\n", dir, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+
+ while (1) {
+ char path[PATH_MAX];
+ struct stat st;
+ const char* name;
+ struct dirent* dd;
+
+ /* clear errno to detect erroneous conditions */
+ errno = 0;
+ dd = readdir(d);
+ if (dd == 0 && errno != 0) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error reading directory '%s'. %s.\n", dir, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+ if (dd == 0) {
+ break; /* finished */
+ }
+
+ /* skip "." and ".." files */
+ name = dd->d_name;
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
+ continue;
+
+ pathprint(path, sizeof(path), "%s", dir);
+ pathslash(path, sizeof(path));
+ pathcat(path, sizeof(path), name);
+
+#if HAVE_STRUCT_DIRENT_D_STAT
+ /* convert dirent to lstat result */
+ dirent_lstat(dd, &st);
+
+ /* if the st_mode field is missing, takes care to fill it using normal lstat() */
+ /* at now this can happen only in Windows (with HAVE_STRUCT_DIRENT_D_STAT defined), */
+ /* because we use a directory reading method that doesn't read info about ReparsePoint. */
+ /* Note that here we cannot call here lstat_sync(), because we don't know what kind */
+ /* of file is it, and lstat_sync() doesn't always work */
+ if (st.st_mode == 0) {
+ if (lstat(path, &st) != 0) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error in stat file/directory '%s'. %s.\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+ }
+#else
+ /* get lstat info about the file */
+ if (lstat(path, &st) != 0) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error in stat file/directory '%s'. %s.\n", path, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+#endif
+
+ if (S_ISREG(st.st_mode)) {
+ search_file(state, path, st.st_size, st.st_mtime, STAT_NSEC(&st));
+ } else if (S_ISDIR(st.st_mode)) {
+ search_dir(state, path);
+ }
+ }
+
+ if (closedir(d) != 0) {
+ /* LCOV_EXCL_START */
+ fprintf(stderr, "Error closing directory '%s'. %s.\n", dir, strerror(errno));
+ exit(EXIT_FAILURE);
+ /* LCOV_EXCL_STOP */
+ }
+}
+
+void state_search(struct snapraid_state* state, const char* dir)
+{
+ printf("Importing...\n");
+
+ search_dir(state, dir);
+}
+
diff --git a/cmdline/search.h b/cmdline/search.h
new file mode 100644
index 00000000..285cfb83
--- /dev/null
+++ b/cmdline/search.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014 Andrea Mazzoleni
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef __SEARCH_H
+#define __SEARCH_H
+
+#include "elem.h"
+#include "state.h"
+
+/****************************************************************************/
+/* search */
+
+/**
+ * Search file.
+ * File used to search for moved data.
+ */
+struct snapraid_search_file {
+ char* path; /**< Full path of the file. */
+ char* name; /**< Pointer of the name inside the path. */
+ data_off_t size;
+ int64_t mtime_sec;
+ int mtime_nsec;
+
+ /* nodes for data structures */
+ tommy_node node;
+};
+
+/**
+ * Deallocates a search file.
+ */
+void search_file_free(struct snapraid_search_file* file);
+
+/**
+ * Fetch a file from the size, timestamp and name.
+ * Return ==0 if the block is found, and copied into buffer.
+ */
+int state_search_fetch(struct snapraid_state* state, int prevhash, struct snapraid_block* missing_block, unsigned char* buffer);
+
+/**
+ * Import files from the specified directory.
+ */
+void state_search(struct snapraid_state* state, const char* dir);
+
+#endif
+