mirror of
https://github.com/NixOS/nix
synced 2025-06-28 22:01:15 +02:00
Merge remote-tracking branch 'upstream/master' into misc-ca
This commit is contained in:
commit
839f0fe095
25 changed files with 249 additions and 141 deletions
|
@ -7,6 +7,7 @@
|
|||
#include "args.hh"
|
||||
#include "hash.hh"
|
||||
#include "archive.hh"
|
||||
#include "split.hh"
|
||||
#include "util.hh"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
@ -15,6 +16,7 @@
|
|||
|
||||
namespace nix {
|
||||
|
||||
|
||||
static size_t regularHashSize(HashType type) {
|
||||
switch (type) {
|
||||
case htMD5: return md5HashSize;
|
||||
|
@ -25,10 +27,11 @@ static size_t regularHashSize(HashType type) {
|
|||
abort();
|
||||
}
|
||||
|
||||
|
||||
std::set<std::string> hashTypes = { "md5", "sha1", "sha256", "sha512" };
|
||||
|
||||
|
||||
void Hash::init()
|
||||
Hash::Hash(HashType type) : type(type)
|
||||
{
|
||||
hashSize = regularHashSize(type);
|
||||
assert(hashSize <= maxHashSize);
|
||||
|
@ -133,57 +136,89 @@ std::string Hash::to_string(Base base, bool includeType) const
|
|||
return s;
|
||||
}
|
||||
|
||||
Hash::Hash(std::string_view s, HashType type) : Hash(s, std::optional { type }) { }
|
||||
Hash::Hash(std::string_view s) : Hash(s, std::optional<HashType>{}) { }
|
||||
|
||||
Hash::Hash(std::string_view original, std::optional<HashType> optType)
|
||||
{
|
||||
Hash Hash::parseSRI(std::string_view original) {
|
||||
auto rest = original;
|
||||
|
||||
size_t pos = 0;
|
||||
// Parse the has type before the separater, if there was one.
|
||||
auto hashRaw = splitPrefixTo(rest, '-');
|
||||
if (!hashRaw)
|
||||
throw BadHash("hash '%s' is not SRI", original);
|
||||
HashType parsedType = parseHashType(*hashRaw);
|
||||
|
||||
return Hash(rest, parsedType, true);
|
||||
}
|
||||
|
||||
// Mutates the string to eliminate the prefixes when found
|
||||
static std::pair<std::optional<HashType>, bool> getParsedTypeAndSRI(std::string_view & rest) {
|
||||
bool isSRI = false;
|
||||
|
||||
// Parse the has type before the separater, if there was one.
|
||||
std::optional<HashType> optParsedType;
|
||||
{
|
||||
auto sep = rest.find(':');
|
||||
if (sep == std::string_view::npos) {
|
||||
sep = rest.find('-');
|
||||
if (sep != std::string_view::npos)
|
||||
auto hashRaw = splitPrefixTo(rest, ':');
|
||||
|
||||
if (!hashRaw) {
|
||||
hashRaw = splitPrefixTo(rest, '-');
|
||||
if (hashRaw)
|
||||
isSRI = true;
|
||||
}
|
||||
if (sep != std::string_view::npos) {
|
||||
auto hashRaw = rest.substr(0, sep);
|
||||
optParsedType = parseHashType(hashRaw);
|
||||
rest = rest.substr(sep + 1);
|
||||
}
|
||||
if (hashRaw)
|
||||
optParsedType = parseHashType(*hashRaw);
|
||||
}
|
||||
|
||||
return {optParsedType, isSRI};
|
||||
}
|
||||
|
||||
Hash Hash::parseAnyPrefixed(std::string_view original)
|
||||
{
|
||||
auto rest = original;
|
||||
auto [optParsedType, isSRI] = getParsedTypeAndSRI(rest);
|
||||
|
||||
// Either the string or user must provide the type, if they both do they
|
||||
// must agree.
|
||||
if (!optParsedType && !optType) {
|
||||
if (!optParsedType)
|
||||
throw BadHash("hash '%s' does not include a type", rest);
|
||||
|
||||
return Hash(rest, *optParsedType, isSRI);
|
||||
}
|
||||
|
||||
Hash Hash::parseAny(std::string_view original, std::optional<HashType> optType)
|
||||
{
|
||||
auto rest = original;
|
||||
auto [optParsedType, isSRI] = getParsedTypeAndSRI(rest);
|
||||
|
||||
// Either the string or user must provide the type, if they both do they
|
||||
// must agree.
|
||||
if (!optParsedType && !optType)
|
||||
throw BadHash("hash '%s' does not include a type, nor is the type otherwise known from context.", rest);
|
||||
} else {
|
||||
this->type = optParsedType ? *optParsedType : *optType;
|
||||
if (optParsedType && optType && *optParsedType != *optType)
|
||||
throw BadHash("hash '%s' should have type '%s'", original, printHashType(*optType));
|
||||
}
|
||||
else if (optParsedType && optType && *optParsedType != *optType)
|
||||
throw BadHash("hash '%s' should have type '%s'", original, printHashType(*optType));
|
||||
|
||||
init();
|
||||
HashType hashType = optParsedType ? *optParsedType : *optType;
|
||||
return Hash(rest, hashType, isSRI);
|
||||
}
|
||||
|
||||
Hash Hash::parseNonSRIUnprefixed(std::string_view s, HashType type)
|
||||
{
|
||||
return Hash(s, type, false);
|
||||
}
|
||||
|
||||
Hash::Hash(std::string_view rest, HashType type, bool isSRI)
|
||||
: Hash(type)
|
||||
{
|
||||
if (!isSRI && rest.size() == base16Len()) {
|
||||
|
||||
auto parseHexDigit = [&](char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
throw BadHash("invalid base-16 hash '%s'", original);
|
||||
throw BadHash("invalid base-16 hash '%s'", rest);
|
||||
};
|
||||
|
||||
for (unsigned int i = 0; i < hashSize; i++) {
|
||||
hash[i] =
|
||||
parseHexDigit(rest[pos + i * 2]) << 4
|
||||
| parseHexDigit(rest[pos + i * 2 + 1]);
|
||||
parseHexDigit(rest[i * 2]) << 4
|
||||
| parseHexDigit(rest[i * 2 + 1]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -195,7 +230,7 @@ Hash::Hash(std::string_view original, std::optional<HashType> optType)
|
|||
for (digit = 0; digit < base32Chars.size(); ++digit) /* !!! slow */
|
||||
if (base32Chars[digit] == c) break;
|
||||
if (digit >= 32)
|
||||
throw BadHash("invalid base-32 hash '%s'", original);
|
||||
throw BadHash("invalid base-32 hash '%s'", rest);
|
||||
unsigned int b = n * 5;
|
||||
unsigned int i = b / 8;
|
||||
unsigned int j = b % 8;
|
||||
|
@ -205,7 +240,7 @@ Hash::Hash(std::string_view original, std::optional<HashType> optType)
|
|||
hash[i + 1] |= digit >> (8 - j);
|
||||
} else {
|
||||
if (digit >> (8 - j))
|
||||
throw BadHash("invalid base-32 hash '%s'", original);
|
||||
throw BadHash("invalid base-32 hash '%s'", rest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +248,7 @@ Hash::Hash(std::string_view original, std::optional<HashType> optType)
|
|||
else if (isSRI || rest.size() == base64Len()) {
|
||||
auto d = base64Decode(rest);
|
||||
if (d.size() != hashSize)
|
||||
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", original);
|
||||
throw BadHash("invalid %s hash '%s'", isSRI ? "SRI" : "base-64", rest);
|
||||
assert(hashSize);
|
||||
memcpy(hash, d.data(), hashSize);
|
||||
}
|
||||
|
@ -231,7 +266,7 @@ Hash newHashAllowEmpty(std::string hashStr, std::optional<HashType> ht)
|
|||
warn("found empty hash, assuming '%s'", h.to_string(SRI, true));
|
||||
return h;
|
||||
} else
|
||||
return Hash(hashStr, ht);
|
||||
return Hash::parseAny(hashStr, ht);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -34,21 +34,31 @@ struct Hash
|
|||
HashType type;
|
||||
|
||||
/* Create a zero-filled hash object. */
|
||||
Hash(HashType type) : type(type) { init(); };
|
||||
Hash(HashType type);
|
||||
|
||||
/* Initialize the hash from a string representation, in the format
|
||||
/* Parse the hash from a string representation in the format
|
||||
"[<type>:]<base16|base32|base64>" or "<type>-<base64>" (a
|
||||
Subresource Integrity hash expression). If the 'type' argument
|
||||
is not present, then the hash type must be specified in the
|
||||
string. */
|
||||
Hash(std::string_view s, std::optional<HashType> type);
|
||||
// type must be provided
|
||||
Hash(std::string_view s, HashType type);
|
||||
// hash type must be part of string
|
||||
Hash(std::string_view s);
|
||||
static Hash parseAny(std::string_view s, std::optional<HashType> type);
|
||||
|
||||
void init();
|
||||
/* Parse a hash from a string representation like the above, except the
|
||||
type prefix is mandatory is there is no separate arguement. */
|
||||
static Hash parseAnyPrefixed(std::string_view s);
|
||||
|
||||
/* Parse a plain hash that musst not have any prefix indicating the type.
|
||||
The type is passed in to disambiguate. */
|
||||
static Hash parseNonSRIUnprefixed(std::string_view s, HashType type);
|
||||
|
||||
static Hash parseSRI(std::string_view original);
|
||||
|
||||
private:
|
||||
/* The type must be provided, the string view must not include <type>
|
||||
prefix. `isSRI` helps disambigate the various base-* encodings. */
|
||||
Hash(std::string_view s, HashType type, bool isSRI);
|
||||
|
||||
public:
|
||||
/* Check whether a hash is set. */
|
||||
operator bool () const { return (bool) type; }
|
||||
|
||||
|
|
33
src/libutil/split.hh
Normal file
33
src/libutil/split.hh
Normal file
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string_view>
|
||||
|
||||
#include "util.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
// If `separator` is found, we return the portion of the string before the
|
||||
// separator, and modify the string argument to contain only the part after the
|
||||
// separator. Otherwise, wer return `std::nullopt`, and we leave the argument
|
||||
// string alone.
|
||||
static inline std::optional<std::string_view> splitPrefixTo(std::string_view & string, char separator) {
|
||||
auto sepInstance = string.find(separator);
|
||||
|
||||
if (sepInstance != std::string_view::npos) {
|
||||
auto prefix = string.substr(0, sepInstance);
|
||||
string.remove_prefix(sepInstance+1);
|
||||
return prefix;
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
static inline bool splitPrefix(std::string_view & string, std::string_view prefix) {
|
||||
bool res = hasPrefix(string, prefix);
|
||||
if (res)
|
||||
string.remove_prefix(prefix.length());
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
40
src/libutil/topo-sort.hh
Normal file
40
src/libutil/topo-sort.hh
Normal file
|
@ -0,0 +1,40 @@
|
|||
#include "error.hh"
|
||||
|
||||
namespace nix {
|
||||
|
||||
template<typename T>
|
||||
std::vector<T> topoSort(std::set<T> items,
|
||||
std::function<std::set<T>(const T &)> getChildren,
|
||||
std::function<Error(const T &, const T &)> makeCycleError)
|
||||
{
|
||||
std::vector<T> sorted;
|
||||
std::set<T> visited, parents;
|
||||
|
||||
std::function<void(const T & path, const T * parent)> dfsVisit;
|
||||
|
||||
dfsVisit = [&](const T & path, const T * parent) {
|
||||
if (parents.count(path)) throw makeCycleError(path, *parent);
|
||||
|
||||
if (!visited.insert(path).second) return;
|
||||
parents.insert(path);
|
||||
|
||||
std::set<T> references = getChildren(path);
|
||||
|
||||
for (auto & i : references)
|
||||
/* Don't traverse into items that don't exist in our starting set. */
|
||||
if (i != path && items.count(i))
|
||||
dfsVisit(i, &path);
|
||||
|
||||
sorted.push_back(path);
|
||||
parents.erase(path);
|
||||
};
|
||||
|
||||
for (auto & i : items)
|
||||
dfsVisit(i, nullptr);
|
||||
|
||||
std::reverse(sorted.begin(), sorted.end());
|
||||
|
||||
return sorted;
|
||||
}
|
||||
|
||||
}
|
|
@ -1450,7 +1450,7 @@ string base64Decode(std::string_view s)
|
|||
|
||||
char digit = decode[(unsigned char) c];
|
||||
if (digit == -1)
|
||||
throw Error("invalid character in Base64 string");
|
||||
throw Error("invalid character in Base64 string: '%c'", c);
|
||||
|
||||
bits += 6;
|
||||
d = d << 6 | digit;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue