openat2(2)

الاسم
openat2 - فتح ملف وربما إنشاؤه (موسع)

المكتبة
مكتبة سي المعيارية (libc، -lc)

موجز
#include <fcntl.h>          /* تعريف ثوابت O_* و S_* */
#include <linux/openat2.h>  /* تعريف ثوابت RESOLVE_* */
#include <sys/syscall.h>    /* تعريف ثوابت SYS_* */
#include <unistd.h>

long syscall(SYS_openat2, int dirfd, const char *path,
             struct open_how *how, size_t size);

ملاحظة: لا توفر glibc غلافًا لـ openat2()، مما يستلزم استخدام syscall(2).

الوصف
استدعاء النظام openat2() هو امتداد لـ openat(2) ويوفر مجموعة شاملة من وظائفه.

يفتح استدعاء النظام openat2() الملف المحدد بواسطة path. إذا لم يكن الملف المحدد موجودًا، فقد يُنشئ اختياريًا (إذا حُددت O_CREAT في how.flags).

كما في openat(2)، إذا كان path نسبيًا، فإنه يُفسر بالنسبة للدليل المشار إليه بواصف الملف dirfd (أو دليل العمل الحالي للعملية المستدعية، إذا كان dirfd هو القيمة الخاصة AT_FDCWD). إذا كان path مطلقًا، فسيُتجاهل dirfd (إلا إذا كان how.resolve يحتوي على RESOLVE_IN_ROOT، وفي هذه الحالة يُحل path بالنسبة لـ dirfd).

بدلاً من أخذ وسيط واحد flags، يُمرر هيكل قابل للتوسيع (how) للسماح بالامتدادات المستقبلية. يجب تحديد الوسيط size كـ sizeof(struct open_how).

  هيكل open_how
يحدد الوسيط how كيفية فتح path، ويعمل كمجموعة شاملة لوسيطي flags و mode لـ openat(2). هذا الوسيط هو مؤشر لهيكل open_how، الموصوف في open_how(2type).

ستٌنفذ أي امتدادات مستقبلية لـ openat2() كحقول جديدة ملحقة بهيكل open_how، مع قيمة صفرية في حقل جديد تؤدي إلى تصرف النواة كما لو أن حقل الامتداد هذا غير موجود. لذلك، يجب على المستدعي ملء هذا الهيكل بالأصفار عند التهيئة. (انظر قسم "قابلية التوسيع" في ملاحظات لمزيد من التفاصيل حول سبب ضرورة ذلك.)

حقول هيكل open_how هي كما يلي:

flags
    يحدد هذا الحقل أعلام إنشاء الملف وحالة الملف لاستخدامها عند فتح الملف. جميع أعلام O_* المعرفة لـ openat(2) هي قيم أعلام صالحة لـ openat2().

    بينما يتجاهل openat(2) البتات غير المعروفة في وسيط flags الخاص به، يُرجع openat2() خطأً إذا حُددت أعلام غير معروفة أو متعارضة في how.flags.

mode
    يحدد هذا الحقل وضع الملف الجديد، بدلالات مطابقة لوسيط mode لـ openat(2).

    بينما يتجاهل openat(2) البتات غير الموجودة في النطاق 07777 في وسيط mode الخاص به، يُرجع openat2() خطأً إذا كان how.mode يحتوي على بتات غير 07777. وبالمثل، يُرجع خطأً إذا اُستدعي openat2() بقيمة غير صفرية لـ how.mode ولا يحتوي how.flags على O_CREAT أو O_TMPFILE.

resolve
    هذا قناع بتات من الأعلام التي تعدل الطريقة التي سيُحل بها جميع مكونات path. (انظر path_resolution(7) للحصول على معلومات أساسية.)

    الاستخدام الأساسي لهذه الأعلام هو السماح للبرامج الموثوقة بتقييد كيفية حل المسارات غير الموثوقة (أو المسارات داخل الدلائل غير الموثوقة). القائمة الكاملة لأعلام resolve هي كما يلي:

    RESOLVE_BENEATH
        لا تسمح بنجاح حل المسار إذا كان أي مكون من مكونات الحل ليس سليلاً للدليل المشار إليه بواسطة dirfd. يؤدي هذا إلى ربط الروابط الرمزية المطلقة (والقيم المطلقة لـ path).

        حاليًا، يعطل هذا العلم أيضًا حل الروابط السحرية (انظر أدناه). ومع ذلك، قد يتغير هذا في المستقبل. لذلك، لضمان عدم حل الروابط السحرية، يجب على المستدعي تحديد RESOLVE_NO_MAGICLINKS صراحةً.

    RESOLVE_IN_ROOT
        عامل الدليل المشار إليه بواسطة dirfd كدليل الجذر أثناء حل path. تُفسر الروابط الرمزية المطلقة بالنسبة لـ dirfd. إذا كان مكون بادئة من path يساوي dirfd، فإن المكون التالي مباشرة .. يساوي أيضًا dirfd (تمامًا كما أن /.. مكافئ تقليديًا لـ /). إذا كان path مطلقًا، فإنه يُفسر أيضًا بالنسبة لـ dirfd.

        تأثير هذا العلم كما لو أن العملية المستدعية استخدمت chroot(2) لتعديل دليل جذرها (مؤقتًا) (إلى الدليل المشار إليه بواسطة dirfd). ومع ذلك، على عكس chroot(2) (الذي يغير جذر نظام الملفات بشكل دائم لعملية ما)، يسمح RESOLVE_IN_ROOT للبرنامج بتقييد حل المسار بكفاءة على أساس كل فتحة.

        حاليًا، يعطل هذا العلم أيضًا حل الروابط السحرية. ومع ذلك، قد يتغير هذا في المستقبل. لذلك، لضمان عدم حل الروابط السحرية، يجب على المستدعي تحديد RESOLVE_NO_MAGICLINKS صراحةً.

    RESOLVE_NO_MAGICLINKS
        تعطيل حل جميع الروابط السحرية أثناء تحليل المسار.

        الروابط السحرية هي كائنات تشبه الروابط الرمزية توجد بشكل بارز في proc(5)؛ تتضمن الأمثلة /proc/pid/exe و /proc/pid/fd/*. (انظر symlink(7) لمزيد من التفاصيل.)

        قد يكون فتح الروابط السحرية دون علم محفوفًا بالمخاطر لبعض التطبيقات. تتضمن أمثلة هذه المخاطر ما يلي:

        •   إذا كانت العملية التي تفتح اسم مسار هي عملية تحكم ليس لديها حاليًا طرفية تحكم (انظر credentials(7))، فإن فتح رابط سحري داخل /proc/pid/fd يشير بالصدفة إلى طرفية سيؤدي إلى حصول العملية على طرفية تحكم.

        •   في بيئة محتواة، قد يشير رابط سحري داخل /proc إلى كائن خارج الحاوية، وبالتالي قد يوفر وسيلة للهروب من الحاوية.

        بسبب هذه المخاطر، قد يفضل أحد التطبيقات تعطيل حل الرابط السحري باستخدام العلم RESOLVE_NO_MAGICLINKS.

        إذا كان المكون التالي (أي اسم الأساس) لـ path رابطًا سحريًا، ويحتوي how.resolve على RESOLVE_NO_MAGICLINKS، ويحتوي how.flags على كل من O_PATH و O_NOFOLLOW، فسيُرجع واصف ملف O_PATH يشير إلى الرابط السحري.

    RESOLVE_NO_SYMLINKS
        تعطيل حل الروابط الرمزية أثناء تحليل المسار. يستلزم هذا الخيار RESOLVE_NO_MAGICLINKS.

        إذا كان المكون التالي (أي اسم الأساس) لـ path رابطًا رمزيًا، ويحتوي how.resolve على RESOLVE_NO_SYMLINKS، ويحتوي how.flags على كل من O_PATH و O_NOFOLLOW، فسيُرجع واصف ملف O_PATH يشير إلى الرابط الرمزي.

        لاحظ أن تأثير العلم RESOLVE_NO_SYMLINKS، الذي يؤثر على معالجة الروابط الرمزية في جميع مكونات path، يختلف عن تأثير علم إنشاء الملف O_NOFOLLOW (في how.flags)، الذي يؤثر على معالجة الروابط الرمزية فقط في المكون الأخير من path.

        يُشجع التطبيقات التي تستخدم العلم RESOLVE_NO_SYMLINKS على جعل استخدامه قابلاً للتكوين (إلا إذا اُستخدم لغرض أمني محدد)، لأن الروابط الرمزية تُستخدم على نطاق واسع جدًا من قبل المستخدمين النهائيين. قد يؤدي تعيين هذا العلم بشكل عشوائي—أي لأغراض لا تتعلق تحديدًا بالأمان—لجميع استخدامات openat2() إلى أخطاء زائفة على أنظمة كانت تعمل سابقًا. قد يحدث هذا إذا، على سبيل المثال، عُدل اسم مسار نظام يستخدمه تطبيق (مثلًا في إصدار توزيعة جديد) بحيث يحتوي مكون اسم المسار (الآن) على رابط رمزي.

    RESOLVE_NO_XDEV
        تعطيل عبور نقاط الوصل أثناء تحليل المسار (بما في ذلك جميع وصلات الربط). وبالتالي، يجب أن يكون path إما على نفس الوصل مثل الدليل المشار إليه بواسطة dirfd، أو على نفس الوصل مثل دليل العمل الحالي إذا حُدد dirfd كـ AT_FDCWD.

        يُشجع التطبيقات التي تستخدم العلم RESOLVE_NO_XDEV على جعل استخدامه قابلاً للتكوين (إلا إذا اُستخدم لغرض أمني محدد)، لأن وصلات الربط تُستخدم على نطاق واسع من قبل المستخدمين النهائيين. قد يؤدي تعيين هذا العلم بشكل عشوائي—أي لأغراض لا تتعلق تحديدًا بالأمان—لجميع استخدامات openat2() إلى أخطاء زائفة على أنظمة كانت تعمل سابقًا. قد يحدث هذا إذا، على سبيل المثال، عُدل اسم مسار نظام يستخدمه تطبيق (مثلًا في إصدار توزيعة جديد) بحيث يحتوي مكون اسم المسار (الآن) على وصل ربط.

    RESOLVE_CACHED (منذ لينكس 5.12)
        جعل عملية الفتح تفشل ما لم تكن جميع مكونات المسار موجودة بالفعل في خبيئة البحث الخاصة بالنواة. إذا كان أي نوع من إعادة التحقق أو الإدخال/الإخراج مطلوبًا لتلبية البحث، يفشل openat2() مع الخطأ EAGAIN. هذا مفيد في توفير فتح سريع يمكن تنفيذه دون اللجوء إلى تفريغ الخيوط، أو آليات أخرى قد يستخدمها التطبيق لتفريغ العمليات الأبطأ.

    إذا عُينت أي بتات غير تلك المذكورة أعلاه في how.resolve، يُرجع خطأ.

قيمة الإرجاع
عند النجاح، يُرجع واصف ملف جديد. عند الخطأ، يُرجع -1، ويعُين errno للإشارة إلى الخطأ.

الأخطاء
تتضمن مجموعة الأخطاء التي يُرجعها openat2() جميع الأخطاء التي يُرجعها openat(2)، بالإضافة إلى الأخطاء الإضافية التالية:

E2BIG
    حُدد امتداد لا تدعمه هذه النواة في how. (انظر قسم "قابلية التوسع" في NOTES لمزيد من التفاصيل حول كيفية معالجة الامتدادات.)

EAGAIN
    يحتوي how.resolve إما على RESOLVE_IN_ROOT أو RESOLVE_BENEATH، ولم تتمكن النواة من ضمان عدم هروب مكون ".." (بسبب حالة سباق أو هجوم محتمل). قد يختار المتصل إعادة محاولة استدعاء openat2().

EAGAIN
    عُين RESOLVE_CACHED، ولا يمكن تنفيذ عملية الفتح باستخدام المعلومات المخبأة فقط. يجب على المتصل إعادة المحاولة دون تعيين RESOLVE_CACHED في how.resolve.

EINVAL
    حُدد علم غير معروف أو قيمة غير صالحة في how.

EINVAL
    mode غير صفري، لكن how.flags لا يحتوي على O_CREAT أو O_TMPFILE.

EINVAL
    كان size أصغر من أي إصدار معروف من struct open_how.

ELOOP
    يحتوي how.resolve على RESOLVE_NO_SYMLINKS، وكان أحد مكونات المسار رابطًا رمزيًا (أو رابطًا سحريًا).

ELOOP
    يحتوي how.resolve على RESOLVE_NO_MAGICLINKS، وكان أحد مكونات المسار رابطًا سحريًا.

EXDEV
    يحتوي how.resolve إما على RESOLVE_IN_ROOT أو RESOLVE_BENEATH، واُكتشف هروب من الجذر أثناء تحليل المسار.

EXDEV
    يحتوي how.resolve على RESOLVE_NO_XDEV، ويعبر مكون مسار نقطة وصل.

المعايير
لينكس.

التاريخ
لينكس 5.6.

دلالات RESOLVE_BENEATH صُممت على غرار O_BENEATH في فري بي إس دي، لكنها تجنبت خطأ صحة معروفًا في تنفيذ فري بي إس دي جعله غير آمن فعليًا. لاحقًا، قدمت فري بي إس دي 13 O_RESOLVE_BENEATH لاستبدال O_BENEATH غير الآمن. دلالات O_RESOLVE_BENEATH في فري بي إس دي مبنية على RESOLVE_BENEATH في لينكس، والاثنان الآن متكافئان وظيفيًا.

ملاحظات

  القابلية للتوسع
للسماح بقابلية التوسع المستقبلية، تتطلب openat2() من تطبيق مساحة المستخدم تحديد حجم بنية open_how التي يمررها. بتوفير هذه المعلومات، يمكن لـ openat2() توفير التوافق الأمامي والخلفي، مع عمل size كرقم إصدار ضمني. (لأن حقول الامتداد الجديدة ستُضاف دائمًا في النهاية، سيزداد حجم البنية دائمًا.) تصميم قابلية التوسع هذا مشابه جدًا لاستدعاءات نظام أخرى مثل sched_setattr(2) و perf_event_open(2) و clone3(2).

إذا جعلنا usize هو حجم البنية كما هو محدد من قبل تطبيق مساحة المستخدم، و ksize هو حجم البنية التي يدعمها النواة، فهناك ثلاث حالات يجب مراعاتها:

•   إذا كان ksize يساوي usize، فلا يوجد عدم تطابق في الإصدار ويمكن استخدام how كما هو.

•   إذا كان ksize أكبر من usize، فهذا يعني وجود بعض حقول التوسعة التي تدعمها النواة ولكن تطبيق مساحة المستخدم لا يدركها. ولأن القيمة الصفرية في أي حقل توسعة مضاف تعني عدم التنفيذ (no-op)، تعامل النواة جميع حقول التوسعة التي لم يوفرها تطبيق مساحة المستخدم على أنها ذات قيم صفرية. يوفر هذا توافقا مع الإصدارات السابقة.

•   إذا كان ksize أصغر من usize، فهناك بعض حقول الامتداد التي يعرفها تطبيق مساحة المستخدم ولكن النواة لا تدعمها. لأن أي حقل امتداد يجب أن تشير قيمه الصفرية إلى عدم إجراء عملية، يمكن للنواة تجاهل حقول الامتداد غير المدعومة بأمان إذا كانت كلها أصفار. إذا كانت أي حقول امتداد غير مدعومة غير صفرية، فسيُرجع -1 ويُعين errno إلى E2BIG. هذا يوفر التوافق الأمامي.

لأن تعريف struct open_how قد يتغير في المستقبل (مع إضافة حقول جديدة عند تحديث رؤوس النظام)، يجب على تطبيقات مساحة المستخدم ملء struct open_how بالأصفار لضمان أن إعادة ترجمة البرنامج مع رؤوس جديدة لن تؤدي إلى أخطاء زائفة في وقت التشغيل. أبسط طريقة هي استخدام مهيئ معين:

    struct open_how how = { .flags = O_RDWR,
                            .resolve = RESOLVE_IN_ROOT };

أو باستخدام memset(3) صراحةً أو ما شابه:

    struct open_how how;
    memset(&how, 0, sizeof(how));
    how.flags = O_RDWR;
    how.resolve = RESOLVE_IN_ROOT;

يمكن لتطبيق في مساحة المستخدم يرغب في تحديد الامتدادات التي تدعمها النواة العاملة القيام بذلك عن طريق إجراء بحث ثنائي على size مع هيكل يحتوي كل بايت فيه على قيمة غير صفرية (للعثور على أكبر قيمة لا تنتج خطأ E2BIG).

انظر أيضًا
openat(2), open_how(2type), path_resolution(7), symlink(7)

ترجمة
تُرجمت هذه الصفحة من الدليل بواسطة زايد السعيدي <zayed.alsaidi@gmail.com> و #

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 (https://www.gnu.org/licenses/gpl-3.0.html) أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

إذا وجدت أي أخطاء في ترجمة صفحة الدليل هذه، يرجى إرسال بريد إلكتروني إلى قائمة بريد المترجمين: kde-l10n-ar@kde.org.