aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEelco Dolstra <eelco.dolstra@logicblox.com>2016-07-26 21:16:52 +0200
committerEelco Dolstra <eelco.dolstra@logicblox.com>2016-07-26 21:16:52 +0200
commit06bbfb6004942bfcddd930e746ee7a2bfe5c3872 (patch)
tree25395e3e0da92eb914abf4f59f33027269a28224
parentee3032e4de439b481ce1a4bc7fd8d71ea6ed2c94 (diff)
builtins.{fetchurl,fetchTarball}: Support a sha256 attribute
Also, allow builtins.{fetchurl,fetchTarball} in restricted mode if a hash is specified.
-rw-r--r--src/libexpr/primops.cc10
-rw-r--r--src/libstore/download.cc35
-rw-r--r--src/libstore/download.hh4
3 files changed, 37 insertions, 12 deletions
diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc
index 565ed69ae..25736ebff 100644
--- a/src/libexpr/primops.cc
+++ b/src/libexpr/primops.cc
@@ -1680,9 +1680,8 @@ static void prim_compareVersions(EvalState & state, const Pos & pos, Value * * a
void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
const string & who, bool unpack)
{
- if (state.restricted) throw Error(format("‘%1%’ is not allowed in restricted mode") % who);
-
string url;
+ Hash expectedHash;
state.forceValue(*args[0]);
@@ -1694,6 +1693,8 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
string name(attr.name);
if (name == "url")
url = state.forceStringNoCtx(*attr.value, *attr.pos);
+ else if (name == "sha256")
+ expectedHash = parseHash16or32(htSHA256, state.forceStringNoCtx(*attr.value, *attr.pos));
else
throw EvalError(format("unsupported argument ‘%1%’ to ‘%2%’, at %3%") % attr.name % who % attr.pos);
}
@@ -1704,7 +1705,10 @@ void fetch(EvalState & state, const Pos & pos, Value * * args, Value & v,
} else
url = state.forceStringNoCtx(*args[0], pos);
- Path res = makeDownloader()->downloadCached(state.store, url, unpack);
+ if (state.restricted && !expectedHash)
+ throw Error(format("‘%1%’ is not allowed in restricted mode") % who);
+
+ Path res = makeDownloader()->downloadCached(state.store, url, unpack, expectedHash);
mkString(v, res, PathSet({res}));
}
diff --git a/src/libstore/download.cc b/src/libstore/download.cc
index 04a2b325c..f09926895 100644
--- a/src/libstore/download.cc
+++ b/src/libstore/download.cc
@@ -3,6 +3,7 @@
#include "globals.hh"
#include "hash.hh"
#include "store-api.hh"
+#include "archive.hh"
#include <curl/curl.h>
@@ -221,10 +222,21 @@ ref<Downloader> makeDownloader()
return make_ref<CurlDownloader>();
}
-Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpack)
+Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpack, const Hash & expectedHash)
{
auto url = resolveUri(url_);
+ string name;
+ auto p = url.rfind('/');
+ if (p != string::npos) name = string(url, p + 1);
+
+ Path expectedStorePath;
+ if (expectedHash) {
+ expectedStorePath = store->makeFixedOutputPath(unpack, expectedHash.type, expectedHash, name);
+ if (store->isValidPath(expectedStorePath))
+ return expectedStorePath;
+ }
+
Path cacheDir = getCacheDir() + "/nix/tarballs";
createDirs(cacheDir);
@@ -258,10 +270,6 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
storePath = "";
}
- string name;
- auto p = url.rfind('/');
- if (p != string::npos) name = string(url, p + 1);
-
if (!skip) {
try {
@@ -269,8 +277,16 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
options.expectedETag = expectedETag;
auto res = download(url, options);
- if (!res.cached)
- storePath = store->addTextToStore(name, *res.data, PathSet(), false);
+ if (!res.cached) {
+ ValidPathInfo info;
+ StringSink sink;
+ dumpString(*res.data, sink);
+ Hash hash = hashString(expectedHash ? expectedHash.type : htSHA256, *res.data);
+ info.path = store->makeFixedOutputPath(false, hash.type, hash, name);
+ info.narHash = hashString(htSHA256, *sink.s);
+ store->addToStore(info, *sink.s, false, true);
+ storePath = info.path;
+ }
assert(!storePath.empty());
replaceSymlink(storePath, fileLink);
@@ -300,9 +316,12 @@ Path Downloader::downloadCached(ref<Store> store, const string & url_, bool unpa
unpackedStorePath = store->addToStore(name, tmpDir, true, htSHA256, defaultPathFilter, false);
}
replaceSymlink(unpackedStorePath, unpackedLink);
- return unpackedStorePath;
+ storePath = unpackedStorePath;
}
+ if (expectedStorePath != "" && storePath != expectedStorePath)
+ throw nix::Error(format("hash mismatch in file downloaded from ‘%s’") % url);
+
return storePath;
}
diff --git a/src/libstore/download.hh b/src/libstore/download.hh
index eb2b76678..efddc5528 100644
--- a/src/libstore/download.hh
+++ b/src/libstore/download.hh
@@ -1,6 +1,7 @@
#pragma once
#include "types.hh"
+#include "hash.hh"
#include <string>
@@ -27,7 +28,8 @@ struct Downloader
{
virtual DownloadResult download(string url, const DownloadOptions & options) = 0;
- Path downloadCached(ref<Store> store, const string & url, bool unpack);
+ Path downloadCached(ref<Store> store, const string & url, bool unpack,
+ const Hash & expectedHash = Hash());
enum Error { NotFound, Forbidden, Misc };
};