#define _GNU_SOURCE /* feature_test_macros(7) 参照 */ #include <sys/mman.h> int memfd_create(const char *name, unsigned int flags);
ファイルの初期サイズは 0 に設定される。 呼び出しの後に、 ftruncate(2) を使ってファイルサイズを設定すべきである (代わりに、 write(2) や同様の関数を呼び出してファイルにデータを書き込むこともできる)。
name に指定された名前はファイル名として使用され、 ディレクトリ /proc/self/fd/ で対応するシンボリックリンクのリンク先として表示される。 表示される名前の前には常に memfd: が付き、 この名前はデバッグ用途としてのみ機能する。 名前はファイルディスクリプターの動作には影響せず、 複数のファイルが同じ名前を持っても副作用はない。
以下の値をビット論理和で flags に指定して、 memfd_create() の動作を変更できる。
flags の未使用のビットは 0 でなければならない。
返り値として memfd_create() は、 作成したファイルを参照するのに使用できる新しいファイルディスクリプターを返す。 このファイルディスクリプターは読み書き両用 (O_RDWR) でオープンされ、 O_LARGEFILE がこのファイルディスクリプターにセットされる。
fork(2) と execve(2) に関しては、 memfd_create() で作成したファイルディスクリプターについても通常の動作が適用される。 ファイルディスクリプターのコピーは fork(2) で生成される子プロセスに継承され、 同じファイルを参照する。 close-on-exec フラグがセットされていない限り、 execve(2) の前後でファイルディスクリプターは保持される。
memfd_create() システムコールは、 file sealing なしでも用途がある (これが明示的に MFD_ALLOW_SEALING フラグが要求されない限り、 file-sealing が無効になる理由である)。 特に、 ファイルシステムに実際にファイルを残す意図がない場合、 tmp にファイルを作成したり open(2) O_TMPFILE を使ったりする際の代替手段として使用できる。
信頼していない相手への対処により、 共有メモリーを利用するコードに余計な複雑性が増すことになる。 メモリー sealing により余計な複雑性をなくすことができる。 相手が望まない方法で共有メモリーを変更できないことを知っていることで、 プロセスは安全に動作できるようになる。
sealing 機構の使い方の例は以下のとおりである。
最初のプログラム t_memfd_create.c は、 memfd_create() を使って tmpfs(5) ファイルを作成し、 そのファイルのサイズを設定し、 メモリーにマッピングし、 要求された場合にはそのファイルに seal を設定する。 このプログラムは最大で 3 つのコマンドライン引数を取り、 最初の 2 つは必須である。 最初の引数はファイルに関連付けられる名前で、 2 番目の引数はファイルに設定されるサイズである。 省略可能な 3 番目の引数は、 このファイルに設定する seal を指定する文字列である。
2 つめのプログラム t_get_seals.c を使うと、 memfd_create() を使って作成された既存のファイルをオープンし、 そのファイルに適用されている seal の集合を調査できる。
以下のシェルのセッションはこれらのプログラムの使用例を示したものである。 まず tmpfs(5) ファイルを作成し、そのファイルに 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
#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\tW - F_SEAL_FUTURE_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: %jd; fd: %d; /proc/%jd/fd/%d\n",
(intmax_t) getpid(), fd, (intmax_t) 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, 'W') != NULL)
seals |= F_SEAL_FUTURE_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();
#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_FUTURE_WRITE)
printf(" FUTURE_WRITE");
if (seals & F_SEAL_SHRINK)
printf(" SHRINK");
printf("\n");
/* Code to map the file and access the contents of the
resulting mapping omitted */