Skip to content

More file system function improvements#15244

Open
Ericson2314 wants to merge 2 commits into
NixOS:masterfrom
obsidiansystems:windows-fs-sink-fixes
Open

More file system function improvements#15244
Ericson2314 wants to merge 2 commits into
NixOS:masterfrom
obsidiansystems:windows-fs-sink-fixes

Conversation

@Ericson2314
Copy link
Copy Markdown
Member

@Ericson2314 Ericson2314 commented Feb 15, 2026

It keeps growing bigger and bigger! See the commit message

@dpulls
Copy link
Copy Markdown

dpulls Bot commented Feb 15, 2026

🎉 All dependencies have been resolved !

@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 7 times, most recently from c3e9b54 to eaf0a71 Compare February 16, 2026 23:12
Comment thread src/libstore/unix/build/derivation-builder.cc Outdated
Comment thread src/libutil/file-descriptor.cc Outdated
Comment thread src/libutil/unix/file-system.cc Outdated
Comment thread src/libutil/windows/file-system.cc Outdated
Comment thread src/libutil/include/nix/util/file-descriptor.hh Outdated
Comment thread src/libutil/windows/file-system-at.cc Outdated
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 3 times, most recently from 7c98970 to 1c9afd4 Compare February 18, 2026 15:30
@github-actions github-actions Bot added new-cli Relating to the "nix" command store Issues and pull requests concerning the Nix store repl The Read Eval Print Loop, "nix repl" command and debugger fetching Networking with the outside (non-Nix) world, input locking labels Feb 18, 2026
@Ericson2314 Ericson2314 changed the title More "file system at" improvements More file system function improvements Feb 18, 2026
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 2 times, most recently from 485ad45 to 0405c86 Compare February 18, 2026 16:18
Comment thread src/libutil/fs-sink.cc Outdated
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 2 times, most recently from 3ef6142 to c3003a2 Compare February 18, 2026 16:59
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 4 times, most recently from d48824a to c1c4646 Compare February 27, 2026 01:16
Comment on lines 495 to 496
if (std::filesystem::exists(destDir))
throw Error("cannot clone into existing path %s", PathFmt(destDir));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't this prevent the case where the destination exists and is a symlink?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, it follows symlinks. We should add a test for this I suppose

Comment thread src/libstore/gc.cc
Comment thread src/libutil/fs-sink.cc
Comment on lines -180 to -181
/* O_EXCL together with O_CREAT ensures symbolic links in the last
component are not followed. */
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we leave this comment in place?

Comment thread src/libutil/unix/file-system-at.cc Outdated
Comment thread src/libutil/unix/file-system.cc Outdated
Comment thread src/libutil/windows/file-system.cc Outdated
Comment thread src/libutil/windows/file-system.cc Outdated
Comment thread src/libutil/file-system.cc Outdated
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 2 times, most recently from bc44c1b to f28c385 Compare February 27, 2026 22:08
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 5 times, most recently from 959f0f5 to 1430607 Compare March 3, 2026 23:00
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch 4 times, most recently from eb72105 to 75abe59 Compare April 11, 2026 23:34
Comment thread src/libutil/unix/file-system-at.cc Outdated
outcome::unchecked<AutoCloseFD, std::error_code>
openDirectoryAt(Descriptor dirFd, const std::filesystem::path & path, bool create, mode_t mode)
{
assert(path.is_relative());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a slight regression there that the path can now contain ... That shouldn't regress I think?

Comment thread src/libutil/unix/file-system-at.cc Outdated
}

OsString readLinkAt(Descriptor dirFd, const CanonPath & path)
OsString readLinkAt(Descriptor dirFd, const std::filesystem::path & path)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure we want to use std::filesystem::path for these functions. Maybe OsCanonPath or something?

Ericson2314 and others added 2 commits April 24, 2026 13:35
Both are newtype around `std::filesystem::path`s. `OsFilename` is a
single filename that is not something special like `.` or `..`, and
`OsCanonPath` is for canonical relative paths suitable for `openat` and
Windows NT equivalents.

`OsCanonPath` provides an iterator yielding `OsFilename` references
for each component, and four `operator/` overloads covering all
combinations of `OsCanonPath` and `OsFilename`.

Convert `readLinkAt`, `openFileEnsureBeneathNoSymlinks`, and
`unix::fchmodatTryNoFollow` from `const CanonPath &` to
`const OsCanonPath &`. Existing call sites work via implicit
`CanonPath -> OsCanonPath` conversion.

Change `SymlinkNotAllowed::path` from `CanonPath` to
`std::variant<CanonPath, std::filesystem::path>`, with constructor
overloads for both types including template variants for custom
error messages.

`writeBuilderFile` now takes `OsFilename` instead of `const std::string &`,
which implicitly converts to `OsCanonPath` for
`openFileEnsureBeneathNoSymlinks`, replacing the manual
`std::filesystem::path` normalization assertion.
Use `OsCanonPath` and `OsFilename` throughout the `*At` functions
and their callers, replacing both `CanonPath` and
`std::filesystem::path` parameters.

- FD-based symlink creation on Unix and Windows with wrappers
  (`createFileSymlinkAt`, `createDirectorySymlinkAt`,
  `createUnknownSymlinkAt`), all taking `OsCanonPath`.

  Windows distinguishes between file and directory symlinks. Directory
  symlinks can be traversed as path components, file symlinks cannot.
  `createUnknownSymlinkAt` opens the symlink's parent directory and
  tries to open the target with `FILE_DIRECTORY_FILE` to determine if
  it's a directory, with `fstat` as defense in depth. This avoids
  relying on `descriptorToPath` which doesn't work correctly in Wine.

  `createUnknownSymlinkAt` is a poor solution, and a replacement for
  NARs should be careful to store enough information to avoid needing it
  at unpack time, but it is the best we can do for now.

- Make `RestoreSink` more cross-platform: use `openDirectoryAt` for
  directory creation, `createUnknownSymlinkAt` for symlinks, and
  `openFileEnsureBeneathNoSymlinks` for intermediate paths on both
  Unix and Windows. Remove the Windows-only `std::filesystem` /
  `CreateFileW` / `nix::createSymlink` code paths that bypassed
  fd-based symlink safety. `getParentFdAndName` now returns
  `OsFilename` instead of `CanonPath`.

- Validate `OsCanonPath` uses preferred separators on Windows
  (no forward slashes). Remove `lexically_normal().make_preferred()`
  calls from Windows `*At` implementations, since `OsCanonPath`
  now guarantees native separators.

- Fix Wine `openFileEnsureBeneathNoSymlinks` final-component handling:
  check for symlinks on `ERROR_DIRECTORY` (suspected Wine-specific
  behavior when opening directory symlinks with
  `FILE_OPEN_REPARSE_POINT | FILE_DIRECTORY_FILE`).

- Fix Windows error propagation in `openFileEnsureBeneathNoSymlinks`:
  call `SetLastError(lastError)` before returning an invalid handle,
  so that callers can inspect the Win32 error code. Previously the
  NTSTATUS was converted via `RtlNtStatusToDosError` but never
  stored, so `GetLastError()` returned 0.

- Add `ThrowsWinError` gmock matcher. Use `NativeSysError` in test
  helpers so the right exception type is thrown per platform.

- Update tests to use `OsFilename`/`CanonPath` instead of bare
  `std::filesystem::path` for `*At` function calls.

Co-authored-by: Sergei Zimmerman <sergei@zimmerman.foo>
@Ericson2314 Ericson2314 force-pushed the windows-fs-sink-fixes branch from ca13d43 to e17a8ea Compare April 24, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fetching Networking with the outside (non-Nix) world, input locking new-cli Relating to the "nix" command repl The Read Eval Print Loop, "nix repl" command and debugger store Issues and pull requests concerning the Nix store with-tests Issues related to testing. PRs with tests have some priority

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants