aboutsummaryrefslogtreecommitdiff
path: root/src/libfetchers
diff options
context:
space:
mode:
authorEelco Dolstra <edolstra@gmail.com>2023-04-06 13:15:50 +0200
committerEelco Dolstra <edolstra@gmail.com>2023-04-06 13:15:50 +0200
commit94812cca98fbb157e5f64a15a85a2b852d289feb (patch)
tree2f02c31fc42c7286f3c35dfd3ff1a88f235ab65b /src/libfetchers
parent5256ba6d87403f2b58ec4586c26d8fb14027252f (diff)
Backport SourcePath from the lazy-trees branch
This introduces the SourcePath type from lazy-trees as an abstraction for accessing files from inputs that may not be materialized in the real filesystem (e.g. Git repositories). Currently, however, it's just a wrapper around CanonPath, so it shouldn't change any behaviour. (On lazy-trees, SourcePath is a <InputAccessor, CanonPath> tuple.)
Diffstat (limited to 'src/libfetchers')
-rw-r--r--src/libfetchers/input-accessor.cc100
-rw-r--r--src/libfetchers/input-accessor.hh149
2 files changed, 249 insertions, 0 deletions
diff --git a/src/libfetchers/input-accessor.cc b/src/libfetchers/input-accessor.cc
new file mode 100644
index 000000000..f9909c218
--- /dev/null
+++ b/src/libfetchers/input-accessor.cc
@@ -0,0 +1,100 @@
+#include "input-accessor.hh"
+#include "store-api.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;
+}
+
+StorePath SourcePath::fetchToStore(
+ ref<Store> store,
+ std::string_view name,
+ PathFilter * filter,
+ RepairFlag repair) const
+{
+ return
+ settings.readOnlyMode
+ ? store->computeStorePathForPath(name, path.abs(), FileIngestionMethod::Recursive, htSHA256, filter ? *filter : defaultPathFilter).first
+ : store->addToStore(name, path.abs(), FileIngestionMethod::Recursive, htSHA256, filter ? *filter : defaultPathFilter, repair);
+}
+
+SourcePath SourcePath::resolveSymlinks() const
+{
+ SourcePath res(CanonPath::root);
+
+ int linksAllowed = 1024;
+
+ for (auto & component : path) {
+ res.path.push(component);
+ while (true) {
+ if (auto st = res.maybeLstat()) {
+ if (!linksAllowed--)
+ throw Error("infinite symlink recursion in path '%s'", path);
+ if (st->type != InputAccessor::tSymlink) break;
+ auto target = res.readLink();
+ if (hasPrefix(target, "/"))
+ res = CanonPath(target);
+ else {
+ res.path.pop();
+ res.path.extend(CanonPath(target));
+ }
+ } else
+ break;
+ }
+ }
+
+ return res;
+}
+
+}
diff --git a/src/libfetchers/input-accessor.hh b/src/libfetchers/input-accessor.hh
new file mode 100644
index 000000000..23c510d4d
--- /dev/null
+++ b/src/libfetchers/input-accessor.hh
@@ -0,0 +1,149 @@
+#pragma once
+
+#include "ref.hh"
+#include "types.hh"
+#include "archive.hh"
+#include "canon-path.hh"
+#include "repair-flag.hh"
+
+namespace nix {
+
+class StorePath;
+class Store;
+
+struct InputAccessor
+{
+ enum Type { tRegular, tSymlink, tDirectory, tMisc };
+
+ struct Stat
+ {
+ Type type = tMisc;
+ //uint64_t fileSize = 0; // regular files only
+ bool isExecutable = false; // regular files only
+ };
+
+ typedef std::optional<Type> DirEntry;
+
+ typedef std::map<std::string, DirEntry> DirEntries;
+};
+
+/**
+ * An abstraction for accessing source files during
+ * evaluation. Currently, it's just a wrapper around `CanonPath` that
+ * accesses files in the regular filesystem, but in the future it will
+ * support fetching files in other ways.
+ */
+struct SourcePath
+{
+ CanonPath path;
+
+ SourcePath(CanonPath path)
+ : path(std::move(path))
+ { }
+
+ std::string_view baseName() const;
+
+ /**
+ * Construct the parent of this `SourcePath`. Aborts if `this`
+ * denotes the root.
+ */
+ SourcePath parent() const;
+
+ /**
+ * If this `SourcePath` denotes a regular file (not a symlink),
+ * return its contents; otherwise throw an error.
+ */
+ std::string readFile() const
+ { return nix::readFile(path.abs()); }
+
+ /**
+ * Return whether this `SourcePath` denotes a file (of any type)
+ * that exists
+ */
+ bool pathExists() const
+ { return nix::pathExists(path.abs()); }
+
+ /**
+ * Return stats about this `SourcePath`, or throw an exception if
+ * it doesn't exist.
+ */
+ InputAccessor::Stat lstat() const;
+
+ /**
+ * Return stats about this `SourcePath`, or std::nullopt if it
+ * doesn't exist.
+ */
+ std::optional<InputAccessor::Stat> maybeLstat() const;
+
+ /**
+ * If this `SourcePath` denotes a directory (not a symlink),
+ * return its directory entries; otherwise throw an error.
+ */
+ InputAccessor::DirEntries readDirectory() const;
+
+ /**
+ * If this `SourcePath` denotes a symlink, return its target;
+ * otherwise throw an error.
+ */
+ std::string readLink() const
+ { return nix::readLink(path.abs()); }
+
+ /**
+ * Dump this `SourcePath` to `sink` as a NAR archive.
+ */
+ void dumpPath(
+ Sink & sink,
+ PathFilter & filter = defaultPathFilter) const
+ { return nix::dumpPath(path.abs(), sink, filter); }
+
+ /**
+ * Copy this `SourcePath` to the Nix store.
+ */
+ StorePath fetchToStore(
+ ref<Store> store,
+ std::string_view name = "source",
+ PathFilter * filter = nullptr,
+ RepairFlag repair = NoRepair) const;
+
+ /**
+ * Return the location of this path in the "real" filesystem, if
+ * it has a physical location.
+ */
+ std::optional<CanonPath> getPhysicalPath() const
+ { return path; }
+
+ std::string to_string() const
+ { return path.abs(); }
+
+ SourcePath operator + (const CanonPath & x) const
+ { return {path + x}; }
+
+ SourcePath operator + (std::string_view c) const
+ { return {path + c}; }
+
+ bool operator == (const SourcePath & x) const
+ {
+ return path == x.path;
+ }
+
+ bool operator != (const SourcePath & x) const
+ {
+ return path != x.path;
+ }
+
+ bool operator < (const SourcePath & x) const
+ {
+ return path < x.path;
+ }
+
+ /**
+ * Resolve any symlinks in this `SourcePath` (including its
+ * parents). The result is a `SourcePath` in which no element is a
+ * symlink.
+ */
+ SourcePath resolveSymlinks() const;
+};
+
+std::ostream & operator << (std::ostream & str, const SourcePath & path);
+
+}