* `nix-store --add-fixed' to preload the outputs of fixed-output

derivations.  This is mostly to simplify the implementation of
  nix-prefetch-{url, svn}, which now work properly in setuid
  installations.

* Enforce valid store names in `nix-store --add / --add-fixed'.
This commit is contained in:
Eelco Dolstra 2005-04-07 14:01:51 +00:00
parent 57d023a184
commit c815aff21b
7 changed files with 152 additions and 57 deletions

6
NEWS
View file

@ -1,4 +1,4 @@
Version 0.8 Version 0.8
NOTE: the hashing scheme in Nix 0.8 changed (as detailed below). As a NOTE: the hashing scheme in Nix 0.8 changed (as detailed below). As a
result, `nix-pull' manifests and channels built for Nix 0.7 and below result, `nix-pull' manifests and channels built for Nix 0.7 and below
@ -161,7 +161,7 @@ Nix 0.8 has the following improvements:
all unsafe, really ;-) all unsafe, really ;-)
Version 0.7 Version 0.7 (January 12, 2005)
* Binary patching. When upgrading components using pre-built binaries * Binary patching. When upgrading components using pre-built binaries
(through nix-pull / nix-channel), Nix can automatically download and (through nix-pull / nix-channel), Nix can automatically download and
@ -183,7 +183,7 @@ Version 0.7
dependencies are revealed. dependencies are revealed.
Version 0.6 Version 0.6 (November 14, 2004)
Major changes include the following: Major changes include the following:

View file

@ -2,10 +2,4 @@
echo "downloading $url into $out" echo "downloading $url into $out"
prefetch=@storedir@/nix-prefetch-url-$outputHash @curl@ --fail --location --max-redirs 20 "$url" > "$out"
if test -f "$prefetch"; then
echo "using prefetched $prefetch";
@coreutils@/mv $prefetch $out
else
@curl@ --fail --location --max-redirs 20 "$url" > "$out"
fi

View file

@ -1,7 +1,7 @@
#! @shell@ -e #! @shell@ -e
url=$1 url=$1
hash=$2 expHash=$2
hashType=$NIX_HASH_ALGO hashType=$NIX_HASH_ALGO
if test -z "$hashType"; then if test -z "$hashType"; then
@ -14,55 +14,54 @@ if test "$hashType" != "md5"; then
fi fi
if test -z "$url"; then if test -z "$url"; then
echo "syntax: nix-prefetch-url URL" >&2 echo "syntax: nix-prefetch-url URL [EXPECTED-HASH]" >&2
exit 1 exit 1
fi fi
# Determine the hash, unless it was given. name=$(basename "$url")
if test -z "$hash"; then if test -z "$name"; then echo "invalid url"; exit 1; fi
# !!! race
tmpPath1=@storedir@/nix-prefetch-url-$$
# Test whether we have write permission in the store. If not, # If the hash was given, a file with that hash may already be in the
# fetch to /tmp and don't copy to the store. This is a hack to # store.
# make this script at least work somewhat in setuid installations. if test -n "$expHash"; then
if ! touch $tmpPath1 2> /dev/null; then finalPath=$(@bindir@/nix-store --print-fixed-path "$hashType" "$expHash" "$name")
echo "(cannot write to the store, result won't be cached)" >&2 if ! @bindir@/nix-store --check-validity "$finalPath" 2> /dev/null; then
dummyMode=1 finalPath=
tmpPath1=/tmp/nix-prefetch-url-$$ # !!! security?
fi fi
hash=$expHash
fi
# If we don't know the hash or a file with that hash doesn't exist,
# download the file and add it to the store.
if test -z "$finalPath"; then
tmpPath=/tmp/nix-prefetch-url-$$ # !!! security?
tmpFile=$tmpPath/$name
mkdir $tmpPath
# Perform the download. # Perform the download.
@curl@ --fail --location --max-redirs 20 "$url" > $tmpPath1 @curl@ --fail --location --max-redirs 20 "$url" > $tmpFile
# Compute the hash. # Compute the hash.
hash=$(@bindir@/nix-hash --type "$hashType" $hashFormat --flat $tmpPath1) hash=$(@bindir@/nix-hash --type "$hashType" $hashFormat --flat $tmpFile)
if ! test -n "$QUIET"; then echo "hash is $hash" >&2; fi if ! test -n "$QUIET"; then echo "hash is $hash" >&2; fi
# Rename it so that the fetchurl builder can find it. # Add the downloaded file to the Nix store.
if test "$dummyMode" != 1; then finalPath=$(@bindir@/nix-store --add-fixed "$hashType" $tmpFile)
tmpPath2=@storedir@/nix-prefetch-url-$hash
test -e $tmpPath2 || mv $tmpPath1 $tmpPath2 # !!! race if test -n "$tmpPath"; then rm -rf $tmpPath || true; fi
if test -n "$expHash" -a "$expHash" != "$hash"; then
echo "hash mismatch for URL \`$url'"
exit 1
fi fi
fi fi
# Create a Nix expression that does a fetchurl.
storeExpr=$( \
echo "(import @datadir@/nix/corepkgs/fetchurl) \
{url = $url; outputHashAlgo = \"$hashType\"; outputHash = \"$hash\"; system = \"@system@\";}" \
| @bindir@/nix-instantiate -)
# Realise it.
finalPath=$(@bindir@/nix-store -r $storeExpr)
if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi if ! test -n "$QUIET"; then echo "path is $finalPath" >&2; fi
if test -n "$tmpPath1" -o -n "$tmpPath2"; then
rm -rf $tmpPath1 $tmpPath2 || true
fi
echo $hash echo $hash
if test -n "$PRINT_PATH"; then if test -n "$PRINT_PATH"; then

View file

@ -267,17 +267,7 @@ static Expr primDerivation(EvalState & state, const ATermVector & _args)
/* Check the derivation name. It shouldn't contain whitespace, /* Check the derivation name. It shouldn't contain whitespace,
but we are conservative here: we check whether only but we are conservative here: we check whether only
alphanumerics and some other characters appear. */ alphanumerics and some other characters appear. */
string validChars = "+-._?="; checkStoreName(drvName);
for (string::iterator i = drvName.begin(); i != drvName.end(); ++i)
if (!((*i >= 'A' && *i <= 'Z') ||
(*i >= 'a' && *i <= 'z') ||
(*i >= '0' && *i <= '9') ||
validChars.find(*i) != string::npos))
{
throw Error(format("invalid character `%1%' in derivation name `%2%'")
% *i % drvName);
}
if (isDerivation(drvName)) if (isDerivation(drvName))
throw Error(format("derivation names are not allowed to end in `%1%'") throw Error(format("derivation names are not allowed to end in `%1%'")
% drvExtension); % drvExtension);

View file

@ -202,6 +202,21 @@ Path toStorePath(const Path & path)
} }
void checkStoreName(const string & name)
{
string validChars = "+-._?=";
for (string::const_iterator i = name.begin(); i != name.end(); ++i)
if (!((*i >= 'A' && *i <= 'Z') ||
(*i >= 'a' && *i <= 'z') ||
(*i >= '0' && *i <= '9') ||
validChars.find(*i) != string::npos))
{
throw Error(format("invalid character `%1%' in name `%2%'")
% *i % name);
}
}
void canonicalisePathMetaData(const Path & path) void canonicalisePathMetaData(const Path & path)
{ {
checkInterrupt(); checkInterrupt();
@ -579,13 +594,28 @@ Path makeStorePath(const string & type,
string s = type + ":sha256:" + printHash(hash) + ":" string s = type + ":sha256:" + printHash(hash) + ":"
+ nixStore + ":" + suffix; + nixStore + ":" + suffix;
checkStoreName(suffix);
return nixStore + "/" return nixStore + "/"
+ printHash32(compressHash(hashString(htSHA256, s), 20)) + printHash32(compressHash(hashString(htSHA256, s), 20))
+ "-" + suffix; + "-" + suffix;
} }
Path addToStore(const Path & _srcPath) Path makeFixedOutputPath(bool recursive,
string hashAlgo, Hash hash, string name)
{
/* !!! copy/paste from primops.cc */
Hash h = hashString(htSHA256, "fixed:out:"
+ (recursive ? (string) "r:" : "") + hashAlgo + ":"
+ printHash(hash) + ":"
+ "");
return makeStorePath("output:out", h, name);
}
static Path _addToStore(bool fixed, bool recursive,
string hashAlgo, const Path & _srcPath)
{ {
Path srcPath(absPath(_srcPath)); Path srcPath(absPath(_srcPath));
debug(format("adding `%1%' to the store") % srcPath); debug(format("adding `%1%' to the store") % srcPath);
@ -597,7 +627,22 @@ Path addToStore(const Path & _srcPath)
} }
string baseName = baseNameOf(srcPath); string baseName = baseNameOf(srcPath);
Path dstPath = makeStorePath("source", h, baseName);
Path dstPath;
if (fixed) {
HashType ht(parseHashType(hashAlgo));
Hash h2(ht);
{
SwitchToOriginalUser sw;
h2 = recursive ? hashPath(ht, srcPath) : hashFile(ht, srcPath);
}
dstPath = makeFixedOutputPath(recursive, hashAlgo, h2, baseName);
}
else dstPath = makeStorePath("source", h, baseName);
addTempRoot(dstPath); addTempRoot(dstPath);
@ -635,6 +680,18 @@ Path addToStore(const Path & _srcPath)
} }
Path addToStore(const Path & srcPath)
{
return _addToStore(false, false, "", srcPath);
}
Path addToStoreFixed(bool recursive, string hashAlgo, const Path & srcPath)
{
return _addToStore(true, recursive, hashAlgo, srcPath);
}
Path addTextToStore(const string & suffix, const string & s, Path addTextToStore(const string & suffix, const string & s,
const PathSet & references) const PathSet & references)
{ {

View file

@ -85,6 +85,8 @@ void assertStorePath(const Path & path);
bool isInStore(const Path & path); bool isInStore(const Path & path);
bool isStorePath(const Path & path); bool isStorePath(const Path & path);
void checkStoreName(const string & name);
/* Chop off the parts after the top-level store name, e.g., /* Chop off the parts after the top-level store name, e.g.,
/nix/store/abcd-foo/bar => /nix/store/abcd-foo. */ /nix/store/abcd-foo/bar => /nix/store/abcd-foo. */
Path toStorePath(const Path & path); Path toStorePath(const Path & path);
@ -137,6 +139,13 @@ Path makeStorePath(const string & type,
the resulting path. The resulting path is returned. */ the resulting path. The resulting path is returned. */
Path addToStore(const Path & srcPath); Path addToStore(const Path & srcPath);
/* Like addToStore(), but for pre-adding the outputs of fixed-output
derivations. */
Path addToStoreFixed(bool recursive, string hashAlgo, const Path & srcPath);
Path makeFixedOutputPath(bool recursive,
string hashAlgo, Hash hash, string name);
/* Like addToStore, but the contents written to the output path is a /* Like addToStore, but the contents written to the output path is a
regular file containing the given string. */ regular file containing the given string. */
Path addTextToStore(const string & suffix, const string & s, Path addTextToStore(const string & suffix, const string & s,

View file

@ -85,8 +85,7 @@ static void opRealise(Strings opFlags, Strings opArgs)
} }
/* Add files to the Nix values directory and print the resulting /* Add files to the Nix store and print the resulting paths. */
paths. */
static void opAdd(Strings opFlags, Strings opArgs) static void opAdd(Strings opFlags, Strings opArgs)
{ {
if (!opFlags.empty()) throw UsageError("unknown flag"); if (!opFlags.empty()) throw UsageError("unknown flag");
@ -96,6 +95,49 @@ static void opAdd(Strings opFlags, Strings opArgs)
} }
/* Preload the output of a fixed-output derivation into the Nix
store. */
static void opAddFixed(Strings opFlags, Strings opArgs)
{
bool recursive = false;
for (Strings::iterator i = opFlags.begin();
i != opFlags.end(); ++i)
if (*i == "--recursive") recursive = true;
else throw UsageError(format("unknown flag `%1%'") % *i);
if (opArgs.empty())
throw UsageError("first argument must be hash algorithm");
string hashAlgo = opArgs.front();
opArgs.pop_front();
for (Strings::iterator i = opArgs.begin(); i != opArgs.end(); ++i)
cout << format("%1%\n") % addToStoreFixed(recursive, hashAlgo, *i);
}
/* Hack to support caching in `nix-prefetch-url'. */
static void opPrintFixedPath(Strings opFlags, Strings opArgs)
{
bool recursive = false;
for (Strings::iterator i = opFlags.begin();
i != opFlags.end(); ++i)
if (*i == "--recursive") recursive = true;
else throw UsageError(format("unknown flag `%1%'") % *i);
Strings::iterator i = opArgs.begin();
string hashAlgo = *i++;
string hash = *i++;
string name = *i++;
cout << format("%1%\n") %
makeFixedOutputPath(recursive, hashAlgo,
parseHash(parseHashType(hashAlgo), hash), name);
}
/* Place in `paths' the set of paths that are required to `realise' /* Place in `paths' the set of paths that are required to `realise'
the given store path, i.e., all paths necessary for valid the given store path, i.e., all paths necessary for valid
deployment of the path. For a derivation, this is the union of deployment of the path. For a derivation, this is the union of
@ -557,6 +599,10 @@ void run(Strings args)
op = opRealise; op = opRealise;
else if (arg == "--add" || arg == "-A") else if (arg == "--add" || arg == "-A")
op = opAdd; op = opAdd;
else if (arg == "--add-fixed")
op = opAddFixed;
else if (arg == "--print-fixed-path")
op = opPrintFixedPath;
else if (arg == "--query" || arg == "-q") else if (arg == "--query" || arg == "-q")
op = opQuery; op = opQuery;
else if (arg == "--substitute") else if (arg == "--substitute")