- 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 *path,
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() مقبض ملف ومعرف وصل يتوافقان مع الملف المحدد بوسيطي dirfd و path. يُعاد مقبض الملف عبر الوسيط 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 هو قناع بتات يُبنى عبر إجراء عملية OR لصفر أو أكثر من AT_HANDLE_FID و AT_HANDLE_MNT_ID_UNIQUE و AT_HANDLE_CONNECTABLE و AT_EMPTY_PATH و AT_SYMLINK_FOLLOW، الموصوفة أدناه.
عندما يحتوي flags على علم AT_HANDLE_FID (منذ لينكس 6.5)، يشير المستدعِي إلى أن file_handle المُعاد مطلوب لتحديد كائن نظام الملفات، وليس لفتح الملف لاحقًا، لذا يُتوقع أن استدعاءً لاحقًا لـ open_by_handle_at() باستخدام file_handle المُعاد قد يفشل.
عندما يحتوي flags على علم AT_HANDLE_MNT_ID_UNIQUE (منذ لينكس 6.12)، يشير المستدعِي إلى أن عرض المخزن المؤقت mount_id هو 64 بت على الأقل، وعندها يكون معرف الوصل المُعاد في ذلك المخزن هو معرف الوصل الفريد مثل ذلك الذي يُعيده statx(2) مع علم STATX_MNT_ID_UNIQUE.
عندما يحتوي flags على علم AT_HANDLE_CONNECTABLE (منذ لينكس 6.13)، يشير المستدعِي إلى أن file_handle المُعاد مطلوب لفتح ملف بمسار معروف لاحقًا، لذا يُتوقع أن استدعاءً لاحقًا لـ open_by_handle_at() باستخدام file_handle المُعاد قد يفشل إذا نُقل الملف، ولكن بخلاف ذلك، يُتوقع أن يكون مسار الملف المفتوح مرئيًا من الرابط السحري /proc/pid/fd/*. لا يمكن استخدام هذا العلم مع علمي AT_HANDLE_FID و/أو AT_EMPTY_PATH.
يحدد وسيطا path و dirfd معًا الملف المطلوب الحصول على مقبض له. هناك أربع حالات متميزة:
- •
- إذا كان path سلسلة غير فارغة تحتوي على اسم مسار مطلق، فيُعاد مقبض للملف المشار إليه باسم المسار ذلك. وفي هذه الحالة، يُتجاهل dirfd.
- •
- إذا كان path سلسلة غير فارغة تحتوي على اسم مسار نسبي وكانت قيمة dirfd هي القيمة الخاصة AT_FDCWD، فيُفسر path بالنسبة إلى دليل العمل الحالي للمستدعِي، ويُعاد مقبض للملف الذي يشير إليه.
- •
- إذا كان path سلسلة غير فارغة تحتوي على اسم مسار نسبي وكان dirfd واصف ملف يشير إلى دليل، فيُفسر path بالنسبة إلى الدليل المشار إليه بواسطة dirfd، ويُعاد مقبض للملف الذي يشير إليه. (انظر openat(2) لشرح سبب فائدة "واصفات ملفات الدليل".)
- •
- إذا كان path سلسلة فارغة وحدد flags القيمة AT_EMPTY_PATH، فيمكن أن يكون dirfd واصف ملف مفتوح يشير إلى أي نوع من الملفات، أو AT_FDCWD، بمعنى دليل العمل الحالي، ويُعاد مقبض للملف الذي يشير إليه.
يُعيد وسيط mount_id معرفًا لوصل نظام الملفات الذي يتوافق مع path. يتوافق هذا مع الحقل الأول في أحد السجلات في /proc/self/mountinfo. ويؤدي فتح اسم المسار في الحقل الخامس من ذلك السجل إلى الحصول على واصف ملف لنقطة الوصل؛ يمكن استخدام واصف الملف هذا في استدعاء لاحق لـ open_by_handle_at(). يُعاد mount_id في حال نجاح الاستدعاء وأيضًا في حال الاستدعاء الذي ينتج عنه الخطأ EOVERFLOW.
افتراضيًا، لا يتبع name_to_handle_at() المسار path إذا كان رابطًا رمزيًا، وبالتالي يُعيد مقبضًا للرابط نفسه. إذا حُدد AT_SYMLINK_FOLLOW في flags، فسيُتبع المسار path إذا كان رابطًا رمزيًا (بحيث يُعيد الاستدعاء مقبضًا للملف الذي يشير إليه الرابط).
لا يطلق 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
- يشير path أو mount_id أو handle إلى خارج مساحة العنوان القابلة للوصول.
- EINVAL
- يتضمن flags قيمة بت غير صالحة أو تركيبة بتات غير صالحة.
- EINVAL
- قيمة handle->handle_bytes أكبر من MAX_HANDLE_SZ.
- ENOENT
- المسار path سلسلة فارغة، ولكن لم يُحدد AT_EMPTY_PATH في flags.
- ENOTDIR
- واصف الملف المقدم في dirfd لا يشير إلى دليل، وليست هذه هي الحالة التي يتضمن فيها flags العلم AT_EMPTY_PATH ويكون path سلسلة فارغة.
- EOPNOTSUPP
- نظام الملفات لا يدعم فك ترميز اسم مسار إلى مقبض ملف.
- EOVERFLOW
- قيمة handle->handle_bytes الممررة في الاستدعاء كانت صغيرة جدًا. عند حدوث هذا الخطأ، تُحدث قيمة handle->handle_bytes للإشارة إلى الحجم المطلوب للمقبض.
يمكن أن يفشل open_by_handle_at() بالأخطاء التالية:
- EBADF
- mount_fd ليس واصف ملف مفتوح.
- EBADF
- المسار path نسبي ولكن 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(). كما يمكن أن يحدث هذا الخطأ إذا تم الحصول على handle باستخدام علم AT_HANDLE_CONNECTABLE ونُقل الملف إلى أصل مختلف.
الإصدارات¶
يحتوي 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. لاحقًا، عندما يريد العميل فتح الملف، يمكنه تمرير المقبض مرة أخرى إلى الخادم. هذا النوع من الوظائف يسمح لخادم الملفات في مساحة المستخدم بالعمل بطريقة عديمة الحالة فيما يتعلق بالملفات التي يخدمها.
إذا كان path يشير إلى رابط رمزي و 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 *path;
struct file_handle *fhp;
if (argc != 2) {
fprintf(stderr, "Usage: %s path\n", argv[0]);
exit(EXIT_FAILURE);
}
path = 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, path, 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, path, 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> #define streq(...) (strcmp(__VA_ARGS__) == 0) /* 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 && streq(argv[1], "--help")) || 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.
| 22 فبراير 2026 | صفحات دليل لينكس 6.18 |