MEMFD_CREATE(2)

名前
memfd_create - 無名ファイル (anonymous file) を作成する

書式
#include <sys/memfd.h>

int memfd_create(const char *name, unsigned int flags);

説明
memfd_create() は、 無名ファイル (anonymous file) を作成し、 そのファイルを参照するファイルディスクリプターを返す。 このファイルは通常のファイルと同様に振る舞い、 変更、切り詰め (truncate)、 メモリーマップなどを行うことができる。 しかし、 通常のファイルとは違い、 このファイルは RAM 上に置かれ、 格納されるストレージは揮発性である。 このファイルへの参照がすべてなくなると、 ファイルは自動的に解放される。 このファイルが置かれるページには無名メモリー (anonymous memory) が使用される。 したがって、 memfd_create() で作成されたフィアルは、 他の無名メモリーの割り当て (MAP_ANONYMOUS フラグ付きの mmap(2) を使って割り当てられた無名メモリーなど) と同じ動作をする。 ファイルの初期サイズは 0 に設定される。 呼び出しの後に、 ftruncate(2) を使ってファイルサイズを設定すべきである (代わりに、 write(2) や同様の関数を呼び出してファイルにデータを書き込むこともできる)。 name に指定された名前はファイル名として使用され、 ディレクトリ /proc/self/fd/ で対応するシンボリックリンクのリンク先として表示される。 表示される名前の前には常に memfd: が付き、 この名前はデバッグ用途としてのみ機能する。 名前はファイルディスクリプターの動作には影響せず、 複数のファイルが同じ名前を持っても副作用はない。 以下の値をビット論理和で flags に指定して、 memfd_create() の動作を変更できる。

MFD_CLOEXEC
    新しいファイルディスクリプターに close-on-exec (FD_CLOEXEC) フラグをセットする。 これが有用な理由については open(2) の O_CLOEXEC フラグの説明を参照のこと。

MFD_ALLOW_SEALING
    このファイルに対して sealing 操作を許可する。 fcntl(2) の F_ADD_SEALS と F_GET_SEALS 操作の議論を参照。 下記の「注意」も参照。 初期の seal 集合は空となる。 このフラグを指定しなかった場合、 初期の seal 集合は F_SEAL_SEAL となり、 これはこのファイルには他の seal をセットできないことということである。

flags の未使用のビットは 0 でなければならない。 返り値として memfd_create() は、 作成したファイルを参照するのに使用できる新しいファイルディスクリプターを返す。 このファイルディスクリプターは読み書き両用 (O_RDWR) でオープンされ、 O_LARGEFILE がこのファイルディスクリプターにセットされる。 fork(2) と execve(2) に関しては、 memfd_create() で作成したファイルディスクリプターについても通常の動作が適用される。 ファイルディスクリプターのコピーは fork(2) で生成される子プロセスに継承され、 同じファイルを参照する。 close-on-exec フラグがセットされていない限り、 execve(2) の前後でファイルディスクリプターは保持される。

返り値
成功の場合、 memfd_create() は新しいファイルディスクリプターを返す。 エラーの場合、-1 を返し、 errno にエラーを示す値を設定する。

エラー

EFAULT
    name のアドレスが無効なメモリーを指している。

EINVAL
    サポートされていない値がいずれかの引き数で指定された。 flags に未知のビットが含まれていたか、 name が長過ぎた。

EMFILE
    オープンされているファイルディスクリプターのプロセス単位の上限に達した。

ENFILE
    システム全体でオープンされているファイルの総数が上限に達した。

ENOMEM
    新しい無名ファイルを作成するのに十分なメモリーがなかった。

バージョン
memfd_create() システムコールは Linux 3.17 で初めて登場した。 GNU C ライブラリでのサポートは検討中である。

準拠
memfd_create() システムコールは Linux 固有である。

注意
memfd_create() システムコールは、 手動で tmpfs ファイルシステムをマウントして、 そのファイルシステムにファイルをオープンするという操作の、 簡単な代替手段を提供している。 memfd_create() の主な目的は、 fcntl(2) が提供する file-sealing API で使用できる、 ファイルとそれに関連付けられるファイルディスクリプターを作成することである。 memfd_create() システムコールは、 file sealing なしでも用途がある (これが明示的に MFD_ALLOW_SEALING フラグが要求されない限り、 file-sealing が無効になる理由である)。 特に、 ファイルシステムに実際にファイルを残す意図がない場合、 tmp にファイルを作成したり open(2) O_TMPFILE を使ったりする際の代替手段として使用できる。

  file sealing
file sealing がない場合、 共有メモリー経由で通信するプロセスは、 互いに信頼するか、 信頼していない相手が共有メモリー領域を問題がある方法で操作する可能性に対処するための対策を講じなければならない。 例えば、 信頼していない相手は、 いつでも共有メモリーの内容を変更したり、 共有メモリー領域を縮小したりする可能性がある。 前者の場合は、 ローカルプロセスでは、 データの確認時点と使用時点の競合条件の問題が起こり得る (通常はこの問題への対処は共有メモリー領域からデータをこぴーしてからデータを確認、使用することである)。 後者の場合は、 ローカルプロセスでは、 共有メモリー領域の存在しなくなった場所にアクセスしようとした際にシグナル SIGBUS が発生する可能性がある (この可能性に対処するにはシグナル SIGBUS に対してハンドラーを使用する必要がある)。 信頼していない相手への対処により、 共有メモリーを利用するコードに余計な複雑性が増すことになる。 メモリー sealing により余計な複雑性をなくすことができる。 相手が望まない方法で共有メモリーを変更できないことを知っていることで、 プロセスは安全に動作できるようになる。 sealing 機構の使い方の例は以下のとおりである。

1.  最初のプロセスは memfd_create() を使って tmpfs ファイルを作成する。 memfd_create() はこれ以降のステップで使用するファイルディスクリプターを返す。

2.  最初のプロセスは ftruncate(2) を使って直前のステップで作成したファイルのサイズを変更し、 mmap(2) を使ってそのファイルをマッピングし、 共有メモリーに所望のデータを配置する。

3.  最初のプロセスは、 このファイルに対する今後の変更を制限するために、 fcntl(2) の F_ADD_SEALS 操作を使って、 そのファイルに seal をいくつか設定する。 (seal F_SEAL_WRITE を設定する場合、 直前のステップで作成した書き込み可能な共有マッピングをまずアンマップする必要が出てくる。)

4.  二つ目のプロセスは tmpfs ファイルのファイルディスクリプターを入手し、 そのファイルをマップする。 以下に示す方法を使用することができる。

    *   memfd_create() を呼び出したプロセスは、 得られたファイルディスクリプターを二つ目のプロセスに UNIX ドメインソケット経由で渡すことができる (unix(7) と cmsg(3) を参照)。 それから、二つ目のプロセスは mmap(2) を使ってファイルをマップする。

    *   二つ目のプロセスを fork(2) を使って作成する。 そうすると、 自動的にファイルディスクリプターとマッピングが継承される。 (この方法と次の方法では、 二つのプロセス間で自然な信頼関係が存在することになる。 なぜなら、 二つのプロセスは同じユーザー ID。 の元で実行されているからである。 したがって、 file sealing は通常は不要であろう。)

    *   二つ目のプロセスは /proc/<pd>/fd/<fd> をオープンする。 <pid> は最初のプロセス (memfd_create() を呼び出したプロセス) の PID で、 <fd> は最初のプロセスでの memfd_create() の呼び出しで返されたファイルディスクリプター番号である。 それからこのファイルを mmap(2) を使ってマッピングする。

5.  二つ目のプロセスは fcntl(2) の F_GET_SEALS 操作を使って、 そのファイルに適用されている seal のビットマスクを取得する。 このビットマスクを調べて、 ファイルの変更に関してどのような制限が適用されているかを知ることができる。 (F_SEAL_SEAL seal がそれまでに適用されていない限りは) 必要であれば、 二つ目のプロセスはさらに seal を設定して追加の制限をかけることができる。

例
以下では memfd_create() と file sealing API の使用例を示すサンプルプログラムを 2 つとりあげる。 最初のプログラム t_memfd_create.c は、 memfd_create() を使って tmpfs ファイルを作成し、 そのファイルのサイズを設定し、 メモリーにマッピングし、 要求された場合にはそのファイルに seal を設定する。 このプログラムは最大で 3 つのコマンドライン引き数を取り、 最初の 2 つは必須である。 最初の引き数はファイルに関連付けられる名前で、 2 番目の引き数はファイルに設定されるサイズである。 省略可能な 3 番目の引き数は、 このファイルに設定する seal を指定する文字列である。 2 つめのプログラム t_get_seals.c を使うと、 memfd_create() を使って作成された既存のファイルをオープンし、 そのファイルに適用されている seal の集合を調査できる。 以下のシェルのセッションはこれらのプログラムの使用例を示したものである。 まず tmpfs ファイルを作成し、そのファイルに seal をいくつか設定している。
$ ./t_memfd_create my_memfd_file 4096 sw &
[1] 11775
PID: 11775; fd: 3; /proc/11775/fd/3
この時点では、 t_memfd_create プログラムはバックグラウンドで動作し続ける。 もう一つのプログラムから、 memfd_create() がオープンしたディスクリプターに対応する /proc/PID/fd ファイルをオープンすることで、 memfd_create() で作成されたファイルのファイルディスクリプターを取得できる。 そのパス名を使って、 /proc/PID/fd シンボリックリンクの内容を調査し、 t_get_seals プログラムを使ってそのファイルに設定されている seal を見ることができる。
$ readlink /proc/11775/fd/3
/memfd:my_memfd_file (deleted)
$ ./t_get_seals /proc/11775/fd/3
Existing seals: WRITE SHRINK

  プログラムのソース: t_memfd_create.c
#include <sys/memfd.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
int
main(int argc, char *argv[])
{
    int fd;
    unsigned int seals;
    char *addr;
    char *name, *seals_arg;
    ssize_t len;
    if (argc < 3) {
        fprintf(stderr, "%s name size [seals]\n", argv[0]);
        fprintf(stderr, "\t'seals' can contain any of the "
                "following characters:\n");
        fprintf(stderr, "\t\tg - F_SEAL_GROW\n");
        fprintf(stderr, "\t\ts - F_SEAL_SHRINK\n");
        fprintf(stderr, "\t\tw - F_SEAL_WRITE\n");
        fprintf(stderr, "\t\tS - F_SEAL_SEAL\n");
        exit(EXIT_FAILURE);
    }
    name = argv[1];
    len = atoi(argv[2]);
    seals_arg = argv[3];
    /* Create an anonymous file in tmpfs; allow seals to be
       placed on the file */
    fd = memfd_create(name, MFD_ALLOW_SEALING);
    if (fd == -1)
        errExit("memfd_create");
    /* Size the file as specified on the command line */
    if (ftruncate(fd, len) == -1)
        errExit("truncate");
    printf("PID: %ld; fd: %d; /proc/%ld/fd/%d\n",
            (long) getpid(), fd, (long) getpid(), fd);
    /* Code to map the file and populate the mapping with data
       omitted */
    /* If a 'seals' command-line argument was supplied, set some
       seals on the file */
    if (seals_arg != NULL) {
        seals = 0;
        if (strchr(seals_arg, 'g') != NULL)
            seals |= F_SEAL_GROW;
        if (strchr(seals_arg, 's') != NULL)
            seals |= F_SEAL_SHRINK;
        if (strchr(seals_arg, 'w') != NULL)
            seals |= F_SEAL_WRITE;
        if (strchr(seals_arg, 'S') != NULL)
            seals |= F_SEAL_SEAL;
        if (fcntl(fd, F_ADD_SEALS, seals) == -1)
            errExit("fcntl");
    }
    /* Keep running, so that the file created by memfd_create()
       continues to exist */
    pause();
    exit(EXIT_SUCCESS);
}

  プログラムのソース: t_get_seals.c
#include <sys/memfd.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
                        } while (0)
int
main(int argc, char *argv[])
{
    int fd;
    unsigned int seals;
    if (argc != 2) {
        fprintf(stderr, "%s /proc/PID/fd/FD\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    fd = open(argv[1], O_RDWR);
    if (fd == -1)
        errExit("open");
    seals = fcntl(fd, F_GET_SEALS);
    if (seals == -1)
        errExit("fcntl");
    printf("Existing seals:");
    if (seals & F_SEAL_SEAL)
        printf(" SEAL");
    if (seals & F_SEAL_GROW)
        printf(" GROW");
    if (seals & F_SEAL_WRITE)
        printf(" WRITE");
    if (seals & F_SEAL_SHRINK)
        printf(" SHRINK");
    printf("\n");
    /* Code to map the file and access the contents of the
       resulting mapping omitted */
    exit(EXIT_SUCCESS);
}

関連項目
fcntl(2), ftruncate(2), mmap(2), shmget(2), shm_open(3)

この文書について
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。