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