aboutsummaryrefslogtreecommitdiff
path: root/libbuild2
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2')
-rw-r--r--libbuild2/dist/operation.cxx47
1 files changed, 44 insertions, 3 deletions
diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx
index 7a85119..a799cfc 100644
--- a/libbuild2/dist/operation.cxx
+++ b/libbuild2/dist/operation.cxx
@@ -105,13 +105,25 @@ namespace build2
// Recursively traverse an src_root subdirectory entering/collecting the
// contained files and file symlinks as the file targets and skipping
// entries that start with a dot. Follow directory symlinks (preserving
- // their names) and fail on dangling symlinks.
+ // their names) and fail on dangling symlinks. Also detect directory
+ // symlink cycles.
//
+ struct subdir
+ {
+ const subdir* prev;
+ const dir_path& dir;
+ };
+
static void
- add_subdir (const scope& rs, const dir_path& sd, action_targets& files)
+ add_subdir (const scope& rs,
+ const dir_path& sd,
+ action_targets& files,
+ const subdir* prev = nullptr)
{
dir_path d (rs.src_path () / sd);
+ const subdir next {prev, d};
+
try
{
for (const dir_entry& e: dir_iterator (d, false /* ignore_dangling */))
@@ -122,7 +134,36 @@ namespace build2
try
{
if (e.type () == entry_type::directory) // Can throw.
- add_subdir (rs, sd / path_cast<dir_path> (n), files);
+ {
+ // If this is a symlink, check that it doesn't cause a cycle.
+ //
+ if (e.ltype () == entry_type::symlink)
+ {
+ // Note that the resulting path will be absolute and
+ // normalized.
+ //
+ dir_path ld (d / path_cast<dir_path> (n));
+ dir_path td (path_cast<dir_path> (followsymlink (ld)));
+
+ const subdir* s (&next);
+ for (; s != nullptr; s = s->prev)
+ {
+ if (s->dir == td)
+ {
+ if (verb)
+ warn << "directory cycle caused by symlink " << ld <<
+ info << "symlink target " << td;
+
+ break;
+ }
+ }
+
+ if (s != nullptr)
+ break;
+ }
+
+ add_subdir (rs, sd / path_cast<dir_path> (n), files, &next);
+ }
else
files.push_back (add_target<file> (rs, sd / n, true, true));
}