1 #include "file_table.h"
2 #include "btree_dir.h"
3 #include "exceptions.h"
4 #include "logger.h"
5 #include "myutils.h"
6 #include "platform.h"
7
8 #include <algorithm>
9 #include <limits>
10 #include <queue>
11 #include <string.h>
12 #include <string>
13 #include <utility>
14 #include <vector>
15
16 namespace securefs
17 {
18
19 typedef std::pair<std::shared_ptr<FileStream>, std::shared_ptr<FileStream>> FileStreamPtrPair;
20 class FileTableIO
21 {
22 DISABLE_COPY_MOVE(FileTableIO)
23
24 public:
25 explicit FileTableIO() = default;
26 virtual ~FileTableIO() = default;
27
28 virtual FileStreamPtrPair open(const id_type& id) = 0;
29 virtual FileStreamPtrPair create(const id_type& id) = 0;
30 virtual void unlink(const id_type& id) noexcept = 0;
31 };
32
33 class FileTableIOVersion1 : public FileTableIO
34 {
35 private:
36 std::shared_ptr<const OSService> m_root;
37 bool m_readonly;
38
39 static const size_t FIRST_LEVEL = 1, SECOND_LEVEL = 5;
40
calculate_paths(const securefs::id_type & id,std::string & first_level_dir,std::string & second_level_dir,std::string & full_filename,std::string & meta_filename)41 static void calculate_paths(const securefs::id_type& id,
42 std::string& first_level_dir,
43 std::string& second_level_dir,
44 std::string& full_filename,
45 std::string& meta_filename)
46 {
47 first_level_dir = securefs::hexify(id.data(), FIRST_LEVEL);
48 second_level_dir
49 = first_level_dir + '/' + securefs::hexify(id.data() + FIRST_LEVEL, SECOND_LEVEL);
50 full_filename = second_level_dir + '/'
51 + securefs::hexify(id.data() + FIRST_LEVEL + SECOND_LEVEL,
52 id.size() - FIRST_LEVEL - SECOND_LEVEL);
53 meta_filename = full_filename + ".meta";
54 }
55
56 public:
FileTableIOVersion1(std::shared_ptr<const OSService> root,bool readonly)57 explicit FileTableIOVersion1(std::shared_ptr<const OSService> root, bool readonly)
58 : m_root(std::move(root)), m_readonly(readonly)
59 {
60 }
61
open(const id_type & id)62 FileStreamPtrPair open(const id_type& id) override
63 {
64 std::string first_level_dir, second_level_dir, filename, metaname;
65 calculate_paths(id, first_level_dir, second_level_dir, filename, metaname);
66
67 int open_flags = m_readonly ? O_RDONLY : O_RDWR;
68 return std::make_pair(m_root->open_file_stream(filename, open_flags, 0),
69 m_root->open_file_stream(metaname, open_flags, 0));
70 }
71
create(const id_type & id)72 FileStreamPtrPair create(const id_type& id) override
73 {
74 std::string first_level_dir, second_level_dir, filename, metaname;
75 calculate_paths(id, first_level_dir, second_level_dir, filename, metaname);
76 m_root->ensure_directory(first_level_dir.c_str(), 0755);
77 m_root->ensure_directory(second_level_dir.c_str(), 0755);
78 int open_flags = O_RDWR | O_CREAT | O_EXCL;
79 return std::make_pair(m_root->open_file_stream(filename, open_flags, 0644),
80 m_root->open_file_stream(metaname, open_flags, 0644));
81 }
82
unlink(const id_type & id)83 void unlink(const id_type& id) noexcept override
84 {
85 std::string first_level_dir, second_level_dir, filename, metaname;
86 calculate_paths(id, first_level_dir, second_level_dir, filename, metaname);
87 m_root->remove_file_nothrow(filename);
88 m_root->remove_file_nothrow(metaname);
89 m_root->remove_directory_nothrow(second_level_dir);
90 m_root->remove_directory_nothrow(second_level_dir);
91 }
92 };
93
94 class FileTableIOVersion2 : public FileTableIO
95 {
96 private:
97 std::shared_ptr<const OSService> m_root;
98 bool m_readonly;
99
calculate_paths(const securefs::id_type & id,std::string & dir,std::string & full_filename,std::string & meta_filename)100 static void calculate_paths(const securefs::id_type& id,
101 std::string& dir,
102 std::string& full_filename,
103 std::string& meta_filename)
104 {
105 dir = securefs::hexify(id.data(), 1);
106 full_filename = dir + '/' + securefs::hexify(id.data() + 1, id.size() - 1);
107 meta_filename = full_filename + ".meta";
108 }
109
110 public:
FileTableIOVersion2(std::shared_ptr<const OSService> root,bool readonly)111 explicit FileTableIOVersion2(std::shared_ptr<const OSService> root, bool readonly)
112 : m_root(std::move(root)), m_readonly(readonly)
113 {
114 }
115
open(const id_type & id)116 FileStreamPtrPair open(const id_type& id) override
117 {
118 std::string dir, filename, metaname;
119 calculate_paths(id, dir, filename, metaname);
120
121 int open_flags = m_readonly ? O_RDONLY : O_RDWR;
122 return std::make_pair(m_root->open_file_stream(filename, open_flags, 0),
123 m_root->open_file_stream(metaname, open_flags, 0));
124 }
125
create(const id_type & id)126 FileStreamPtrPair create(const id_type& id) override
127 {
128 std::string dir, filename, metaname;
129 calculate_paths(id, dir, filename, metaname);
130 m_root->ensure_directory(dir, 0755);
131 int open_flags = O_RDWR | O_CREAT | O_EXCL;
132 return std::make_pair(m_root->open_file_stream(filename, open_flags, 0644),
133 m_root->open_file_stream(metaname, open_flags, 0644));
134 }
135
unlink(const id_type & id)136 void unlink(const id_type& id) noexcept override
137 {
138 std::string dir, filename, metaname;
139 calculate_paths(id, dir, filename, metaname);
140 m_root->remove_file_nothrow(filename);
141 m_root->remove_file_nothrow(metaname);
142 m_root->remove_directory_nothrow(dir);
143 }
144 };
145
FileTable(int version,std::shared_ptr<const OSService> root,const key_type & master_key,uint32_t flags,unsigned block_size,unsigned iv_size)146 FileTable::FileTable(int version,
147 std::shared_ptr<const OSService> root,
148 const key_type& master_key,
149 uint32_t flags,
150 unsigned block_size,
151 unsigned iv_size)
152 : m_flags(flags), m_block_size(block_size), m_iv_size(iv_size), m_root(root)
153 {
154 memcpy(m_master_key.data(), master_key.data(), master_key.size());
155 switch (version)
156 {
157 case 1:
158 m_fio.reset(new FileTableIOVersion1(root, is_readonly()));
159 break;
160 case 2:
161 case 3:
162 m_fio.reset(new FileTableIOVersion2(root, is_readonly()));
163 break;
164 default:
165 throwInvalidArgumentException("Unknown version");
166 }
167 }
168
~FileTable()169 FileTable::~FileTable()
170 {
171 for (auto&& pair : m_files)
172 finalize(pair.second);
173 }
174
open_as(const id_type & id,int type)175 FileBase* FileTable::open_as(const id_type& id, int type)
176 {
177 auto it = m_files.find(id);
178 if (it != m_files.end())
179 {
180 // Remove the marking that this id is closed
181 auto closed_id_iter = std::find(m_closed_ids.begin(), m_closed_ids.end(), id);
182 if (closed_id_iter != m_closed_ids.end())
183 m_closed_ids.erase(closed_id_iter);
184
185 if (it->second->type() != type)
186 m_files.erase(it);
187 else
188 {
189 it->second->incref();
190 return it->second.get();
191 }
192 }
193
194 std::shared_ptr<FileStream> data_fd, meta_fd;
195 std::tie(data_fd, meta_fd) = m_fio->open(id);
196 auto fb = btree_make_file_from_type(type,
197 data_fd,
198 meta_fd,
199 m_master_key,
200 id,
201 is_auth_enabled(),
202 m_block_size,
203 m_iv_size,
204 is_time_stored());
205 fb->setref(1);
206 auto result = fb.get();
207 m_files.emplace(id, std::move(fb));
208 return result;
209 }
210
create_as(const id_type & id,int type)211 FileBase* FileTable::create_as(const id_type& id, int type)
212 {
213 if (is_readonly())
214 throwVFSException(EROFS);
215 if (m_files.find(id) != m_files.end())
216 throwVFSException(EEXIST);
217
218 std::shared_ptr<FileStream> data_fd, meta_fd;
219 std::tie(data_fd, meta_fd) = m_fio->create(id);
220 auto fb = btree_make_file_from_type(type,
221 data_fd,
222 meta_fd,
223 m_master_key,
224 id,
225 is_auth_enabled(),
226 m_block_size,
227 m_iv_size,
228 is_time_stored());
229 fb->setref(1);
230 auto result = fb.get();
231 m_files.emplace(id, std::move(fb));
232 return result;
233 }
234
close(FileBase * fb)235 void FileTable::close(FileBase* fb)
236 {
237 if (!fb)
238 throwVFSException(EFAULT);
239
240 auto iter = m_files.find(fb->get_id());
241 if (iter == m_files.end() || iter->second.get() != fb)
242 throwInvalidArgumentException("ID does not match the table");
243
244 if (fb->getref() <= 0)
245 throwInvalidArgumentException("Closing an closed file");
246
247 if (fb->decref() <= 0)
248 {
249 finalize(iter->second);
250 if (iter->second)
251 {
252 // This means the file is not deleted
253 // The handle shall remain in the cache
254 m_closed_ids.push_back(iter->second->get_id());
255 gc();
256 }
257 else
258 {
259 m_files.erase(iter);
260 }
261 }
262 }
263
eject()264 void FileTable::eject()
265 {
266 auto num_eject = std::min<size_t>(NUM_EJECT, m_closed_ids.size());
267 for (size_t i = 0; i < num_eject; ++i)
268 {
269 m_files.erase(m_closed_ids[i]);
270 TRACE_LOG("Evicting file with ID=%s from cache", hexify(m_closed_ids[i]).c_str());
271 }
272 m_closed_ids.erase(m_closed_ids.begin(), m_closed_ids.begin() + num_eject);
273 }
274
finalize(std::unique_ptr<FileBase> & fb)275 void FileTable::finalize(std::unique_ptr<FileBase>& fb)
276 {
277 if (!fb)
278 return;
279
280 if (fb->is_unlinked())
281 {
282 id_type id = fb->get_id();
283 fb.reset();
284 m_fio->unlink(id);
285 }
286 else
287 {
288 fb->flush();
289 }
290 }
291
gc()292 void FileTable::gc()
293 {
294 if (m_closed_ids.size() >= MAX_NUM_CLOSED)
295 eject();
296 }
297 } // namespace securefs
298