blob: 63647c4f5a19a12287aabb44a0546f0cb21afe4a [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"
Alexander Afanasyev95f9f552013-02-26 23:05:20 -080024#include <boost/bind.hpp>
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080025
26INIT_LOGGER ("FileState");
27
28using namespace boost;
29using namespace std;
30
31const std::string INIT_DATABASE = "\
32 \n\
33CREATE TABLE FileState ( \n\
34 type INTEGER NOT NULL, /* 0 - newest, 1 - oldest */ \n\
35 filename TEXT NOT NULL, \n\
36 directory TEXT, \n\
37 device_name BLOB NOT NULL, \n\
38 seq_no INTEGER NOT NULL, \n\
39 file_hash BLOB NOT NULL, \n\
40 file_atime TIMESTAMP, \n\
41 file_mtime TIMESTAMP, \n\
42 file_ctime TIMESTAMP, \n\
43 file_chmod INTEGER, \n\
44 file_seg_num INTEGER, \n\
45 is_complete INTEGER, \n\
46 \n\
47 PRIMARY KEY (type, filename) \n\
48); \n\
49 \n\
50CREATE INDEX FileState_device_name_seq_no ON FileState (device_name, seq_no); \n\
51CREATE INDEX FileState_type_file_hash ON FileState (type, file_hash); \n\
52";
53
54FileState::FileState (const boost::filesystem::path &path)
55 : DbHelper (path / ".chronoshare", "file-state.db")
56{
57 sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, NULL);
58 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080059}
60
61FileState::~FileState ()
62{
63}
64
65void
66FileState::UpdateFile (const std::string &filename, const Hash &hash, const Ccnx::CcnxCharbuf &device_name, sqlite3_int64 seq_no,
67 time_t atime, time_t mtime, time_t ctime, int mode, int seg_num)
68{
69 sqlite3_stmt *stmt;
70 sqlite3_prepare_v2 (m_db, "UPDATE FileState "
71 "SET "
72 "device_name=?, seq_no=?, "
73 "file_hash=?,"
74 "file_atime=datetime(?, 'unixepoch'),"
75 "file_mtime=datetime(?, 'unixepoch'),"
76 "file_ctime=datetime(?, 'unixepoch'),"
77 "file_chmod=?, "
78 "file_seg_num=? "
79 "WHERE type=0 AND filename=?", -1, &stmt, 0);
80
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080081 sqlite3_bind_blob (stmt, 1, device_name.buf (), device_name.length (), SQLITE_STATIC);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080082 sqlite3_bind_int64 (stmt, 2, seq_no);
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080083 sqlite3_bind_blob (stmt, 3, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080084 sqlite3_bind_int64 (stmt, 4, atime);
85 sqlite3_bind_int64 (stmt, 5, mtime);
86 sqlite3_bind_int64 (stmt, 6, ctime);
87 sqlite3_bind_int (stmt, 7, mode);
88 sqlite3_bind_int (stmt, 8, seg_num);
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080089 sqlite3_bind_text (stmt, 9, filename.c_str (), -1, SQLITE_STATIC);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080090
91 sqlite3_step (stmt);
92
93 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_ROW && sqlite3_errcode (m_db) != SQLITE_DONE,
94 sqlite3_errmsg (m_db));
95
96 sqlite3_finalize (stmt);
97
98 int affected_rows = sqlite3_changes (m_db);
99 if (affected_rows == 0) // file didn't exist
100 {
101 sqlite3_stmt *stmt;
102 sqlite3_prepare_v2 (m_db, "INSERT INTO FileState "
103 "(type,filename,device_name,seq_no,file_hash,file_atime,file_mtime,file_ctime,file_chmod,file_seg_num) "
104 "VALUES (0, ?, ?, ?, ?, "
105 "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?)", -1, &stmt, 0);
106
107 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
108
109 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
110 sqlite3_bind_blob (stmt, 2, device_name.buf (), device_name.length (), SQLITE_STATIC);
111 sqlite3_bind_int64 (stmt, 3, seq_no);
112 sqlite3_bind_blob (stmt, 4, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
113 sqlite3_bind_int64 (stmt, 5, atime);
114 sqlite3_bind_int64 (stmt, 6, mtime);
115 sqlite3_bind_int64 (stmt, 7, ctime);
116 sqlite3_bind_int (stmt, 8, mode);
117 sqlite3_bind_int (stmt, 9, seg_num);
118 // sqlite3_bind_text (stmt, 10, filename.c_str (), -1, SQLITE_STATIC);
119
120 sqlite3_step (stmt);
121 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
122 sqlite3_errmsg (m_db));
123 sqlite3_finalize (stmt);
124
125 sqlite3_prepare_v2 (m_db, "UPDATE FileState SET directory=directory_name(filename) WHERE filename=?", -1, &stmt, 0);
126 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
127
128 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
129 sqlite3_step (stmt);
130 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
131 sqlite3_errmsg (m_db));
132 sqlite3_finalize (stmt);
133 }
134}
135
136
137void
138FileState::DeleteFile (const std::string &filename)
139{
140
141 sqlite3_stmt *stmt;
142 sqlite3_prepare_v2 (m_db, "DELETE FROM FileState WHERE type=0 AND filename=?", -1, &stmt, 0);
143 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
144
145 _LOG_DEBUG ("Delete " << filename);
146
147 sqlite3_step (stmt);
148 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
149 sqlite3_errmsg (m_db));
150 sqlite3_finalize (stmt);
151}
152
153
154void
155FileState::SetFileComplete (const std::string &filename)
156{
157 sqlite3_stmt *stmt;
158 sqlite3_prepare_v2 (m_db,
159 "UPDATE FileState SET is_complete=1 WHERE type = 0 AND filename = ?", -1, &stmt, 0);
160 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
161 sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
162
163 sqlite3_step (stmt);
164 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
165
166 sqlite3_finalize (stmt);
167}
168
169
170/**
171 * @todo Implement checking modification time and permissions
172 */
173FileItemPtr
174FileState::LookupFile (const std::string &filename)
175{
176 sqlite3_stmt *stmt;
177 sqlite3_prepare_v2 (m_db,
178 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
179 " FROM FileState "
180 " WHERE type = 0 AND filename = ?", -1, &stmt, 0);
181 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
182 sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
183
184 FileItemPtr retval;
185 if (sqlite3_step (stmt) == SQLITE_ROW)
186 {
187 retval = make_shared<FileItem> ();
188 retval->set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
189 retval->set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
190 retval->set_seq_no (sqlite3_column_int64 (stmt, 2));
191 retval->set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
192 retval->set_mtime (sqlite3_column_int (stmt, 4));
193 retval->set_mode (sqlite3_column_int (stmt, 5));
194 retval->set_seg_num (sqlite3_column_int64 (stmt, 6));
195 retval->set_is_complete (sqlite3_column_int (stmt, 7));
196 }
197 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
198 sqlite3_finalize (stmt);
199
200 return retval;
201}
202
203FileItemsPtr
204FileState::LookupFilesForHash (const Hash &hash)
205{
206 sqlite3_stmt *stmt;
207 sqlite3_prepare_v2 (m_db,
208 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
209 " FROM FileState "
210 " WHERE type = 0 AND file_hash = ?", -1, &stmt, 0);
211 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
212 sqlite3_bind_blob(stmt, 1, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
213 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
214
215 FileItemsPtr retval = make_shared<FileItems> ();
216 while (sqlite3_step (stmt) == SQLITE_ROW)
217 {
218 FileItem file;
219 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
220 file.set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
221 file.set_seq_no (sqlite3_column_int64 (stmt, 2));
222 file.set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
223 file.set_mtime (sqlite3_column_int (stmt, 4));
224 file.set_mode (sqlite3_column_int (stmt, 5));
225 file.set_seg_num (sqlite3_column_int64 (stmt, 6));
226 file.set_is_complete (sqlite3_column_int (stmt, 7));
227
228 retval->push_back (file);
229 }
230 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
231
232 sqlite3_finalize (stmt);
233
234 return retval;
235}
236
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800237void
238FileState::LookupFilesInFolder (const boost::function<void (const FileItem&)> &visitor, const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800239{
240 sqlite3_stmt *stmt;
241 sqlite3_prepare_v2 (m_db,
242 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
243 " FROM FileState "
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800244 " WHERE type = 0 AND directory = ?"
245 " LIMIT ? OFFSET ?", -1, &stmt, 0);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800246 if (folder.size () == 0)
247 sqlite3_bind_null (stmt, 1);
248 else
249 sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
250
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800251 sqlite3_bind_int (stmt, 2, limit);
252 sqlite3_bind_int (stmt, 3, offset);
253
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800254 while (sqlite3_step (stmt) == SQLITE_ROW)
255 {
256 FileItem file;
257 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
258 file.set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
259 file.set_seq_no (sqlite3_column_int64 (stmt, 2));
260 file.set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
261 file.set_mtime (sqlite3_column_int (stmt, 4));
262 file.set_mode (sqlite3_column_int (stmt, 5));
263 file.set_seg_num (sqlite3_column_int64 (stmt, 6));
264 file.set_is_complete (sqlite3_column_int (stmt, 7));
265
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800266 visitor (file);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800267 }
268
269 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
270
271 sqlite3_finalize (stmt);
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800272}
273
274FileItemsPtr
275FileState::LookupFilesInFolder (const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
276{
277 FileItemsPtr retval = make_shared<FileItems> ();
278 LookupFilesInFolder (boost::bind (&FileItems::push_back, retval.get (), _1), folder, offset, limit);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800279
280 return retval;
281}
282
283FileItemsPtr
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800284FileState::LookupFilesInFolderRecursively (const boost::function<void (const FileItem&)> &visitor, const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800285{
286 _LOG_DEBUG ("LookupFilesInFolderRecursively: [" << folder << "]");
287
288 sqlite3_stmt *stmt;
289 if (folder != "")
290 {
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800291 /// @todo Do something to improve efficiency of this query. Right now it is basically scanning the whole database
292
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800293 sqlite3_prepare_v2 (m_db,
294 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
295 " FROM FileState "
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800296 " WHERE type = 0 AND is_prefix (?, directory)=1 "
297 " LIMIT ? OFFSET ?", -1, &stmt, 0); // there is a small ambiguity with is_prefix matching, but should be ok for now
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800298 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
299
300 sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
301 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
302
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800303 sqlite3_bind_int (stmt, 2, limit);
304 sqlite3_bind_int (stmt, 3, offset);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800305 }
306 else
307 {
308 sqlite3_prepare_v2 (m_db,
309 "SELECT filename,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
310 " FROM FileState "
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800311 " WHERE type = 0"
312 " LIMIT ? OFFSET ?", -1, &stmt, 0);
313 sqlite3_bind_int (stmt, 1, limit);
314 sqlite3_bind_int (stmt, 2, offset);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800315 }
316
317 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
318
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800319 while (sqlite3_step (stmt) == SQLITE_ROW)
320 {
321 FileItem file;
322 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
323 file.set_device_name (sqlite3_column_blob (stmt, 1), sqlite3_column_bytes (stmt, 1));
324 file.set_seq_no (sqlite3_column_int64 (stmt, 2));
325 file.set_file_hash (sqlite3_column_blob (stmt, 3), sqlite3_column_bytes (stmt, 3));
326 file.set_mtime (sqlite3_column_int (stmt, 4));
327 file.set_mode (sqlite3_column_int (stmt, 5));
328 file.set_seg_num (sqlite3_column_int64 (stmt, 6));
329 file.set_is_complete (sqlite3_column_int (stmt, 7));
330
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800331 visitor (file);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800332 }
333
334 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
335
336 sqlite3_finalize (stmt);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800337}
338
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800339FileItemsPtr
340FileState::LookupFilesInFolderRecursively (const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800341{
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800342 FileItemsPtr retval = make_shared<FileItems> ();
343 LookupFilesInFolder (boost::bind (&FileItems::push_back, retval.get (), _1), folder, offset, limit);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800344
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800345 return retval;
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800346}