Skip to content

[wasi] Implement MksTemps/MkdTemp so GetTempFileName and CreateTempSubdirectory work - #134958

Merged
lewing merged 2 commits into
dotnet:mainfrom
lewing:lewing-wasi-mkstemps-fix
Oct 6, 2026
Merged

lewing merged 2 commits into
dotnet:mainfrom
lewing:lewing-wasi-mkstemps-fix

Conversation

@lewing

@lewing lewing commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

wasi-libc provides neither mkstemps/mkstemp nor mkdtemp. So on WASI, SystemNative_MksTemps asserted and returned -1 without setting errno, and SystemNative_MkdTemp returned NULL the same way. As a result, Path.GetTempFileName threw IOException : Success : '/tmp/', and Directory.CreateTempSubdirectory was also broken. The temp directory itself works fine; the only missing piece was creating a unique name in it.

Change

src/native/libs/System.Native/pal_io.c, WASI only. This mirrors how libc implements these functions:

  • GetTempNameRandomChars validates the template. It requires six X characters immediately before the suffix and otherwise fails with errno = EINVAL.
  • FillTempNameRandomChars fills the XXXXXX from getentropy (backed by wasi:random), mapped to [A-Za-z0-9].
  • SystemNative_MksTemps loops up to 100 times calling open(path, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, 0600). It returns the fd on success, retries on EEXIST, and returns -1 with errno preserved on any other error. If every attempt collides, it fails with errno = EEXIST.
  • SystemNative_MkdTemp runs the same loop with mkdir(path, 0700). It returns the template on success and NULL with errno set on failure.

Non-WASI native code paths are unchanged. The WASI preprocessor branch also uses defined(TARGET_WASI). Because this lives in the shared System.Native, the fix applies to both CoreCLR and Mono on WASI.

After #134813 merged, this branch was rebased onto upstream main fa693c42fb6. It removes all 89 WASI ActiveIssue attributes against #134943 across 24 test files, preserving other attributes, comments, and test code.

Removing the class-wide configuration quarantine exposed a separate missing-user-profile failure. Only ConfigurationTests.TouchingFileWillReloadForUserSecrets remains quarantined on WASI, now against #135225. No harness defaults or unrelated product code change in this PR.

Validation after rebase and quarantine removal

Local macOS arm64 host, wasi-sdk 34, wasmtime via XHarness, CoreCLR WASI interpreter (not ReadyToRun). The worktree-local tool-cache override avoids modifying a concurrently used checkout.

The product command below passed with 0 warnings and 0 errors. Initially the existing CMake caches retained SDK-33 compiler paths despite selecting the SDK-34 toolchain and failed to link WASI networking symbols. Clearing only this worktree's generated WASI CoreCLR/native object directories resolved the mismatch; no source workaround was used.

export DOTNET_WASM_TOOL_CACHE_DIR="$PWD/.dotnet/wasm-tools"
./build.sh -os wasi -arch wasm -rf CoreCLR -s clr+libs+host+packs -c Release
./dotnet.sh build src/libraries/Microsoft.Extensions.Configuration.FileExtensions/tests/Microsoft.Extensions.Configuration.FileExtensions.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/Microsoft.Extensions.Configuration/tests/FunctionalTests/Microsoft.Extensions.Configuration.Functional.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/Microsoft.VisualBasic.Core/tests/Microsoft.VisualBasic.Core.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.CodeDom/tests/System.CodeDom.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.ComponentModel.Composition/tests/System.ComponentModel.Composition.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.Data.Common/tests/System.Data.Common.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.Diagnostics.TextWriterTraceListener/tests/System.Diagnostics.TextWriterTraceListener.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.Memory.Data/tests/System.Memory.Data.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.Private.Xml/tests/System.Private.Xml.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.Reflection.Metadata/tests/System.Reflection.Metadata.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.Runtime.Loader/tests/RefEmitLoadContext/System.Runtime.Loader.RefEmitLoadContext.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
WasmXHarnessMonoArgs='--env=DOTNET_CI=true' ./dotnet.sh build src/libraries/System.Runtime/tests/System.IO.Tests/System.IO.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release
./dotnet.sh build src/libraries/System.ServiceModel.Syndication/tests/System.ServiceModel.Syndication.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release

All commands above exited 0. Counts were checked in the runner logs and xUnit XML:

Suite Run Passed Failed Skipped
Microsoft.Extensions.Configuration.FileExtensions.Tests 20 20 0 0
Microsoft.Extensions.Configuration.Functional.Tests 32 29 0 3
Microsoft.VisualBasic.Core.Tests 22,219 22,215 0 4
System.CodeDom.Tests 5,982 5,977 0 5
System.ComponentModel.Composition.Tests 1,431 1,426 0 5
System.Data.Common.Tests 11,717 11,708 0 9
System.Diagnostics.TextWriterTraceListener.Tests 132 130 0 2
System.Memory.Data.Tests 87 81 0 6
System.Private.Xml.Tests 48,626 48,558 0 68
System.Reflection.Metadata.Tests 880 869 0 11
System.Runtime.Loader.RefEmitLoadContext.Tests 1 1 0 0
System.IO.Tests 3,019 2,963 0 56
System.ServiceModel.Syndication.Tests 1,323 1,323 0 0
Total 95,469 95,300 0 169

System.ComponentModel.Composition.Tests was exercised in the interpreter lane, even though it is excluded from the trimmed WASI R2R lane (#135045).

Two initial-run findings:

  • Configuration Functional initially reported 33 run, 29 passed, 1 failed, 3 skipped: the missing HOME failure now tracked in [wasi] Library tests have no HOME/user profile (user secrets path) #135225. A diagnostic re-run of the unquarantined bundle with guest HOME=/tmp reported 33 run, 30 passed, 0 failed, 3 skipped. The final normal-environment run after the method-only quarantine reported the 32-test result above; 27 tests in ConfigurationTests still passed.
  • System.IO initially reported 3,020 run, 2,963 passed, 1 failed, 56 skipped, solely in the unchanged MemoryStream_CapacityBoundaryChecks large-allocation test. That test already carries SkipOnCI; forwarding the existing CI guest setting DOTNET_CI=true reproduces the interpreter CI lane and gives the passing result above. No new MemoryStream quarantine was added, and all eight re-enabled StreamReader/StreamWriter temporary-file cases passed.

Original before/after reproduction

Before the test-standup merge, on the SDK-33 baseline:

./build.sh -os wasi -arch wasm -rf CoreCLR -s clr+libs -c Release
./build.sh -os wasi -arch wasm -rf CoreCLR -s libs.native -c Release
./dotnet.sh build src/libraries/System.Memory.Data/tests/System.Memory.Data.Tests.csproj /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm /p:RuntimeFlavor=CoreCLR /p:Configuration=Release /p:TargetFramework=net11.0

The product build passed with zero warnings/errors. Stashing the native fix, rebuilding libs.native, and running Memory.Data reproduced 87 run, 80 passed, 1 failed, 6 skipped (BinaryDataTests.CanCreateBinaryDataFromFileStream: IOException : Success : '/tmp/'). Restoring the fix, rebuilding libs.native, and repeating the same test command passed: 87 run, 81 passed, 0 failed, 6 skipped.

The per-app wasihost links libSystem.Native.a from the runtime pack, so the rebuilt library is what the tests use.

System.Runtime.Extensions.Tests (PathTests.GetTempFileName) and System.IO.FileSystem.Tests (Directory_CreateTempSubdirectory) do not target WASI (-windows;-unix;-browser only), so they were not run. Mono WASI and the ReadyToRun lane were not run.

Resolves #134943

Note

This PR was authored with assistance from GitHub Copilot.

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).
13 pipeline(s) were filtered out due to trigger conditions.
There may be pipelines that require an authorized user to comment /azp run to run.

@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @dotnet/area-system-io
See info in area-owners.md if you want to be subscribed.

@lewing lewing added arch-wasm WebAssembly architecture os-wasi Related to WASI variant of arch-wasm labels Sep 30, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to 'arch-wasm': @lewing, @pavelsavara
See info in area-owners.md if you want to be subscribed.

lewing added a commit to lewing/runtime that referenced this pull request Sep 30, 2026
Path.GetTempFileName/Directory.CreateTempSubdirectory fail because MksTemps/MkdTemp are stubbed (dotnet#134943, fix in dotnet#134958), and File.Copy fails because fchmod returns ENOSYS (dotnet#134944, fix in dotnet#134959). Quarantine at class level where the test constructor fails and at method level otherwise, so the lanes can go green before the fixes merge.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Oct 1, 2026
Path.GetTempFileName/Directory.CreateTempSubdirectory fail because MksTemps/MkdTemp are stubbed (dotnet#134943, fix in dotnet#134958), and File.Copy fails because fchmod returns ENOSYS (dotnet#134944, fix in dotnet#134959). Quarantine at class level where the test constructor fails and at method level otherwise, so the lanes can go green before the fixes merge.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Oct 1, 2026
Path.GetTempFileName/Directory.CreateTempSubdirectory fail because MksTemps/MkdTemp are stubbed (dotnet#134943, fix in dotnet#134958), and File.Copy fails because fchmod returns ENOSYS (dotnet#134944, fix in dotnet#134959). Quarantine at class level where the test constructor fails and at method level otherwise, so the lanes can go green before the fixes merge.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Oct 2, 2026
Path.GetTempFileName/Directory.CreateTempSubdirectory fail because MksTemps/MkdTemp are stubbed (dotnet#134943, fix in dotnet#134958), and File.Copy fails because fchmod returns ENOSYS (dotnet#134944, fix in dotnet#134959). Quarantine at class level where the test constructor fails and at method level otherwise, so the lanes can go green before the fixes merge.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lewing
lewing marked this pull request as ready for review October 2, 2026 01:46
lewing added a commit that referenced this pull request Oct 2, 2026
)

The CoreCLR WASI host (`wasihost`) initializes the runtime with only
`TRUSTED_PLATFORM_ASSEMBLIES`, `APP_PATHS` and the host runtime
contract. It never reads the app's runtime configuration, so none of the
`configProperties` from `<app>.runtimeconfig.json` reach `AppContext`:
feature switches, `RuntimeHostConfigurationOption` items and so on. For
example, `System.Text.Encoding.EnableUnsafeUTF7Encoding` never arrives,
so UTF-7 tests throw `NotSupportedException : Support for UTF-7 is
disabled`. When trimming,
`System.Data.DataSet.XmlSerializationIsSupported` is seen as missing by
test `PlatformDetection`.

## Change

- `src/native/corehost/wasihost/wasihost.cpp`: read `runtimeconfig.bin`
from next to the entry assembly and append its properties to the init
properties passed to `coreclr_initialize`. Properties the host sets
itself take precedence. `get_runtime_property` already serves from the
same vectors, so the contract callback returns the same values. A
missing file is ignored, and a malformed one fails startup with a
message.
- `src/mono/wasi/build/WasiApp.CoreCLR.targets`: copy
`runtimeconfig.bin` into `managed/`, next to the entry assembly, the
framework, `icudt.dat` and the host. `WasiAppBuilder` places it at the
bundle root, which `run-wasmtime.sh` (`cd managed && wasmtime --dir .
...`) does not preopen.

`runtimeconfig.bin` is the file `RuntimeConfigParserTask` already
produces for WASI apps, and Mono's WASI driver consumes the same file.
Its format is an ECMA-335 compressed count followed by key/value
serialized strings, so the host needs no JSON parser. The browser
CoreCLR host follows the same model: `configProperties` first, with
host-set properties overriding them.

## Validation

Local, macOS arm64 host, wasi-sdk 33, wasmtime 45.

- `./build.sh -os wasi -arch wasm -rf CoreCLR -s clr+libs+host -c
Release` on clean upstream `main` → succeeded, 0 warnings, 0 errors.
- `./build.sh -os wasi -arch wasm -rf CoreCLR -s host.native -c Release`
with the change → succeeded; `wasihost.cpp` compiles with no warnings.

**System.Text.Encoding.Tests** (untrimmed, `./dotnet.sh build
src/libraries/System.Runtime/tests/System.Text.Encoding.Tests/System.Text.Encoding.Tests.csproj
-f net11.0 /t:Test /p:TargetOS=wasi /p:TargetArchitecture=wasm
/p:RuntimeFlavor=CoreCLR /p:Configuration=Release`)

On `main`, the full suite hangs in
`TranscodingStreamTests.ReadAsync_LoopsWhenPartialDataReceived`, a
blocking wait on single-threaded WASI that is handled in #134813. So I
compared the UTF-7-related classes directly with wasmtime on the bundle
(`-class` for EncodingMiscTests, EncodingGetEncodingTest,
NegativeEncodingTests and the seven `UTF7Encoding*` classes):

| | Tests run | Passed | Failed |
|---|---|---|---|
| Without fix | 1692 | 1654 | **38** (all `Support for UTF-7 is
disabled`) |
| With fix | 1720 | 1720 | **0** |

Full suite with the fix, with the hanging test skipped locally only (not
part of this PR): 14614 run, 14605 passed, 4 failed, 5 skipped, 0 UTF-7
failures. The 4 failures are unrelated:
`FromUtf16Tests`/`ToUtf16Tests.SomeNonAsciiInput` need
System.Security.Cryptography (#99126), and
`TranscodingStreamTests.ReadApm`/`WriteApm` throw
`PlatformNotSupportedException` from `TaskToAsyncResult.Begin`.

**System.Data.Common.Tests** (untrimmed, same command with the
System.Data.Common.Tests project)

- With fix: 11734 run, 11658 passed, 67 failed, 9 skipped. The same
bundle run with `managed/runtimeconfig.bin` removed, which reproduces
the pre-fix behavior, gives the identical 67 failures. So there are no
regressions. 54 of the failures are `IOException : Success : '/tmp/'`
(#134958), and the rest are DbProviderFactories and serialization-guard
failures unrelated to runtimeconfig.
- The DataSet switch only lands in runtimeconfig when trimming, and
trimmed CoreCLR-WASI test builds don't work on `main` yet (ILLink can't
resolve System.Private.CoreLib; #134813 sets that up). To confirm the
switch now reaches both the product and `PlatformDetection`, I added
`System.Data.DataSet.XmlSerializationIsSupported=false` to the bundle's
`runtimeconfig.bin` and ran `-class System.Data.Tests.DataSetTest -class
System.Data.Tests.DataTableTest5`. The original config gave 63 run, 48
passed, 14 failed (`/tmp`), 1 skipped. With the switch it gave 63 run,
44 passed, 0 failed, 19 skipped: the guarded tests skip and nothing
fails from a product/test mismatch.

This is split out from the WASI R2R test-standup work in #134813, where
the missing properties account for the 38 UTF-7 failures and about 360
System.Data.Common failures in trimmed runs.

Resolves #134954

> [!NOTE]
> This PR was authored with assistance from GitHub Copilot.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
lewing added a commit to lewing/runtime that referenced this pull request Oct 3, 2026
Path.GetTempFileName/Directory.CreateTempSubdirectory fail because MksTemps/MkdTemp are stubbed (dotnet#134943, fix in dotnet#134958), and File.Copy fails because fchmod returns ENOSYS (dotnet#134944, fix in dotnet#134959). Quarantine at class level where the test constructor fails and at method level otherwise, so the lanes can go green before the fixes merge.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@jkotas

jkotas commented Oct 5, 2026

Copy link
Copy Markdown
Member

Re-enable tests disabled against #134943

wasi-libc provides neither mkstemp(s) nor mkdtemp, so on WASI these entry
points were stubbed out and returned failure without setting errno. This made
Path.GetTempFileName throw "IOException : Success : '/tmp/'" and broke
Directory.CreateTempSubdirectory.

Emulate them the way libc does: validate the template, fill the trailing
XXXXXX from getentropy mapped to [A-Za-z0-9], and create the entry with
open(O_CREAT|O_EXCL) / mkdir, retrying on EEXIST a bounded number of times.
errno is set on every failure path. Non-WASI code paths are unchanged.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Remove all 89 WASI quarantines against issue 134943 after the MksTemps/MkdTemp fix. Preserve unrelated attributes and test code.

Keep only the independently failing user-secrets reload test quarantined against issue 135225; the rest of ConfigurationTests now runs on WASI.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@lewing

lewing commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

filed #135268 for the error

@lewing
lewing requested a review from akoeplinger October 6, 2026 09:25
@lewing

lewing commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

/azp run runtime-coreclr outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 1 pipeline(s).

@lewing

lewing commented Oct 6, 2026

Copy link
Copy Markdown
Member Author

/ba-g unrelated failures are filed

@lewing
lewing merged commit d0e41cb into dotnet:main Oct 6, 2026
170 of 174 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-wasm WebAssembly architecture area-System.IO os-wasi Related to WASI variant of arch-wasm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[wasi] Path.GetTempFileName and Directory.CreateTempSubdirectory fail: MksTemps/MkdTemp are stubbed on WASI

3 participants