[インデックス 14289] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにLinuxの拡張属性(Extended Attributes, xattr)を操作するためのシステムコールを追加するものです。具体的には、ファイルの拡張属性の取得、設定、一覧表示、削除を行うGetxattr
, Setxattr
, Listxattr
, Removexattr
の各関数が導入されました。これにより、GoプログラムからLinuxファイルシステムの拡張属性を直接操作できるようになります。
コミット
commit 4094c1bae7cb6ace9311e560623bc0eb80bae91e
Author: Han-Wen Nienhuys <hanwen@google.com>
Date: Thu Nov 1 14:49:38 2012 -0400
syscall: add {Get,Set,List,Remove}xattr on Linux.
R=golang-dev, minux.ma, fullung, dave, rsc, hanwenn
CC=golang-dev
https://golang.org/cl/6350074
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/4094c1bae7cb6ace9311e560623bc0eb80bae91e
元コミット内容
syscall: add {Get,Set,List,Remove}xattr on Linux.
このコミットは、Linux上で拡張属性(xattr)を操作するためのGetxattr
, Setxattr
, Listxattr
, Removexattr
システムコールをsyscall
パッケージに追加します。
変更の背景
Linuxのファイルシステムには、標準的なファイル属性(パーミッション、所有者、タイムスタンプなど)に加えて、任意のメタデータをファイルやディレクトリに関連付けることができる「拡張属性(Extended Attributes, xattr)」という機能があります。これは、アクセス制御リスト(ACLs)、セキュリティラベル(SELinuxなど)、ファイルチェックサム、カスタムタグなど、様々な目的で利用されます。
Go言語のsyscall
パッケージは、オペレーティングシステムの低レベルな機能、特にシステムコールへのインターフェースを提供します。このコミット以前は、Goプログラムから直接Linuxの拡張属性を操作する標準的な方法が提供されていませんでした。そのため、Goでファイルシステムのより高度な機能を活用したり、特定のセキュリティ要件を満たすアプリケーションを開発したりする際に、この機能の欠如が制約となっていました。
この変更の背景には、Go言語がシステムプログラミングやインフラストラクチャツール開発においてより強力な選択肢となるよう、Linuxカーネルが提供する重要な機能へのアクセスを拡充するという意図があります。拡張属性のサポートを追加することで、Goプログラムはファイルシステムレベルでのより詳細な制御と情報管理が可能になり、より堅牢で機能豊富なアプリケーションを構築できるようになります。
前提知識の解説
1. Linux拡張属性 (Extended Attributes, xattr)
Linuxの拡張属性は、ファイルやディレクトリに名前と値のペア形式で追加のメタデータを保存する機能です。これは、通常のファイルデータとは別に保存され、ファイルシステムによってサポートされます。主な特徴は以下の通りです。
- 名前空間 (Namespaces): 拡張属性は通常、
user.
,system.
,security.
,trusted.
といった名前空間でプレフィックスされます。user.*
: ユーザーが自由に設定できる属性。通常のファイルパーミッションに従います。system.*
: カーネルがACLsなどのシステムオブジェクトに使用します。security.*
: SELinuxなどのセキュリティモジュールが使用します。trusted.*
:CAP_SYS_ADMIN
権限を持つプロセスのみがアクセスできる属性。
- 用途: ACLsの実装、SELinuxなどの強制アクセス制御、ファイルチェックサムの保存、カスタムタグ付けなど。
- システムコール: Linuxカーネルは、拡張属性を操作するための以下のシステムコールを提供しています。
setxattr(2)
: 属性を設定または更新。getxattr(2)
: 属性の値を取得。listxattr(2)
: 属性名のリストを取得。removexattr(2)
: 属性を削除。 これらには、シンボリックリンク自体を操作するl*xattr
や、ファイルディスクリプタを操作するf*xattr
のバリエーションも存在します。
2. Go言語の syscall
パッケージ
syscall
パッケージは、Goプログラムからオペレーティングシステムの低レベルなプリミティブ関数(システムコール)にアクセスするためのインターフェースを提供します。このパッケージは、OS固有の定数、データ構造、およびシステムコールを直接呼び出すための関数を含んでいます。
- システムコール: ユーザー空間のプログラムがカーネルにサービスを要求するためのメカニズムです。例えば、ファイルの読み書き、プロセスの作成、ネットワーク通信などがシステムコールを通じて行われます。
//sys
および//sysnb
ディレクティブ:syscall
パッケージのGoソースファイル(例:syscall_linux.go
)には、//sys
や//sysnb
といった特殊なコメントディレクティブが記述されています。これらはgo generate
ツールによって解釈され、実際のシステムコールを呼び出すためのアーキテクチャ固有のGo関数(例:zsyscall_linux_amd64.go
内の関数)が自動生成されます。//sys
: 通常のシステムコール。エラーハンドリングや引数の変換など、より複雑な処理を伴う場合があります。//sysnb
: "no blocking" の略で、ブロックしないシステムコール。通常、引数が少なく、単純なラッパーで済む場合に用いられます。
3. unsafe.Pointer
Go言語のunsafe
パッケージは、型安全性をバイパスしてメモリを直接操作するための機能を提供します。unsafe.Pointer
は、任意の型のポインタを保持できる特殊なポインタ型で、C言語のvoid*
に似ています。システムコールを呼び出す際には、Goのバイトスライスや文字列をC言語のポインタとしてカーネルに渡す必要があるため、unsafe.Pointer
が頻繁に利用されます。
技術的詳細
このコミットは、Linuxの拡張属性を操作するための4つの主要なシステムコール(Getxattr
, Setxattr
, Listxattr
, Removexattr
)をGoのsyscall
パッケージに追加します。
-
syscall_linux.go
の変更:- このファイルは、Linuxシステムコールに対するGoのインターフェースを定義する中心的なファイルです。
//sys
ディレクティブを使用して、新しいxattr関連のシステムコールのGo関数シグネチャが追加されています。このディレクティブは、go generate
ツールがアーキテクチャ固有のzsyscall_linux_*.go
ファイルを生成する際に使用されます。- 追加された行は以下の通りです。
//sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
//sys Listxattr(path string, dest []byte) (sz int, err error)
//sys Removexattr(path string, attr string) (err error)
//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
- 既存のコメントアウトされた(おそらく未実装または不要になった)xattr関連のシステムコール定義が削除されています。これは、新しい実装がそれらを置き換えるためです。
-
zsyscall_linux_386.go
,zsyscall_linux_amd64.go
,zsyscall_linux_arm.go
の変更:- これらのファイルは、
syscall_linux.go
内の//sys
ディレクティブに基づいてgo generate
によって自動生成される、アーキテクチャ固有のシステムコールラッパーを含んでいます。 - コミットでは、各アーキテクチャ(386, amd64, arm)向けに、
Getxattr
,Setxattr
,Listxattr
,Removexattr
の実際のGo関数実装が追加されています。 - これらの関数は、Goの文字列やバイトスライスをCスタイルのポインタに変換し、
Syscall
またはSyscall6
関数(Goの低レベルなシステムコール呼び出し関数)を使用して対応するLinuxシステムコールを呼び出します。 unsafe.Pointer
が、Goのバイトスライス([]byte
)の先頭アドレスをシステムコールに渡すために使用されています。例えば、dest
やdata
スライスが空の場合に_zero
(ゼロ値のバイトへのポインタ)を使用することで、ヌルポインタの代わりに有効なアドレスを渡すようにしています。- システムコールの戻り値(レジスタ
r0
,r1
など)とエラーコード(e1
)をGoの戻り値(sz
,err
)に適切にマッピングしています。
- これらのファイルは、
この変更により、Go開発者はsyscall
パッケージを介して、Linuxの拡張属性を直接、かつGoの型システムに適合した形で操作できるようになりました。これは、Goが提供するOSとの低レベルなインタラクション能力をさらに拡張するものです。
コアとなるコードの変更箇所
src/pkg/syscall/syscall_linux.go
--- a/src/pkg/syscall/syscall_linux.go
+++ b/src/pkg/syscall/syscall_linux.go
@@ -849,6 +849,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb Getppid() (ppid int)
//sysnb Getrusage(who int, rusage *Rusage) (err error)
//sysnb Gettid() (tid int)
+//sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
//sys InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error)
//sysnb InotifyInit() (fd int, err error)
//sysnb InotifyInit1(flags int) (fd int, err error)
@@ -856,6 +857,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb Kill(pid int, sig Signal) (err error)
//sys Klogctl(typ int, buf []byte) (n int, err error) = SYS_SYSLOG
//sys Link(oldpath string, newpath string) (err error)
+//sys Listxattr(path string, dest []byte) (sz int, err error)
//sys Mkdir(path string, mode uint32) (err error)
//sys Mkdirat(dirfd int, path string, mode uint32) (err error)
//sys Mknod(path string, mode uint32, dev int) (err error)
@@ -866,6 +868,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb prlimit(pid int, resource int, old *Rlimit, newlimit *Rlimit) (err error) = SYS_PRLIMIT64
//sys read(fd int, p []byte) (n int, err error)
//sys Readlink(path string, buf []byte) (n int, err error)
+//sys Removexattr(path string, attr string) (err error)
//sys Rename(oldpath string, newpath string) (err error)
//sys Renameat(olddirfd int, oldpath string, newdirfd int, newpath string) (err error)
//sys Rmdir(path string) (err error)
@@ -875,6 +878,7 @@ func Mount(source string, target string, fstype string, flags uintptr, data stri
//sysnb Setsid() (pid int, err error)
//sysnb Settimeofday(tv *Timeval) (err error)
//sysnb Setuid(uid int) (err error)
+//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
//sys Symlink(oldpath string, newpath string) (err error)
//sys Sync()
//sysnb Sysinfo(info *Sysinfo_t) (err error)
@@ -954,7 +958,6 @@ func Munmap(b []byte) (err error) {
// Getitimer
// Getpmsg
// Getpriority
-// Getxattr
// IoCancel
// IoDestroy
// IoGetevents
@@ -966,7 +969,6 @@ func Munmap(b []byte) (err error) {
// KexecLoad
// Keyctl
// Lgetxattr
-// Listxattr
// Llistxattr
// LookupDcookie
// Lremovexattr
@@ -1004,7 +1006,6 @@ func Munmap(b []byte) (err error) {
// Readahead
// Readv
// RemapFilePages
-// Removexattr
// RequestKey
// RestartSyscall
// RtSigaction
@@ -1033,7 +1034,6 @@ func Munmap(b []byte) (err error) {
// SetThreadArea
// SetTidAddress
// Setpriority
-// Setxattr
// Shmat
// Shmctl
// Shmdt
@@ -1059,5 +1059,4 @@ func Munmap(b []byte) (err error) {
// Vmsplice
// Vserver
// Waitid
-// Writev
// _Sysctl
src/pkg/syscall/zsyscall_linux_386.go
(amd64, armも同様のパターン)
--- a/src/pkg/syscall/zsyscall_linux_386.go
+++ b/src/pkg/syscall/zsyscall_linux_386.go
@@ -527,6 +527,33 @@ func Gettid() (tid int) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Getxattr(path string, attr string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(dest) > 0 {
+ _p2 = unsafe.Pointer(&dest[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall6(SYS_GETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(dest)), 0, 0)
+ sz = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func InotifyAddWatch(fd int, pathname string, mask uint32) (watchdesc int, err error) {
var _p0 *byte
_p0, err = BytePtrFromString(pathname)
@@ -623,6 +650,28 @@ func Link(oldpath string, newpath string) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Listxattr(path string, dest []byte) (sz int, err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 unsafe.Pointer
+ if len(dest) > 0 {
+ _p1 = unsafe.Pointer(&dest[0])
+ } else {
+ _p1 = unsafe.Pointer(&_zero)
+ }
+ r0, _, e1 := Syscall(SYS_LISTXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(_p1), uintptr(len(dest)))
+ sz = int(r0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Mkdir(path string, mode uint32) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(path)
@@ -772,6 +821,26 @@ func Readlink(path string, buf []byte) (n int, err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Removexattr(path string, attr string) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ _, _, e1 := Syscall(SYS_REMOVEXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Rename(oldpath string, newpath string) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(oldpath)
@@ -900,6 +969,32 @@ func Setuid(uid int) (err error) {
// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+func Setxattr(path string, attr string, data []byte, flags int) (err error) {
+ var _p0 *byte
+ _p0, err = BytePtrFromString(path)
+ if err != nil {
+ return
+ }
+ var _p1 *byte
+ _p1, err = BytePtrFromString(attr)
+ if err != nil {
+ return
+ }
+ var _p2 unsafe.Pointer
+ if len(data) > 0 {
+ _p2 = unsafe.Pointer(&data[0])
+ } else {
+ _p2 = unsafe.Pointer(&_zero)
+ }
+ _, _, e1 := Syscall6(SYS_SETXATTR, uintptr(unsafe.Pointer(_p0)), uintptr(unsafe.Pointer(_p1)), uintptr(_p2), uintptr(len(data)), uintptr(flags), 0)
+ if e1 != 0 {
+ err = e1
+ }
+ return
+}
+
+// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
+
func Symlink(oldpath string, newpath string) (err error) {
var _p0 *byte
_p0, err = BytePtrFromString(oldpath)
コアとなるコードの解説
このコミットの核心は、Goのsyscall
パッケージがLinuxの拡張属性システムコールを呼び出すためのラッパー関数を提供することです。
-
syscall_linux.go
における//sys
ディレクティブ://sys Getxattr(path string, attr string, dest []byte) (sz int, err error)
:path
: 拡張属性を取得するファイルのパス。attr
: 取得する拡張属性の名前。dest
: 取得した属性値を格納するバイトスライス。sz
: 取得した属性値のサイズ(バイト数)。err
: エラー情報。
//sys Listxattr(path string, dest []byte) (sz int, err error)
:path
: 拡張属性名を一覧表示するファイルのパス。dest
: 取得した属性名(ヌル終端された文字列のリスト)を格納するバイトスライス。sz
: 取得した属性名の合計サイズ。err
: エラー情報。
//sys Removexattr(path string, attr string) (err error)
:path
: 拡張属性を削除するファイルのパス。attr
: 削除する拡張属性の名前。err
: エラー情報。
//sys Setxattr(path string, attr string, data []byte, flags int) (err error)
:path
: 拡張属性を設定するファイルのパス。attr
: 設定する拡張属性の名前。data
: 設定する属性値のバイトスライス。flags
: 属性設定の動作を制御するフラグ(例:XATTR_CREATE
で新規作成のみ、XATTR_REPLACE
で既存属性の置き換えのみ)。err
: エラー情報。
これらのディレクティブは、Goの関数シグネチャを定義し、
go generate
ツールに対して、対応するLinuxシステムコールを呼び出すための低レベルなGoコードを生成するように指示します。 -
zsyscall_linux_*.go
における生成された関数:- これらのファイル(例:
zsyscall_linux_386.go
)には、上記の//sys
ディレクティブに基づいて生成された実際のシステムコールラッパー関数が含まれています。 - 各関数は、Goの文字列(
path
,attr
)をCスタイルのヌル終端バイトポインタ(_p0
,_p1
)に変換するためにBytePtrFromString
を使用します。 - バイトスライス(
dest
,data
)は、その先頭アドレスをunsafe.Pointer
を使って取得し、システムコールに渡されます。スライスが空の場合には、_zero
というゼロ値のバイトへのポインタが使用され、有効なアドレスが常に渡されるように工夫されています。 Syscall
またはSyscall6
関数が、実際のLinuxシステムコールを呼び出します。これらの関数は、システムコール番号(例:SYS_GETXATTR
)と引数をuintptr
型で受け取ります。- システムコールからの戻り値(通常はレジスタ
r0
,r1
)とエラーコード(e1
)が、Goの関数の戻り値(sz
,err
)に変換されます。e1 != 0
の場合、システムコールがエラーを返したことを意味し、Goのエラー型に変換されます。
- これらのファイル(例:
このメカニズムにより、Go開発者はGoの慣用的な方法でファイルシステムの拡張属性を操作できるようになり、Goプログラムの機能性が向上しました。
関連リンク
- Linux Extended Attributes (xattr) - Wikipedia: https://en.wikipedia.org/wiki/Extended_file_attributes
setxattr(2)
man page: https://man7.org/linux/man-pages/man2/setxattr.2.htmlgetxattr(2)
man page: https://man7.org/linux/man-pages/man2/getxattr.2.htmllistxattr(2)
man page: https://man7.org/linux/man-pages/man2/listxattr.2.htmlremovexattr(2)
man page: https://man7.org/linux/man-pages/man2/removexattr.2.html- Go
syscall
package documentation: https://pkg.go.dev/syscall
参考にした情報源リンク
- Web検索結果: "linux extended attributes xattr system calls"
- 特に、Linux extended attributes (xattrs) の概要、システムコール、およびコマンドラインツールに関する情報源。
man7.org
のシステムコールマニュアルページ。wikipedia.org
の拡張属性に関する記事。ibm.com
の拡張属性に関する記事。archlinux.org
の拡張属性に関する記事。linux-audit.com
の拡張属性に関する記事。github.io
の拡張属性に関する記事。unix.com
の拡張属性に関する記事。lesbonscomptes.com
の拡張属性に関する記事。linuxcampus.net
の拡張属性に関する記事。