2021年3月21日日曜日

Directory.CreateDirectory()を実行する前にディレクトリが作成可能かを確認する

ここ数日悩んでいた件です。

C#にはDirectory.CreateDirectory()というメソッドがあり、指定したパスにディレクトリを作ることができます。しかし、当然どんなところにでも作れるわけではなく、例えば存在しないドライブに書き込もうとした、管理者権限じゃないと書き込めないフォルダだった("C:\Program Files"など)、書き込み権限の無いフォルダだったなどの理由で、このメソッドは実行時に失敗する(例外を吐く)可能性があります。

そこで、実際にディレクトリを作るわけではなく、事前にディレクトリを作ることができるのかを確認する方法を模索していました。

 

結論を言います。

良いから「ディレクトリを1回作ってみて、作れたらそのディレクトリを削除する」という方法で片付けろ。

 

いろいろ調べていたのですが、これ以上良い方法が見つかりませんでした(逆に何かいい方法知っている人いたら教えてください…)。

フォルダの書き込み権限を確認する

一応、C#でもフォルダの書き込み権限を確認することはできます。ただし、Windows依存のコードになります。

var access = new DirectoryInfo(path).GetAccessControl();
var rules = access.GetAccessRules(true, true, typeof(NTAccount));

ただし、このGetAccessControl()はFileSystemAclExtensions.GetAccessControl()拡張メソッドで、Nuget経由でパッケージをインストールする必要があります。

このGetAccessRules()メソッドが返す値が、いわゆるフォルダを右クリック→プロパティ→セキュリティ→詳細設定で出てくるフォルダの読み書き権限に相当するものとなります。

今実行中のアプリケーションとして書き込めるかどうかを知りたいだけなのに、ファイルシステムの権限情報が全部出てくるのです。今の自分はこのどれに相当するのかを見分けなければなりません。暗礁に乗り上げてきた気がしてきました。

実行中のアプリケーションのプリンシパルを取得する

実行中のアプリケーションのプリンシパルを取得しようとしたら、WindowsIdentity.Current()を呼ぶことになります。

そのメソッドが返したWindowsIdentityインスタンスのNameプロパティを見ると現在実行中のユーザー名、Groupsプロパティを見ると自身が属するグループ名が出てきます。これらを前項のプリンシパルと突き合わせれば良いのかと思いきや、どうもそれは完全ではないようです。例えば、ネットワークドライブ上のフォルダはそれでうまくいきませんでした。

 

そもそも、OS上では充分に抽象化された「ディレクトリ」いう概念でプログラムを書いていたのに、一気に具体的なファイルシステムがどうとか、Windowsのアクセスコントロール機能がどうとか、そういうレイヤーのプログラムを書くことを強いられてしまいます。世の中には多種多様な記録媒体やファイルシステムがあり、それらすべてについて正確に書き込みができるかどうかを判断するコードをこのレベルで書くのはそう簡単にできるものではありません。

ですので、最初に書いた通り、ひとまずディレクトリを作ってみて確かめるのがやはり一番いい方法という結論に私は達しました。 

ディレクトリを作って削除するのも、実際のファイルシステムを汚している感があったり、後のDeleteで誤ったフォルダを削除してしまわないように慎重にコードを書く必要があったり、そもそも例外発生前提でプログラムを書く必要があるのでそれなりに神経を使う必要があったりと、こちらも何かと苦労があります。ですから、書き込み権限を事前に確認して終わらせようという発想になっていたはずです。

はあ、こういうの、標準でDirectory.CanCreateDirectory()みたいなメソッドが用意されてワンタッチに判定してもらえるようになったりしないのかなあ。しないんだろうなあ。抽象化(=様々なOS/ファイルシステムで正常に動作するように)するのが難しいから用意されていないんだろうなあ…。

何かいい方法ご存知の方いれば教えてください。

0 件のコメント:

コメントを投稿