- trixie-backports 4.31.0-1~bpo13+1
- testing 4.31.0-1
- unstable 4.31.0-1
| open_by_handle_at(2) | System Calls Manual | open_by_handle_at(2) |
الاسم¶
name_to_handle_at, open_by_handle_at - الحصول على مقبض لمسار ملف وفتح ملف عبر مقبض
المكتبة¶
مكتبة سي المعيارية (libc، -lc)
موجز¶
#define _GNU_SOURCE /* انظر feature_test_macros(7) */ #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);
الوصف¶
استدعاءات النظام name_to_handle_at() و open_by_handle_at() تقسم وظيفة openat(2) إلى جزئين: يُعيد name_to_handle_at() مقبضًا غير شفاف يتوافق مع ملف محدد؛ ويفتح open_by_handle_at() الملف المقابل لمقبض أُعيد بواسطة استدعاء سابق لـ name_to_handle_at() ويُعيد واصف ملف مفتوح.
name_to_handle_at()¶
يُعيد استدعاء النظام name_to_handle_at() مقبض ملف ومعرف وصل (mount ID) يقابل الملف المحدد عبر معطيات dirfd و pathname. يُعاد مقبض الملف عبر المعطى handle، وهو مؤشر إلى بنية بالشكل التالي:
struct file_handle {
unsigned int handle_bytes; /* حجم f_handle [مدخل، مخرج] */
int handle_type; /* نوع المقبض [مخرج] */
unsigned char f_handle[0]; /* معرف الملف (حدد حجمه
المستدعِي) [مخرج] */
};
تقع على عاتق المستدعِي مسؤولية تخصيص البنية بحجم كبير كافٍ لاحتواء المقبض المُعاد في f_handle. قبل الاستدعاء، يجب تهيئة الحقل handle_bytes لاحتواء الحجم المخصص لـ f_handle. (يحدد الثابت MAX_HANDLE_SZ، المُعرّف في <fcntl.h>، أقصى حجم متوقع لمقبض ملف. ليس حدًا أعلى مضمونًا لأن أنظمة الملفات المستقبلية قد تتطلب مساحة أكبر.) عند النجاح، يُحدّث الحقل handle_bytes لاحتواء عدد البايتات المكتوبة فعليًا في f_handle.
يمكن للمستدعِي اكتشاف الحجم المطلوب لبنية file_handle عبر إجراء استدعاء تكون فيه قيمة handle->handle_bytes صفرًا؛ وفي هذه الحالة، يفشل الاستدعاء بالخطأ EOVERFLOW وتُضبط قيمة handle->handle_bytes للإشارة إلى الحجم المطلوب؛ يمكن للمستدعِي بعد ذلك استخدام هذه المعلومات لتخصيص بنية بالحجم الصحيح (انظر الأمثلة أدناه). يجب توخي الحذر هنا لأن EOVERFLOW قد يشير أيضًا إلى عدم توفر مقبض ملف لهذا الاسم المعين في نظام ملفات يدعم عادةً البحث عن مقابض الملفات. يمكن اكتشاف هذه الحالة عند إرجاع الخطأ EOVERFLOW دون زيادة handle_bytes.
بخلاف استخدام حقل handle_bytes، يجب على المستدعِي معاملة بنية file_handle كنوع بيانات غير شفاف: يمكن استخدام حقلي handle_type و f_handle في استدعاء لاحق لـ open_by_handle_at(). يمكن للمستدعِي أيضًا استخدام file_handle غير الشفاف لمقارنة هوية كائنات نظام الملفات التي تم الاستعلام عنها في أوقات مختلفة وربما في مسارات مختلفة. يمكن للنظام الفرعي fanotify(7) الإبلاغ عن الأحداث بسجل معلومات يحتوي على file_handle لتحديد كائن نظام الملفات.
المعطى flags هو قناع بتات (bit mask) يُبنى عبر إجراء عملية OR لصفر أو أكثر من القيم AT_HANDLE_FID، و AT_EMPTY_PATH، و AT_SYMLINK_FOLLOW، الموصوفة أدناه.
عندما يحتوي flags على علم AT_HANDLE_FID (منذ لينكس 6.5)، يشير المستدعِي إلى أن file_handle المُعاد مطلوب لتحديد كائن نظام الملفات، وليس لفتح الملف لاحقًا، لذا يُتوقع أن استدعاءً لاحقًا لـ open_by_handle_at() باستخدام file_handle المُعاد قد يفشل.
يحدد المعطيان pathname و dirfd معًا الملف المراد الحصول على مقبض له. هناك أربع حالات متميزة:
- •
- إذا كان pathname سلسلة نصية غير فارغة تحتوي على مسار مطلق، فيُعاد مقبض للملف الذي يشير إليه ذلك المسار. في هذه الحالة، يُتجاهل dirfd.
- •
- إذا كان pathname سلسلة نصية غير فارغة تحتوي على مسار نسبي وكان لـ dirfd القيمة الخاصة 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(). يُعاد mount_id في حال نجاح الاستدعاء وأيضًا في حال الاستدعاء الذي ينتج عنه الخطأ EOVERFLOW.
مبدئيًا، لا يتبع name_to_handle_at() الارتباط التشعبي في pathname إذا كان رابطًا رمزيًا، وبالتالي يُعيد مقبضًا للرابط نفسه. أما إذا حُددت AT_SYMLINK_FOLLOW في flags، فيُتَّبع الرابط إذا كان رابطًا رمزيًا (بحيث يُعيد الاستدعاء مقبضًا للملف الذي يشير إليه الرابط).
لا يطلق name_to_handle_at() عملية وصل عندما يكون المكون الأخير من اسم المسار نقطة وصل آلي. عندما يدعم نظام ملفات مقابض الملفات ونقاط الوصل الآلي معًا، فإن استدعاء name_to_handle_at() على نقطة وصل آلي سينتهي بالخطأ EOVERFLOW دون زيادة handle_bytes. يمكن أن يحدث هذا منذ لينكس 4.13 مع NFS عند الوصول إلى دليل موجود على نظام ملفات منفصل على الخادم. في هذه الحالة، يمكن إطلاق الوصل الآلي عبر إضافة "/" إلى نهاية اسم المسار.
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 إذا حُدد.
يجب أن يمتلك المستدعِي قدرة CAP_DAC_READ_SEARCH لاستدعاء open_by_handle_at().
قيمة الإرجاع¶
عند النجاح، يُعيد 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 قيمة بت غير صالحة.
- EINVAL
- قيمة handle->handle_bytes أكبر من MAX_HANDLE_SZ.
- ENOENT
- المسار pathname سلسلة فارغة، لكن لم تُحدد AT_EMPTY_PATH في flags.
- ENOTDIR
- واصف الملف المزود في dirfd لا يشير إلى دليل، وليست الحالة هي تضمن flags لـ AT_EMPTY_PATH وكون pathname سلسلة فارغة معًا.
- EOPNOTSUPP
- نظام الملفات لا يدعم فك ترميز اسم مسار إلى مقبض ملف.
- EOVERFLOW
- قيمة handle->handle_bytes الممررة في الاستدعاء كانت صغيرة جدًا. عند حدوث هذا الخطأ، تُحدث قيمة handle->handle_bytes للإشارة إلى الحجم المطلوب للمقبض.
يمكن أن يفشل open_by_handle_at() بالأخطاء التالية:
- EBADF
- mount_fd ليس واصف ملف مفتوح.
- EBADF
- مسار الملف pathname نسبي ولكن dirfd ليس AT_FDCWD ولا واصف ملف صالح.
- EFAULT
- يشير handle إلى خارج مساحة العنوان القابل للوصول.
- EINVAL
- قيمة handle->handle_bytes أكبر من MAX_HANDLE_SZ أو تساوي الصفر.
- ELOOP
- يشير handle إلى رابط رمزي، ولكن لم يُحدد O_PATH في flags.
- EPERM
- المستدعِي لا يملك قدرة CAP_DAC_READ_SEARCH.
- ESTALE
- المقبض handle المحدد غير صالح لفتح ملف. يحدث هذا الخطأ إذا حُذف الملف مثلًا. يمكن أن يحدث هذا الخطأ أيضًا إذا استُحصل على handle باستخدام علامة AT_HANDLE_FID وكان نظام الملفات لا يدعم open_by_handle_at().
الإصدارات¶
يحتوي FreeBSD على زوج مشابه إلى حد كبير من استدعاءات النظام في شكل getfh() و fhopen().
المعايير¶
لينكس.
التاريخ¶
لينكس 2.6.39، glibc 2.14.
ملاحظات¶
يمكن إنشاء مقبض ملف في عملية واحدة باستخدام name_to_handle_at() واستخدامه لاحقًا في عملية مختلفة تستدعي open_by_handle_at().
بعض أنظمة الملفات لا تدعم ترجمة أسماء المسارات إلى مقابض ملفات، على سبيل المثال، /proc و /sys وأنظمة ملفات شبكية متنوعة. بعض أنظمة الملفات تدعم ترجمة أسماء المسارات إلى مقابض ملفات، لكنها لا تدعم استخدام مقابض الملفات تلك في open_by_handle_at().
قد يصبح مقبض الملف غير صالح ("قديم") إذا تم حذف ملف، أو لأسباب أخرى خاصة بنظام الملفات. يتم الإبلاغ عن المقابض غير الصالحة بواسطة خطأ ESTALE من open_by_handle_at().
صُممت استدعاءات النظام هذه لاستخدامها من قبل خوادم الملفات في مساحة المستخدم. على سبيل المثال، قد يُنشئ خادم NFS في مساحة المستخدم مقبض ملف ويمرره إلى عميل NFS. لاحقًا، عندما يريد العميل فتح الملف، يمكنه تمرير المقبض مرة أخرى إلى الخادم. هذا النوع من الوظائف يسمح لخادم الملفات في مساحة المستخدم بالعمل بطريقة عديمة الحالة فيما يتعلق بالملفات التي يخدمها.
إذا كان pathname يشير إلى رابط رمزي ولم تحدد flags القيمة AT_SYMLINK_FOLLOW، فإن name_to_handle_at() يُعيد مقبضًا للرابط (بدلًا من الملف الذي يشير إليه). يمكن للعملية التي تستلم المقبض إجراء عمليات لاحقًا على الرابط الرمزي عبر تحويل المقبض إلى واصف ملف باستخدام open_by_handle_at() مع علامة O_PATH، ثم تمرير واصف الملف كمعطى dirfd في استدعاءات النظام مثل readlinkat(2) و fchownat(2).
الحصول على معرف نظام ملفات دائم¶
يمكن إعادة استخدام معرفات التوصيل في /proc/self/mountinfo عند فصل وتوصيل أنظمة الملفات. لذلك، لا ينبغي التعامل مع معرف التوصيل الذي تُرجعه name_to_handle_at() (في *mount_id) كمعرف دائم لنظام الملفات المُوصَّل المقابل. ومع ذلك، يمكن للتطبيق استخدام المعلومات في سجل mountinfo الذي يتوافق مع معرف التوصيل لاستخلاص معرف دائم.
على سبيل المثال، يمكن استخدام اسم الجهاز في الحقل الخامس من سجل 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 # Need CAP_SYS_ADMIN Read 31 bytes $ rm cecilia.txt
الآن نحذف الملف ونعيد إنشاءه (بسرعة) بحيث يكون له نفس المحتوى و (بالصدفة) نفس العقدة. ومع ذلك، تتعرف open_by_handle_at() على أن الملف الأصلي المشار إليه بواسطة مقبض الملف لم يعد موجودًا.
$ stat --printf="%i\n" cecilia.txt # Display inode number 4072121 $ rm cecilia.txt $ echo 'Can you please think about it?' > cecilia.txt $ stat --printf="%i\n" cecilia.txt # Check inode number 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 <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
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\n", 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()\n");
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\n", 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("\n");
exit(EXIT_SUCCESS);
}
مصدر البرنامج: t_open_by_handle_at.c¶
#define _GNU_SOURCE #include <err.h> #include <fcntl.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <unistd.h> /* Scan /proc/self/mountinfo to find the line whose mount ID matches
'mount_id'. (An easier way to do this is to install and use the
'libmount' library provided by the 'util-linux' 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()\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); } 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]\n", argv[0]);
exit(EXIT_FAILURE);
}
/* Standard input contains mount ID and file handle information:
Line 1: <mount_id>
Line 2: <handle_bytes> <handle_type> <bytes of handle in hex>
*/
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);
/* 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 'mount_id'
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\n", nread);
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/
ترجمة¶
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com>
هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.
إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.
| 15 يونيو 2024 | صفحات دليل لينكس 6.9.1 |