1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
#ifndef CERT_TRANS_LOG_FILE_STORAGE_H_
#define CERT_TRANS_LOG_FILE_STORAGE_H_
#include <stdint.h>
#include <memory>
#include <set>
#include <string>
#include "base/macros.h"
#include "util/status.h"
namespace cert_trans {
class FilesystemOps;
// A simple filesystem-based database for (key, data) entries,
// structured as follows:
//
// <root>/storage - Storage for the data, filenames are derived from
// the hex key like so: "89abcd" becomes "8/9/a/bcd"
// (for storage depth 3). This is because filesystems
// tend to perform badly with very large
// directories. For this to work, we assume keys are
// hashes, i.e., random, and of reasonable
// length. However, numerical monotonically
// increasing keys can be made to work too: for
// example, 4-byte keys could be set up with max 256
// entries/directory by setting storage_depth=6:
//
// "00000000" -> "0/0/0/0/0/0/00"
// "00000001" -> "0/0/0/0/0/0/01"
// ...
// "00000100" -> "0/0/0/0/0/1/00"
// ...
//
// Each key corresponds to a file with the
// data. Writes to these files are atomic
// (i.e. create a new file and move into place).
//
// <root>/tmp - Temporary storage for atomicity. Must be on the
// same filesystem as <root>/storage.
//
// FileStorage aborts upon any FilesystemOps error. This class is
// threadsafe.
class FileStorage {
public:
// Default constructor, uses BasicFilesystemOps.
FileStorage(const std::string& file_base, int storage_depth);
// Takes ownership of the FilesystemOps.
FileStorage(const std::string& file_base, int storage_depth,
cert_trans::FilesystemOps* file_op);
~FileStorage();
// Scan the entire database and return the list of keys.
std::set<std::string> Scan() const;
// Write (key, data) unless an entry matching |key| already exists.
util::Status CreateEntry(const std::string& key, const std::string& data);
// Update an existing entry; fail if it doesn't already exist.
util::Status UpdateEntry(const std::string& key, const std::string& data);
// Lookup entry based on key.
util::Status LookupEntry(const std::string& key, std::string* result) const;
private:
std::string StoragePathBasename(const std::string& hex) const;
std::string StoragePathComponent(const std::string& hex, int n) const;
std::string StoragePath(const std::string& key) const;
std::string StorageKey(const std::string& storage_path) const;
// Write or overwrite.
void WriteStorageEntry(const std::string& key, const std::string& data);
void ScanFiles(const std::string& dir_path,
std::set<std::string>* keys) const;
void ScanDir(const std::string& dir_path, int depth,
std::set<std::string>* keys) const;
// The following methods abort upon any error.
bool FileExists(const std::string& file_path) const;
void AtomicWriteBinaryFile(const std::string& file_path,
const std::string& data);
// Create directory, unless it already exists.
void CreateMissingDirectory(const std::string& dir_path);
const std::string storage_dir_;
const std::string tmp_dir_;
const std::string tmp_file_template_;
const int storage_depth_;
const std::unique_ptr<cert_trans::FilesystemOps> file_op_;
DISALLOW_COPY_AND_ASSIGN(FileStorage);
};
} // namespace cert_trans
#endif // CERT_TRANS_LOG_FILE_STORAGE_H_
|