Check for and repair bad .links entries

A corrupt entry in .links prevents adding a fixed version of that file
to the store in any path.  The user experience is that corruption
present in the store 'spreads' to new paths added to the store:

(With store optimisation enabled)

1. A file in the store gets corrupted somehow (eg: filesystem bug).
2. The user tries to add a thing to the store which contains a good copy
   of the corrupted file.
3. The file being added to the store is hashed, found to match the bad
   .links entry, and is replaced by a link to the bad .links entry.
   (The .links entry's hash is not verified during add -- this would
   impose a substantial performance burden.)
4. The user observes that the thing in the store that is supposed to be
   a copy of what they were trying to add is not a correct copy -- some
   files have different contents!  Running "nix-store --verify
   --check-contents --repair" does not fix the problem.

This change makes "nix-store --verify --check-contents --repair" fix
this problem.  Bad .links entries are simply removed, allowing future
attempts to insert a good copy of the file to succeed.
This commit is contained in:
Chuck 2019-11-15 11:55:36 -08:00
parent fd900c45b5
commit 3e2c77d001

View file

@ -1238,7 +1238,35 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair)
/* Optionally, check the content hashes (slow). */ /* Optionally, check the content hashes (slow). */
if (checkContents) { if (checkContents) {
printInfo("checking hashes...");
printInfo("checking link hashes...");
AutoCloseDir dir(opendir(linksDir.c_str()));
if (!dir) throw SysError(format("opening directory '%1%'") % linksDir);
struct dirent * dirent;
while (errno = 0, dirent = readdir(dir.get())) { /* sic */
checkInterrupt();
if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) continue;
Path linkPath = linksDir + "/" + dirent->d_name;
string hash = hashPath(htSHA256, linkPath).first.to_string(Base32, false);
if (hash != dirent->d_name) {
printError(format("link '%1%' was modified! "
"expected hash '%2%', got '%3%'")
% linkPath % dirent->d_name % hash);
if (repair) {
if (unlink(linkPath.c_str()) == 0)
printError(format("Removed link '%1%'") % linkPath);
else
throw SysError(format("removing corrupt link '%1%'") % linkPath);
} else {
errors = true;
}
}
}
if (errno) throw SysError(format("reading directory '%1%'") % linksDir);
printInfo("checking store hashes...");
Hash nullHash(htSHA256); Hash nullHash(htSHA256);