blob: 6affed974bdfc94405b45df0134fccbbfdc06843 [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\
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -080036 version INTEGER, \n\
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080037 directory TEXT, \n\
38 device_name BLOB NOT NULL, \n\
39 seq_no INTEGER NOT NULL, \n\
40 file_hash BLOB NOT NULL, \n\
41 file_atime TIMESTAMP, \n\
42 file_mtime TIMESTAMP, \n\
43 file_ctime TIMESTAMP, \n\
44 file_chmod INTEGER, \n\
45 file_seg_num INTEGER, \n\
46 is_complete INTEGER, \n\
47 \n\
48 PRIMARY KEY (type, filename) \n\
49); \n\
50 \n\
51CREATE INDEX FileState_device_name_seq_no ON FileState (device_name, seq_no); \n\
52CREATE INDEX FileState_type_file_hash ON FileState (type, file_hash); \n\
53";
54
Yingdi Yuadb54eb2013-08-15 10:28:28 -070055FileState::FileState (const boost::filesystem::path &path, bool cow)
56 : DbHelper (path / ".chronoshare", (cow ? "file-state-tmp.db" : "file-state.db"))
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080057{
58 sqlite3_exec (m_db, INIT_DATABASE.c_str (), NULL, NULL, NULL);
59 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080060}
61
62FileState::~FileState ()
63{
64}
65
66void
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -080067FileState::UpdateFile (const std::string &filename, sqlite3_int64 version,
Alexander Afanasyev1dd37ed2013-08-14 18:08:09 -070068 const Hash &hash, const Ndnx::NdnxCharbuf &device_name, sqlite3_int64 seq_no,
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080069 time_t atime, time_t mtime, time_t ctime, int mode, int seg_num)
70{
71 sqlite3_stmt *stmt;
72 sqlite3_prepare_v2 (m_db, "UPDATE FileState "
73 "SET "
74 "device_name=?, seq_no=?, "
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -080075 "version=?,"
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080076 "file_hash=?,"
77 "file_atime=datetime(?, 'unixepoch'),"
78 "file_mtime=datetime(?, 'unixepoch'),"
79 "file_ctime=datetime(?, 'unixepoch'),"
80 "file_chmod=?, "
81 "file_seg_num=? "
82 "WHERE type=0 AND filename=?", -1, &stmt, 0);
83
Alexander Afanasyev026eaf32013-02-23 16:37:14 -080084 sqlite3_bind_blob (stmt, 1, device_name.buf (), device_name.length (), SQLITE_STATIC);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080085 sqlite3_bind_int64 (stmt, 2, seq_no);
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -080086 sqlite3_bind_int64 (stmt, 3, version);
87 sqlite3_bind_blob (stmt, 4, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
88 sqlite3_bind_int64 (stmt, 5, atime);
89 sqlite3_bind_int64 (stmt, 6, mtime);
90 sqlite3_bind_int64 (stmt, 7, ctime);
91 sqlite3_bind_int (stmt, 8, mode);
92 sqlite3_bind_int (stmt, 9, seg_num);
93 sqlite3_bind_text (stmt, 10, filename.c_str (), -1, SQLITE_STATIC);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -080094
95 sqlite3_step (stmt);
96
97 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_ROW && sqlite3_errcode (m_db) != SQLITE_DONE,
98 sqlite3_errmsg (m_db));
99
100 sqlite3_finalize (stmt);
101
102 int affected_rows = sqlite3_changes (m_db);
103 if (affected_rows == 0) // file didn't exist
104 {
105 sqlite3_stmt *stmt;
106 sqlite3_prepare_v2 (m_db, "INSERT INTO FileState "
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800107 "(type,filename,version,device_name,seq_no,file_hash,file_atime,file_mtime,file_ctime,file_chmod,file_seg_num) "
108 "VALUES (0, ?, ?, ?, ?, ?, "
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800109 "datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), datetime(?, 'unixepoch'), ?, ?)", -1, &stmt, 0);
110
111 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
112
113 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800114 sqlite3_bind_int64 (stmt, 2, version);
115 sqlite3_bind_blob (stmt, 3, device_name.buf (), device_name.length (), SQLITE_STATIC);
116 sqlite3_bind_int64 (stmt, 4, seq_no);
117 sqlite3_bind_blob (stmt, 5, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
118 sqlite3_bind_int64 (stmt, 6, atime);
119 sqlite3_bind_int64 (stmt, 7, mtime);
120 sqlite3_bind_int64 (stmt, 8, ctime);
121 sqlite3_bind_int (stmt, 9, mode);
122 sqlite3_bind_int (stmt, 10, seg_num);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800123
124 sqlite3_step (stmt);
125 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
126 sqlite3_errmsg (m_db));
127 sqlite3_finalize (stmt);
128
129 sqlite3_prepare_v2 (m_db, "UPDATE FileState SET directory=directory_name(filename) WHERE filename=?", -1, &stmt, 0);
130 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
131
132 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
133 sqlite3_step (stmt);
134 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
135 sqlite3_errmsg (m_db));
136 sqlite3_finalize (stmt);
137 }
138}
139
140
141void
142FileState::DeleteFile (const std::string &filename)
143{
144
145 sqlite3_stmt *stmt;
146 sqlite3_prepare_v2 (m_db, "DELETE FROM FileState WHERE type=0 AND filename=?", -1, &stmt, 0);
147 sqlite3_bind_text (stmt, 1, filename.c_str (), -1, SQLITE_STATIC);
148
149 _LOG_DEBUG ("Delete " << filename);
150
151 sqlite3_step (stmt);
152 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE,
153 sqlite3_errmsg (m_db));
154 sqlite3_finalize (stmt);
155}
156
157
158void
159FileState::SetFileComplete (const std::string &filename)
160{
161 sqlite3_stmt *stmt;
162 sqlite3_prepare_v2 (m_db,
163 "UPDATE FileState SET is_complete=1 WHERE type = 0 AND filename = ?", -1, &stmt, 0);
164 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
165 sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
166
167 sqlite3_step (stmt);
168 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
169
170 sqlite3_finalize (stmt);
171}
172
173
174/**
175 * @todo Implement checking modification time and permissions
176 */
177FileItemPtr
178FileState::LookupFile (const std::string &filename)
179{
180 sqlite3_stmt *stmt;
181 sqlite3_prepare_v2 (m_db,
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800182 "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800183 " FROM FileState "
184 " WHERE type = 0 AND filename = ?", -1, &stmt, 0);
185 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
186 sqlite3_bind_text(stmt, 1, filename.c_str(), -1, SQLITE_STATIC);
187
188 FileItemPtr retval;
189 if (sqlite3_step (stmt) == SQLITE_ROW)
190 {
191 retval = make_shared<FileItem> ();
192 retval->set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800193 retval->set_version (sqlite3_column_int64 (stmt, 1));
194 retval->set_device_name (sqlite3_column_blob (stmt, 2), sqlite3_column_bytes (stmt, 2));
195 retval->set_seq_no (sqlite3_column_int64 (stmt, 3));
196 retval->set_file_hash (sqlite3_column_blob (stmt, 4), sqlite3_column_bytes (stmt, 4));
197 retval->set_mtime (sqlite3_column_int (stmt, 5));
198 retval->set_mode (sqlite3_column_int (stmt, 6));
199 retval->set_seg_num (sqlite3_column_int64 (stmt, 7));
200 retval->set_is_complete (sqlite3_column_int (stmt, 8));
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800201 }
202 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
203 sqlite3_finalize (stmt);
204
205 return retval;
206}
207
208FileItemsPtr
209FileState::LookupFilesForHash (const Hash &hash)
210{
211 sqlite3_stmt *stmt;
212 sqlite3_prepare_v2 (m_db,
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800213 "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800214 " FROM FileState "
215 " WHERE type = 0 AND file_hash = ?", -1, &stmt, 0);
216 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
217 sqlite3_bind_blob(stmt, 1, hash.GetHash (), hash.GetHashBytes (), SQLITE_STATIC);
218 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
219
220 FileItemsPtr retval = make_shared<FileItems> ();
221 while (sqlite3_step (stmt) == SQLITE_ROW)
222 {
223 FileItem file;
224 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800225 file.set_version (sqlite3_column_int64 (stmt, 1));
226 file.set_device_name (sqlite3_column_blob (stmt, 2), sqlite3_column_bytes (stmt, 2));
227 file.set_seq_no (sqlite3_column_int64 (stmt, 3));
228 file.set_file_hash (sqlite3_column_blob (stmt, 4), sqlite3_column_bytes (stmt, 4));
229 file.set_mtime (sqlite3_column_int (stmt, 5));
230 file.set_mode (sqlite3_column_int (stmt, 6));
231 file.set_seg_num (sqlite3_column_int64 (stmt, 7));
232 file.set_is_complete (sqlite3_column_int (stmt, 8));
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800233
234 retval->push_back (file);
235 }
236 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
237
238 sqlite3_finalize (stmt);
239
240 return retval;
241}
242
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800243void
244FileState::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 -0800245{
246 sqlite3_stmt *stmt;
247 sqlite3_prepare_v2 (m_db,
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800248 "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800249 " FROM FileState "
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800250 " WHERE type = 0 AND directory = ?"
251 " LIMIT ? OFFSET ?", -1, &stmt, 0);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800252 if (folder.size () == 0)
253 sqlite3_bind_null (stmt, 1);
254 else
255 sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
256
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800257 sqlite3_bind_int (stmt, 2, limit);
258 sqlite3_bind_int (stmt, 3, offset);
259
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800260 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));
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800264 file.set_version (sqlite3_column_int64 (stmt, 1));
265 file.set_device_name (sqlite3_column_blob (stmt, 2), sqlite3_column_bytes (stmt, 2));
266 file.set_seq_no (sqlite3_column_int64 (stmt, 3));
267 file.set_file_hash (sqlite3_column_blob (stmt, 4), sqlite3_column_bytes (stmt, 4));
268 file.set_mtime (sqlite3_column_int (stmt, 5));
269 file.set_mode (sqlite3_column_int (stmt, 6));
270 file.set_seg_num (sqlite3_column_int64 (stmt, 7));
271 file.set_is_complete (sqlite3_column_int (stmt, 8));
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800272
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800273 visitor (file);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800274 }
275
276 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
277
278 sqlite3_finalize (stmt);
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800279}
280
281FileItemsPtr
282FileState::LookupFilesInFolder (const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
283{
284 FileItemsPtr retval = make_shared<FileItems> ();
285 LookupFilesInFolder (boost::bind (&FileItems::push_back, retval.get (), _1), folder, offset, limit);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800286
287 return retval;
288}
289
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800290bool
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800291FileState::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 -0800292{
293 _LOG_DEBUG ("LookupFilesInFolderRecursively: [" << folder << "]");
294
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800295 if (limit >= 0)
296 limit ++;
297
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800298 sqlite3_stmt *stmt;
299 if (folder != "")
300 {
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800301 /// @todo Do something to improve efficiency of this query. Right now it is basically scanning the whole database
302
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800303 sqlite3_prepare_v2 (m_db,
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800304 "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800305 " FROM FileState "
Alexander Afanasyev20fd84a2013-02-27 12:17:00 -0800306 " WHERE type = 0 AND is_dir_prefix (?, directory)=1 "
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800307 " ORDER BY filename "
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800308 " 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 -0800309 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
310
311 sqlite3_bind_text (stmt, 1, folder.c_str (), folder.size (), SQLITE_STATIC);
312 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
313
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800314 sqlite3_bind_int (stmt, 2, limit);
315 sqlite3_bind_int (stmt, 3, offset);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800316 }
317 else
318 {
319 sqlite3_prepare_v2 (m_db,
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800320 "SELECT filename,version,device_name,seq_no,file_hash,strftime('%s', file_mtime),file_chmod,file_seg_num,is_complete "
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800321 " FROM FileState "
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800322 " WHERE type = 0"
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800323 " ORDER BY filename "
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800324 " LIMIT ? OFFSET ?", -1, &stmt, 0);
325 sqlite3_bind_int (stmt, 1, limit);
326 sqlite3_bind_int (stmt, 2, offset);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800327 }
328
329 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_OK, sqlite3_errmsg (m_db));
330
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800331 while (sqlite3_step (stmt) == SQLITE_ROW)
332 {
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800333 if (limit == 1)
334 break;
335
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800336 FileItem file;
337 file.set_filename (reinterpret_cast<const char *> (sqlite3_column_text (stmt, 0)), sqlite3_column_bytes (stmt, 0));
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800338 file.set_version (sqlite3_column_int64 (stmt, 1));
339 file.set_device_name (sqlite3_column_blob (stmt, 2), sqlite3_column_bytes (stmt, 2));
340 file.set_seq_no (sqlite3_column_int64 (stmt, 3));
341 file.set_file_hash (sqlite3_column_blob (stmt, 4), sqlite3_column_bytes (stmt, 4));
342 file.set_mtime (sqlite3_column_int (stmt, 5));
343 file.set_mode (sqlite3_column_int (stmt, 6));
344 file.set_seg_num (sqlite3_column_int64 (stmt, 7));
345 file.set_is_complete (sqlite3_column_int (stmt, 8));
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800346
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800347 visitor (file);
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800348 limit --;
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800349 }
350
351 _LOG_DEBUG_COND (sqlite3_errcode (m_db) != SQLITE_DONE, sqlite3_errmsg (m_db));
352
353 sqlite3_finalize (stmt);
Alexander Afanasyev6fb5dc62013-02-27 11:34:52 -0800354
355 return (limit == 1);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800356}
357
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800358FileItemsPtr
359FileState::LookupFilesInFolderRecursively (const std::string &folder, int offset/*=0*/, int limit/*=-1*/)
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800360{
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800361 FileItemsPtr retval = make_shared<FileItems> ();
362 LookupFilesInFolder (boost::bind (&FileItems::push_back, retval.get (), _1), folder, offset, limit);
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800363
Alexander Afanasyev95f9f552013-02-26 23:05:20 -0800364 return retval;
Alexander Afanasyevd6364ef2013-02-06 13:13:07 -0800365}