ScpClient: allow disabling the -d flag (#1751)

* ScpClient: allow disabling the -d flag

fixes #1746

* Apply suggestion from @Rob-Hague

---------

Co-authored-by: Rob Hague <rob.hague00@gmail.com>
This commit is contained in:
mus65
2026-03-22 10:26:37 +01:00
committed by GitHub
parent 0ff2c50765
commit 25a931cafe
2 changed files with 83 additions and 5 deletions
+24 -5
View File
@@ -121,6 +121,25 @@ namespace Renci.SshNet
}
}
/// <summary>
/// Gets or sets a value indicating whether the "-d" flag should be passed to the scp process on the server
/// when uploading files. Defaults to <see langword="true"/>.
/// </summary>
/// <remarks>
/// The "-d" flag is an undocumented flag that ensures that the target is actually a directory. However,
/// some scp implementations (like Cisco) do not support this flag and will fail.
/// You can set this to <see langword="false"/> to work around this.
/// </remarks>
public bool UseDirectoryFlag { get; set; } = true;
private string EnsureIsDirectoryArg
{
get
{
return UseDirectoryFlag ? "-d" : string.Empty;
}
}
/// <summary>
/// Occurs when downloading file.
/// </summary>
@@ -258,9 +277,9 @@ namespace Renci.SshNet
channel.Closed += (sender, e) => input.Dispose();
channel.Open();
// Pass only the directory part of the path to the server, and use the (hidden) -d option to signal
// Pass only the directory part of the path to the server, and optionally use the (hidden) -d option to signal
// that we expect the target to be a directory.
if (!channel.SendExecRequest(string.Format("scp -t -d {0}", _remotePathTransformation.Transform(posixPath.Directory))))
if (!channel.SendExecRequest($"scp -t {EnsureIsDirectoryArg} {_remotePathTransformation.Transform(posixPath.Directory)}"))
{
throw SecureExecutionRequestRejectedException();
}
@@ -301,9 +320,9 @@ namespace Renci.SshNet
channel.Closed += (sender, e) => input.Dispose();
channel.Open();
// Pass only the directory part of the path to the server, and use the (hidden) -d option to signal
// Pass only the directory part of the path to the server, and optionally use the (hidden) -d option to signal
// that we expect the target to be a directory.
if (!channel.SendExecRequest($"scp -t -d {_remotePathTransformation.Transform(posixPath.Directory)}"))
if (!channel.SendExecRequest($"scp -t {EnsureIsDirectoryArg} {_remotePathTransformation.Transform(posixPath.Directory)}"))
{
throw SecureExecutionRequestRejectedException();
}
@@ -352,7 +371,7 @@ namespace Renci.SshNet
// -r copy directories recursively
// -d expect path to be a directory
// -t copy to remote
if (!channel.SendExecRequest($"scp -r -p -d -t {_remotePathTransformation.Transform(path)}"))
if (!channel.SendExecRequest($"scp -r -p {EnsureIsDirectoryArg} -t {_remotePathTransformation.Transform(path)}"))
{
throw SecureExecutionRequestRejectedException();
}
@@ -2098,5 +2098,64 @@ namespace Renci.SshNet.IntegrationTests
return path1 + "/" + path2;
}
[TestMethod]
public async Task UploadWithUseDirectoryFlagFalse()
{
string remoteDirectory = "/home/sshnet/usedirectoryflagfalsetest";
// remote cleanup
using (var sftpClient = new SftpClient(_connectionInfoFactory.Create()))
{
await sftpClient.ConnectAsync(CancellationToken.None);
if (await sftpClient.ExistsAsync(remoteDirectory))
{
await sftpClient.DeleteDirectoryAsync(remoteDirectory);
}
await sftpClient.CreateDirectoryAsync(remoteDirectory);
}
using (var client = new ScpClient(_connectionInfoFactory.Create()))
{
client.UseDirectoryFlag = false;
await client.ConnectAsync(CancellationToken.None);
int tempFileSize = 1024;
string tempFilePath = CreateTempFile(tempFileSize);
MemoryStream downloadedStream = new();
// FileInfo overload
client.Upload(new FileInfo(tempFilePath), $"{remoteDirectory}/file1");
client.Download($"{remoteDirectory}/file1", downloadedStream);
Assert.AreEqual(tempFileSize, downloadedStream.Length);
// Stream overload
downloadedStream = new();
using (Stream stream = File.OpenRead(tempFilePath))
{
client.Upload(stream, $"{remoteDirectory}/file2");
client.Download($"{remoteDirectory}/file2", downloadedStream);
Assert.AreEqual(tempFileSize, downloadedStream.Length);
}
// DirectoryInfo overload
downloadedStream = new();
string tempDir = Path.Combine(Path.GetTempPath(), "SSH.NET_UploadWithUseDirectoryFlagFalseTest");
if (Directory.Exists(tempDir))
{
Directory.Delete(tempDir, true);
}
Directory.CreateDirectory(tempDir);
File.Move(tempFilePath, $"{tempDir}/file3");
client.Upload(new DirectoryInfo(tempDir), remoteDirectory);
client.Download($"{remoteDirectory}/file3", downloadedStream);
Assert.AreEqual(tempFileSize, downloadedStream.Length);
}
}
}
}