Главная » 2017 » Ноябрь » 21 » man 2 name_to_handle_at
01:02
man 2 name_to_handle_at

SEO sprint - Всё для максимальной раскрутки!





ИМЯ


name_to_handle_at, open_by_handle_at - получение описателя для пути и открытие
файла через описатель



ОБЗОР


#define _GNU_SOURCE /* Смотрите feature_test_macros(7) */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int name_to_handle_at(int dirfd, const char *pathname,
struct file_handle *handle,
int *mount_id, int flags);

int open_by_handle_at(int mount_fd, struct file_handle *handle,
int flags);



ОПИСАНИЕ


Функциональное назначение openat(2) было разделено на две части и добавлено в
системные вызовы name_to_handle_at() и open_by_handle_at(): name_to_handle_at()
возвращает описатель с произвольной формой(opaque), который соответствует
указанному файлу; open_by_handle_at() открывает файл по описателю, который был
возвращён предыдущим вызовом name_to_handle_at() и возвращает дескриптор открытого
файла.

name_to_handle_at()
Системный вызов name_to_handle_at() возвращает файловый описатель и идентификатор
монтирования для файла, указанного в аргументах dirfd и pathname. Файловый
описатель возвращается через аргумент handle, который является указателем на
следующую структуру:

struct file_handle {
unsigned int handle_bytes; /* размер f_handle [in, out] */
int handle_type; /* тип описателя [out] */
unsigned char f_handle[0]; /* идентификатор файла (размер
задаёт вызывающий) [out] */
};

Вызывающий должен выделить память достаточного размера под структуру описателя,
возвращаемого в f_handle. Перед вызовом поле handle_bytes должно содержать размер
выделенной памяти для f_handle (константа MAX_HANDLE_SZ, определённая в <fcntl.h>,
равна максимально возможному размеру описателя файла). При успешном выполнении
поле handle_bytes обновляется и содержит количество байт действительно занятых под
f_handle.

Вызывающий может определить требуемый размер структуры file_handle указав при
вызове значение handle->handle_bytes равное нулю; в этому случае вызов завершается
с ошибкой EOVERFLOW, а в поле handle->handle_bytes записывается требуемый размер;
вызывающий затем может использовать эту информацию для выделения памяти под
структуру правильного размера (смотрите ПРИМЕР ниже).

Все остальные поля кроме handle_bytes структуры file_handle вызывающий должен
считать неизвестными: поля handle_type и f_handle требуются только в последующих
вызовах open_by_handle_at().

Аргумент flags представляет собой битовую маску (OR) из комбинации нуля или более
флагов AT_EMPTY_PATH и AT_SYMLINK_FOLLOW, описанных ниже.
равно специальному значению AT_FDCWD, то pathname рассматривается относительно
текущего рабочего каталога вызывающего и описатель возвращается для файла, на
который он указывает.

* Если значение pathname — непустая строка, содержащая относительный путь и dirfd
равно файловому дескриптору, указывающему на каталог, то pathname
рассматривается относительно каталога, на который указывает dirfd, и описатель
возвращается для файла, на который он указывает (смотрите в openat(2)
объяснение полезности «файловых дескрипторов каталогов»).

* Если значение pathname — пустая строка и значение flags равно AT_EMPTY_PATH, то
dirfd может быть открытым файловым дескриптором, указывающим на файл любого
типа, или AT_FDCWD, означающим текущий рабочий каталог, и описатель
возвращается для файла, на который он указывает.

В аргументе mount_id возвращается идентификатор точки монтирования в файловой
системе, в которой расположен pathname. Это значение соответствует первому полю
одной из записей в /proc/self/mountinfo. Открытие файла из пятого поля этой записи
возвращает файловый дескриптор этой точки монтирования; этот файловый дескриптор
можно использовать в последующем вызове open_by_handle_at().

По умолчанию, name_to_handle_at() не разыменовывает pathname, если это
символическая ссылка, и поэтому возвращается описатель самой ссылки. Если в flags
указан AT_SYMLINK_FOLLOW, то pathname разыменовывается, если это символическая
ссылка (то есть вызов возвращает описатель файла, на который указывает ссылка).

open_by_handle_at()
Системный вызов open_by_handle_at() открывает файл, на который указывает handle,
файловый описатель, полученный от предшествующего вызова name_to_handle_at().

Аргумент mount_fd — это файловый дескриптор любого объекта (файла, каталога и т.
д.) в смонтированной файловой системе, в которой должен находиться handle. Может
быть равен специальному значению AT_FDCWD, которое обозначает текущий рабочий
каталог вызывающего.

Значение аргумента flags как у open(2). Если handle указывает на символическую
ссылку, то вызывающий должен указать флаг O_PATH, и символическая ссылка не
разыменовывается; флаг O_NOFOLLOW игнорируется.

Для вызова open_by_handle_at() вызывающий должен иметь мандат CAP_DAC_READ_SEARCH.



ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ


При успешном выполнении name_to_handle_at() возвращает 0, а open_by_handle_at()
возвращает неотрицательный файловый дескриптор.

В случае ошибки системные вызовы возвращают -1, а в errno записывается код ошибки.



ОШИБКИ


Вызовы name_to_handle_at() и open_by_handle_at() могут завершиться с теми же
ошибками что и openat(2). Также могут возникать следующие ошибки:

Вызов name_to_handle_at() может завершиться со следующими ошибками:

EFAULT Значение pathname, mount_id или handle указывает за пределы доступного
адресного пространства.

EINVAL Значение flags содержит некорректно установленный бит.
строке.

EOPNOTSUPP
Файловая система не поддерживает преобразование пути в файловый описатель.

EOVERFLOW
Значение handle->handle_bytes, переданное в вызов, слишком мало. При этой
ошибке handle->handle_bytes присваивается требуемый размер для описателя.

Вызов open_by_handle_at() может завершиться со следующими ошибками:

EBADF Значение mount_fd не является открытым файловым дескриптором.

EFAULT Значение handle указывает за пределы доступного адресного пространства.

EINVAL Значение handle->handle_bytes больше MAX_HANDLE_SZ или равно нулю.

ELOOP Значение handle указывает на символическую ссылку, но в flags не указан
O_PATH.

EPERM Вызывающий не имеет мандата CAP_DAC_READ_SEARCH.

ESTALE Значение handle некорректно. Эта ошибка возникает, например, из-за удаления
файла.



ВЕРСИИ


Данные системные вызовы впервые появились в Linux 2.6.39. Поддержка в glibc
появилась в версии 2.14.



СООТВЕТСТВИЕ СТАНДАРТАМ


Данные системные вызовы являются нестандартными расширениями Linux.

В FreeBSD есть относительно похожая пара системных вызовов getfh() и openfh().



ЗАМЕЧАНИЯ


Файловый описатель может быть сгенерирован с помощью name_to_handle_at() в одном
процессе и использован в вызовах open_by_handle_at() в другом.

Некоторые файловые системы не поддерживают трансляцию путей в файловые описатели,
например: /proc, /sys и различные сетевые файловые системы.

Файловый описатель может стать некорректным («просроченным»), если файл удалён,
или по другим причинам, относящимся к файловой системе. Для некорректных
описателей open_by_handle_at() возвращает ошибку ESTALE.

Данные системные вызовы предназначены для использования в файловых серверах
пространства пользователя. Например, сервер пользовательского пространства NFS
может генерировать файловый описатель и передавать его клиенту NFS. Позднее, когда
клиент захочет открыть файл, он может передать описатель обратно серверу. Такого
рода возможность позволяет файловому серверу пространства пользователя работать
без формирования состояния (stateless fashion) для файлов, которые они
обслуживают.

Если pathname указывает на символическую ссылку и в flags отсутствует
AT_SYMLINK_FOLLOW, то name_to_handle_at() возвращает описатель ссылки (а не файла,
на который она ссылается). Процесс, получивший описатель, может позднее выполнить
операции над символической ссылкой, преобразовав описатель в файловый дескриптор,
соответствует идентификатору монтирования, для получения постоянного
идентификатора.

Например, можно использовать имя устройства в пятом поле записи mountinfo для
поиска соответствующего устройству UUID через символические ссылки в
/dev/disks/by-uuid (более удобный способ получения UUID — использовать библиотеку
libblkid(3)). Этот процесс может быть и обратным — используя UUID найти имя
устройства, и затем получить соответствующую точку монтирования, чтобы создать
аргумент mount_fd, используемый для open_by_handle_at().



ПРИМЕР


Две представленные далее программы демонстрируют использование name_to_handle_at()
и open_by_handle_at(). Первая программа (t_name_to_handle_at.c) использует
name_to_handle_at() для получения файлового описателя и идентификатора
монтирования для файла, указанного в аргументе командной строки; описатель и
идентификатор монтирования записываются в стандартный вывод.

Вторая программа (t_open_by_handle_at.c) читает идентификатор монтирования и
файловый описатель из стандартного ввода. Затем программа, используя описатель,
применяет open_by_handle_at() для открытия файла. Если указан необязательный
параметр командной строки, то аргумент mount_fd для open_by_handle_at() создаётся
из открытия каталога, указанного в аргументе. В противном случае mount_fd
заполняется результатом сканированием /proc/self/mountinfo в целях найти запись,
чей идентификатор монтирования совпадает с идентификатором монтирования из
стандартного ввода, и открывается каталог монтирования, указанный в этой записи
(эти программы не учитывают, что идентификатор монтирования не постоянен).

Следующий сеанс работы в оболочке показывает использование этих программ:

$ echo 'Can you please think about it?' > cecilia.txt
$ ./t_name_to_handle_at cecilia.txt > fh
$ ./t_open_by_handle_at < fh
open_by_handle_at: Operation not permitted
$ sudo ./t_open_by_handle_at < fh # требуется CAP_SYS_ADMIN
Read 31 bytes
$ rm cecilia.txt

Теперь мы удаляем и (быстро) пересоздаём файл с тем же содержимым и (если повезёт)
с той же инодой. Не смотря на это, open_by_handle_at() распознаёт, что
первоначальный файл, на который указывал файловый описатель, больше не существует.

$ stat --printf="%i\n" cecilia.txt # вывести номер иноды
4072121
$ rm cecilia.txt
$ echo 'Can you please think about it?' > cecilia.txt
$ stat --printf="%i\n" cecilia.txt # проверить номер иноды
4072121
$ sudo ./t_open_by_handle_at < fh
open_by_handle_at: Stale NFS file handle

Исходный код программы: t_name_to_handle_at.c

#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
main(int argc, char *argv[])
{
struct file_handle *fhp;
int mount_id, fhsize, flags, dirfd, j;
char *pathname;

if (argc != 2) {
fprintf(stderr, "Использование: %s путь\n", argv[0]);
exit(EXIT_FAILURE);
}

pathname = argv[1];

/* выделяем место под структуру file_handle */

fhsize = sizeof(*fhp);
fhp = malloc(fhsize);
if (fhp == NULL)
errExit("malloc");

/* выполняем первоначальный вызов name_to_handle_at() для
определения требуемого размера файлового описателя */

dirfd = AT_FDCWD; /* для вызовов name_to_handle_at() */
flags = 0; /* для вызовов name_to_handle_at() */
fhp->handle_bytes = 0;
if (name_to_handle_at(dirfd, pathname, fhp,
&mount_id, flags) != -1 || errno != EOVERFLOW) {
fprintf(stderr, "Unexpected result from name_to_handle_at()\n");
exit(EXIT_FAILURE);
}

/* перераспределяем структуру file_handle с правильным размером */

fhsize = sizeof(struct file_handle) + fhp->handle_bytes;
fhp = realloc(fhp, fhsize); /* копируем fhp->handle_bytes */
if (fhp == NULL)
errExit("realloc");

/* получаем файловый описатель из пути, который
указан в командной строке */

if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == -1)
errExit("name_to_handle_at");

/* пишем идентификатор монтирования, размер файлового описателя
и файловый описатель в stdout
для повторного использования в t_open_by_handle_at.c */

printf("%d\n", mount_id);
printf("%d %d ", fhp->handle_bytes, fhp->handle_type);
for (j = 0; j < fhp->handle_bytes; j++)
printf(" %02x", fhp->f_handle[j]);
printf("\n");

exit(EXIT_SUCCESS);
}
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)

/* сканируем /proc/self/mountinfo в поиске строки, чей идентификатор
монтирования совпадает с 'mount_id' (простейший способ,
это установить и использовать библиотеку
«libmount», предоставляемую проектом «util-linux»).
Открываем соответствующий путь монтирования и возвращаем
полученный файловый дескриптор. */

static int
open_mount_path_by_id(int mount_id)
{
char *linep;
size_t lsize;
char mount_path[PATH_MAX];
int mi_mount_id, found;
ssize_t nread;
FILE *fp;

fp = fopen("/proc/self/mountinfo", "r");
if (fp == NULL)
errExit("fopen");

found = 0;
linep = NULL;
while (!found) {
nread = getline(&linep, &lsize, fp);
if (nread == -1)
break;

nread = sscanf(linep, "%d %*d %*s %*s %s",
&mi_mount_id, mount_path);
if (nread != 2) {
fprintf(stderr, "Bad sscanf()\n");
exit(EXIT_FAILURE);
}

if (mi_mount_id == mount_id)
found = 1;
}
free(linep);

fclose(fp);

if (!found) {
fprintf(stderr, "Could not find mount point\n");
exit(EXIT_FAILURE);
}

return open(mount_path, O_RDONLY);
}

char *nextp;

if ((argc > 1 && strcmp(argv[1], "--help") == 0) || argc > 2) {
fprintf(stderr, "Использование: %s [путь-монт.]\n", argv[0]);
exit(EXIT_FAILURE);
}

/* Стандартный ввод содержит идентификатор монтирования и
информацию о файловом описателе:

Строка 1: <идентификатор монтирования>
Строка 2: <handle_bytes> <handle_type>
<байты описателя в шестнад. системе счисления>
*/

if ((fgets(line1, sizeof(line1), stdin) == NULL) ||
(fgets(line2, sizeof(line2), stdin) == NULL)) {
fprintf(stderr, "Missing mount_id / file handle\n");
exit(EXIT_FAILURE);
}

mount_id = atoi(line1);

handle_bytes = strtoul(line2, &nextp, 0);

/* получаем handle_bytes, теперь мы можем выделить место
под структуру file_handle */

fhp = malloc(sizeof(struct file_handle) + handle_bytes);
if (fhp == NULL)
errExit("malloc");

fhp->handle_bytes = handle_bytes;

fhp->handle_type = strtoul(nextp, &nextp, 0);

for (j = 0; j < fhp->handle_bytes; j++)
fhp->f_handle[j] = strtoul(nextp, &nextp, 16);

/* получаем файловый дескриптор для точки монтирования, или
открываем путь, указанный в командной строке, или сканируем
/proc/self/mounts в поиске монтирования, которое совпадает с
«mount_id», который мы получили из stdin. */

if (argc > 1)
mount_fd = open(argv[1], O_RDONLY);
else
mount_fd = open_mount_path_by_id(mount_id);

if (mount_fd == -1)
errExit("opening mount fd");

/* открываем файл, используя описатель и точку монтирования */

fd = open_by_handle_at(mount_fd, fhp, O_RDONLY);
if (fd == -1)
errExit("open_by_handle_at");

exit(EXIT_SUCCESS);
}



СМОТРИТЕ ТАКЖЕ


open(2), libblkid(3), blkid(8), findfs(8), mount(8)

Документация libblkid и libmount в последнем выпуске util-linux
⟨https://www.kernel.org/pub/linux/utils/util-linux/⟩



Категория: (2) Системные вызовы ядра (функции языка Си) | Просмотров: 380 | Добавил: Администратор | Рейтинг: 0.0/0
Всего комментариев: 0
avatar