[ledger] DCHECK that the parent dir exists before moving to destination.
Test: LevelDbFactoryTest.InitWithCachedDbAvailable
Rest of existing tests still pass.
Change-Id: I65debc6ee085c21d9c112d89bbce0a1c9c2d253b
diff --git a/bin/ledger/storage/impl/leveldb_factory.cc b/bin/ledger/storage/impl/leveldb_factory.cc
index 5427e94..8486544 100644
--- a/bin/ledger/storage/impl/leveldb_factory.cc
+++ b/bin/ledger/storage/impl/leveldb_factory.cc
@@ -40,6 +40,16 @@
constexpr size_t kRandomBytesCount = 16;
+// Returns whether the parent directory of |path| exists. If it is not possible
+// to access the parent directory, returns whether the given |path| exists.
+bool ParentDirectoryExists(ledger::DetachedPath path) {
+ size_t last_slash = path.path().find_last_of('/');
+ return files::IsDirectoryAt(path.root_fd(),
+ last_slash == std::string::npos
+ ? path.path()
+ : path.path().substr(0, last_slash));
+}
+
} // namespace
enum class LevelDbFactory::CreateInStagingPath : bool {
@@ -221,6 +231,15 @@
if (status != Status::OK) {
return status;
}
+ // If the parent directory doesn't exist, renameat will fail.
+ // Note that |cached_db_path_| will also be created throught the staging path
+ // and thus, this code path will be reached. Its parent directory is lazily
+ // created when result->Init() (see code above) is called:
+ // - |staging_path_| and |cached_db_path_| share the same parent (the
+ // |cache_path| given on the constructor), and
+ // - in LevelDb initialization, the directories up to the db path are created.
+ FXL_DCHECK(ParentDirectoryExists(db_path))
+ << "Parent directory does not exit for path: " << db_path.path();
// Move it to the final destination.
if (renameat(tmp_destination.root_fd(), tmp_destination.path().c_str(),
db_path.root_fd(), db_path.path().c_str()) != 0) {
diff --git a/bin/ledger/storage/impl/leveldb_factory_unittest.cc b/bin/ledger/storage/impl/leveldb_factory_unittest.cc
index 5a2e5be..ddb62c4 100644
--- a/bin/ledger/storage/impl/leveldb_factory_unittest.cc
+++ b/bin/ledger/storage/impl/leveldb_factory_unittest.cc
@@ -181,19 +181,24 @@
// one directly.
scoped_tmpfs::ScopedTmpFS tmpfs;
ledger::DetachedPath cache_path(tmpfs.root_fd(), "cache");
+ // Must be the same as |kCachedDbPath| in leveldb_factory.cc.
+ ledger::DetachedPath cached_db_path = cache_path.SubPath("cached_db");
+
auto db_factory = std::make_unique<LevelDbFactory>(&environment_, cache_path);
- // The cache directory should not be created, yet.
- EXPECT_FALSE(files::IsDirectoryAt(cache_path.root_fd(), cache_path.path()));
+ // The cached db directory should not be created, yet.
+ EXPECT_FALSE(
+ files::IsDirectoryAt(cached_db_path.root_fd(), cached_db_path.path()));
// Initialize and wait for the cached instance to be created.
db_factory->Init();
RunLoopUntilIdle();
// Close the factory. This will not affect the created cached instance, which
- // was created under |cache_path|.
+ // was created under |cached_db_path|.
db_factory.reset();
- EXPECT_TRUE(files::IsDirectoryAt(cache_path.root_fd(), cache_path.path()));
+ EXPECT_TRUE(
+ files::IsDirectoryAt(cached_db_path.root_fd(), cached_db_path.path()));
// Reset and re-initialize the factory object. It should now use the
// previously created instance.