blob: e4a2c1b6f3934e3a4e145b5b1927f2363372db88 [file] [log] [blame]
Alexander Afanasyev0a30a0c2013-01-29 17:25:42 -08001/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2012-2013 University of California, Los Angeles
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
19 * Zhenkai Zhu <zhenkai@cs.ucla.edu>
20 */
21
22#include "file-state.h"
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080023#include "logging.h"
24
25INIT_LOGGER ("FileState");
26
27using namespace boost;
28using namespace std;
29
30const std::string INIT_DATABASE = "\
31 \n\
32CREATE TABLE FileState ( \n\
33 type INTEGER NOT NULL, /* 0 - newest, 1 - oldest */ \n\
34 filename TEXT NOT NULL, \n\
35 directory TEXT, \n\
36 device_name BLOB NOT NULL, \n\
37 seq_no INTEGER NOT NULL, \n\
38 file_hash BLOB NOT NULL, \n\
39 file_atime TIMESTAMP, \n\
40 file_mtime TIMESTAMP, \n\
41 file_ctime TIMESTAMP, \n\
42 file_chmod INTEGER, \n\
43 file_seg_num INTEGER, \n\
44 is_complete INTEGER, \n\
45 \n\
46 PRIMARY KEY (type, filename) \n\
47); \n\
48 \n\
49CREATE INDEX FileState_device_name_seq_no ON FileState (device_name, seq_no); \n\
50CREATE INDEX FileState_type_file_hash ON FileState (type, file_hash); \n\
51";
52
53FileState::FileState (const boost::filesystem::path &path)
54 : DbHelper (path / ".chronoshare", "file-state.db")
55{
56 sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, NULL);
57 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
58
59 int res = sqlite3_create_function (m_db, "directory_name", -1, SQLITE_ANY, 0,
60 FileState::directory_name_xFun,
61 0, 0);
62 if (res != SQLITE_OK)
63 {
64 BOOST_THROW_EXCEPTION (Error::Db ()
65 << errmsg_info_str ("Cannot create function ``directory_name''"));
66 }
67}
68
69FileState::~FileState ()
70{
71}
72
73void
74FileState::UpdateFile (const std::string &filename, const Hash &hash, const Ccnx::CcnxCharbuf &device_name, sqlite3_int64 seq_no,
75 time_t atime, time_t mtime, time_t ctime, int mode, int seg_num)
76{
77 sqlite3_stmt *stmt;
78 sqlite3_prepare_v2 (m_db, "UPDATE FileState "
79 "SET "
80 "device_name=?, seq_no=?, "
81 "file_hash=?,"
82 "file_atime=datetime(?, 'unixepoch'),"
83 "file_mtime=datetime(?, 'unixepoch'),"
84 "file_ctime=datetime(?, 'unixepoch'),"
85 "file_chmod=?, "
86 "file_seg_num=? "
87 "WHERE type=0 AND filename=?", -1, &stmt, 0);
88
89 sqlite3_bind_blob (stmt, 1, device_name.buf (), device_name.length (), SQLITE_TRANSIENT);
90 sqlite3_bind_int64 (stmt, 2, seq_no);
91 sqlite3_bind_blob (stmt, 3, hash.GetHash (), hash.GetHashBytes (), SQLITE_TRANSIENT);
92 sqlite3_bind_int64 (stmt, 4, atime);
93 sqlite3_bind_int64 (stmt, 5, mtime);
94 sqlite3_bind_int64 (stmt, 6, ctime);
95 sqlite3_bind_int (stmt, 7, mode);
96 sqlite3_bind_int (stmt, 8, seg_num);
97 sqlite3_bind_text (stmt, 9, filename.c_str (), -1, SQLITE_TRANSIENT);
98
99 sqlite3_step (stmt);
100
101 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_ROW && sqlite3_errcode (m_db) != SQLITE_DONE,
102 sqlite3_errmsg (m_db));
103
104 sqlite3_finalize (stmt);
105
106 int affected_rows = sqlite3_changes (m_db);
107 if (affected_rows == 0) // file didn't exist
108 {
109 sqlite3_stmt *stmt;
110 sqlite3_prepare_v2 (m_db, "INSERT INTO FileState "
111 "(type,filename,device_name,seq_no,file_hash,file_atime,file_mtime,file_ctime,file_chmod,file_seg_num) "
112 "VALUES (0, ?, ?, ?, ?, "
113 "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?)", -1, &stmt, 0);
114
115 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
116
117 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
118 sqlite3_bind_blob (stmt, 2, device_name.buf (), device_name.length (), SQLITE_STATIC);
119 sqlite3_bind_int64 (stmt, 3, seq_no);
120 sqlite3_bind_blob (stmt, 4, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
121 sqlite3_bind_int64 (stmt, 5, atime);
122 sqlite3_bind_int64 (stmt, 6, mtime);
123 sqlite3_bind_int64 (stmt, 7, ctime);
124 sqlite3_bind_int (stmt, 8, mode);
125 sqlite3_bind_int (stmt, 9, seg_num);
126 // sqlite3_bind_text (stmt, 10, filename.c_str (), -1, SQLITE_STATIC);
127
128 sqlite3_step (stmt);
129 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
130 sqlite3_errmsg (m_db));
131 sqlite3_finalize (stmt);
132
133 sqlite3_prepare_v2 (m_db, "UPDATE FileState SET directory=directory_name(filename) WHERE filename=?", -1, &stmt, 0);
134 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
135
136 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
137 sqlite3_step (stmt);
138 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
139 sqlite3_errmsg (m_db));
140 sqlite3_finalize (stmt);
141 }
142}
143
144
145void
146FileState::DeleteFile (const std::string &filename)
147{
148
149 sqlite3_stmt *stmt;
150 sqlite3_prepare_v2 (m_db, "DELETE FROM FileState WHERE type=0 AND filename=?", -1, &stmt, 0);
151 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
152
153 _LOG_DEBUG ("Delete " << filename);
154
155 sqlite3_step (stmt);
156 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
157 sqlite3_errmsg (m_db));
158 sqlite3_finalize (stmt);
159}
160
161
162void
163FileState::SetFileComplete (const std::string &filename)
164{
165 sqlite3_stmt *stmt;
166 sqlite3_prepare_v2 (m_db,
167 "UPDATE FileState SET is_complete=1 WHERE type = 0 AND filename = ?", -1, &stmt, 0);
168 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
169 sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
170
171 sqlite3_step (stmt);
172 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
173
174 sqlite3_finalize (stmt);
175}
176
177
178/**
179 * @todo Implement checking modification time and permissions
180 */
181FileItemPtr
182FileState::LookupFile (const std::string &filename)
183{
184 sqlite3_stmt *stmt;
185 sqlite3_prepare_v2 (m_db,
186 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
187 " FROM FileState "
188 " WHERE type = 0 AND filename = ?", -1, &stmt, 0);
189 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
190 sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
191
192 FileItemPtr retval;
193 if (sqlite3_step (stmt) == SQLITE_ROW)
194 {
195 retval = make_shared<FileItem> ();
196 retval->set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
197 retval->set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
198 retval->set_seq_no (sqlite3_column_int64 (stmt, 2));
199 retval->set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
200 retval->set_mtime (sqlite3_column_int (stmt, 4));
201 retval->set_mode (sqlite3_column_int (stmt, 5));
202 retval->set_seg_num (sqlite3_column_int64 (stmt, 6));
203 retval->set_is_complete (sqlite3_column_int (stmt, 7));
204 }
205 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
206 sqlite3_finalize (stmt);
207
208 return retval;
209}
210
211FileItemsPtr
212FileState::LookupFilesForHash (const Hash &hash)
213{
214 sqlite3_stmt *stmt;
215 sqlite3_prepare_v2 (m_db,
216 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
217 " FROM FileState "
218 " WHERE type = 0 AND file_hash = ?", -1, &stmt, 0);
219 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
220 sqlite3_bind_blob(stmt, 1, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
221 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
222
223 FileItemsPtr retval = make_shared<FileItems> ();
224 while (sqlite3_step (stmt) == SQLITE_ROW)
225 {
226 FileItem file;
227 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
228 file.set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
229 file.set_seq_no (sqlite3_column_int64 (stmt, 2));
230 file.set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
231 file.set_mtime (sqlite3_column_int (stmt, 4));
232 file.set_mode (sqlite3_column_int (stmt, 5));
233 file.set_seg_num (sqlite3_column_int64 (stmt, 6));
234 file.set_is_complete (sqlite3_column_int (stmt, 7));
235
236 retval->push_back (file);
237 }
238 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
239
240 sqlite3_finalize (stmt);
241
242 return retval;
243}
244
245
246FileItemsPtr
247FileState::LookupFilesInFolder (const std::string &folder)
248{
249 sqlite3_stmt *stmt;
250 sqlite3_prepare_v2 (m_db,
251 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
252 " FROM FileState "
253 " WHERE type = 0 AND directory = ?", -1, &stmt, 0);
254 if (folder.size () == 0)
255 sqlite3_bind_null (stmt, 1);
256 else
257 sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
258
259 FileItemsPtr retval = make_shared<FileItems> ();
260 while (sqlite3_step (stmt) == SQLITE_ROW)
261 {
262 FileItem file;
263 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
264 file.set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
265 file.set_seq_no (sqlite3_column_int64 (stmt, 2));
266 file.set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
267 file.set_mtime (sqlite3_column_int (stmt, 4));
268 file.set_mode (sqlite3_column_int (stmt, 5));
269 file.set_seg_num (sqlite3_column_int64 (stmt, 6));
270 file.set_is_complete (sqlite3_column_int (stmt, 7));
271
272 retval->push_back (file);
273 }
274
275 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
276
277 sqlite3_finalize (stmt);
278
279 return retval;
280}
281
282FileItemsPtr
283FileState::LookupFilesInFolderRecursively (const std::string &folder)
284{
285 _LOG_DEBUG ("LookupFilesInFolderRecursively: [" << folder << "]");
286
287 sqlite3_stmt *stmt;
288 if (folder != "")
289 {
290 sqlite3_prepare_v2 (m_db,
291 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
292 " FROM FileState "
293 " WHERE type = 0 AND (directory = ? OR directory LIKE ?)", -1, &stmt, 0);
294 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
295
296 sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
297 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
298
299 ostringstream escapedFolder;
300 for (string::const_iterator ch = folder.begin (); ch != folder.end (); ch ++)
301 {
302 if (*ch == '%')
303 escapedFolder << "\\%";
304 else
305 escapedFolder << *ch;
306 }
307 escapedFolder << "/" << "%";
308 string escapedFolderStr = escapedFolder.str ();
309
310 sqlite3_bind_text (stmt, 2, escapedFolderStr.c_str (), escapedFolderStr.size (), SQLITE_STATIC);
311 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
312 }
313 else
314 {
315 sqlite3_prepare_v2 (m_db,
316 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
317 " FROM FileState "
318 " WHERE type = 0", -1, &stmt, 0);
319 }
320
321 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
322
323 FileItemsPtr retval = make_shared<FileItems> ();
324 while (sqlite3_step (stmt) == SQLITE_ROW)
325 {
326 FileItem file;
327 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
328 file.set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
329 file.set_seq_no (sqlite3_column_int64 (stmt, 2));
330 file.set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
331 file.set_mtime (sqlite3_column_int (stmt, 4));
332 file.set_mode (sqlite3_column_int (stmt, 5));
333 file.set_seg_num (sqlite3_column_int64 (stmt, 6));
334 file.set_is_complete (sqlite3_column_int (stmt, 7));
335
336 retval->push_back (file);
337 }
338
339 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
340
341 sqlite3_finalize (stmt);
342
343 return retval;
344}
345
346void
347FileState::directory_name_xFun (sqlite3_context *context, int argc, sqlite3_value **argv)
348{
349 if (argc != 1)
350 {
351 sqlite3_result_error (context, "``directory_name'' expects 1 text argument", -1);
352 sqlite3_result_null (context);
353 return;
354 }
355
356 if (sqlite3_value_bytes (argv[0]) == 0)
357 {
358 sqlite3_result_null (context);
359 return;
360 }
361
362 filesystem::path filePath (string (reinterpret_cast<const char*> (sqlite3_value_text (argv[0])), sqlite3_value_bytes (argv[0])));
363 string dirPath = filePath.parent_path ().generic_string ();
364 // _LOG_DEBUG ("directory_name FUN: " << dirPath);
365 if (dirPath.size () == 0)
366 {
367 sqlite3_result_null (context);
368 }
369 else
370 {
371 sqlite3_result_text (context, dirPath.c_str (), dirPath.size (), SQLITE_TRANSIENT);
372 }
373}