mirror of
https://github.com/NixOS/nix.git
synced 2025-11-15 15:02:42 +01:00
Merge remote-tracking branch 'upstream/master' into path-info
This commit is contained in:
commit
f4f3203aa7
303 changed files with 32784 additions and 3308 deletions
|
|
@ -53,12 +53,15 @@ struct LocalStore::State::Stmts {
|
|||
SQLiteStmt InvalidatePath;
|
||||
SQLiteStmt AddDerivationOutput;
|
||||
SQLiteStmt RegisterRealisedOutput;
|
||||
SQLiteStmt UpdateRealisedOutput;
|
||||
SQLiteStmt QueryValidDerivers;
|
||||
SQLiteStmt QueryDerivationOutputs;
|
||||
SQLiteStmt QueryRealisedOutput;
|
||||
SQLiteStmt QueryAllRealisedOutputs;
|
||||
SQLiteStmt QueryPathFromHashPart;
|
||||
SQLiteStmt QueryValidPaths;
|
||||
SQLiteStmt QueryRealisationReferences;
|
||||
SQLiteStmt AddRealisationReference;
|
||||
};
|
||||
|
||||
int getSchema(Path schemaPath)
|
||||
|
|
@ -76,7 +79,7 @@ int getSchema(Path schemaPath)
|
|||
|
||||
void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
||||
{
|
||||
const int nixCASchemaVersion = 1;
|
||||
const int nixCASchemaVersion = 2;
|
||||
int curCASchema = getSchema(schemaPath);
|
||||
if (curCASchema != nixCASchemaVersion) {
|
||||
if (curCASchema > nixCASchemaVersion) {
|
||||
|
|
@ -94,7 +97,39 @@ void migrateCASchema(SQLite& db, Path schemaPath, AutoCloseFD& lockFd)
|
|||
#include "ca-specific-schema.sql.gen.hh"
|
||||
;
|
||||
db.exec(schema);
|
||||
curCASchema = nixCASchemaVersion;
|
||||
}
|
||||
|
||||
if (curCASchema < 2) {
|
||||
SQLiteTxn txn(db);
|
||||
// Ugly little sql dance to add a new `id` column and make it the primary key
|
||||
db.exec(R"(
|
||||
create table Realisations2 (
|
||||
id integer primary key autoincrement not null,
|
||||
drvPath text not null,
|
||||
outputName text not null, -- symbolic output id, usually "out"
|
||||
outputPath integer not null,
|
||||
signatures text, -- space-separated list
|
||||
foreign key (outputPath) references ValidPaths(id) on delete cascade
|
||||
);
|
||||
insert into Realisations2 (drvPath, outputName, outputPath, signatures)
|
||||
select drvPath, outputName, outputPath, signatures from Realisations;
|
||||
drop table Realisations;
|
||||
alter table Realisations2 rename to Realisations;
|
||||
)");
|
||||
db.exec(R"(
|
||||
create index if not exists IndexRealisations on Realisations(drvPath, outputName);
|
||||
|
||||
create table if not exists RealisationsRefs (
|
||||
referrer integer not null,
|
||||
realisationReference integer,
|
||||
foreign key (referrer) references Realisations(id) on delete cascade,
|
||||
foreign key (realisationReference) references Realisations(id) on delete restrict
|
||||
);
|
||||
)");
|
||||
txn.commit();
|
||||
}
|
||||
|
||||
writeFile(schemaPath, fmt("%d", nixCASchemaVersion));
|
||||
lockFile(lockFd.get(), ltRead, true);
|
||||
}
|
||||
|
|
@ -106,9 +141,6 @@ LocalStore::LocalStore(const Params & params)
|
|||
, LocalStoreConfig(params)
|
||||
, Store(params)
|
||||
, LocalFSStore(params)
|
||||
, realStoreDir_{this, false, rootDir != "" ? rootDir + "/nix/store" : storeDir, "real",
|
||||
"physical path to the Nix store"}
|
||||
, realStoreDir(realStoreDir_)
|
||||
, dbDir(stateDir + "/db")
|
||||
, linksDir(realStoreDir + "/.links")
|
||||
, reservedPath(dbDir + "/reserved")
|
||||
|
|
@ -153,13 +185,13 @@ LocalStore::LocalStore(const Params & params)
|
|||
printError("warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup);
|
||||
else {
|
||||
struct stat st;
|
||||
if (stat(realStoreDir.c_str(), &st))
|
||||
if (stat(realStoreDir.get().c_str(), &st))
|
||||
throw SysError("getting attributes of path '%1%'", realStoreDir);
|
||||
|
||||
if (st.st_uid != 0 || st.st_gid != gr->gr_gid || (st.st_mode & ~S_IFMT) != perm) {
|
||||
if (chown(realStoreDir.c_str(), 0, gr->gr_gid) == -1)
|
||||
if (chown(realStoreDir.get().c_str(), 0, gr->gr_gid) == -1)
|
||||
throw SysError("changing ownership of path '%1%'", realStoreDir);
|
||||
if (chmod(realStoreDir.c_str(), perm) == -1)
|
||||
if (chmod(realStoreDir.get().c_str(), perm) == -1)
|
||||
throw SysError("changing permissions on path '%1%'", realStoreDir);
|
||||
}
|
||||
}
|
||||
|
|
@ -310,13 +342,22 @@ LocalStore::LocalStore(const Params & params)
|
|||
if (settings.isExperimentalFeatureEnabled("ca-derivations")) {
|
||||
state->stmts->RegisterRealisedOutput.create(state->db,
|
||||
R"(
|
||||
insert or replace into Realisations (drvPath, outputName, outputPath, signatures)
|
||||
insert into Realisations (drvPath, outputName, outputPath, signatures)
|
||||
values (?, ?, (select id from ValidPaths where path = ?), ?)
|
||||
;
|
||||
)");
|
||||
state->stmts->UpdateRealisedOutput.create(state->db,
|
||||
R"(
|
||||
update Realisations
|
||||
set signatures = ?
|
||||
where
|
||||
drvPath = ? and
|
||||
outputName = ?
|
||||
;
|
||||
)");
|
||||
state->stmts->QueryRealisedOutput.create(state->db,
|
||||
R"(
|
||||
select Output.path, Realisations.signatures from Realisations
|
||||
select Realisations.id, Output.path, Realisations.signatures from Realisations
|
||||
inner join ValidPaths as Output on Output.id = Realisations.outputPath
|
||||
where drvPath = ? and outputName = ?
|
||||
;
|
||||
|
|
@ -328,6 +369,19 @@ LocalStore::LocalStore(const Params & params)
|
|||
where drvPath = ?
|
||||
;
|
||||
)");
|
||||
state->stmts->QueryRealisationReferences.create(state->db,
|
||||
R"(
|
||||
select drvPath, outputName from Realisations
|
||||
join RealisationsRefs on realisationReference = Realisations.id
|
||||
where referrer = ?;
|
||||
)");
|
||||
state->stmts->AddRealisationReference.create(state->db,
|
||||
R"(
|
||||
insert or replace into RealisationsRefs (referrer, realisationReference)
|
||||
values (
|
||||
(select id from Realisations where drvPath = ? and outputName = ?),
|
||||
(select id from Realisations where drvPath = ? and outputName = ?));
|
||||
)");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -437,14 +491,14 @@ void LocalStore::makeStoreWritable()
|
|||
if (getuid() != 0) return;
|
||||
/* Check if /nix/store is on a read-only mount. */
|
||||
struct statvfs stat;
|
||||
if (statvfs(realStoreDir.c_str(), &stat) != 0)
|
||||
if (statvfs(realStoreDir.get().c_str(), &stat) != 0)
|
||||
throw SysError("getting info about the Nix store mount point");
|
||||
|
||||
if (stat.f_flag & ST_RDONLY) {
|
||||
if (unshare(CLONE_NEWNS) == -1)
|
||||
throw SysError("setting up a private mount namespace");
|
||||
|
||||
if (mount(0, realStoreDir.c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
if (mount(0, realStoreDir.get().c_str(), "none", MS_REMOUNT | MS_BIND, 0) == -1)
|
||||
throw SysError("remounting %1% writable", realStoreDir);
|
||||
}
|
||||
#endif
|
||||
|
|
@ -664,14 +718,54 @@ void LocalStore::registerDrvOutput(const Realisation & info, CheckSigsFlag check
|
|||
void LocalStore::registerDrvOutput(const Realisation & info)
|
||||
{
|
||||
settings.requireExperimentalFeature("ca-derivations");
|
||||
auto state(_state.lock());
|
||||
retrySQLite<void>([&]() {
|
||||
state->stmts->RegisterRealisedOutput.use()
|
||||
(info.id.strHash())
|
||||
(info.id.outputName)
|
||||
(printStorePath(info.outPath))
|
||||
(concatStringsSep(" ", info.signatures))
|
||||
.exec();
|
||||
auto state(_state.lock());
|
||||
if (auto oldR = queryRealisation_(*state, info.id)) {
|
||||
if (info.isCompatibleWith(*oldR)) {
|
||||
auto combinedSignatures = oldR->signatures;
|
||||
combinedSignatures.insert(info.signatures.begin(),
|
||||
info.signatures.end());
|
||||
state->stmts->UpdateRealisedOutput.use()
|
||||
(concatStringsSep(" ", combinedSignatures))
|
||||
(info.id.strHash())
|
||||
(info.id.outputName)
|
||||
.exec();
|
||||
} else {
|
||||
throw Error("Trying to register a realisation of '%s', but we already "
|
||||
"have another one locally.\n"
|
||||
"Local: %s\n"
|
||||
"Remote: %s",
|
||||
info.id.to_string(),
|
||||
printStorePath(oldR->outPath),
|
||||
printStorePath(info.outPath)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
state->stmts->RegisterRealisedOutput.use()
|
||||
(info.id.strHash())
|
||||
(info.id.outputName)
|
||||
(printStorePath(info.outPath))
|
||||
(concatStringsSep(" ", info.signatures))
|
||||
.exec();
|
||||
}
|
||||
for (auto & [outputId, depPath] : info.dependentRealisations) {
|
||||
auto localRealisation = queryRealisationCore_(*state, outputId);
|
||||
if (!localRealisation)
|
||||
throw Error("unable to register the derivation '%s' as it "
|
||||
"depends on the non existent '%s'",
|
||||
info.id.to_string(), outputId.to_string());
|
||||
if (localRealisation->second.outPath != depPath)
|
||||
throw Error("unable to register the derivation '%s' as it "
|
||||
"depends on a realisation of '%s' that doesn’t"
|
||||
"match what we have locally",
|
||||
info.id.to_string(), outputId.to_string());
|
||||
state->stmts->AddRealisationReference.use()
|
||||
(info.id.strHash())
|
||||
(info.id.outputName)
|
||||
(outputId.strHash())
|
||||
(outputId.outputName)
|
||||
.exec();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -978,14 +1072,19 @@ StorePathSet LocalStore::querySubstitutablePaths(const StorePathSet & paths)
|
|||
}
|
||||
|
||||
|
||||
// FIXME: move this, it's not specific to LocalStore.
|
||||
void LocalStore::querySubstitutablePathInfos(const StorePathCAMap & paths, SubstitutablePathInfos & infos)
|
||||
{
|
||||
if (!settings.useSubstitutes) return;
|
||||
for (auto & sub : getDefaultSubstituters()) {
|
||||
for (auto & path : paths) {
|
||||
if (infos.count(path.first))
|
||||
// Choose first succeeding substituter.
|
||||
continue;
|
||||
|
||||
auto subPath(path.first);
|
||||
|
||||
// recompute store path so that we can use a different store root
|
||||
// Recompute store path so that we can use a different store root.
|
||||
if (path.second) {
|
||||
subPath = makeFixedOutputPathFromCA({
|
||||
.name = std::string { path.first.name() },
|
||||
|
|
@ -1151,23 +1250,15 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
|||
|
||||
deletePath(realPath);
|
||||
|
||||
// text hashing has long been allowed to have non-self-references because it is used for drv files.
|
||||
if (info.ca.has_value() && !info.references.empty() && !(std::holds_alternative<TextHash>(*info.ca) && !info.hasSelfReference))
|
||||
settings.requireExperimentalFeature("ca-references");
|
||||
|
||||
/* While restoring the path from the NAR, compute the hash
|
||||
of the NAR. */
|
||||
std::unique_ptr<AbstractHashSink> hashSink;
|
||||
if (!info.ca.has_value() || !info.hasSelfReference)
|
||||
hashSink = std::make_unique<HashSink>(htSHA256);
|
||||
else
|
||||
hashSink = std::make_unique<HashModuloSink>(htSHA256, std::string(info.path.hashPart()));
|
||||
HashSink hashSink(htSHA256);
|
||||
|
||||
TeeSource wrapperSource { source, *hashSink };
|
||||
TeeSource wrapperSource { source, hashSink };
|
||||
|
||||
restorePath(realPath, wrapperSource);
|
||||
|
||||
auto hashResult = hashSink->finish();
|
||||
auto hashResult = hashSink.finish();
|
||||
|
||||
if (hashResult.first != info.narHash)
|
||||
throw Error("hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
|
|
@ -1177,6 +1268,31 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source,
|
|||
throw Error("size mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
printStorePath(info.path), info.narSize, hashResult.second);
|
||||
|
||||
if (info.ca) {
|
||||
if (auto foHash = std::get_if<FixedOutputHash>(&*info.ca)) {
|
||||
auto actualFoHash = hashCAPath(
|
||||
foHash->method,
|
||||
foHash->hash.type,
|
||||
info.path
|
||||
);
|
||||
if (foHash->hash != actualFoHash.hash) {
|
||||
throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
printStorePath(info.path),
|
||||
foHash->hash.to_string(Base32, true),
|
||||
actualFoHash.hash.to_string(Base32, true));
|
||||
}
|
||||
}
|
||||
if (auto textHash = std::get_if<TextHash>(&*info.ca)) {
|
||||
auto actualTextHash = hashString(htSHA256, readFile(realPath));
|
||||
if (textHash->hash != actualTextHash) {
|
||||
throw Error("ca hash mismatch importing path '%s';\n specified: %s\n got: %s",
|
||||
printStorePath(info.path),
|
||||
textHash->hash.to_string(Base32, true),
|
||||
actualTextHash.to_string(Base32, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
autoGC();
|
||||
|
||||
canonicalisePathMetaData(realPath, -1);
|
||||
|
|
@ -1458,14 +1574,10 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
|
|||
/* Check the content hash (optionally - slow). */
|
||||
printMsg(lvlTalkative, "checking contents of '%s'", printStorePath(i));
|
||||
|
||||
std::unique_ptr<AbstractHashSink> hashSink;
|
||||
if (!info->ca || !info->hasSelfReference)
|
||||
hashSink = std::make_unique<HashSink>(info->narHash.type);
|
||||
else
|
||||
hashSink = std::make_unique<HashModuloSink>(info->narHash.type, std::string(info->path.hashPart()));
|
||||
auto hashSink = HashSink(info->narHash.type);
|
||||
|
||||
dumpPath(Store::toRealPath(i), *hashSink);
|
||||
auto current = hashSink->finish();
|
||||
dumpPath(Store::toRealPath(i), hashSink);
|
||||
auto current = hashSink.finish();
|
||||
|
||||
if (info->narHash != nullHash && info->narHash != current.first) {
|
||||
printError("path '%s' was modified! expected hash '%s', got '%s'",
|
||||
|
|
@ -1683,19 +1795,97 @@ void LocalStore::createUser(const std::string & userName, uid_t userId)
|
|||
}
|
||||
}
|
||||
|
||||
std::optional<const Realisation> LocalStore::queryRealisation(
|
||||
const DrvOutput& id) {
|
||||
typedef std::optional<const Realisation> Ret;
|
||||
return retrySQLite<Ret>([&]() -> Ret {
|
||||
std::optional<std::pair<int64_t, Realisation>> LocalStore::queryRealisationCore_(
|
||||
LocalStore::State & state,
|
||||
const DrvOutput & id)
|
||||
{
|
||||
auto useQueryRealisedOutput(
|
||||
state.stmts->QueryRealisedOutput.use()
|
||||
(id.strHash())
|
||||
(id.outputName));
|
||||
if (!useQueryRealisedOutput.next())
|
||||
return std::nullopt;
|
||||
auto realisationDbId = useQueryRealisedOutput.getInt(0);
|
||||
auto outputPath = parseStorePath(useQueryRealisedOutput.getStr(1));
|
||||
auto signatures =
|
||||
tokenizeString<StringSet>(useQueryRealisedOutput.getStr(2));
|
||||
|
||||
return {{
|
||||
realisationDbId,
|
||||
Realisation{
|
||||
.id = id,
|
||||
.outPath = outputPath,
|
||||
.signatures = signatures,
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
std::optional<const Realisation> LocalStore::queryRealisation_(
|
||||
LocalStore::State & state,
|
||||
const DrvOutput & id)
|
||||
{
|
||||
auto maybeCore = queryRealisationCore_(state, id);
|
||||
if (!maybeCore)
|
||||
return std::nullopt;
|
||||
auto [realisationDbId, res] = *maybeCore;
|
||||
|
||||
std::map<DrvOutput, StorePath> dependentRealisations;
|
||||
auto useRealisationRefs(
|
||||
state.stmts->QueryRealisationReferences.use()
|
||||
(realisationDbId));
|
||||
while (useRealisationRefs.next()) {
|
||||
auto depId = DrvOutput {
|
||||
Hash::parseAnyPrefixed(useRealisationRefs.getStr(0)),
|
||||
useRealisationRefs.getStr(1),
|
||||
};
|
||||
auto dependentRealisation = queryRealisationCore_(state, depId);
|
||||
assert(dependentRealisation); // Enforced by the db schema
|
||||
auto outputPath = dependentRealisation->second.outPath;
|
||||
dependentRealisations.insert({depId, outputPath});
|
||||
}
|
||||
|
||||
res.dependentRealisations = dependentRealisations;
|
||||
|
||||
return { res };
|
||||
}
|
||||
|
||||
std::optional<const Realisation>
|
||||
LocalStore::queryRealisation(const DrvOutput & id)
|
||||
{
|
||||
return retrySQLite<std::optional<const Realisation>>([&]() {
|
||||
auto state(_state.lock());
|
||||
auto use(state->stmts->QueryRealisedOutput.use()(id.strHash())(
|
||||
id.outputName));
|
||||
if (!use.next())
|
||||
return std::nullopt;
|
||||
auto outputPath = parseStorePath(use.getStr(0));
|
||||
auto signatures = tokenizeString<StringSet>(use.getStr(1));
|
||||
return Ret{Realisation{
|
||||
.id = id, .outPath = outputPath, .signatures = signatures}};
|
||||
return queryRealisation_(*state, id);
|
||||
});
|
||||
}
|
||||
|
||||
FixedOutputHash LocalStore::hashCAPath(
|
||||
const FileIngestionMethod & method, const HashType & hashType,
|
||||
const StorePath & path)
|
||||
{
|
||||
return hashCAPath(method, hashType, Store::toRealPath(path), path.hashPart());
|
||||
}
|
||||
|
||||
FixedOutputHash LocalStore::hashCAPath(
|
||||
const FileIngestionMethod & method,
|
||||
const HashType & hashType,
|
||||
const Path & path,
|
||||
const std::string_view pathHash
|
||||
)
|
||||
{
|
||||
HashModuloSink caSink ( hashType, std::string(pathHash) );
|
||||
switch (method) {
|
||||
case FileIngestionMethod::Recursive:
|
||||
dumpPath(path, caSink);
|
||||
break;
|
||||
case FileIngestionMethod::Flat:
|
||||
readFile(path, caSink);
|
||||
break;
|
||||
}
|
||||
auto hash = caSink.finish().first;
|
||||
return FixedOutputHash{
|
||||
.method = method,
|
||||
.hash = hash,
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace nix
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue