Skip to content

Commit c2552e4

Browse files
committed
SftpFileStream:
Only allow Append mode when combined with write-only access. Fixes issue #267.
1 parent ea36b24 commit c2552e4

File tree

3 files changed

+21
-129
lines changed

3 files changed

+21
-129
lines changed

src/Renci.SshNet.Tests/Classes/Sftp/SftpFileStreamTest_Ctor_FileModeAppend_FileAccessRead.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void CtorShouldHaveThrownArgumentException()
4848
{
4949
Assert.IsNotNull(_actualException);
5050
Assert.IsNull(_actualException.InnerException);
51-
Assert.AreEqual(string.Format("Combining {0}: {1} with {2}: {3} is invalid.", typeof(FileMode).Name, _fileMode, typeof(FileAccess).Name, _fileAccess), _actualException.Message);
51+
Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message);
5252
Assert.IsNull(_actualException.ParamName);
5353
}
5454
}
Lines changed: 15 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
using System;
22
using System.IO;
3-
using System.Threading;
43
using Microsoft.VisualStudio.TestTools.UnitTesting;
5-
using Moq;
64
using Renci.SshNet.Sftp;
7-
using Renci.SshNet.Tests.Common;
85

96
namespace Renci.SshNet.Tests.Classes.Sftp
107
{
@@ -16,11 +13,7 @@ public class SftpFileStreamTest_Ctor_FileModeAppend_FileAccessReadWrite : SftpFi
1613
private FileMode _fileMode;
1714
private FileAccess _fileAccess;
1815
private int _bufferSize;
19-
private uint _readBufferSize;
20-
private uint _writeBufferSize;
21-
private byte[] _handle;
22-
private SftpFileStream _target;
23-
private SftpFileAttributes _fileAttributes;
16+
private ArgumentException _actualException;
2417

2518
protected override void SetupData()
2619
{
@@ -31,138 +24,32 @@ protected override void SetupData()
3124
_fileMode = FileMode.Append;
3225
_fileAccess = FileAccess.ReadWrite;
3326
_bufferSize = _random.Next(5, 1000);
34-
_readBufferSize = (uint) _random.Next(5, 1000);
35-
_writeBufferSize = (uint) _random.Next(5, 1000);
36-
_handle = GenerateRandom(_random.Next(1, 10), _random);
37-
_fileAttributes = new SftpFileAttributesBuilder().WithLastAccessTime(DateTime.Now.AddSeconds(_random.Next()))
38-
.WithLastWriteTime(DateTime.Now.AddSeconds(_random.Next()))
39-
.WithSize(_random.Next())
40-
.WithUserId(_random.Next())
41-
.WithGroupId(_random.Next())
42-
.WithPermissions((uint)_random.Next())
43-
.Build();
4427
}
4528

4629
protected override void SetupMocks()
4730
{
48-
SftpSessionMock.InSequence(MockSequence)
49-
.Setup(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.Append, false))
50-
.Returns(_handle);
51-
SftpSessionMock.InSequence(MockSequence)
52-
.Setup(p => p.CalculateOptimalReadLength((uint)_bufferSize))
53-
.Returns(_readBufferSize);
54-
SftpSessionMock.InSequence(MockSequence)
55-
.Setup(p => p.CalculateOptimalWriteLength((uint)_bufferSize, _handle))
56-
.Returns(_writeBufferSize);
57-
SftpSessionMock.InSequence(MockSequence)
58-
.Setup(p => p.RequestFStat(_handle, false))
59-
.Returns(_fileAttributes);
6031
}
6132

6233
protected override void Act()
6334
{
64-
_target = new SftpFileStream(SftpSessionMock.Object,
65-
_path,
66-
_fileMode,
67-
_fileAccess,
68-
_bufferSize);
35+
try
36+
{
37+
new SftpFileStream(SftpSessionMock.Object, _path, _fileMode, _fileAccess, _bufferSize);
38+
Assert.Fail();
39+
}
40+
catch (ArgumentException ex)
41+
{
42+
_actualException = ex;
43+
}
6944
}
7045

7146
[TestMethod]
72-
public void CanReadShouldReturnTrue()
47+
public void CtorShouldHaveThrownArgumentException()
7348
{
74-
Assert.IsTrue(_target.CanRead);
75-
}
76-
77-
[TestMethod]
78-
public void CanSeekShouldReturnTrue()
79-
{
80-
Assert.IsTrue(_target.CanSeek);
81-
}
82-
83-
[TestMethod]
84-
public void CanWriteShouldReturnTrue()
85-
{
86-
Assert.IsTrue(_target.CanWrite);
87-
}
88-
89-
[TestMethod]
90-
public void CanTimeoutShouldReturnTrue()
91-
{
92-
Assert.IsTrue(_target.CanTimeout);
93-
}
94-
95-
[TestMethod]
96-
public void PositionShouldReturnSizeOfFile()
97-
{
98-
SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
99-
100-
var actual = _target.Position;
101-
102-
Assert.AreEqual(_fileAttributes.Size, actual);
103-
104-
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1));
105-
}
106-
107-
[TestMethod]
108-
public void ReadShouldStartReadingAtEndOfFile()
109-
{
110-
var buffer = new byte[8];
111-
var data = new byte[] {5, 4, 3, 2, 1};
112-
var expected = new byte[] {0, 5, 4, 3, 2, 1, 0, 0};
113-
114-
SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
115-
SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, (ulong)_fileAttributes.Size, _readBufferSize)).Returns(data);
116-
117-
var actual = _target.Read(buffer, 1, data.Length);
118-
119-
Assert.AreEqual(data.Length, actual);
120-
Assert.IsTrue(buffer.IsEqualTo(expected));
121-
122-
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1));
123-
SftpSessionMock.Verify(p => p.RequestRead(_handle, (ulong)_fileAttributes.Size, _readBufferSize), Times.Once);
124-
}
125-
126-
[TestMethod]
127-
public void ReadByteShouldStartReadingAtEndOfFile()
128-
{
129-
var data = GenerateRandom(5, _random);
130-
131-
SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
132-
SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestRead(_handle, (ulong) _fileAttributes.Size, _readBufferSize)).Returns(data);
133-
134-
var actual = _target.ReadByte();
135-
136-
Assert.AreEqual(data[0], actual);
137-
138-
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1));
139-
SftpSessionMock.Verify(p => p.RequestRead(_handle, (ulong) _fileAttributes.Size, _readBufferSize), Times.Once);
140-
}
141-
142-
[TestMethod]
143-
public void WriteShouldStartWritingAtEndOfFile()
144-
{
145-
var buffer = new byte[_writeBufferSize];
146-
147-
SftpSessionMock.InSequence(MockSequence).Setup(p => p.IsOpen).Returns(true);
148-
SftpSessionMock.InSequence(MockSequence).Setup(p => p.RequestWrite(_handle, (ulong) _fileAttributes.Size, buffer, 0, buffer.Length, It.IsNotNull<AutoResetEvent>(), null));
149-
150-
_target.Write(buffer, 0, buffer.Length);
151-
152-
SftpSessionMock.Verify(p => p.IsOpen, Times.Exactly(1));
153-
SftpSessionMock.Verify(p => p.RequestWrite(_handle, (ulong) _fileAttributes.Size, buffer, 0, buffer.Length, It.IsNotNull<AutoResetEvent>(), null), Times.Once);
154-
}
155-
156-
[TestMethod]
157-
public void RequestOpenOnSftpSessionShouldBeInvokedOnce()
158-
{
159-
SftpSessionMock.Verify(p => p.RequestOpen(_path, Flags.Read | Flags.Write | Flags.Append, false), Times.Once);
160-
}
161-
162-
[TestMethod]
163-
public void RequestFStatOnSftpSessionShouldBeInvokedOnce()
164-
{
165-
SftpSessionMock.Verify(p => p.RequestFStat(_handle, false), Times.Once);
49+
Assert.IsNotNull(_actualException);
50+
Assert.IsNull(_actualException.InnerException);
51+
Assert.AreEqual(string.Format("{0} mode can be requested only when combined with write-only access.", _fileMode), _actualException.Message);
52+
Assert.IsNull(_actualException.ParamName);
16653
}
16754
}
16855
}

src/Renci.SshNet/Sftp/SftpFileStream.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ internal SftpFileStream(ISftpSession session, string path, FileMode mode, FileAc
202202
throw new ArgumentOutOfRangeException("access");
203203
}
204204

205+
if ((access & FileAccess.Read) != 0 && mode == FileMode.Append)
206+
{
207+
throw new ArgumentException(string.Format("{0} mode can be requested only when combined with write-only access.", mode.ToString("G")));
208+
}
209+
205210
if ((access & FileAccess.Write) == 0)
206211
{
207212
if (mode == FileMode.Create || mode == FileMode.CreateNew || mode == FileMode.Truncate || mode == FileMode.Append)

0 commit comments

Comments
 (0)