Uh oh!
There was an error while loading. Please reload this page.
Fix apphost/bundle creation failure on chmod-hostile filesystems - #129342
Conversation
There was a problem hiding this comment.
Pull request overview
This PR updates Microsoft.NET.HostModel’s apphost and single-file bundle output writing logic to better handle Unix permission setting on filesystems where a separate chmod step can fail (e.g., EPERM on certain bind mounts). It centralizes “create executable at file creation time” and “enforce permissions when needed” behavior into HostModelUtils, and adds a Unix-only regression test for apphost overwrite behavior.
Changes:
- Add
HostModelUtils.CreateFileStreamForHost(usesFileStreamOptions.UnixCreateModeon Unix) andHostModelUtils.SetPermissionsForHost(conditional permission enforcement). - Update
HostWriterandBundlerto use the shared helpers instead of unconditional post-writeSetUnixFileMode. - Add a Unix-only test ensuring apphost overwrites a pre-existing non-executable destination with executable permissions; remove the net472-only Unix
chmodshim.
Show a summary per file
| File | Description |
|---|---|
| src/installer/tests/Microsoft.NET.HostModel.Tests/AppHost/CreateAppHost.cs | Adds Unix-only regression coverage for overwriting an existing non-executable apphost destination. |
| src/installer/managed/Microsoft.NET.HostModel/Utils/UnixUtils.cs | Removes the net472-only Unix chmod shim (no longer needed with #if NET gating). |
| src/installer/managed/Microsoft.NET.HostModel/HostModelUtils.cs | Introduces shared helpers for “create with exec perms” and “conditional chmod enforcement” on Unix. |
| src/installer/managed/Microsoft.NET.HostModel/Bundle/Bundler.cs | Switches bundle output creation to the new helpers and centralizes permission enforcement. |
| src/installer/managed/Microsoft.NET.HostModel/AppHost/HostWriter.cs | Switches apphost destination stream creation to the new helper and uses shared permission enforcement. |
Copilot's findings
- Files reviewed: 5/5 changed files
- Comments generated: 2
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
752cf33 to
5071bc7CompareCreateAppHost and single-file bundle generation failed inside rootless podman devcontainers because File.SetUnixFileMode (chmod 755) returns EPERM on bind-mounted Windows folders that appear root-owned to the non-root container user, surfacing as: System.UnauthorizedAccessException: Access to the path '.../apphost' is denied. ---> System.IO.IOException: Operation not permitted at System.IO.File.SetUnixFileMode(...) at Microsoft.NET.HostModel.AppHost.HostWriter.CreateAppHost(...) Set the executable permissions when the file is created (via the open syscall using FileStreamOptions.UnixCreateMode) instead of relying on a separate chmod, which is not permitted on such filesystems. The post-write chmod is now only attempted when the file is missing the required permissions, so it is skipped on filesystems where the file was already created executable and still enforces exact permissions (e.g. under a restrictive umask) where chmod is supported. Both HostWriter (apphost) and Bundler (single-file) now share the CreateFileStreamForHost and SetPermissionsForHost helpers. The net472 libc chmod shim (UnixUtils.cs) is removed: net472 only runs on Windows in the modern SDK, where Unix permissions do not apply. Fixesdotnet#129040 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
5071bc7 to
4272452CompareUh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
…9342) ## Summary `dotnet build` and single-file `dotnet publish` fail inside **rootless podman** devcontainers because `File.SetUnixFileMode` (chmod 755) returns `EPERM` on bind-mounted Windows folders. Under rootless podman's default user-namespace mapping, the bind-mounted files appear **root-owned** to the non-root container user (e.g. `vscode`, uid 1000), so a non-owner cannot `chmod` them — even though they are writable. This surfaces as: ``` error MSB4018: The "CreateAppHost" task failed unexpectedly. System.UnauthorizedAccessException: Access to the path '.../obj/Debug/net10.0/apphost' is denied. ---> System.IO.IOException: Operation not permitted at System.IO.File.SetUnixFileMode(String path, UnixFileMode mode) at Microsoft.NET.HostModel.AppHost.HostWriter.CreateAppHost(...) ``` Fixes#129040. cc @dotnet/appmodel@AaronRobinsonMSFT ## Change Set the executable permissions **when the file is created** (via the `open` syscall, using `FileStreamOptions.UnixCreateMode`) instead of relying on a separate `chmod`, which is not permitted on such filesystems. The post-write `chmod` is now only attempted when the file is actually **missing** the required permissions, so it is: - **skipped** on chmod-hostile filesystems where the file was already created executable (fixing the bug), and - still applied where `chmod` is supported, enforcing exact `0755` (e.g. under a restrictive `umask`, where `UnixCreateMode` alone would be masked). Both `HostWriter` (apphost) and `Bundler` (single-file bundle) are affected by the same bug and now share two helpers in `HostModelUtils`: - `CreateFileStreamForHost` — creates the output stream, requesting `0755` at creation time on Unix. - `SetPermissionsForHost` — best-effort enforcement that only `chmod`s when needed. In `HostWriter`, `SetPermissionsForHost` runs inside the existing try/catch so that if the result is genuinely non-executable, the broken apphost is deleted and the error is surfaced rather than silently shipped. The net472-only `libc` `chmod` P/Invoke shim (`Utils/UnixUtils.cs`) is removed — it was the only consumer of `File.SetUnixFileMode` on net472, and net472 only runs on Windows in the modern SDK, where Unix permissions do not apply. ## Testing - Added `ExecutableImageOverwritesExistingNonExecutableFile` (Unix-only) verifying that creating an apphost over a pre-existing non-executable file still yields `-rwxr-xr-x`. - Existing `ExecutableImage` test continues to cover the executable-permission invariant. - Validated end-to-end under **real rootless podman** (non-root user, root-owned bind mount, .NET 10 SDK): both `dotnet build` and `Bundler.GenerateBundle` fail without this change and succeed with it. The conditional-chmod fallback was also confirmed on a normal Linux filesystem. > [!NOTE] > This pull request was authored with the assistance of GitHub Copilot. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
dotnet buildand single-filedotnet publishfail inside rootless podman devcontainers becauseFile.SetUnixFileMode(chmod 755) returnsEPERMon bind-mounted Windows folders. Under rootless podman's default user-namespace mapping, the bind-mounted files appear root-owned to the non-root container user (e.g.vscode, uid 1000), so a non-owner cannotchmodthem — even though they are writable. This surfaces as:Fixes#129040.
cc @dotnet/appmodel @AaronRobinsonMSFT
Change
Set the executable permissions when the file is created (via the
opensyscall, usingFileStreamOptions.UnixCreateMode) instead of relying on a separatechmod, which is not permitted on such filesystems. The post-writechmodis now only attempted when the file is actually missing the required permissions, so it is:chmodis supported, enforcing exact0755(e.g. under a restrictiveumask, whereUnixCreateModealone would be masked).Both
HostWriter(apphost) andBundler(single-file bundle) are affected by the same bug and now share two helpers inHostModelUtils:CreateFileStreamForHost— creates the output stream, requesting0755at creation time on Unix.SetPermissionsForHost— best-effort enforcement that onlychmods when needed.In
HostWriter,SetPermissionsForHostruns inside the existing try/catch so that if the result is genuinely non-executable, the broken apphost is deleted and the error is surfaced rather than silently shipped.The net472-only
libcchmodP/Invoke shim (Utils/UnixUtils.cs) is removed — it was the only consumer ofFile.SetUnixFileModeon net472, and net472 only runs on Windows in the modern SDK, where Unix permissions do not apply.Testing
ExecutableImageOverwritesExistingNonExecutableFile(Unix-only) verifying that creating an apphost over a pre-existing non-executable file still yields-rwxr-xr-x.ExecutableImagetest continues to cover the executable-permission invariant.dotnet buildandBundler.GenerateBundlefail without this change and succeed with it. The conditional-chmod fallback was also confirmed on a normal Linux filesystem.Note
This pull request was authored with the assistance of GitHub Copilot.