diff --git a/src/libfetchers/github.cc b/src/libfetchers/github.cc index b00609f73..f79446111 100644 --- a/src/libfetchers/github.cc +++ b/src/libfetchers/github.cc @@ -195,30 +195,23 @@ struct GitArchiveInputScheme : InputScheme {"rev", rev->gitRev()}, }); - if (auto res = getCache()->lookup(store, lockedAttrs)) { - // FIXME - //input.attrs.insert_or_assign("lastModified", getIntAttr(res->first, "lastModified")); - return {std::move(res->second), input}; - } + if (auto res = getCache()->lookup(store, lockedAttrs)) + return {std::move(res->second), std::move(input)}; auto url = getDownloadUrl(input); auto res = downloadFile(store, url.url, input.getName(), true, url.headers); - //input.attrs.insert_or_assign("lastModified", uint64_t(lastModified)); - getCache()->add( store, lockedAttrs, { {"rev", rev->gitRev()}, - // FIXME: get lastModified - //{"lastModified", uint64_t(lastModified)} }, res.storePath, true); - return {res.storePath, input}; + return {res.storePath, std::move(input)}; } std::pair, Input> getAccessor(ref store, const Input & input) const override @@ -227,6 +220,10 @@ struct GitArchiveInputScheme : InputScheme auto accessor = makeZipInputAccessor(CanonPath(store->toRealPath(storePath))); + auto lastModified = accessor->getLastModified(); + assert(lastModified); + input2.attrs.insert_or_assign("lastModified", uint64_t(*lastModified)); + accessor->setPathDisplay("«" + input2.to_string() + "»"); return {accessor, input2}; diff --git a/src/libfetchers/input-accessor.hh b/src/libfetchers/input-accessor.hh index a085b1fa5..c1636b20b 100644 --- a/src/libfetchers/input-accessor.hh +++ b/src/libfetchers/input-accessor.hh @@ -85,6 +85,13 @@ struct InputAccessor : public std::enable_shared_from_this virtual std::string showPath(const CanonPath & path); SourcePath root(); + + /* Return the maximum last-modified time of the files in this + tree, if available. */ + virtual std::optional getLastModified() + { + return std::nullopt; + } }; typedef std::function MakeNotAllowedError; diff --git a/src/libfetchers/zip-input-accessor.cc b/src/libfetchers/zip-input-accessor.cc index 8b820bbf3..e391e5b71 100644 --- a/src/libfetchers/zip-input-accessor.cc +++ b/src/libfetchers/zip-input-accessor.cc @@ -1,6 +1,7 @@ #include "input-accessor.hh" #include +#include namespace nix { @@ -28,6 +29,8 @@ struct ZipInputAccessor : InputAccessor typedef std::map Members; Members members; + time_t lastModified = 0; + ZipInputAccessor(const CanonPath & _zipPath) : zipPath(_zipPath) { @@ -47,10 +50,34 @@ struct ZipInputAccessor : InputAccessor for (zip_uint64_t n = 0; n < nrEntries; ++n) { if (zip_stat_index(zipFile, n, 0, &sb)) throw Error("couldn't stat archive member #%d in '%s': %s", n, zipPath, zip_strerror(zipFile)); + + /* Get the timestamp of this file. */ + #if 0 + if (sb.valid & ZIP_STAT_MTIME) + lastModified = std::max(lastModified, sb.mtime); + #endif + auto nExtra = zip_file_extra_fields_count(zipFile, n, ZIP_FL_CENTRAL); + for (auto i = 0; i < nExtra; ++i) { + zip_uint16_t id, len; + auto extra = zip_file_extra_field_get(zipFile, i, 0, &id, &len, ZIP_FL_CENTRAL); + if (id == 0x5455 && len >= 5) + lastModified = std::max(lastModified, (time_t) le32toh(*((uint32_t *) (extra + 1)))); + } + auto slash = strchr(sb.name, '/'); if (!slash) continue; members.emplace(slash, sb); } + + #if 0 + /* Sigh, libzip returns a local time, so convert to Unix + time. */ + if (lastModified) { + struct tm tm; + localtime_r(&lastModified, &tm); + lastModified = timegm(&tm); + } + #endif } ~ZipInputAccessor() @@ -164,6 +191,11 @@ struct ZipInputAccessor : InputAccessor return _readFile(path); } + + std::optional getLastModified() override + { + return lastModified; + } }; ref makeZipInputAccessor(const CanonPath & path)