Skip to content

Commit

Permalink
Merge pull request #9 from NixLayeredStore/gc
Browse files Browse the repository at this point in the history
Add a GC test, fix hardlinking issue
  • Loading branch information
Ericson2314 authored Jul 25, 2023
2 parents 272cfd6 + 9ef0a9e commit a1a7f31
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 3 deletions.
7 changes: 4 additions & 3 deletions src/libstore/build/local-derivation-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -386,8 +386,9 @@ void LocalDerivationGoal::cleanupPostOutputsRegisteredModeNonCheck()


#if __linux__
static void linkOrCopy(const Path & from, const Path & to)
static void linkOrCopy(LocalFSStore & store, const StorePath & from_, const Path & to)
{
auto from = store.toRealPathForHardLink(from_);
if (link(from.c_str(), to.c_str()) == -1) {
/* Hard-linking fails if we exceed the maximum link count on a
file (e.g. 32000 of ext3), which is quite possible after a
Expand Down Expand Up @@ -712,7 +713,7 @@ void LocalDerivationGoal::startBuilder()
if (S_ISDIR(lstat(r).st_mode))
dirsInChroot.insert_or_assign(p, r);
else
linkOrCopy(r, chrootRootDir + p);
linkOrCopy(getLocalStore(), i, chrootRootDir + p);
}

/* If we're repairing, checking or rebuilding part of a
Expand Down Expand Up @@ -1574,7 +1575,7 @@ void LocalDerivationGoal::addDependency(const StorePath & path)
throw Error("could not add path '%s' to sandbox", worker.store.printStorePath(path));

} else
linkOrCopy(source, target);
linkOrCopy(getLocalStore(), path, target);

#else
throw Error("don't know how to make path '%s' (produced by a recursive Nix call) appear in the sandbox",
Expand Down
10 changes: 10 additions & 0 deletions src/libstore/local-fs-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,16 @@ public:
return getRealStoreDir() + "/" + std::string(storePath, storeDir.size() + 1);
}

/**
* If the real path is hardlinked with something else, we might
* prefer to refer to the other path instead. This is the case with
* overlayfs, for example.
*/
virtual Path toRealPathForHardLink(const StorePath & storePath)
{
return Store::toRealPath(storePath);
}

std::optional<std::string> getBuildLogExact(const StorePath & path) override;

};
Expand Down
7 changes: 7 additions & 0 deletions src/libstore/local-overlay-store.cc
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,13 @@ void LocalOverlayStore::optimiseStore()
}
}

Path LocalOverlayStore::toRealPathForHardLink(const StorePath & path)
{
return lowerStore->isValidPath(path)
? lowerStore->Store::toRealPath(path)
: Store::toRealPath(path);
}

static RegisterStoreImplementation<LocalOverlayStore, LocalOverlayStoreConfig> regLocalOverlayStore;

}
2 changes: 2 additions & 0 deletions src/libstore/local-overlay-store.hh
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ private:
void deleteGCPath(const Path & path, uint64_t & bytesFreed) override;

void optimiseStore() override;

Path toRealPathForHardLink(const StorePath & storePath) override;
};

}
57 changes: 57 additions & 0 deletions tests/overlay-local-store/gc-inner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#!/usr/bin/env bash

set -eu -o pipefail

source common.sh

# Avoid store dir being inside sandbox build-dir
unset NIX_STORE_DIR
unset NIX_STATE_DIR

storeDirs

initLowerStore

mountOverlayfs

export NIX_REMOTE="$storeB"
stateB="$storeBRoot/nix/var/nix"
outPath=$(nix-build ../hermetic.nix --no-out-link --arg busybox "$busybox" --arg seed 2)

# Set a GC root.
mkdir -p "$stateB"
rm -f "$stateB"/gcroots/foo
ln -sf $outPath "$stateB"/gcroots/foo

[ "$(nix-store -q --roots $outPath)" = "$stateB/gcroots/foo -> $outPath" ]

nix-store --gc --print-roots | grep $outPath
nix-store --gc --print-live | grep $outPath
if nix-store --gc --print-dead | grep -E $outPath$; then false; fi

nix-store --gc --print-dead

expect 1 nix-store --delete $outPath
test -e "$storeBRoot/$outPath"

shopt -s nullglob
for i in $storeBRoot/*; do
if [[ $i =~ /trash ]]; then continue; fi # compat with old daemon
touch $i.lock
touch $i.chroot
done

nix-collect-garbage

# Check that the root and its dependencies haven't been deleted.
cat "$storeBRoot/$outPath"

rm "$stateB"/gcroots/foo

nix-collect-garbage

# Check that the output has been GC'd.
test ! -e $outPath

# Check that the store is empty.
[ "$(ls -1 "$storeBTop" | wc -l)" = "0" ]
5 changes: 5 additions & 0 deletions tests/overlay-local-store/gc.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
source common.sh

requireEnvironment
setupConfig
execUnshare ./gc-inner.sh
1 change: 1 addition & 0 deletions tests/overlay-local-store/local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ overlay-local-store-tests := \
$(d)/build.sh \
$(d)/bad-uris.sh \
$(d)/add-lower.sh \
$(d)/gc.sh \
$(d)/verify.sh \
$(d)/optimise.sh

Expand Down

0 comments on commit a1a7f31

Please sign in to comment.