浏览代码

Add Support for SFTP posix-rename@openssh.com and statvfs@openssh.com requests
Partially apply 12705 patch

olegkap_cp 13 年之前
父节点
当前提交
e738ebfb1e

+ 13 - 1
Renci.SshClient/Renci.SshNet.NET35/Renci.SshNet.NET35.csproj

@@ -636,6 +636,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Flags.cs">
       <Link>Sftp\Flags.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Requests\PosixRenameRequest.cs">
+      <Link>Sftp\Requests\PosixRenameRequest.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\Requests\SftpCloseRequest.cs">
       <Link>Sftp\Requests\SftpCloseRequest.cs</Link>
     </Compile>
@@ -696,6 +699,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Requests\SftpWriteRequest.cs">
       <Link>Sftp\Requests\SftpWriteRequest.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Requests\StatVfsRequest.cs">
+      <Link>Sftp\Requests\StatVfsRequest.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\Responses\SftpAttrsResponse.cs">
       <Link>Sftp\Responses\SftpAttrsResponse.cs</Link>
     </Compile>
@@ -720,6 +726,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Responses\SftpVersionResponse.cs">
       <Link>Sftp\Responses\SftpVersionResponse.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Responses\StatVfsResponse.cs">
+      <Link>Sftp\Responses\StatVfsResponse.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpDataMessage.cs">
       <Link>Sftp\SftpDataMessage.cs</Link>
     </Compile>
@@ -735,6 +744,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileStream.cs">
       <Link>Sftp\SftpFileStream.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\SftpFileSystemInformation.cs">
+      <Link>Sftp\SftpFileSystemInformation.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpListDirectoryAsyncResult.cs">
       <Link>Sftp\SftpListDirectoryAsyncResult.cs</Link>
     </Compile>
@@ -787,7 +799,7 @@
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
   <ProjectExtensions>
     <VisualStudio>
-      <UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
+      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 13 - 1
Renci.SshClient/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj

@@ -603,6 +603,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Flags.cs">
       <Link>Sftp\Flags.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Requests\PosixRenameRequest.cs">
+      <Link>Sftp\Requests\PosixRenameRequest.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\Requests\SftpCloseRequest.cs">
       <Link>Sftp\Requests\SftpCloseRequest.cs</Link>
     </Compile>
@@ -663,6 +666,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Requests\SftpWriteRequest.cs">
       <Link>Sftp\Requests\SftpWriteRequest.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Requests\StatVfsRequest.cs">
+      <Link>Sftp\Requests\StatVfsRequest.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\Responses\SftpAttrsResponse.cs">
       <Link>Sftp\Responses\SftpAttrsResponse.cs</Link>
     </Compile>
@@ -687,6 +693,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Responses\SftpVersionResponse.cs">
       <Link>Sftp\Responses\SftpVersionResponse.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Responses\StatVfsResponse.cs">
+      <Link>Sftp\Responses\StatVfsResponse.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpDataMessage.cs">
       <Link>Sftp\SftpDataMessage.cs</Link>
     </Compile>
@@ -702,6 +711,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileStream.cs">
       <Link>Sftp\SftpFileStream.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\SftpFileSystemInformation.cs">
+      <Link>Sftp\SftpFileSystemInformation.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpListDirectoryAsyncResult.cs">
       <Link>Sftp\SftpListDirectoryAsyncResult.cs</Link>
     </Compile>
@@ -756,7 +768,7 @@
       <FlavorProperties GUID="{A1591282-1198-4647-A2B1-27E5FF5F6F3B}">
         <SilverlightProjectProperties />
       </FlavorProperties>
-      <UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
+      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 13 - 1
Renci.SshClient/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj

@@ -607,6 +607,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Flags.cs">
       <Link>Sftp\Flags.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Requests\PosixRenameRequest.cs">
+      <Link>Sftp\Requests\PosixRenameRequest.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\Requests\SftpCloseRequest.cs">
       <Link>Sftp\Requests\SftpCloseRequest.cs</Link>
     </Compile>
@@ -667,6 +670,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Requests\SftpWriteRequest.cs">
       <Link>Sftp\Requests\SftpWriteRequest.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Requests\StatVfsRequest.cs">
+      <Link>Sftp\Requests\StatVfsRequest.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\Responses\SftpAttrsResponse.cs">
       <Link>Sftp\Responses\SftpAttrsResponse.cs</Link>
     </Compile>
@@ -691,6 +697,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\Responses\SftpVersionResponse.cs">
       <Link>Sftp\Responses\SftpVersionResponse.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\Responses\StatVfsResponse.cs">
+      <Link>Sftp\Responses\StatVfsResponse.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpDataMessage.cs">
       <Link>Sftp\SftpDataMessage.cs</Link>
     </Compile>
@@ -706,6 +715,9 @@
     <Compile Include="..\Renci.SshNet\Sftp\SftpFileStream.cs">
       <Link>Sftp\SftpFileStream.cs</Link>
     </Compile>
+    <Compile Include="..\Renci.SshNet\Sftp\SftpFileSystemInformation.cs">
+      <Link>Sftp\SftpFileSystemInformation.cs</Link>
+    </Compile>
     <Compile Include="..\Renci.SshNet\Sftp\SftpListDirectoryAsyncResult.cs">
       <Link>Sftp\SftpListDirectoryAsyncResult.cs</Link>
     </Compile>
@@ -752,7 +764,7 @@
   <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
   <ProjectExtensions>
     <VisualStudio>
-      <UserProperties ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" />
+      <UserProperties ProjectLinkReference="2f5f8c90-0bd1-424f-997c-7bc6280919d1" ProjectLinkerExcludeFilter="\\?desktop(\\.*)?$;\\?silverlight(\\.*)?$;\.desktop;\.silverlight;\.xaml;^service references(\\.*)?$;\.clientconfig;^web references(\\.*)?$" />
     </VisualStudio>
   </ProjectExtensions>
   <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 

+ 0 - 1
Renci.SshClient/Renci.SshNet/PrivateKeyAuthenticationMethod.cs

@@ -1,7 +1,6 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 using System.Collections.ObjectModel;
 using Renci.SshNet.Messages.Authentication;
 using Renci.SshNet.Messages;

+ 12 - 0
Renci.SshClient/Renci.SshNet/Renci.SshNet.csproj

@@ -357,6 +357,9 @@
     </Compile>
     <Compile Include="SftpClient.NET40.cs" />
     <Compile Include="Sftp\Flags.cs" />
+    <Compile Include="Sftp\Requests\PosixRenameRequest.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="Sftp\Requests\SftpCloseRequest.cs" />
     <Compile Include="Sftp\Requests\SftpFSetStatRequest.cs" />
     <Compile Include="Sftp\Requests\SftpFStatRequest.cs" />
@@ -377,6 +380,9 @@
     <Compile Include="Sftp\Requests\SftpStatRequest.cs" />
     <Compile Include="Sftp\Requests\SftpSymLinkRequest.cs" />
     <Compile Include="Sftp\Requests\SftpWriteRequest.cs" />
+    <Compile Include="Sftp\Requests\StatVfsRequest.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="Sftp\Responses\SftpAttrsResponse.cs" />
     <Compile Include="Sftp\Responses\SftpDataResponse.cs" />
     <Compile Include="Sftp\Responses\SftpExtendedReplyResponse.cs" />
@@ -385,6 +391,9 @@
     <Compile Include="Sftp\Responses\SftpResponse.cs" />
     <Compile Include="Sftp\Responses\SftpStatusResponse.cs" />
     <Compile Include="Sftp\Responses\SftpVersionResponse.cs" />
+    <Compile Include="Sftp\Responses\StatVfsResponse.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="Sftp\SftpDataMessage.cs">
       <SubType>Code</SubType>
     </Compile>
@@ -392,6 +401,9 @@
     <Compile Include="Sftp\SftpFile.cs" />
     <Compile Include="Sftp\SftpFileAttributes.cs" />
     <Compile Include="Sftp\SftpFileStream.cs" />
+    <Compile Include="Sftp\SftpFileSystemInformation.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="Sftp\SftpListDirectoryAsyncResult.cs" />
     <Compile Include="Sftp\SftpMessage.cs" />
     <Compile Include="Sftp\SftpMessageTypes.cs">

+ 34 - 0
Renci.SshClient/Renci.SshNet/Sftp/Requests/PosixRenameRequest.cs

@@ -0,0 +1,34 @@
+using System;
+using Renci.SshNet.Sftp.Responses;
+
+namespace Renci.SshNet.Sftp.Requests
+{
+    internal class PosixRenameRequest : SftpRequest
+    {
+        public const string NAME = "posix-rename@openssh.com";
+
+        public override SftpMessageTypes SftpMessageType
+        {
+            get { return SftpMessageTypes.Extended; }
+        }
+
+        public string OldPath { get; private set; }
+
+        public string NewPath { get; private set; }
+
+        public PosixRenameRequest(uint requestId, string oldPath, string newPath, Action<SftpStatusResponse> statusAction)
+            : base(requestId, statusAction)
+        {
+            this.OldPath = oldPath;
+            this.NewPath = newPath;
+        }
+
+        protected override void SaveData()
+        {
+            base.SaveData();
+            this.Write(PosixRenameRequest.NAME);
+            this.Write(this.OldPath);
+            this.Write(this.NewPath);
+        }
+    }
+}

+ 31 - 0
Renci.SshClient/Renci.SshNet/Sftp/Requests/StatVfsRequest.cs

@@ -0,0 +1,31 @@
+using System;
+using Renci.SshNet.Sftp.Responses;
+
+namespace Renci.SshNet.Sftp.Requests
+{
+    internal class StatVfsRequest : SftpRequest
+    {
+        public const string NAME = "statvfs@openssh.com";
+
+        public override SftpMessageTypes SftpMessageType
+        {
+            get { return SftpMessageTypes.Extended; }
+        }
+
+        public string Path { get; private set; }
+
+        public StatVfsRequest(uint requestId, string path, Action<SftpExtendedReplyResponse> extendedAction, Action<SftpStatusResponse> statusAction)
+            : base(requestId, statusAction)
+        {
+            this.Path = path;
+            this.SetAction(extendedAction);
+        }
+
+        protected override void SaveData()
+        {
+            base.SaveData();
+            this.Write(StatVfsRequest.NAME);
+            this.Write(this.Path);
+        }
+    }
+}

+ 19 - 0
Renci.SshClient/Renci.SshNet/Sftp/Responses/StatVfsResponse.cs

@@ -0,0 +1,19 @@
+namespace Renci.SshNet.Sftp.Responses
+{
+    internal class StatVfsResponse : SftpExtendedReplyResponse
+    {
+        public SftpFileSytemInformation Information { get; private set; }
+
+        protected override void LoadData()
+        {
+            base.LoadData();
+
+            this.Information = new SftpFileSytemInformation(this.ReadUInt64(), this.ReadUInt64(),
+                                                                     this.ReadUInt64(), this.ReadUInt64(),
+                                                                     this.ReadUInt64(), this.ReadUInt64(),
+                                                                     this.ReadUInt64(), this.ReadUInt64(),
+                                                                     this.ReadUInt64(), this.ReadUInt64(),
+                                                                     this.ReadUInt64());
+        }
+    }
+}

+ 56 - 0
Renci.SshClient/Renci.SshNet/Sftp/SftpFileSystemInformation.cs

@@ -0,0 +1,56 @@
+namespace Renci.SshNet.Sftp
+{
+    /// <summary>
+    /// Contains File system information exposed by statvfs@openssh.com request.
+    /// </summary>
+    public class SftpFileSytemInformation
+    {
+        private ulong _flag;
+
+        private const ulong SSH_FXE_STATVFS_ST_RDONLY = 0x1;
+
+        private const ulong SSH_FXE_STATVFS_ST_NOSUID = 0x2;
+
+        public ulong BlockSize { get; private set; }
+
+        public ulong TotalBlocks { get; private set; }
+
+        public ulong FreeBlocks { get; private set; }
+
+        public ulong AvailableBlocks { get; private set; }
+
+        public ulong TotalNodes { get; private set; }
+
+        public ulong FreeNodes { get; private set; }
+
+        public ulong AvailableNodes { get; private set; }
+
+        public ulong Sid { get; private set; }
+
+        public bool IsReadOnly
+        {
+            get { return (_flag & SSH_FXE_STATVFS_ST_RDONLY) == SSH_FXE_STATVFS_ST_RDONLY; }
+        }
+
+        public bool SupportsSetUid
+        {
+            get { return (_flag & SSH_FXE_STATVFS_ST_NOSUID) == 0; }
+        }
+
+        public ulong MaxNameLenght { get; private set; }
+
+        internal SftpFileSytemInformation(ulong bsize, ulong frsize, ulong blocks, ulong bfree, ulong bavail, ulong files, ulong ffree, ulong favail, ulong sid, ulong flag, ulong namemax)
+        {
+            this.BlockSize = frsize;
+            this.TotalBlocks = blocks;
+            this.FreeBlocks = bfree;
+            this.AvailableBlocks = bavail;
+            this.TotalNodes = files;
+            this.FreeNodes = ffree;
+            this.AvailableNodes = favail;
+            this.Sid = sid;
+            this._flag = flag;
+            this.MaxNameLenght = namemax;
+        }
+    }
+}

+ 66 - 14
Renci.SshClient/Renci.SshNet/Sftp/SftpSession.cs

@@ -253,24 +253,10 @@ namespace Renci.SshNet.Sftp
             }
 
             this.SendMessage(request);
-            //this.SendData(new SftpDataMessage(this.ChannelNumber, request));
-
-            //var messageData = request.GetBytes();
-
-            //var data = new byte[4 + messageData.Length];
-
-            //((uint)messageData.Length).GetBytes().CopyTo(data, 0);
-            //messageData.CopyTo(data, 4);
-
-            //this.SendData(data);
-
         }
 
         #region SFTP API functions
 
-        //#define SSH_FXP_INIT                1
-        //#define SSH_FXP_VERSION             2
-
         /// <summary>
         /// Performs SSH_FXP_OPEN request
         /// </summary>
@@ -890,6 +876,72 @@ namespace Renci.SshNet.Sftp
             }
         }
 
+        /// <summary>
+        /// Performs posix-rename@openssh.com extended request.
+        /// </summary>
+        /// <param name="oldPath">The old path.</param>
+        /// <param name="newPath">The new path.</param>
+        internal void RequestPosixRename(string oldPath, string newPath)
+        {
+            using (var wait = new AutoResetEvent(false))
+            {
+                var request = new PosixRenameRequest(this.NextRequestId, oldPath, newPath,
+                    (response) =>
+                    {
+                        if (response.StatusCode == StatusCodes.Ok)
+                        {
+                            wait.Set();
+                        }
+                        else
+                        {
+                            ThrowSftpException(response);
+                        }
+                    });
+
+                this.SendRequest(request);
+
+                this.WaitHandle(wait, this._operationTimeout);
+            }
+        }
+
+        /// <summary>
+        /// Performs statvfs@openssh.com extended request.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <param name="nullOnError">if set to <c>true</c> [null on error].</param>
+        /// <returns></returns>
+        internal SftpFileSytemInformation RequestStatVfs(string path, bool nullOnError = false)
+        {
+            SftpFileSytemInformation information = null;
+            using (var wait = new AutoResetEvent(false))
+            {
+                var request = new StatVfsRequest(this.NextRequestId, path,
+                    (response) =>
+                    {
+                        information = response.OfType<StatVfsResponse>().Information;
+                        wait.Set();
+                    },
+                    (response) =>
+                    {
+                        if (nullOnError)
+                        {
+                            wait.Set();
+                        }
+                        else
+                        {
+                            ThrowSftpException(response);
+                        }
+
+                    });
+
+                this.SendRequest(request);
+
+                this.WaitHandle(wait, this._operationTimeout);
+            }
+
+            return information;
+        }
+
         #endregion
 
         private void ThrowSftpException(SftpStatusResponse response)

+ 44 - 1
Renci.SshClient/Renci.SshNet/SftpClient.cs

@@ -239,6 +239,22 @@ namespace Renci.SshNet
         /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to rename the file was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
         /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message"/> is the message from the remote host.</exception>
         public void RenameFile(string oldPath, string newPath)
+        {
+            this.RenameFile(oldPath, newPath, false);
+        }
+
+        /// <summary>
+        /// Renames remote file from old path to new path.
+        /// </summary>
+        /// <param name="oldPath">Path to the old file location.</param>
+        /// <param name="newPath">Path to the new file location.</param>
+        /// <param name="isPosix">if set to <c>true</c> then perform a posix rename.</param>
+        /// <exception cref="System.ArgumentNullException">oldPath</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="oldPath" /> is <b>null</b>. <para>-or-</para> or <paramref name="newPath" /> is <b>null</b>.</exception>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="Renci.SshNet.Common.SftpPermissionDeniedException">Permission to rename the file was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
+        /// <exception cref="Renci.SshNet.Common.SshException">A SSH error where <see cref="P:SshException.Message" /> is the message from the remote host.</exception>
+        public void RenameFile(string oldPath, string newPath, bool isPosix)
         {
             if (oldPath == null)
                 throw new ArgumentNullException("oldPath");
@@ -253,7 +269,14 @@ namespace Renci.SshNet
 
             var newFullPath = this._sftpSession.GetCanonicalPath(newPath);
 
-            this._sftpSession.RequestRename(oldFullPath, newFullPath);
+            if (isPosix)
+            {
+                this._sftpSession.RequestPosixRename(oldFullPath, newFullPath);
+            }
+            else
+            {
+                this._sftpSession.RequestRename(oldFullPath, newFullPath);
+            }
         }
 
         /// <summary>
@@ -611,6 +634,26 @@ namespace Renci.SshNet
             ar.EndInvoke();
         }
 
+        /// <summary>
+        /// Gets status using statvfs@openssh.com request.
+        /// </summary>
+        /// <param name="path">The path.</param>
+        /// <returns></returns>
+        /// <exception cref="SshConnectionException">Client is not connected.</exception>
+        /// <exception cref="ArgumentNullException"><paramref name="path"/> is <b>null</b>.</exception>
+        public SftpFileSytemInformation GetStatus(string path)
+        {
+            if (path == null)
+                throw new ArgumentNullException("path");
+
+            //  Ensure that connection is established.
+            this.EnsureConnection();
+
+            var fullPath = this._sftpSession.GetCanonicalPath(path);
+
+            return this._sftpSession.RequestStatVfs(fullPath);
+        }
+
         #region File Methods
 
         /// <summary>