diff options
-rw-r--r-- | sos/archive.py | 27 | ||||
-rw-r--r-- | sos/plugins/__init__.py | 20 |
2 files changed, 42 insertions, 5 deletions
diff --git a/sos/archive.py b/sos/archive.py index 483d66f4..e5819432 100644 --- a/sos/archive.py +++ b/sos/archive.py @@ -424,6 +424,29 @@ class FileCacheArchive(Archive): host_path_name = os.path.realpath(os.path.join(source_dir, source)) dest_path_name = self.dest_path(host_path_name) + def is_loop(link_name, source): + """Return ``True`` if the symbolic link ``link_name`` is part + of a file system loop, or ``False`` otherwise. + """ + link_dir = os.path.dirname(link_name) + if not os.path.isabs(source): + source = os.path.realpath(os.path.join(link_dir, source)) + link_name = os.path.realpath(link_name) + + # Simple a -> a loop + if link_name == source: + return True + + # Find indirect loops (a->b-a) by stat()ing the first step + # in the symlink chain + try: + os.stat(link_name) + except OSError as e: + if e.errno == 40: + return True + raise + return False + if not os.path.exists(dest_path_name): if os.path.islink(host_path_name): # Normalised path for the new link_name @@ -433,6 +456,10 @@ class FileCacheArchive(Archive): # Relative source path of the new link source = os.path.join(dest_dir, os.readlink(host_path_name)) source = os.path.relpath(source, dest_dir) + if is_loop(link_name, source): + self.log_debug("Link '%s' - '%s' loops: skipping..." % + (link_name, source)) + return self.log_debug("Adding link %s -> %s for link follow up" % (link_name, source)) self.add_link(source, link_name) diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py index 7d011a02..7d2a8b2d 100644 --- a/sos/plugins/__init__.py +++ b/sos/plugins/__init__.py @@ -376,6 +376,21 @@ class Plugin(object): self._log_debug("link '%s' is a directory, skipping..." % linkdest) return + self.copied_files.append({'srcpath': srcpath, + 'dstpath': dstpath, + 'symlink': "yes", + 'pointsto': linkdest}) + + # Check for indirect symlink loops by stat()ing the next step + # in the link chain. + try: + os.stat(absdest) + except OSError as e: + if e.errno == 40: + self._log_debug("link '%s' is part of a file system " + "loop, skipping target..." % dstpath) + return + # copy the symlink target translating relative targets # to absolute paths to pass to _do_copy_path. self._log_debug("normalized link target '%s' as '%s'" @@ -388,11 +403,6 @@ class Plugin(object): self._log_debug("link '%s' points to itself, skipping target..." % linkdest) - self.copied_files.append({'srcpath': srcpath, - 'dstpath': dstpath, - 'symlink': "yes", - 'pointsto': linkdest}) - def _copy_dir(self, srcpath): try: for afile in os.listdir(srcpath): |