.\" -*- coding: UTF-8 -*- .\" Copyright (c) 2014 by Michael Kerrisk .\" .\" SPDX-License-Identifier: Linux-man-pages-copyleft .\" .\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH open_by_handle_at 2 "3 мая 2023 г." "Linux man\-pages 6.05.01" .SH ИМЯ name_to_handle_at, open_by_handle_at \- получение описателя для пути и открытие файла через описатель .SH LIBRARY Standard C library (\fIlibc\fP, \fI\-lc\fP) .SH СИНТАКСИС .nf \fB#define _GNU_SOURCE\fP /* см. feature_test_macros(7) */ \fB#include \fP .PP \fBint name_to_handle_at(int \fP\fIdirfd\fP\fB, const char *\fP\fIpathname\fP\fB,\fP \fB struct file_handle *\fP\fIhandle\fP\fB,\fP \fB int *\fP\fImount_id\fP\fB, int \fP\fIflags\fP\fB);\fP \fBint open_by_handle_at(int \fP\fImount_fd\fP\fB, struct file_handle *\fP\fIhandle\fP\fB,\fP \fB int \fP\fIflags\fP\fB);\fP .fi .SH ОПИСАНИЕ .\" .\" Функциональное назначение \fBopenat\fP(2) было разделено на две части и добавлено в системные вызовы \fBname_to_handle_at\fP() и \fBopen_by_handle_at\fP(): \fBname_to_handle_at\fP() возвращает описатель с произвольной формой(opaque), который соответствует указанному файлу; \fBopen_by_handle_at\fP() открывает файл по описателю, который был возвращён предыдущим вызовом \fBname_to_handle_at\fP() и возвращает дескриптор открытого файла. .SS name_to_handle_at() Системный вызов \fBname_to_handle_at\fP() возвращает файловый описатель и идентификатор монтирования для файла, указанного в аргументах \fIdirfd\fP и \fIpathname\fP. Файловый описатель возвращается через аргумент \fIhandle\fP, который является указателем на следующую структуру: .PP .in +4n .EX struct file_handle { unsigned int handle_bytes; /* размер f_handle [in, out] */ int handle_type; /* тип описателя [out] */ unsigned char f_handle[0]; /* идентификатор файла (размер задаёт вызывающий) [out] */ }; .EE .in .PP Вызывающий должен выделить память достаточного размера под структуру описателя, возвращаемого в \fIf_handle\fP. Перед вызовом поле \fIhandle_bytes\fP должно содержать размер выделенной памяти для \fIf_handle\fP (константа \fBMAX_HANDLE_SZ\fP, определённая в \fI\fP, равна максимально ожидаемому размеру описателя файла. Это не гарантированное верхнее ограничение, так как файловые системы в будущем могут потребовать больше места). При успешном выполнении поле \fIhandle_bytes\fP обновляется и содержит количество байт действительно занятых под \fIf_handle\fP. .PP Вызывающий может определить требуемый размер структуры \fIfile_handle\fP указав при вызове значение \fIhandle\->handle_bytes\fP равное нулю; в этому случае вызов завершается с ошибкой \fBEOVERFLOW\fP, а в поле \fIhandle\->handle_bytes\fP записывается требуемый размер; затем вызывающий может использовать эту информацию для выделения памяти под структуру правильного размера (смотрите ПРИМЕРЫ ниже). Здесь нужно учесть, что \fBEOVERFLOW\fP может указывать на то, что файловый описатель недоступен для этого заданного имени в файловой системе, которая, обычно, поддерживает поиск файловых описателей. На данный случай указывает то, что ошибка \fBEOVERFLOW\fP возвращается без увеличившегося значения \fIhandle_bytes\fP. .PP Все остальные поля, кроме \fIhandle_bytes\fP структуры \fIfile_handle\fP, вызывающий должен считать неизвестными: поля \fIhandle_type\fP и \fIf_handle\fP требуются только в последующих вызовах \fBopen_by_handle_at\fP(). .PP Аргумент \fIflags\fP представляет собой битовую маску (OR) из комбинации нуля или более флагов \fBAT_EMPTY_PATH\fP и \fBAT_SYMLINK_FOLLOW\fP, описанных ниже. .PP Аргументы \fIpathname\fP и \fIdirfd\fP вместе задают файл, для которого будет получен описатель. Есть четыре различных варианта: .IP \[bu] 3 Если значение \fIpathname\fP — непустая строка, содержащая абсолютный путь, то описатель возвращается для файла, на который указывает путь. В этом случае \fIdirfd\fP игнорируется. .IP \[bu] Если значение \fIpathname\fP — непустая строка, содержащая относительный путь и \fIdirfd\fP равно специальному значению \fBAT_FDCWD\fP, то \fIpathname\fP рассматривается относительно текущего рабочего каталога вызывающего и описатель возвращается для файла, на который он указывает. .IP \[bu] Если значение \fIpathname\fP — непустая строка, содержащая относительный путь и \fIdirfd\fP равно файловому дескриптору, указывающему на каталог, то \fIpathname\fP рассматривается относительно каталога, на который указывает \fIdirfd\fP, и описатель возвращается для файла, на который он указывает (смотрите в \fBopenat\fP(2) объяснение полезности «файловых дескрипторов каталогов»). .IP \[bu] Если значение \fIpathname\fP — пустая строка и значение \fIflags\fP равно \fBAT_EMPTY_PATH\fP, то \fIdirfd\fP может быть открытым файловым дескриптором, указывающим на файл любого типа, или \fBAT_FDCWD\fP, означающим текущий рабочий каталог, и описатель возвращается для файла, на который он указывает. .PP В аргументе \fImount_id\fP возвращается идентификатор точки монтирования в файловой системе, соответствующий \fIpathname\fP. Это значение соответствует первому полю одной из записей в \fI/proc/self/mountinfo\fP. Открытие пути из пятого поля этой записи возвращает файловый дескриптор этой точки монтирования; этот файловый дескриптор можно использовать в последующем вызове \fBopen_by_handle_at\fP(). Аргумент \fImount_id\fP возвращается при успешном выполнении, а также при ошибке \fBEOVERFLOW\fP(). .PP По умолчанию, \fBname_to_handle_at\fP() не разыменовывает \fIpathname\fP, если это символическая ссылка, и поэтому возвращается описатель самой ссылки. Если в \fIflags\fP указан \fBAT_SYMLINK_FOLLOW\fP, то \fIpathname\fP разыменовывается, если это символическая ссылка (то есть вызов возвращает описатель файла, на который указывает ссылка). .PP .\" commit 20fa19027286983ab2734b5910c4a687436e0c31 Системный вызов \fBname_to_handle_at\fP() не вызывает монтирования, если конечная часть пути является автоматической точкой монтирования. Если файловая система поддерживает файловые описатели и автоматические точки монтирования, то вызов \fBname_to_handle_at\fP() для автоматической точки монтирования завершится ошибкой \fBEOVERFLOW\fP без увеличения значения \fIhandle_bytes\fP. Это может происходить с NFS начиная с версии Linux 4.13, когда задействованный каталог находится в отдельной файловой системе на сервере. В этом случае автомонтирование можно получить добавлением «/» в конец пути. .SS open_by_handle_at() Системный вызов \fBopen_by_handle_at\fP() открывает файл, на который указывает \fIhandle\fP, файловый описатель, полученный от предшествующего вызова \fBname_to_handle_at\fP(). .PP Аргумент \fImount_fd\fP — это файловый дескриптор любого объекта (файла, каталога и т. д.) в смонтированной файловой системе, в которой должен находиться \fIhandle\fP. Может быть равен специальному значению \fBAT_FDCWD\fP, которое обозначает текущий рабочий каталог вызывающего. .PP Значение аргумента \fIflags\fP как у \fBopen\fP(2). Если \fIhandle\fP указывает на символическую ссылку, то вызывающий должен указать флаг \fBO_PATH\fP, и символическая ссылка не разыменовывается; флаг \fBO_NOFOLLOW\fP игнорируется. .PP Для вызова \fBopen_by_handle_at\fP() вызывающий должен иметь мандат \fBCAP_DAC_READ_SEARCH\fP. .SH "ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ" On success, \fBname_to_handle_at\fP() returns 0, and \fBopen_by_handle_at\fP() returns a file descriptor (a nonnegative integer). .PP In the event of an error, both system calls return \-1 and set \fIerrno\fP to indicate the error. .SH ОШИБКИ Вызовы \fBname_to_handle_at\fP() и \fBopen_by_handle_at\fP() могут завершиться с теми же ошибками что и \fBopenat\fP(2). Также могут возникать следующие ошибки: .PP Вызов \fBname_to_handle_at\fP() может завершиться со следующими ошибками: .TP \fBEFAULT\fP Значение \fIpathname\fP, \fImount_id\fP или \fIhandle\fP указывает за пределы доступного адресного пространства. .TP \fBEINVAL\fP Значение \fIflags\fP содержит некорректно установленный бит. .TP \fBEINVAL\fP Значение \fIhandle\->handle_bytes\fP больше \fBMAX_HANDLE_SZ\fP. .TP \fBENOENT\fP Значение \fIpathname\fP равно пустой строке, но в \fIflags\fP не указано значение \fBAT_EMPTY_PATH\fP. .TP \fBENOTDIR\fP Файловый дескриптор, указанный в \fIdirfd\fP, не ссылается на каталог и это не тот случай, когда \fIflags\fP содержит \fBAT_EMPTY_PATH\fP и \fIpathname\fP равно пустой строке. .TP \fBEOPNOTSUPP\fP Файловая система не поддерживает преобразование пути в файловый описатель. .TP \fBEOVERFLOW\fP .\" .\" Значение \fIhandle\->handle_bytes\fP, переданное в вызов, слишком мало. При этой ошибке \fIhandle\->handle_bytes\fP присваивается требуемый размер для описателя. .PP Вызов \fBopen_by_handle_at\fP() может завершиться со следующими ошибками: .TP \fBEBADF\fP Значение \fImount_fd\fP не является открытым файловым дескриптором. .TP \fBEBADF\fP В \fIpathname\fP содержится относительный путь, но значение \fIdirfd\fP не равно \fBAT_FDCWD\fP и не является правильным файловым дескриптором. .TP \fBEFAULT\fP Значение \fIhandle\fP указывает за пределы доступного адресного пространства. .TP \fBEINVAL\fP Значение \fIhandle\->handle_bytes\fP больше \fBMAX_HANDLE_SZ\fP или равно нулю. .TP \fBELOOP\fP Значение \fIhandle\fP указывает на символическую ссылку, но в \fIflags\fP не указан \fBO_PATH\fP. .TP \fBEPERM\fP Вызывающий не имеет мандата \fBCAP_DAC_READ_SEARCH\fP. .TP \fBESTALE\fP Значение \fIhandle\fP некорректно. Эта ошибка возникает, например, из\-за удаления файла. .SH ВЕРСИИ В FreeBSD есть относительно похожая пара системных вызовов \fBgetfh\fP() и \fBopenfh\fP(). .SH СТАНДАРТЫ Linux. .SH ИСТОРИЯ Linux 2.6.39, glibc 2.14. .SH ЗАМЕЧАНИЯ Файловый описатель может быть сгенерирован с помощью \fBname_to_handle_at\fP() в одном процессе и использован в вызовах \fBopen_by_handle_at\fP() в другом. .PP Некоторые файловые системы не поддерживают трансляцию путей в файловые описатели, например: \fI/proc\fP, \fI/sys\fP и различные сетевые файловые системы. .PP Файловый описатель может стать некорректным («просроченным»), если файл удалён, или по другим причинам, относящимся к файловой системе. Для некорректных описателей \fBopen_by_handle_at\fP() возвращает ошибку \fBESTALE\fP. .PP .\" https://lwn.net/Articles/375888/ .\" "Open by handle" - Jonathan Corbet, 2010-02-23 Данные системные вызовы предназначены для использования в файловых серверах пространства пользователя. Например, сервер пользовательского пространства NFS может генерировать файловый описатель и передавать его клиенту NFS. Позднее, когда клиент захочет открыть файл, он может передать описатель обратно серверу. Такого рода возможность позволяет файловому серверу пространства пользователя работать без формирования состояния (stateless fashion) для файлов, которые они обслуживают. .PP .\" commit bcda76524cd1fa32af748536f27f674a13e56700 Если \fIpathname\fP указывает на символическую ссылку и в \fIflags\fP отсутствует \fBAT_SYMLINK_FOLLOW\fP, то \fBname_to_handle_at\fP() возвращает описатель ссылки (а не файла, на который она ссылается). Процесс, получивший описатель, может позднее выполнить операции над символической ссылкой, преобразовав описатель в файловый дескриптор, используя \fBopen_by_handle_at\fP() с флагом \fBO_PATH\fP, и затем передав файловый дескриптор через аргумент \fIdirfd\fP в системные вызовы \fBreadlinkat\fP(2) и \fBfchownat\fP(2). .SS "Получение постоянного идентификатора файловой системы" Идентификаторы монтирования в \fI/proc/self/mountinfo\fP могут быть использованы повторно после размонтирования и монтирования файловой системы. Поэтому, идентификатор монтирования, возвращаемый \fBname_to_handle_at\fP() (в \fI*mount_id\fP), не должен считаться постоянным идентификатором соответствующей файловой системы. Однако, приложение может использовать информацию в записи \fImountinfo\fP, которая соответствует идентификатору монтирования, для получения постоянного идентификатора. .PP .\" e.g., http://stackoverflow.com/questions/6748429/using-libblkid-to-find-uuid-of-a-partition Например, можно использовать имя устройства в пятом поле записи \fImountinfo\fP для поиска соответствующего устройству UUID через символические ссылки в \fI/dev/disks/by\-uuid\fP (более удобный способ получения UUID — использовать библиотеку \fBlibblkid\fP(3)). Этот процесс может быть и обратным — используя UUID найти имя устройства, и затем получить соответствующую точку монтирования, чтобы создать аргумент \fImount_fd\fP, используемый для \fBopen_by_handle_at\fP(). .SH ПРИМЕРЫ Две представленные далее программы демонстрируют использование \fBname_to_handle_at\fP() и \fBopen_by_handle_at\fP(). Первая программа (\fIt_name_to_handle_at.c\fP) использует \fBname_to_handle_at\fP() для получения файлового описателя и идентификатора монтирования для файла, указанного в аргументе командной строки; описатель и идентификатор монтирования записываются в стандартный вывод. .PP Вторая программа (\fIt_open_by_handle_at.c\fP) читает идентификатор монтирования и файловый описатель из стандартного ввода. Затем программа, используя описатель, применяет \fBopen_by_handle_at\fP() для открытия файла. Если указан необязательный параметр командной строки, то аргумент \fImount_fd\fP для \fBopen_by_handle_at\fP() создаётся из открытия каталога, указанного в аргументе. В противном случае \fImount_fd\fP заполняется результатом сканированием \fI/proc/self/mountinfo\fP в целях найти запись, чей идентификатор монтирования совпадает с идентификатором монтирования из стандартного ввода, и открывается каталог монтирования, указанный в этой записи (эти программы не учитывают, что идентификатор монтирования не постоянен). .PP Следующий сеанс работы в оболочке показывает использование этих программ: .PP .in +4n .EX $ \fBecho \[aq]Can you please think about it?\[aq] > cecilia.txt\fP $ \fB./t_name_to_handle_at cecilia.txt > fh\fP $ \fB./t_open_by_handle_at < fh\fP open_by_handle_at: Operation not permitted $ \fBsudo ./t_open_by_handle_at < fh\fP # Need CAP_SYS_ADMIN Read 31 bytes $ \fBrm cecilia.txt\fP .EE .in .PP .\" Christoph Hellwig: That's why the file handles contain a generation .\" counter that gets incremented in this case. Теперь мы удаляем и (быстро) пересоздаём файл с тем же содержимым и (если повезёт) с той же инодой. Не смотря на это, \fBopen_by_handle_at\fP() распознаёт, что первоначальный файл, на который указывал файловый описатель, больше не существует. .PP .in +4n .EX $ \fBstat \-\-printf="%i\en" cecilia.txt\fP # Display inode number 4072121 $ \fBrm cecilia.txt\fP $ \fBecho \[aq]Can you please think about it?\[aq] > cecilia.txt\fP $ \fBstat \-\-printf="%i\en" cecilia.txt\fP # Check inode number 4072121 $ \fBsudo ./t_open_by_handle_at < fh\fP open_by_handle_at: Stale NFS file handle .EE .in .SS "Исходный код программы: t_name_to_handle_at.c" .\" SRC BEGIN (t_name_to_handle_at.c) \& .EX #define _GNU_SOURCE #include #include #include #include #include \& int main(int argc, char *argv[]) { int mount_id, fhsize, flags, dirfd; char *pathname; struct file_handle *fhp; \& if (argc != 2) { fprintf(stderr, "Usage: %s pathname\en", argv[0]); exit(EXIT_FAILURE); } \& pathname = argv[1]; \& /* Allocate file_handle structure. */ \& fhsize = sizeof(*fhp); fhp = malloc(fhsize); if (fhp == NULL) err(EXIT_FAILURE, "malloc"); \& /* Make an initial call to name_to_handle_at() to discover the size required for file handle. */ \& dirfd = AT_FDCWD; /* For name_to_handle_at() calls */ flags = 0; /* For name_to_handle_at() calls */ 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()\en"); exit(EXIT_FAILURE); } \& /* Reallocate file_handle structure with correct size. */ \& fhsize = sizeof(*fhp) + fhp\->handle_bytes; fhp = realloc(fhp, fhsize); /* Copies fhp\->handle_bytes */ if (fhp == NULL) err(EXIT_FAILURE, "realloc"); \& /* Get file handle from pathname supplied on command line. */ \& if (name_to_handle_at(dirfd, pathname, fhp, &mount_id, flags) == \-1) err(EXIT_FAILURE, "name_to_handle_at"); \& /* Write mount ID, file handle size, and file handle to stdout, for later reuse by t_open_by_handle_at.c. */ \& printf("%d\en", mount_id); printf("%u %d ", fhp\->handle_bytes, fhp\->handle_type); for (size_t j = 0; j < fhp\->handle_bytes; j++) printf(" %02x", fhp\->f_handle[j]); printf("\en"); \& exit(EXIT_SUCCESS); } .EE .\" SRC END .SS "Исходный код программы: t_open_by_handle_at.c" .\" SRC BEGIN (t_open_by_handle_at.c) \& .EX #define _GNU_SOURCE #include #include #include #include #include #include #include \& /* Scan /proc/self/mountinfo to find the line whose mount ID matches \[aq]mount_id\[aq]. (An easier way to do this is to install and use the \[aq]libmount\[aq] library provided by the \[aq]util\-linux\[aq] project.) Open the corresponding mount path and return the resulting file descriptor. */ \& static int open_mount_path_by_id(int mount_id) { int mi_mount_id, found; char mount_path[PATH_MAX]; char *linep; FILE *fp; size_t lsize; ssize_t nread; \& fp = fopen("/proc/self/mountinfo", "r"); if (fp == NULL) err(EXIT_FAILURE, "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()\en"); exit(EXIT_FAILURE); } \& if (mi_mount_id == mount_id) found = 1; } free(linep); \& fclose(fp); \& if (!found) { fprintf(stderr, "Could not find mount point\en"); exit(EXIT_FAILURE); } \& return open(mount_path, O_RDONLY); } \& int main(int argc, char *argv[]) { int mount_id, fd, mount_fd, handle_bytes; char buf[1000]; #define LINE_SIZE 100 char line1[LINE_SIZE], line2[LINE_SIZE]; char *nextp; ssize_t nread; struct file_handle *fhp; \& if ((argc > 1 && strcmp(argv[1], "\-\-help") == 0) || argc > 2) { fprintf(stderr, "Usage: %s [mount\-path]\en", argv[0]); exit(EXIT_FAILURE); } \& /* Standard input contains mount ID and file handle information: \& Line 1: Line 2: */ \& if (fgets(line1, sizeof(line1), stdin) == NULL || fgets(line2, sizeof(line2), stdin) == NULL) { fprintf(stderr, "Missing mount_id / file handle\en"); exit(EXIT_FAILURE); } \& mount_id = atoi(line1); \& handle_bytes = strtoul(line2, &nextp, 0); \& /* Given handle_bytes, we can now allocate file_handle structure. */ \& fhp = malloc(sizeof(*fhp) + handle_bytes); if (fhp == NULL) err(EXIT_FAILURE, "malloc"); \& fhp\->handle_bytes = handle_bytes; \& fhp\->handle_type = strtoul(nextp, &nextp, 0); \& for (size_t j = 0; j < fhp\->handle_bytes; j++) fhp\->f_handle[j] = strtoul(nextp, &nextp, 16); \& /* Obtain file descriptor for mount point, either by opening the pathname specified on the command line, or by scanning /proc/self/mounts to find a mount that matches the \[aq]mount_id\[aq] that we received from 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) err(EXIT_FAILURE, "opening mount fd"); \& /* Open file using handle and mount point. */ \& fd = open_by_handle_at(mount_fd, fhp, O_RDONLY); if (fd == \-1) err(EXIT_FAILURE, "open_by_handle_at"); \& /* Try reading a few bytes from the file. */ \& nread = read(fd, buf, sizeof(buf)); if (nread == \-1) err(EXIT_FAILURE, "read"); \& printf("Read %zd bytes\en", nread); \& exit(EXIT_SUCCESS); } .EE .\" SRC END .SH "СМ. ТАКЖЕ" \fBopen\fP(2), \fBlibblkid\fP(3), \fBblkid\fP(8), \fBfindfs\fP(8), \fBmount\fP(8) .PP Документация \fIlibblkid\fP и \fIlibmount\fP в последнем выпуске \fIutil\-linux\fP .UR https://www.kernel.org/pub/linux/utils/util\-linux/ .UE .PP .SH ПЕРЕВОД Русский перевод этой страницы руководства был сделан Azamat Hackimov , Konstantin Shvaykovskiy , Yuri Kozlov и Иван Павлов . .PP Этот перевод является бесплатной документацией; прочитайте .UR https://www.gnu.org/licenses/gpl-3.0.html Стандартную общественную лицензию GNU версии 3 .UE или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ. .PP Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на .MT man-pages-ru-talks@lists.sourceforge.net .ME .