Skip to content

Commit e3b969c

Browse files
committed
devenv: resolve physical path through impure mode union accessor
The issue: 1. Local directories are mounted at virtual store paths in the storeFS filesystem 2. When files are accessed through these mounts, they go through a UnionSourceAccessor that sees the store paths 3. The original path information was lost in the process We now keep track of the original store paths and provide a unified getPhysicalPath method to access them.
1 parent 031c3cf commit e3b969c

File tree

4 files changed

+91
-5
lines changed

4 files changed

+91
-5
lines changed

src/libexpr/eval.cc

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,58 @@ void EvalState::allowAndSetStorePathString(const StorePath & storePath, Value &
411411
mkStorePathString(storePath, v);
412412
}
413413

414+
std::optional<std::filesystem::path> EvalState::getPhysicalPath(const SourcePath & path)
415+
{
416+
// FIRST check if this is a store path that might be mounted
417+
// This needs to happen before checking the direct physical path
418+
// because in impure mode, the UnionSourceAccessor will return the actual
419+
// /nix/store path as a physical path, but we want to resolve to the original mount
420+
421+
// Check if this path starts with the store directory
422+
bool isStorePath = path.path.abs().starts_with(store->storeDir + "/");
423+
424+
if (isStorePath) {
425+
// Extract just the store path part (without subpaths)
426+
std::string pathStr = path.path.abs();
427+
std::string storePrefix = store->storeDir + "/";
428+
std::string relativePath = pathStr.substr(storePrefix.length());
429+
430+
// Find the end of the store path (next slash or end of string)
431+
auto slashPos = relativePath.find('/');
432+
std::string storePathName = slashPos != std::string::npos
433+
? relativePath.substr(0, slashPos)
434+
: relativePath;
435+
436+
std::string fullStorePath = storePrefix + storePathName;
437+
438+
auto storePath = store->parseStorePath(fullStorePath);
439+
auto mountPath = CanonPath(store->printStorePath(storePath));
440+
441+
if (auto mount = storeFS.dynamic_pointer_cast<MountedSourceAccessor>()) {
442+
if (auto accessor = mount->getMount(mountPath)) {
443+
// Get the relative path within the mount
444+
std::string relPath;
445+
if (slashPos != std::string::npos) {
446+
relPath = relativePath.substr(slashPos + 1);
447+
}
448+
449+
// Try to get the physical path from the mounted accessor
450+
auto mountPhysical = accessor->getPhysicalPath(CanonPath::root);
451+
if (mountPhysical) {
452+
if (relPath.empty()) {
453+
return *mountPhysical;
454+
} else {
455+
return *mountPhysical / relPath;
456+
}
457+
}
458+
}
459+
}
460+
}
461+
462+
// If not a mounted store path, try the path's own physical path
463+
return path.getPhysicalPath();
464+
}
465+
414466
inline static bool isJustSchemePrefix(std::string_view prefix)
415467
{
416468
return
@@ -1123,7 +1175,7 @@ void EvalState::evalFile(const SourcePath & path, Value & v, bool mustBeTrivial)
11231175
return;
11241176
}
11251177

1126-
auto physicalPath = resolvedPath.getPhysicalPath();
1178+
auto physicalPath = getPhysicalPath(resolvedPath);
11271179
printTalkative("evaluating file '%1%'", physicalPath ? physicalPath->string() : resolvedPath.to_string());
11281180
Expr * e = nullptr;
11291181

src/libexpr/include/nix/expr/eval.hh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,12 @@ public:
610610
*/
611611
std::string computeBaseName(const SourcePath & path);
612612

613+
/**
614+
* Get the physical filesystem path for a SourcePath, if available.
615+
* Returns the physical path if it exists, or attempts to resolve store paths.
616+
*/
617+
std::optional<std::filesystem::path> getPhysicalPath(const SourcePath & path);
618+
613619
/**
614620
* Path coercion.
615621
*

src/libexpr/primops.cc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,7 +1773,7 @@ static void prim_pathExists(EvalState & state, const PosIdx pos, Value * * args,
17731773
mustBeDir ? SymlinkResolution::Full : SymlinkResolution::Ancestors;
17741774
auto path = realisePath(state, pos, arg, symlinkResolution);
17751775

1776-
auto physicalPath = path.getPhysicalPath();
1776+
auto physicalPath = state.getPhysicalPath(path);
17771777
printTalkative("devenv pathExists: '%1%'", physicalPath ? physicalPath->string() : path.to_string());
17781778

17791779
auto st = path.maybeLstat();
@@ -1876,7 +1876,7 @@ static RegisterPrimOp primop_dirOf({
18761876
static void prim_readFile(EvalState & state, const PosIdx pos, Value * * args, Value & v)
18771877
{
18781878
auto path = realisePath(state, pos, *args[0]);
1879-
auto physicalPath = path.getPhysicalPath();
1879+
auto physicalPath = state.getPhysicalPath(path);
18801880
printTalkative("devenv readFile: '%1%'", physicalPath ? physicalPath->string() : path.to_string());
18811881
auto s = path.readFile();
18821882
if (s.find((char) 0) != std::string::npos)
@@ -2146,7 +2146,7 @@ static RegisterPrimOp primop_readFileType({
21462146
static void prim_readDir(EvalState & state, const PosIdx pos, Value * * args, Value & v)
21472147
{
21482148
auto path = realisePath(state, pos, *args[0]);
2149-
auto physicalPath = path.getPhysicalPath();
2149+
auto physicalPath = state.getPhysicalPath(path);
21502150
printTalkative("devenv readDir: '%1%'", physicalPath ? physicalPath->string() : path.to_string());
21512151

21522152
// Retrieve directory entries for all nodes in a directory.

src/libutil/mounted-source-accessor.cc

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ namespace nix {
55
struct MountedSourceAccessorImpl : MountedSourceAccessor
66
{
77
std::map<CanonPath, ref<SourceAccessor>> mounts;
8+
// Track original paths for virtual store paths to enable proper path display
9+
std::map<CanonPath, std::optional<std::filesystem::path>> originalPaths;
810

911
MountedSourceAccessorImpl(std::map<CanonPath, ref<SourceAccessor>> _mounts)
1012
: mounts(std::move(_mounts))
@@ -72,14 +74,40 @@ struct MountedSourceAccessorImpl : MountedSourceAccessor
7274

7375
std::optional<std::filesystem::path> getPhysicalPath(const CanonPath & path) override
7476
{
77+
// First resolve which accessor and subpath we're dealing with
7578
auto [accessor, subpath] = resolve(path);
79+
80+
// Get the mount point for this accessor
81+
for (const auto & [mountPoint, mountedAccessor] : mounts) {
82+
if (mountedAccessor == accessor) {
83+
// Check if we have an original path stored for this mount
84+
auto it = originalPaths.find(mountPoint);
85+
if (it != originalPaths.end() && it->second) {
86+
// Reconstruct the full path using the original path
87+
if (subpath.isRoot()) {
88+
return *it->second;
89+
} else {
90+
return *it->second / subpath.rel();
91+
}
92+
}
93+
break;
94+
}
95+
}
96+
97+
// Fallback to the accessor's own physical path
7698
return accessor->getPhysicalPath(subpath);
7799
}
78100

79101
void mount(CanonPath mountPoint, ref<SourceAccessor> accessor) override
80102
{
81103
// FIXME: thread-safety
82-
mounts.insert_or_assign(std::move(mountPoint), accessor);
104+
mounts.insert_or_assign(mountPoint, accessor);
105+
106+
// Try to get the physical path from the accessor and store it
107+
auto physicalPath = accessor->getPhysicalPath(CanonPath::root);
108+
if (physicalPath) {
109+
originalPaths[mountPoint] = physicalPath;
110+
}
83111
}
84112

85113
std::shared_ptr<SourceAccessor> getMount(CanonPath mountPoint) override

0 commit comments

Comments
 (0)