Go言語で、ファイルの「所有者」を拾えるか? You、DLL呼び出しちゃいなよ
発端
java.nio.file.attributeで「所有者」情報が取れる。
PowerShellでも取れる。
では、Go言語はどうだ?
調査
たとえば、C++でそれを行う場合、こういうことをする。
Finding the Owner of a File Object in C++ (Windows)
ここで登場する「GetSecurityInfo関数」はGo言語で使われているか?
調べた結果、使われていない。
大抵のdll呼び出しは、"src/syscall/zsyscall_windows.go"を読むと分かる。
「GetSecurityInfo関数」は、「advapi32.dll」に含まれています。
GetSecurityInfo function (Windows)
Go言語は、内部で「advapi32.dll」を読み込んではいるものの、「GetSecurityInfo関数」を呼び出せるようにはなっていません。
今見ているソースはGo1.8です。
挑戦
ネジがゆるい私は、「よし、DLL呼び出してみよう」と、泥船に足を突っ込んだ。
Go内部では、ロードしているDLLですが、変数が公開されていません。
しかたない、自分でロードすんぜ。
var ( modadvapi32 = syscall.NewLazyDLL("advapi32.dll") procGetSecurityInfo = modadvapi32.NewProc("GetSecurityInfo") )
はい、ただのもろパクリです。
あ、でもsysdll.Add()を挟むのは除外しました、なぜならばinternalはコンパイルエラーになるからです。
NewLazyDLLやNewProcについては、「src/syscall/dll_windows.go」に書かれています。
Procには呼び出し用のポインタレシーバが付いてます。
func (p *Proc) Call(a ...uintptr) (r1, r2 uintptr, lastErr error)
とりあえず、引数を全部uintptrで渡せってことか。
1つ目の引数 HANDLE
handleには、ファイルハンドルを渡すことになる。
os.OpenFileして、fileからFdを得ると、それはuintptrなので、そのまま渡してみる。
2つ目の引数 SE_OBJECT_TYPE
今回は、SE_FILE_OBJECTを渡したいわけです。
SE_OBJECT_TYPE enumeration (Windows)
enumで先頭が0初期化してますから、2つ目の定数SE_FILE_OBJECTは、1ってことですね。
これをuintptrで渡すとか、まどろっこしいなぁ。
3つ目の引数 SECURITY_INFORMATION
今回はOWNER_SECURITY_INFORMATIONを渡したいので、おそらく1です
DWORDとあるので、uint32にしときます。
4つ目の引数 ppsidOwner 型はPSID
これが受け取りたい。ぜひ受け取りたい。
でもPSIDって何?
どうやら、可変長な構造であるSIDをポイントしているポインターっぽい。
var psidOwner = make([]byte, uint32(50)) sid := (unsafe.Pointer(&psidOwner)) ppsidOwner := uintptr(unsafe.Pointer(sid))
ちょっと変数名をどうするか、ちぐはぐな感じになったが、こんな感じで書いてみた。
これは、「src/syscall/security_windows.go」にあるfunc LookupSIDを参考にしている。
残りの引数
どうせオプションだし、渡さないことにする。
ダメだった点
1をあらわしている引数をuintptrにして渡してるのがどうもダメっぽかったので修正。
最終的にこの形にした。1をベタ書きです。。。ひどい。なんだコレ。
r1, r2, lastErr := procGetSecurityInfo.Call(handle, 1, 1, ppsidOwner)
なんとか結果が返ってきた。
ここからバッファ渡してるやつをポイントしなおす。
ownerSID := (*syscall.SID)(unsafe.Pointer(&psidOwner[0])) str, convertErr := ownerSID.String() if convertErr != nil { } else { fmt.Printf("string SID = %s\n", str) } account, domain, accountType, lookUpErr := ownerSID.LookupAccount("") if lookUpErr != nil { } else { fmt.Printf("account = %s\n", account) fmt.Printf("domain = %s\n", domain) fmt.Printf("accountType = %d\n", accountType) }
ownerSID.String()すると「S-X-X-XX-XXXXXXXX」みたいな形式のSIDが取れてます。
ownerSID.LookupAccount("")するとアカウントがとれました。
このあたりのことは「src/syscall/security_windows.go」を見てください。
こまごました点は考慮していませんが、DLL呼び出せちゃいました。
.