【问题标题】:Renci SSH.NET: can create symlink, but deleting symlink fails, why?Renci SSH.NET:可以创建符号链接,但删除符号链接失败,为什么?
【发布时间】:2016-09-15 19:18:44
【问题描述】:

我目前正在尝试使用 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 似乎正在尝试删除链接文件夹。我想要的是删除符号链接本身,而不是后面的文件夹。

【问题讨论】:

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


    【解决方案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提出请求,支持删除链接。

    【讨论】:

    • 由于这里涉及文件删除,我建议不要使用黑客攻击,而是使用常规 ssh 命令删除链接(使用相同的库,而不是 sftp)
    • 这也是一个 hack,因为 1) 你不能保证 SFTP 和 shell 使用相同的文件系统/路径语法。例如,一个可以被 chroot,而另一个不能。 2) 你不能保证你有一个 shell 访问权限。 3) 虽然 SFTP 在所有系统上的工作方式都是一样的,但不同的系统使用不同的 shell/命令(系统甚至可能不是 *nix 系统,您似乎认为是这样)。
    【解决方案2】:

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

    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();
            }
        }
    

    【讨论】:

      【解决方案3】:

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

      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();
      

      考虑全名比较

      【讨论】:

        猜你喜欢
        • 2016-10-14
        • 2013-07-11
        • 2013-11-07
        • 2017-06-10
        • 1970-01-01
        • 2019-03-31
        • 1970-01-01
        • 2021-12-19
        相关资源
        最近更新 更多