Skip to content

Commit 1e1097d

Browse files
omkarhgawdemeta-codesync[bot]
authored andcommitted
Fix close(-1) on failed open in btrfs rename fsync path (facebook#14443)
Summary: Pull Request resolved: facebook#14443 In `PosixDirectory::FsyncWithDirOptions()`, when handling btrfs file rename syncing, if `open()` fails and `fd` is -1, the code unconditionally calls `close(fd)`. Calling `close(-1)` is undefined behavior per POSIX (returns EBADF on Linux), and overwrites the original meaningful open error with a misleading "While closing file after fsync" error message. Fix: Guard the `close()` call with `fd >= 0`. Reviewed By: xingbowang Differential Revision: D95303407 fbshipit-source-id: 9b64b45a09e6ba41d87d164a1c094b4d22f7c186
1 parent e44a99f commit 1e1097d

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

env/io_posix.cc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1940,7 +1940,10 @@ IOStatus PosixDirectory::FsyncWithDirOptions(
19401940
assert(fd_ >= 0); // Check use after close
19411941
IOStatus s = IOStatus::OK();
19421942
#ifndef OS_AIX
1943-
if (is_btrfs_) {
1943+
bool test_is_btrfs = is_btrfs_;
1944+
TEST_SYNC_POINT_CALLBACK("PosixDirectory::FsyncWithDirOptions:ForceBtrfs",
1945+
&test_is_btrfs);
1946+
if (test_is_btrfs) {
19441947
// skip dir fsync for new file creation, which is not needed for btrfs
19451948
if (dir_fsync_options.reason == DirFsyncOptions::kNewFileSynced) {
19461949
return s;
@@ -1959,7 +1962,7 @@ IOStatus PosixDirectory::FsyncWithDirOptions(
19591962
} else if (fsync(fd) < 0) {
19601963
s = IOError("While fsync renaming file", new_name, errno);
19611964
}
1962-
if (close(fd) < 0) {
1965+
if (fd >= 0 && close(fd) < 0) {
19631966
s = IOError("While closing file after fsync", new_name, errno);
19641967
}
19651968
return s;

env/io_posix_test.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@
33
// COPYING file in the root directory) and Apache 2.0 License
44
// (found in the LICENSE.Apache file in the root directory).
55

6+
#include "test_util/sync_point.h"
67
#include "test_util/testharness.h"
78
#include "util/random.h"
89

910
#ifdef ROCKSDB_LIB_IO_POSIX
1011
#include "env/io_posix.h"
12+
#include "rocksdb/file_system.h"
1113

1214
namespace ROCKSDB_NAMESPACE {
1315

@@ -174,6 +176,47 @@ TEST_F(PosixWritableFileTest, SeekAfterExtend) {
174176
ASSERT_OK(fs->DeleteFile(path, IOOptions(), nullptr));
175177
}
176178

179+
#ifdef OS_LINUX
180+
class PosixDirectoryTest : public testing::Test {};
181+
182+
TEST_F(PosixDirectoryTest, BtrfsFsyncFailedOpenDoesNotCloseInvalidFd) {
183+
// When FsyncWithDirOptions is called with kFileRenamed on a btrfs filesystem
184+
// and open() fails, close(-1) should not be called. Without the fix,
185+
// close(-1) is called which is POSIX undefined behavior and overwrites the
186+
// meaningful open error with a misleading "While closing file after fsync".
187+
std::shared_ptr<FileSystem> fs = FileSystem::Default();
188+
std::string dir_path =
189+
test::PerThreadDBPath("PosixDirectoryTest_BtrfsFsyncFailedOpen");
190+
ASSERT_OK(fs->CreateDirIfMissing(dir_path, IOOptions(), nullptr));
191+
192+
std::unique_ptr<FSDirectory> dir;
193+
ASSERT_OK(fs->NewDirectory(dir_path, IOOptions(), &dir, nullptr));
194+
195+
// Force the btrfs code path via sync point
196+
SyncPoint::GetInstance()->SetCallBack(
197+
"PosixDirectory::FsyncWithDirOptions:ForceBtrfs",
198+
[](void* arg) { *static_cast<bool*>(arg) = true; });
199+
SyncPoint::GetInstance()->EnableProcessing();
200+
201+
// Call FsyncWithDirOptions with a non-existent file for rename sync.
202+
// open() will fail since the file doesn't exist.
203+
DirFsyncOptions opts(std::string(dir_path + "/nonexistent_file"));
204+
IOStatus s = dir->FsyncWithDirOptions(IOOptions(), nullptr, opts);
205+
206+
// Should get an error about open failing, NOT about closing
207+
ASSERT_TRUE(s.IsIOError());
208+
// The error message should mention "open", not "closing"
209+
ASSERT_TRUE(s.ToString().find("open") != std::string::npos);
210+
ASSERT_TRUE(s.ToString().find("closing") == std::string::npos);
211+
212+
SyncPoint::GetInstance()->DisableProcessing();
213+
SyncPoint::GetInstance()->ClearAllCallBacks();
214+
215+
ASSERT_OK(dir->Close(IOOptions(), nullptr));
216+
ASSERT_OK(fs->DeleteDir(dir_path, IOOptions(), nullptr));
217+
}
218+
#endif
219+
177220
} // namespace ROCKSDB_NAMESPACE
178221
#endif
179222

0 commit comments

Comments
 (0)