Merge "timed-wait for fetcher in hope to solve crash problem"
diff --git a/src/action-log.cc b/src/action-log.cc
index 85a691c..19c5c9d 100644
--- a/src/action-log.cc
+++ b/src/action-log.cc
@@ -31,7 +31,6 @@
INIT_LOGGER ("ActionLog");
const std::string INIT_DATABASE = "\
- PRAGMA foreign_keys = OFF; /* foreign key here are very bad idea...*/ \n\
CREATE TABLE ActionLog ( \n\
device_name BLOB NOT NULL, \n\
seq_no INTEGER NOT NULL, \n\
@@ -80,32 +79,12 @@
strftime('%s', NEW.file_atime),strftime('%s', NEW.file_mtime),strftime('%s', NEW.file_ctime), \
NEW.file_chmod, NEW.file_seg_num); /* function that applies action and adds record the FileState */ \n \
END; \n\
- \n\
-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\
- file_atime TIMESTAMP, \n\
- file_mtime TIMESTAMP, \n\
- file_ctime TIMESTAMP, \n\
- file_chmod INTEGER, \n\
- file_seg_num INTEGER, \n\
- is_complete INTEGER, \n\
- \n\
- PRIMARY KEY (type, filename) \n\
-); \n\
- \n\
-CREATE INDEX FileState_device_name_seq_no ON FileState (device_name, seq_no); \n\
-CREATE INDEX FileState_type_file_hash ON FileState (type, file_hash); \n\
";
-static void xTrace (void*, const char* q)
-{
- _LOG_TRACE ("SQLITE: " << q);
-}
+// static void xTrace (void*, const char* q)
+// {
+// _LOG_TRACE ("SQLITE: " << q);
+// }
ActionLog::ActionLog (Ccnx::CcnxWrapperPtr ccnx, const boost::filesystem::path &path,
SyncLogPtr syncLog,
@@ -134,29 +113,7 @@
<< 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);
- // _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
-
- // sqlite3_exec (m_db, "CREATE INDEX FileState_directory ON FileState (directory)", NULL, NULL, NULL);
- // _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
-
- // sqlite3_exec (m_db, "UPDATE FileState SET directory = directory_name(filename) WHERE directory IS NULL", NULL, NULL, NULL);
- // _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
-
- // // Another "upgrade"
- // sqlite3_exec (m_db, "BEGIN TRANSACTION; ALTER TABLE FileState ADD COLUMN is_complete INTEGER; UPDATE FileState SET is_complete = 1; END TRANSACTION;", NULL, NULL, NULL);
- // _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+ m_fileState = make_shared<FileState> (path);
}
tuple<sqlite3_int64 /*version*/, Ccnx::CcnxCharbufPtr /*device name*/, sqlite3_int64 /*seq_no*/>
@@ -300,11 +257,11 @@
sqlite3_finalize (stmt);
- // set complete for local file
- SetFileComplete(filename);
-
sqlite3_exec (m_db, "END TRANSACTION;", 0,0,0);
+ // set complete for local file
+ m_fileState->SetFileComplete(filename);
+
return item;
}
@@ -601,9 +558,8 @@
return retval;
}
-
///////////////////////////////////////////////////////////////////////////////////
-// SHOULD BE MOVED TO SEPARATE FILESTATE CLASS "EVENTUALLY"
+///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
void
@@ -638,85 +594,13 @@
_LOG_DEBUG ("Update " << filename << " " << atime << " " << mtime << " " << ctime << " " << hash);
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2 (the->m_db, "UPDATE FileState "
- "SET "
- "device_name=?, seq_no=?, "
- "file_hash=?,"
- "file_atime=datetime(?, 'unixepoch'),"
- "file_mtime=datetime(?, 'unixepoch'),"
- "file_ctime=datetime(?, 'unixepoch'),"
- "file_chmod=?, "
- "file_seg_num=? "
- "WHERE type=0 AND filename=?", -1, &stmt, 0);
+ the->m_fileState->UpdateFile (filename, hash, device_name, seq_no, atime, mtime, ctime, mode, seg_num);
- sqlite3_bind_blob (stmt, 1, device_name.buf (), device_name.length (), SQLITE_TRANSIENT);
- sqlite3_bind_int64 (stmt, 2, seq_no);
- sqlite3_bind_blob (stmt, 3, hash.GetHash (), hash.GetHashBytes (), SQLITE_TRANSIENT);
- sqlite3_bind_int64 (stmt, 4, atime);
- sqlite3_bind_int64 (stmt, 5, mtime);
- sqlite3_bind_int64 (stmt, 6, ctime);
- sqlite3_bind_int (stmt, 7, mode);
- sqlite3_bind_int (stmt, 8, seg_num);
- sqlite3_bind_text (stmt, 9, filename.c_str (), -1, SQLITE_TRANSIENT);
-
- sqlite3_step (stmt);
-
- _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);
-
- int affected_rows = sqlite3_changes (the->m_db);
- if (affected_rows == 0) // file didn't exist
- {
- 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) "
- "VALUES (0, ?, ?, ?, ?, "
- "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?)", -1, &stmt, 0);
-
- _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_OK, sqlite3_errmsg (the->m_db));
-
- sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
- sqlite3_bind_blob (stmt, 2, device_name.buf (), device_name.length (), SQLITE_STATIC);
- sqlite3_bind_int64 (stmt, 3, seq_no);
- sqlite3_bind_blob (stmt, 4, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
- sqlite3_bind_int64 (stmt, 5, atime);
- sqlite3_bind_int64 (stmt, 6, mtime);
- 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_STATIC);
-
- sqlite3_step (stmt);
- _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_DONE,
- sqlite3_errmsg (the->m_db));
- sqlite3_finalize (stmt);
-
- sqlite3_prepare_v2 (the->m_db, "UPDATE FileState SET directory=directory_name(filename) WHERE filename=?", -1, &stmt, 0);
- _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_OK, sqlite3_errmsg (the->m_db));
-
- sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
- sqlite3_step (stmt);
- _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_DONE,
- sqlite3_errmsg (the->m_db));
- sqlite3_finalize (stmt);
-
- }
+ // no callback here
}
else if (action == 1) // delete
{
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2 (the->m_db, "DELETE FROM FileState WHERE type=0 AND filename=?", -1, &stmt, 0);
- sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
-
- _LOG_DEBUG ("Delete " << filename);
-
- sqlite3_step (stmt);
- _LOG_DEBUG_COND (sqlite3_errcode (the->m_db) != SQLITE_DONE,
- sqlite3_errmsg (the->m_db));
- sqlite3_finalize (stmt);
+ the->m_fileState->DeleteFile (filename);
the->m_onFileRemoved (filename);
}
@@ -725,215 +609,3 @@
}
-void
-ActionLog::SetFileComplete (const std::string &filename)
-{
- sqlite3_stmt *stmt;
- sqlite3_prepare_v2 (m_db,
- "UPDATE FileState SET is_complete=1 WHERE type = 0 AND filename = ?", -1, &stmt, 0);
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
- sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
-
- sqlite3_step (stmt);
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
-
- sqlite3_finalize (stmt);
-}
-
-
-/**
- * @todo Implement checking modification time and permissions
- */
-FileItemPtr
-ActionLog::LookupFile (const std::string &filename)
-{
- 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,is_complete "
- " FROM FileState "
- " WHERE type = 0 AND filename = ?", -1, &stmt, 0);
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
- sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
-
- FileItemPtr retval;
- if (sqlite3_step (stmt) == SQLITE_ROW)
- {
- retval = make_shared<FileItem> ();
- retval->set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
- retval->set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
- retval->set_seq_no (sqlite3_column_int64 (stmt, 2));
- retval->set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
- retval->set_mtime (sqlite3_column_int (stmt, 4));
- retval->set_mode (sqlite3_column_int (stmt, 5));
- retval->set_seg_num (sqlite3_column_int64 (stmt, 6));
- retval->set_is_complete (sqlite3_column_int (stmt, 7));
- }
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
- sqlite3_finalize (stmt);
-
- return retval;
-}
-
-FileItemsPtr
-ActionLog::LookupFilesForHash (const Hash &hash)
-{
- 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,is_complete "
- " FROM FileState "
- " WHERE type = 0 AND file_hash = ?", -1, &stmt, 0);
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
- sqlite3_bind_blob(stmt, 1, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
- _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));
- file.set_is_complete (sqlite3_column_int (stmt, 7));
-
- retval->push_back (file);
- }
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
-
- sqlite3_finalize (stmt);
-
- 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,is_complete "
- " FROM FileState "
- " WHERE type = 0 AND directory = ?", -1, &stmt, 0);
- if (folder.size () == 0)
- sqlite3_bind_null (stmt, 1);
- else
- 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));
- file.set_is_complete (sqlite3_column_int (stmt, 7));
-
- 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)
-{
- _LOG_DEBUG ("LookupFilesInFolderRecursively: [" << folder << "]");
-
- 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,is_complete "
- " FROM FileState "
- " WHERE type = 0 AND (directory = ? OR directory LIKE ?)", -1, &stmt, 0);
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
-
- sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
-
- 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);
- _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
- }
- else
- {
- sqlite3_prepare_v2 (m_db,
- "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
- " 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));
- file.set_is_complete (sqlite3_column_int (stmt, 7));
-
- 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 ();
- // _LOG_DEBUG ("directory_name FUN: " << dirPath);
- if (dirPath.size () == 0)
- {
- sqlite3_result_null (context);
- }
- else
- {
- sqlite3_result_text (context, dirPath.c_str (), dirPath.size (), SQLITE_TRANSIENT);
- }
-}
diff --git a/src/action-log.h b/src/action-log.h
index b175001..f0ed9cd 100644
--- a/src/action-log.h
+++ b/src/action-log.h
@@ -37,7 +37,6 @@
typedef boost::shared_ptr<ActionItem> ActionItemPtr;
class ActionLog : public DbHelper
- , public FileState
{
public:
typedef boost::function<void (std::string /*filename*/, Ccnx::Name /*device_name*/, sqlite3_int64 /*seq_no*/,
@@ -100,28 +99,10 @@
ActionItemPtr
LookupAction (const Ccnx::Name &actionName);
- /**
- * @brief Set "complete" flag
- *
- * The call will do nothing if FileState does not have a record for the file (e.g., file got subsequently deleted)
- */
- void
- SetFileComplete (const std::string &filename);
- ///////////////////////////
- // File state operations //
- ///////////////////////////
- virtual FileItemPtr
- LookupFile (const std::string &filename);
-
- virtual FileItemsPtr
- LookupFilesForHash (const Hash &hash);
-
- virtual FileItemsPtr
- LookupFilesInFolder (const std::string &folder);
-
- virtual FileItemsPtr
- LookupFilesInFolderRecursively (const std::string &folder);
+ //
+ inline FileStatePtr
+ GetFileState ();
public:
// for test purposes
@@ -135,11 +116,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;
+ FileStatePtr m_fileState;
Ccnx::CcnxWrapperPtr m_ccnx;
std::string m_sharedFolderName;
@@ -153,5 +132,11 @@
struct ActionLog : virtual boost::exception, virtual std::exception { };
}
+inline FileStatePtr
+ActionLog::GetFileState ()
+{
+ return m_fileState;
+}
+
#endif // ACTION_LOG_H
diff --git a/src/dispatcher.cc b/src/dispatcher.cc
index 22fbdbd..38ba37e 100644
--- a/src/dispatcher.cc
+++ b/src/dispatcher.cc
@@ -60,6 +60,8 @@
// bind (&Dispatcher::Did_ActionLog_ActionApply_AddOrModify, this, _1, _2, _3, _4, _5, _6, _7),
ActionLog::OnFileAddedOrChangedCallback (), // don't really need this callback
bind (&Dispatcher::Did_ActionLog_ActionApply_Delete, this, _1));
+ m_fileState = m_actionLog->GetFileState ();
+
Name syncPrefix = Name(BROADCAST_DOMAIN)(CHRONOSHARE_APP)(sharedFolder);
// m_server needs a different ccnx face
@@ -157,7 +159,7 @@
return;
}
- FileItemPtr currentFile = m_actionLog->LookupFile (relativeFilePath.generic_string ());
+ FileItemPtr currentFile = m_fileState->LookupFile (relativeFilePath.generic_string ());
if (currentFile &&
*Hash::FromFileContent (absolutePath) == Hash (currentFile->file_hash ().c_str (), currentFile->file_hash ().size ())
// The following two are commented out to prevent front end from reporting intermediate files
@@ -214,7 +216,7 @@
return;
}
- FileItemPtr currentFile = m_actionLog->LookupFile (relativeFilePath.generic_string ());
+ FileItemPtr currentFile = m_fileState->LookupFile (relativeFilePath.generic_string ());
if (!currentFile)
{
_LOG_ERROR ("File already deleted [" << relativeFilePath << "]");
@@ -388,7 +390,7 @@
_LOG_ERROR ("no db available for this file: " << hash);
}
- FileItemsPtr filesToAssemble = m_actionLog->LookupFilesForHash (hash);
+ FileItemsPtr filesToAssemble = m_fileState->LookupFilesForHash (hash);
for (FileItems::iterator file = filesToAssemble->begin ();
file != filesToAssemble->end ();
@@ -420,7 +422,7 @@
last_write_time (filePath, file->mtime ());
permissions (filePath, static_cast<filesystem::perms> (file->mode ()));
- m_actionLog->SetFileComplete (file->filename ());
+ m_fileState->SetFileComplete (file->filename ());
}
else
{
diff --git a/src/dispatcher.h b/src/dispatcher.h
index 100123f..902ec62 100644
--- a/src/dispatcher.h
+++ b/src/dispatcher.h
@@ -77,9 +77,6 @@
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
@@ -164,6 +161,7 @@
SyncCore *m_core;
SyncLogPtr m_syncLog;
ActionLogPtr m_actionLog;
+ FileStatePtr m_fileState;
boost::filesystem::path m_rootDir;
Executor m_executor;
diff --git a/src/file-state.cc b/src/file-state.cc
index 343d6a5..e4a2c1b 100644
--- a/src/file-state.cc
+++ b/src/file-state.cc
@@ -20,3 +20,354 @@
*/
#include "file-state.h"
+#include "logging.h"
+
+INIT_LOGGER ("FileState");
+
+using namespace boost;
+using namespace std;
+
+const std::string INIT_DATABASE = "\
+ \n\
+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\
+ file_atime TIMESTAMP, \n\
+ file_mtime TIMESTAMP, \n\
+ file_ctime TIMESTAMP, \n\
+ file_chmod INTEGER, \n\
+ file_seg_num INTEGER, \n\
+ is_complete INTEGER, \n\
+ \n\
+ PRIMARY KEY (type, filename) \n\
+); \n\
+ \n\
+CREATE INDEX FileState_device_name_seq_no ON FileState (device_name, seq_no); \n\
+CREATE INDEX FileState_type_file_hash ON FileState (type, file_hash); \n\
+";
+
+FileState::FileState (const boost::filesystem::path &path)
+ : DbHelper (path / ".chronoshare", "file-state.db")
+{
+ sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, NULL);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+
+ int res = sqlite3_create_function (m_db, "directory_name", -1, SQLITE_ANY, 0,
+ FileState::directory_name_xFun,
+ 0, 0);
+ if (res != SQLITE_OK)
+ {
+ BOOST_THROW_EXCEPTION (Error::Db ()
+ << errmsg_info_str ("Cannot create function ``directory_name''"));
+ }
+}
+
+FileState::~FileState ()
+{
+}
+
+void
+FileState::UpdateFile (const std::string &filename, const Hash &hash, const Ccnx::CcnxCharbuf &device_name, sqlite3_int64 seq_no,
+ time_t atime, time_t mtime, time_t ctime, int mode, int seg_num)
+{
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2 (m_db, "UPDATE FileState "
+ "SET "
+ "device_name=?, seq_no=?, "
+ "file_hash=?,"
+ "file_atime=datetime(?, 'unixepoch'),"
+ "file_mtime=datetime(?, 'unixepoch'),"
+ "file_ctime=datetime(?, 'unixepoch'),"
+ "file_chmod=?, "
+ "file_seg_num=? "
+ "WHERE type=0 AND filename=?", -1, &stmt, 0);
+
+ sqlite3_bind_blob (stmt, 1, device_name.buf (), device_name.length (), SQLITE_TRANSIENT);
+ sqlite3_bind_int64 (stmt, 2, seq_no);
+ sqlite3_bind_blob (stmt, 3, hash.GetHash (), hash.GetHashBytes (), SQLITE_TRANSIENT);
+ sqlite3_bind_int64 (stmt, 4, atime);
+ sqlite3_bind_int64 (stmt, 5, mtime);
+ sqlite3_bind_int64 (stmt, 6, ctime);
+ sqlite3_bind_int (stmt, 7, mode);
+ sqlite3_bind_int (stmt, 8, seg_num);
+ sqlite3_bind_text (stmt, 9, filename.c_str (), -1, SQLITE_TRANSIENT);
+
+ sqlite3_step (stmt);
+
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_ROW && sqlite3_errcode (m_db) != SQLITE_DONE,
+ sqlite3_errmsg (m_db));
+
+ sqlite3_finalize (stmt);
+
+ int affected_rows = sqlite3_changes (m_db);
+ if (affected_rows == 0) // file didn't exist
+ {
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2 (m_db, "INSERT INTO FileState "
+ "(type,filename,device_name,seq_no,file_hash,file_atime,file_mtime,file_ctime,file_chmod,file_seg_num) "
+ "VALUES (0, ?, ?, ?, ?, "
+ "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?)", -1, &stmt, 0);
+
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+
+ sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
+ sqlite3_bind_blob (stmt, 2, device_name.buf (), device_name.length (), SQLITE_STATIC);
+ sqlite3_bind_int64 (stmt, 3, seq_no);
+ sqlite3_bind_blob (stmt, 4, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
+ sqlite3_bind_int64 (stmt, 5, atime);
+ sqlite3_bind_int64 (stmt, 6, mtime);
+ 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_STATIC);
+
+ sqlite3_step (stmt);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
+ sqlite3_errmsg (m_db));
+ sqlite3_finalize (stmt);
+
+ sqlite3_prepare_v2 (m_db, "UPDATE FileState SET directory=directory_name(filename) WHERE filename=?", -1, &stmt, 0);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+
+ sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
+ sqlite3_step (stmt);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
+ sqlite3_errmsg (m_db));
+ sqlite3_finalize (stmt);
+ }
+}
+
+
+void
+FileState::DeleteFile (const std::string &filename)
+{
+
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2 (m_db, "DELETE FROM FileState WHERE type=0 AND filename=?", -1, &stmt, 0);
+ sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
+
+ _LOG_DEBUG ("Delete " << filename);
+
+ sqlite3_step (stmt);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
+ sqlite3_errmsg (m_db));
+ sqlite3_finalize (stmt);
+}
+
+
+void
+FileState::SetFileComplete (const std::string &filename)
+{
+ sqlite3_stmt *stmt;
+ sqlite3_prepare_v2 (m_db,
+ "UPDATE FileState SET is_complete=1 WHERE type = 0 AND filename = ?", -1, &stmt, 0);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+ sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
+
+ sqlite3_step (stmt);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
+
+ sqlite3_finalize (stmt);
+}
+
+
+/**
+ * @todo Implement checking modification time and permissions
+ */
+FileItemPtr
+FileState::LookupFile (const std::string &filename)
+{
+ 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,is_complete "
+ " FROM FileState "
+ " WHERE type = 0 AND filename = ?", -1, &stmt, 0);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+ sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
+
+ FileItemPtr retval;
+ if (sqlite3_step (stmt) == SQLITE_ROW)
+ {
+ retval = make_shared<FileItem> ();
+ retval->set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
+ retval->set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
+ retval->set_seq_no (sqlite3_column_int64 (stmt, 2));
+ retval->set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
+ retval->set_mtime (sqlite3_column_int (stmt, 4));
+ retval->set_mode (sqlite3_column_int (stmt, 5));
+ retval->set_seg_num (sqlite3_column_int64 (stmt, 6));
+ retval->set_is_complete (sqlite3_column_int (stmt, 7));
+ }
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
+ sqlite3_finalize (stmt);
+
+ return retval;
+}
+
+FileItemsPtr
+FileState::LookupFilesForHash (const Hash &hash)
+{
+ 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,is_complete "
+ " FROM FileState "
+ " WHERE type = 0 AND file_hash = ?", -1, &stmt, 0);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+ sqlite3_bind_blob(stmt, 1, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
+ _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));
+ file.set_is_complete (sqlite3_column_int (stmt, 7));
+
+ retval->push_back (file);
+ }
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
+
+ sqlite3_finalize (stmt);
+
+ return retval;
+}
+
+
+FileItemsPtr
+FileState::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,is_complete "
+ " FROM FileState "
+ " WHERE type = 0 AND directory = ?", -1, &stmt, 0);
+ if (folder.size () == 0)
+ sqlite3_bind_null (stmt, 1);
+ else
+ 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));
+ file.set_is_complete (sqlite3_column_int (stmt, 7));
+
+ retval->push_back (file);
+ }
+
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
+
+ sqlite3_finalize (stmt);
+
+ return retval;
+}
+
+FileItemsPtr
+FileState::LookupFilesInFolderRecursively (const std::string &folder)
+{
+ _LOG_DEBUG ("LookupFilesInFolderRecursively: [" << folder << "]");
+
+ 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,is_complete "
+ " FROM FileState "
+ " WHERE type = 0 AND (directory = ? OR directory LIKE ?)", -1, &stmt, 0);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+
+ sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+
+ 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);
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
+ }
+ else
+ {
+ sqlite3_prepare_v2 (m_db,
+ "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
+ " 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));
+ file.set_is_complete (sqlite3_column_int (stmt, 7));
+
+ retval->push_back (file);
+ }
+
+ _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
+
+ sqlite3_finalize (stmt);
+
+ return retval;
+}
+
+void
+FileState::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 ();
+ // _LOG_DEBUG ("directory_name FUN: " << dirPath);
+ if (dirPath.size () == 0)
+ {
+ sqlite3_result_null (context);
+ }
+ else
+ {
+ sqlite3_result_text (context, dirPath.c_str (), dirPath.size (), SQLITE_TRANSIENT);
+ }
+}
diff --git a/src/file-state.h b/src/file-state.h
index c7396f4..91bb3d2 100644
--- a/src/file-state.h
+++ b/src/file-state.h
@@ -22,6 +22,9 @@
#ifndef FILE_STATE_H
#define FILE_STATE_H
+#include "db-helper.h"
+
+#include "ccnx-name.h"
#include "file-item.pb.h"
#include "hash-helper.h"
@@ -35,22 +38,64 @@
typedef boost::shared_ptr<FileItems> FileItemsPtr;
-class FileState
+class FileState : public DbHelper
{
public:
- virtual FileItemPtr
- LookupFile (const std::string &filename) = 0;
+ FileState (const boost::filesystem::path &path);
+ ~FileState ();
- virtual FileItemsPtr
- LookupFilesForHash (const Hash &hash) = 0;
+ /**
+ * @brief Update or add a file
+ */
+ void
+ UpdateFile (const std::string &filename, const Hash &hash, const Ccnx::CcnxCharbuf &device_name, sqlite3_int64 seqno,
+ time_t atime, time_t mtime, time_t ctime, int mode, int seg_num);
- virtual FileItemsPtr
- LookupFilesInFolder (const std::string &folder) = 0;
+ /**
+ * @brief Delete file
+ */
+ void
+ DeleteFile (const std::string &filename);
- virtual FileItemsPtr
- LookupFilesInFolderRecursively (const std::string &folder) = 0;
+ /**
+ * @brief Set "complete" flag
+ *
+ * The call will do nothing if FileState does not have a record for the file (e.g., file got subsequently deleted)
+ */
+ void
+ SetFileComplete (const std::string &filename);
+
+ /**
+ * @brief Lookup file state using file name
+ */
+ FileItemPtr
+ LookupFile (const std::string &filename) ;
+
+ /**
+ * @brief Lookup file state using content hash (multiple items may be returned)
+ */
+ FileItemsPtr
+ LookupFilesForHash (const Hash &hash);
+
+ /**
+ * @brief Lookup all files in the specified folder
+ */
+ FileItemsPtr
+ LookupFilesInFolder (const std::string &folder);
+
+ /**
+ * @brief Recursively lookup all files in the specified folder
+ */
+ FileItemsPtr
+ LookupFilesInFolderRecursively (const std::string &folder);
+
+private:
+ static void
+ directory_name_xFun (sqlite3_context *context, int argc, sqlite3_value **argv);
};
+typedef boost::shared_ptr<FileState> FileStatePtr;
+
namespace Error {
struct FileState : virtual boost::exception, virtual std::exception { };
}
diff --git a/src/sync-log.cc b/src/sync-log.cc
index 3ce8cfe..74506c6 100644
--- a/src/sync-log.cc
+++ b/src/sync-log.cc
@@ -124,26 +124,6 @@
sqlite3_finalize (stmt);
}
-// void
-// SyncLog::initYP(map<Name, Name> &yp)
-// {
-// sqlite3_stmt *stmt;
-// sqlite3_prepare_v2(m_db, "SELECT device_name, last_known_locator FROM SyncNodes;", -1, &stmt, 0);
-
-// while (sqlite3_step(stmt) == SQLITE_ROW)
-// {
-// Name deviceName((const unsigned char *)sqlite3_column_blob(stmt, 0), sqlite3_column_bytes(stmt, 0));
-// Name locator;
-// if (sqlite3_column_type(stmt, 1) == SQLITE_BLOB)
-// {
-// locator = Name((const unsigned char *)sqlite3_column_blob(stmt, 1), sqlite3_column_bytes(stmt, 1));
-// }
-// yp.insert(make_pair(deviceName, locator));
-// }
-
-// sqlite3_finalize(stmt);
-// }
-
sqlite3_int64
SyncLog::GetNextLocalSeqNo ()
{
@@ -371,7 +351,7 @@
SyncLog::UpdateLocator(const Name &deviceName, const Name &locator)
{
sqlite3_stmt *stmt;
- sqlite3_prepare_v2 (m_db, "UPDATE SyncNodes SET last_known_locator=? WHERE device_name=?;", -1, &stmt, 0);
+ sqlite3_prepare_v2 (m_db, "UPDATE SyncNodes SET last_known_locator=?,last_update=datetime('now') WHERE device_name=?;", -1, &stmt, 0);
Ccnx::CcnxCharbufPtr nameBuf = deviceName;
Ccnx::CcnxCharbufPtr locatorBuf = locator;
sqlite3_bind_blob (stmt, 1, locatorBuf->buf(), locatorBuf->length(), SQLITE_STATIC);