From 06bbfb6004942bfcddd930e746ee7a2bfe5c3872 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 26 Jul 2016 21:16:52 +0200 Subject: builtins.{fetchurl,fetchTarball}: Support a sha256 attribute Also, allow builtins.{fetchurl,fetchTarball} in restricted mode if a hash is specified. --- src/libexpr/primops.cc | 10 +++++++--- src/libstore/download.cc | 35 +++++++++++++++++++++++++++-------- src/libstore/download.hh | 4 +++- 3 files changed, 37 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/libexpr/primops.cc b/src/libexpr/primops.cc index 565ed69ae77b..25736ebff065 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 04a2b325c651..f099268953ab 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 @@ -221,10 +222,21 @@ ref makeDownloader() return make_ref(); } -Path Downloader::downloadCached(ref store, const string & url_, bool unpack) +Path Downloader::downloadCached(ref 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, 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, 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, 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 eb2b76678ac7..efddc55281fe 100644 --- a/src/libstore/download.hh +++ b/src/libstore/download.hh @@ -1,6 +1,7 @@ #pragma once #include "types.hh" +#include "hash.hh" #include @@ -27,7 +28,8 @@ struct Downloader { virtual DownloadResult download(string url, const DownloadOptions & options) = 0; - Path downloadCached(ref store, const string & url, bool unpack); + Path downloadCached(ref store, const string & url, bool unpack, + const Hash & expectedHash = Hash()); enum Error { NotFound, Forbidden, Misc }; }; -- cgit 1.4.1