From db7f204e4378e4fa86239976a3392bfd832e0de7 Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Sat, 22 Apr 2023 16:55:45 +0300 Subject: [PATCH] rsync: use a more robust check for relative link than "startswith" `startswith` is incorrect for paths in general, e.g. doesn't handle Windows extended-length paths and other such. The particular code here also wasn't careful about `/a/bc` not being relative to `/a/b`. --- execnet/rsync.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/execnet/rsync.py b/execnet/rsync.py index 32911e7e..1484d49d 100644 --- a/execnet/rsync.py +++ b/execnet/rsync.py @@ -173,10 +173,26 @@ def _send_directory(self, path): self._send_directory_structure(p) def _send_link_structure(self, path): - linkpoint = os.readlink(path) + sourcedir = self._sourcedir basename = path[len(self._sourcedir) + 1 :] - if linkpoint.startswith(self._sourcedir): - self._send_link("linkbase", basename, linkpoint[len(self._sourcedir) + 1 :]) + linkpoint = os.readlink(path) + # On Windows, readlink returns an extended path (//?/) for + # absolute links, but relpath doesn't like mixing extended + # and non-extended paths. So fix it up ourselves. + if ( + os.path.__name__ == "ntpath" + and linkpoint.startswith("\\\\?\\") + and not self._sourcedir.startswith("\\\\?\\") + ): + sourcedir = "\\\\?\\" + self._sourcedir + try: + relpath = os.path.relpath(linkpoint, sourcedir) + except ValueError: + relpath = None + if relpath not in (None, os.curdir, os.pardir) and not relpath.startswith( + os.pardir + os.sep + ): + self._send_link("linkbase", basename, relpath) else: # relative or absolute link, just send it self._send_link("link", basename, linkpoint)