aboutsummaryrefslogtreecommitdiff
path: root/src/libutil/source-path.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/libutil/source-path.cc')
-rw-r--r--src/libutil/source-path.cc93
1 files changed, 93 insertions, 0 deletions
diff --git a/src/libutil/source-path.cc b/src/libutil/source-path.cc
new file mode 100644
index 000000000..b6563ee90
--- /dev/null
+++ b/src/libutil/source-path.cc
@@ -0,0 +1,93 @@
+#include "source-path.hh"
+
+namespace nix {
+
+std::ostream & operator << (std::ostream & str, const SourcePath & path)
+{
+ str << path.to_string();
+ return str;
+}
+
+std::string_view SourcePath::baseName() const
+{
+ return path.baseName().value_or("source");
+}
+
+SourcePath SourcePath::parent() const
+{
+ auto p = path.parent();
+ assert(p);
+ return std::move(*p);
+}
+
+InputAccessor::Stat SourcePath::lstat() const
+{
+ auto st = nix::lstat(path.abs());
+ return InputAccessor::Stat {
+ .type =
+ S_ISREG(st.st_mode) ? InputAccessor::tRegular :
+ S_ISDIR(st.st_mode) ? InputAccessor::tDirectory :
+ S_ISLNK(st.st_mode) ? InputAccessor::tSymlink :
+ InputAccessor::tMisc,
+ .isExecutable = S_ISREG(st.st_mode) && st.st_mode & S_IXUSR
+ };
+}
+
+std::optional<InputAccessor::Stat> SourcePath::maybeLstat() const
+{
+ // FIXME: merge these into one operation.
+ if (!pathExists())
+ return {};
+ return lstat();
+}
+
+InputAccessor::DirEntries SourcePath::readDirectory() const
+{
+ InputAccessor::DirEntries res;
+ for (auto & entry : nix::readDirectory(path.abs())) {
+ std::optional<InputAccessor::Type> type;
+ switch (entry.type) {
+ case DT_REG: type = InputAccessor::Type::tRegular; break;
+ case DT_LNK: type = InputAccessor::Type::tSymlink; break;
+ case DT_DIR: type = InputAccessor::Type::tDirectory; break;
+ }
+ res.emplace(entry.name, type);
+ }
+ return res;
+}
+
+SourcePath SourcePath::resolveSymlinks() const
+{
+ SourcePath res(CanonPath::root);
+
+ int linksAllowed = 1024;
+
+ std::list<std::string> todo;
+ for (auto & c : path)
+ todo.push_back(std::string(c));
+
+ while (!todo.empty()) {
+ auto c = *todo.begin();
+ todo.pop_front();
+ if (c == "" || c == ".")
+ ;
+ else if (c == "..")
+ res.path.pop();
+ else {
+ res.path.push(c);
+ if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) {
+ if (!linksAllowed--)
+ throw Error("infinite symlink recursion in path '%s'", path);
+ auto target = res.readLink();
+ res.path.pop();
+ if (hasPrefix(target, "/"))
+ res.path = CanonPath::root;
+ todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
+ }
+ }
+ }
+
+ return res;
+}
+
+}