Compare commits

...

26 Commits

Author SHA1 Message Date
internal-nix-ci[bot]
ac3532d0f2 Merge pull request #14354 from NixOS/backport-14343-to-2.32-maintenance
[Backport 2.32-maintenance] Revert "libmain: Catch logger exceptions in `handleExceptions`"
2025-10-25 00:00:15 +00:00
Sergei Zimmerman
84dbf182d4 Revert "libmain: Catch logger exceptions in handleExceptions"
This reverts commit 90d1ff4805.

The initial issue with EPIPE was solved in 9f680874c5.
Now this patch does move bad than good by eating up boost::io::format_error that are
bugs.

(cherry picked from commit 4f5af471fb)
2025-10-24 23:28:53 +00:00
internal-nix-ci[bot]
4a27d70132 Merge pull request #14280 from NixOS/backport-14276-to-2.32-maintenance
[Backport 2.32-maintenance] libstore/registerOutputs: Don't try to optimize a non-existent actual…
2025-10-16 22:17:53 +00:00
Sergei Zimmerman
dadb5b01b7 libstore/registerOutputs: Don't try to optimize a non-existent actualPath
Since 3c610df550 this resulted in `getting status of`
errors on paths inside the chroot if a path was already valid. Careful inspection
of the logic shows that if buildMode != bmCheck actualPath gets reassigned to
store.toRealPath(finalDestPath). The only branch that cares about actualPath is
the buildMode == bmCheck case, which doesn't lead to optimisePath anyway.

(cherry picked from commit 4cbcaad435)
2025-10-16 21:46:08 +00:00
internal-nix-ci[bot]
3c39583e55 Merge pull request #14267 from NixOS/backport-14263-to-2.32-maintenance
[Backport 2.32-maintenance] Restore `ServeProto::Command::ImportPaths`
2025-10-16 00:07:32 +00:00
Sergei Zimmerman
a038c92d38 Restore ServeProto::Command::ImportPaths
This partially reverts commit 5e46df973f,
partially reversing changes made to
8c789db05b.

We do this because Hydra, while using the newer version of the protocol,
still uses this command, even though Nix (as a client) doesn't use it.
On that basis, we don't want to remove it (or consider it only part of
the older versions of the protocol) until Hydra no longer uses the
Legacy SSH Protocol.

(cherry picked from commit 0deb492b3d)
2025-10-15 23:38:26 +00:00
internal-nix-ci[bot]
cf6ad228ae Merge pull request #14259 from NixOS/backport-14253-to-2.32-maintenance
[Backport 2.32-maintenance] libfetchers/git-utils: Be more correct about validating refnames
2025-10-15 20:33:35 +00:00
Sergei Zimmerman
44701007b4 libfetchers/git-utils: Be more correct about validating refnames
Turns out there's a much better API for this that doesn't have the
footguns of the previous method.

isLegalRefName is somewhat of a misnomer, since it's mainly used to
validate user inputs that can be either references, branch names,
psedorefs or tags.

(cherry picked from commit 5d1178b817)
2025-10-15 20:08:43 +00:00
internal-nix-ci[bot]
ff1f145992 Merge pull request #14256 from NixOS/backport-14205-to-2.32-maintenance
[Backport 2.32-maintenance] Improved backwards compatibility hack for git URLs using dir=...
2025-10-15 16:31:21 +00:00
Graham Dennis
3519ad2ca6 Improve comment
(cherry picked from commit 8d9e9bc400)
2025-10-15 15:58:18 +00:00
Graham Dennis
16af6a8ed1 Improved backwards compatibility hack for git URLs using dir=... attribute
(cherry picked from commit 43b01b6790)
2025-10-15 15:58:18 +00:00
Eelco Dolstra
549a2e8272 Bump version 2025-10-14 11:26:39 +02:00
internal-nix-ci[bot]
2531dcad75 Merge pull request #14238 from NixOS/backport-14237-to-2.32-maintenance
[Backport 2.32-maintenance] Remove validation of URLs passed to FileTransferRequest verbatim
2025-10-13 21:23:05 +00:00
Sergei Zimmerman
11f9c59140 Remove validation of URLs passed to FileTransferRequest verbatim
CURL is not very strict about validation of URLs passed to it. We
should reflect this in our handling of URLs that we get from the user
in <nix/fetchurl.nix> or builtins.fetchurl. ValidURL was an attempt to
rectify this, but it turned out to be too strict. The only good way to
resolve this is to pass (in some cases) the user-provided string verbatim
to CURL. Other usages in libfetchers still benefit from using structured
ParsedURL and validation though.

nix store prefetch-file --name foo 'https://cdn.skypack.dev/big.js@^5.2.2'
error: 'https://cdn.skypack.dev/big.js@^5.2.2' is not a valid URL: leftover

(cherry picked from commit 47f427a172)
2025-10-13 20:48:15 +00:00
internal-nix-ci[bot]
a25a219e79 Merge pull request #14213 from NixOS/backport-14194-to-2.32-maintenance
[Backport 2.32-maintenance] libutil: Print stack trace on assertion failure
2025-10-11 00:40:50 +00:00
Sergei Zimmerman
f07486b205 libutil: Print stack trace on assertion failure
This change overrides __assert_fail on glibc/musl
to instead call std::terminate that we have a custom
handler for. This ensures that we have more context
to diagnose issues encountered by users in the wild.

(cherry picked from commit 46382ade74)
2025-10-11 00:08:36 +00:00
internal-nix-ci[bot]
9ec98f7844 Merge pull request #14212 from NixOS/backport-14210-to-2.32-maintenance
[Backport 2.32-maintenance] libstore: Fix double-quoting of paths in logs
2025-10-10 23:26:18 +00:00
Sergei Zimmerman
634e1d3b65 libstore: Fix double-quoting of paths in logs
std::filesystem::path is already quoted by boost::format with double quotes (").
(cherry picked from commit f30cb8667b)
2025-10-10 22:54:22 +00:00
internal-nix-ci[bot]
70655061e3 Merge pull request #14202 from NixOS/backport-14199-to-2.32-maintenance
[Backport 2.32-maintenance] packaging: only override `toml11` when necessary
2025-10-09 18:04:05 +00:00
Seth Flynn
da328e6004 packaging: only override toml11 when necessary
v4.4.0 hit Nixpkgs in https://github.com/NixOS/nixpkgs/pull/442682.
Ideally we'd just use that, but this keeps the fallback behavior until
it's more widespread

(cherry picked from commit 0f016f9bf5)
2025-10-09 17:56:10 +00:00
internal-nix-ci[bot]
6b16af8c0e Merge pull request #14197 from NixOS/backport-14191-to-2.32-maintenance
[Backport 2.32-maintenance] libutil: Fix renderAuthorityAndPath unreachable for path:/ URLs
2025-10-09 00:00:28 +00:00
internal-nix-ci[bot]
010b78e0cf Merge pull request #14196 from NixOS/backport-14189-to-2.32-maintenance
[Backport 2.32-maintenance] exportReferencesGraph: Handle heterogeneous arrays
2025-10-08 23:43:51 +00:00
Sergei Zimmerman
98b7654390 libutil: Fix renderAuthorityAndPath unreachable for path:/ URLs
This was mistakenly triggered by path:/ URL, since the `//` would
correspond to 3 empty segments.

(cherry picked from commit 1d8dd77e1d)
2025-10-08 23:24:01 +00:00
Eelco Dolstra
c5799aa62c exportReferencesGraph: Handle heterogeneous arrays
This barfed with

   error: [json.exception.type_error.302] type must be string, but is array

on `nix build github:malt3/bazel-env#bazel-env` because it has a `exportReferencesGraph` with a value like `["string",...["string"]]`.

(cherry picked from commit 94f410b628)
2025-10-08 23:13:09 +00:00
Eelco Dolstra
72e3dd396c Bump version 2025-10-07 17:14:49 +02:00
Eelco Dolstra
d069633b3d Mark official release 2025-10-07 13:30:16 +02:00
30 changed files with 267 additions and 142 deletions

View File

@@ -1 +1 @@
2.32.0
2.32.2

View File

@@ -32,7 +32,7 @@
let
inherit (nixpkgs) lib;
officialRelease = false;
officialRelease = true;
linux32BitSystems = [ "i686-linux" ];
linux64BitSystems = [

View File

@@ -0,0 +1,32 @@
can_wrap_assert_fail_test_code = '''
#include <cstdlib>
#include <cassert>
int main()
{
assert(0);
}
extern "C" void * __real___assert_fail(const char *, const char *, unsigned int, const char *);
extern "C" void *
__wrap___assert_fail(const char *, const char *, unsigned int, const char *)
{
return __real___assert_fail(nullptr, nullptr, 0, nullptr);
}
'''
wrap_assert_fail_args = [ '-Wl,--wrap=__assert_fail' ]
can_wrap_assert_fail = cxx.links(
can_wrap_assert_fail_test_code,
args : wrap_assert_fail_args,
name : 'linker can wrap __assert_fail',
)
if can_wrap_assert_fail
deps_other += declare_dependency(
sources : 'wrap-assert-fail.cc',
link_args : wrap_assert_fail_args,
)
endif

View File

@@ -0,0 +1,17 @@
#include "nix/util/error.hh"
#include <cstdio>
#include <cstdlib>
#include <cinttypes>
#include <string_view>
extern "C" [[noreturn]] void __attribute__((weak))
__wrap___assert_fail(const char * assertion, const char * file, unsigned int line, const char * function)
{
char buf[512];
int n =
snprintf(buf, sizeof(buf), "Assertion '%s' failed in %s at %s:%" PRIuLEAST32, assertion, function, file, line);
if (n < 0)
nix::panic("Assertion failed and could not format error message");
nix::panic(std::string_view(buf, std::min(static_cast<int>(sizeof(buf)), n)));
}

View File

@@ -44,3 +44,5 @@ endif
# Darwin ld doesn't like "X.Y.Zpre"
nix_soversion = meson.project_version().split('pre')[0]
subdir('assert-fail')

View File

@@ -57,15 +57,20 @@ scope: {
prevAttrs.postInstall;
});
toml11 = pkgs.toml11.overrideAttrs rec {
version = "4.4.0";
src = pkgs.fetchFromGitHub {
owner = "ToruNiina";
repo = "toml11";
tag = "v${version}";
hash = "sha256-sgWKYxNT22nw376ttGsTdg0AMzOwp8QH3E8mx0BZJTQ=";
};
};
# TODO: Remove this when https://github.com/NixOS/nixpkgs/pull/442682 is included in a stable release
toml11 =
if lib.versionAtLeast pkgs.toml11.version "4.4.0" then
pkgs.toml11
else
pkgs.toml11.overrideAttrs rec {
version = "4.4.0";
src = pkgs.fetchFromGitHub {
owner = "ToruNiina";
repo = "toml11";
tag = "v${version}";
hash = "sha256-sgWKYxNT22nw376ttGsTdg0AMzOwp8QH3E8mx0BZJTQ=";
};
};
# TODO Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed.
boost =

View File

@@ -175,6 +175,12 @@ TEST_F(GitUtilsTest, peel_reference)
TEST(GitUtils, isLegalRefName)
{
ASSERT_TRUE(isLegalRefName("A/b"));
ASSERT_TRUE(isLegalRefName("AaA/b"));
ASSERT_TRUE(isLegalRefName("FOO/BAR/BAZ"));
ASSERT_TRUE(isLegalRefName("HEAD"));
ASSERT_TRUE(isLegalRefName("refs/tags/1.2.3"));
ASSERT_TRUE(isLegalRefName("refs/heads/master"));
ASSERT_TRUE(isLegalRefName("foox"));
ASSERT_TRUE(isLegalRefName("1337"));
ASSERT_TRUE(isLegalRefName("foo.baz"));

View File

@@ -12,6 +12,7 @@
#include <git2/attr.h>
#include <git2/blob.h>
#include <git2/branch.h>
#include <git2/commit.h>
#include <git2/config.h>
#include <git2/describe.h>
@@ -28,6 +29,7 @@
#include <git2/submodule.h>
#include <git2/sys/odb_backend.h>
#include <git2/sys/mempack.h>
#include <git2/tag.h>
#include <git2/tree.h>
#include <boost/unordered/unordered_flat_map.hpp>
@@ -1323,63 +1325,33 @@ GitRepo::WorkdirInfo GitRepo::getCachedWorkdirInfo(const std::filesystem::path &
return workdirInfo;
}
/**
* Checks that the git reference is valid and normalizes slash '/' sequences.
*
* Accepts shorthand references (one-level refnames are allowed).
*/
bool isValidRefNameAllowNormalizations(const std::string & refName)
{
/* Unfortunately libgit2 doesn't expose the limit in headers, but its internal
limit is also 1024. */
std::array<char, 1024> normalizedRefBuffer;
/* It would be nice to have a better API like git_reference_name_is_valid, but
* with GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND flag. libgit2 uses it internally
* but doesn't expose it in public headers [1].
* [1]:
* https://github.com/libgit2/libgit2/blob/9d5f1bacc23594c2ba324c8f0d41b88bf0e9ef04/src/libgit2/refs.c#L1362-L1365
*/
auto res = git_reference_normalize_name(
normalizedRefBuffer.data(),
normalizedRefBuffer.size(),
refName.c_str(),
GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL | GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND);
return res == 0;
}
bool isLegalRefName(const std::string & refName)
{
initLibGit2();
/* Since `git_reference_normalize_name` is the best API libgit2 has for verifying
* reference names with shorthands (see comment in normalizeRefName), we need to
* ensure that exceptions to the validity checks imposed by normalization [1] are checked
* explicitly.
* [1]: https://git-scm.com/docs/git-check-ref-format#Documentation/git-check-ref-format.txt---normalize
*/
/* Check for cases that don't get rejected by libgit2.
* FIXME: libgit2 should reject this. */
if (refName == "@")
return false;
/* Leading slashes and consecutive slashes are stripped during normalizatiton. */
if (refName.starts_with('/') || refName.find("//") != refName.npos)
return false;
/* Refer to libgit2. */
if (!isValidRefNameAllowNormalizations(refName))
return false;
/* libgit2 doesn't barf on DEL symbol.
* FIXME: libgit2 should reject this. */
if (refName.find('\177') != refName.npos)
return false;
return true;
for (auto * func : {
git_reference_name_is_valid,
git_branch_name_is_valid,
git_tag_name_is_valid,
}) {
int valid = 0;
if (func(&valid, refName.c_str()))
throw Error("checking git reference '%s': %s", refName, git_error_last()->message);
if (valid)
return true;
}
return false;
}
} // namespace nix

View File

@@ -496,6 +496,36 @@ struct GitInputScheme : InputScheme
Git interprets them as part of the file name. So get
rid of them. */
url.query.clear();
/* Backward compatibility hack: In old versions of Nix, if you had
a flake input like
inputs.foo.url = "git+https://foo/bar?dir=subdir";
it would result in a lock file entry like
"original": {
"dir": "subdir",
"type": "git",
"url": "https://foo/bar?dir=subdir"
}
New versions of Nix remove `?dir=subdir` from the `url` field,
since the subdirectory is intended for `FlakeRef`, not the
fetcher (and specifically the remote server), that is, the
flakeref is parsed into
"original": {
"dir": "subdir",
"type": "git",
"url": "https://foo/bar"
}
However, new versions of nix parsing old flake.lock files would pass the dir=
query parameter in the "url" attribute to git, which will then complain.
For this reason, we are filtering the `dir` query parameter from the URL
before passing it to git. */
url.query.erase("dir");
repoInfo.location = url;
}

View File

@@ -158,9 +158,12 @@ struct Setter
};
/**
* Checks that the git reference is valid and normalized.
* Checks that the string can be a valid git reference, branch or tag name.
* Accepts shorthand references (one-level refnames are allowed), pseudorefs
* like `HEAD`.
*
* Accepts shorthand references (one-level refnames are allowed).
* @note This is a coarse test to make sure that the refname is at least something
* that Git can make sense of.
*/
bool isLegalRefName(const std::string & refName);

View File

@@ -42,7 +42,7 @@ DownloadFileResult downloadFile(
if (cached && !cached->expired)
return useCached();
FileTransferRequest request(ValidURL{url});
FileTransferRequest request(VerbatimURL{url});
request.headers = headers;
if (cached)
request.expectedETag = getStrAttr(cached->value, "etag");
@@ -107,13 +107,13 @@ DownloadFileResult downloadFile(
static DownloadTarballResult downloadTarball_(
const Settings & settings, const std::string & urlS, const Headers & headers, const std::string & displayPrefix)
{
ValidURL url = urlS;
ParsedURL url = parseURL(urlS);
// Some friendly error messages for common mistakes.
// Namely lets catch when the url is a local file path, but
// it is not in fact a tarball.
if (url.scheme() == "file") {
std::filesystem::path localPath = renderUrlPathEnsureLegal(url.path());
if (url.scheme == "file") {
std::filesystem::path localPath = renderUrlPathEnsureLegal(url.path);
if (!exists(localPath)) {
throw Error("tarball '%s' does not exist.", localPath);
}
@@ -164,7 +164,7 @@ static DownloadTarballResult downloadTarball_(
/* Note: if the download is cached, `importTarball()` will receive
no data, which causes it to import an empty tarball. */
auto archive = !url.path().empty() && hasSuffix(toLower(url.path().back()), ".zip") ? ({
auto archive = !url.path.empty() && hasSuffix(toLower(url.path.back()), ".zip") ? ({
/* In streaming mode, libarchive doesn't handle
symlinks in zip files correctly (#10649). So write
the entire file to disk so libarchive can access it
@@ -178,7 +178,7 @@ static DownloadTarballResult downloadTarball_(
}
TarArchive{path};
})
: TarArchive{*source};
: TarArchive{*source};
auto tarballCache = getTarballCache();
auto parseSink = tarballCache->getFileSystemObjectSink();
auto lastModified = unpackTarfileToSink(archive, *parseSink);

View File

@@ -320,34 +320,29 @@ int handleExceptions(const std::string & programName, std::function<void()> fun)
std::string error = ANSI_RED "error:" ANSI_NORMAL " ";
try {
try {
try {
fun();
} catch (...) {
/* Subtle: we have to make sure that any `interrupted'
condition is discharged before we reach printMsg()
below, since otherwise it will throw an (uncaught)
exception. */
setInterruptThrown();
throw;
}
} catch (Exit & e) {
return e.status;
} catch (UsageError & e) {
logError(e.info());
printError("Try '%1% --help' for more information.", programName);
return 1;
} catch (BaseError & e) {
logError(e.info());
return e.info().status;
} catch (std::bad_alloc & e) {
printError(error + "out of memory");
return 1;
} catch (std::exception & e) {
printError(error + e.what());
return 1;
fun();
} catch (...) {
/* Subtle: we have to make sure that any `interrupted'
condition is discharged before we reach printMsg()
below, since otherwise it will throw an (uncaught)
exception. */
setInterruptThrown();
throw;
}
} catch (...) {
/* In case logger also throws just give up. */
} catch (Exit & e) {
return e.status;
} catch (UsageError & e) {
logError(e.info());
printError("Try '%1% --help' for more information.", programName);
return 1;
} catch (BaseError & e) {
logError(e.info());
return e.info().status;
} catch (std::bad_alloc & e) {
printError(error + "out of memory");
return 1;
} catch (std::exception & e) {
printError(error + e.what());
return 1;
}

View File

@@ -37,7 +37,7 @@ static void builtinFetchurl(const BuiltinBuilderContext & ctx)
auto fetch = [&](const std::string & url) {
auto source = sinkToSource([&](Sink & sink) {
FileTransferRequest request(ValidURL{url});
FileTransferRequest request(VerbatimURL{url});
request.decompress = false;
auto decompressor = makeDecompressionSink(unpack && hasSuffix(mainUrl, ".xz") ? "xz" : "none", sink);

View File

@@ -99,6 +99,17 @@ DerivationOptions DerivationOptions::fromStructuredAttrs(
return fromStructuredAttrs(env, parsed ? &*parsed : nullptr);
}
static void flatten(const nlohmann::json & value, StringSet & res)
{
if (value.is_array())
for (auto & v : value)
flatten(v, res);
else if (value.is_string())
res.insert(value);
else
throw Error("'exportReferencesGraph' value is not an array or a string");
}
DerivationOptions
DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAttrs * parsed, bool shouldWarn)
{
@@ -219,12 +230,9 @@ DerivationOptions::fromStructuredAttrs(const StringMap & env, const StructuredAt
if (!e || !e->is_object())
return ret;
for (auto & [key, value] : getObject(*e)) {
if (value.is_array())
ret.insert_or_assign(key, value);
else if (value.is_string())
ret.insert_or_assign(key, StringSet{value});
else
throw Error("'exportReferencesGraph' value is not an array or a string");
StringSet ss;
flatten(value, ss);
ret.insert_or_assign(key, std::move(ss));
}
} else {
auto s = getOr(env, "exportReferencesGraph", "");

View File

@@ -79,7 +79,7 @@ extern const unsigned int RETRY_TIME_MS_DEFAULT;
struct FileTransferRequest
{
ValidURL uri;
VerbatimURL uri;
Headers headers;
std::string expectedETag;
bool verifyTLS = true;
@@ -93,7 +93,7 @@ struct FileTransferRequest
std::string mimeType;
std::function<void(std::string_view data)> dataCallback;
FileTransferRequest(ValidURL uri)
FileTransferRequest(VerbatimURL uri)
: uri(std::move(uri))
, parentAct(getCurActivity())
{

View File

@@ -82,6 +82,8 @@ struct ServeProto::BasicClientConnection
BuildResult getBuildDerivationResponse(const StoreDirConfig & store);
void narFromPath(const StoreDirConfig & store, const StorePath & path, std::function<void(Source &)> fun);
void importPaths(const StoreDirConfig & store, std::function<void(Sink &)> fun);
};
struct ServeProto::BasicServerConnection

View File

@@ -108,6 +108,13 @@ enum struct ServeProto::Command : uint64_t {
QueryValidPaths = 1,
QueryPathInfos = 2,
DumpStorePath = 3,
/**
* @note This is no longer used by Nix (as a client), but it is used
* by Hydra. We should therefore not remove it until Hydra no longer
* uses it either.
*/
ImportPaths = 4,
// ExportPaths = 5,
BuildPaths = 6,
QueryClosure = 7,
BuildDerivation = 8,

View File

@@ -1383,7 +1383,7 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
for (auto & link : DirectoryIterator{linksDir}) {
checkInterrupt();
auto name = link.path().filename();
printMsg(lvlTalkative, "checking contents of '%s'", name);
printMsg(lvlTalkative, "checking contents of %s", name);
PosixSourceAccessor accessor;
std::string hash = hashPath(
PosixSourceAccessor::createAtRoot(link.path()),
@@ -1391,10 +1391,10 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
HashAlgorithm::SHA256)
.first.to_string(HashFormat::Nix32, false);
if (hash != name.string()) {
printError("link '%s' was modified! expected hash '%s', got '%s'", link.path(), name, hash);
printError("link %s was modified! expected hash %s, got '%s'", link.path(), name, hash);
if (repair) {
std::filesystem::remove(link.path());
printInfo("removed link '%s'", link.path());
printInfo("removed link %s", link.path());
} else {
errors = true;
}

View File

@@ -202,7 +202,7 @@ void LocalStore::optimisePath_(
full. When that happens, it's fine to ignore it: we
just effectively disable deduplication of this
file. */
printInfo("cannot link '%s' to '%s': %s", linkPath, path, strerror(errno));
printInfo("cannot link %s to '%s': %s", linkPath, path, strerror(errno));
return;
}
@@ -216,11 +216,11 @@ void LocalStore::optimisePath_(
auto stLink = lstat(linkPath.string());
if (st.st_ino == stLink.st_ino) {
debug("'%1%' is already linked to '%2%'", path, linkPath);
debug("'%1%' is already linked to %2%", path, linkPath);
return;
}
printMsg(lvlTalkative, "linking '%1%' to '%2%'", path, linkPath);
printMsg(lvlTalkative, "linking '%1%' to %2%", path, linkPath);
/* Make the containing directory writable, but only if it's not
the store itself (we don't want or need to mess with its
@@ -245,7 +245,7 @@ void LocalStore::optimisePath_(
systems). This is likely to happen with empty files.
Just shrug and ignore. */
if (st.st_size)
printInfo("'%1%' has maximum number of links", linkPath);
printInfo("%1% has maximum number of links", linkPath);
return;
}
throw;
@@ -256,13 +256,13 @@ void LocalStore::optimisePath_(
std::filesystem::rename(tempLink, path);
} catch (std::filesystem::filesystem_error & e) {
std::filesystem::remove(tempLink);
printError("unable to unlink '%1%'", tempLink);
printError("unable to unlink %1%", tempLink);
if (e.code() == std::errc::too_many_links) {
/* Some filesystems generate too many links on the rename,
rather than on the original link. (Probably it
temporarily increases the st_nlink field before
decreasing it again.) */
debug("'%s' has reached maximum number of links", linkPath);
debug("%s has reached maximum number of links", linkPath);
return;
}
throw;

View File

@@ -93,4 +93,14 @@ void ServeProto::BasicClientConnection::narFromPath(
fun(from);
}
void ServeProto::BasicClientConnection::importPaths(const StoreDirConfig & store, std::function<void(Sink &)> fun)
{
to << ServeProto::Command::ImportPaths;
fun(to);
to.flush();
if (readInt(from) != 1)
throw Error("remote machine failed to import closure");
}
} // namespace nix

View File

@@ -1717,7 +1717,6 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
if (buildMode == bmRepair) {
/* Path already exists, need to replace it */
replaceValidPath(store.toRealPath(finalDestPath), actualPath);
actualPath = store.toRealPath(finalDestPath);
} else if (buildMode == bmCheck) {
/* Path already exists, and we want to compare, so we leave out
new path in place. */
@@ -1731,7 +1730,6 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
auto destPath = store.toRealPath(finalDestPath);
deletePath(destPath);
movePath(actualPath, destPath);
actualPath = destPath;
}
}
@@ -1784,7 +1782,9 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs()
debug("unreferenced input: '%1%'", store.printStorePath(i));
}
store.optimisePath(actualPath, NoRepair); // FIXME: combine with scanForReferences()
if (!store.isValidPath(newInfo.path))
store.optimisePath(
store.toRealPath(finalDestPath), NoRepair); // FIXME: combine with scanForReferences()
newInfo.deriver = drvPath;
newInfo.ultimate = true;

View File

@@ -868,6 +868,12 @@ TEST_P(ParsedURLPathSegmentsTest, segmentsAreCorrect)
EXPECT_EQ(encodeUrlPath(segments), testCase.path);
}
TEST_P(ParsedURLPathSegmentsTest, to_string)
{
const auto & testCase = GetParam();
EXPECT_EQ(testCase.url, parseURL(testCase.url).to_string());
}
INSTANTIATE_TEST_SUITE_P(
ParsedURL,
ParsedURLPathSegmentsTest,
@@ -886,6 +892,13 @@ INSTANTIATE_TEST_SUITE_P(
.skipEmpty = false,
.description = "empty_authority_empty_path",
},
ParsedURLPathSegmentsTestCase{
.url = "path:/",
.segments = {"", ""},
.path = "/",
.skipEmpty = false,
.description = "empty_authority_root_path",
},
ParsedURLPathSegmentsTestCase{
.url = "scheme:///",
.segments = {"", ""},

View File

@@ -6,6 +6,9 @@
#include "nix/util/error.hh"
#include "nix/util/canon-path.hh"
#include "nix/util/split.hh"
#include "nix/util/util.hh"
#include "nix/util/variant-wrapper.hh"
namespace nix {
@@ -342,8 +345,7 @@ ParsedURL fixGitURL(const std::string & url);
bool isValidSchemeName(std::string_view scheme);
/**
* Either a ParsedURL or a verbatim string, but the string must be a valid
* ParsedURL. This is necessary because in certain cases URI must be passed
* Either a ParsedURL or a verbatim string. This is necessary because in certain cases URI must be passed
* verbatim (e.g. in builtin fetchers), since those are specified by the user.
* In those cases normalizations performed by the ParsedURL might be surprising
* and undesirable, since Nix must be a universal client that has to work with
@@ -354,23 +356,23 @@ bool isValidSchemeName(std::string_view scheme);
*
* Though we perform parsing and validation for internal needs.
*/
struct ValidURL : private ParsedURL
struct VerbatimURL
{
std::optional<std::string> encoded;
using Raw = std::variant<std::string, ParsedURL>;
Raw raw;
ValidURL(std::string str)
: ParsedURL(parseURL(str, /*lenient=*/false))
, encoded(std::move(str))
VerbatimURL(std::string_view s)
: raw(std::string{s})
{
}
ValidURL(std::string_view str)
: ValidURL(std::string{str})
VerbatimURL(std::string s)
: raw(std::move(s))
{
}
ValidURL(ParsedURL parsed)
: ParsedURL{std::move(parsed)}
VerbatimURL(ParsedURL url)
: raw(std::move(url))
{
}
@@ -379,25 +381,35 @@ struct ValidURL : private ParsedURL
*/
std::string to_string() const
{
return encoded.or_else([&]() -> std::optional<std::string> { return ParsedURL::to_string(); }).value();
return std::visit(
overloaded{
[](const std::string & str) { return str; }, [](const ParsedURL & url) { return url.to_string(); }},
raw);
}
const ParsedURL & parsed() const &
const ParsedURL parsed() const
{
return *this;
return std::visit(
overloaded{
[](const std::string & str) { return parseURL(str); }, [](const ParsedURL & url) { return url; }},
raw);
}
std::string_view scheme() const &
{
return ParsedURL::scheme;
}
const auto & path() const &
{
return ParsedURL::path;
return std::visit(
overloaded{
[](std::string_view str) {
auto scheme = splitPrefixTo(str, ':');
if (!scheme)
throw BadURL("URL '%s' doesn't have a scheme", str);
return *scheme;
},
[](const ParsedURL & url) -> std::string_view { return url.scheme; }},
raw);
}
};
std::ostream & operator<<(std::ostream & os, const ValidURL & url);
std::ostream & operator<<(std::ostream & os, const VerbatimURL & url);
} // namespace nix

View File

@@ -350,7 +350,7 @@ std::string ParsedURL::renderAuthorityAndPath() const
must either be empty or begin with a slash ("/") character. */
assert(path.empty() || path.front().empty());
res += authority->to_string();
} else if (std::ranges::equal(std::views::take(path, 2), std::views::repeat("", 2))) {
} else if (std::ranges::equal(std::views::take(path, 3), std::views::repeat("", 3))) {
/* If a URI does not contain an authority component, then the path cannot begin
with two slash characters ("//") */
unreachable();
@@ -434,7 +434,7 @@ bool isValidSchemeName(std::string_view s)
return std::regex_match(s.begin(), s.end(), regex, std::regex_constants::match_default);
}
std::ostream & operator<<(std::ostream & os, const ValidURL & url)
std::ostream & operator<<(std::ostream & os, const VerbatimURL & url)
{
os << url.to_string();
return os;

View File

@@ -986,6 +986,16 @@ static void opServe(Strings opFlags, Strings opArgs)
store->narFromPath(store->parseStorePath(readString(in)), out);
break;
case ServeProto::Command::ImportPaths: {
if (!writeAllowed)
throw Error("importing paths is not allowed");
// FIXME: should we skip sig checking?
importPaths(*store, in, NoCheckSigs);
// indicate success
out << 1;
break;
}
case ServeProto::Command::BuildPaths: {
if (!writeAllowed)

View File

@@ -105,7 +105,7 @@ std::tuple<StorePath, Hash> prefetchFile(
FdSink sink(fd.get());
FileTransferRequest req(ValidURL{url});
FileTransferRequest req(VerbatimURL{url});
req.decompress = false;
getFileTransfer()->download(std::move(req), sink);
}

View File

@@ -59,6 +59,9 @@ invalid_ref() {
}
valid_ref 'A/b'
valid_ref 'AaA/b'
valid_ref 'FOO/BAR/BAZ'
valid_ref 'foox'
valid_ref '1337'
valid_ref 'foo.baz'

View File

@@ -88,8 +88,3 @@ requireDaemonNewerThan "2.20"
expected=100
if [[ -v NIX_DAEMON_PACKAGE ]]; then expected=1; fi # work around the daemon not returning a 100 status correctly
expectStderr $expected nix-build --expr '{ url }: builtins.derivation { name = "nix-cache-info"; system = "x86_64-linux"; builder = "builtin:fetchurl"; inherit url; outputHashMode = "flat"; }' --argstr url "file://$narxz" 2>&1 | grep 'must be a fixed-output or impure derivation'
requireDaemonNewerThan "2.32.0pre20250831"
expect 1 nix-build --expr 'import <nix/fetchurl.nix>' --argstr name 'name' --argstr url "file://authority.not.allowed/fetchurl.sh?a=1&a=2" --no-out-link |&
grepQuiet "error: file:// URL 'file://authority.not.allowed/fetchurl.sh?a=1&a=2' has unexpected authority 'authority.not.allowed'"

View File

@@ -82,4 +82,8 @@ mkDerivation {
"foo$" = "BAD";
exportReferencesGraph.refs = [ dep ];
exportReferencesGraph.refs2 = [
dep
[ dep ]
]; # regression test for heterogeneous arrays
}

View File

@@ -2,9 +2,8 @@
source common.sh
# 27ce722638 required some incompatible changes to the nix file, so skip this
# tests for the older versions
requireDaemonNewerThan "2.4pre20210712"
# https://github.com/NixOS/nix/pull/14189
requireDaemonNewerThan "2.33"
clearStoreIfPossible