From 90a13a6c5a0742288db251c9ca23c36c1a57ea04 Mon Sep 17 00:00:00 2001
From: Bill Menees <13545825+menees@users.noreply.github.com>
Date: Sat, 18 Jul 2020 03:59:30 -0500
Subject: [PATCH 01/19] Fix SFTP file UTC time handling (#356)
* Improved SFTP file UTC time handling
---
...achedFollowedBySuccessInAlternateBranch.cs | 2 +-
.../Classes/Common/BigIntegerTest.cs | 2 +
.../ChannelExtendedDataMessageTest.cs | 2 +-
.../Classes/Sftp/SftpFileReaderTestBase.cs | 3 +-
...est_Ctor_FileModeAppend_FileAccessWrite.cs | 4 +-
...eadBuffer_NewLengthGreatherThanPosition.cs | 6 +-
...aInReadBuffer_NewLengthLessThanPosition.cs | 4 +-
...iteBuffer_NewLengthGreatherThanPosition.cs | 4 +-
...InWriteBuffer_NewLengthLessThanPosition.cs | 4 +-
...tLength_SessionOpen_FIleAccessReadWrite.cs | 4 +-
...t_SetLength_SessionOpen_FIleAccessWrite.cs | 4 +-
...tGreatherThanTwoTimesTheWriteBufferSize.cs | 4 +-
.../Classes/SftpClientTest.cs | 10 +-
src/Renci.SshNet.Tests/Common/Extensions.cs | 4 +-
.../Common/SftpFileAttributesBuilder.cs | 10 +-
src/Renci.SshNet/Sftp/SftpFile.cs | 8 +-
src/Renci.SshNet/Sftp/SftpFileAttributes.cs | 118 +++++++++++++++---
17 files changed, 144 insertions(+), 49 deletions(-)
diff --git a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs
index d60549e5..16f08ba9 100644
--- a/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs
+++ b/src/Renci.SshNet.Tests/Classes/ClientAuthenticationTest_Success_MultiList_PartialSuccessLimitReachedFollowedBySuccessInAlternateBranch.cs
@@ -181,4 +181,4 @@ namespace Renci.SshNet.Tests.Classes
PublicKeyAuthenticationMethodMock.Verify(p => p.Authenticate(SessionMock.Object), Times.Exactly(2));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs b/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs
index 8cca838d..c99fb2a6 100644
--- a/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs
+++ b/src/Renci.SshNet.Tests/Classes/Common/BigIntegerTest.cs
@@ -1506,10 +1506,12 @@ namespace Renci.SshNet.Tests.Classes.Common
Assert.AreEqual("0", a.ToString(), "#4");
a = new BigInteger();
+#pragma warning disable CS1718 // Comparison made to same variable
Assert.AreEqual(true, a == a, "#5");
a = new BigInteger();
Assert.AreEqual(false, a < a, "#6");
+#pragma warning restore CS1718 // Comparison made to same variable
a = new BigInteger();
Assert.AreEqual(true, a < 10L, "#7");
diff --git a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs
index e50f5fa4..469908ea 100644
--- a/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs
+++ b/src/Renci.SshNet.Tests/Classes/Messages/Connection/ChannelExtendedDataMessageTest.cs
@@ -30,7 +30,7 @@ namespace Renci.SshNet.Tests.Classes.Messages.Connection
[Ignore] // placeholder
public void ChannelExtendedDataMessageConstructorTest1()
{
- uint localChannelNumber = 0; // TODO: Initialize to an appropriate value
+ //uint localChannelNumber = 0; // TODO: Initialize to an appropriate value
//ChannelExtendedDataMessage target = new ChannelExtendedDataMessage(localChannelNumber, null, null);
Assert.Inconclusive("TODO: Implement code to verify target");
}
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs
index fe73f9a6..2f2f1bbf 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileReaderTestBase.cs
@@ -38,7 +38,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
protected static SftpFileAttributes CreateSftpFileAttributes(long size)
{
- return new SftpFileAttributes(default(DateTime), default(DateTime), size, default(int), default(int), default(uint), null);
+ var utcDefault = DateTime.SpecifyKind(default(DateTime), DateTimeKind.Utc);
+ return new SftpFileAttributes(utcDefault, utcDefault, size, default(int), default(int), default(uint), null);
}
protected static byte[] CreateByteArray(Random random, int length)
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs
index d8c4904c..87183067 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessWrite.cs
@@ -34,8 +34,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
_readBufferSize = (uint) _random.Next(5, 1000);
_writeBufferSize = (uint) _random.Next(5, 1000);
_handle = GenerateRandom(_random.Next(1, 10), _random);
- _fileAttributes = new SftpFileAttributesBuilder().WithLastAccessTime(DateTime.Now.AddSeconds(_random.Next()))
- .WithLastWriteTime(DateTime.Now.AddSeconds(_random.Next()))
+ _fileAttributes = new SftpFileAttributesBuilder().WithLastAccessTime(DateTime.UtcNow.AddSeconds(_random.Next()))
+ .WithLastWriteTime(DateTime.UtcNow.AddSeconds(_random.Next()))
.WithSize(_random.Next())
.WithUserId(_random.Next())
.WithGroupId(_random.Next())
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs
index aee4b276..59a0135f 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthGreatherThanPosition.cs
@@ -53,8 +53,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
_fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
.WithExtension("V", "VValue")
.WithGroupId(random.Next())
- .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
- .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next()))
.WithPermissions((uint) random.Next())
.WithSize(_length + 100)
.WithUserId(random.Next())
@@ -201,4 +201,4 @@ namespace Renci.SshNet.Tests.Classes.Sftp
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(4));
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs
index ae3952cc..a6116339 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInReadBuffer_NewLengthLessThanPosition.cs
@@ -50,8 +50,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
_fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
.WithExtension("V", "VValue")
.WithGroupId(random.Next())
- .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
- .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next()))
.WithPermissions((uint)random.Next())
.WithSize(_length + 100)
.WithUserId(random.Next())
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs
index 5a19aa3e..c71b43c8 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthGreatherThanPosition.cs
@@ -54,8 +54,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
_fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
.WithExtension("V", "VValue")
.WithGroupId(random.Next())
- .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
- .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next()))
.WithPermissions((uint)random.Next())
.WithSize(_length + 100)
.WithUserId(random.Next())
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs
index cfcf2315..96ad14b1 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_DataInWriteBuffer_NewLengthLessThanPosition.cs
@@ -54,8 +54,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
_fileAttributes = new SftpFileAttributesBuilder().WithExtension("X", "ABC")
.WithExtension("V", "VValue")
.WithGroupId(random.Next())
- .WithLastAccessTime(DateTime.Now.AddSeconds(random.Next()))
- .WithLastWriteTime(DateTime.Now.AddSeconds(random.Next()))
+ .WithLastAccessTime(DateTime.UtcNow.AddSeconds(random.Next()))
+ .WithLastWriteTime(DateTime.UtcNow.AddSeconds(random.Next()))
.WithPermissions((uint) random.Next())
.WithSize(_length + 100)
.WithUserId(random.Next())
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs
index 0fde319a..ef89483b 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessReadWrite.cs
@@ -48,8 +48,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
_writeBufferSize = (uint) random.Next(0, 1000);
_length = random.Next();
- _fileAttributesLastAccessTime = DateTime.Now.AddSeconds(random.Next());
- _fileAttributesLastWriteTime = DateTime.Now.AddSeconds(random.Next());
+ _fileAttributesLastAccessTime = DateTime.UtcNow.AddSeconds(random.Next());
+ _fileAttributesLastWriteTime = DateTime.UtcNow.AddSeconds(random.Next());
_fileAttributesSize = random.Next();
_fileAttributesUserId = random.Next();
_fileAttributesGroupId = random.Next();
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs
index ce2619a5..73b5a953 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_SetLength_SessionOpen_FIleAccessWrite.cs
@@ -48,8 +48,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
_writeBufferSize = (uint) random.Next(0, 1000);
_length = random.Next();
- _fileAttributesLastAccessTime = DateTime.Now.AddSeconds(random.Next());
- _fileAttributesLastWriteTime = DateTime.Now.AddSeconds(random.Next());
+ _fileAttributesLastAccessTime = DateTime.UtcNow.AddSeconds(random.Next());
+ _fileAttributesLastWriteTime = DateTime.UtcNow.AddSeconds(random.Next());
_fileAttributesSize = random.Next();
_fileAttributesUserId = random.Next();
_fileAttributesGroupId = random.Next();
diff --git a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs
index 68020d55..e7d53706 100644
--- a/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs
+++ b/src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Write_SessionOpen_CountGreatherThanTwoTimesTheWriteBufferSize.cs
@@ -119,8 +119,8 @@ namespace Renci.SshNet.Tests.Classes.Sftp
[TestMethod]
public void LengthShouldFlushBufferAndReturnSizeOfFile()
{
- var lengthFileAttributes = new SftpFileAttributes(DateTime.Now,
- DateTime.Now,
+ var lengthFileAttributes = new SftpFileAttributes(DateTime.UtcNow,
+ DateTime.UtcNow,
_random.Next(),
_random.Next(),
_random.Next(),
diff --git a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs
index 3edd7c7b..f562f66b 100644
--- a/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs
+++ b/src/Renci.SshNet.Tests/Classes/SftpClientTest.cs
@@ -1076,7 +1076,9 @@ namespace Renci.SshNet.Tests.Classes
SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
string path = string.Empty; // TODO: Initialize to an appropriate value
DateTime lastAccessTime = new DateTime(); // TODO: Initialize to an appropriate value
+#pragma warning disable CS0618 // Type or member is obsolete
target.SetLastAccessTime(path, lastAccessTime);
+#pragma warning restore CS0618 // Type or member is obsolete
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
@@ -1091,7 +1093,9 @@ namespace Renci.SshNet.Tests.Classes
SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
string path = string.Empty; // TODO: Initialize to an appropriate value
DateTime lastAccessTimeUtc = new DateTime(); // TODO: Initialize to an appropriate value
+#pragma warning disable CS0618 // Type or member is obsolete
target.SetLastAccessTimeUtc(path, lastAccessTimeUtc);
+#pragma warning restore CS0618 // Type or member is obsolete
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
@@ -1106,7 +1110,9 @@ namespace Renci.SshNet.Tests.Classes
SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
string path = string.Empty; // TODO: Initialize to an appropriate value
DateTime lastWriteTime = new DateTime(); // TODO: Initialize to an appropriate value
+#pragma warning disable CS0618 // Type or member is obsolete
target.SetLastWriteTime(path, lastWriteTime);
+#pragma warning restore CS0618 // Type or member is obsolete
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
@@ -1121,7 +1127,9 @@ namespace Renci.SshNet.Tests.Classes
SftpClient target = new SftpClient(connectionInfo); // TODO: Initialize to an appropriate value
string path = string.Empty; // TODO: Initialize to an appropriate value
DateTime lastWriteTimeUtc = new DateTime(); // TODO: Initialize to an appropriate value
+#pragma warning disable CS0618 // Type or member is obsolete
target.SetLastWriteTimeUtc(path, lastWriteTimeUtc);
+#pragma warning restore CS0618 // Type or member is obsolete
Assert.Inconclusive("A method that does not return a value cannot be verified.");
}
@@ -1403,4 +1411,4 @@ namespace Renci.SshNet.Tests.Classes
public SftpDownloadAsyncResult DownloadResult { get; set; }
}
}
-}
\ No newline at end of file
+}
diff --git a/src/Renci.SshNet.Tests/Common/Extensions.cs b/src/Renci.SshNet.Tests/Common/Extensions.cs
index 911f3413..4e88a790 100644
--- a/src/Renci.SshNet.Tests/Common/Extensions.cs
+++ b/src/Renci.SshNet.Tests/Common/Extensions.cs
@@ -49,8 +49,8 @@ namespace Renci.SshNet.Tests.Common
clonedExtensions = null;
}
- return new SftpFileAttributes(value.LastAccessTime,
- value.LastWriteTime,
+ return new SftpFileAttributes(value.LastAccessTimeUtc,
+ value.LastWriteTimeUtc,
value.Size,
value.UserId,
value.GroupId,
diff --git a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs
index 1f2d3324..2cf1b11a 100644
--- a/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs
+++ b/src/Renci.SshNet.Tests/Common/SftpFileAttributesBuilder.cs
@@ -64,9 +64,15 @@ namespace Renci.SshNet.Tests.Common
public SftpFileAttributes Build()
{
if (_lastAccessTime == null)
- _lastAccessTime = DateTime.MinValue;
+ _lastAccessTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
+ else if (_lastAccessTime.Value.Kind != DateTimeKind.Utc)
+ _lastAccessTime = _lastAccessTime.Value.ToUniversalTime();
+
if (_lastWriteTime == null)
- _lastWriteTime = DateTime.MinValue;
+ _lastWriteTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
+ else if (_lastWriteTime.Value.Kind != DateTimeKind.Utc)
+ _lastWriteTime = _lastWriteTime.Value.ToUniversalTime();
+
if (_size == null)
_size = 0;
if (_userId == null)
diff --git a/src/Renci.SshNet/Sftp/SftpFile.cs b/src/Renci.SshNet/Sftp/SftpFile.cs
index de729e13..cfcc37a2 100644
--- a/src/Renci.SshNet/Sftp/SftpFile.cs
+++ b/src/Renci.SshNet/Sftp/SftpFile.cs
@@ -99,11 +99,11 @@ namespace Renci.SshNet.Sftp
{
get
{
- return Attributes.LastAccessTime.ToUniversalTime();
+ return Attributes.LastAccessTimeUtc;
}
set
{
- Attributes.LastAccessTime = value.ToLocalTime();
+ Attributes.LastAccessTimeUtc = value;
}
}
@@ -117,11 +117,11 @@ namespace Renci.SshNet.Sftp
{
get
{
- return Attributes.LastWriteTime.ToUniversalTime();
+ return Attributes.LastWriteTimeUtc;
}
set
{
- Attributes.LastWriteTime = value.ToLocalTime();
+ Attributes.LastWriteTimeUtc = value;
}
}
diff --git a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs
index d99dfc2d..573deb92 100644
--- a/src/Renci.SshNet/Sftp/SftpFileAttributes.cs
+++ b/src/Renci.SshNet/Sftp/SftpFileAttributes.cs
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using Renci.SshNet.Common;
+using System.Diagnostics;
namespace Renci.SshNet.Sftp
{
@@ -11,7 +12,7 @@ namespace Renci.SshNet.Sftp
///
public class SftpFileAttributes
{
- #region Bitmask constats
+ #region Bitmask constants
private const uint S_IFMT = 0xF000; // bitmask for the file type bitfields
@@ -60,8 +61,8 @@ namespace Renci.SshNet.Sftp
private bool _isGroupIDBitSet;
private bool _isStickyBitSet;
- private readonly DateTime _originalLastAccessTime;
- private readonly DateTime _originalLastWriteTime;
+ private readonly DateTime _originalLastAccessTimeUtc;
+ private readonly DateTime _originalLastWriteTimeUtc;
private readonly long _originalSize;
private readonly int _originalUserId;
private readonly int _originalGroupId;
@@ -70,12 +71,12 @@ namespace Renci.SshNet.Sftp
internal bool IsLastAccessTimeChanged
{
- get { return _originalLastAccessTime != LastAccessTime; }
+ get { return _originalLastAccessTimeUtc != LastAccessTimeUtc; }
}
internal bool IsLastWriteTimeChanged
{
- get { return _originalLastWriteTime != LastWriteTime; }
+ get { return _originalLastWriteTimeUtc != LastWriteTimeUtc; }
}
internal bool IsSizeChanged
@@ -104,20 +105,58 @@ namespace Renci.SshNet.Sftp
}
///
- /// Gets or sets the time the current file or directory was last accessed.
+ /// Gets or sets the local time the current file or directory was last accessed.
///
///
- /// The time that the current file or directory was last accessed.
+ /// The local time that the current file or directory was last accessed.
///
- public DateTime LastAccessTime { get; set; }
+ public DateTime LastAccessTime
+ {
+ get
+ {
+ return ToLocalTime(this.LastAccessTimeUtc);
+ }
+
+ set
+ {
+ this.LastAccessTimeUtc = ToUniversalTime(value);
+ }
+ }
///
- /// Gets or sets the time when the current file or directory was last written to.
+ /// Gets or sets the local time when the current file or directory was last written to.
///
///
- /// The time the current file was last written.
+ /// The local time the current file was last written.
///
- public DateTime LastWriteTime { get; set; }
+ public DateTime LastWriteTime
+ {
+ get
+ {
+ return ToLocalTime(this.LastWriteTimeUtc);
+ }
+
+ set
+ {
+ this.LastWriteTimeUtc = ToUniversalTime(value);
+ }
+ }
+
+ ///
+ /// Gets or sets the UTC time the current file or directory was last accessed.
+ ///
+ ///
+ /// The UTC time that the current file or directory was last accessed.
+ ///
+ public DateTime LastAccessTimeUtc { get; set; }
+
+ ///
+ /// Gets or sets the UTC time when the current file or directory was last written to.
+ ///
+ ///
+ /// The UTC time the current file was last written.
+ ///
+ public DateTime LastWriteTimeUtc { get; set; }
///
/// Gets or sets the size, in bytes, of the current file.
@@ -395,10 +434,10 @@ namespace Renci.SshNet.Sftp
{
}
- internal SftpFileAttributes(DateTime lastAccessTime, DateTime lastWriteTime, long size, int userId, int groupId, uint permissions, IDictionary extensions)
+ internal SftpFileAttributes(DateTime lastAccessTimeUtc, DateTime lastWriteTimeUtc, long size, int userId, int groupId, uint permissions, IDictionary extensions)
{
- LastAccessTime = _originalLastAccessTime = lastAccessTime;
- LastWriteTime = _originalLastWriteTime = lastWriteTime;
+ LastAccessTimeUtc = _originalLastAccessTimeUtc = lastAccessTimeUtc;
+ LastWriteTimeUtc = _originalLastWriteTimeUtc = lastWriteTimeUtc;
Size = _originalSize = size;
UserId = _originalUserId = userId;
GroupId = _originalGroupId = groupId;
@@ -491,9 +530,9 @@ namespace Renci.SshNet.Sftp
if (IsLastAccessTimeChanged || IsLastWriteTimeChanged)
{
- var time = (uint)(LastAccessTime.ToFileTime() / 10000000 - 11644473600);
+ var time = (uint)(LastAccessTimeUtc.ToFileTimeUtc() / 10000000 - 11644473600);
stream.Write(time);
- time = (uint)(LastWriteTime.ToFileTime() / 10000000 - 11644473600);
+ time = (uint)(LastWriteTimeUtc.ToFileTimeUtc() / 10000000 - 11644473600);
stream.Write(time);
}
@@ -521,8 +560,8 @@ namespace Renci.SshNet.Sftp
var userId = -1;
var groupId = -1;
uint permissions = 0;
- var accessTime = DateTime.MinValue;
- var modifyTime = DateTime.MinValue;
+ DateTime accessTime;
+ DateTime modifyTime;
IDictionary extensions = null;
if ((flag & 0x00000001) == 0x00000001) // SSH_FILEXFER_ATTR_SIZE
@@ -544,10 +583,17 @@ namespace Renci.SshNet.Sftp
if ((flag & 0x00000008) == 0x00000008) // SSH_FILEXFER_ATTR_ACMODTIME
{
+ // The incoming times are "Unix times", so they're already in UTC. We need to preserve that
+ // to avoid losing information in a local time conversion during the "fall back" hour in DST.
var time = stream.ReadUInt32();
- accessTime = DateTime.FromFileTime((time + 11644473600) * 10000000);
+ accessTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000);
time = stream.ReadUInt32();
- modifyTime = DateTime.FromFileTime((time + 11644473600) * 10000000);
+ modifyTime = DateTime.FromFileTimeUtc((time + 11644473600) * 10000000);
+ }
+ else
+ {
+ accessTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
+ modifyTime = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
}
if ((flag & 0x80000000) == 0x80000000) // SSH_FILEXFER_ATTR_EXTENDED
@@ -572,5 +618,37 @@ namespace Renci.SshNet.Sftp
return FromBytes(stream);
}
}
+
+ private static DateTime ToLocalTime(DateTime value)
+ {
+ DateTime result;
+
+ if (value == DateTime.MinValue)
+ {
+ result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Local);
+ }
+ else
+ {
+ result = value.ToLocalTime();
+ }
+
+ return result;
+ }
+
+ private static DateTime ToUniversalTime(DateTime value)
+ {
+ DateTime result;
+
+ if (value == DateTime.MinValue)
+ {
+ result = DateTime.SpecifyKind(DateTime.MinValue, DateTimeKind.Utc);
+ }
+ else
+ {
+ result = value.ToUniversalTime();
+ }
+
+ return result;
+ }
}
}
From cefdc203d98cd890815e029bc759bc43ec5a9643 Mon Sep 17 00:00:00 2001
From: Eric Brumfield
Date: Sun, 19 Jul 2020 12:49:12 -0400
Subject: [PATCH 02/19] Add ISftpClient interface to SftpClient (#197)
Add ISftpClient interface to **SftpClient for mocking and testing purposes.
Fixes #193.
---
.../Renci.SshNet.Silverlight.csproj | 3 +
.../Renci.SshNet.Silverlight5.csproj | 3 +
.../Renci.SshNet.UAP10.csproj | 3 +
.../Renci.SshNet.WindowsPhone.csproj | 3 +
.../Renci.SshNet.WindowsPhone8.csproj | 5 +-
src/Renci.SshNet/ISftpClient.cs | 1065 +++++++++++++++++
src/Renci.SshNet/SftpClient.cs | 2 +-
7 files changed, 1082 insertions(+), 2 deletions(-)
create mode 100644 src/Renci.SshNet/ISftpClient.cs
diff --git a/src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj b/src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj
index 6769eabf..39f66a0e 100644
--- a/src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj
+++ b/src/Renci.SshNet.Silverlight/Renci.SshNet.Silverlight.csproj
@@ -1183,6 +1183,9 @@
SftpClient.cs
+
+
+ ISftpClient.cs
Sftp\Flags.cs
diff --git a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj b/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj
index 6892d3b6..fb59067a 100644
--- a/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj
+++ b/src/Renci.SshNet.Silverlight5/Renci.SshNet.Silverlight5.csproj
@@ -1189,6 +1189,9 @@
SftpClient.cs
+
+
+ ISftpClient.cs
Sftp\Flags.cs
diff --git a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj b/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj
index 0e66873e..bc78664d 100644
--- a/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj
+++ b/src/Renci.SshNet.UAP10/Renci.SshNet.UAP10.csproj
@@ -381,6 +381,9 @@
ISession.cs
+
+ ISftpClient.cs
+
ISubsystemSession.cs
diff --git a/src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj b/src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj
index 9e1d2da0..6f7d4576 100644
--- a/src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj
+++ b/src/Renci.SshNet.WindowsPhone/Renci.SshNet.WindowsPhone.csproj
@@ -1168,6 +1168,9 @@
SftpClient.cs
+
+
+ ISftpClient.cs
Sftp\Flags.cs
diff --git a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj b/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj
index 9fe3202e..cac46177 100644
--- a/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj
+++ b/src/Renci.SshNet.WindowsPhone8/Renci.SshNet.WindowsPhone8.csproj
@@ -358,6 +358,9 @@
ISession.cs
+
+ ISftpClient.cs
+
ISubsystemSession.cs
@@ -1441,7 +1444,7 @@
-
+