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>
Ben Hillis committed
Jan 23, 2026 at 14:09 UTC
081388c5f5a7e6468c394c2444ded8f2a6370dcc
3 files changed
+191
-14
packages.config
+1
-1
@@ -18,7 +18,7 @@
18
<package id="Microsoft.WSL.bsdtar" version="0.0.2-2" />
19
<package id="Microsoft.WSL.Dependencies.amd64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
20
<package id="Microsoft.WSL.Dependencies.arm64fre" version="10.0.27820.1000-250318-1700.rs-base2-hyp" targetFramework="native" />
21
- <package id="Microsoft.WSL.DeviceHost" version="1.1.8-0" />
21
+ <package id="Microsoft.WSL.DeviceHost" version="1.1.10-0" />
22
<package id="Microsoft.WSL.Kernel" version="6.6.114.1-1" targetFramework="native" />
23
<package id="Microsoft.WSL.LinuxSdk" version="1.20.0" targetFramework="native" />
24
<package id="Microsoft.WSL.TestDistro" version="2.5.7-47" />
test/linux/unit_tests/drvfs.c
+179
-7
@@ -156,6 +156,8 @@ LXT_VARIATION_HANDLER DrvFsTestFatWslPath;
156
157
LXT_VARIATION_HANDLER DrvFsTestFstat;
158
159
+LXT_VARIATION_HANDLER DrvFsTestStatx;
160
+
161
LXT_VARIATION_HANDLER DrvFsTestGetDents64Alignment;
162
163
LXT_VARIATION_HANDLER DrvFsTestGetDentsAlignment;
@@ -246,6 +248,7 @@ static const LXT_VARIATION g_LxtVariations[] = {
248
{"DrvFs - hard links", DrvFsTestHardLinks},
249
{"DrvFs - block count", DrvFsTestBlockCount},
250
{"DrvFs - fstat", DrvFsTestFstat},
251
+ {"DrvFs - statx", DrvFsTestStatx},
252
{"DrvFs - reopen unlinked file", DrvFsTestReopenUnlinked},
253
{"DrvFs - delete loop", DrvFsTestDeleteLoop},
254
{"DrvFs - seek", DrvFsTestSeek},
@@ -336,6 +339,7 @@ static const LXT_VARIATION g_LxtMetadataVariations[] = {
339
{"DrvFs - hard links", DrvFsTestHardLinks},
340
{"DrvFs - block count", DrvFsTestBlockCount},
341
{"DrvFs - fstat", DrvFsTestFstat},
342
+ {"DrvFs - statx", DrvFsTestStatx},
343
{"DrvFs - reopen unlinked file", DrvFsTestReopenUnlinked},
344
{"DrvFs - delete loop", DrvFsTestDeleteLoop},
345
{"DrvFs - seek", DrvFsTestSeek},
@@ -368,6 +372,7 @@ static const LXT_VARIATION g_LxtReFsVariations[] = {
372
{"DrvFs - hard links", DrvFsTestHardLinks},
373
{"DrvFs - block count", DrvFsTestBlockCount},
374
{"DrvFs - fstat", DrvFsTestFstat},
375
+ {"DrvFs - statx", DrvFsTestStatx},
376
{"DrvFs - reopen unlinked file", DrvFsTestReopenUnlinked},
377
{"DrvFs - delete loop", DrvFsTestDeleteLoop},
378
{"DrvFs - seek", DrvFsTestSeek},
@@ -1389,13 +1394,6 @@ Return Value:
1394
1395
int Result;
1396
1392
- if (g_LxtFsInfo.FsType == LxtFsTypeVirtioFs)
1393
- {
1394
- LxtLogInfo("TODO: debug this test on virtiofs.");
1395
- Result = 0;
1396
- goto ErrorExit;
1397
- }
1398
-
1397
LxtCheckErrno(LxtFsDeleteCurrentWorkingDirectoryCommon(DRVFS_PREFIX, FS_DELETE_DRVFS));
1398
1399
ErrorExit:
@@ -2027,6 +2025,180 @@ ErrorExit:
2025
return Result;
2026
}
2027
2028
+int DrvFsTestStatx(PLXT_ARGS Args)
2029
+
2030
+/*++
2031
+
2032
+Description:
2033
+
2034
+ This routine tests the statx system call on drvfs files.
2035
+
2036
+Arguments:
2037
+
2038
+ Args - Supplies the command line arguments.
2039
+
2040
+Return Value:
2041
+
2042
+ Returns 0 on success, -1 on failure.
2043
+
2044
+--*/
2045
+
2046
+{
2047
+
2048
+ int Fd;
2049
+ int Result;
2050
+ struct stat Stat1;
2051
+ struct statx Statx1;
2052
+ struct statx Statx2;
2053
+
2054
+ Fd = -1;
2055
+
2056
+ if (g_LxtFsInfo.FsType == LxtFsTypeDrvFs)
2057
+ {
2058
+ LxtLogInfo("statx is not supported on drvfs in WSL1.");
2059
+ Result = 0;
2060
+ goto ErrorExit;
2061
+ }
2062
+
2063
+ //
2064
+ // Create a test file and symlink.
2065
+ //
2066
+
2067
+ LxtCheckErrnoZeroSuccess(mkdir(DRVFS_BASIC_PREFIX, 0777));
2068
+ LxtCheckErrno(Fd = creat(DRVFS_BASIC_PREFIX "/testfile", 0666));
2069
+ LxtCheckErrnoZeroSuccess(symlink(DRVFS_BASIC_PREFIX "/testfile", DRVFS_BASIC_PREFIX "/testlink"));
2070
+
2071
+ //
2072
+ // Stat and statx should have consistent results.
2073
+ //
2074
+
2075
+ LxtCheckErrnoZeroSuccess(stat(DRVFS_BASIC_PREFIX "/testfile", &Stat1));
2076
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_BASIC_STATS, &Statx1));
2077
+ LxtCheckEqual(Stat1.st_ino, Statx1.stx_ino, "%llu");
2078
+ LxtCheckEqual(Stat1.st_size, Statx1.stx_size, "%llu");
2079
+ LxtCheckEqual(Stat1.st_mode, Statx1.stx_mode, "0%o");
2080
+
2081
+ //
2082
+ // Statx with AT_EMPTY_PATH on an fd.
2083
+ //
2084
+
2085
+ LxtCheckErrnoZeroSuccess(statx(Fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &Statx2));
2086
+ LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
2087
+
2088
+ //
2089
+ // Test AT_SYMLINK_NOFOLLOW flag.
2090
+ //
2091
+
2092
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testlink", AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, &Statx2));
2093
+ LxtCheckTrue(S_ISLNK(Statx2.stx_mode));
2094
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testlink", 0, STATX_BASIC_STATS, &Statx2));
2095
+ LxtCheckTrue(S_ISREG(Statx2.stx_mode));
2096
+
2097
+ //
2098
+ // Test STATX_BTIME (birth/creation time).
2099
+ //
2100
+
2101
+ memset(&Statx2, 0, sizeof(Statx2));
2102
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_BASIC_STATS | STATX_BTIME, &Statx2));
2103
+ if (Statx2.stx_mask & STATX_BTIME)
2104
+ {
2105
+ LxtLogInfo("Birth time supported: tv_sec=%lld", (long long)Statx2.stx_btime.tv_sec);
2106
+ }
2107
+
2108
+ //
2109
+ // Test sync flags.
2110
+ //
2111
+
2112
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", AT_STATX_FORCE_SYNC, STATX_BASIC_STATS, &Statx2));
2113
+ LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
2114
+
2115
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", AT_STATX_DONT_SYNC, STATX_BASIC_STATS, &Statx2));
2116
+ LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
2117
+
2118
+ //
2119
+ // Test individual field masks.
2120
+ //
2121
+
2122
+ memset(&Statx2, 0, sizeof(Statx2));
2123
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_TYPE, &Statx2));
2124
+ LxtCheckTrue((Statx2.stx_mask & STATX_TYPE) != 0);
2125
+ LxtCheckTrue(S_ISREG(Statx2.stx_mode));
2126
+
2127
+ memset(&Statx2, 0, sizeof(Statx2));
2128
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_MODE, &Statx2));
2129
+ LxtCheckTrue((Statx2.stx_mask & STATX_MODE) != 0);
2130
+ LxtCheckEqual((Statx1.stx_mode & 0777), (Statx2.stx_mode & 0777), "0%o");
2131
+
2132
+ memset(&Statx2, 0, sizeof(Statx2));
2133
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_NLINK, &Statx2));
2134
+ LxtCheckTrue((Statx2.stx_mask & STATX_NLINK) != 0);
2135
+ LxtCheckEqual(Statx1.stx_nlink, Statx2.stx_nlink, "%u");
2136
+
2137
+ memset(&Statx2, 0, sizeof(Statx2));
2138
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_UID, &Statx2));
2139
+ LxtCheckTrue((Statx2.stx_mask & STATX_UID) != 0);
2140
+ LxtCheckEqual(Statx1.stx_uid, Statx2.stx_uid, "%u");
2141
+
2142
+ memset(&Statx2, 0, sizeof(Statx2));
2143
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_GID, &Statx2));
2144
+ LxtCheckTrue((Statx2.stx_mask & STATX_GID) != 0);
2145
+ LxtCheckEqual(Statx1.stx_gid, Statx2.stx_gid, "%u");
2146
+
2147
+ memset(&Statx2, 0, sizeof(Statx2));
2148
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_ATIME, &Statx2));
2149
+ LxtCheckTrue((Statx2.stx_mask & STATX_ATIME) != 0);
2150
+ LxtCheckEqual(Statx1.stx_atime.tv_sec, Statx2.stx_atime.tv_sec, "%lld");
2151
+
2152
+ memset(&Statx2, 0, sizeof(Statx2));
2153
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_MTIME, &Statx2));
2154
+ LxtCheckTrue((Statx2.stx_mask & STATX_MTIME) != 0);
2155
+ LxtCheckEqual(Statx1.stx_mtime.tv_sec, Statx2.stx_mtime.tv_sec, "%lld");
2156
+
2157
+ memset(&Statx2, 0, sizeof(Statx2));
2158
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_CTIME, &Statx2));
2159
+ LxtCheckTrue((Statx2.stx_mask & STATX_CTIME) != 0);
2160
+ LxtCheckEqual(Statx1.stx_ctime.tv_sec, Statx2.stx_ctime.tv_sec, "%lld");
2161
+
2162
+ memset(&Statx2, 0, sizeof(Statx2));
2163
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_INO, &Statx2));
2164
+ LxtCheckTrue((Statx2.stx_mask & STATX_INO) != 0);
2165
+ LxtCheckEqual(Statx1.stx_ino, Statx2.stx_ino, "%llu");
2166
+
2167
+ memset(&Statx2, 0, sizeof(Statx2));
2168
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_SIZE, &Statx2));
2169
+ LxtCheckTrue((Statx2.stx_mask & STATX_SIZE) != 0);
2170
+ LxtCheckEqual(Statx1.stx_size, Statx2.stx_size, "%llu");
2171
+
2172
+ memset(&Statx2, 0, sizeof(Statx2));
2173
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/testfile", 0, STATX_BLOCKS, &Statx2));
2174
+ LxtCheckTrue((Statx2.stx_mask & STATX_BLOCKS) != 0);
2175
+ LxtCheckEqual(Statx1.stx_blocks, Statx2.stx_blocks, "%llu");
2176
+
2177
+ //
2178
+ // Test on a directory.
2179
+ //
2180
+
2181
+ LxtCheckErrnoZeroSuccess(statx(AT_FDCWD, DRVFS_BASIC_PREFIX, 0, STATX_BASIC_STATS, &Statx2));
2182
+ LxtCheckTrue(S_ISDIR(Statx2.stx_mode));
2183
+
2184
+ //
2185
+ // Test error case.
2186
+ //
2187
+
2188
+ LxtCheckErrnoFailure(statx(AT_FDCWD, DRVFS_BASIC_PREFIX "/nonexistent", 0, STATX_BASIC_STATS, &Statx2), ENOENT);
2189
+
2190
+ErrorExit:
2191
+ if (Fd >= 0)
2192
+ {
2193
+ close(Fd);
2194
+ }
2195
+
2196
+ unlink(DRVFS_BASIC_PREFIX "/testlink");
2197
+ unlink(DRVFS_BASIC_PREFIX "/testfile");
2198
+ rmdir(DRVFS_BASIC_PREFIX);
2199
+ return Result;
2200
+}
2201
+
2202
int DrvFsTestGetDents64Alignment(PLXT_ARGS Args)
2203
2204
/*++
test/linux/unit_tests/lxtfs.c
+11
-6
@@ -444,13 +444,18 @@ Return Value:
444
//
445
// Try to chdir to the parent.
446
//
447
+ // N.B. This currently doesn't work on virtiofs.
448
+ //
449
448
- LxtCheckErrnoZeroSuccess(chdir(".."));
449
- LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));
450
- LxtCheckStringEqual(Path, BaseDir);
451
- memset(Path, 0, sizeof(Path));
452
- LxtCheckErrno(readlink(FS_PROC_SELF_CWD, Path, sizeof(Path)));
453
- LxtCheckStringEqual(Path, BaseDir);
450
+ if (g_LxtFsInfo.FsType != LxtFsTypeVirtioFs)
451
+ {
452
+ LxtCheckErrnoZeroSuccess(chdir(".."));
453
+ LxtCheckErrno(LxtGetcwd(Path, sizeof(Path)));
454
+ LxtCheckStringEqual(Path, BaseDir);
455
+ memset(Path, 0, sizeof(Path));
456
+ LxtCheckErrno(readlink(FS_PROC_SELF_CWD, Path, sizeof(Path)));
457
+ LxtCheckStringEqual(Path, BaseDir);
458
+ }
459
460
//
461
// Try to chdir back to the deleted directory.