[インデックス 14006] ファイルの概要
このコミットは、Go言語の os/user
パッケージにおけるWindows環境でのユーザー情報ルックアップに関するバグ修正です。具体的には、ドメインユーザーがドメインから切断された状態にある場合に、ユーザーのフルネームを取得する際に発生するエラーをハンドリングし、その際にユーザー名をフルネームとして代替使用するように変更しています。これにより、特定の条件下でユーザー情報取得が失敗する問題(Issue 4113)が解決されます。
コミット
commit 5f7f9062dbebd68978b449e241e66873320186ca
Author: Shivakumar GN <shivakumar.gn@gmail.com>
Date: Wed Oct 3 10:33:09 2012 +1000
os/user : use username as fullname if all else fails (on windows)
Fixes #4113.
R=golang-dev, alex.brainman
CC=golang-dev
https://golang.org/cl/6545054
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/5f7f9062dbebd68978b449e241e66873320186ca
元コミット内容
commit 5f7f9062dbebd68978b449e241e66873320186ca
Author: Shivakumar GN <shivakumar.gn@gmail.com>
Date: Wed Oct 3 10:33:09 2012 +1000
os/user : use username as fullname if all else fails (on windows)
Fixes #4113.
R=golang-dev, alex.brainman
CC=golang-dev
https://golang.org/cl/6545054
---
src/pkg/os/user/lookup_windows.go | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n
diff --git a/src/pkg/os/user/lookup_windows.go b/src/pkg/os/user/lookup_windows.go
index 90fe0662e6..3626a4e9f0 100644
--- a/src/pkg/os/user/lookup_windows.go
+++ b/src/pkg/os/user/lookup_windows.go
@@ -27,7 +27,9 @@ func lookupFullName(domain, username, domainAndUser string) (string, error) {
var p *byte
e = syscall.NetUserGetInfo(d, u, 10, &p)
if e != nil {
- return "", e
+ // path executed when a domain user is disconnected from the domain
+ // pretend username is fullname
+ return username, nil
}
defer syscall.NetApiBufferFree(p)
i := (*syscall.UserInfo10)(unsafe.Pointer(p))\n
変更の背景
このコミットは、Go言語の os/user
パッケージがWindows環境でユーザーのフルネームを取得する際に発生する特定のバグを修正するために行われました。このバグは、Go issue trackerの Issue 4113 で報告されていました。
報告された問題は、Windows環境において、ドメインユーザーがドメインコントローラーから切断された状態(例えば、ノートPCが社内ネットワークから持ち出された状態)で os/user
パッケージの Lookup
関数(または内部的に呼び出される関連関数)を使用すると、ユーザーのフルネームを取得する syscall.NetUserGetInfo
関数がエラーを返すというものでした。このエラーが発生すると、os/user
パッケージはユーザー情報を適切に取得できず、アプリケーションが予期せぬ動作をしたり、クラッシュしたりする可能性がありました。
このコミットの目的は、このような特定のシナリオで NetUserGetInfo
がエラーを返した場合に、エラーをそのまま返すのではなく、ユーザー名をフルネームとして代替的に使用することで、より堅牢なユーザー情報取得メカニズムを提供することです。これにより、ドメインから切断された状態のドメインユーザーに対しても、少なくともユーザー名という形で情報を提供できるようになり、アプリケーションの安定性が向上します。
前提知識の解説
このコミットを理解するためには、以下の概念について知っておく必要があります。
-
Go言語の
os/user
パッケージ:os/user
パッケージは、現在のシステムユーザーに関する情報を取得するためのGo言語の標準ライブラリです。ユーザー名、UID/GID、ホームディレクトリ、フルネームなどの情報を提供します。OSに依存する実装を持っており、Windows、Linux、macOSなど、各OSのAPIを内部的に呼び出して情報を取得します。 -
Windows API
NetUserGetInfo
:NetUserGetInfo
は、WindowsのNet Services APIの一部であり、特定のユーザーアカウントに関する情報を取得するために使用されます。この関数は、ユーザー名、パスワード、権限、フルネームなど、さまざまなレベル(情報レベル)のユーザー情報を取得できます。このコミットでは、情報レベル10
を使用しており、これはUSER_INFO_10
構造体に対応し、ユーザーのフルネーム(usri10_full_name
)などの基本情報を含みます。 -
Windows API
NetApiBufferFree
:NetApiBufferFree
は、Net Services APIによって割り当てられたメモリを解放するために使用される関数です。NetUserGetInfo
のような関数は、情報を格納するために内部的にメモリを割り当て、そのポインタを呼び出し元に返します。このメモリは、使用後にNetApiBufferFree
を呼び出して明示的に解放する必要があります。これはメモリリークを防ぐために重要です。 -
syscall
パッケージ: Go言語のsyscall
パッケージは、低レベルのオペレーティングシステムプリミティブへのアクセスを提供します。これには、システムコール、プロセス管理、ファイルシステム操作、ネットワーク操作などが含まれます。Windows固有のAPI(NetUserGetInfo
など)をGoから呼び出す際には、このsyscall
パッケージを介して行われます。 -
unsafe.Pointer
: Go言語のunsafe
パッケージは、Goの型安全性をバイパスする操作を可能にします。unsafe.Pointer
は、任意の型のポインタを保持できる特殊なポインタ型です。C言語のvoid*
に似ています。このコミットでは、syscall.NetUserGetInfo
から返された生バイトポインタをsyscall.UserInfo10
構造体へのポインタに変換するために使用されています。これは、Goの型システムでは直接許可されていない操作であり、低レベルのシステムプログラミングで必要となる場合があります。 -
ドメインユーザーとドメインからの切断: WindowsのActive Directoryドメイン環境では、ユーザーアカウントはドメインコントローラーによって管理されます。ドメインユーザーは、ドメイン内のどのコンピューターからでもログインできます。しかし、ノートPCなどがドメインネットワークから切断された状態(例えば、自宅や外出先)にある場合、ドメインコントローラーとの通信ができないため、一部のドメイン関連の操作(ユーザー情報の詳細な取得など)が失敗することがあります。このコミットは、まさにこの「ドメインから切断された状態のドメインユーザー」が原因で発生する問題に対処しています。
技術的詳細
このコミットの技術的な核心は、Windows API NetUserGetInfo
のエラーハンドリングの改善にあります。
src/pkg/os/user/lookup_windows.go
ファイルの lookupFullName
関数は、指定されたユーザーのフルネームを取得する役割を担っています。この関数は内部で syscall.NetUserGetInfo
を呼び出しています。
元のコードでは、syscall.NetUserGetInfo
の呼び出しがエラー e
を返した場合、そのエラーをそのまま呼び出し元に返していました。
e = syscall.NetUserGetInfo(d, u, 10, &p)
if e != nil {
return "", e // エラーをそのまま返す
}
しかし、Issue 4113で報告されたように、ドメインユーザーがドメインから切断された状態にある場合、NetUserGetInfo
はエラーを返します。このエラーは、ユーザーが存在しないことを意味するものではなく、単にドメインコントローラーにアクセスできないために詳細な情報を取得できないことを意味します。このような状況でエラーを返してしまうと、os/user
パッケージの利用者はユーザー情報を取得できなくなってしまいます。
このコミットでは、この特定のエラーケースを考慮し、エラーが発生した場合のフォールバックメカニズムを導入しています。
e = syscall.NetUserGetInfo(d, u, 10, &p)
if e != nil {
// path executed when a domain user is disconnected from the domain
// pretend username is fullname
return username, nil // エラーが発生しても、ユーザー名をフルネームとして返し、エラーは返さない
}
変更後のコードでは、NetUserGetInfo
がエラーを返した場合でも、空文字列とエラーを返す代わりに、引数として渡された username
をフルネームとして返し、エラーとしては nil
(エラーなし)を返します。
この変更の意図は、以下のコメントに明確に示されています。
// path executed when a domain user is disconnected from the domain
// pretend username is fullname
これは、「ドメインユーザーがドメインから切断された場合に実行されるパスであり、ユーザー名をフルネームとして扱う」という設計判断を示しています。これにより、完全なフルネームが取得できない場合でも、少なくともユーザー名という形で有効な情報を返すことで、os/user
パッケージの堅牢性と使いやすさを向上させています。
この変更は、Windowsのユーザー管理の複雑さと、ネットワーク接続の状態がAPIの挙動に与える影響を考慮した、実用的な解決策と言えます。
コアとなるコードの変更箇所
--- a/src/pkg/os/user/lookup_windows.go
+++ b/src/pkg/os/user/lookup_windows.go
@@ -27,7 +27,9 @@ func lookupFullName(domain, username, domainAndUser string) (string, error) {
var p *byte
e = syscall.NetUserGetInfo(d, u, 10, &p)
if e != nil {
- return "", e
+ // path executed when a domain user is disconnected from the domain
+ // pretend username is fullname
+ return username, nil
}
defer syscall.NetApiBufferFree(p)
i := (*syscall.UserInfo10)(unsafe.Pointer(p))\n
コアとなるコードの解説
変更は src/pkg/os/user/lookup_windows.go
ファイル内の lookupFullName
関数に集中しています。
この関数は、Windows環境でユーザーのフルネームを取得するために使用されます。 引数:
domain
: ユーザーが属するドメイン名。username
: ユーザー名。domainAndUser
: ドメインとユーザー名を組み合わせた文字列(例:DOMAIN\username
)。
関数内部では、以下の処理が行われます。
-
syscall.NetUserGetInfo(d, u, 10, &p)
を呼び出して、ユーザー情報を取得します。d
: ドメイン名(domain
)。u
: ユーザー名(username
)。10
: 取得する情報レベル。これはUSER_INFO_10
構造体に対応し、ユーザーのフルネームなどの基本情報を含みます。&p
: 取得したユーザー情報が格納されるメモリへのポインタ。
-
NetUserGetInfo
の呼び出し結果として返されるエラーe
をチェックします。-
変更前:
if e != nil { return "", e }
NetUserGetInfo
がエラーを返した場合 (e != nil
)、関数は空文字列と取得したエラーe
をそのまま返していました。これは、ユーザー情報の取得に失敗したことを意味します。 -
変更後:
if e != nil { // path executed when a domain user is disconnected from the domain // pretend username is fullname return username, nil }
NetUserGetInfo
がエラーを返した場合でも、関数はエラーを返さず、代わりにusername
引数の値をフルネームとして返し、エラーとしてはnil
を返します。 この変更により、ドメインユーザーがドメインから切断されているなどの理由でNetUserGetInfo
が失敗した場合でも、os/user
パッケージは少なくともユーザー名を提供できるようになり、アプリケーションがクラッシュしたり、不完全な情報を返したりするのを防ぎます。
-
-
defer syscall.NetApiBufferFree(p)
:NetUserGetInfo
によって割り当てられたメモリp
を、関数の終了時に必ず解放するようにdefer
ステートメントでNetApiBufferFree
を呼び出しています。これはメモリリークを防ぐための重要な処理であり、変更前後で変わりません。 -
i := (*syscall.UserInfo10)(unsafe.Pointer(p))
: 取得した生バイトポインタp
をsyscall.UserInfo10
構造体へのポインタに型変換しています。 -
return syscall.UTF16ToString(i.Usri10_full_name), nil
:UserInfo10
構造体からUsri10_full_name
フィールド(UTF-16エンコードされたフルネーム)を取得し、syscall.UTF16ToString
を使用してGoの文字列に変換して返します。これはエラーが発生しなかった場合の通常の成功パスです。
このコミットの主要な変更点は、NetUserGetInfo
がエラーを返した場合の挙動を、エラーをそのまま返すのではなく、ユーザー名を代替のフルネームとして返すように変更した点にあります。これにより、特定の条件下でのユーザー情報取得の失敗を許容し、より堅牢な動作を実現しています。
関連リンク
- GitHubコミットページ: https://github.com/golang/go/commit/5f7f9062dbebd68978b449e241e66873320186ca
- Go Issue 4113: https://github.com/golang/go/issues/4113
- Gerrit Change-ID: https://golang.org/cl/6545054
参考にした情報源リンク
- Go issue tracker: https://github.com/golang/go/issues/4113
- Microsoft Learn - NetUserGetInfo function: https://learn.microsoft.com/en-us/windows/win32/api/lmuseflg/nf-lmuseflg-netusergetinfo
- Microsoft Learn - NetApiBufferFree function: https://learn.microsoft.com/en-us/windows/win32/api/lmapibuf/nf-lmapibuf-netapibufferfree
- Go Documentation - os/user package: https://pkg.go.dev/os/user
- Go Documentation - syscall package: https://pkg.go.dev/syscall
- Go Documentation - unsafe package: https://pkg.go.dev/unsafe