Deletion detection
diff --git a/cmd/csd.cc b/cmd/csd.cc
index 3259e7a..a1d4d41 100644
--- a/cmd/csd.cc
+++ b/cmd/csd.cc
@@ -53,8 +53,8 @@
 
   FsWatcher watcher (path.c_str (),
                      bind (&Dispatcher::Did_LocalFile_AddOrModify, &dispatcher, _1),
-                     bind (&Dispatcher::Did_LocalFile_Delete,      &dispatcher, _1));
-
+                     bind (&Dispatcher::Did_LocalFile_Delete,      &dispatcher, _1),
+                     dispatcher.GetFileState ());
 
   return app.exec ();
 }
diff --git a/cmd/dump-db.cc b/cmd/dump-db.cc
index 568c3db..a4040d8 100644
--- a/cmd/dump-db.cc
+++ b/cmd/dump-db.cc
@@ -45,7 +45,7 @@
     sqlite3_prepare_v2 (m_db,
                         "SELECT device_name, seq_no, action, filename, version, file_hash, file_seg_num, parent_device_name, parent_seq_no "
                         "   FROM ActionLog "
-                        /*"   ORDER BY filename,version"*/, -1, &stmt, 0);
+                        "   ORDER BY action_timestamp", -1, &stmt, 0);
 
     cout.setf(std::ios::left, std::ios::adjustfield);
     cout << ">> ACTION LOG <<" << endl;
diff --git a/gui/chronosharegui.cpp b/gui/chronosharegui.cpp
index f310594..d1c6898 100644
--- a/gui/chronosharegui.cpp
+++ b/gui/chronosharegui.cpp
@@ -93,7 +93,8 @@
   // Alex: this **must** be here, otherwise m_dirPath will be uninitialized
   m_watcher = new FsWatcher (m_dirPath,
                              bind (&Dispatcher::Did_LocalFile_AddOrModify, m_dispatcher, _1),
-                             bind (&Dispatcher::Did_LocalFile_Delete,      m_dispatcher, _1));
+                             bind (&Dispatcher::Did_LocalFile_Delete,      m_dispatcher, _1),
+                             m_dispatcher->GetFileState ());
 }
 
 ChronoShareGui::~ChronoShareGui()
diff --git a/gui/fs-watcher.cc b/gui/fs-watcher.cc
index 94c6b3f..7eff410 100644
--- a/gui/fs-watcher.cc
+++ b/gui/fs-watcher.cc
@@ -35,6 +35,7 @@
 
 FsWatcher::FsWatcher (QString dirPath,
                       LocalFile_Change_Callback onChange, LocalFile_Change_Callback onDelete,
+                      FileState *fileState,
                       QObject* parent)
   : QObject(parent)
   , m_watcher (new QFileSystemWatcher())
@@ -42,6 +43,7 @@
   , m_dirPath (dirPath)
   , m_onChange (onChange)
   , m_onDelete (onDelete)
+  , m_fileState (fileState)
 {
   _LOG_DEBUG ("Monitor dir: " << m_dirPath.toStdString ());
   // add main directory to monitor
@@ -54,8 +56,12 @@
 
   m_scheduler->start ();
 
-  Scheduler::scheduleOneTimeTask (m_scheduler, 0.1,
-                                  bind (&FsWatcher::ScanDirectory_Notify_Execute, this, m_dirPath),
+  Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
+                                  bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, m_dirPath),
+                                  "r-" + m_dirPath.toStdString ()); // only one task will be scheduled per directory
+
+  Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
+                                  bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, m_dirPath),
                                   m_dirPath.toStdString ()); // only one task will be scheduled per directory
 }
 
@@ -69,10 +75,23 @@
 {
   _LOG_DEBUG ("Triggered DirPath: " << dirPath.toStdString ());
 
-  // m_executor.execute (bind (&FsWatcher::ScanDirectory_Notify_Execute, this, dirPath));
-  Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
-                                  bind (&FsWatcher::ScanDirectory_Notify_Execute, this, dirPath),
-                                  dirPath.toStdString ()); // only one task will be scheduled per directory
+  filesystem::path absPathTriggeredDir (dirPath.toStdString ());
+  dirPath.remove (0, m_dirPath.size ());
+
+  filesystem::path triggeredDir (dirPath.toStdString ());
+  if (!filesystem::exists (filesystem::path (absPathTriggeredDir)))
+    {
+      Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
+                                      bind (&FsWatcher::ScanDirectory_NotifyRemovals_Execute, this, dirPath),
+                                      "r-" + dirPath.toStdString ()); // only one task will be scheduled per directory
+    }
+  else
+    {
+      // m_executor.execute (bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath));
+      Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
+                                      bind (&FsWatcher::ScanDirectory_NotifyUpdates_Execute, this, dirPath),
+                                      dirPath.toStdString ()); // only one task will be scheduled per directory
+    }
 }
 
 void
@@ -103,13 +122,15 @@
 
       Scheduler::scheduleOneTimeTask (m_scheduler, 0.5,
                                       bind (m_onDelete, triggeredFile.relative_path ()),
-                                      triggeredFile.relative_path ().string());
+                                      "r-" + triggeredFile.relative_path ().string());
     }
 }
 
 void
-FsWatcher::ScanDirectory_Notify_Execute (QString dirPath)
+FsWatcher::ScanDirectory_NotifyUpdates_Execute (QString dirPath)
 {
+  // _LOG_TRACE (" >> ScanDirectory_NotifyUpdates_Execute");
+
   // exclude working only on last component, not the full path; iterating through all directories, even excluded from monitoring
   QRegExp exclude ("^(\\.|\\.\\.|\\.chronoshare|.*~|.*\\.swp)$");
 
@@ -139,25 +160,6 @@
             {
               DidFileChanged (absFilePath);
             }
-          // // if this is a directory
-          // if(fileInfo.isDir())
-          //   {
-          //     QStringList dirList = m_watcher->directories();
-
-          //     // if the directory is not already being watched
-          //     if (absFilePath.startsWith(m_dirPath) && !dirList.contains(absFilePath))
-          //       {
-          //         _LOG_DEBUG ("Add new dir to watchlist: " << absFilePath.toStdString ());
-          //         // add this directory to the watch list
-          //         m_watcher->addPath(absFilePath);
-          //       }
-          //   }
-          // else
-          //   {
-          //     _LOG_DEBUG ("Found file: " << absFilePath.toStdString ());
-          //     // add this file to the file list
-          //     // currentState.insert(absFilePath, fileInfo.created().toMSecsSinceEpoch());
-          //   }
         }
       else
         {
@@ -166,6 +168,31 @@
     }
 }
 
+
+void
+FsWatcher::ScanDirectory_NotifyRemovals_Execute (QString dirPath)
+{
+  _LOG_DEBUG ("Triggered DirPath: " << dirPath.toStdString ());
+
+  filesystem::path absPathTriggeredDir (dirPath.toStdString ());
+  dirPath.remove (0, m_dirPath.size ());
+
+  filesystem::path triggeredDir (dirPath.toStdString ());
+
+  FileItemsPtr files = m_fileState->LookupFilesInFolderRecursively (triggeredDir.generic_string ());
+  for (std::list<FileItem>::iterator file = files->begin (); file != files->end (); file ++)
+    {
+      filesystem::path testFile = filesystem::path (m_dirPath.toStdString ()) / file->filename ();
+      _LOG_DEBUG ("Check file for deletion [" << testFile.generic_string () << "]");
+
+      if (!filesystem::exists (testFile))
+        {
+          _LOG_DEBUG ("Notifying about removed file [" << file->filename () << "]");
+          m_onDelete (file->filename ());
+        }
+    }
+}
+
 // std::vector<sEventInfo> FsWatcher::reconcileDirectory(QHash<QString, qint64> currentState, QString dirPath)
 // {
 //   // list of files changed
diff --git a/gui/fs-watcher.h b/gui/fs-watcher.h
index 109e8da..b541382 100644
--- a/gui/fs-watcher.h
+++ b/gui/fs-watcher.h
@@ -27,6 +27,7 @@
 #include <boost/filesystem.hpp>
 
 #include "scheduler.h"
+#include "file-state.h"
 
 class FsWatcher : public QObject
 {
@@ -38,6 +39,7 @@
   // constructor
   FsWatcher (QString dirPath,
              LocalFile_Change_Callback onChange, LocalFile_Change_Callback onDelete,
+             FileState *fileState,
              QObject* parent = 0);
 
   // destructor
@@ -59,7 +61,10 @@
   // handle callback from the watcher
   // scan directory and notify callback about any file changes
   void
-  ScanDirectory_Notify_Execute (QString dirPath);
+  ScanDirectory_NotifyUpdates_Execute (QString dirPath);
+
+  void
+  ScanDirectory_NotifyRemovals_Execute (QString dirPath);
 
   // // reconcile directory, find changes
   // std::vector<sEventInfo>
@@ -79,6 +84,8 @@
 
   LocalFile_Change_Callback m_onChange;
   LocalFile_Change_Callback m_onDelete;
+
+  FileState *m_fileState;
 };
 
 #endif // FILESYSTEMWATCHER_H
diff --git a/src/action-log.cc b/src/action-log.cc
index 32820b6..2bab7ca 100644
--- a/src/action-log.cc
+++ b/src/action-log.cc
@@ -88,6 +88,7 @@
 CREATE TABLE FileState (                                                \n\
     type        INTEGER NOT NULL, /* 0 - newest, 1 - oldest */          \n\
     filename    TEXT NOT NULL,                                          \n\
+    directory   TEXT,                                                   \n\
     device_name BLOB NOT NULL,                                          \n\
     seq_no      INTEGER NOT NULL,                                       \n\
     file_hash   BLOB NOT NULL,                                          \n\
@@ -104,6 +105,10 @@
 CREATE INDEX FileState_type_file_hash ON FileState (type, file_hash);   \n\
 ";
 
+static void xTrace (void*, const char* q)
+{
+  _LOG_TRACE ("SQLITE: " << q);
+}
 
 ActionLog::ActionLog (Ccnx::CcnxWrapperPtr ccnx, const boost::filesystem::path &path,
                       SyncLogPtr syncLog,
@@ -127,6 +132,21 @@
       BOOST_THROW_EXCEPTION (Error::Db ()
                              << errmsg_info_str ("Cannot create function ``apply_action''"));
     }
+
+
+  res = sqlite3_create_function (m_db, "directory_name", -1, SQLITE_ANY, 0,
+                                     ActionLog::directory_name_xFun,
+                                     0, 0);
+  if (res != SQLITE_OK)
+    {
+      BOOST_THROW_EXCEPTION (Error::Db ()
+                             << errmsg_info_str ("Cannot create function ``directory_name''"));
+    }
+
+  // "Upgrading" database
+  sqlite3_exec (m_db, "ALTER TABLE FileState ADD COLUMN directory TEXT", NULL, NULL, NULL);
+  sqlite3_exec (m_db, "CREATE INDEX FileState_directory ON FileState (directory)", NULL, NULL, NULL);
+  sqlite3_exec (m_db, "UPDATE FileState SET directory = directory_name(filename) WHERE directory IS NULL", NULL, NULL, NULL);
 }
 
 tuple<sqlite3_int64 /*version*/, Ccnx::CcnxCharbufPtr /*device name*/, sqlite3_int64 /*seq_no*/>
@@ -263,7 +283,7 @@
 
   sqlite3_step (stmt);
 
-  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK && sqlite3_errcode (m_db) != SQLITE_ROW, sqlite3_errmsg (m_db));
+  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
 
   sqlite3_finalize (stmt);
 
@@ -314,14 +334,14 @@
   sqlite3_bind_blob  (stmt, 1, device_name->buf (), device_name->length (), SQLITE_TRANSIENT);
   sqlite3_bind_int64 (stmt, 2, seq_no);
   sqlite3_bind_int   (stmt, 3, 1);
-  sqlite3_bind_text  (stmt, 4, filename.c_str (), filename.size (), SQLITE_TRANSIENT);
+  sqlite3_bind_text  (stmt, 4, filename.c_str (), filename.size (), SQLITE_TRANSIENT);  // file
+
   sqlite3_bind_int64 (stmt, 5, version);
   sqlite3_bind_int64 (stmt, 6, action_time);
 
   sqlite3_bind_blob  (stmt, 7, parent_device_name->buf (), parent_device_name->length (), SQLITE_TRANSIENT);
   sqlite3_bind_int64 (stmt, 8, parent_seq_no);
 
-
   ActionItemPtr item = make_shared<ActionItem> ();
   item->set_action (ActionItem::DELETE);
   item->set_filename (filename);
@@ -342,7 +362,7 @@
 
   sqlite3_step (stmt);
 
-  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK && sqlite3_errcode (m_db) != SQLITE_ROW, sqlite3_errmsg (m_db));
+  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
 
   // cout << Ccnx::Name (parent_device_name) << endl;
 
@@ -417,7 +437,7 @@
     {
       _LOG_TRACE ("No action found for name: " << actionName);
     }
-  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK && sqlite3_errcode (m_db) != SQLITE_ROW, sqlite3_errmsg (m_db));
+  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_ROW, sqlite3_errmsg (m_db));
   sqlite3_finalize (stmt);
 
   return retval;
@@ -495,7 +515,7 @@
 
   // if action needs to be applied to file state, the trigger will take care of it
 
-  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK && sqlite3_errcode (m_db) != SQLITE_ROW, sqlite3_errmsg (m_db));
+  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
 
   sqlite3_finalize (stmt);
 
@@ -590,7 +610,7 @@
                           "file_mtime=datetime(?, 'unixepoch'),"
                           "file_ctime=datetime(?, 'unixepoch'),"
                           "file_chmod=?, "
-                          "file_seg_num=? "
+                          "file_seg_num=?, "
                           "WHERE type=0 AND filename=?", -1, &stmt, 0);
 
       sqlite3_bind_blob  (stmt, 1, device_name.buf (), device_name.length (), SQLITE_TRANSIENT);
@@ -605,7 +625,7 @@
 
       sqlite3_step (stmt);
 
-      _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_OK,
+      _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_ROW && sqlite3_errcode (the->m_db) != SQLITE_DONE,
                        sqlite3_errmsg (the->m_db));
 
       sqlite3_finalize (stmt);
@@ -615,9 +635,9 @@
         {
           sqlite3_stmt *stmt;
           sqlite3_prepare_v2 (the->m_db, "INSERT INTO FileState "
-                              "(type,filename,device_name,seq_no,file_hash,file_atime,file_mtime,file_ctime,file_chmod,file_seg_num) "
+                              "(type,filename,device_name,seq_no,file_hash,file_atime,file_mtime,file_ctime,file_chmod,file_seg_num,directory) "
                               "VALUES (0, ?, ?, ?, ?, "
-                              "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?)", -1, &stmt, 0);
+                              "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?, directory_name(?))", -1, &stmt, 0);
 
           sqlite3_bind_text  (stmt, 1, filename.c_str (), -1, SQLITE_TRANSIENT);
           sqlite3_bind_blob  (stmt, 2, device_name.buf (), device_name.length (), SQLITE_TRANSIENT);
@@ -628,9 +648,10 @@
           sqlite3_bind_int64 (stmt, 7, ctime);
           sqlite3_bind_int   (stmt, 8, mode);
           sqlite3_bind_int   (stmt, 9, seg_num);
+          sqlite3_bind_text  (stmt, 10, filename.c_str (), -1, SQLITE_TRANSIENT);
 
           sqlite3_step (stmt);
-          _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_OK,
+          _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_DONE,
                            sqlite3_errmsg (the->m_db));
           sqlite3_finalize (stmt);
         }
@@ -711,3 +732,118 @@
 
   return retval;
 }
+
+
+FileItemsPtr
+ActionLog::LookupFilesInFolder (const std::string &folder)
+{
+  sqlite3_stmt *stmt;
+  sqlite3_prepare_v2 (m_db,
+                      "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num "
+                      "   FROM FileState "
+                      "   WHERE type = 0 AND directory = ?", -1, &stmt, 0);
+  sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
+
+  FileItemsPtr retval = make_shared<FileItems> ();
+  while (sqlite3_step (stmt) == SQLITE_ROW)
+    {
+      FileItem file;
+      file.set_filename    (reinterpret_cast<const char *> (sqlite3_column_text  (stmt, 0)), sqlite3_column_bytes (stmt, 0));
+      file.set_device_name (sqlite3_column_blob  (stmt, 1), sqlite3_column_bytes (stmt, 1));
+      file.set_seq_no      (sqlite3_column_int64 (stmt, 2));
+      file.set_file_hash   (sqlite3_column_blob  (stmt, 3), sqlite3_column_bytes (stmt, 3));
+      file.set_mtime       (sqlite3_column_int   (stmt, 4));
+      file.set_mode        (sqlite3_column_int   (stmt, 5));
+      file.set_seg_num     (sqlite3_column_int64 (stmt, 6));
+
+      retval->push_back (file);
+    }
+
+  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
+
+  sqlite3_finalize (stmt);
+
+  return retval;
+}
+
+FileItemsPtr
+ActionLog::LookupFilesInFolderRecursively (const std::string &folder)
+{
+  // sqlite3_trace(m_db, xTrace, NULL);
+
+  sqlite3_stmt *stmt;
+
+  if (folder != "")
+    {
+      sqlite3_prepare_v2 (m_db,
+                          "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num "
+                          "   FROM FileState "
+                          "   WHERE type = 0 AND (directory = ? OR directory LIKE ?)", -1, &stmt, 0);
+      sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
+
+      ostringstream escapedFolder;
+      for (string::const_iterator ch = folder.begin (); ch != folder.end (); ch ++)
+        {
+          if (*ch == '%')
+            escapedFolder << "\\%";
+          else
+            escapedFolder << *ch;
+        }
+      escapedFolder << "/" << "%";
+      string escapedFolderStr = escapedFolder.str ();
+      sqlite3_bind_text (stmt, 2, escapedFolderStr.c_str (), escapedFolderStr.size (), SQLITE_STATIC);
+    }
+  else
+    {
+      sqlite3_prepare_v2 (m_db,
+                          "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num "
+                          "   FROM FileState "
+                          "   WHERE type = 0", -1, &stmt, 0);
+    }
+
+  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+
+  FileItemsPtr retval = make_shared<FileItems> ();
+  while (sqlite3_step (stmt) == SQLITE_ROW)
+    {
+      FileItem file;
+      file.set_filename    (reinterpret_cast<const char *> (sqlite3_column_text  (stmt, 0)), sqlite3_column_bytes (stmt, 0));
+      file.set_device_name (sqlite3_column_blob  (stmt, 1), sqlite3_column_bytes (stmt, 1));
+      file.set_seq_no      (sqlite3_column_int64 (stmt, 2));
+      file.set_file_hash   (sqlite3_column_blob  (stmt, 3), sqlite3_column_bytes (stmt, 3));
+      file.set_mtime       (sqlite3_column_int   (stmt, 4));
+      file.set_mode        (sqlite3_column_int   (stmt, 5));
+      file.set_seg_num     (sqlite3_column_int64 (stmt, 6));
+
+      retval->push_back (file);
+    }
+
+  _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
+
+  sqlite3_finalize (stmt);
+
+  return retval;
+}
+
+void
+ActionLog::ActionLog::directory_name_xFun (sqlite3_context *context, int argc, sqlite3_value **argv)
+{
+  if (argc != 1)
+    {
+      sqlite3_result_error (context, "``directory_name'' expects 1 text argument", -1);
+      sqlite3_result_null (context);
+      return;
+    }
+
+  if (sqlite3_value_bytes (argv[0]) == 0)
+    {
+      sqlite3_result_null (context);
+      return;
+    }
+
+  filesystem::path filePath (string (reinterpret_cast<const char*> (sqlite3_value_text (argv[0])), sqlite3_value_bytes (argv[0])));
+  string dirPath = filePath.parent_path ().generic_string ();
+
+  sqlite3_result_text (context, dirPath.c_str (), dirPath.size (), SQLITE_TRANSIENT);
+}
+
diff --git a/src/action-log.h b/src/action-log.h
index 9833b92..f28a398 100644
--- a/src/action-log.h
+++ b/src/action-log.h
@@ -23,6 +23,7 @@
 #define ACTION_LOG_H
 
 #include "db-helper.h"
+#include "file-state.h"
 #include "sync-log.h"
 #include "action-item.pb.h"
 #include "file-item.pb.h"
@@ -35,11 +36,8 @@
 typedef boost::shared_ptr<ActionLog> ActionLogPtr;
 typedef boost::shared_ptr<ActionItem> ActionItemPtr;
 
-typedef std::list<FileItem> FileItems;
-typedef boost::shared_ptr<FileItem>  FileItemPtr;
-typedef boost::shared_ptr<FileItems> FileItemsPtr;
-
 class ActionLog : public DbHelper
+                , public FileState
 {
 public:
   typedef boost::function<void (std::string /*filename*/, Ccnx::Name /*device_name*/, sqlite3_int64 /*seq_no*/,
@@ -53,6 +51,8 @@
              const std::string &sharedFolder,
              OnFileAddedOrChangedCallback onFileAddedOrChanged, OnFileRemovedCallback onFileRemoved);
 
+  virtual ~ActionLog () { }
+
   //////////////////////////
   // Local operations     //
   //////////////////////////
@@ -103,12 +103,18 @@
   ///////////////////////////
   // File state operations //
   ///////////////////////////
-  FileItemPtr
+  virtual FileItemPtr
   LookupFile (const std::string &filename);
 
-  FileItemsPtr
+  virtual FileItemsPtr
   LookupFilesForHash (const Hash &hash);
 
+  virtual FileItemsPtr
+  LookupFilesInFolder (const std::string &folder);
+
+  virtual FileItemsPtr
+  LookupFilesInFolderRecursively (const std::string &folder);
+
 public:
   // for test purposes
   sqlite3_int64
@@ -121,6 +127,9 @@
   static void
   apply_action_xFun (sqlite3_context *context, int argc, sqlite3_value **argv);
 
+  static void
+  directory_name_xFun (sqlite3_context *context, int argc, sqlite3_value **argv);
+
 private:
   SyncLogPtr m_syncLog;
 
diff --git a/src/dispatcher.cc b/src/dispatcher.cc
index 03de6a9..cc677b6 100644
--- a/src/dispatcher.cc
+++ b/src/dispatcher.cc
@@ -118,6 +118,12 @@
   m_server->deregisterPrefix(oldLocalPrefix);
 }
 
+void
+Dispatcher::Restore_LocalFile (FileItemPtr file)
+{
+  m_executor.execute (bind (&Dispatcher::Restore_LocalFile_Execute, this, file));
+}
+
 /////////////////////////////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////////////////////////////
 /////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -387,3 +393,29 @@
       permissions (filePath, static_cast<filesystem::perms> (file->mode ()));
     }
 }
+
+void
+Dispatcher::Restore_LocalFile_Execute (FileItemPtr file)
+{
+  _LOG_DEBUG ("Got request to restore local file [" << file->filename () << "]");
+  // the rest will gracefully fail if object-db is missing or incomplete
+
+  boost::filesystem::path filePath = m_rootDir / file->filename ();
+  Name deviceName (file->device_name ().c_str (), file->device_name ().size ());
+  Hash hash (file->file_hash ().c_str (), file->file_hash ().size ());
+
+  if (filesystem::exists (filePath) &&
+      filesystem::last_write_time (filePath) == file->mtime () &&
+      filesystem::status (filePath).permissions () == static_cast<filesystem::perms> (file->mode ()) &&
+      *Hash::FromFileContent (filePath) == hash)
+    {
+      _LOG_DEBUG ("Asking to assemble a file, but file already exists on a filesystem");
+      return;
+    }
+
+  m_objectManager.objectsToLocalFile (deviceName, hash, filePath);
+
+  last_write_time (filePath, file->mtime ());
+  permissions (filePath, static_cast<filesystem::perms> (file->mode ()));
+
+}
diff --git a/src/dispatcher.h b/src/dispatcher.h
index b2b1e5c..100123f 100644
--- a/src/dispatcher.h
+++ b/src/dispatcher.h
@@ -64,10 +64,22 @@
   void
   Did_LocalFile_Delete (const boost::filesystem::path &relativeFilepath);
 
+  /**
+   * @brief Invoked when FileState is detected to have a file which does not exist on a file system
+   */
+  void
+  Restore_LocalFile (FileItemPtr file);
+
+  void
+  Restore_LocalFile_Execute (FileItemPtr file);
+
   // for test
   HashPtr
   SyncRoot() { return m_core->root(); }
 
+  FileState *
+  GetFileState () { return m_actionLog.get (); }
+
 private:
   void
   Did_LocalFile_AddOrModify_Execute (boost::filesystem::path relativeFilepath); // cannot be const & for Execute event!!! otherwise there will be segfault
diff --git a/src/file-state.cc b/src/file-state.cc
new file mode 100644
index 0000000..343d6a5
--- /dev/null
+++ b/src/file-state.cc
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ *	   Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ */
+
+#include "file-state.h"
diff --git a/src/file-state.h b/src/file-state.h
new file mode 100644
index 0000000..c7396f4
--- /dev/null
+++ b/src/file-state.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
+/*
+ * Copyright (c) 2012-2013 University of California, Los Angeles
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation;
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
+ *	   Zhenkai Zhu <zhenkai@cs.ucla.edu>
+ */
+
+#ifndef FILE_STATE_H
+#define FILE_STATE_H
+
+#include "file-item.pb.h"
+#include "hash-helper.h"
+
+#include <boost/tuple/tuple.hpp>
+#include <boost/exception/all.hpp>
+
+#include <list>
+
+typedef std::list<FileItem> FileItems;
+typedef boost::shared_ptr<FileItem>  FileItemPtr;
+typedef boost::shared_ptr<FileItems> FileItemsPtr;
+
+
+class FileState
+{
+public:
+  virtual FileItemPtr
+  LookupFile (const std::string &filename) = 0;
+
+  virtual FileItemsPtr
+  LookupFilesForHash (const Hash &hash) = 0;
+
+  virtual FileItemsPtr
+  LookupFilesInFolder (const std::string &folder) = 0;
+
+  virtual FileItemsPtr
+  LookupFilesInFolderRecursively (const std::string &folder) = 0;
+};
+
+namespace Error {
+struct FileState : virtual boost::exception, virtual std::exception { };
+}
+
+
+#endif // ACTION_LOG_H
diff --git a/src/sync-log.cc b/src/sync-log.cc
index 4298a9e..06eb1e7 100644
--- a/src/sync-log.cc
+++ b/src/sync-log.cc
@@ -32,7 +32,7 @@
 using namespace std;
 using namespace Ccnx;
 
-void xTrace (void*, const char* q)
+static  void xTrace (void*, const char* q)
 {
   cout << q << endl;
 }