Commit Graph

132 Commits

Author SHA1 Message Date
John Ericson
bbcf2041e1 File system error improvements
- Make `descriptorToPath` cross-platform (renamed from
  `windows::handleToPath`). Uses `/proc/self/fd` on Linux and
  `F_GETPATH` on macOS. Add `HAVE_F_GETPATH` meson check.

  This is based on 7226a116a0, which was
  removed in 479c356510, but is now
  introduced more judiciously.

- Unix error messages in `readFull`, `writeFull`, `readLine` now include
  file paths via `descriptorToPath`.

- Convert `std::filesystem::filesystem_error` to `SystemError`

  Wrappers like `readLink`, `createDirs`, `DirectoryIterator`, etc. now
  catch `std::filesystem::filesystem_error` and rethrow as `SystemError`
  with the error code preserved. This ensures consistent exception types
  throughout the codebase.

  Call sites that previously caught `filesystem_error` and rethrew with
  `throw;` now throw `SystemError(e.code(), ...)` instead.

  Some call sites can stop catching `filesystem_error` at all,
  because they only call the wrapped functions.

- Rework `SystemError` constructors to auto-append error message

  The public `SystemError(std::error_code, ...)` constructor now
  automatically appends `errorCode.message()` to the error message.
  A protected constructor takes an explicit error message string for
  subclasses.

  `SysError` delegates to the protected constructor with `strerror(errNo)`.
  `WinError` delegates with `renderError(lastError)` (now static).

  This removes the need to manually append `e.code().message()` at call
  sites when converting `filesystem_error` to `SystemError`.

- Use perfect forwarding (`Args &&...` with `std::forward`) consistently
  in `BaseError`, `SystemError`, `SysError`, and `WinError` constructors.

Co-authored-by: Sergei Zimmerman <sergei@zimmerman.foo>
2026-02-18 12:29:11 -05:00
John Ericson
06d4d5779f Merge pull request #15251 from obsidiansystems/file-system-at
Split `file-system-at.{cc,hh}` from `file-descriptor.{cc,hh}`
2026-02-16 20:10:28 +00:00
John Ericson
af7e585009 Split file-system-at.{cc,hh} from file-descriptor.{cc,hh}
`file-descriptor.{cc,hh}` was getting too big, split out
`file-system-at.{cc,hh}` for the FD-based file system stuff,
`file-descriptor.{cc,hh}` will only be for the fundamental primitives
that are file-system agnostic and work on almost all file types.

Review with `git show --color-moved` to see that this is indeed all
moving.
2026-02-16 14:21:52 -05:00
Robert Hensing
771421a34e fix(ref): improve cast exception type and add demangled type names
When ref::cast() fails, the error message was cryptic ("null pointer
cast to ref"). Now it throws a proper bad_ref_cast (a std::bad_cast
subclass) with a clear message showing the actual types involved:

    ref<nix::Base> cannot be cast to ref<nix::Derived>

This also adds a demangle.hh utility.
2026-02-16 17:07:40 +01:00
Robert Hensing
5aaa0cc4a6 refactor(ref): clarify implicit conversion semantics with requires clause
ref<Derived> was already implicitly convertible to ref<Base>, but the
mechanism was unclear and error messages for rejected downcasts were
more cryptic than necessary. This change:

- Adds RefImplicitlyUpcastableTo concept to constrain the conversion
  operator, making the intent explicit and improving error messages
- Documents .cast() and .dynamic_pointer_cast() as alternatives for
  explicit downcasting
- Adds unit tests for covariance behavior
2026-02-16 16:43:08 +01:00
Amaan Qureshi
ad0055e67c libutil: fix isInDir rejecting paths starting with dot
The old check rejected any relative path whose first character was a
dot, producing false negatives for valid descendants like `.ssh` or
`.config`. This commit changes the logic such that now it inspects the
first path component via `path::begin()`, only rejects `.` and `..`
rather than anything dot-prefixed. Fixes #15207.
2026-02-15 10:04:08 -05:00
Sergei Zimmerman
d77c131df3 libutil: Add overflow check to alignUp
Old code with size + (size % 8 ? 8 - (size % 8) : 0) also suffered from this.
2026-02-05 02:04:33 +03:00
John Ericson
a4c0295822 Merge pull request #15060 from NixOS/read-link-at
Support `readLinkAt` and `openFileEnsureBeneathNoSymlinks` on Windows too
2026-02-04 20:40:08 +00:00
John Ericson
c0ab135860 Some small fixes
I think this has to do with the 25.11 bump.
2026-01-27 00:06:59 -05:00
John Ericson
6e2e53a8d2 Share the exception-using chmod wrapper with more code
It is not just useful to `DerivationBuilder`.
2026-01-24 17:03:48 -05:00
John Ericson
1100c9dc23 Support readLinkAt and openFileEnsureBeneathNoSymlinks on Windows too
This means that `RestoreSink` can work in the TOCTOU-resilliant way on
Windows too. And it also bodes will for the upcoming OS source accessor
improvements.

A few misc little refactors around error handling and whatnot are done
along the way too. (No more attempt to support pre Windows Vista! lol.)

This cannot be realiably automatically tested until we have a newer
version of Wine, but it does build, so I am inclined to say we just try
it for now.
2026-01-22 17:35:28 -05:00
Sergei Zimmerman
52100c6ee1 libutil: Add unix::readLinkAt function
This will be used for TOCTOU-free NAR serialisation and recursive copying.
2026-01-22 02:50:39 +03:00
Sergei Zimmerman
6ba067831a Use CompressionAlgo throughout
Instead of the stringly typed code we should use an enum class, this is
more clear and less error-prone. Also adds settings implementations for
CompressionAlgo and std::optional<CompressionAlgo>. The first is used
for NAR compression, since we never accepted empty strings there:

error: unknown compression method ''

The other one is used for optional .narinfo, .ls, and log/ compression.
Those treated empty strings as compression being disabled. The same exact
semantics is kept.

This has the benefit of improving error messages for incorrect values:

error: option 'compression' has invalid value 'bz'
       Did you mean one of br, xz or lz4?
2026-01-20 04:35:16 +03:00
Sergei Zimmerman
5d2938520c Use known folders for nix data on windows
This is the usual conventions on windows.

See https://learn.microsoft.com/en-us/windows/win32/shell/knownfolderid and
https://github.com/adrg/xdg for examples of the mapping of XDG paths to Windows
known folders.

Additionally, on Windows, this allows us to dispense with a hard-coded
default for `nixConfDir`, which is both nice (fewer compile-time
configuration options) and necessary, because we don't know what drive
the `ProgramData` directory will live on.

Tested on wine.

Co-Authored-By: John Ericson <John.Ericson@Obsidian.Systems>
2026-01-17 02:12:41 +03:00
Robert Hensing
3b028edbf7 fixGitURL: fix crash for relative paths and reject unsupported SCP URLs
Relative paths (e.g., "relative/repo") would crash in renderAuthorityAndPath()
because an empty authority was set, violating RFC 3986 section 3.3 which
requires paths to start with "/" when an authority is present.

Fix by only setting authority for absolute paths:
- Absolute paths: file:///path (empty authority)
- Relative paths: file:path (no authority)

Also reject SCP-like URLs without a user (e.g., "github.com:path") with a
clear error message, since proper support requires careful implementation,
which is not something I can do right now.
2026-01-12 13:03:57 +01:00
David McFarland
ac24ef84fa libutil-tests: fix openFileEnsureBeneathNoSymlinks.works on cygwin 2026-01-08 18:59:46 -04:00
Sergei Zimmerman
9a63752317 libutil: Implement unix::fchmodatTryNoFollow
Using fchmodat after a fstatat in deletePath has a slight TOCTOU
window. We can plug it by using fchmodat (the libc wrapper with
AT_SYMLINK_NOFOLLOW), but it tries fchmodat2 and falls back to the
O_PATH trick while failing when procfs isn't mounted. We can do a bit
better than that and also cache whether syscalls are unsupported to
avoid the repeated context switching that glibc would impose.

Also tests the fallback path. It's only for kernels older than 6.6 and
when procfs isn't accessible that we fall back to the racy fchmodat
without AT_SYMLINK_NOFOLLOW.

What previously used to be:

openat(AT_FDCWD, "/tmp/store-race/nix/var/nix/builds", O_RDONLY) = 11
newfstatat(11, "nix-2704212-84654554", {st_mode=S_IFDIR|000, st_size=3, ...}, AT_SYMLINK_NOFOLLOW) = 0
fchmodat(11, "nix-2704212-84654554", 040700) = 0

Is now a TOCTOU-free sequence of syscalls:

openat(AT_FDCWD, "/tmp/store-race/nix/var/nix/builds", O_RDONLY) = 11
newfstatat(11, "nix-2704953-1733606057", {st_mode=S_IFDIR|000, st_size=3, ...}, AT_SYMLINK_NOFOLLOW) = 0
fchmodat2(11, "nix-2704953-1733606057", 040700, AT_SYMLINK_NOFOLLOW) = 0

Or if the fchmodat2 is not supported:

openat(11, "nix-2705443-3010460784", O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH) = 12
fstat(12, {st_mode=S_IFDIR|000, st_size=3, ...}) = 0
chmod("/proc/self/fd/12", 040700)       = 0
openat(11, "nix-2705443-3010460784", O_RDONLY|O_NOFOLLOW|O_DIRECTORY) = 12

This prevents a potentially arbitrary chmod that follows symlinks,
though the race window is very small. Also in the case that fchmodat2
isn't supported we could instead open the /proc/self/fd/N path instead
of using openat, but that's pretty much equivalent. We only care
about ensuring that the thing we chmodded wasn't a symlink since
fchmodat follows symlinks and the support for AT_SYMLINK_NOFOLLOW
in libc for that is pretty spotty on Linux. E.g. glibc fails if the
AT_SYMLINK_NOFOLLOW is specified and procfs isn't available even on
regular files. The patch also includes a test that uses a user namespace
on Linux to test this exact scenario (though it's rather exotic).
2026-01-07 14:59:05 +03:00
Sergei Zimmerman
75da37f792 libutil-tests: Move unix-specific tests for file descriptors to unix/file-descriptor.cc 2026-01-06 18:46:34 +03:00
Sergei Zimmerman
8093df5255 Merge pull request #14845 from Zaczero/zaczero/BufferedSource--readLine
Add buffered line reads to BufferedSource
2025-12-27 20:55:43 +00:00
Kamil Monicz
b813ed2602 Add buffered line reads to BufferedSource
Provide BufferedSource::readLine for opt-in buffered line reading. Migrate applicable call sites.
2025-12-27 23:10:10 +03:00
Sergei Zimmerman
76c6f3cfd0 libutil: Make MemorySourceAccessor throw more precise errors
Makes the error messages render paths correctly, also introduces
a new hierarchy of error classes for SourceAccessor related errors
that we might want to handle differently (e.g. like when doing a readFile
on a directory and such). This should make it easier to implement better
UnionSourceAccessor and AllowListSourceAccessor by catching these errors
consistently.
2025-12-24 01:49:29 +03:00
John Ericson
188cb798ad Merge pull request #14817 from NixOS/fix-socket-mingw
Windows fixes
2025-12-18 00:30:19 +00:00
John Ericson
30cd9e43e1 Fix windows build of new source accessor test
We don't have the dirFd on window at this time.
2025-12-17 16:51:01 -05:00
Sergei Zimmerman
89dc57f6aa libutil: Implement HANDLE-based lseek for Windows
For windows we should live fully in the HANDLE land instead
of converting back-n-forth (which sometimes is destructive).
Using native API is much better for this.
2025-12-18 00:30:06 +03:00
Sergei Zimmerman
675656ffba libutil: Fix canonPath, makeTempPath and createTempDir on windows
This at least makes canonPath not consider the drive letter as a path
component. There still some issues with it on windows, but at least
this gets us through some of the libutil-tests.

Also since we don't want to change which env variables nix considers
we don't use std::filesystem::temp_directory_path and implement the
windows version directly.
2025-12-18 00:30:04 +03:00
Sergei Zimmerman
017fae3f14 libutil-tests: Add tests for makeFSSourceAccessor
Should be pretty self-explanatory. We didn't really have unit tests
for the filesystem source accessor. Now we do and this will be immensely
useful for implementing a unix-only smarter accessor that doesn't suffer
from TOCTOU on symlinks.
2025-12-17 04:42:31 +03:00
John Ericson
61de9222b0 Use SRI hash (strings) as the official JSON format for Hash after all
The fact that we were introducing a conversion from the output of `nix
path-info` into the input of `builtins.fetchTree` was the deciding
factor. We want scripting outputs into inputs like that to be easy.

Since JSON strings and objects are trivially distinguishable, we still
have the option of introducing the JSON format as an alternative input
scheme in the future, should we want to. (The output format would still
be SRI in that case, presumably.)
2025-12-08 16:50:25 -05:00
Eelco Dolstra
c555af2c77 More mingw fixes 2025-12-04 17:56:07 +01:00
John Ericson
bec3c5cfcd JSON for Hash now has to be Base16
Fix #14532.

As discussed on the call today:

1. We'll stick with `format = "base16"` and `hash = "<hash>"`, not do
   `base16 = "<hash>"`, in order to be forward compatible with
   supporting more versioning formats.

   The motivation we discussed for someday *possibly* doing this is
   making it easier to write very slap-dash lang2nix tools that create
   (not consume) derivations with dynamic derivations.

2. We will remove support for non-base16 (and make that the default, not
   base64) in `Hash`, so this is strictly forward contingency, *not*
   yet something we support. (And also not something we have concrete
   plans to start supporting.)
2025-12-03 16:08:05 -05:00
Sergei Zimmerman
4b3536e092 libutil: Get rid of restartableSourceFromFactory
Instead we can just seek back in the file - duh. Also this makes use
of the anonymous temp file facility, since that is much safer (no need
window where the we don't have an open file descriptor for it).
2025-12-01 04:49:27 +03:00
Sergei Zimmerman
4ad272015e libutil: Implement createAnonymousTempFile
There are a lot of cases where we don't care about having
the temporary file linked anywhere at all -- just a descriptor is more
than enough.
2025-12-01 04:49:26 +03:00
Sergei Zimmerman
0778b861a9 libutil: Use openFileEnsureBeneathNoSymlinks in RestoreSink::createRegularFile
Add more assertions for preconditions of openFileEnsureBeneathNoSymlinks to prevent
misuse. Also start using it for regular file creation as well.
2025-11-26 03:49:33 +03:00
Sergei Zimmerman
3a9be9fd2f libutil: Use openFileEnsureBeneathNoSymlinks in RestoreSink::createDirectory
Starts using the new function.
2025-11-25 01:10:35 +03:00
Sergei Zimmerman
77990e7cca libutil/file-descriptor: Add safer utilities for opening files relative to dirFd
Implements a safe no symlink following primitive operation for opening file descriptors.
This is unix-only for the time being, since windows doesn't really suffer from symlink
races, since they are admin-only.

Tested with enosys --syscall openat2 as well.
2025-11-25 00:42:57 +03:00
John Ericson
7357a654de nlohmann::json instance and JSON Schema for MemorySourceAccessor
Also do a better JSON and testing for deep and shallow NAR listings.

As documented, this for file system objects themselves, since
`MemorySourceAccessor` is an implementation detail.
2025-11-20 15:19:24 -05:00
John Ericson
437b9b9879 Rename MemorySourceAccessor::File::Directory::{contents -> entries}
This matches the "NAR Listing" JSON format, and also helps distinguish
from regular file contents.

Why we want to match that will become clear in the next comments, when
we will in fact use (variations of) this data type for NAR listings.
2025-11-20 14:44:41 -05:00
David McFarland
b115c90043 Disable MonitorFdHup test on cygwin 2025-11-16 23:33:28 -04:00
Bernardo Meurer Costa
182ae393d1 refactor(libutil/topo-sort): return variant instead of throwing
The variant has on the left-hand side the topologically sorted vector
and the right-hand side is a pair showing the path and its parent that
represent a cycle in the graph making the sort impossible.

This change prepares for enhanced cycle error messages that can provide
more context about the cycle. The variant approach allows callers to
handle cycles more flexibly, enabling better error reporting that shows
the full cycle path and which files are involved.

Adapted from Lix commit f7871fcb5.

Change-Id: I70a987f470437df8beb3b1cc203ff88701d0aa1b
Co-Authored-By: Maximilian Bosch <maximilian@mbosch.me>
2025-11-10 15:04:45 -05:00
Eelco Dolstra
709a73e7ae Merge pull request #14492 from NixOS/fix-14429
fetchGit: Drop `git+` from the `url` attribute
2025-11-10 17:16:04 +00:00
Marcel
0c53c88367 progress-bar: use dynamic size units 2025-11-07 23:50:38 +01:00
Eelco Dolstra
40f600644d fetchGit: Drop git+ from the url attribute
This was already dropped in `inputFromURL()`, but not in
`inputFromAttrs()`. Now it's done in `fixGitURL()`, which is used by
both.

In principle, `git+` shouldn't be used in the `url` attribute, since
we already know that it's a Git URL. But since it currently works, we
don't want to break it.

Fixes #14429.
2025-11-06 16:34:19 +01:00
Marcel
9d4d10954a diff-closures: print sizes with dynamic unit 2025-10-27 02:05:03 +01:00
John Ericson
0f0d9255c6 Clean up JSON utils in a few ways
In particular

- Remove `get`, it is redundant with `valueAt` and the `get` in
  `util.hh`.

- Remove `nullableValueAt`. It is morally just the function composition
  `getNullable . valueAt`, not an orthogonal combinator like the others.

- `optionalValueAt` return a pointer, not `std::optional`. This also
  expresses optionality, but without creating a needless copy. This
  brings it in line with the other combinators which also return
  references.

- Delete `valueAt` and `optionalValueAt` taking the map by value, as we
  did for `get` in 408c09a120, which
  prevents bugs / unnecessary copies.

`adl_serializer<DerivationOptions::OutputChecks>::from_json` was the one
use of `getNullable`. I give it a little static function for the
ultimate creation of a `std::optional` it does need to do (after
switching it to using `getNullable . valueAt`. That could go in
`json-utils.hh` eventually, but I didn't bother for now since only one
things needs it.

Co-authored-by: Sergei Zimmerman <sergei@zimmerman.foo>
2025-10-25 14:49:51 -04:00
John Ericson
c87f29a0b6 Fix some characterization tests
A few changes had cropped up with `_NIX_TEST_ACCEPT=1`:

1. Blake hashing test JSON had a different indentation

2. Store URI had improper non-quoted spaces

(1) was is just fixed, as we trust nlohmann JSON to parse JSON
correctly, regardless of whitespace.

For (2), the existing URL was made a read-only test, since we very much
wish to continue parsing such invalid URLs directly. And then the
original read/write test was updated to properly percent-encode the
space, as the normal form should be.
2025-10-23 14:03:21 -04:00
John Ericson
ef8218f2e3 Merge pull request #14307 from NixOS/json-schema-hash
`nlohmann::json` instance and JSON Schema for `Hash`
2025-10-21 06:03:20 +00:00
John Ericson
5e7ee808de nlohmann::json instance and JSON Schema for Hash
Improving and codifying our experimental JSON interfacing.

Co-Authored-By: Robert Hensing <robert@roberthensing.nl>
2025-10-20 15:21:07 -04:00
Sergei Zimmerman
a91b787524 libutil: Add alignUp helper function 2025-10-20 21:11:00 +03:00
John Ericson
1fb4ff8c0e Merge pull request #14232 from roberth/dyndrv-messages
Better dyndrv messages
2025-10-14 15:40:27 +00:00
Sergei Zimmerman
1633ceaff2 libutil: Ensure that CanonPath does not contain NUL bytes
This, alongside the other invariants of the CanonPath is important
to uphold. std::filesystem happily crashes on NUL bytes in the constructor,
as we've seen with `path:%00` prior to c436b7a32a.
Best to stay clear of NUL bytes when we're talking about syscalls, especially
on Unix where strings are null terminated.

Very nice to have if we decide to switch over to pascal-style strings.
2025-10-14 02:33:42 +03:00
Robert Hensing
5dcfddf997 strings: Add optionalBracket helper 2025-10-13 13:59:39 +02:00