From 861f0d603e1cd487ef020464aa0d42984cff53e4 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 7 Oct 2024 14:38:18 +0200 Subject: [PATCH] SftpFileEntry.Path: don't append a directory separator when directory is '/'. --- src/Tmds.Ssh/RemotePath.cs | 2 +- src/Tmds.Ssh/SftpChannel.cs | 2 +- src/Tmds.Ssh/SftpFileEntry.cs | 15 +++++++++++---- test/Tmds.Ssh.Tests/SftpClientTests.cs | 17 +++++++++++++++++ 4 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/Tmds.Ssh/RemotePath.cs b/src/Tmds.Ssh/RemotePath.cs index 1fad022..c1d1eac 100644 --- a/src/Tmds.Ssh/RemotePath.cs +++ b/src/Tmds.Ssh/RemotePath.cs @@ -30,7 +30,7 @@ public static ReadOnlySpan TrimEndingDirectorySeparators(ReadOnlySpan path) => + public static bool EndsInDirectorySeparator(ReadOnlySpan path) => path.Length > 0 && IsDirectorySeparator(path[path.Length - 1]); private static bool IsDirectorySeparator(char c) diff --git a/src/Tmds.Ssh/SftpChannel.cs b/src/Tmds.Ssh/SftpChannel.cs index 0d2ee94..1a17242 100644 --- a/src/Tmds.Ssh/SftpChannel.cs +++ b/src/Tmds.Ssh/SftpChannel.cs @@ -620,7 +620,7 @@ public async ValueTask DownloadDirectoryEntriesAsync(string remoteDirPath, strin DownloadEntriesOptions.ReplaceCharacters replaceInvalidCharacters = options.ReplaceInvalidCharacters ?? throw new ArgumentNullException(nameof(options.ReplaceInvalidCharacters)); int trimRemoteDirectory = remoteDirPath.Length; - if (!LocalPath.EndsInDirectorySeparator(remoteDirPath)) + if (remoteDirPath.Length != 0 && !RemotePath.EndsInDirectorySeparator(remoteDirPath)) { trimRemoteDirectory++; } diff --git a/src/Tmds.Ssh/SftpFileEntry.cs b/src/Tmds.Ssh/SftpFileEntry.cs index 388ba3b..8ae165e 100644 --- a/src/Tmds.Ssh/SftpFileEntry.cs +++ b/src/Tmds.Ssh/SftpFileEntry.cs @@ -39,11 +39,18 @@ public ReadOnlySpan Path { if (_pathLength == 0) { - _directoryPath.AsSpan().CopyTo(_pathBuffer); int length = _directoryPath.Length; - - _pathBuffer[length] = RemotePath.DirectorySeparatorChar; - length++; + if (length > 0) + { + _directoryPath.AsSpan().CopyTo(_pathBuffer); + + // Append '/' unless _directorPath == '/'. + if (length != 1 || _directoryPath[0] != RemotePath.DirectorySeparatorChar) + { + _pathBuffer[length] = RemotePath.DirectorySeparatorChar; + length++; + } + } ReadOnlySpan filename = FileName; filename.CopyTo(_pathBuffer.AsSpan(length)); diff --git a/test/Tmds.Ssh.Tests/SftpClientTests.cs b/test/Tmds.Ssh.Tests/SftpClientTests.cs index f6f98ff..b87139a 100644 --- a/test/Tmds.Ssh.Tests/SftpClientTests.cs +++ b/test/Tmds.Ssh.Tests/SftpClientTests.cs @@ -520,6 +520,23 @@ public async Task EnumerateDirectoryFollowDirectoryLinks(bool follow) } } + [Fact] + public async Task EnumerateRootDirectory() + { + using var sftpClient = await _sshServer.CreateSftpClientAsync(); + + var entries = await sftpClient.GetDirectoryEntriesAsync("/").ToListAsync(); + Assert.NotEmpty(entries); + + foreach (var entry in entries) + { + string path = entry.Path; + Assert.True(path.Length > 2); + Assert.StartsWith("/", path); + Assert.False(path.StartsWith("//")); + } + } + [Fact] public void DefaultEnumerationOptions() {