verify can delete paths created during its initial scanning phase of the store #1120

Open
opened 2026-02-05 01:21:44 +00:00 by raito · 0 comments
Owner

Store verification works that way:

kj::Promise<Result<bool>> LocalStore::verifyStore(bool checkContents, RepairFlag repair)
try {
    printInfo("reading the Nix store...");

    bool errors = false;

    /* Acquire the global GC lock to get a consistent snapshot of
       existing and valid paths. */
    auto fdGCLock = openGCLock();
    auto gcLock = TRY_AWAIT(
        FdLock::lockAsync(fdGCLock, ltRead, "waiting for the big garbage collector lock...")
    );

    StorePathSet validPaths;

    {
        StorePathSet storePathsInStoreDir;
        /* Why aren't we using `queryAllValidPaths`? Because that would
           tell us about all the paths than the database knows about. Here we
           want to know about all the store paths in the store directory,
           regardless of what the database thinks.

           We will end up cross-referencing these two sources of truth (the
           database and the filesystem) in the loop below, in order to catch
           invalid states.
         */
        for (auto & i : readDirectory(config_.realStoreDir)) {
            try {
                storePathsInStoreDir.insert({i.name});
            } catch (BadStorePath &) { }
        }

        /* Check whether all valid paths actually exist. */
        printInfo("checking path existence...");

        StorePathSet done;

        for (auto & i : TRY_AWAIT(queryAllValidPaths()))
            TRY_AWAIT(verifyPath(i, storePathsInStoreDir, done, validPaths, repair, errors));
    }

Note that the phase where we are populating storePathsInStoreDir is inherently racy with other Lix invocations that may be creating the inode before readDirectory (readdir loop) can find about it.

By then, we may have registered it and we will delete it because do not attempt to revalidate the existence of the inode at the end.

Store verification works that way: ```cpp kj::Promise<Result<bool>> LocalStore::verifyStore(bool checkContents, RepairFlag repair) try { printInfo("reading the Nix store..."); bool errors = false; /* Acquire the global GC lock to get a consistent snapshot of existing and valid paths. */ auto fdGCLock = openGCLock(); auto gcLock = TRY_AWAIT( FdLock::lockAsync(fdGCLock, ltRead, "waiting for the big garbage collector lock...") ); StorePathSet validPaths; { StorePathSet storePathsInStoreDir; /* Why aren't we using `queryAllValidPaths`? Because that would tell us about all the paths than the database knows about. Here we want to know about all the store paths in the store directory, regardless of what the database thinks. We will end up cross-referencing these two sources of truth (the database and the filesystem) in the loop below, in order to catch invalid states. */ for (auto & i : readDirectory(config_.realStoreDir)) { try { storePathsInStoreDir.insert({i.name}); } catch (BadStorePath &) { } } /* Check whether all valid paths actually exist. */ printInfo("checking path existence..."); StorePathSet done; for (auto & i : TRY_AWAIT(queryAllValidPaths())) TRY_AWAIT(verifyPath(i, storePathsInStoreDir, done, validPaths, repair, errors)); } ``` Note that the phase where we are populating `storePathsInStoreDir` is inherently racy with other Lix invocations that may be creating the inode before `readDirectory` (`readdir` loop) can find about it. By then, we may have registered it and we will delete it because do not attempt to revalidate the existence of the inode at the end.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lix-project/lix#1120
No description provided.