about summary refs log tree commit diff
path: root/src/libstore
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstore')
-rw-r--r--src/libstore/download.cc35
-rw-r--r--src/libstore/download.hh4
2 files changed, 30 insertions, 9 deletions
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 <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 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 <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 };
 };