diff --git a/certbot/certbot/_internal/account.py b/certbot/certbot/_internal/account.py index 013c4a609..b4619beba 100644 --- a/certbot/certbot/_internal/account.py +++ b/certbot/certbot/_internal/account.py @@ -325,7 +325,7 @@ class AccountFileStorage(interfaces.AccountStorage): if server_path in reused_servers: next_server_path = reused_servers[server_path] next_dir_path = link_func(next_server_path) - if os.path.islink(next_dir_path) and filesystem.realpath(next_dir_path) == dir_path: + if os.path.islink(next_dir_path) and filesystem.readlink(next_dir_path) == dir_path: possible_next_link = True server_path = next_server_path dir_path = next_dir_path @@ -333,7 +333,7 @@ class AccountFileStorage(interfaces.AccountStorage): # if there's not a next one up to delete, then delete me # and whatever I link to while os.path.islink(dir_path): - target = filesystem.realpath(dir_path) + target = filesystem.readlink(dir_path) os.unlink(dir_path) dir_path = target diff --git a/certbot/certbot/_internal/storage.py b/certbot/certbot/_internal/storage.py index 5351a4f8a..7919a168f 100644 --- a/certbot/certbot/_internal/storage.py +++ b/certbot/certbot/_internal/storage.py @@ -214,7 +214,7 @@ def get_link_target(link): """ try: - target = filesystem.realpath(link) + target = filesystem.readlink(link) except OSError: raise errors.CertStorageError( "Expected {0} to be a symlink".format(link)) @@ -666,7 +666,7 @@ class RenewableCert(interfaces.RenewableCert): current_link = getattr(self, kind) if os.path.lexists(current_link): os.unlink(current_link) - os.symlink(filesystem.realpath(previous_link), current_link) + os.symlink(filesystem.readlink(previous_link), current_link) for _, link in previous_symlinks: if os.path.exists(link): @@ -847,7 +847,7 @@ class RenewableCert(interfaces.RenewableCert): link = getattr(self, kind) filename = "{0}{1}.pem".format(kind, version) # Relative rather than absolute target directory - target_directory = os.path.dirname(filesystem.realpath(link)) + target_directory = os.path.dirname(filesystem.readlink(link)) # TODO: it could be safer to make the link first under a temporary # filename, then unlink the old link, then rename the new link # to the old link; this ensures that this process is able to @@ -1122,7 +1122,7 @@ class RenewableCert(interfaces.RenewableCert): # The behavior below keeps the prior key by creating a new # symlink to the old key or the target of the old key symlink. if os.path.islink(old_privkey): - old_privkey = filesystem.realpath(old_privkey) + old_privkey = filesystem.readlink(old_privkey) else: old_privkey = "privkey{0}.pem".format(prior_version) logger.debug("Writing symlink to old private key, %s.", old_privkey) diff --git a/certbot/certbot/compat/filesystem.py b/certbot/certbot/compat/filesystem.py index 09c15f80b..3e73ce792 100644 --- a/certbot/certbot/compat/filesystem.py +++ b/certbot/certbot/compat/filesystem.py @@ -385,8 +385,29 @@ def realpath(file_path): return os.path.abspath(file_path) +def readlink(link_path): + # type: (str) -> str + """ + Return a string representing the path to which the symbolic link points. + + :param str link_path: The symlink path to resolve + :return: The path the symlink points to + :returns: str + """ + path = os.readlink(link_path) + + # Max length of a normal path is 260 characters on Windows, including the non printable + # termination character "". The termination character is not included in Python + # strings, giving a max length of 259 characters, + 4 characters for the extended form + # prefix, to an effective max length of the studied path of 263 characters. + if not POSIX_MODE and path.startswith('\\\\?\\') and len(path) < 264: + path = path[4:] + + return path + + # On Windows is_executable run from an unprivileged shell may claim that a path is -# executable when it is excutable only if run from a privileged shell. This result +# executable when it is executable only if run from a privileged shell. This result # is due to the fact that GetEffectiveRightsFromAcl calculate effective rights # without taking into consideration if the target user has currently required the # elevated privileges or not. However this is not a problem since certbot always diff --git a/certbot/tests/cert_manager_test.py b/certbot/tests/cert_manager_test.py index cb8ecc5a1..b26c1f624 100644 --- a/certbot/tests/cert_manager_test.py +++ b/certbot/tests/cert_manager_test.py @@ -99,7 +99,7 @@ class UpdateLiveSymlinksTest(BaseCertManagerTest): for kind in ALL_FOUR: os.chdir(os.path.dirname(self.config_files[domain][kind])) self.assertEqual( - filesystem.realpath(self.config_files[domain][kind]), + filesystem.realpath(filesystem.readlink(self.config_files[domain][kind])), filesystem.realpath(archive_paths[domain][kind])) finally: os.chdir(prev_dir) diff --git a/certbot/tests/storage_test.py b/certbot/tests/storage_test.py index 0ea38df54..9ae26532f 100644 --- a/certbot/tests/storage_test.py +++ b/certbot/tests/storage_test.py @@ -330,7 +330,7 @@ class RenewableCertTests(BaseRenewableCertTest): self.test_rc._update_link_to("chain", 3000) # However, current_version doesn't allow querying the resulting # version (because it's a broken link). - self.assertEqual(os.path.basename(filesystem.realpath(self.test_rc.chain)), + self.assertEqual(os.path.basename(filesystem.readlink(self.test_rc.chain)), "chain3000.pem") def test_version(self): @@ -514,7 +514,7 @@ class RenewableCertTests(BaseRenewableCertTest): # privkey. for i in (6, 7, 8): self.assertTrue(os.path.islink(self.test_rc.version("privkey", i))) - self.assertEqual("privkey3.pem", os.path.basename(filesystem.realpath( + self.assertEqual("privkey3.pem", os.path.basename(filesystem.readlink( self.test_rc.version("privkey", i)))) for kind in ALL_FOUR: