Introduction

前回は、System.IO.FileStreamを使ってファイルのアクセス権限を確認してみました。

今回は少し高度なファイルへのアクセス可否についてです。

P/Invoke

ファイルに実際にアクセスしてみて、アクセスできるかどうかを試すって、結構良くないと思います。
かといって、Windowsの.NET Frameworkにアクセスできるかどうかを試すAPIは無かったはずです。

ですが、Linuxのシステムコールを使えば、それが実現可能です。
システムコールの呼び出しは当然P/Invokeで実現します。

Linuxのシステムコールで、ファイルに対して、読み込みができるか、書き込みが出来るか、実行できるか、という状態を調べるには、accessを呼び出します。

大抵のシステムコールはlibcに定義されています。
実際に、libcに定義されている関数の一覧からaccessを探してみます。
そのために、nmコマンドに -D オプションを渡します。

1
2
3
4
5
$ nm -D /usr/lib64/lib.so.6 | grep access
00000000000e8ce0 W access
00000000000e8d10 W eaccess
00000000000e8d10 W euidaccess
00000000000e8e30 T faccessat

これでlibcにaccessが定義されていることがわかりました。
次にaccessの使い方です。
定義は下記です。

1
int access(const char *pathname, int mode);

第一引数はファイルパスです。
第二引数は調べるモードを表します。

意味
F_OK 存在するか
R_OK 読み込み可能か
W_OK 書き込み可能か
X_OK 実行可能か

第二引数のモードを組み合わせることで、アクセス可否をチェックします。
指定したモードを満たせば、0を返します。
満たさない場合は-1を返します。

Try

今回は、**/etc/passwd**を調べてみます。

1
2
$ ls -la /etc/passwd
-rw-r--r--. 1 root root 2336 4月 20 23:58 /etc/passwd

root以外は、読み込みしかできません。

では、これをコードで調べてみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;

namespace ConsoleApplication
{
public class Program
{

internal enum Mode : int
{
F_OK = 0,
X_OK = 1,
W_OK = 2,
R_OK = 4
}

[DllImport("libc")]
internal static extern int access(string path, Mode mode);

public static void Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("Must specify full path!!");
return;
}

var path = args[0];
if (access(path, Mode.F_OK) != 0)
{
Console.WriteLine("Specified path does not exist!!");
return;
}

Console.WriteLine(access(path, Mode.R_OK) == 0 ? "Can read" : "Can not read");
Console.WriteLine(access(path, Mode.W_OK) == 0 ? "Can write" : "Can not write");
Console.WriteLine(access(path, Mode.X_OK) == 0 ? "Can exec" : "Can not exec");
}
}

}

引数で指定したファイルの読み込み、書き込み、実行の可否を調べます。

前回同様、まずは、一般ユーザで確認します。
whichでdotnetコマンドの場所を調べているのは、rootユーザでdotnetまでのパスが通っていなかったので、実施しているだけです。
パスが通っているなら不要です。
引数のファイルパスはdotnet runの直後に追記することで指定できます。

1
2
3
4
5
6
7
8
# dotnetコマンドの場所を調べます
$ which dotnet
/opt/rh/rh-dotnetcore11/root/usr/bin/dotnet

$ /opt/rh/rh-dotnetcore11/root/usr/bin/dotnet run /etc/passwd
Can read
Can not write
Can not exec

読み込みしかできないようです。
ls -laで調べたとおりです。

続いて、rootで確認します。

1
2
3
4
$ sudo /opt/rh/rh-dotnetcore11/root/usr/bin/dotnet run /etc/passwd
Can read
Can write
Can not exec

読み書きができて、実行できないことが確認できました。
これも、ls -laで調べたとおりです。

では、存在しないファイルを指定してみます。

1
2
$ /opt/rh/rh-dotnetcore11/root/usr/bin/dotnet run /etc/passwd2
Specified path does not exist!!

きちんと存在しないことを確認してくれます。

フォルダも調べることが出来ます。

1
2
3
4
5
6
7
8
9
10
$ ls -lad /etc
drwxr-xr-x. 141 root root 8192 5月 27 16:15 /etc
$ /opt/rh/rh-dotnetcore11/root/usr/bin/dotnet run /etc
Can read
Can not write
Can exec
$ sudo /opt/rh/rh-dotnetcore11/root/usr/bin/dotnet run /etc
Can read
Can write
Can exec

Conclusion

Windows同様、P/Invokeが利用可能であることを確認できました。