diff --git a/src/libmain/shared.cc b/src/libmain/shared.cc index 50f90bfb3..99e8fcedb 100644 --- a/src/libmain/shared.cc +++ b/src/libmain/shared.cc @@ -174,15 +174,6 @@ void initNix(bool loadConfig) everybody. */ umask(0022); - /* Initialise the PRNG. */ - struct timeval tv; - gettimeofday(&tv, 0); -#ifndef _WIN32 - srandom(tv.tv_usec); -#endif - srand(tv.tv_usec); - - } diff --git a/src/libstore/build/derivation-goal.cc b/src/libstore/build/derivation-goal.cc index 34ed16a38..03d984cbe 100644 --- a/src/libstore/build/derivation-goal.cc +++ b/src/libstore/build/derivation-goal.cc @@ -765,64 +765,6 @@ Goal::Co DerivationGoal::tryLocalBuild() { } -static void chmod_(const Path & path, mode_t mode) -{ - if (chmod(path.c_str(), mode) == -1) - throw SysError("setting permissions on '%s'", path); -} - - -/* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if - it's a directory and we're not root (to be able to update the - directory's parent link ".."). */ -static void movePath(const Path & src, const Path & dst) -{ - auto st = lstat(src); - - bool changePerm = ( -#ifndef _WIN32 - geteuid() -#else - !isRootUser() -#endif - && S_ISDIR(st.st_mode) && !(st.st_mode & S_IWUSR)); - - if (changePerm) - chmod_(src, st.st_mode | S_IWUSR); - - std::filesystem::rename(src, dst); - - if (changePerm) - chmod_(dst, st.st_mode); -} - - -void replaceValidPath(const Path & storePath, const Path & tmpPath) -{ - /* We can't atomically replace storePath (the original) with - tmpPath (the replacement), so we have to move it out of the - way first. We'd better not be interrupted here, because if - we're repairing (say) Glibc, we end up with a broken system. */ - Path oldPath = fmt("%1%.old-%2%-%3%", storePath, getpid(), rand()); - if (pathExists(storePath)) - movePath(storePath, oldPath); - - try { - movePath(tmpPath, storePath); - } catch (...) { - try { - // attempt to recover - movePath(oldPath, storePath); - } catch (...) { - ignoreExceptionExceptInterrupt(); - } - throw; - } - - deletePath(oldPath); -} - - int DerivationGoal::getChildStatus() { #ifndef _WIN32 // TODO enable build hook on Windows diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc index e9e4b2c44..9bd338cf7 100644 --- a/src/libstore/filetransfer.cc +++ b/src/libstore/filetransfer.cc @@ -7,6 +7,7 @@ #include "finally.hh" #include "callback.hh" #include "signals.hh" +#include "rng.hh" #if ENABLE_S3 #include @@ -26,7 +27,6 @@ #include #include #include -#include #include #include @@ -42,8 +42,7 @@ struct curlFileTransfer : public FileTransfer { CURLM * curlm = 0; - std::random_device rd; - std::mt19937 mt19937; + RandomFloatGenerator rng{0.0, 1.0}; struct TransferItem : public std::enable_shared_from_this { @@ -502,7 +501,7 @@ struct curlFileTransfer : public FileTransfer || writtenToSink == 0 || (acceptRanges && encoding.empty()))) { - int ms = request.baseRetryTimeMs * std::pow(2.0f, attempt - 1 + std::uniform_real_distribution<>(0.0, 0.5)(fileTransfer.mt19937)); + int ms = request.baseRetryTimeMs * std::pow(2.0f, attempt - 1 + fileTransfer.rng()); if (writtenToSink) warn("%s; retrying from offset %d in %d ms", exc.what(), writtenToSink, ms); else @@ -539,7 +538,6 @@ struct curlFileTransfer : public FileTransfer std::thread workerThread; curlFileTransfer() - : mt19937(rd()) { static std::once_flag globalInit; std::call_once(globalInit, curl_global_init, CURL_GLOBAL_ALL); diff --git a/src/libstore/indirect-root-store.cc b/src/libstore/indirect-root-store.cc index 844d0d6ed..744928ec9 100644 --- a/src/libstore/indirect-root-store.cc +++ b/src/libstore/indirect-root-store.cc @@ -8,7 +8,7 @@ void IndirectRootStore::makeSymlink(const Path & link, const Path & target) createDirs(dirOf(link)); /* Create the new symlink. */ - Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rand()); + Path tempLink = fmt("%1%.tmp-%2%-%3%", link, getpid(), rng()); createSymlink(target, tempLink); /* Atomically replace the old one. */ diff --git a/src/libstore/indirect-root-store.hh b/src/libstore/indirect-root-store.hh index b74ebc1ee..1a692e600 100644 --- a/src/libstore/indirect-root-store.hh +++ b/src/libstore/indirect-root-store.hh @@ -2,6 +2,7 @@ ///@file #include "local-fs-store.hh" +#include "rng.hh" namespace nix { @@ -68,6 +69,8 @@ struct IndirectRootStore : public virtual LocalFSStore */ virtual void addIndirectRoot(const Path & path) = 0; + RandomIntGenerator rng{}; + protected: void makeSymlink(const Path & link, const Path & target); }; diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index aeff24c64..99c833517 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -222,7 +222,7 @@ void LocalStore::optimisePath_(Activity * act, OptimiseStats & stats, its timestamp back to 0. */ MakeReadOnly makeReadOnly(mustToggle ? dirOfPath : ""); - std::filesystem::path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), rand()); + std::filesystem::path tempLink = fmt("%1%/.tmp-link-%2%-%3%", realStoreDir, getpid(), rng()); try { std::filesystem::create_hard_link(linkPath, tempLink); diff --git a/src/libstore/sqlite.cc b/src/libstore/sqlite.cc index f02e472fd..0c77dd04e 100644 --- a/src/libstore/sqlite.cc +++ b/src/libstore/sqlite.cc @@ -3,6 +3,7 @@ #include "util.hh" #include "url.hh" #include "signals.hh" +#include "rng.hh" #include @@ -258,7 +259,7 @@ void handleSQLiteBusy(const SQLiteBusy & e, time_t & nextWarning) is likely to fail again. */ checkInterrupt(); /* <= 0.1s */ - std::this_thread::sleep_for(std::chrono::milliseconds { rand() % 100 }); + std::this_thread::sleep_for(std::chrono::milliseconds { RandomIntGenerator{0, 100}() }); } } diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc index b4685b3a7..fc6a825df 100644 --- a/src/libstore/unix/build/local-derivation-goal.cc +++ b/src/libstore/unix/build/local-derivation-goal.cc @@ -275,9 +275,11 @@ static void chmod_(const Path & path, mode_t mode) } -/* Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if - it's a directory and we're not root (to be able to update the - directory's parent link ".."). */ +/** + * Move/rename path 'src' to 'dst'. Temporarily make 'src' writable if + * it's a directory and we're not root (to be able to update the + * directory's parent link ".."). + */ static void movePath(const Path & src, const Path & dst) { auto st = lstat(src); @@ -294,7 +296,30 @@ static void movePath(const Path & src, const Path & dst) } -extern void replaceValidPath(const Path & storePath, const Path & tmpPath); +void LocalDerivationGoal::replaceValidPath(const Path & storePath, const Path & tmpPath) +{ + /* We can't atomically replace storePath (the original) with + tmpPath (the replacement), so we have to move it out of the + way first. We'd better not be interrupted here, because if + we're repairing (say) Glibc, we end up with a broken system. */ + Path oldPath = fmt("%1%.old-%2%-%3%", storePath, getpid(), getLocalStore().rng()); + if (pathExists(storePath)) + movePath(storePath, oldPath); + + try { + movePath(tmpPath, storePath); + } catch (...) { + try { + // attempt to recover + movePath(oldPath, storePath); + } catch (...) { + ignoreExceptionExceptInterrupt(); + } + throw; + } + + deletePath(oldPath); +} int LocalDerivationGoal::getChildStatus() diff --git a/src/libstore/unix/build/local-derivation-goal.hh b/src/libstore/unix/build/local-derivation-goal.hh index 1ea247661..a20b0c3fe 100644 --- a/src/libstore/unix/build/local-derivation-goal.hh +++ b/src/libstore/unix/build/local-derivation-goal.hh @@ -319,6 +319,8 @@ struct LocalDerivationGoal : public DerivationGoal * rewrites caught everything */ StorePath makeFallbackPath(OutputNameView outputName); + + void replaceValidPath(const Path & storePath, const Path & tmpPath); }; } diff --git a/src/libutil/meson.build b/src/libutil/meson.build index 57b741a50..c67f3367c 100644 --- a/src/libutil/meson.build +++ b/src/libutil/meson.build @@ -248,6 +248,7 @@ headers = [config_h] + files( 'util.hh', 'variant-wrapper.hh', 'xml-writer.hh', + 'rng.hh' ) if host_machine.system() == 'linux' diff --git a/src/libutil/rng.hh b/src/libutil/rng.hh new file mode 100644 index 000000000..ca9514977 --- /dev/null +++ b/src/libutil/rng.hh @@ -0,0 +1,40 @@ +#pragma once +/*! \file + * \brief Psuedo-random number generators class for easier use of C++'s random number generation facilities. + */ + +#include +#include + +namespace nix { + +// Inspired by the book "A Tour of C++, Third Edition" (ISBN-10 0136816487) +template +struct RandomNumberGenerator +{ +public: + using limits = std::numeric_limits; + RandomNumberGenerator(T low = limits::min(), T high = limits::max()) + : engine(std::random_device{}()) + , dist(low, high){}; + RandomNumberGenerator(std::seed_seq seed, T low, T high) + : engine(seed) + , dist(low, high){}; + T operator()() + { + return dist(engine); + } + void seed(int s) + { + engine.seed(s); + } +private: + Engine engine; + Distribution dist; +}; + +using RandomIntGenerator = RandomNumberGenerator, std::default_random_engine>; +using RandomFloatGenerator = + RandomNumberGenerator, std::default_random_engine>; + +} // namespace nix