MEMFD_CREATE(2) | Руководство программиста Linux | MEMFD_CREATE(2) |
memfd_create - создаёт анонимный файл
#define _GNU_SOURCE /* смотрите feature_test_macros(7) */ #include <sys/mman.h>
int memfd_create(const char *name, unsigned int flags);
Вызов memfd_create() создаёт анонимный файл и возвращает ссылающийся на него файловый дескриптор. Анонимный файл ведёт себя как обычный файл и может быть изменён, обрезан, отображён в память и т.д. Однако в отличие от обычного файла он располагается в ОЗУ и не имеет энергонезависимого хранилища. Как только все ссылки на файл удаляются, он автоматически исчезает. Анонимные файлы располагаются в анонимной памяти. Поэтому у файлов, создаваемых memfd_create(), такая же семантика как и областей анонимной памяти, выделяемой с помощью mmap(2) с флагом MAP_ANONYMOUS.
Первоначально, размер файла равен 0. После этого вызова нужно задать размер файла с помощью ftruncate(2) (или заполнить файл с помощью write(2) или подобными).
Имя, указанное в name, используется в качестве имени файла и будет показываться как цель соответствующей символьной ссылки в каталоге. /proc/self/fd/. Отображаемое имя всегда начинается с memfd: и служит только для отладки. Имена не влияют на поведение файлового дескриптора и поэтому несколько файлов могут иметь одно имя без каких-либо последствий.
Для изменения поведения memfd_create() можно использовать следующие значения flags (через OR):
Неиспользуемые биты в flags должны быть равны 0.
В качестве возвращаемого значения memfd_create() возвращает новый файловый дескриптор, который можно использовать для обращения к файлу. Данный файловый дескриптор открыт на чтение и запись (O_RDWR) и в файловом дескрипторе установлен флаг O_LARGEFILE.
При вызове fork(2) и execve(2) с файловым дескриптором, созданным memfd_create(), применяется обычная семантика. Копия файлового дескриптора наследуется потомком, созданным fork(2), указывает на тот же файл. Файловый дескриптор сохраняется при после execve(2), если не установлен флаг close-on-exec.
При успешном выполнении memfd_create() возвращает новый файловый дескриптор. При ошибке возвращается -1, и errno устанавливается в соответствующее значение.
Системный вызов memfd_create() появился в Linux 3.17; поддержка в glibc началась с версии 2.27.
Системный вызов memfd_create() есть только в Linux.
Системный вызов memfd_create() предоставляет простую альтернативу ручному монтированию файловой системы tmpfs(5), созданию и открытию файла в этой файловой системе. Основным предназначением memfd_create() является создание файлов и соответствующих файловых дескрипторов, которые используются с программным интерфейсом опечатывания файлов, предоставляемым fcntl(2).
Системный вызов memfd_create() также используется и без опечатывания файла (вот почему опечатывание файлов отключено, если этого не запросить явно с помощью флага MFD_ALLOW_SEALING). В частности, он может использоваться как альтернатива созданию файлов в tmp или использованию open(2) с O_TMPFILE в случаях, когда не требуется реальная ссылка на конечный файл в файловой системе.
Ели файл не опечатан, то процессы, которые связываются через общую память, должны или доверять друг другу, или учитывать возможность того, что недоверенная сторона может работать с общей памятью проблемным способом. Например, недоверенная сторона может изменить содержимое общей памяти в любое время или уменьшить область общей памяти. Первая возможность делает локальный процесс уязвимым к состязательности условий «момент проверки — момент использования» (обычно решается копированием данных из области общей памяти перед проверкой и использованием). Последняя возможность делает локальный процесс уязвимым к сигналам SIGBUS, которые возникают при попытке получить доступ к теперь несуществующему расположению в области общей памяти (решается использованием обработчика сигнала SIGBUS).
Взаимодействие с недоверенными сторонами приводит к усложнению кода для работы с общей памятью. Опечатывание памяти позволяет устранить эту сложность, позволяя процессу безопасно работать, зная что его партнёр не может изменить общую память нежелательным способом.
В примере использования механизма опечатывания происходит следующее:
Далее показано два примера программы, в которых продемонстрировано использование memfd_create() и программный интерфейс опечатывания файла.
Первая программа, t_memfd_create.c, создаёт файл tmpfs(5) с помощью memfd_create(), изменяет его размер, отображает в память и, возможно, накладывает несколько печатей на файл. Программа принимает не более трёх аргументов командной строки, два первых обязательные. Первым аргументом задаётся имя файла, во втором — размер файла, а в необязательном третьем — строка символов, задающих устанавливаемые печати на файл.
Вторая программа, t_get_seals.c, может использоваться для открытия существующего файла, созданного memfd_create(), и проверки набора печатей, применённых к файлу.
Следующий пример сеанса показывает как использовать программу. Сначала создаётся файл tmpfs(5) и накладываются печати:
$ ./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 для просмотра печатей, которые установлены на файл:
$ readlink /proc/11775/fd/3 /memfd:my_memfd_file (удалён) $ ./t_get_seals /proc/11775/fd/3 Наложенные печати: WRITE SHRINK
#define _GNU_SOURCE #include <sys/mman.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 имя размер [печати]\n", argv[0]); fprintf(stderr, "\t в 'печати' могут быть " "следующие символы:\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]; /* Создаётся анонимный файл в tmpfs; на файл накладываются указанные печати */ fd = memfd_create(name, MFD_ALLOW_SEALING); if (fd == -1) errExit("memfd_create"); /* Размер файл как указано в командной строке */ if (ftruncate(fd, len) == -1) errExit("truncate"); printf("PID: %ld; fd: %d; /proc/%ld/fd/%d\n", (long) getpid(), fd, (long) getpid(), fd); /* Здесь может быть код для отображения файла и заполнения данными */ /* Если в командной строке задан параметр 'печати', то накладываем их на файл */ 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"); } /* Продолжаем выполнение для того, чтобы файл, созданный memfd_create(), продолжал существовать */ pause(); exit(EXIT_SUCCESS); }
#define _GNU_SOURCE #include <sys/mman.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("Наложенные печати:"); 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"); /* Здесь может быть код для отображения и доступа к содержимому файла */ exit(EXIT_SUCCESS); }
2019-03-06 | Linux |