mirror of
https://github.com/NixOS/nix
synced 2025-07-07 01:51:47 +02:00
Merge pull request #9497 from edolstra/move-access-control
Move restricted/pure-eval access control out of the evaluator and into the accessor
This commit is contained in:
commit
d4f6b1d38b
23 changed files with 412 additions and 305 deletions
|
@ -205,8 +205,19 @@ public:
|
|||
* `CanonPath(this.makeRelative(x), this) == path`.
|
||||
*/
|
||||
std::string makeRelative(const CanonPath & path) const;
|
||||
|
||||
friend class std::hash<CanonPath>;
|
||||
};
|
||||
|
||||
std::ostream & operator << (std::ostream & stream, const CanonPath & path);
|
||||
|
||||
}
|
||||
|
||||
template<>
|
||||
struct std::hash<nix::CanonPath>
|
||||
{
|
||||
std::size_t operator ()(const nix::CanonPath & s) const noexcept
|
||||
{
|
||||
return std::hash<std::string>{}(s.path);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
#include "posix-source-accessor.hh"
|
||||
#include "signals.hh"
|
||||
#include "sync.hh"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
namespace nix {
|
||||
|
||||
|
@ -8,9 +11,9 @@ void PosixSourceAccessor::readFile(
|
|||
Sink & sink,
|
||||
std::function<void(uint64_t)> sizeCallback)
|
||||
{
|
||||
// FIXME: add O_NOFOLLOW since symlinks should be resolved by the
|
||||
// caller?
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
assertNoSymlinks(path);
|
||||
|
||||
AutoCloseFD fd = open(path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW);
|
||||
if (!fd)
|
||||
throw SysError("opening file '%1%'", path);
|
||||
|
||||
|
@ -42,30 +45,55 @@ void PosixSourceAccessor::readFile(
|
|||
|
||||
bool PosixSourceAccessor::pathExists(const CanonPath & path)
|
||||
{
|
||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||
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)
|
||||
{
|
||||
struct stat st;
|
||||
if (::lstat(path.c_str(), &st)) {
|
||||
if (errno == ENOENT) return std::nullopt;
|
||||
throw SysError("getting status of '%s'", showPath(path));
|
||||
}
|
||||
mtime = std::max(mtime, st.st_mtime);
|
||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||
auto st = cachedLstat(path);
|
||||
if (!st) return std::nullopt;
|
||||
mtime = std::max(mtime, st->st_mtime);
|
||||
return Stat {
|
||||
.type =
|
||||
S_ISREG(st.st_mode) ? tRegular :
|
||||
S_ISDIR(st.st_mode) ? tDirectory :
|
||||
S_ISLNK(st.st_mode) ? tSymlink :
|
||||
S_ISREG(st->st_mode) ? tRegular :
|
||||
S_ISDIR(st->st_mode) ? tDirectory :
|
||||
S_ISLNK(st->st_mode) ? tSymlink :
|
||||
tMisc,
|
||||
.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,
|
||||
.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,
|
||||
};
|
||||
}
|
||||
|
||||
SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath & path)
|
||||
{
|
||||
assertNoSymlinks(path);
|
||||
DirEntries res;
|
||||
for (auto & entry : nix::readDirectory(path.abs())) {
|
||||
std::optional<Type> type;
|
||||
|
@ -81,6 +109,7 @@ SourceAccessor::DirEntries PosixSourceAccessor::readDirectory(const CanonPath &
|
|||
|
||||
std::string PosixSourceAccessor::readLink(const CanonPath & path)
|
||||
{
|
||||
if (auto parent = path.parent()) assertNoSymlinks(*parent);
|
||||
return nix::readLink(path.abs());
|
||||
}
|
||||
|
||||
|
@ -89,4 +118,14 @@ std::optional<CanonPath> PosixSourceAccessor::getPhysicalPath(const CanonPath &
|
|||
return path;
|
||||
}
|
||||
|
||||
void PosixSourceAccessor::assertNoSymlinks(CanonPath path)
|
||||
{
|
||||
while (!path.isRoot()) {
|
||||
auto st = cachedLstat(path);
|
||||
if (st && S_ISLNK(st->st_mode))
|
||||
throw Error("path '%s' is a symlink", showPath(path));
|
||||
path.pop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -29,6 +29,15 @@ struct PosixSourceAccessor : virtual SourceAccessor
|
|||
std::string readLink(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.
|
||||
*/
|
||||
void assertNoSymlinks(CanonPath path);
|
||||
|
||||
std::optional<struct stat> cachedLstat(const CanonPath & path);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue