mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2025-01-19 17:46:46 +02:00
PosixSourceAccessor: Cache lstat() calls
Since we're doing a lot of them in assertNoSymlinks().
This commit is contained in:
parent
504e4fc457
commit
57246c4c38
2 changed files with 42 additions and 18 deletions
|
@ -1,5 +1,8 @@
|
||||||
#include "posix-source-accessor.hh"
|
#include "posix-source-accessor.hh"
|
||||||
#include "signals.hh"
|
#include "signals.hh"
|
||||||
|
#include "sync.hh"
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -46,23 +49,45 @@ bool PosixSourceAccessor::pathExists(const CanonPath & path)
|
||||||
return nix::pathExists(path.abs());
|
return nix::pathExists(path.abs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<struct stat> PosixSourceAccessor::cachedLstat(const CanonPath & path)
|
||||||
|
{
|
||||||
|
static Sync<std::unordered_map<CanonPath, std::optional<struct stat>>> _cache;
|
||||||
|
|
||||||
|
{
|
||||||
|
auto cache(_cache.lock());
|
||||||
|
auto i = cache->find(path);
|
||||||
|
if (i != cache->end()) return i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<struct stat> st{std::in_place};
|
||||||
|
if (::lstat(path.c_str(), &*st)) {
|
||||||
|
if (errno == ENOENT || errno == ENOTDIR)
|
||||||
|
st.reset();
|
||||||
|
else
|
||||||
|
throw SysError("getting status of '%s'", showPath(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cache(_cache.lock());
|
||||||
|
if (cache->size() >= 16384) cache->clear();
|
||||||
|
cache->emplace(path, st);
|
||||||
|
|
||||||
|
return st;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SourceAccessor::Stat> PosixSourceAccessor::maybeLstat(const CanonPath & path)
|
std::optional<SourceAccessor::Stat> PosixSourceAccessor::maybeLstat(const CanonPath & path)
|
||||||
{
|
{
|
||||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||||
struct stat st;
|
auto st = cachedLstat(path);
|
||||||
if (::lstat(path.c_str(), &st)) {
|
if (!st) return std::nullopt;
|
||||||
if (errno == ENOENT || errno == ENOTDIR) return std::nullopt;
|
mtime = std::max(mtime, st->st_mtime);
|
||||||
throw SysError("getting status of '%s'", showPath(path));
|
|
||||||
}
|
|
||||||
mtime = std::max(mtime, st.st_mtime);
|
|
||||||
return Stat {
|
return Stat {
|
||||||
.type =
|
.type =
|
||||||
S_ISREG(st.st_mode) ? tRegular :
|
S_ISREG(st->st_mode) ? tRegular :
|
||||||
S_ISDIR(st.st_mode) ? tDirectory :
|
S_ISDIR(st->st_mode) ? tDirectory :
|
||||||
S_ISLNK(st.st_mode) ? tSymlink :
|
S_ISLNK(st->st_mode) ? tSymlink :
|
||||||
tMisc,
|
tMisc,
|
||||||
.fileSize = S_ISREG(st.st_mode) ? std::optional<uint64_t>(st.st_size) : std::nullopt,
|
.fileSize = S_ISREG(st->st_mode) ? std::optional<uint64_t>(st->st_size) : std::nullopt,
|
||||||
.isExecutable = S_ISREG(st.st_mode) && st.st_mode & S_IXUSR,
|
.isExecutable = S_ISREG(st->st_mode) && st->st_mode & S_IXUSR,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -95,14 +120,9 @@ std::optional<CanonPath> PosixSourceAccessor::getPhysicalPath(const CanonPath &
|
||||||
|
|
||||||
void PosixSourceAccessor::assertNoSymlinks(CanonPath path)
|
void PosixSourceAccessor::assertNoSymlinks(CanonPath path)
|
||||||
{
|
{
|
||||||
// FIXME: cache this since it potentially causes a lot of lstat calls.
|
|
||||||
while (!path.isRoot()) {
|
while (!path.isRoot()) {
|
||||||
struct stat st;
|
auto st = cachedLstat(path);
|
||||||
if (::lstat(path.c_str(), &st)) {
|
if (st && S_ISLNK(st->st_mode))
|
||||||
if (errno != ENOENT)
|
|
||||||
throw SysError("getting status of '%s'", showPath(path));
|
|
||||||
}
|
|
||||||
if (S_ISLNK(st.st_mode))
|
|
||||||
throw Error("path '%s' is a symlink", showPath(path));
|
throw Error("path '%s' is a symlink", showPath(path));
|
||||||
path.pop();
|
path.pop();
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,14 @@ struct PosixSourceAccessor : virtual SourceAccessor
|
||||||
|
|
||||||
std::optional<CanonPath> getPhysicalPath(const CanonPath & path) override;
|
std::optional<CanonPath> getPhysicalPath(const CanonPath & path) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Throw an error if `path` or any of its ancestors are symlinks.
|
* Throw an error if `path` or any of its ancestors are symlinks.
|
||||||
*/
|
*/
|
||||||
void assertNoSymlinks(CanonPath path);
|
void assertNoSymlinks(CanonPath path);
|
||||||
|
|
||||||
|
std::optional<struct stat> cachedLstat(const CanonPath & path);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue