mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-13 09:46:16 +02:00
Add CanonPath wrapper to represent canonicalized paths
This commit is contained in:
parent
de35e2d3b4
commit
a71f209330
31 changed files with 503 additions and 187 deletions
|
@ -94,8 +94,8 @@ SourcePath lookupFileArg(EvalState & state, std::string_view s)
|
||||||
if (isUri(s)) {
|
if (isUri(s)) {
|
||||||
auto storePath = fetchers::downloadTarball(
|
auto storePath = fetchers::downloadTarball(
|
||||||
state.store, resolveUri(s), "source", false).first.storePath;
|
state.store, resolveUri(s), "source", false).first.storePath;
|
||||||
auto & accessor = state.registerAccessor(makeFSInputAccessor(state.store->toRealPath(storePath)));
|
auto & accessor = state.registerAccessor(makeFSInputAccessor(CanonPath(state.store->toRealPath(storePath))));
|
||||||
return {accessor, "/"};
|
return {accessor, CanonPath::root};
|
||||||
} else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
|
} else if (s.size() > 2 && s.at(0) == '<' && s.at(s.size() - 1) == '>') {
|
||||||
Path p(s.substr(1, s.size() - 2));
|
Path p(s.substr(1, s.size() - 2));
|
||||||
return state.findFile(p);
|
return state.findFile(p);
|
||||||
|
|
|
@ -425,7 +425,7 @@ Value & AttrCursor::forceValue()
|
||||||
else if (v.type() == nPath) {
|
else if (v.type() == nPath) {
|
||||||
// FIXME: take accessor into account?
|
// FIXME: take accessor into account?
|
||||||
auto path = v.path().path;
|
auto path = v.path().path;
|
||||||
cachedValue = {root->db->setString(getKey(), path), string_t{path, {}}};
|
cachedValue = {root->db->setString(getKey(), path.abs()), string_t{path.abs(), {}}};
|
||||||
}
|
}
|
||||||
else if (v.type() == nBool)
|
else if (v.type() == nBool)
|
||||||
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
|
cachedValue = {root->db->setBool(getKey(), v.boolean), v.boolean};
|
||||||
|
|
|
@ -461,9 +461,9 @@ EvalState::EvalState(
|
||||||
, sPrefix(symbols.create("prefix"))
|
, sPrefix(symbols.create("prefix"))
|
||||||
, repair(NoRepair)
|
, repair(NoRepair)
|
||||||
, emptyBindings(0)
|
, emptyBindings(0)
|
||||||
, rootFS(makeFSInputAccessor("",
|
, rootFS(makeFSInputAccessor(CanonPath::root,
|
||||||
evalSettings.restrictEval || evalSettings.pureEval
|
evalSettings.restrictEval || evalSettings.pureEval
|
||||||
? std::optional<PathSet>(PathSet())
|
? std::optional<std::set<CanonPath>>(std::set<CanonPath>())
|
||||||
: std::nullopt))
|
: std::nullopt))
|
||||||
, corepkgsFS(makeMemoryInputAccessor())
|
, corepkgsFS(makeMemoryInputAccessor())
|
||||||
, store(store)
|
, store(store)
|
||||||
|
@ -515,7 +515,7 @@ EvalState::EvalState(
|
||||||
createBaseEnv();
|
createBaseEnv();
|
||||||
|
|
||||||
corepkgsFS->addFile(
|
corepkgsFS->addFile(
|
||||||
"/fetchurl.nix",
|
CanonPath("fetchurl.nix"),
|
||||||
#include "fetchurl.nix.gen.hh"
|
#include "fetchurl.nix.gen.hh"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -528,15 +528,15 @@ EvalState::~EvalState()
|
||||||
|
|
||||||
void EvalState::allowPath(const Path & path)
|
void EvalState::allowPath(const Path & path)
|
||||||
{
|
{
|
||||||
rootFS->allowPath(path);
|
rootFS->allowPath(CanonPath(path)); // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::allowPath(const StorePath & storePath)
|
void EvalState::allowPath(const StorePath & storePath)
|
||||||
{
|
{
|
||||||
rootFS->allowPath(store->toRealPath(storePath));
|
rootFS->allowPath(CanonPath(store->toRealPath(storePath))); // FIXME
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::allowAndSetStorePathString(const StorePath &storePath, Value & v)
|
void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value & v)
|
||||||
{
|
{
|
||||||
allowPath(storePath);
|
allowPath(storePath);
|
||||||
|
|
||||||
|
@ -612,12 +612,12 @@ void EvalState::checkURI(const std::string & uri)
|
||||||
/* If the URI is a path, then check it against allowedPaths as
|
/* If the URI is a path, then check it against allowedPaths as
|
||||||
well. */
|
well. */
|
||||||
if (hasPrefix(uri, "/")) {
|
if (hasPrefix(uri, "/")) {
|
||||||
rootFS->checkAllowed(uri);
|
rootFS->checkAllowed(CanonPath(uri));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPrefix(uri, "file://")) {
|
if (hasPrefix(uri, "file://")) {
|
||||||
rootFS->checkAllowed(uri.substr(7));
|
rootFS->checkAllowed(CanonPath(uri.substr(7)));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,7 +758,7 @@ void EvalState::throwEvalError(const PosIdx pos, const Suggestions & suggestions
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvalState::throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const
|
void EvalState::throwEvalError(const PosIdx pos, const char * s, std::string_view s2) const
|
||||||
{
|
{
|
||||||
throw EvalError(ErrorInfo {
|
throw EvalError(ErrorInfo {
|
||||||
.msg = hintfmt(s, s2),
|
.msg = hintfmt(s, s2),
|
||||||
|
@ -890,7 +890,7 @@ void Value::mkStringMove(const char * s, const PathSet & context)
|
||||||
|
|
||||||
void Value::mkPath(const SourcePath & path)
|
void Value::mkPath(const SourcePath & path)
|
||||||
{
|
{
|
||||||
mkPath(&path.accessor, makeImmutableString(path.path));
|
mkPath(&path.accessor, makeImmutableString(path.path.abs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1827,7 +1827,7 @@ void ExprConcatStrings::eval(EvalState & state, Env & env, Value & v)
|
||||||
else if (firstType == nPath) {
|
else if (firstType == nPath) {
|
||||||
if (!context.empty())
|
if (!context.empty())
|
||||||
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
state.throwEvalError(pos, "a string that refers to a store path cannot be appended to a path");
|
||||||
v.mkPath({.accessor = *accessor, .path = canonPath(str())});
|
v.mkPath({.accessor = *accessor, .path = CanonPath(str())});
|
||||||
} else
|
} else
|
||||||
v.mkStringMove(c_str(), context);
|
v.mkStringMove(c_str(), context);
|
||||||
}
|
}
|
||||||
|
@ -2027,7 +2027,7 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
||||||
auto path = v.path();
|
auto path = v.path();
|
||||||
return copyToStore
|
return copyToStore
|
||||||
? store->printStorePath(copyPathToStore(context, path))
|
? store->printStorePath(copyPathToStore(context, path))
|
||||||
: path.path;
|
: BackedStringView((Path) path.path.abs());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.type() == nAttrs) {
|
if (v.type() == nAttrs) {
|
||||||
|
@ -2071,7 +2071,7 @@ BackedStringView EvalState::coerceToString(const PosIdx pos, Value & v, PathSet
|
||||||
|
|
||||||
StorePath EvalState::copyPathToStore(PathSet & context, const SourcePath & path)
|
StorePath EvalState::copyPathToStore(PathSet & context, const SourcePath & path)
|
||||||
{
|
{
|
||||||
if (nix::isDerivation(path.path))
|
if (nix::isDerivation(path.path.abs()))
|
||||||
throw EvalError("file names are not allowed to end in '%s'", drvExtension);
|
throw EvalError("file names are not allowed to end in '%s'", drvExtension);
|
||||||
|
|
||||||
auto i = srcToStore.find(path);
|
auto i = srcToStore.find(path);
|
||||||
|
@ -2103,7 +2103,10 @@ SourcePath EvalState::coerceToPath(const PosIdx pos, Value & v, PathSet & contex
|
||||||
|
|
||||||
if (v.type() == nString) {
|
if (v.type() == nString) {
|
||||||
copyContext(v, context);
|
copyContext(v, context);
|
||||||
return {*rootFS, v.string.s};
|
auto path = v.str();
|
||||||
|
if (path == "" || path[0] != '/')
|
||||||
|
throwEvalError(pos, "string '%1%' doesn't represent an absolute path", path);
|
||||||
|
return {*rootFS, CanonPath(path)};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v.type() == nPath)
|
if (v.type() == nPath)
|
||||||
|
|
|
@ -160,7 +160,7 @@ public:
|
||||||
|
|
||||||
SearchPath getSearchPath() { return searchPath; }
|
SearchPath getSearchPath() { return searchPath; }
|
||||||
|
|
||||||
SourcePath rootPath(Path path);
|
SourcePath rootPath(const Path & path);
|
||||||
|
|
||||||
InputAccessor & registerAccessor(ref<InputAccessor> accessor);
|
InputAccessor & registerAccessor(ref<InputAccessor> accessor);
|
||||||
|
|
||||||
|
@ -256,7 +256,7 @@ public:
|
||||||
void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
void throwEvalError(const PosIdx pos, const Suggestions & suggestions, const char * s,
|
||||||
const std::string & s2) const;
|
const std::string & s2) const;
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
void throwEvalError(const PosIdx pos, const char * s, const std::string & s2) const;
|
void throwEvalError(const PosIdx pos, const char * s, std::string_view s2) const;
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const;
|
void throwEvalError(const char * s, const std::string & s2, const std::string & s3) const;
|
||||||
[[gnu::noinline, gnu::noreturn]]
|
[[gnu::noinline, gnu::noreturn]]
|
||||||
|
|
|
@ -206,8 +206,8 @@ static Flake readFlake(
|
||||||
InputAccessor & accessor,
|
InputAccessor & accessor,
|
||||||
const InputPath & lockRootPath)
|
const InputPath & lockRootPath)
|
||||||
{
|
{
|
||||||
auto flakeDir = canonPath("/" + resolvedRef.subdir);
|
CanonPath flakeDir(resolvedRef.subdir);
|
||||||
SourcePath flakePath{accessor, canonPath(flakeDir + "/flake.nix")};
|
SourcePath flakePath{accessor, flakeDir + CanonPath("flake.nix")};
|
||||||
|
|
||||||
if (!flakePath.pathExists())
|
if (!flakePath.pathExists())
|
||||||
throw Error("source tree referenced by '%s' does not contain a file named '%s'", resolvedRef, flakePath.path);
|
throw Error("source tree referenced by '%s' does not contain a file named '%s'", resolvedRef, flakePath.path);
|
||||||
|
@ -232,7 +232,7 @@ static Flake readFlake(
|
||||||
auto sInputs = state.symbols.create("inputs");
|
auto sInputs = state.symbols.create("inputs");
|
||||||
|
|
||||||
if (auto inputs = vInfo.attrs->get(sInputs))
|
if (auto inputs = vInfo.attrs->get(sInputs))
|
||||||
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir, lockRootPath);
|
flake.inputs = parseFlakeInputs(state, inputs->value, inputs->pos, flakeDir.abs(), lockRootPath);
|
||||||
|
|
||||||
auto sOutputs = state.symbols.create("outputs");
|
auto sOutputs = state.symbols.create("outputs");
|
||||||
|
|
||||||
|
@ -337,7 +337,7 @@ Flake getFlake(EvalState & state, const FlakeRef & originalRef, bool allowLookup
|
||||||
|
|
||||||
static LockFile readLockFile(const Flake & flake)
|
static LockFile readLockFile(const Flake & flake)
|
||||||
{
|
{
|
||||||
auto lockFilePath = flake.path.parent().append("/flake.lock");
|
auto lockFilePath = flake.path.parent() + "flake.lock";
|
||||||
return lockFilePath.pathExists()
|
return lockFilePath.pathExists()
|
||||||
? LockFile(lockFilePath.readFile(), fmt("%s", lockFilePath))
|
? LockFile(lockFilePath.readFile(), fmt("%s", lockFilePath))
|
||||||
: LockFile();
|
: LockFile();
|
||||||
|
@ -721,7 +721,7 @@ void callFlake(EvalState & state,
|
||||||
|
|
||||||
emitTreeAttrs(
|
emitTreeAttrs(
|
||||||
state,
|
state,
|
||||||
{lockedFlake.flake.path.accessor, "/"},
|
{lockedFlake.flake.path.accessor, CanonPath::root},
|
||||||
lockedFlake.flake.lockedRef.input,
|
lockedFlake.flake.lockedRef.input,
|
||||||
*vRootSrc,
|
*vRootSrc,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -188,7 +188,7 @@ struct ExprPath : Expr
|
||||||
ExprPath(SourcePath && _path)
|
ExprPath(SourcePath && _path)
|
||||||
: path(_path)
|
: path(_path)
|
||||||
{
|
{
|
||||||
v.mkPath(&path.accessor, path.path.c_str());
|
v.mkPath(&path.accessor, path.path.abs().data());
|
||||||
}
|
}
|
||||||
COMMON_METHODS
|
COMMON_METHODS
|
||||||
Value * maybeThunk(EvalState & state, Env & env);
|
Value * maybeThunk(EvalState & state, Env & env);
|
||||||
|
|
|
@ -508,10 +508,12 @@ string_parts_interpolated
|
||||||
|
|
||||||
path_start
|
path_start
|
||||||
: PATH {
|
: PATH {
|
||||||
SourcePath path { data->basePath.accessor, absPath({$1.p, $1.l}, data->basePath.path) };
|
SourcePath path { data->basePath.accessor, CanonPath({$1.p, $1.l}, data->basePath.path) };
|
||||||
|
#if 0
|
||||||
/* add back in the trailing '/' to the first segment */
|
/* add back in the trailing '/' to the first segment */
|
||||||
if ($1.p[$1.l-1] == '/' && $1.l > 1)
|
if ($1.p[$1.l-1] == '/' && $1.l > 1)
|
||||||
path.path += "/";
|
path.path += "/";
|
||||||
|
#endif
|
||||||
$$ = new ExprPath(std::move(path));
|
$$ = new ExprPath(std::move(path));
|
||||||
}
|
}
|
||||||
| HPATH {
|
| HPATH {
|
||||||
|
@ -699,7 +701,7 @@ SourcePath resolveExprPath(const SourcePath & path)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// FIXME
|
// FIXME
|
||||||
auto path2 = path.append("/default.nix");
|
auto path2 = path + "default.nix";
|
||||||
return path2.pathExists() ? path2 : path;
|
return path2.pathExists() ? path2 : path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -716,7 +718,7 @@ Expr * EvalState::parseExprFromFile(const SourcePath & path, StaticEnv & staticE
|
||||||
// readFile hopefully have left some extra space for terminators
|
// readFile hopefully have left some extra space for terminators
|
||||||
buffer.append("\0\0", 2);
|
buffer.append("\0\0", 2);
|
||||||
// FIXME: pass SourcePaths
|
// FIXME: pass SourcePaths
|
||||||
return parse(buffer.data(), buffer.size(), foFile, path.path, path.parent(), staticEnv);
|
return parse(buffer.data(), buffer.size(), foFile, path.path.abs(), path.parent(), staticEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -779,13 +781,13 @@ SourcePath EvalState::findFile(SearchPath & searchPath, const std::string_view p
|
||||||
suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
|
suffix = path.size() == s ? "" : concatStrings("/", path.substr(s));
|
||||||
}
|
}
|
||||||
if (auto path = resolveSearchPathElem(i)) {
|
if (auto path = resolveSearchPathElem(i)) {
|
||||||
auto res = path->append("/" + suffix);
|
auto res = *path + CanonPath(suffix);
|
||||||
if (res.pathExists()) return res;
|
if (res.pathExists()) return res;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasPrefix(path, "nix/"))
|
if (hasPrefix(path, "nix/"))
|
||||||
return {*corepkgsFS, (std::string) path.substr(3)};
|
return {*corepkgsFS, CanonPath(path.substr(3))};
|
||||||
|
|
||||||
throw ThrownError({
|
throw ThrownError({
|
||||||
.msg = hintfmt(evalSettings.pureEval
|
.msg = hintfmt(evalSettings.pureEval
|
||||||
|
@ -808,8 +810,8 @@ std::optional<SourcePath> EvalState::resolveSearchPathElem(const SearchPathElem
|
||||||
try {
|
try {
|
||||||
auto storePath = fetchers::downloadTarball(
|
auto storePath = fetchers::downloadTarball(
|
||||||
store, resolveUri(elem.second), "source", false).first.storePath;
|
store, resolveUri(elem.second), "source", false).first.storePath;
|
||||||
auto & accessor = registerAccessor(makeFSInputAccessor(store->toRealPath(storePath)));
|
auto & accessor = registerAccessor(makeFSInputAccessor(CanonPath(store->toRealPath(storePath))));
|
||||||
res.emplace(SourcePath {accessor, "/"});
|
res.emplace(SourcePath {accessor, CanonPath::root});
|
||||||
} catch (FileTransferError & e) {
|
} catch (FileTransferError & e) {
|
||||||
logWarning({
|
logWarning({
|
||||||
.msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
|
.msg = hintfmt("Nix search path entry '%1%' cannot be downloaded, ignoring", elem.second)
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
SourcePath EvalState::rootPath(Path path)
|
SourcePath EvalState::rootPath(const Path & path)
|
||||||
{
|
{
|
||||||
return {*rootFS, std::move(path)};
|
return {*rootFS, CanonPath(path)};
|
||||||
}
|
}
|
||||||
|
|
||||||
InputAccessor & EvalState::registerAccessor(ref<InputAccessor> accessor)
|
InputAccessor & EvalState::registerAccessor(ref<InputAccessor> accessor)
|
||||||
|
|
|
@ -1335,12 +1335,13 @@ static RegisterPrimOp primop_placeholder({
|
||||||
*************************************************************/
|
*************************************************************/
|
||||||
|
|
||||||
|
|
||||||
/* Convert the argument to a path. !!! obsolete? */
|
/* Convert the argument to a path and then to a string (confusing,
|
||||||
|
eh?). !!! obsolete? */
|
||||||
static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
static void prim_toPath(EvalState & state, const PosIdx pos, Value * * args, Value & v)
|
||||||
{
|
{
|
||||||
PathSet context;
|
PathSet context;
|
||||||
auto path = state.coerceToPath(pos, *args[0], context);
|
auto path = state.coerceToPath(pos, *args[0], context);
|
||||||
v.mkString(canonPath(path.path), context);
|
v.mkString(path.path.abs(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_toPath({
|
static RegisterPrimOp primop_toPath({
|
||||||
|
@ -1375,17 +1376,17 @@ static void prim_storePath(EvalState & state, const PosIdx pos, Value * * args,
|
||||||
/* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink
|
/* Resolve symlinks in ‘path’, unless ‘path’ itself is a symlink
|
||||||
directly in the store. The latter condition is necessary so
|
directly in the store. The latter condition is necessary so
|
||||||
e.g. nix-push does the right thing. */
|
e.g. nix-push does the right thing. */
|
||||||
if (!state.store->isStorePath(path)) path = canonPath(path, true);
|
if (!state.store->isStorePath(path.abs())) path = path.resolveSymlinks();
|
||||||
if (!state.store->isInStore(path))
|
if (!state.store->isInStore(path.abs()))
|
||||||
throw EvalError({
|
throw EvalError({
|
||||||
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
.msg = hintfmt("path '%1%' is not in the Nix store", path),
|
||||||
.errPos = state.positions[pos]
|
.errPos = state.positions[pos]
|
||||||
});
|
});
|
||||||
auto path2 = state.store->toStorePath(path).first;
|
auto path2 = state.store->toStorePath(path.abs()).first;
|
||||||
if (!settings.readOnlyMode)
|
if (!settings.readOnlyMode)
|
||||||
state.store->ensurePath(path2);
|
state.store->ensurePath(path2);
|
||||||
context.insert(state.store->printStorePath(path2));
|
context.insert(state.store->printStorePath(path2));
|
||||||
v.mkString(path, context);
|
v.mkString(path.abs(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
static RegisterPrimOp primop_storePath({
|
static RegisterPrimOp primop_storePath({
|
||||||
|
@ -1492,8 +1493,8 @@ static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, V
|
||||||
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
throw Error("the contents of the file '%1%' cannot be represented as a Nix string", path);
|
||||||
// FIXME: only do queryPathInfo if path.accessor is the store accessor
|
// FIXME: only do queryPathInfo if path.accessor is the store accessor
|
||||||
auto refs =
|
auto refs =
|
||||||
state.store->isInStore(path.path) ?
|
state.store->isInStore(path.path.abs()) ?
|
||||||
state.store->queryPathInfo(state.store->toStorePath(path.path).first)->references :
|
state.store->queryPathInfo(state.store->toStorePath(path.path.abs()).first)->references :
|
||||||
StorePathSet{};
|
StorePathSet{};
|
||||||
auto context = state.store->printStorePathSet(refs);
|
auto context = state.store->printStorePathSet(refs);
|
||||||
v.mkString(s, context);
|
v.mkString(s, context);
|
||||||
|
@ -1949,14 +1950,14 @@ static void addPath(
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
PathFilter filter = filterFun ? ([&](const Path & p) {
|
PathFilter filter = filterFun ? ([&](const Path & p) {
|
||||||
SourcePath path2{path.accessor, canonPath(p)};
|
SourcePath path2{path.accessor, CanonPath(p)};
|
||||||
|
|
||||||
auto st = path2.lstat();
|
auto st = path2.lstat();
|
||||||
|
|
||||||
/* Call the filter function. The first argument is the path,
|
/* Call the filter function. The first argument is the path,
|
||||||
the second is a string indicating the type of the file. */
|
the second is a string indicating the type of the file. */
|
||||||
Value arg1;
|
Value arg1;
|
||||||
arg1.mkString(path2.path);
|
arg1.mkString(path2.path.abs());
|
||||||
|
|
||||||
Value arg2;
|
Value arg2;
|
||||||
// assert that type is not "unknown"
|
// assert that type is not "unknown"
|
||||||
|
|
|
@ -195,7 +195,7 @@ static void fetchTree(
|
||||||
|
|
||||||
emitTreeAttrs(
|
emitTreeAttrs(
|
||||||
state,
|
state,
|
||||||
{state.registerAccessor(accessor), "/"},
|
{state.registerAccessor(accessor), CanonPath::root},
|
||||||
input2,
|
input2,
|
||||||
v,
|
v,
|
||||||
params.emptyRevFallback,
|
params.emptyRevFallback,
|
||||||
|
|
|
@ -104,7 +104,7 @@ namespace nix {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
auto path = arg.path();
|
auto path = arg.path();
|
||||||
if (path.path != p) {
|
if (path.path != CanonPath(p)) {
|
||||||
*result_listener << "Expected a path that equals \"" << p << "\" but got: " << path.path;
|
*result_listener << "Expected a path that equals \"" << p << "\" but got: " << path.path;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,7 +415,13 @@ public:
|
||||||
SourcePath path() const
|
SourcePath path() const
|
||||||
{
|
{
|
||||||
assert(internalType == tPath);
|
assert(internalType == tPath);
|
||||||
return SourcePath { .accessor = *_path.accessor, .path = _path.path };
|
return SourcePath { .accessor = *_path.accessor, .path = CanonPath(CanonPath::unchecked_t(), _path.path) };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view str() const
|
||||||
|
{
|
||||||
|
assert(internalType == tString);
|
||||||
|
return std::string_view(string.s);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -315,7 +315,7 @@ std::pair<ref<InputAccessor>, Input> InputScheme::lazyFetch(ref<Store> store, co
|
||||||
{
|
{
|
||||||
auto [storePath, input2] = fetch(store, input);
|
auto [storePath, input2] = fetch(store, input);
|
||||||
|
|
||||||
return {makeFSInputAccessor(store->toRealPath(storePath)), input2};
|
return {makeFSInputAccessor(CanonPath(store->toRealPath(storePath))), input2};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -395,17 +395,17 @@ struct GitInputScheme : InputScheme
|
||||||
return repoInfo;
|
return repoInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::set<std::string> listFiles(const RepoInfo & repoInfo)
|
std::set<CanonPath> listFiles(const RepoInfo & repoInfo)
|
||||||
{
|
{
|
||||||
auto gitOpts = Strings({ "-C", repoInfo.url, "ls-files", "-z" });
|
auto gitOpts = Strings({ "-C", repoInfo.url, "ls-files", "-z" });
|
||||||
if (repoInfo.submodules)
|
if (repoInfo.submodules)
|
||||||
gitOpts.emplace_back("--recurse-submodules");
|
gitOpts.emplace_back("--recurse-submodules");
|
||||||
|
|
||||||
std::set<std::string> res;
|
std::set<CanonPath> res;
|
||||||
|
|
||||||
for (auto & p : tokenizeString<std::set<std::string>>(
|
for (auto & p : tokenizeString<std::set<std::string>>(
|
||||||
runProgram("git", true, gitOpts), "\0"s))
|
runProgram("git", true, gitOpts), "\0"s))
|
||||||
res.insert(canonPath("/" + p));
|
res.insert(CanonPath(p));
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -683,6 +683,8 @@ struct GitInputScheme : InputScheme
|
||||||
auto files = listFiles(repoInfo);
|
auto files = listFiles(repoInfo);
|
||||||
|
|
||||||
PathFilter filter = [&](const Path & p) -> bool {
|
PathFilter filter = [&](const Path & p) -> bool {
|
||||||
|
abort();
|
||||||
|
#if 0
|
||||||
assert(hasPrefix(p, repoInfo.url));
|
assert(hasPrefix(p, repoInfo.url));
|
||||||
std::string file(p, repoInfo.url.size() + 1);
|
std::string file(p, repoInfo.url.size() + 1);
|
||||||
|
|
||||||
|
@ -695,6 +697,7 @@ struct GitInputScheme : InputScheme
|
||||||
}
|
}
|
||||||
|
|
||||||
return files.count(file);
|
return files.count(file);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
auto storePath = store->addToStore(input.getName(), repoInfo.url, FileIngestionMethod::Recursive, htSHA256, filter);
|
auto storePath = store->addToStore(input.getName(), repoInfo.url, FileIngestionMethod::Recursive, htSHA256, filter);
|
||||||
|
@ -724,7 +727,7 @@ struct GitInputScheme : InputScheme
|
||||||
|
|
||||||
// FIXME: return updated input.
|
// FIXME: return updated input.
|
||||||
|
|
||||||
return {makeFSInputAccessor(repoInfo.url, listFiles(repoInfo)), input};
|
return {makeFSInputAccessor(CanonPath(repoInfo.url), listFiles(repoInfo)), input};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -233,7 +233,7 @@ struct GitArchiveInputScheme : InputScheme
|
||||||
{
|
{
|
||||||
auto [storePath, input2] = downloadArchive(store, input);
|
auto [storePath, input2] = downloadArchive(store, input);
|
||||||
|
|
||||||
return {makeZipInputAccessor(store->toRealPath(storePath)), input2};
|
return {makeZipInputAccessor(CanonPath(store->toRealPath(storePath))), input2};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,11 +17,11 @@ const std::string narVersionMagic1 = "nix-archive-1";
|
||||||
static std::string caseHackSuffix = "~nix~case~hack~";
|
static std::string caseHackSuffix = "~nix~case~hack~";
|
||||||
|
|
||||||
void InputAccessor::dumpPath(
|
void InputAccessor::dumpPath(
|
||||||
const Path & path,
|
const CanonPath & path,
|
||||||
Sink & sink,
|
Sink & sink,
|
||||||
PathFilter & filter)
|
PathFilter & filter)
|
||||||
{
|
{
|
||||||
auto dumpContents = [&](PathView path)
|
auto dumpContents = [&](const CanonPath & path)
|
||||||
{
|
{
|
||||||
// FIXME: pipe
|
// FIXME: pipe
|
||||||
auto s = readFile(path);
|
auto s = readFile(path);
|
||||||
|
@ -30,9 +30,9 @@ void InputAccessor::dumpPath(
|
||||||
writePadding(s.size(), sink);
|
writePadding(s.size(), sink);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::function<void(const std::string & path)> dump;
|
std::function<void(const CanonPath & path)> dump;
|
||||||
|
|
||||||
dump = [&](const std::string & path) {
|
dump = [&](const CanonPath & path) {
|
||||||
checkInterrupt();
|
checkInterrupt();
|
||||||
|
|
||||||
auto st = lstat(path);
|
auto st = lstat(path);
|
||||||
|
@ -57,20 +57,20 @@ void InputAccessor::dumpPath(
|
||||||
std::string name(i.first);
|
std::string name(i.first);
|
||||||
size_t pos = i.first.find(caseHackSuffix);
|
size_t pos = i.first.find(caseHackSuffix);
|
||||||
if (pos != std::string::npos) {
|
if (pos != std::string::npos) {
|
||||||
debug(format("removing case hack suffix from '%s'") % (path + "/" + i.first));
|
debug("removing case hack suffix from '%s'", path + i.first);
|
||||||
name.erase(pos);
|
name.erase(pos);
|
||||||
}
|
}
|
||||||
if (!unhacked.emplace(name, i.first).second)
|
if (!unhacked.emplace(name, i.first).second)
|
||||||
throw Error("file name collision in between '%s' and '%s'",
|
throw Error("file name collision in between '%s' and '%s'",
|
||||||
(path + "/" + unhacked[name]),
|
(path + unhacked[name]),
|
||||||
(path + "/" + i.first));
|
(path + i.first));
|
||||||
} else
|
} else
|
||||||
unhacked.emplace(i.first, i.first);
|
unhacked.emplace(i.first, i.first);
|
||||||
|
|
||||||
for (auto & i : unhacked)
|
for (auto & i : unhacked)
|
||||||
if (filter(path + "/" + i.first)) {
|
if (filter((path + i.first).abs())) {
|
||||||
sink << "entry" << "(" << "name" << i.first << "node";
|
sink << "entry" << "(" << "name" << i.first << "node";
|
||||||
dump(path + "/" + i.second);
|
dump(path + i.second);
|
||||||
sink << ")";
|
sink << ")";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,46 +87,39 @@ void InputAccessor::dumpPath(
|
||||||
dump(path);
|
dump(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string InputAccessor::showPath(PathView path)
|
std::string InputAccessor::showPath(const CanonPath & path)
|
||||||
{
|
{
|
||||||
return "/virtual/" + std::to_string(number) + path;
|
return "/virtual/" + std::to_string(number) + path.abs();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FSInputAccessorImpl : FSInputAccessor
|
struct FSInputAccessorImpl : FSInputAccessor
|
||||||
{
|
{
|
||||||
Path root;
|
CanonPath root;
|
||||||
std::optional<PathSet> allowedPaths;
|
std::optional<std::set<CanonPath>> allowedPaths;
|
||||||
|
|
||||||
FSInputAccessorImpl(const Path & root, std::optional<PathSet> && allowedPaths)
|
FSInputAccessorImpl(const CanonPath & root, std::optional<std::set<CanonPath>> && allowedPaths)
|
||||||
: root(root)
|
: root(root)
|
||||||
, allowedPaths(allowedPaths)
|
, allowedPaths(allowedPaths)
|
||||||
{
|
{ }
|
||||||
if (allowedPaths) {
|
|
||||||
for (auto & p : *allowedPaths) {
|
|
||||||
assert(hasPrefix(p, "/"));
|
|
||||||
assert(!hasSuffix(p, "/"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string readFile(PathView path) override
|
std::string readFile(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto absPath = makeAbsPath(path);
|
auto absPath = makeAbsPath(path);
|
||||||
checkAllowed(absPath);
|
checkAllowed(absPath);
|
||||||
return nix::readFile(absPath);
|
return nix::readFile(absPath.abs());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pathExists(PathView path) override
|
bool pathExists(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto absPath = makeAbsPath(path);
|
auto absPath = makeAbsPath(path);
|
||||||
return isAllowed(absPath) && nix::pathExists(absPath);
|
return isAllowed(absPath) && nix::pathExists(absPath.abs());
|
||||||
}
|
}
|
||||||
|
|
||||||
Stat lstat(PathView path) override
|
Stat lstat(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto absPath = makeAbsPath(path);
|
auto absPath = makeAbsPath(path);
|
||||||
checkAllowed(absPath);
|
checkAllowed(absPath);
|
||||||
auto st = nix::lstat(absPath);
|
auto st = nix::lstat(absPath.abs());
|
||||||
return Stat {
|
return Stat {
|
||||||
.type =
|
.type =
|
||||||
S_ISREG(st.st_mode) ? tRegular :
|
S_ISREG(st.st_mode) ? tRegular :
|
||||||
|
@ -137,44 +130,44 @@ struct FSInputAccessorImpl : FSInputAccessor
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries readDirectory(PathView path) override
|
DirEntries readDirectory(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto absPath = makeAbsPath(path);
|
auto absPath = makeAbsPath(path);
|
||||||
checkAllowed(absPath);
|
checkAllowed(absPath);
|
||||||
DirEntries res;
|
DirEntries res;
|
||||||
for (auto & entry : nix::readDirectory(absPath)) {
|
for (auto & entry : nix::readDirectory(absPath.abs())) {
|
||||||
std::optional<Type> type;
|
std::optional<Type> type;
|
||||||
switch (entry.type) {
|
switch (entry.type) {
|
||||||
case DT_REG: type = Type::tRegular; break;
|
case DT_REG: type = Type::tRegular; break;
|
||||||
case DT_LNK: type = Type::tSymlink; break;
|
case DT_LNK: type = Type::tSymlink; break;
|
||||||
case DT_DIR: type = Type::tDirectory; break;
|
case DT_DIR: type = Type::tDirectory; break;
|
||||||
}
|
}
|
||||||
if (isAllowed(absPath + "/" + entry.name))
|
if (isAllowed(absPath + entry.name))
|
||||||
res.emplace(entry.name, type);
|
res.emplace(entry.name, type);
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readLink(PathView path) override
|
std::string readLink(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto absPath = makeAbsPath(path);
|
auto absPath = makeAbsPath(path);
|
||||||
checkAllowed(absPath);
|
checkAllowed(absPath);
|
||||||
return nix::readLink(absPath);
|
return nix::readLink(absPath.abs());
|
||||||
}
|
}
|
||||||
|
|
||||||
Path makeAbsPath(PathView path)
|
CanonPath makeAbsPath(const CanonPath & path)
|
||||||
{
|
{
|
||||||
// FIXME: resolve symlinks in 'path' and check that any
|
// FIXME: resolve symlinks in 'path' and check that any
|
||||||
// intermediate path is allowed.
|
// intermediate path is allowed.
|
||||||
assert(hasPrefix(path, "/"));
|
auto p = root + path;
|
||||||
try {
|
try {
|
||||||
return canonPath(root + std::string(path), true);
|
return p.resolveSymlinks();
|
||||||
} catch (Error &) {
|
} catch (Error &) {
|
||||||
return canonPath(root + std::string(path));
|
return p;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkAllowed(PathView absPath) override
|
void checkAllowed(const CanonPath & absPath) override
|
||||||
{
|
{
|
||||||
if (!isAllowed(absPath))
|
if (!isAllowed(absPath))
|
||||||
// FIXME: for Git trees, show a custom error message like
|
// FIXME: for Git trees, show a custom error message like
|
||||||
|
@ -182,9 +175,9 @@ struct FSInputAccessorImpl : FSInputAccessor
|
||||||
throw Error("access to path '%s' is forbidden", absPath);
|
throw Error("access to path '%s' is forbidden", absPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isAllowed(PathView absPath)
|
bool isAllowed(const CanonPath & absPath)
|
||||||
{
|
{
|
||||||
if (!isDirOrInDir(absPath, root))
|
if (!absPath.isWithin(root))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (allowedPaths) {
|
if (allowedPaths) {
|
||||||
|
@ -205,7 +198,7 @@ struct FSInputAccessorImpl : FSInputAccessor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void allowPath(Path path) override
|
void allowPath(CanonPath path) override
|
||||||
{
|
{
|
||||||
if (allowedPaths)
|
if (allowedPaths)
|
||||||
allowedPaths->insert(std::move(path));
|
allowedPaths->insert(std::move(path));
|
||||||
|
@ -216,15 +209,15 @@ struct FSInputAccessorImpl : FSInputAccessor
|
||||||
return (bool) allowedPaths;
|
return (bool) allowedPaths;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string showPath(PathView path) override
|
std::string showPath(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return root + path;
|
return (root + path).abs();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<FSInputAccessor> makeFSInputAccessor(
|
ref<FSInputAccessor> makeFSInputAccessor(
|
||||||
const Path & root,
|
const CanonPath & root,
|
||||||
std::optional<PathSet> && allowedPaths)
|
std::optional<std::set<CanonPath>> && allowedPaths)
|
||||||
{
|
{
|
||||||
return make_ref<FSInputAccessorImpl>(root, std::move(allowedPaths));
|
return make_ref<FSInputAccessorImpl>(root, std::move(allowedPaths));
|
||||||
}
|
}
|
||||||
|
@ -235,48 +228,42 @@ std::ostream & operator << (std::ostream & str, const SourcePath & path)
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
SourcePath SourcePath::append(std::string_view s) const
|
|
||||||
{
|
|
||||||
// FIXME: canonicalize?
|
|
||||||
return {accessor, path + s};
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MemoryInputAccessorImpl : MemoryInputAccessor
|
struct MemoryInputAccessorImpl : MemoryInputAccessor
|
||||||
{
|
{
|
||||||
std::map<Path, std::string> files;
|
std::map<CanonPath, std::string> files;
|
||||||
|
|
||||||
std::string readFile(PathView path) override
|
std::string readFile(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto i = files.find((Path) path);
|
auto i = files.find(path);
|
||||||
if (i == files.end())
|
if (i == files.end())
|
||||||
throw Error("file '%s' does not exist", path);
|
throw Error("file '%s' does not exist", path);
|
||||||
return i->second;
|
return i->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pathExists(PathView path) override
|
bool pathExists(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto i = files.find((Path) path);
|
auto i = files.find(path);
|
||||||
return i != files.end();
|
return i != files.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stat lstat(PathView path) override
|
Stat lstat(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
throw UnimplementedError("MemoryInputAccessor::lstat");
|
throw UnimplementedError("MemoryInputAccessor::lstat");
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries readDirectory(PathView path) override
|
DirEntries readDirectory(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readLink(PathView path) override
|
std::string readLink(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
throw UnimplementedError("MemoryInputAccessor::readLink");
|
throw UnimplementedError("MemoryInputAccessor::readLink");
|
||||||
}
|
}
|
||||||
|
|
||||||
void addFile(PathView path, std::string && contents) override
|
void addFile(CanonPath path, std::string && contents) override
|
||||||
{
|
{
|
||||||
files.emplace(path, std::move(contents));
|
files.emplace(std::move(path), std::move(contents));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -287,14 +274,14 @@ ref<MemoryInputAccessor> makeMemoryInputAccessor()
|
||||||
|
|
||||||
std::string_view SourcePath::baseName() const
|
std::string_view SourcePath::baseName() const
|
||||||
{
|
{
|
||||||
// FIXME
|
return path.baseName().value_or("source");
|
||||||
return path == "" || path == "/" ? "source" : baseNameOf(path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SourcePath SourcePath::parent() const
|
SourcePath SourcePath::parent() const
|
||||||
{
|
{
|
||||||
// FIXME:
|
auto p = path.parent();
|
||||||
return {accessor, dirOf(path)};
|
assert(p);
|
||||||
|
return {accessor, std::move(*p)};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "ref.hh"
|
#include "ref.hh"
|
||||||
#include "types.hh"
|
#include "types.hh"
|
||||||
#include "archive.hh"
|
#include "archive.hh"
|
||||||
|
#include "canon-path.hh"
|
||||||
|
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
@ -15,9 +16,9 @@ struct InputAccessor
|
||||||
virtual ~InputAccessor()
|
virtual ~InputAccessor()
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
virtual std::string readFile(PathView path) = 0;
|
virtual std::string readFile(const CanonPath & path) = 0;
|
||||||
|
|
||||||
virtual bool pathExists(PathView path) = 0;
|
virtual bool pathExists(const CanonPath & path) = 0;
|
||||||
|
|
||||||
enum Type { tRegular, tSymlink, tDirectory, tMisc };
|
enum Type { tRegular, tSymlink, tDirectory, tMisc };
|
||||||
|
|
||||||
|
@ -28,18 +29,18 @@ struct InputAccessor
|
||||||
bool isExecutable = false; // regular files only
|
bool isExecutable = false; // regular files only
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual Stat lstat(PathView path) = 0;
|
virtual Stat lstat(const CanonPath & path) = 0;
|
||||||
|
|
||||||
typedef std::optional<Type> DirEntry;
|
typedef std::optional<Type> DirEntry;
|
||||||
|
|
||||||
typedef std::map<std::string, DirEntry> DirEntries;
|
typedef std::map<std::string, DirEntry> DirEntries;
|
||||||
|
|
||||||
virtual DirEntries readDirectory(PathView path) = 0;
|
virtual DirEntries readDirectory(const CanonPath & path) = 0;
|
||||||
|
|
||||||
virtual std::string readLink(PathView path) = 0;
|
virtual std::string readLink(const CanonPath & path) = 0;
|
||||||
|
|
||||||
virtual void dumpPath(
|
virtual void dumpPath(
|
||||||
const Path & path,
|
const CanonPath & path,
|
||||||
Sink & sink,
|
Sink & sink,
|
||||||
PathFilter & filter = defaultPathFilter);
|
PathFilter & filter = defaultPathFilter);
|
||||||
|
|
||||||
|
@ -53,30 +54,30 @@ struct InputAccessor
|
||||||
return number < x.number;
|
return number < x.number;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual std::string showPath(PathView path);
|
virtual std::string showPath(const CanonPath & path);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FSInputAccessor : InputAccessor
|
struct FSInputAccessor : InputAccessor
|
||||||
{
|
{
|
||||||
virtual void checkAllowed(PathView absPath) = 0;
|
virtual void checkAllowed(const CanonPath & absPath) = 0;
|
||||||
|
|
||||||
virtual void allowPath(Path path) = 0;
|
virtual void allowPath(CanonPath path) = 0;
|
||||||
|
|
||||||
virtual bool hasAccessControl() = 0;
|
virtual bool hasAccessControl() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<FSInputAccessor> makeFSInputAccessor(
|
ref<FSInputAccessor> makeFSInputAccessor(
|
||||||
const Path & root,
|
const CanonPath & root,
|
||||||
std::optional<PathSet> && allowedPaths = {});
|
std::optional<std::set<CanonPath>> && allowedPaths = {});
|
||||||
|
|
||||||
struct MemoryInputAccessor : InputAccessor
|
struct MemoryInputAccessor : InputAccessor
|
||||||
{
|
{
|
||||||
virtual void addFile(PathView path, std::string && contents) = 0;
|
virtual void addFile(CanonPath path, std::string && contents) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<MemoryInputAccessor> makeMemoryInputAccessor();
|
ref<MemoryInputAccessor> makeMemoryInputAccessor();
|
||||||
|
|
||||||
ref<InputAccessor> makeZipInputAccessor(PathView path);
|
ref<InputAccessor> makeZipInputAccessor(const CanonPath & path);
|
||||||
|
|
||||||
ref<InputAccessor> makePatchingInputAccessor(
|
ref<InputAccessor> makePatchingInputAccessor(
|
||||||
ref<InputAccessor> next,
|
ref<InputAccessor> next,
|
||||||
|
@ -85,7 +86,7 @@ ref<InputAccessor> makePatchingInputAccessor(
|
||||||
struct SourcePath
|
struct SourcePath
|
||||||
{
|
{
|
||||||
InputAccessor & accessor;
|
InputAccessor & accessor;
|
||||||
Path path;
|
CanonPath path;
|
||||||
|
|
||||||
std::string_view baseName() const;
|
std::string_view baseName() const;
|
||||||
|
|
||||||
|
@ -111,7 +112,11 @@ struct SourcePath
|
||||||
std::string to_string() const
|
std::string to_string() const
|
||||||
{ return accessor.showPath(path); }
|
{ return accessor.showPath(path); }
|
||||||
|
|
||||||
SourcePath append(std::string_view s) const;
|
SourcePath operator + (const CanonPath & x) const
|
||||||
|
{ return {accessor, path + x}; }
|
||||||
|
|
||||||
|
SourcePath operator + (std::string_view c) const
|
||||||
|
{ return {accessor, path + c}; }
|
||||||
|
|
||||||
bool operator == (const SourcePath & x) const
|
bool operator == (const SourcePath & x) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,7 @@ struct PatchingInputAccessor : InputAccessor
|
||||||
{
|
{
|
||||||
ref<InputAccessor> next;
|
ref<InputAccessor> next;
|
||||||
|
|
||||||
std::map<Path, std::vector<std::string>> patchesPerFile;
|
std::map<CanonPath, std::vector<std::string>> patchesPerFile;
|
||||||
|
|
||||||
PatchingInputAccessor(
|
PatchingInputAccessor(
|
||||||
ref<InputAccessor> next,
|
ref<InputAccessor> next,
|
||||||
|
@ -29,7 +29,7 @@ struct PatchingInputAccessor : InputAccessor
|
||||||
if (slash == fileName.npos) return;
|
if (slash == fileName.npos) return;
|
||||||
fileName = fileName.substr(slash);
|
fileName = fileName.substr(slash);
|
||||||
debug("found patch for '%s'", fileName);
|
debug("found patch for '%s'", fileName);
|
||||||
patchesPerFile.emplace(Path(fileName), std::vector<std::string>())
|
patchesPerFile.emplace(fileName, std::vector<std::string>())
|
||||||
.first->second.push_back(std::string(contents));
|
.first->second.push_back(std::string(contents));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,11 +60,11 @@ struct PatchingInputAccessor : InputAccessor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readFile(PathView path) override
|
std::string readFile(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto contents = next->readFile(path);
|
auto contents = next->readFile(path);
|
||||||
|
|
||||||
auto i = patchesPerFile.find((Path) path);
|
auto i = patchesPerFile.find(path);
|
||||||
if (i != patchesPerFile.end()) {
|
if (i != patchesPerFile.end()) {
|
||||||
for (auto & patch : i->second) {
|
for (auto & patch : i->second) {
|
||||||
auto tempDir = createTempDir();
|
auto tempDir = createTempDir();
|
||||||
|
@ -84,22 +84,22 @@ struct PatchingInputAccessor : InputAccessor
|
||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pathExists(PathView path) override
|
bool pathExists(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return next->pathExists(path);
|
return next->pathExists(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Stat lstat(PathView path) override
|
Stat lstat(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return next->lstat(path);
|
return next->lstat(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries readDirectory(PathView path) override
|
DirEntries readDirectory(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return next->readDirectory(path);
|
return next->readDirectory(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readLink(PathView path) override
|
std::string readLink(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
return next->readLink(path);
|
return next->readLink(path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,25 +81,25 @@ struct PathInputScheme : InputScheme
|
||||||
// nothing to do
|
// nothing to do
|
||||||
}
|
}
|
||||||
|
|
||||||
Path getAbsPath(ref<Store> store, const Input & input)
|
CanonPath getAbsPath(ref<Store> store, const Input & input)
|
||||||
{
|
{
|
||||||
auto path = getStrAttr(input.attrs, "path");
|
auto path = getStrAttr(input.attrs, "path");
|
||||||
|
|
||||||
if (path[0] == '/')
|
if (path[0] == '/')
|
||||||
return path;
|
return CanonPath(path);
|
||||||
|
|
||||||
if (!input.parent)
|
if (!input.parent)
|
||||||
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
throw Error("cannot fetch input '%s' because it uses a relative path", input.to_string());
|
||||||
|
|
||||||
auto parent = canonPath(*input.parent);
|
CanonPath parent(*input.parent);
|
||||||
|
|
||||||
// the path isn't relative, prefix it
|
// the path isn't relative, prefix it
|
||||||
auto absPath = nix::absPath(path, parent);
|
auto absPath = CanonPath(path, parent);
|
||||||
|
|
||||||
// for security, ensure that if the parent is a store path, it's inside it
|
// for security, ensure that if the parent is a store path, it's inside it
|
||||||
if (store->isInStore(parent)) {
|
if (store->isInStore(parent.abs())) {
|
||||||
auto storePath = store->printStorePath(store->toStorePath(parent).first);
|
auto storePath = store->printStorePath(store->toStorePath(parent.abs()).first);
|
||||||
if (!isDirOrInDir(absPath, storePath))
|
if (!absPath.isWithin(CanonPath(storePath)))
|
||||||
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
throw BadStorePath("relative path '%s' points outside of its parent's store path '%s'", path, storePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ struct PathInputScheme : InputScheme
|
||||||
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
|
Activity act(*logger, lvlTalkative, actUnknown, fmt("copying '%s'", absPath));
|
||||||
|
|
||||||
// FIXME: check whether access to 'path' is allowed.
|
// FIXME: check whether access to 'path' is allowed.
|
||||||
auto storePath = store->maybeParseStorePath(absPath);
|
auto storePath = store->maybeParseStorePath(absPath.abs());
|
||||||
|
|
||||||
if (storePath)
|
if (storePath)
|
||||||
store->addTempRoot(*storePath);
|
store->addTempRoot(*storePath);
|
||||||
|
@ -124,7 +124,7 @@ struct PathInputScheme : InputScheme
|
||||||
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
|
if (!storePath || storePath->name() != "source" || !store->isValidPath(*storePath)) {
|
||||||
// FIXME: try to substitute storePath.
|
// FIXME: try to substitute storePath.
|
||||||
auto src = sinkToSource([&](Sink & sink) {
|
auto src = sinkToSource([&](Sink & sink) {
|
||||||
mtime = dumpPathAndGetMtime(absPath, sink, defaultPathFilter);
|
mtime = dumpPathAndGetMtime(absPath.abs(), sink, defaultPathFilter);
|
||||||
});
|
});
|
||||||
storePath = store->addToStoreFromDump(*src, "source");
|
storePath = store->addToStoreFromDump(*src, "source");
|
||||||
}
|
}
|
||||||
|
@ -137,7 +137,7 @@ struct PathInputScheme : InputScheme
|
||||||
{
|
{
|
||||||
auto absPath = getAbsPath(store, input);
|
auto absPath = getAbsPath(store, input);
|
||||||
auto input2(input);
|
auto input2(input);
|
||||||
input2.attrs.emplace("path", absPath);
|
input2.attrs.emplace("path", (std::string) absPath.abs());
|
||||||
return {makeFSInputAccessor(absPath), std::move(input2)};
|
return {makeFSInputAccessor(absPath), std::move(input2)};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,13 +22,13 @@ struct ZipMember
|
||||||
|
|
||||||
struct ZipInputAccessor : InputAccessor
|
struct ZipInputAccessor : InputAccessor
|
||||||
{
|
{
|
||||||
Path zipPath;
|
CanonPath zipPath;
|
||||||
struct zip * zipFile = nullptr;
|
struct zip * zipFile = nullptr;
|
||||||
|
|
||||||
typedef std::map<const char *, struct zip_stat, cmp_str> Members;
|
typedef std::map<const char *, struct zip_stat, cmp_str> Members;
|
||||||
Members members;
|
Members members;
|
||||||
|
|
||||||
ZipInputAccessor(PathView _zipPath)
|
ZipInputAccessor(const CanonPath & _zipPath)
|
||||||
: zipPath(_zipPath)
|
: zipPath(_zipPath)
|
||||||
{
|
{
|
||||||
int error;
|
int error;
|
||||||
|
@ -58,9 +58,9 @@ struct ZipInputAccessor : InputAccessor
|
||||||
if (zipFile) zip_close(zipFile);
|
if (zipFile) zip_close(zipFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string _readFile(PathView path)
|
std::string _readFile(const CanonPath & path)
|
||||||
{
|
{
|
||||||
auto i = members.find(((std::string) path).c_str());
|
auto i = members.find(((std::string) path.abs()).c_str());
|
||||||
if (i == members.end())
|
if (i == members.end())
|
||||||
throw Error("file '%s' does not exist", path);
|
throw Error("file '%s' does not exist", path);
|
||||||
|
|
||||||
|
@ -76,37 +76,32 @@ struct ZipInputAccessor : InputAccessor
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readFile(PathView _path) override
|
std::string readFile(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto path = canonPath(_path);
|
|
||||||
|
|
||||||
if (lstat(path).type != tRegular)
|
if (lstat(path).type != tRegular)
|
||||||
throw Error("file '%s' is not a regular file");
|
throw Error("file '%s' is not a regular file");
|
||||||
|
|
||||||
return _readFile(path);
|
return _readFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool pathExists(PathView _path) override
|
bool pathExists(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto path = canonPath(_path);
|
|
||||||
return
|
return
|
||||||
members.find(((std::string) path).c_str()) != members.end()
|
members.find(path.c_str()) != members.end()
|
||||||
|| members.find(((std::string) path + "/").c_str()) != members.end();
|
|| members.find(((std::string) path.abs() + "/").c_str()) != members.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
Stat lstat(PathView _path) override
|
Stat lstat(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto path = canonPath(_path);
|
if (path.isRoot())
|
||||||
|
|
||||||
if (path == "/")
|
|
||||||
return Stat { .type = tDirectory };
|
return Stat { .type = tDirectory };
|
||||||
|
|
||||||
Type type = tRegular;
|
Type type = tRegular;
|
||||||
bool isExecutable = false;
|
bool isExecutable = false;
|
||||||
|
|
||||||
auto i = members.find(((std::string) path).c_str());
|
auto i = members.find(path.c_str());
|
||||||
if (i == members.end()) {
|
if (i == members.end()) {
|
||||||
i = members.find(((std::string) path + "/").c_str());
|
i = members.find(((std::string) path.abs() + "/").c_str());
|
||||||
type = tDirectory;
|
type = tDirectory;
|
||||||
}
|
}
|
||||||
if (i == members.end())
|
if (i == members.end())
|
||||||
|
@ -138,12 +133,12 @@ struct ZipInputAccessor : InputAccessor
|
||||||
return Stat { .type = type, .isExecutable = isExecutable };
|
return Stat { .type = type, .isExecutable = isExecutable };
|
||||||
}
|
}
|
||||||
|
|
||||||
DirEntries readDirectory(PathView _path) override
|
DirEntries readDirectory(const CanonPath & _path) override
|
||||||
{
|
{
|
||||||
auto path = canonPath(_path);
|
std::string path(_path.abs());
|
||||||
if (path != "/") path += "/";
|
if (path != "/") path += "/";
|
||||||
|
|
||||||
auto i = members.find(((std::string) path).c_str());
|
auto i = members.find(path.c_str());
|
||||||
if (i == members.end())
|
if (i == members.end())
|
||||||
throw Error("directory '%s' does not exist", path);
|
throw Error("directory '%s' does not exist", path);
|
||||||
|
|
||||||
|
@ -162,18 +157,16 @@ struct ZipInputAccessor : InputAccessor
|
||||||
return entries;
|
return entries;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string readLink(PathView _path) override
|
std::string readLink(const CanonPath & path) override
|
||||||
{
|
{
|
||||||
auto path = canonPath(_path);
|
|
||||||
|
|
||||||
if (lstat(path).type != tSymlink)
|
if (lstat(path).type != tSymlink)
|
||||||
throw Error("file '%s' is not a symlink");
|
throw Error("file '%s' is not a symlink");
|
||||||
|
|
||||||
return _readFile(canonPath(_path));
|
return _readFile(path);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ref<InputAccessor> makeZipInputAccessor(PathView path)
|
ref<InputAccessor> makeZipInputAccessor(const CanonPath & path)
|
||||||
{
|
{
|
||||||
return make_ref<ZipInputAccessor>(path);
|
return make_ref<ZipInputAccessor>(path);
|
||||||
}
|
}
|
||||||
|
|
|
@ -448,7 +448,7 @@ std::string Derivation::unparse(const Store & store, bool maskOutputs,
|
||||||
|
|
||||||
|
|
||||||
// FIXME: remove
|
// FIXME: remove
|
||||||
bool isDerivation(const std::string & fileName)
|
bool isDerivation(std::string_view fileName)
|
||||||
{
|
{
|
||||||
return hasSuffix(fileName, drvExtension);
|
return hasSuffix(fileName, drvExtension);
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ StorePath writeDerivation(Store & store,
|
||||||
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name);
|
Derivation parseDerivation(const Store & store, std::string && s, std::string_view name);
|
||||||
|
|
||||||
// FIXME: remove
|
// FIXME: remove
|
||||||
bool isDerivation(const std::string & fileName);
|
bool isDerivation(std::string_view fileName);
|
||||||
|
|
||||||
/* Calculate the name that will be used for the store path for this
|
/* Calculate the name that will be used for the store path for this
|
||||||
output.
|
output.
|
||||||
|
|
|
@ -17,21 +17,21 @@
|
||||||
namespace nix {
|
namespace nix {
|
||||||
|
|
||||||
|
|
||||||
bool Store::isInStore(const Path & path) const
|
bool Store::isInStore(PathView path) const
|
||||||
{
|
{
|
||||||
return isInDir(path, storeDir);
|
return isInDir(path, storeDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::pair<StorePath, Path> Store::toStorePath(const Path & path) const
|
std::pair<StorePath, Path> Store::toStorePath(PathView path) const
|
||||||
{
|
{
|
||||||
if (!isInStore(path))
|
if (!isInStore(path))
|
||||||
throw Error("path '%1%' is not in the Nix store", path);
|
throw Error("path '%1%' is not in the Nix store", path);
|
||||||
Path::size_type slash = path.find('/', storeDir.size() + 1);
|
auto slash = path.find('/', storeDir.size() + 1);
|
||||||
if (slash == Path::npos)
|
if (slash == Path::npos)
|
||||||
return {parseStorePath(path), ""};
|
return {parseStorePath(path), ""};
|
||||||
else
|
else
|
||||||
return {parseStorePath(std::string_view(path).substr(0, slash)), path.substr(slash)};
|
return {parseStorePath(path.substr(0, slash)), (Path) path.substr(slash)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -178,7 +178,7 @@ public:
|
||||||
|
|
||||||
/* Return true if ‘path’ is in the Nix store (but not the Nix
|
/* Return true if ‘path’ is in the Nix store (but not the Nix
|
||||||
store itself). */
|
store itself). */
|
||||||
bool isInStore(const Path & path) const;
|
bool isInStore(PathView path) const;
|
||||||
|
|
||||||
/* Return true if ‘path’ is a store path, i.e. a direct child of
|
/* Return true if ‘path’ is a store path, i.e. a direct child of
|
||||||
the Nix store. */
|
the Nix store. */
|
||||||
|
@ -186,7 +186,7 @@ public:
|
||||||
|
|
||||||
/* Split a path like /nix/store/<hash>-<name>/<bla> into
|
/* Split a path like /nix/store/<hash>-<name>/<bla> into
|
||||||
/nix/store/<hash>-<name> and /<bla>. */
|
/nix/store/<hash>-<name> and /<bla>. */
|
||||||
std::pair<StorePath, Path> toStorePath(const Path & path) const;
|
std::pair<StorePath, Path> toStorePath(PathView path) const;
|
||||||
|
|
||||||
/* Follow symlinks until we end up with a path in the Nix store. */
|
/* Follow symlinks until we end up with a path in the Nix store. */
|
||||||
Path followLinksToStore(std::string_view path) const;
|
Path followLinksToStore(std::string_view path) const;
|
||||||
|
|
72
src/libutil/canon-path.cc
Normal file
72
src/libutil/canon-path.cc
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
#include "canon-path.hh"
|
||||||
|
#include "util.hh"
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
CanonPath CanonPath::root = CanonPath("/");
|
||||||
|
|
||||||
|
CanonPath::CanonPath(std::string_view raw)
|
||||||
|
: path(absPath((Path) raw, "/"))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
CanonPath::CanonPath(std::string_view raw, const CanonPath & root)
|
||||||
|
: path(absPath((Path) raw, root.abs()))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
std::optional<CanonPath> CanonPath::parent() const
|
||||||
|
{
|
||||||
|
if (isRoot()) return std::nullopt;
|
||||||
|
return CanonPath(unchecked_t(), path.substr(0, path.rfind('/')));
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonPath CanonPath::resolveSymlinks() const
|
||||||
|
{
|
||||||
|
return CanonPath(unchecked_t(), canonPath(abs(), true));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CanonPath::isWithin(const CanonPath & parent) const
|
||||||
|
{
|
||||||
|
return !(
|
||||||
|
path.size() < parent.path.size()
|
||||||
|
|| path.substr(0, parent.path.size()) != parent.path
|
||||||
|
|| (parent.path.size() > 1 && path.size() > parent.path.size()
|
||||||
|
&& path[parent.path.size()] != '/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanonPath::extend(const CanonPath & x)
|
||||||
|
{
|
||||||
|
if (x.isRoot()) return;
|
||||||
|
if (isRoot())
|
||||||
|
path += x.rel();
|
||||||
|
else
|
||||||
|
path += x.abs();
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonPath CanonPath::operator + (const CanonPath & x) const
|
||||||
|
{
|
||||||
|
auto res = *this;
|
||||||
|
res.extend(x);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CanonPath::push(std::string_view c)
|
||||||
|
{
|
||||||
|
assert(c.find('/') == c.npos);
|
||||||
|
if (!isRoot()) path += '/';
|
||||||
|
path += c;
|
||||||
|
}
|
||||||
|
|
||||||
|
CanonPath CanonPath::operator + (std::string_view c) const
|
||||||
|
{
|
||||||
|
auto res = *this;
|
||||||
|
res.push(c);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream & operator << (std::ostream & stream, const CanonPath & path)
|
||||||
|
{
|
||||||
|
stream << path.abs();
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
139
src/libutil/canon-path.hh
Normal file
139
src/libutil/canon-path.hh
Normal file
|
@ -0,0 +1,139 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <cassert>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
/* A canonical representation of a path. It ensures the following:
|
||||||
|
|
||||||
|
- It always starts with a slash.
|
||||||
|
|
||||||
|
- It never ends with a slash, except if the path is "/".
|
||||||
|
|
||||||
|
- A slash is never followed by a slash (i.e. no empty components).
|
||||||
|
|
||||||
|
- There are no components equal to '.' or '..'.
|
||||||
|
|
||||||
|
Note that the path does not need to correspond to an actually
|
||||||
|
existing path, and there is no guarantee that symlinks are
|
||||||
|
resolved.
|
||||||
|
*/
|
||||||
|
class CanonPath
|
||||||
|
{
|
||||||
|
std::string path;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/* Construct a canon path from a non-canonical path. Any '.', '..'
|
||||||
|
or empty components are removed. */
|
||||||
|
CanonPath(std::string_view raw);
|
||||||
|
|
||||||
|
explicit CanonPath(const char * raw)
|
||||||
|
: CanonPath(std::string_view(raw))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
struct unchecked_t { };
|
||||||
|
|
||||||
|
CanonPath(unchecked_t _, std::string path)
|
||||||
|
: path(std::move(path))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
static CanonPath root;
|
||||||
|
|
||||||
|
/* If `raw` starts with a slash, return
|
||||||
|
`CanonPath(raw)`. Otherwise return a `CanonPath` representing
|
||||||
|
`root + "/" + raw`. */
|
||||||
|
CanonPath(std::string_view raw, const CanonPath & root);
|
||||||
|
|
||||||
|
bool isRoot() const
|
||||||
|
{ return path.size() <= 1; }
|
||||||
|
|
||||||
|
explicit operator std::string_view() const
|
||||||
|
{ return path; }
|
||||||
|
|
||||||
|
const std::string & abs() const
|
||||||
|
{ return path; }
|
||||||
|
|
||||||
|
const char * c_str() const
|
||||||
|
{ return path.c_str(); }
|
||||||
|
|
||||||
|
std::string_view rel() const
|
||||||
|
{ return ((std::string_view) path).substr(1); }
|
||||||
|
|
||||||
|
struct Iterator
|
||||||
|
{
|
||||||
|
std::string_view remaining;
|
||||||
|
size_t slash;
|
||||||
|
|
||||||
|
Iterator(std::string_view remaining)
|
||||||
|
: remaining(remaining)
|
||||||
|
, slash(remaining.find('/'))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool operator != (const Iterator & x) const
|
||||||
|
{ return remaining.data() != x.remaining.data(); }
|
||||||
|
|
||||||
|
const std::string_view operator * () const
|
||||||
|
{ return remaining.substr(0, slash); }
|
||||||
|
|
||||||
|
void operator ++ ()
|
||||||
|
{
|
||||||
|
if (slash == remaining.npos)
|
||||||
|
remaining = remaining.substr(remaining.size());
|
||||||
|
else {
|
||||||
|
remaining = remaining.substr(slash + 1);
|
||||||
|
slash = remaining.find('/');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Iterator begin() { return Iterator(rel()); }
|
||||||
|
Iterator end() { return Iterator(rel().substr(path.size() - 1)); }
|
||||||
|
|
||||||
|
std::optional<CanonPath> parent() const;
|
||||||
|
|
||||||
|
std::optional<std::string_view> dirOf() const
|
||||||
|
{
|
||||||
|
if (isRoot()) return std::nullopt;
|
||||||
|
return path.substr(0, path.rfind('/'));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::string_view> baseName() const
|
||||||
|
{
|
||||||
|
if (isRoot()) return std::nullopt;
|
||||||
|
return ((std::string_view) path).substr(path.rfind('/') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator == (const CanonPath & x) const
|
||||||
|
{ return path == x.path; }
|
||||||
|
|
||||||
|
bool operator != (const CanonPath & x) const
|
||||||
|
{ return path != x.path; }
|
||||||
|
|
||||||
|
bool operator < (const CanonPath & x) const
|
||||||
|
{ return path < x.path; }
|
||||||
|
|
||||||
|
CanonPath resolveSymlinks() const;
|
||||||
|
|
||||||
|
/* Return true if `this` is equal to `parent` or a child of
|
||||||
|
`parent`. */
|
||||||
|
bool isWithin(const CanonPath & parent) const;
|
||||||
|
|
||||||
|
/* Append another path to this one. */
|
||||||
|
void extend(const CanonPath & x);
|
||||||
|
|
||||||
|
/* Concatenate two paths. */
|
||||||
|
CanonPath operator + (const CanonPath & x) const;
|
||||||
|
|
||||||
|
/* Add a path component to this one. It must not contain any slashes. */
|
||||||
|
void push(std::string_view c);
|
||||||
|
|
||||||
|
CanonPath operator + (std::string_view c) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream & operator << (std::ostream & stream, const CanonPath & path);
|
||||||
|
|
||||||
|
}
|
|
@ -148,7 +148,7 @@ inline hintformat hintfmt(const std::string & fs, const Args & ... args)
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline hintformat hintfmt(std::string plain_string)
|
inline hintformat hintfmt(const std::string & plain_string)
|
||||||
{
|
{
|
||||||
// we won't be receiving any args in this case, so just print the original string
|
// we won't be receiving any args in this case, so just print the original string
|
||||||
return hintfmt("%s", normaltxt(plain_string));
|
return hintfmt("%s", normaltxt(plain_string));
|
||||||
|
|
98
src/libutil/tests/canon-path.cc
Normal file
98
src/libutil/tests/canon-path.cc
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
#include "canon-path.hh"
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
namespace nix {
|
||||||
|
|
||||||
|
TEST(CanonPath, basic) {
|
||||||
|
{
|
||||||
|
CanonPath p("/");
|
||||||
|
ASSERT_EQ(p.abs(), "/");
|
||||||
|
ASSERT_EQ(p.rel(), "");
|
||||||
|
ASSERT_EQ(p.baseName(), std::nullopt);
|
||||||
|
ASSERT_EQ(p.dirOf(), std::nullopt);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/foo//");
|
||||||
|
ASSERT_EQ(p.abs(), "/foo");
|
||||||
|
ASSERT_EQ(p.rel(), "foo");
|
||||||
|
ASSERT_EQ(*p.baseName(), "foo");
|
||||||
|
ASSERT_EQ(*p.dirOf(), ""); // FIXME: do we want this?
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("foo/bar");
|
||||||
|
ASSERT_EQ(p.abs(), "/foo/bar");
|
||||||
|
ASSERT_EQ(p.rel(), "foo/bar");
|
||||||
|
ASSERT_EQ(*p.baseName(), "bar");
|
||||||
|
ASSERT_EQ(*p.dirOf(), "/foo");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("foo//bar/");
|
||||||
|
ASSERT_EQ(p.abs(), "/foo/bar");
|
||||||
|
ASSERT_EQ(p.rel(), "foo/bar");
|
||||||
|
ASSERT_EQ(*p.baseName(), "bar");
|
||||||
|
ASSERT_EQ(*p.dirOf(), "/foo");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, iter) {
|
||||||
|
{
|
||||||
|
CanonPath p("a//foo/bar//");
|
||||||
|
std::vector<std::string_view> ss;
|
||||||
|
for (auto & c : p) ss.push_back(c);
|
||||||
|
ASSERT_EQ(ss, std::vector<std::string_view>({"a", "foo", "bar"}));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/");
|
||||||
|
std::vector<std::string_view> ss;
|
||||||
|
for (auto & c : p) ss.push_back(c);
|
||||||
|
ASSERT_EQ(ss, std::vector<std::string_view>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, concat) {
|
||||||
|
{
|
||||||
|
CanonPath p1("a//foo/bar//");
|
||||||
|
CanonPath p2("xyzzy/bla");
|
||||||
|
ASSERT_EQ((p1 + p2).abs(), "/a/foo/bar/xyzzy/bla");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p1("/");
|
||||||
|
CanonPath p2("/a/b");
|
||||||
|
ASSERT_EQ((p1 + p2).abs(), "/a/b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p1("/a/b");
|
||||||
|
CanonPath p2("/");
|
||||||
|
ASSERT_EQ((p1 + p2).abs(), "/a/b");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/foo/bar");
|
||||||
|
ASSERT_EQ((p + "x").abs(), "/foo/bar/x");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
CanonPath p("/");
|
||||||
|
ASSERT_EQ((p + "foo" + "bar").abs(), "/foo/bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(CanonPath, within) {
|
||||||
|
{
|
||||||
|
ASSERT_TRUE(CanonPath("foo").isWithin(CanonPath("foo")));
|
||||||
|
ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("bar")));
|
||||||
|
ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("fo")));
|
||||||
|
ASSERT_TRUE(CanonPath("foo/bar").isWithin(CanonPath("foo")));
|
||||||
|
ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("foo/bar")));
|
||||||
|
ASSERT_TRUE(CanonPath("/foo/bar/default.nix").isWithin(CanonPath("/")));
|
||||||
|
ASSERT_TRUE(CanonPath("/").isWithin(CanonPath("/")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -721,4 +721,11 @@ inline std::string operator + (std::string && s, std::string_view s2)
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline std::string operator + (std::string_view s1, const char * s2)
|
||||||
|
{
|
||||||
|
std::string s(s1);
|
||||||
|
s.append(s2);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1477,7 +1477,7 @@ static int main_nix_env(int argc, char * * argv)
|
||||||
if (file != "")
|
if (file != "")
|
||||||
// FIXME: check that the accessor returned by
|
// FIXME: check that the accessor returned by
|
||||||
// lookupFileArg() is the root FS.
|
// lookupFileArg() is the root FS.
|
||||||
globals.instSource.nixExprPath = lookupFileArg(*globals.state, file).path;
|
globals.instSource.nixExprPath = lookupFileArg(*globals.state, file).path.abs();
|
||||||
|
|
||||||
globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
|
globals.instSource.autoArgs = myArgs.getAutoArgs(*globals.state);
|
||||||
|
|
||||||
|
|
|
@ -179,7 +179,7 @@ static void showHelp(std::vector<std::string> subcommand, MultiCommand & topleve
|
||||||
, state.rootPath("/")), *vGenerateManpage);
|
, state.rootPath("/")), *vGenerateManpage);
|
||||||
|
|
||||||
state.corepkgsFS->addFile(
|
state.corepkgsFS->addFile(
|
||||||
"/utils.nix",
|
CanonPath("utils.nix"),
|
||||||
#include "utils.nix.gen.hh"
|
#include "utils.nix.gen.hh"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue