Skip to content

Fix Seek Operations in SftpFileStream - #910

Merged
drieseng merged 7 commits into
sshnet:developfrom
lemonyte:develop
Nov 29, 2022
Merged

Fix Seek Operations in SftpFileStream#910
drieseng merged 7 commits into
sshnet:developfrom
lemonyte:develop

Conversation

@lemonyte

@lemonyte lemonyte commented Jan 5, 2022

Copy link
Copy Markdown
Contributor

Changed SftpFileStream.Seek() to add offset instead of subtracting when origin is set to SeekOrigin.End. Fixes #909, fixes #1018.

@IgorMilavec IgorMilavec left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Can you also cover cases for invalid origin and trying to move before the beginning of the file? Here are the exceptions I feel would better describe the problems (and are somewhat aligned with the FileStream behavior):

throw new ArgumentException("Invalid seek origin.", "origin")
throw new IOException("An attempt was made to move the file pointer before the beginning of the file.");
throw new EndOfStreamException("An attempt was made to move the file pointer past the end of the file.");

I see that you have changed formatting of the code. Based on my past experience the maintainer will request you to undo these changes.

Can you add tests for the cases you mentioned were not covered?

@lemonyte

lemonyte commented Jan 25, 2022

Copy link
Copy Markdown
Contributor Author

I see that you have changed formatting of the code.

I am not sure what formatting changes you are referring to. The only change other than replacing - with + was the removal of a duplicate identical code block, as below:

if (condition)
{
    // Condition specific code

    switch (origin)
    {
        // ...
    }

    if (newPosn == -1)
    {
        throw new EndOfStreamException("End of stream.");
    }
    _position = newPosn;
}
else
{
    // Condition specific code

    switch (origin)
    {
        // ...
    }

    if (newPosn < 0)
    {
        throw new EndOfStreamException();
    }
    _position = newPosn;
}

Refactored to:

if (condition)
{
    // Condition specific code
}
else
{
    // Condition specific code
}

switch (origin)
{
    // ...
}
if (newPosn < 0)
{
    throw new EndOfStreamException("End of stream.");
}
_position = newPosn;
throw new EndOfStreamException("An attempt was made to move the file pointer past the end of the file.");

I think a seek operation past the end of a file is a valid operation, but correct me if I am wrong. Most platforms allow seeking past the end of a file, only raising an exception when a read is attempted after EOF. A case for seeking before the beginning of a file already exists:

throw new EndOfStreamException("End of stream.");

throw new EndOfStreamException();

Can you add tests for the cases you mentioned were not covered?

I am not very experienced in C#, being primarily a Python developer, but I'll see what I can reverse-engineer from the existing tests. Some guidance on how to use the available tests would be much appreciated.

@IgorMilavec

Copy link
Copy Markdown
Collaborator

To run the tests in VS 2019, open Test Explorer and you will see a list of tests. Filter Traits (there is a filter icon at the column heading) and deselect "integration" and "LongRunning". Then run all the tests from Test Explorer.

To cover this scenario, you create new test classes SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginEndAndOffset* by copiing SftpFileStreamTest_Seek_PositionedAtBeginningOfStream_OriginBeginAndOffset* and adjusting appropriately.

Also, due to the changes in this PR, the SeekShouldHaveThrownEndOfStreamException test is failing, I will flag the probable cause for this in the code review.

Comment thread src/Renci.SshNet/Sftp/SftpFileStream.cs Outdated
_position = newPosn;
if (newPosn < 0)
{
throw new EndOfStreamException("End of stream.");

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are providing a non-default exception message, causing the test SeekShouldHaveThrownEndOfStreamException to fail. I beleive if you just use throw new EndOfStreamException(); this will be fixed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for pointing that out. The exception message is already present in another part of the code, so I assumed I should leave it in there.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@drieseng There have been suggestions in other issues that we should localize exception messages. While that is probably too much work at this time, I propose to use default exception messages where possible and to stop checking error messages in the tests and rely on only asserting exception type (and other structured info in the exception) in the tests. Do you agree?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have noticed that the exception message format is different across .NET versions. For example:
.NET Framework 3.5

ArgumentException error message.
Parameter name: ParamName

.NET Core 3.0

ArgumentException error message. (Parameter 'ParamName')

This causes some tests to pass on one framework and fail on another.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@IgorMilavec, in general I prefer very specific asserts. To be honest, I think we have more important stuff to deal with than localization. But I'm ok to discuss this as a team, and decide where we want to go with this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@LemonPi314, that's indeed something I was already aware of and which I dealt with in other (professional) projects.

@lemonyte
lemonyte requested a review from drieseng as a code owner February 16, 2022 23:57
@IgorMilavec

Copy link
Copy Markdown
Collaborator

Changed SftpFileStream.Seek() to add offset instead of subtracting when origin is set to SeekOrigin.End. (#909)

@LemonPi314 can you please edit the above to include "Fix #909" so this PR will be linked to the issue? Tnx!

@daviburg

Copy link
Copy Markdown
Collaborator

Test case for the error(s) been fixed is still needed in this PR.

Comment thread src/Renci.SshNet/Sftp/SftpFileStream.cs Outdated
Comment thread src/Renci.SshNet/Sftp/SftpFileStream.cs
@daviburg

Copy link
Copy Markdown
Collaborator

Can you also cover cases for invalid origin and trying to move before the beginning of the file? Here are the exceptions I feel would better describe the problems (and are somewhat aligned with the FileStream behavior):

throw new ArgumentException("Invalid seek origin.", "origin")
throw new IOException("An attempt was made to move the file pointer before the beginning of the file.");
throw new EndOfStreamException("An attempt was made to move the file pointer past the end of the file.");

I second this suggestion to use specific and descriptive exceptions.

@daviburg

Copy link
Copy Markdown
Collaborator

I see that you have changed formatting of the code. Based on my past experience the maintainer will request you to undo these changes.

I am ambivalent about that. If the formatting change is specific to the code been already updated for the fix, and covered by the tests, I find it a good opportunity to clean up.
If the formatting change is extensive and makes it difficult to understand the functional change, I can see how that may best be done in a separate PR.

@drieseng

Copy link
Copy Markdown
Member

I am ambivalent about that. If the formatting change is specific to the code been already updated for the fix, and covered by the tests, I find it a good opportunity to clean up. If the formatting change is extensive and makes it difficult to understand the functional change, I can see how that may best be done in a separate PR.

In general, I prefer a separate PR unless we're talking about a very minimal change.
To avoid discussions on what such a minimal change is, it may be better to not mix formatting and non-formatting changes at all.

@lemonyte

Copy link
Copy Markdown
Contributor Author

I've added tests for seeking from SeekOrigin.End.
Regarding formatting changes, to be frank I do not see what formatting exactly is causing an issue, but I will keep your feedback in mind for future contributions.
As for exceptions and exception messages, those are out of the scope of this PR, and would best be addressed separately in a new PR.

I do not want to pull this thread any more off-topic than it already is, but I think it is worth noting that while developing this PR I've encountered several issues with the tests. Notably, failures in tests unrelated to the changed portions (see AppVeyor checks 45419708 and 45419751), and failures caused by exception messages not matching (see below and this comment).

 Ctor_PartialSuccessLimit_Zero
   Source: ClientAuthenticationTest.cs line 19
   Duration: < 1 ms

  Message: 
    Assert.AreEqual failed. Expected:<Cannot be less than one.
    Parameter name: partialSuccessLimit>. Actual:<Cannot be less than one. (Parameter 'partialSuccessLimit')>. 

  Stack Trace: 
    ClientAuthenticationTest.Ctor_PartialSuccessLimit_Zero() line 31

If there is anything else I can do to improve this PR, let me know. In any case, hopefully this fix is merged soon.

@drieseng

drieseng commented Nov 19, 2022

Copy link
Copy Markdown
Member

@LemonPi314, your changes are looking good. Thanks!

I'd only like to hold off on merging until I (or someone else) had time to add a few integration tests for this.
Should this take too long, do not hesitate to spam me :-)

@drieseng

Copy link
Copy Markdown
Member

I'll submit a PR for the integration test changes once sshnet/IntegrationTests#8 has landed.

This was referenced Aug 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SFTP file stream not working with System.IO.Compression.Zip Incorrect Seek Operations in SftpFileStream

5 participants