Skip to content

Commit 0100771

Browse files
xiaoyang-hhhclaude
andauthored
fix(fs): support SEEK_DATA/SEEK_HOLE for AioFileAdaptor (alibaba#1535)
Override lseek in AioFileAdaptor to handle SEEK_DATA/SEEK_HOLE by delegating to the kernel (::lseek on the real fd) and syncing the self-managed m_offset with the result; all other whence values keep the VirtualFile (m_offset-based) route. Guarded by #if defined(SEEK_DATA) && defined(SEEK_HOLE) so platforms without these macros (e.g. iocp on Windows) still compile. Previously AioFileAdaptor inherited VirtualFile::lseek, which only supports SEEK_SET/CUR/END and returned -1 for SEEK_DATA/SEEK_HOLE, so sparse-file queries (e.g. full_file_cache) failed on aio-backed files. PsyncFileAdaptor keeps its own kernel-offset lseek and is unaffected. Add LocalFileSystem.aio_seek_data_hole to verify SEEK_DATA/SEEK_HOLE on an AioFileAdaptor over a sparse file, including m_offset sync. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c4c51f9 commit 0100771

2 files changed

Lines changed: 48 additions & 0 deletions

File tree

fs/localfs.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,17 @@ namespace fs
287287
{
288288
return AIOEngine::fdatasync(fd);
289289
}
290+
#ifdef __linux__
291+
virtual off_t lseek(off_t offset, int whence) override
292+
{
293+
if (whence == SEEK_DATA || whence == SEEK_HOLE) {
294+
off_t ret = UISysCall(::lseek(fd, offset, whence));
295+
if (ret >= 0) m_offset = ret;
296+
return ret;
297+
}
298+
return VirtualFile::lseek(offset, whence);
299+
}
300+
#endif
290301
virtual int close() override
291302
{
292303
if (fd < 0) return 0;

fs/test/test.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -937,6 +937,43 @@ TEST(LocalFileSystem, basic) {
937937
lf->fsync();
938938
}
939939

940+
#ifdef __linux__
941+
TEST(LocalFileSystem, aio_seek_data_hole) {
942+
const off_t bs = 4096;
943+
const char* path = "/tmp/photon_seek_data_hole";
944+
// Build a sparse file with a plain fd:
945+
// data[0, bs) hole[bs, 2*bs) data[2*bs, 3*bs)
946+
int fd = ::open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
947+
ASSERT_GE(fd, 0);
948+
DEFER(::unlink(path));
949+
std::unique_ptr<char[]> block(new char[bs]);
950+
memset(block.get(), 'A', bs);
951+
ASSERT_EQ(bs, ::pwrite(fd, block.get(), bs, 0));
952+
ASSERT_EQ(bs, ::pwrite(fd, block.get(), bs, 2 * bs));
953+
::fsync(fd);
954+
955+
// Wrap the fd in an AioFileAdaptor (posixaio).
956+
std::unique_ptr<IFile> f(new_localfile_adaptor(fd, ioengine_posixaio));
957+
ASSERT_NE(nullptr, f.get());
958+
959+
// SEEK_DATA / SEEK_HOLE reach the kernel and sync the self-managed offset.
960+
off_t d = f->lseek(0, SEEK_DATA);
961+
EXPECT_EQ(0, d);
962+
EXPECT_EQ(d, f->lseek(0, SEEK_CUR));
963+
964+
off_t h = f->lseek(0, SEEK_HOLE);
965+
EXPECT_EQ(bs, h);
966+
EXPECT_EQ(h, f->lseek(0, SEEK_CUR));
967+
968+
EXPECT_EQ(3 * bs, f->lseek(2 * bs, SEEK_HOLE));
969+
970+
// Plain whence values still follow the VirtualFile (m_offset) route.
971+
EXPECT_EQ(0, f->lseek(0, SEEK_SET));
972+
EXPECT_EQ(bs, f->lseek(bs, SEEK_CUR));
973+
EXPECT_EQ(3 * bs, f->lseek(0, SEEK_END));
974+
}
975+
#endif // __linux__
976+
940977
std::unique_ptr<char[]> random_block(uint64_t size) {
941978
std::unique_ptr<char[]> buff(new char[size]);
942979
char * p = buff.get();

0 commit comments

Comments
 (0)