Scroll to navigation

fanotify(7) Miscellaneous Information Manual fanotify(7)

الاسم

fanotify - مراقبة أحداث نظام الملفات

الوصف

توفر واجهة برمجة تطبيقات fanotify إشعارات واعتراضًا لأحداث نظام الملفات. تشمل حالات الاستخدام فحص الفيروسات وإدارة التخزين الهرمي. في واجهة fanotify الأصلية، دُعمت مجموعة محدودة فقط من الأحداث. على وجه الخصوص، لم يتوفر دعم لأحداث الإنشاء والحذف والنقل. أُضيف الدعم لهذه الأحداث في لينكس 5.1. (انظر inotify(7) لتفاصيل واجهة برمجة التطبيقات التي كانت تُشعر بتلك الأحداث قبل إصدار لينكس 5.1.)

تشمل القدرات الإضافية مقارنة بواجهة inotify(7) القدرة على مراقبة جميع الكائنات في نظام ملفات موصول، والقدرة على اتخاذ قرارات أذونات الوصول، وإمكانية قراءة الملفات أو تعديلها قبل وصول التطبيقات الأخرى إليها.

تُستخدم استدعاءات النظام التالية مع هذه الواجهة: fanotify_init(2)، و fanotify_mark(2)، و read(2)، و write(2)، و close(2).

fanotify_init()‎، و fanotify_mark()‎، ومجموعات الإشعارات

يُنشئ استدعاء النظام fanotify_init(2) مجموعة إشعارات fanotify ويُهيئها ويعيد واصف ملف يشير إليها.

مجموعة إشعارات fanotify هي كائن داخلي في النواة يحمل قائمة بالملفات، والأدلة، وأنظمة الملفات، والوصولات التي يجب إنشاء أحداث لها.

لكل مدخلة في مجموعة إشعارات fanotify، يوجد قناعان من البتات: قناع mark وقناع ignore. يحدد قناع العلامة (mark) أنشطة الملفات التي يجب إنشاء حدث لها. ويحدد قناع التجاهل (ignore) الأنشطة التي لا يجب توليد أحداث لها. يسمح وجود هذين النوعين من الأقنعة بوسم نظام ملفات أو وصلة أو دليل لاستلام الأحداث، وفي الوقت نفسه تجاهل الأحداث لكائنات محددة تحت تلك الوصلة أو الدليل.

يضيف استدعاء النظام fanotify_mark(2) ملفًا أو دليلاً أو نظام ملفات أو وصلة إلى مجموعة إشعارات ويحدد الأحداث التي يجب الإبلاغ عنها (أو تجاهلها)، أو يزيل أو يعدل مثل هذه المدخلة.

من الاستخدامات الممكنة لقناع التجاهل هي خبيئة الملفات. الأحداث المهمة لخبيئة الملفات هي تعديل الملف وإغلاقه. لذا، يوسم الدليل أو الوصلة المخبأة لاستلام هذه الأحداث. بعد استلام أول حدث يفيد بتعديل ملف ما، ستُبطل مدخلة الخبيئة المقابلة. لا تهم أي أحداث تعديل إضافية لهذا الملف حتى يُغلق. لذا، يمكن إضافة حدث التعديل إلى قناع التجاهل. وعند استلام حدث الإغلاق، يمكن إزالة حدث التعديل من قناع التجاهل وتحديث مدخلة خبيئة الملف.

تشير المدخلات في مجموعات إشعارات fanotify إلى الملفات والأدلة عبر رقم inode الخاص بها وإلى الوصلات عبر معرف الوصلة (mount ID). إذا أُعيدت تسمية الملفات أو الأدلة أو نُقلت ضمن نفس الوصلة، فإن المدخلات المعنية تبقى. أما إذا حُذفت الملفات أو الأدلة أو نُقلت إلى وصلة أخرى أو إذا فُصلت أنظمة الملفات أو الوصلات، فستُحذف المدخلات المقابلة.

طابور الأحداث

عند وقوع أحداث على كائنات نظام الملفات المراقبة بواسطة مجموعة إشعارات، يولد نظام fanotify أحداثًا تُجمع في طابور. يمكن بعد ذلك قراءة هذه الأحداث (باستخدام read(2) أو ما شابه) من واصف ملف fanotify الذي أعاده fanotify_init(2).

يولَّد نوعان من الأحداث: أحداث إشعار وأحداث إذن. أحداث الإشعار هي معلوماتية فقط ولا تتطلب اتخاذ أي إجراء من قبل التطبيق المستلم باستثناء حالة واحدة: إذا وُفر واصف ملف صالح ضمن حدث عام، فيجب إغلاق واصف الملف هذا. أحداث الإذن هي طلبات للتطبيق المستلم ليقرر ما إذا كان سيُمنح إذن للوصول إلى الملف أم لا. بالنسبة لهذه الأحداث، يجب على المستلم كتابة رد يقرر ما إذا كان سيُسمح بالوصول أم لا.

يُزال الحدث من طابور أحداث مجموعة fanotify عند قراءته. أحداث الإذن التي قُرئت تُحفظ في قائمة داخلية لمجموعة fanotify حتى يتم اتخاذ قرار بشأن الإذن عن طريق الكتابة إلى واصف ملف fanotify أو حتى يُغلق واصف ملف fanotify.

قراءة أحداث fanotify

استدعاء read(2) لواصف الملف الذي أعاده fanotify_init(2) يحجب (إذا لم يُحدد علم FAN_NONBLOCK في استدعاء fanotify_init(2)) حتى يقع حدث ملف أو يُقاطع الاستدعاء بإشارة (انظر signal(7)).

بعد نجاح read(2)، ستحتوي مخبأة القراءة على بنية واحدة أو أكثر من البنى التالية:


struct fanotify_event_metadata {

__u32 event_len;
__u8 vers;
__u8 reserved;
__u16 metadata_len;
__aligned_u64 mask;
__s32 fd;
__s32 pid; };

سجلات المعلومات هي قطع تكميلية من المعلومات التي قد تُوفر جنباً إلى جنب مع بنية fanotify_event_metadata العامة. تؤثر الـ flags الممررة إلى fanotify_init(2) على نوع سجلات المعلومات التي قد تُعاد لحدث ما. على سبيل المثال، إذا هُيئت مجموعة إشعارات باستخدام FAN_REPORT_FID أو FAN_REPORT_DIR_FID، فيجب على مستمعي الأحداث توقع استلام بنية fanotify_event_info_fid مع بنية fanotify_event_metadata، حيث تُستخدم مقابض الملفات لتحديد كائنات نظام الملفات بدلاً من واصفات الملفات. يمكن أيضًا تكديم سجلات المعلومات، مما يعني أن استخدام أعلام FAN_REPORT_* المتنوعة مع بعضها البعض أمر مدعوم. في هذه الحالات، يمكن إعادة سجلات معلومات متعددة لحدث ما. على سبيل المثال، إذا هُيئت مجموعة إشعارات باستخدام FAN_REPORT_TARGET_FID و FAN_REPORT_PIDFD، فيجب على مستمع الأحداث توقع استلام ما يصل إلى سجلي معلومات fanotify_event_info_fid وسجل معلومات fanotify_event_info_pidfd واحد. والأهم من ذلك، لا يوفر fanotify أي ضمانات حول ترتيب سجلات المعلومات عند تهيئة مجموعة إشعارات بتكوين مكدس. لكل سجل معلومات بنية متداخلة من نوع fanotify_event_info_header. من الضروري لمستمعي الأحداث فحص حقل info_type في هذه البنية لتحديد نوع سجل المعلومات الذي استُلم لحدث معين.

في الحالات التي تحدد فيها مجموعة fanotify كائنات نظام الملفات عن طريق مقابض الملفات، يجب على مستمعي الأحداث أيضًا توقع استلام واحد أو أكثر من كائنات سجل المعلومات أدناه مع بنية fanotify_event_metadata العامة داخل مخبأة القراءة:


struct fanotify_event_info_fid {

struct fanotify_event_info_header hdr;
__kernel_fsid_t fsid;
unsigned char handle[]; };

في الحالات التي تُهيأ فيها مجموعة fanotify باستخدام FAN_REPORT_PIDFD، يجب على مستمعي الأحداث توقع استلام كائن سجل المعلومات أدناه مع بنية fanotify_event_metadata العامة داخل مخبأة القراءة:


struct fanotify_event_info_pidfd {

struct fanotify_event_info_header hdr;
__s32 pidfd; };

في حالة حدث FAN_FS_ERROR، يُعاد سجل معلومات إضافي يصف الخطأ الذي حدث مع بنية fanotify_event_metadata العامة داخل مخبأة القراءة. تُعرف هذه البنية كالتالي:


struct fanotify_event_info_error {

struct fanotify_event_info_header hdr;
__s32 error;
__u32 error_count; };

تحتوي جميع سجلات المعلومات على بنية متداخلة من نوع fanotify_event_info_header. تحمل هذه البنية معلومات وصفية حول سجل المعلومات الذي قد يكون قد أعيد مع بنية fanotify_event_metadata العامة. تُعرف هذه البنية كالتالي:


struct fanotify_event_info_header {
	__u8 info_type;
	__u8 pad;
	__u16 len;
};

لأسباب تتعلق بالأداء، يوصى باستخدام حجم مخبأة كبير (على سبيل المثال، 4096 بايت)، بحيث يمكن جلب أحداث متعددة من خلال استدعاء read(2) واحد.

قيمة الإرجاع لـ read(2) هي عدد البايتات التي وُضعت في المخبأة، أو -1 في حالة حدوث خطأ (لكن انظر قسم BUGS).

حقول بنية fanotify_event_metadata هي كما يلي:

هذا هو طول البيانات للحدث الحالي والإزاحة إلى الحدث التالي في الخبيئة. ما لم تحدد المجموعة كائنات نظام الملفات بواسطة مقابض الملفات، فإن قيمة event_len تكون دائمًا FAN_EVENT_METADATA_LEN. بالنسبة لمجموعة تحدد كائنات نظام الملفات بواسطة مقابض الملفات، فإن event_len تتضمن أيضًا سجلات معرف الملف ذات الطول المتغير.
يحمل هذا الحقل رقم إصدار البنية. يجب مقارنته بـ FANOTIFY_METADATA_VERSION للتحقق من أن البنى التي أُعيدت في وقت التشغيل تطابق البنى المحددة في وقت التصريف. في حالة عدم التطابق، يجب على التطبيق التخلي عن محاولة استخدام واصف ملف fanotify.
هذا الحقل غير مستخدم.
هذا هو طول البنية. قُدم هذا الحقل لتسهيل تنفيذ ترويسات اختيارية لكل نوع حدث. لا توجد مثل هذه الترويسات الاختيارية في التنفيذ الحالي.
هذا قناع بتات يصف الحدث (انظر أدناه).
هذا واصف ملف مفتوح للكائن الذي يتم الوصول إليه، أو FAN_NOFD في حالة حدوث طوفان في الطابور. مع مجموعة fanotify التي تحدد كائنات نظام الملفات عن طريق مقابض الملفات، يجب أن تتوقع التطبيقات ضبط هذه القيمة على FAN_NOFD لكل حدث يُستلم. يمكن استخدام واصف الملف للوصول إلى محتويات الملف أو الدليل المراقب. التطبيق القارئ مسؤول عن إغلاق واصف الملف هذا.
عند استدعاء fanotify_init(2)، قد يحدد المستدعِي (عبر وسيط event_f_flags) أعلام حالة ملف متنوعة ليتم ضبطها على وصف الملف المفتوح الذي يقابل واصف الملف هذا. بالإضافة إلى ذلك، يُضبط علم حالة الملف FMODE_NONOTIFY (الداخلي للنواة) على وصف الملف المفتوح. هذا العلم يكبح توليد أحداث fanotify. ومن ثم، عندما يصل مستلم حدث fanotify إلى الملف أو الدليل المُشعر به باستخدام واصف الملف هذا، لن تُنشأ أي أحداث إضافية.
إذا ضُبط العلم FAN_REPORT_TID في fanotify_init(2)، فهذا هو معرف الخيط (TID) للخيط الذي تسبب في الحدث. وإلا، فهذا هو معرف العملية (PID) للعملية التي تسببت في الحدث.

يمكن لبرنامج يستمع إلى أحداث fanotify مقارنة معرف العملية (PID) هذا بالمعرف الذي يعيده getpid(2)، لتحديد ما إذا كان الحدث ناتجاً عن المستمع نفسه، أو بسبب وصول لملف من قبل عملية أخرى.

يشير قناع البتات في mask إلى الأحداث التي وقعت لكائن نظام ملفات واحد. قد تُضبط بتات متعددة في هذا القناع إذا وقع أكثر من حدث لكائن نظام الملفات المراقب. على وجه الخصوص، قد تُدمج الأحداث المتتالية لنفس كائن نظام الملفات والناتجة عن نفس العملية في حدث واحد، باستثناء أحداث الإذن حيث لا تُدمج أبداً في مدخلة طابور واحدة.

البتات التي قد تظهر في mask هي كما يلي:

وُصل إلى ملف أو دليل (قراءة) (لكن انظر قسم BUGS).
فُتح ملف أو دليل.
فُتح ملف بقصد تنفيذه. انظر قسم NOTES في fanotify_mark(2) لمزيد من التفاصيل.
غُيرت البيانات الوصفية لملف أو دليل.
أُنشئ ملف أو دليل ابن في دليل أب مراقب.
حُذف ملف أو دليل ابن في دليل أب مراقب.
حُذف ملف أو دليل مراقب.
كُشف عن خطأ في نظام الملفات.
نُقل ملف أو دليل من أو إلى دليل أب مراقب.
نُقل ملف أو دليل من دليل أب مراقب.
نُقل ملف أو دليل إلى دليل أب مراقب.
نُقل ملف أو دليل مراقب.
عُدل ملف.
أُغلق ملف كان مفتوحاً للكتابة (O_WRONLY أو O_RDWR).
أُغلق ملف أو دليل كان مفتوحاً للقراءة فقط (O_RDONLY).
تجاوز طابور الأحداث الحد المسموح به لعدد الأحداث. يمكن تخطي هذا الحد عن طريق تحديد علم FAN_UNLIMITED_QUEUE عند استدعاء fanotify_init(2).
يريد تطبيق قراءة ملف أو دليل، على سبيل المثال باستخدام read(2) أو readdir(2). يجب على القارئ كتابة رد (كما هو موضح أدناه) يحدد ما إذا كان سيُمنح إذن للوصول إلى كائن نظام الملفات.
يريد تطبيق فتح ملف أو دليل. يجب على القارئ كتابة رد يحدد ما إذا كان سيُمنح إذن لفتح كائن نظام الملفات.
يريد تطبيق ما فتح ملف للتنفيذ. يجب على القارئ كتابة استجابة تحدد ما إذا كان سيُمنح إذن فتح كائن نظام الملفات للتنفيذ. راجع الملاحظات (NOTES) في fanotify_mark(2) لمزيد من التفاصيل.

للتحقق من أي حدث إغلاق، يمكن استخدام قناع البتات التالي:

أُغلق ملف. هذا مرادف لـ:

FAN_CLOSE_WRITE | FAN_CLOSE_NOWRITE
    

للتحقق من أي حدث نقل، يمكن استخدام قناع البتات التالي:

نُقل ملف أو دليل. هذا مرادف لـ:

FAN_MOVED_FROM | FAN_MOVED_TO
    

قد تظهر البتات التالية في mask فقط بالاقتران مع بتات أنواع أحداث أخرى:

الأحداث الموضحة في mask وقعت على كائن دليل. يتطلب الإبلاغ عن الأحداث في الأدلة ضبط هذه الراية في قناع العلامة. راجع fanotify_mark(2) لمزيد من التفاصيل. يُبلغ عن الراية FAN_ONDIR في قناع الحدث فقط إذا كانت مجموعة fanotify تحدد كائنات نظام الملفات عن طريق مقابض الملفات.

سجلات المعلومات التي تُوفر جنباً إلى جنب مع هيكل fanotify_event_metadata العام ستحتوي دائماً على هيكل متداخل من نوع fanotify_event_info_header. حقول fanotify_event_info_header هي كما يلي:

قيمة صحيحة فريدة تمثل نوع كائن سجل المعلومات المستلم لحدث ما. يمكن ضبط قيمة هذا الحقل على أحد الخيارات التالية: FAN_EVENT_INFO_TYPE_FID أو FAN_EVENT_INFO_TYPE_DFID أو FAN_EVENT_INFO_TYPE_DFID_NAME أو FAN_EVENT_INFO_TYPE_PIDFD. تعتمد القيمة المضبوطة لهذا الحقل على الأعلام التي وُفرت لـ fanotify_init(2). ارجع إلى تفاصيل الحقل لكل نوع كائن سجل معلومات أدناه لفهم الحالات المختلفة التي يمكن فيها ضبط قيم info_type.
هذا الحقل غير مُستخدم حالياً من قبل أي نوع كائن سجل معلومات، ولذلك يُضبط على الصفر.
تُضبط قيمة len على حجم كائن سجل المعلومات، بما في ذلك fanotify_event_info_header. لا يُتوقع أن يكون الحجم الإجمالي لجميع سجلات المعلومات الإضافية أكبر من (event_len - metadata_len).

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

هذا هيكل من نوع fanotify_event_info_header. على سبيل المثال، عندما يُنشأ واصف ملف fanotify باستخدام FAN_REPORT_FID، يُتوقع إرفاق سجل معلومات واحد بالحدث مع قيمة حقل info_type هي FAN_EVENT_INFO_TYPE_FID. عندما يُنشأ واصف ملف fanotify باستخدام مزيج من FAN_REPORT_FID و FAN_REPORT_DIR_FID، قد يكون هناك سجلان للمعلومات مرفقان بالحدث: أحدهما بقيمة حقل info_type هي FAN_EVENT_INFO_TYPE_DFID، للتعريف بكائن دليل أب، والآخر بقيمة حقل info_type هي FAN_EVENT_INFO_TYPE_FID، للتعريف بكائن ابن. لاحظ أنه بالنسبة لأحداث تعديل مدخلات الدليل FAN_CREATE و FAN_DELETE و FAN_MOVE و FAN_RENAME، لا يُبلغ عن سجل معلومات يحدد الكائن الابن المنشأ/المحذوف/المنقول إلا إذا بُدئت مجموعة fanotify باستخدام الراية FAN_REPORT_TARGET_FID.
هذا معرف فريد لنظام الملفات الذي يحتوي على الكائن المرتبط بالحدث. وهو بنية من النوع __kernel_fsid_t ويحتوي على القيمة نفسها لـ f_fsid عند استدعاء statfs(2).
يحتوي هذا الحقل على بنية متغيرة الطول من النوع struct file_handle. وهو مقبض مبهم يقابل كائنًا محددًا في نظام الملفات كما يُرجعه name_to_handle_at(2). يمكن استخدامه لتحديد ملف بشكل فريد في نظام ملفات ويمكن تمريره كمعامل إلى open_by_handle_at(2). إذا كانت قيمة حقل info_type هي FAN_EVENT_INFO_TYPE_DFID_NAME، فيتبع مقبض الملف سلسلة نصية تنتهي بـ null تحدد اسم مدخل الدليل المنشأ/المحذوف/المنقول. بالنسبة للأحداث الأخرى مثل FAN_OPEN و FAN_ATTRIB و FAN_DELETE_SELF و FAN_MOVE_SELF، فإذا كانت قيمة حقل info_type هي FAN_EVENT_INFO_TYPE_FID، فإن handle يحدد الكائن المرتبط بالحدث. وإذا كانت قيمة حقل info_type هي FAN_EVENT_INFO_TYPE_DFID، فإن handle يحدد كائن الدليل المرتبط بالحدث أو الدليل الأب لكائن ليس دليلًا ومرتبط بالحدث. وإذا كانت قيمة حقل info_type هي FAN_EVENT_INFO_TYPE_DFID_NAME، فإن handle يحدد كائن الدليل نفسه الذي سيُبلغ عنه مع FAN_EVENT_INFO_TYPE_DFID ويتبع مقبض الملف سلسلة نصية تنتهي بـ null تحدد اسم مدخل دليل في ذلك الدليل، أو '.' لتحديد كائن الدليل نفسه.

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

هذا هيكل من نوع fanotify_event_info_header. عندما تُبدأ مجموعة fanotify باستخدام FAN_REPORT_PIDFD، تُضبط قيمة حقل info_type في fanotify_event_info_header على FAN_EVENT_INFO_TYPE_PIDFD.
هذا واصف ملف عملية يشير إلى العملية المسؤولة عن توليد الحدث. واصف ملف العملية المُعاد لا يختلف عن ذلك الذي يمكن الحصول عليه يدوياً إذا استدعي pidfd_open(2) على fanotify_event_metadata.pid. في حالة مواجهة خطأ أثناء إنشاء pidfd، قد يُعاد أحد نوعي أخطاء محتملين ممثلين بقيمة صحيحة سالبة في حقل pidfd هذا. في الحالات التي تنتهي فيها العملية المسؤولة عن توليد الحدث قبل أن يتمكن مستمع الأحداث من قراءة الأحداث من طابور الإشعارات، يُعاد FAN_NOPIDFD. يُنفذ إنشاء pidfd للحدث فقط في وقت قراءة الأحداث من طابور الإشعارات. تُمثل جميع إخفاقات إنشاء pidfd المحتملة الأخرى بـ FAN_EPIDFD. بمجرد أن يتعامل مستمع الحدث مع الحدث ولم يعد الـ pidfd مطلوباً، يجب إغلاق الـ pidfd عبر close(2).

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

هذا هيكل من نوع fanotify_event_info_header. يُضبط حقل info_type على FAN_EVENT_INFO_TYPE_ERROR.
يحدد نوع الخطأ الذي حدث.
هذا عداد لعدد الأخطاء التي كُتمت منذ آخر قراءة للخطأ.

تتوفر وحدات الماكرو التالية للتكرار عبر مخزن يحتوي على بيانات وصفية لأحداث fanotify أعادها read(2) من واصف ملف fanotify:

يتحقق هذا الماكرو من الطول المتبقي len للخبيئة meta مقابل طول بنية البيانات الوصفية وحقل event_len لأول بنية بيانات وصفية في الخبيئة.
يستخدم هذا الماكرو الطول المشار إليه في حقل event_len لبنية البيانات الوصفية التي يشير إليها meta لحساب عنوان بنية البيانات الوصفية التالية التي تلي meta. len هو عدد بايتات البيانات الوصفية المتبقية حاليًا في الخبيئة. يُرجع الماكرو مؤشرًا إلى بنية البيانات الوصفية التالية التي تلي meta، ويقلل len بمقدار عدد البايتات في بنية البيانات الوصفية التي جرى تخطيها (أي يطرح meta->event_len من len).

بالإضافة إلى ذلك، هناك:

يعيد هذا الماكرو حجم (بالبايتات) الهيكل fanotify_event_metadata. هذا هو الحد الأدنى للحجم (والحجم الوحيد حالياً) لأي بيانات وصفية للحدث.

مراقبة واصف ملف fanotify بحثاً عن أحداث

عند وقوع حدث fanotify، يشير واصف ملف fanotify إلى أنه قابل للقراءة عند تمريره إلى epoll(7) أو poll(2) أو select(2).

التعامل مع أحداث الأذونات

بالنسبة لأحداث الأذونات، يجب على التطبيق استدعاء write(2) لهيكل بالشكل التالي في واصف ملف fanotify:


struct fanotify_response {

__s32 fd;
__u32 response; };

حقول هذا الهيكل هي كالتالي:

هذا هو واصف الملف من الهيكل fanotify_event_metadata.
يشير هذا الحقل إلى ما إذا كان سيُمنح الإذن أم لا. يجب أن تكون قيمته إما FAN_ALLOW للسماح بعملية الملف أو FAN_DENY لرفض عملية الملف.

إذا رُفض الوصول، فسيستلم استدعاء التطبيق الطالب خطأ EPERM. بالإضافة إلى ذلك، إذا أُنشئت مجموعة الإشعارات مع علم FAN_ENABLE_AUDIT، فيمكن ضبط علم FAN_AUDIT في حقل response. في تلك الحالة، سيقوم نظام التدقيق (audit) بتسجيل معلومات حول قرار الوصول في سجلات التدقيق.

مراقبة أنظمة الملفات بحثاً عن أخطاء

يُخزن حدث FAN_FS_ERROR واحد لكل نظام ملفات في المرة الواحدة. تُكتم رسائل الأخطاء الإضافية وتُحسب في حقل error_count لسجل حدث FAN_FS_ERROR الموجود، ولكن تُفقد تفاصيل تلك الأخطاء.

الأخطاء التي يبلغ عنها FAN_FS_ERROR هي قيم errno عامة، ولكن لا يُبلغ عن جميع أنواع الأخطاء من قبل جميع أنظمة الملفات.

الأخطاء غير المرتبطة مباشرة بملف (أي تلف الكتلة الفائقة) يُبلغ عنها بـ handle غير صالح. لهذه الأخطاء، سيُضبط حقل handle_type في handle على FILEID_INVALID، ويُضبط حجم مخزن المقبض على 0.

إغلاق واصف ملف fanotify

عند إغلاق جميع واصفات الملفات التي تشير إلى مجموعة إشعارات fanotify، تُحرر مجموعة fanotify وتُخلى مواردها ليعيد الـ kernel استخدامها. عند close(2)، ستُضبط أحداث الأذونات المعلقة على مسموح بها.

واجهات /proc

يحتوي الملف /proc/pid/fdinfo/fd على معلومات حول علامات fanotify لواصف الملف fd للعملية pid. راجع proc(5) للتفاصيل.

منذ لينكس 5.13، يمكن استخدام الواجهات التالية للتحكم في كمية موارد النواة التي يستهلكها fanotify:

/proc/sys/fs/fanotify/max_queued_events
تُستخدم القيمة الموجودة في هذا الملف عندما يستدعي تطبيق ما fanotify_init(2) لضبط حد أقصى لعدد الأحداث التي يمكن وضعها في طابور مجموعة fanotify المقابلة. تُسقط الأحداث التي تتجاوز هذا الحد، ولكن يُولد دائماً حدث FAN_Q_OVERFLOW. قبل نواة لينكس 5.13، كان الحد المبرمج صلباً هو 16384 حدثاً.
/proc/sys/fs/fanotify/max_user_group
يحدد هذا حداً أقصى لعدد مجموعات fanotify التي يمكن إنشاؤها لكل معرف مستخدم حقيقي. قبل نواة لينكس 5.13، كان الحد المبرمج صلباً هو 128 مجموعة لكل مستخدم.
/proc/sys/fs/fanotify/max_user_marks
يحدد هذا حداً أقصى لعدد علامات fanotify التي يمكن إنشاؤها لكل معرف مستخدم حقيقي. قبل نواة لينكس 5.13، كان الحد المبرمج صلباً هو 8192 علامة لكل مجموعة (وليس لكل مستخدم).

الأخطاء

بالإضافة إلى الأخطاء المعتادة لـ read(2)، يمكن أن تحدث الأخطاء التالية عند القراءة من واصف ملف fanotify:

الخبيئة أصغر من أن تتسع لهذا الحدث.
بُلغ الحد الأقصى لعدد الملفات المفتوحة لكل عملية. انظر وصف RLIMIT_NOFILE في getrlimit(2).
بُلغ الحد الأقصى لعدد الملفات المفتوحة على مستوى النظام بأكمله. انظر /proc/sys/fs/file-max في proc(5).
يُرجع الاستدعاء read(2) هذا الخطأ إذا حُدد O_RDWR أو O_WRONLY في المعامل event_f_flags عند استدعاء fanotify_init(2) ووقع حدث لملف مراقب يجري تنفيذه حاليًا.

بالإضافة إلى الأخطاء المعتادة للاستدعاء write(2)، يمكن أن تقع الأخطاء التالية عند الكتابة في واصف ملف fanotify:

أذونات الوصول لـ Fanotify غير مفعّلة في ضبط النواة أو أن قيمة response في بنية الاستجابة غير صالحة.
واصف الملف fd في بنية الاستجابة غير صالح. قد يحدث هذا عندما تكون استجابة حدث الإذن قد كُتبت بالفعل.

المعايير

لينكس.

التاريخ

قُدمت واجهة برمجة تطبيقات fanotify في لينكس 2.6.36 وفُعّلت في لينكس 2.6.37. أُضيف دعم fdinfo في لينكس 3.8.

ملاحظات

واجهة برمجة تطبيقات fanotify متاحة فقط إذا بُنيت النواة مع تفعيل خيار الضبط CONFIG_FANOTIFY. بالإضافة إلى ذلك، فإن معالجة أذونات fanotify متاحة فقط إذا فُعّل خيار الضبط CONFIG_FANOTIFY_ACCESS_PERMISSIONS.

القيود والتحذيرات

يبلغ fanotify فقط عن الأحداث التي يطلقها برنامج في مساحة المستخدم عبر واجهة برمجة تطبيقات نظام الملفات. ونتيجة لذلك، فإنه لا يلتقط الأحداث البعيدة التي تقع في أنظمة الملفات الشبكية.

لا تبلغ واجهة برمجة تطبيقات fanotify عن عمليات الوصول إلى الملفات وتعديلها التي قد تحدث بسبب mmap(2) و msync(2) و munmap(2).

تُنشأ أحداث الأدلة فقط إذا فُتح الدليل نفسه وقُرئ وأُغلق. إضافة أو إزالة أو تغيير أبناء دليل موسوم لا يُنشئ أحداثًا للدليل المراقب نفسه.

مراقبة fanotify للأدلة ليست عودية: لمراقبة الأدلة الفرعية تحت دليل ما، يجب إنشاء وسوم إضافية. يمكن استخدام حدث FAN_CREATE لاكتشاف وقت إنشاء دليل فرعي تحت دليل موسوم. يجب بعد ذلك وضع وسم إضافي على الدليل الفرعي المنشأ حديثًا. هذا النهج عرضة لظروف السباق (racy)، لأنه قد يفقد الأحداث التي وقعت داخل الدليل الفرعي المنشأ حديثًا قبل إضافة وسم عليه. توفر مراقبة نقاط الوصل القدرة على مراقبة شجرة أدلة كاملة بطريقة خالية من السباق. توفر مراقبة أنظمة الملفات القدرة على مراقبة التغييرات التي تجري من أي وصل لمثيل نظام ملفات بطريقة خالية من السباق.

قد يفيض طابور الأحداث. في هذه الحالة، تُفقد الأحداث.

العلل

قبل لينكس 3.19، لم يكن fallocate(2) يولد أحداث fanotify. منذ لينكس 3.19، تولد استدعاءات fallocate(2) أحداث FAN_MODIFY.

بدءًا من لينكس 3.17، توجد العلل التالية:

في لينكس، يمكن الوصول إلى كائن نظام ملفات عبر مسارات متعددة، على سبيل المثال، قد يُعاد وصل جزء من نظام ملفات باستخدام خيار --bind للأمر mount(8). المستمع الذي وسم وصلًا سيُخطر فقط بالأحداث التي أُطلقت لكائن نظام ملفات باستخدام الوصل نفسه. أي حدث آخر سيمر دون ملاحظة.
عند توليد حدث، لا يُجرى أي فحص لمعرفة ما إذا كان معرف المستخدم للعملية المستلمة لديه تفويض للقراءة أو الكتابة في الملف قبل تمرير واصف ملف لذلك الملف. يمثل هذا خطرًا أمنيًا، عندما تُضبط إمكانية CAP_SYS_ADMIN للبرامج التي ينفذها مستخدمون غير مفوضين.
إذا عالج استدعاء read(2) أحداثًا متعددة من طابور fanotify ووقع خطأ، فستكون القيمة المرجعة هي الطول الإجمالي للأحداث التي نُسخت بنجاح إلى خبيئة مساحة المستخدم قبل وقوع الخطأ. لن تكون القيمة المرجعة -1، ولن يُضبط errno. وبالتالي، لا تملك التطبيقات القارئة أي وسيلة لاكتشاف الخطأ.

أمثلة

يوضح البرنامجان المثالان أدناه استخدام واجهة برمجة تطبيقات fanotify.

برنامج مثال: fanotify_example.c

البرنامج الأول هو مثال لاستخدام fanotify مع تمرير معلومات كائن الحدث الخاص به في شكل واصف ملف. يوسم البرنامج نقطة الوصل الممررة كمعامل في سطر الأوامر وينتظر أحداثًا من النوع FAN_OPEN_PERM و FAN_CLOSE_WRITE. عند وقوع حدث إذن، تُعطى استجابة FAN_ALLOW.

تُظهر جلسة الصدفة التالية مثالًا على تشغيل هذا البرنامج. تضمنت هذه الجلسة تحرير الملف /home/user/temp/notes. قبل أن يُفتح الملف، وقع حدث FAN_OPEN_PERM. بعد إغلاق الملف، وقع حدث FAN_CLOSE_WRITE. ينتهي تنفيذ البرنامج عندما يضغط المستخدم على مفتاح ENTER.


#./fanotify_example /home;
اضغط على مفتاح ENTER للإنهاء.
الاستماع للأحداث.
FAN_OPEN_PERM: File /home/user/temp/notes
FAN_CLOSE_WRITE: File /home/user/temp/notes
توقف الاستماع للأحداث.

مصدر البرنامج: fanotify_example.c

#define _GNU_SOURCE     /* Needed to get O_LARGEFILE definition */
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/fanotify.h>
#include <unistd.h>
/* Read all available fanotify events from the file descriptor 'fd'. */
static void
handle_events(int fd)
{

const struct fanotify_event_metadata *metadata;
struct fanotify_event_metadata buf[200];
ssize_t len;
char path[PATH_MAX];
ssize_t path_len;
char procfd_path[PATH_MAX];
struct fanotify_response response;
/* Loop while events can be read from fanotify file descriptor. */
for (;;) {
/* Read some events. */
len = read(fd, buf, sizeof(buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* Check if end of available data reached. */
if (len <= 0)
break;
/* Point to the first event in the buffer. */
metadata = buf;
/* Loop over all events in the buffer. */
while (FAN_EVENT_OK(metadata, len)) {
/* Check that run-time and compile-time structures match. */
if (metadata->vers != FANOTIFY_METADATA_VERSION) {
fprintf(stderr,
"Mismatch of fanotify metadata version.\n");
exit(EXIT_FAILURE);
}
/* metadata->fd contains either FAN_NOFD, indicating a
queue overflow, or a file descriptor (a nonnegative
integer). Here, we simply ignore queue overflow. */
if (metadata->fd >= 0) {
/* Handle open permission event. */
if (metadata->mask & FAN_OPEN_PERM) {
printf("FAN_OPEN_PERM: ");
/* Allow file to be opened. */
response.fd = metadata->fd;
response.response = FAN_ALLOW;
write(fd, &response, sizeof(response));
}
/* Handle closing of writable file event. */
if (metadata->mask & FAN_CLOSE_WRITE)
printf("FAN_CLOSE_WRITE: ");
/* Retrieve and print pathname of the accessed file. */
snprintf(procfd_path, sizeof(procfd_path),
"/proc/self/fd/%d", metadata->fd);
path_len = readlink(procfd_path, path,
sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
path[path_len] = '\0';
printf("File %s\n", path);
/* Close the file descriptor of the event. */
close(metadata->fd);
}
/* Advance to next event. */
metadata = FAN_EVENT_NEXT(metadata, len);
}
} } int main(int argc, char *argv[]) {
char buf;
int fd, poll_num;
nfds_t nfds;
struct pollfd fds[2];
/* Check mount point is supplied. */
if (argc != 2) {
fprintf(stderr, "Usage: %s MOUNT\n", argv[0]);
exit(EXIT_FAILURE);
}
printf("Press enter key to terminate.\n");
/* Create the file descriptor for accessing the fanotify API. */
fd = fanotify_init(FAN_CLOEXEC | FAN_CLASS_CONTENT | FAN_NONBLOCK,
O_RDONLY | O_LARGEFILE);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}
/* Mark the mount for:
- permission events before opening files
- notification events after closing a write-enabled
file descriptor. */
if (fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_MOUNT,
FAN_OPEN_PERM | FAN_CLOSE_WRITE, AT_FDCWD,
argv[1]) == -1) {
perror("fanotify_mark");
exit(EXIT_FAILURE);
}
/* Prepare for polling. */
nfds = 2;
fds[0].fd = STDIN_FILENO; /* Console input */
fds[0].events = POLLIN;
fds[1].fd = fd; /* Fanotify input */
fds[1].events = POLLIN;
/* This is the loop to wait for incoming events. */
printf("Listening for events.\n");
while (1) {
poll_num = poll(fds, nfds, -1);
if (poll_num == -1) {
if (errno == EINTR) /* Interrupted by a signal */
continue; /* Restart poll() */
perror("poll"); /* Unexpected error */
exit(EXIT_FAILURE);
}
if (poll_num > 0) {
if (fds[0].revents & POLLIN) {
/* Console input is available: empty stdin and quit. */
while (read(STDIN_FILENO, &buf, 1) > 0 && buf != '\n')
continue;
break;
}
if (fds[1].revents & POLLIN) {
/* Fanotify events are available. */
handle_events(fd);
}
}
}
printf("Listening for events stopped.\n");
exit(EXIT_SUCCESS); }

برنامج مثال: fanotify_fid.c

البرنامج الثاني هو مثال لاستخدام fanotify مع مجموعة تحدد الكائنات بواسطة مقابض الملفات. يوسم البرنامج كائن نظام الملفات الذي يُمرر كمعامل سطر أوامر وينتظر حتى يقع حدث من النوع FAN_CREATE. يشير قناع الحدث إلى نوع كائن نظام الملفات—إما ملف أو دليل—الذي أُنشئ. وبمجرد قراءة جميع الأحداث من الخبيئة ومعالجتها وفقًا لذلك، ينتهي البرنامج ببساطة.

تُظهر جلسات الصدفة التالية استدعاءين مختلفين لهذا البرنامج، مع تنفيذ إجراءات مختلفة على كائن مراقب.

تُظهر الجلسة الأولى وضع وسم على /home/user. ويتبع ذلك إنشاء ملف عادي، /home/user/testfile.txt. ينتج عن هذا توليد حدث FAN_CREATE والإبلاغ عنه مقابل كائن الدليل الأب المراقب للملف ومع اسم الملف المنشأ. ينتهي تنفيذ البرنامج بمجرد معالجة جميع الأحداث الملتقطة داخل الخبيئة.


# ./fanotify_fid /home/user
Listening for events.
FAN_CREATE (file created):

Directory /home/user has been modified.
Entry 'testfile.txt' is not a subdirectory. All events processed successfully. Program exiting. $ touch /home/user/testfile.txt # In another terminal

تُظهر الجلسة الثانية وضع وسم على /home/user. ويتبع ذلك إنشاء دليل، /home/user/testdir. ينتج عن هذا الإجراء المحدد توليد حدث FAN_CREATE ويُبلغ عنه مع تعيين علم FAN_ONDIR ومع اسم الدليل المنشأ.


# ./fanotify_fid /home/user
Listening for events.
FAN_CREATE | FAN_ONDIR (subdirectory created):

Directory /home/user has been modified.
Entry 'testdir' is a subdirectory. All events processed successfully. Program exiting. $ mkdir -p /home/user/testdir # In another terminal

مصدر البرنامج: fanotify_fid.c

#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fanotify.h>
#include <unistd.h>
#define BUF_SIZE 256
int
main(int argc, char *argv[])
{

int fd, ret, event_fd, mount_fd;
ssize_t len, path_len;
char path[PATH_MAX];
char procfd_path[PATH_MAX];
char events_buf[BUF_SIZE];
struct file_handle *file_handle;
struct fanotify_event_metadata *metadata;
struct fanotify_event_info_fid *fid;
const char *file_name;
struct stat sb;
if (argc != 2) {
fprintf(stderr, "Invalid number of command line arguments.\n");
exit(EXIT_FAILURE);
}
mount_fd = open(argv[1], O_DIRECTORY | O_RDONLY);
if (mount_fd == -1) {
perror(argv[1]);
exit(EXIT_FAILURE);
}
/* Create an fanotify file descriptor with FAN_REPORT_DFID_NAME as
a flag so that program can receive fid events with directory
entry name. */
fd = fanotify_init(FAN_CLASS_NOTIF | FAN_REPORT_DFID_NAME, 0);
if (fd == -1) {
perror("fanotify_init");
exit(EXIT_FAILURE);
}
/* Place a mark on the filesystem object supplied in argv[1]. */
ret = fanotify_mark(fd, FAN_MARK_ADD | FAN_MARK_ONLYDIR,
FAN_CREATE | FAN_ONDIR,
AT_FDCWD, argv[1]);
if (ret == -1) {
perror("fanotify_mark");
exit(EXIT_FAILURE);
}
printf("Listening for events.\n");
/* Read events from the event queue into a buffer. */
len = read(fd, events_buf, sizeof(events_buf));
if (len == -1 && errno != EAGAIN) {
perror("read");
exit(EXIT_FAILURE);
}
/* Process all events within the buffer. */
for (metadata = (struct fanotify_event_metadata *) events_buf;
FAN_EVENT_OK(metadata, len);
metadata = FAN_EVENT_NEXT(metadata, len)) {
fid = (struct fanotify_event_info_fid *) (metadata + 1);
file_handle = (struct file_handle *) fid->handle;
/* Ensure that the event info is of the correct type. */
if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_FID ||
fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID) {
file_name = NULL;
} else if (fid->hdr.info_type == FAN_EVENT_INFO_TYPE_DFID_NAME) {
file_name = file_handle->f_handle +
file_handle->handle_bytes;
} else {
fprintf(stderr, "Received unexpected event info type.\n");
exit(EXIT_FAILURE);
}
if (metadata->mask == FAN_CREATE)
printf("FAN_CREATE (file created):\n");
if (metadata->mask == (FAN_CREATE | FAN_ONDIR))
printf("FAN_CREATE | FAN_ONDIR (subdirectory created):\n"); /* metadata->fd is set to FAN_NOFD when the group identifies objects by file handles. To obtain a file descriptor for the file object corresponding to an event you can use the struct file_handle that's provided within the fanotify_event_info_fid in conjunction with the open_by_handle_at(2) system call. A check for ESTALE is done to accommodate for the situation where the file handle for the object was deleted prior to this system call. */
event_fd = open_by_handle_at(mount_fd, file_handle, O_RDONLY);
if (event_fd == -1) {
if (errno == ESTALE) {
printf("File handle is no longer valid. "
"File has been deleted\n");
continue;
} else {
perror("open_by_handle_at");
exit(EXIT_FAILURE);
}
}
snprintf(procfd_path, sizeof(procfd_path), "/proc/self/fd/%d",
event_fd);
/* Retrieve and print the path of the modified dentry. */
path_len = readlink(procfd_path, path, sizeof(path) - 1);
if (path_len == -1) {
perror("readlink");
exit(EXIT_FAILURE);
}
path[path_len] = '\0';
printf("\tDirectory '%s' has been modified.\n", path);
if (file_name) {
ret = fstatat(event_fd, file_name, &sb, 0);
if (ret == -1) {
if (errno != ENOENT) {
perror("fstatat");
exit(EXIT_FAILURE);
}
printf("\tEntry '%s' does not exist.\n", file_name);
} else if ((sb.st_mode & S_IFMT) == S_IFDIR) {
printf("\tEntry '%s' is a subdirectory.\n", file_name);
} else {
printf("\tEntry '%s' is not a subdirectory.\n",
file_name);
}
}
/* Close associated file descriptor for this event. */
close(event_fd);
}
printf("All events processed successfully. Program exiting.\n");
exit(EXIT_SUCCESS); }

انظر أيضًا

fanotify_init(2)، fanotify_mark(2)، inotify(7)

ترجمة

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

هذه الترجمة هي وثيقة مجانية؛ راجع رخصة جنو العامة الإصدار 3 أو ما بعده للاطلاع على شروط حقوق النشر. لا توجد أي ضمانات.

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

15 يونيو 2024 صفحات دليل لينكس 6.9.1