Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

SFTP CreateDirectoryAsync is not idempotent

オープン
#1,747 コメント 4 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

評価

難易度
4/5
見積もり時間
3〜5日
初心者へのやさしさ
38/100
issue の種類
機能追加
明瞭さ
おおむね明確
活発さ
静か
技術スタック
csharp
領域
networking

調査の方向性

SftpClient.CreateDirectoryAsync と既存の XML ドキュメントから開始し、ディレクトリがすでに存在する場合の動作を ExistsAsync および報告された SFTP 応答と比較します。要求された冪等な動作またはより具体的な例外のどちらがプロトコルに適合するかを判断します。完了とは、呼び出し元が既存のディレクトリを他の失敗と区別できることです。

索引モデルが issue の本文から書いたものです。

説明

My specifications:
OS: Ubuntu Desktop and Windows 11
Framework: .NET 9
Package version: 2025.1.0

This is my current code:

using SftpClient client = new SftpClient(host, port, username, password);
await client.ConnectAsync(cancellationToken);
try
{
        await client.CreateDirectoryAsync(someDir, cancellationToken);
}
catch (SftpException ex)
{
        // Only some logging code here.
}

When this directory already exists, all I get as an exception message is "failure".

Renci.SshNet.Common.SftpException: failure
 at Renci.SshNet.SubsystemSession.<WaitOnHandleAsync> g__DoWaitAsync | 38_0[T](TaskCompletionSource`1 tcs, Int32 millisecondsTimeout, CancellationToken cancellationToken)
 at Renci.SshNet.SftpClient.CreateDirectoryAsync(String path, CancellationToken cancellationToken)

Problems:

  • I can't know for sure if the exception is about the directory already existing, or whether something else is wrong. Type SftpException with message "failure" is just not descriptive enough.
  • CreateDirectoryAsync is not idempotent. Throwing an exception seems very counter-intuitive for a method like this.
  • The SftpException is not described in the method documentation:
        /// <summary>
        /// Asynchronously requests to create a remote directory specified by path.
        /// </summary>
        /// <param name="path">Directory path to create.</param>
        /// <param name="cancellationToken">The <see cref="CancellationToken"/> to observe.</param>
        /// <returns>A <see cref="Task"/> that represents the asynchronous create directory operation.</returns>
        /// <exception cref="ArgumentException"><paramref name="path"/> is <see langword="null"/> or contains only whitespace characters.</exception>
        /// <exception cref="SshConnectionException">Client is not connected.</exception>
        /// <exception cref="SftpPermissionDeniedException">Permission to create the directory was denied by the remote host. <para>-or-</para> A SSH command was denied by the server.</exception>
        /// <exception cref="SshException">A SSH error where <see cref="Exception.Message"/> is the message from the remote host.</exception>
        /// <exception cref="ObjectDisposedException">The method was called after the client was disposed.</exception>

If I would want to do this properly, I need quite a lot of code. This is the extension method I wrote:

internal static async ValueTask EnsureDirectoryExists(this SftpClient client, string directory, CancellationToken cancellationToken)
{
    bool directoryExists = await client.ExistsAsync(directory, cancellationToken);
    if (directoryExists) return;

    try
    {
        await client.CreateDirectoryAsync(directory, cancellationToken);
    }
    catch (SftpException ex)
    {
        try
        {
            bool directoryAlreadyExisted= await client.ExistsAsync(directory, cancellationToken);
            if (directoryAlreadyExisted)
            {
                // The directory already existed, so we can ignore the exception.
                return;
            }
            else
            {
                // The directory does not exist.
                throw;
            }
        }
        catch (SftpException)
        {
            // Failed checking if the directory exists.
            // Throwing the original exception as that illustrates what really is the problem here.
            throw ex;
        }
    }
}

There are 2 ways this could be fixed:

  1. Add an EnsureDirectoryExists method. I'm sure this can be done much closer to the actual SFTP protocol than the extension method I created.
  2. Add an SftpDirectoryAlreadyExistsException that inherits from SftpException.

I'm very curious, is there a reason why something like EnsureDirectoryExists was never added to SSH.NET?

主要言語
C#
スター
4.4k
フォーク
994
PR マージ指標
30日以内にマージされた PR はありません

環境構築

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

sshnet/SSH.NET のほかの issue

sshnet/SSH.NET の issue をすべて見る

似ている issue

C# の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。