virtiofs: add support for statx syscall (#14087)

* virtiofs: add support for statx syscall

* add statx drvfs test

* skip statx drvfs test on wsl1

* skip stax tests on smb and fat

---------

Co-authored-by: Ben Hillis <benhill@ntdev.microsoft.com>
This commit is contained in:
Ben Hillis 2026-01-23 14:09:41 -08:00 committed by GitHub
parent d8cd707384
commit 081388c5f5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 191 additions and 14 deletions

View File

@ -18,7 +18,7 @@
<package id="Microsoft.WSL.bsdtar" version="0.0.2-2" />
<package id="Microsoft.WSL.Dependencies.amd64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
<package id="Microsoft.WSL.Dependencies.arm64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
<package id="Microsoft.WSL.DeviceHost" version="1.1.8-0" />
<package id="Microsoft.WSL.DeviceHost" version="1.1.10-0" />
<package id="Microsoft.WSL.Kernel" version="6.6.114.1-1" targetFramework="native" />
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
<package id="Microsoft.WSL.TestDistro" version="2.5.7-47" />

View File

@ -156,6 +156,8 @@ LXT_VARIATION_HANDLER DrvFsTestFatWslPath;
LXT_VARIATION_HANDLER DrvFsTestFstat;
LXT_VARIATION_HANDLER DrvFsTestStatx;
LXT_VARIATION_HANDLER DrvFsTestGetDents64Alignment;
LXT_VARIATION_HANDLER DrvFsTestGetDentsAlignment;
@ -246,6 +248,7 @@ static const LXT_VARIATION g_LxtVariations[] = {
{"DrvFs - hard links", DrvFsTestHardLinks},
{"DrvFs - block count", DrvFsTestBlockCount},
{"DrvFs - fstat", DrvFsTestFstat},
{"DrvFs - statx", DrvFsTestStatx},
{"DrvFs - reopen unlinked file", DrvFsTestReopenUnlinked},
{"DrvFs - delete loop", DrvFsTestDeleteLoop},
{"DrvFs - seek", DrvFsTestSeek},
@ -336,6 +339,7 @@ static const LXT_VARIATION g_LxtMetadataVariations[] = {
{"DrvFs - hard links", DrvFsTestHardLinks},
{"DrvFs - block count", DrvFsTestBlockCount},
{"DrvFs - fstat", DrvFsTestFstat},
{"DrvFs - statx", DrvFsTestStatx},
{"DrvFs - reopen unlinked file", DrvFsTestReopenUnlinked},
{"DrvFs - delete loop", DrvFsTestDeleteLoop},
{"DrvFs - seek", DrvFsTestSeek},
@ -368,6 +372,7 @@ static const LXT_VARIATION g_LxtReFsVariations[] = {
{"DrvFs - hard links", DrvFsTestHardLinks},
{"DrvFs - block count", DrvFsTestBlockCount},
{"DrvFs - fstat", DrvFsTestFstat},
{"DrvFs - statx", DrvFsTestStatx},
{"DrvFs - reopen unlinked file", DrvFsTestReopenUnlinked},
{"DrvFs - delete loop", DrvFsTestDeleteLoop},
{"DrvFs - seek", DrvFsTestSeek},
@ -1389,13 +1394,6 @@ Return Value:
int Result;
if (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)
{
LxtLogInfo("TODO: debug this test on virtiofs.");
Result = 0;
goto ErrorExit;
}
LxtCheckErrno(LxtFsDeleteCurrentWorkingDirectoryCommon(DRVFS_PREFIX, FS_DELETE_DRVFS));
ErrorExit:
@ -2027,6 +2025,180 @@ ErrorExit:
return Result;
}
int DrvFsTestStatx(PLXT_ARGS Args)
/*++
Description:
This routine tests the statx system call on drvfs files.
Arguments:
Args - Supplies the command line arguments.
Return Value:
Returns 0 on success, -1 on failure.
--*/
{
int Fd;
int Result;
struct stat Stat1;
struct statx Statx1;
struct statx Statx2;
Fd = -1;
if (g_LxtFsInfo.FsType == LxtFsTypeDrvFs)
{
LxtLogInfo("statx is not supported on drvfs in WSL1.");
Result = 0;
goto ErrorExit;
}
//
// Create a test file and symlink.
//
LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));
LxtCheckErrno(Fd = creat(DRVFS_BASIC_PREFIX "/testfile", 0666));
LxtCheckErrnoZeroSuccess(symlink(DRVFS_BASIC_PREFIX "/testfile", DRVFS_BASIC_PREFIX "/testlink"));
//
// Stat and statx should have consistent results.
//
LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX "/testfile", &Stat1));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_BASIC_STATS, &Statx1));
LxtCheckEqual(Stat1.st_ino, Statx1.stx_ino, "%llu");
LxtCheckEqual(Stat1.st_size, Statx1.stx_size, "%llu");
LxtCheckEqual(Stat1.st_mode, Statx1.stx_mode, "0%o");
//
// Statx with AT_EMPTY_PATH on an fd.
//
LxtCheckErrnoZeroSuccess(statx(Fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &Statx2));
LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
//
// Test AT_SYMLINK_NOFOLLOW flag.
//
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testlink", AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, &Statx2));
LxtCheckTrue(S_ISLNK(Statx2.stx_mode));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testlink", 0, STATX_BASIC_STATS, &Statx2));
LxtCheckTrue(S_ISREG(Statx2.stx_mode));
//
// Test STATX_BTIME (birth/creation time).
//
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_BASIC_STATS | STATX_BTIME, &Statx2));
if (Statx2.stx_mask & STATX_BTIME)
{
LxtLogInfo("Birth time supported: tv_sec=%lld", (long long)Statx2.stx_btime.tv_sec);
}
//
// Test sync flags.
//
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", AT_STATX_FORCE_SYNC, STATX_BASIC_STATS, &Statx2));
LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", AT_STATX_DONT_SYNC, STATX_BASIC_STATS, &Statx2));
LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
//
// Test individual field masks.
//
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_TYPE, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_TYPE) != 0);
LxtCheckTrue(S_ISREG(Statx2.stx_mode));
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_MODE, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_MODE) != 0);
LxtCheckEqual((Statx1.stx_mode & 0777), (Statx2.stx_mode & 0777), "0%o");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_NLINK, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_NLINK) != 0);
LxtCheckEqual(Statx1.stx_nlink, Statx2.stx_nlink, "%u");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_UID, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_UID) != 0);
LxtCheckEqual(Statx1.stx_uid, Statx2.stx_uid, "%u");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_GID, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_GID) != 0);
LxtCheckEqual(Statx1.stx_gid, Statx2.stx_gid, "%u");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_ATIME, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_ATIME) != 0);
LxtCheckEqual(Statx1.stx_atime.tv_sec, Statx2.stx_atime.tv_sec, "%lld");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_MTIME, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_MTIME) != 0);
LxtCheckEqual(Statx1.stx_mtime.tv_sec, Statx2.stx_mtime.tv_sec, "%lld");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_CTIME, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_CTIME) != 0);
LxtCheckEqual(Statx1.stx_ctime.tv_sec, Statx2.stx_ctime.tv_sec, "%lld");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_INO, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_INO) != 0);
LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_SIZE, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_SIZE) != 0);
LxtCheckEqual(Statx1.stx_size, Statx2.stx_size, "%llu");
memset(&Statx2, 0, sizeof(Statx2));
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_BLOCKS, &Statx2));
LxtCheckTrue((Statx2.stx_mask & STATX_BLOCKS) != 0);
LxtCheckEqual(Statx1.stx_blocks, Statx2.stx_blocks, "%llu");
//
// Test on a directory.
//
LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX, 0, STATX_BASIC_STATS, &Statx2));
LxtCheckTrue(S_ISDIR(Statx2.stx_mode));
//
// Test error case.
//
LxtCheckErrnoFailure(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/nonexistent", 0, STATX_BASIC_STATS, &Statx2), ENOENT);
ErrorExit:
if (Fd >= 0)
{
close(Fd);
}
unlink(DRVFS_BASIC_PREFIX "/testlink");
unlink(DRVFS_BASIC_PREFIX "/testfile");
rmdir(DRVFS_BASIC_PREFIX);
return Result;
}
int DrvFsTestGetDents64Alignment(PLXT_ARGS Args)
/*++

View File

@ -444,13 +444,18 @@ Return Value:
//
// Try to chdir to the parent.
//
// N.B. This currently doesn't work on virtiofs.
//
LxtCheckErrnoZeroSuccess(chdir(".."));
LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));
LxtCheckStringEqual(Path, BaseDir);
memset(Path, 0, sizeof(Path));
LxtCheckErrno(readlink(FS_PROC_SELF_CWD, Path, sizeof(Path)));
LxtCheckStringEqual(Path, BaseDir);
if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)
{
LxtCheckErrnoZeroSuccess(chdir(".."));
LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));
LxtCheckStringEqual(Path, BaseDir);
memset(Path, 0, sizeof(Path));
LxtCheckErrno(readlink(FS_PROC_SELF_CWD, Path, sizeof(Path)));
LxtCheckStringEqual(Path, BaseDir);
}
//
// Try to chdir back to the deleted directory.