Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use the fs accessor for readInvalidDerivation #4366

Merged
merged 1 commit into from
Dec 23, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Use the fs accessor for readInvalidDerivation
Extend `FSAccessor::readFile` to allow not checking that the path is a
valid one, and rewrite `readInvalidDerivation` using this extended
`readFile`.

Several places in the code use `readInvalidDerivation`, either because
they need to read a derivation that has been written in the store but
not registered yet, or more generally to prevent a deadlock because
`readDerivation` tries to lock the state, so can't be called from a
place where the lock is already held.
However, `readInvalidDerivation` implicitely assumes that the store is a
`LocalFSStore`, which isn't always the case.

The concrete motivation for this is that it's required for `nix copy
--from someBinaryCache` to work, which is tremendously useful for the
tests.
thufschmitt committed Dec 15, 2020
commit 7080321618e29033a8b5dc2f9fc938dcf2df270d
9 changes: 8 additions & 1 deletion src/libstore/fs-accessor.hh
Original file line number Diff line number Diff line change
@@ -25,7 +25,14 @@ public:

virtual StringSet readDirectory(const Path & path) = 0;

virtual std::string readFile(const Path & path) = 0;
/**
* Read a file inside the store.
*
* If `requireValidPath` is set to `true` (the default), the path must be
* inside a valid store path, otherwise it just needs to be physically
* present (but not necessarily properly registered)
*/
virtual std::string readFile(const Path & path, bool requireValidPath = true) = 0;

virtual std::string readLink(const Path & path) = 0;
};
8 changes: 4 additions & 4 deletions src/libstore/local-fs-store.cc
Original file line number Diff line number Diff line change
@@ -19,10 +19,10 @@ struct LocalStoreAccessor : public FSAccessor

LocalStoreAccessor(ref<LocalFSStore> store) : store(store) { }

Path toRealPath(const Path & path)
Path toRealPath(const Path & path, bool requireValidPath = true)
{
auto storePath = store->toStorePath(path).first;
if (!store->isValidPath(storePath))
if (requireValidPath && !store->isValidPath(storePath))
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));
return store->getRealStoreDir() + std::string(path, store->storeDir.size());
}
@@ -61,9 +61,9 @@ struct LocalStoreAccessor : public FSAccessor
return res;
}

std::string readFile(const Path & path) override
std::string readFile(const Path & path, bool requireValidPath = true) override
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the overrides shouldn't have the default args? Elsewhere in the codebase they at least are only part of the declarations, IIRC.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good question. Triggered me to read https://stackoverflow.com/questions/6464404/virtual-function-default-arguments-behaviour which is… interesting.

The gist of it seems to that if we only call accessor.readFile(path) where accessor is declared of type FSAccessor (which is probably the case) then we don't need the default arguments for the overrides. But that looks quite fragile. Maybe I could just split the interface as:

virtual bool isValidPath(const Path& path);
virtual std::string readFileUnchecked(const Path& path);
std::string readFile(const Path& path) {
  if (!isValidPath(path)) throw InvalidPath(...);
  return readFileUnchecked(path);
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I read it the other way around, where up casting certainly gives you the outer default arg, but no upcasting would also give you the outer default arg if there is no inner default arg.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nah, just checked:

struct Foo {
  virtual void foo(int n = 1) { }
};

struct Bar : Foo {
  void foo(int n) override { }
};

int main() {
  Foo x;
  x.foo();
  Bar y;
  y.foo();
}

x.foo() typechecks fine, but y.foo() doesn't

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry to have misled you with that wrong interpretation then.

{
return nix::readFile(toRealPath(path));
return nix::readFile(toRealPath(path, requireValidPath));
}

std::string readLink(const Path & path) override
2 changes: 1 addition & 1 deletion src/libstore/nar-accessor.cc
Original file line number Diff line number Diff line change
@@ -203,7 +203,7 @@ struct NarAccessor : public FSAccessor
return res;
}

std::string readFile(const Path & path) override
std::string readFile(const Path & path, bool requireValidPath = true) override
{
auto i = get(path);
if (i.type != FSAccessor::Type::tRegular)
8 changes: 4 additions & 4 deletions src/libstore/remote-fs-accessor.cc
Original file line number Diff line number Diff line change
@@ -43,13 +43,13 @@ void RemoteFSAccessor::addToCache(std::string_view hashPart, const std::string &
}
}

std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_)
std::pair<ref<FSAccessor>, Path> RemoteFSAccessor::fetch(const Path & path_, bool requireValidPath)
{
auto path = canonPath(path_);

auto [storePath, restPath] = store->toStorePath(path);

if (!store->isValidPath(storePath))
if (requireValidPath && !store->isValidPath(storePath))
throw InvalidPath("path '%1%' is not a valid store path", store->printStorePath(storePath));

auto i = nars.find(std::string(storePath.hashPart()));
@@ -113,9 +113,9 @@ StringSet RemoteFSAccessor::readDirectory(const Path & path)
return res.first->readDirectory(res.second);
}

std::string RemoteFSAccessor::readFile(const Path & path)
std::string RemoteFSAccessor::readFile(const Path & path, bool requireValidPath)
{
auto res = fetch(path);
auto res = fetch(path, requireValidPath);
return res.first->readFile(res.second);
}

4 changes: 2 additions & 2 deletions src/libstore/remote-fs-accessor.hh
Original file line number Diff line number Diff line change
@@ -14,7 +14,7 @@ class RemoteFSAccessor : public FSAccessor

Path cacheDir;

std::pair<ref<FSAccessor>, Path> fetch(const Path & path_);
std::pair<ref<FSAccessor>, Path> fetch(const Path & path_, bool requireValidPath = true);

friend class BinaryCacheStore;

@@ -32,7 +32,7 @@ public:

StringSet readDirectory(const Path & path) override;

std::string readFile(const Path & path) override;
std::string readFile(const Path & path, bool requireValidPath = true) override;

std::string readLink(const Path & path) override;
};
21 changes: 9 additions & 12 deletions src/libstore/store-api.cc
Original file line number Diff line number Diff line change
@@ -1018,26 +1018,23 @@ Derivation Store::derivationFromPath(const StorePath & drvPath)
return readDerivation(drvPath);
}


Derivation Store::readDerivation(const StorePath & drvPath)
Derivation readDerivationCommon(Store& store, const StorePath& drvPath, bool requireValidPath)
{
auto accessor = getFSAccessor();
auto accessor = store.getFSAccessor();
try {
return parseDerivation(*this,
accessor->readFile(printStorePath(drvPath)),
return parseDerivation(store,
accessor->readFile(store.printStorePath(drvPath), requireValidPath),
Derivation::nameFromPath(drvPath));
} catch (FormatError & e) {
throw Error("error parsing derivation '%s': %s", printStorePath(drvPath), e.msg());
throw Error("error parsing derivation '%s': %s", store.printStorePath(drvPath), e.msg());
}
}

Derivation Store::readDerivation(const StorePath & drvPath)
{ return readDerivationCommon(*this, drvPath, true); }

Derivation Store::readInvalidDerivation(const StorePath & drvPath)
{
return parseDerivation(
*this,
readFile(Store::toRealPath(drvPath)),
Derivation::nameFromPath(drvPath));
}
{ return readDerivationCommon(*this, drvPath, false); }

}