diff options
author | Chuck <chuck@intelligence.org> | 2019-11-15 11:55:36 -0800 |
---|---|---|
committer | Chuck <chuck@intelligence.org> | 2019-11-15 11:55:36 -0800 |
commit | 3e2c77d001a31952e62bfbb825bc7e034678ae12 (patch) | |
tree | 42f51f7319d30788d0784ea497c02c6ed71a26ac /src/libstore | |
parent | fd900c45b5ff9c6dc7f3ec814eca4ad603720899 (diff) |
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.
Diffstat (limited to 'src/libstore')
-rw-r--r-- | src/libstore/local-store.cc | 30 |
1 files changed, 29 insertions, 1 deletions
diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index b5b0f1ba7..2a21f816c 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -1238,7 +1238,35 @@ bool LocalStore::verifyStore(bool checkContents, RepairFlag repair) /* Optionally, check the content hashes (slow). */ 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); |