Renci SSH.NET:可以创建符号链接,但删除符号链接失败,为什么?

时间:2016-05-19 05:02:14

标签: c# .net ssh sftp ssh.net

我目前正在尝试使用Renci SSH.NET将文件复制到使用SFTP的Unix服务器,此外,我想创建一个指向我复制的文件的符号链接。这基本上就是我的代码的样子,请注意变量sftpSftpClient的工作实例:

string symlinkSource = @"/msyerver/SymSource/Test001"; //source to link to, exists
string newPath = @"/msyerver/somedirectory/Test001"; //place where the symlink should be created
sftp.SymbolicLink(symlinkSource, newPath); //link newPath to symlinkSource, works!
sftp.Delete(newPath); //fails with exception!

问题是:如何正确删除符号链接?请注意:我只想删除文件夹Test001的链接,而不是删除引用的文件夹本身。为什么这不起作用?遗憾的是,SSH.NET没有抛出有意义的异常,我得到的唯一文本是“失败”,由于这内部通过一些“请求”机制工作,我无法调试问题的确切来源。

当我查看异常时,我发现: Data: {System.Collections.ListDictionaryInternal}

很明显,看起来SSH.NET正试图删除链接文件夹。我想要的是删除符号链接本身,而不是后面的文件夹。

3 个答案:

答案 0 :(得分:1)

实际上,SftpClient.Delete(和SftpClient.DeleteFile)的实现方式,它们无法删除符号链接。他们首先使用路径调用SftpSession.GetCanonicalPath,解析链接的内容。所以你实际上是试图删除链接目标而不是链接本身,由于某种原因失败了。

无法使用SSH.NET API删除链接本身。

虽然有一些反思黑客攻击你可以绕过SftpSession.GetCanonicalPath电话:

public static class SftpClientExtensions
{
    public static void DeleteLink(this SftpClient client, string path)
    {
        Type sftpClientType = client.GetType();
        FieldInfo sftpSessionField = sftpClientType.GetField("_sftpSession", BindingFlags.NonPublic | BindingFlags.Instance);
        object sftpSession = sftpSessionField.GetValue(client);
        Type sftpSessionType = sftpSession.GetType();
        MethodInfo requestRemoveMethod = sftpSessionType.GetMethod("RequestRemove", BindingFlags.NonPublic | BindingFlags.Instance);
        requestRemoveMethod.Invoke(sftpSession, new object[] { path });
    }
}

使用上述扩展方法,您现在可以使用:

sftp.DeleteLink(newPath);

更好的方法是获取SSH.NET源代码的副本并将该方法直接添加到SftpClient类。并向SSH.NET project提交请求以支持删除链接。

答案 1 :(得分:1)

从列表目录中获取文件项,而不是反射 hack。

SftpFile[] listDirectory = this.sftpClient.ListDirectory(path.Substring(0, Math.Max(0, path.LastIndexOf('/')))).ToArray();
SftpFile sftpFile = listDirectory.FirstOrDefault(f => f.FullName.Equals(path));
sftpFile?.Delete();

考虑全名比较

答案 2 :(得分:0)

此方法删除文件夹中的所有文件,包括符号链接

public void DeleteDirectory(string path)
    {
        using (var sftp = new SftpClient(ost, Settings.Instance.Deployment_user, Settings.Instance.Deployment_password))
        {
            sftp.Connect();
            foreach (SftpFile file in sftp.ListDirectory(path))
            {
                if ((file.Name != ".") && (file.Name != ".."))
                {
                    if (file.IsDirectory)
                    {
                        DeleteDirectory(file.FullName);
                    }
                    else
                    {                          
                        file.Delete();
                    }
                }
            }

            sftp.DeleteDirectory(path);
            sftp.Disconnect();
        }
    }