SSD Nodes Learn 🎉 VPS من $5.50/شهر
الأدلة Matt Connorبقلم Matt Connor

استرداد ملفات حُذفت باستخدام rm -rf على ext4

شغّلت rm -rf على المسار الخطأ؟ أوقف الكتابة فوراً، ثم اتبع خيارات الاسترداد المتاحة فعلاً على ext4، بدءاً من إعادة التركيب للقراءة فقط.

ما يجب فعله خلال أول ستين ثانية

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

لا يمسح rm أي شيء. بل يزيل إدخال الدليل، ثم يعلّم inode وكتل بيانات الملف على أنها حرة. تظل البايتات موجودة على الجهاز. وتبقى كذلك إلى أن يسلّم مخصّص الكتل تلك الكتل إلى شيء آخر، فيكتب ذلك الشيء فوقها. في كل ثانية يظل فيها نظام الملفات مركّباً ونشطاً، قد يكتب daemon سطراً في سجل أو تفرغ قاعدة بيانات صفحة، وقد تصل أي من هاتين الكتابتين إلى الكتل التي تريد استردادها.

لذلك، يجب أن تكون الأوامر الأولى هي الأوامر التي توقف الكتابة، لا الأوامر التي تسترد الملفات.

sudo systemctl stop nginx postgresql
sudo umount /mnt/data

إذا أعاد umount الإجابة umount: /mnt/data: target is busy.، فابحث عن العملية التي تُبقي نظام الملفات مفتوحاً.

sudo fuser -vm /mnt/data
sudo lsof +D /mnt/data

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

sudo mount -o remount,ro /mnt/data

إذا كان المسار المحذوف موجوداً على نظام الملفات الجذر، فالأمر أصعب. سيفشل sudo mount -o remount,ro / عادةً مع mount: /: cannot remount /dev/vda1 read-only.، لأن العمليات قيد التشغيل تُبقي الملفات مفتوحة للكتابة، ولن تجبر النواة تلك العمليات على إغلاقها. في VPS، الحل العملي هو وضع الإنقاذ أو الاسترداد الذي يوفّره مزود الخدمة: إذ يقلع نظاماً حياً منفصلاً مع إرفاق القرص، من دون تركيبه. عندئذ تنفَّذ كل الأوامر أدناه على جهاز لا تكتب إليه أي عملية.

تنطبق قاعدة واحدة على الدليل بأكمله. لا تكتب أبداً الملفات المستردة أو صورة قرص أو أداة مثبّتة حديثاً على نظام الملفات الذي تسترد البيانات منه. أرفق وحدة تخزين ثانية، أو أرسل الناتج إلى جهاز آخر عبر SSH.

لماذا تكون استعادة الملفات بعد rm -rf على ext4 شبه مستحيلة

حدّد توقعاتك قبل تثبيت أي شيء. تحقّق من نظام الملفات الذي تتعامل معه:

lsblk -f

في ext4، وهو الإعداد الافتراضي في معظم صور VPS تقريباً، يُخزّن inode موقع بيانات الملف في شكل شجرة extents. الـextent هو سجل واحد يحدد أن الكتلة المنطقية N من هذا الملف تبدأ عند الكتلة الفعلية M وتمتد على L كتل. تحتفظ الملفات الصغيرة بما يصل إلى 4 سجلات من هذه السجلات داخل inode نفسه. أما الملفات الأكبر فتشير إلى كتل إضافية تحتوي على بقية الشجرة.

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

وهذا هو الفرق عن ext3، حيث كان inode المحذوف يحتفظ بمعلومات كافية تتيح لأداة مثل ext3grep تتبعه. ما يزال بإمكانك عرض inodes المحذوفة على ext4:

sudo debugfs -R lsdel /dev/vdb1

يفتح debugfs الجهاز للقراءة فقط ما لم تمرّر -w، لذلك يكون استخدامه آمناً على جهاز غير مركّب ولا يكلّفك شيئاً لتجربته. ستُعرض inodes. لكن العملية تنتهي عند تفريغ إحداها، لأن خريطة الكتل التي كان inode يحتفظ بها قد مُسحت، ولذلك لا يملك dump ما يتبعه.

تحاول أداتان تجاوز هذه المشكلة بقراءة journal. والـjournal عبارة عن حلقة ثابتة الحجم يستخدمها ext4 للحفاظ على اتساق البيانات الوصفية بعد حدوث عطل، وقد تظل فيه نسخة أقدم من inode تعود إلى ما قبل الحذف. تبحث كل من extundelete وext4magic فيه. تحقّق من الحجم الذي تتعامل معه:

sudo dumpe2fs -h /dev/vdb1 | grep -i journal

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

إذا أبلغ lsblk -f عن xfs، فلن تكون الصورة أفضل، لأنه لا توجد أيضاً أداة مدعومة لاستعادة الملفات المحذوفة على XFS. ولا يغيّر ترتيب الخيارات أدناه شيئاً.

هل ما يزال الملف مفتوحاً في عملية قيد التشغيل؟

هذه هي عملية الاسترداد الوحيدة في هذه الصفحة ذات احتمالات نجاح جيدة، ولهذا يجب ألا تعيد تشغيل الخدمة التي كانت تستخدم الملف.

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

اعثر على الملفات المفتوحة التي انخفض عدد روابطها إلى الصفر:

sudo lsof +L1

يعني +L1 سرد الملفات المفتوحة التي يقل عدد روابطها عن 1. يعرض كل تطابق العملية، ورقم واصف الملف، وNLINK من 0، ومساراً ينتهي بـ(deleted). خذ PID ورقم الواصف إلى /proc:

sudo ls -l /proc/1234/fd

يبدو الإدخال هكذا: 3 -> /var/log/app/events.log (deleted). يظل هذا الرابط يصل إلى البيانات. انسخها إلى نظام ملفات مختلف:

sudo cp /proc/1234/fd/3 /mnt/rescue/events.log

استخدم cp، وليس mv. يؤدي فتح /proc/1234/fd/3 إلى إنشاء واصف جديد للـinode نفسه بدءاً من الإزاحة صفر، ولذلك تحصل على الملف كاملاً بدلاً من الجزء الذي يلي الموضع الحالي للكاتب.

هناك حدّان تجدر معرفتهما. لا تعود شجرة دليل محذوفة بهذه الطريقة، لأن الملفات الفردية التي كانت عملية ما قد فتحتها هي وحدها التي تظل محتفظاً بها. كما أن ملف قاعدة بيانات يُنسخ أثناء تنفيذ المحرك عملية كتابة يكون نسخة متسقة مع حالة الانهيار، ولذلك خطط لتشغيل عملية الاسترداد الخاصة بالمحرك عليه بدلاً من اعتباره ملفاً سليماً. الإدخالات التي يعرضها lsof مع mem بدلاً من رقم واصف تكون معيّنة في الذاكرة، ولا يوجد لها إدخال /proc/<pid>/fd يمكن النسخ منه.

هل لديك لقطة على btrfs أو ZFS أو LVM؟

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

يحتفظ btrfs باللقطات كوحدات فرعية:

sudo btrfs subvolume list /

تصفّح اللقطة وانسخ المسارات التي تحتاج إليها باستخدام cp -a. يُفضّل نسخ المسارات المفردة بدلاً من التراجع عن وحدة فرعية كاملة، لأن التراجع يتخلص أيضاً من كل ما كُتب منذ إنشاء اللقطة.

يعرض ZFS كل لقطة كدليل للقراءة فقط:

zfs list -t snapshot
ls /tank/data/.zfs/snapshot/

يكون الدليل .zfs مخفياً، ولن يظهر عند تنفيذ ls عادي على جذر مجموعة البيانات، لكن يمكنك الدخول إليه باستخدام اسمه. انسخ الملفات منه. يعيد zfs rollback مجموعة البيانات كاملة إلى الحالة السابقة، ويدمر كل لقطة أحدث من اللقطة التي تحددها، لذلك استخدمه كخيار أخير.

لقطات LVM هي وحدات تخزين تعمل بآلية النسخ عند الكتابة وبحجم ثابت:

sudo lvs
sudo mount -o ro /dev/vg0/data-snap /mnt/snap

صِلها للقراءة فقط وانسخ الملفات منها. تحقّق من lvs قبل الاعتماد عليها، لأن لقطة LVM التي تمتلئ مساحتها المخصصة يلغي kernel صلاحيتها، وبعد حدوث ذلك تختفي محتوياتها.

اللقطة ليست نسخة احتياطية. فهي توجد على القرص نفسه أو في pool نفسه الذي توجد عليه النسخة الأصلية، ولذلك تشاركها جميع حالات الفشل. وهي مناسبة جداً للتراجع عن خطأ حدث قبل دقيقتين، وهذا هو المطلوب هنا تحديداً.

الاستعادة باستخدام PhotoRec، على صورة القرص وليس على القرص قيد التشغيل

إذا لم ينطبق أي مما سبق، فلا يتبقى سوى الاستعادة بالنحت: فحص الجهاز الخام بحثاً عن أنماط بايتات تحدد بداية نوع ملف معروف، ثم كتابة كل ما يليها. تقرأ هذه العملية بيانات الملفات فقط. أسماء الملفات وبنية المجلدات والطوابع الزمنية والملكية كلها بيانات وصفية لنظام الملفات، وهذه هي البيانات التي أتلفها rm، لذلك لا يعود أي منها. ستحصل على ملفات باسم f0384512.jpg داخل مجلد إخراج مرقّم، وعليك فرزها يدوياً.

تحدد قاعدتان ما إذا كانت هذه العملية ستنجح أصلاً.

أولاً، أنشئ صورة للجهاز قبل توجيه أي أداة أخرى إليه. في Debian وUbuntu، الحزمة هي gddrescue، والملف التنفيذي الذي تثبته هو ddrescue.

sudo apt update && sudo apt install -y gddrescue testdisk
sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map

يجب أن يكون /mnt/rescue على جهاز مختلف، مع مساحة حرة لا تقل عن حجم القسم. يعرض lsblk -b الأحجام الدقيقة بالبايت. يتيح ملف الخريطة استئناف النسخ المتوقف بدلاً من البدء من جديد. بعد إنشاء الصورة، يمكنك تجربة أداة ثانية لاحقاً على البايتات نفسها تماماً، وهو ما لا يمكنك فعله إذا كتبت الأداة الأولى فوق القرص.

ثانياً، وجّه أداة الاستعادة إلى ملف الصورة.

sudo photorec /d /mnt/rescue/recup /mnt/rescue/vdb1.img

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

يحتوي testdisk، من الحزمة نفسها، على وظيفة خاصة به لاستعادة الملفات المحذوفة، وهو يدعم FAT وexFAT وNTFS وext2 فقط. أما في ext4، فيترك ذلك photorec.

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

المسافة الزائدة: كيف حُذف المسار الخطأ

تكون معظم حوادث rm -rf ناتجة عن مشكلة في الصدفة. يتلقى rm قائمة بالمسارات ويزيل كل مسار منها بالتتابع. ولا يعرف أبداً ما الذي قصدته.

الحالة الكلاسيكية هي وجود مسافة واحدة:

rm -rf /home/deploy/app /old
rm -rf /home/deploy/app/old

السطر الأول يتضمن وسيطين. يحذف التطبيق، ثم يحذف /old. إذا لم يكن /old موجوداً، فلا يعرض rm أي شيء على الإطلاق، لأن -f يكتم خطأ الملف المفقود. عدم ظهور رسالة لا يعني نجاح العملية.

النمط الثاني هو متغير غير موضوع بين علامتي اقتباس ويحتوي على مسافة:

dir="/srv/my app"
rm -rf $dir

تقسّم الصدفة القيمة عند المسافات البيضاء، لذلك يتلقى rm كلاً من /srv/my وapp باعتبارهما مسارين منفصلين. أما عند كتابته بالشكل rm -rf "$dir"، فيكون مساراً واحداً.

النمط الثالث هو متغير فارغ، ويحدث ذلك عادةً لأن الأمر الذي كان يفترض أن يملأه فشل:

rm -rf "$TARGET"/*

عندما يكون TARGET غير مضبوط، يتمدد ذلك إلى rm -rf /*. يرفض GNU rm الصيغة المجردة: يعرض rm -rf / الرسالة rm: it is dangerous to operate recursively on '/' ثم يتوقف. ولا يحصل نمط glob على هذه الحماية، لأن الصدفة تستبدل /* بقائمة من المسارات الحقيقية في المستوى الأعلى قبل تشغيل rm، ولا يكون / واحداً منها، ولذلك لا يُفعَّل الحارس.

عادات تمنع الحادثة التالية

  • ضع علامات اقتباس حول كل متغير يُستخدم كمسار. اكتب "$dir" في كل مرة، بما في ذلك داخل الاختبارات والحلقات.
  • افشل عند وجود قيمة فارغة. يجعل rm -rf "${TARGET:?TARGET is not set}"/* الصدفة تتوقف وتعرض رسالتك قبل بدء rm، كلما كان TARGET غير معيّن أو فارغاً. ضع set -euo pipefail في أعلى أي سكربت يحذف الملفات.
  • أضف --one-file-system. يطلب من rm تخطي أي مجلد موجود على نظام ملفات مختلف عن المسار الذي مررته إليه، لذلك لا يستطيع الحذف التكراري الدخول إلى وحدة تخزين احتياطية مركّبة أو bind mount.
  • لا تحذف باستخدام root. لا يستطيع حساب الخدمة إتلاف إلا ما يملكه، وهذه هي الحجة الأساسية لتشغيل كل خدمة باستخدام مستخدم غير مميّز خاص بها. إذا لم تكن متأكداً مما يمكن لحساب معيّن الوصول إليه، فإن قراءة بتات الصلاحيات في ناتج ls تجيب عن ذلك بأمر واحد.
  • اطبع القائمة قبل تنفيذ الإجراء عليها. في السكربت، أنشئ المسارات، ثم نفّذ printf '%s\n' عليها، واقرأ الناتج، وبعد ذلك احذفها في تمريرة ثانية.
  • أبقِ أمراً لنقل الملفات إلى سلة المهملات في متناولك. يمنحك sudo apt install trash-cli كلاً من trash-put وtrash-list وtrash-restore وtrash-empty. تُنقل الملفات المحذوفة إلى ~/.local/share/Trash، ويمسح trash-empty 30 كل ما يتجاوز عمره ثلاثين يوماً.

قد يبدو إنشاء اسم مستعار يجعل rm ينفّذ trash-put هو الخطوة التالية الواضحة، لكنه فخ. يدرّب الاسم المستعار استجابة تلقائية تفشل على الخادم التالي الذي لا يحتوي عليه، كما أن الأسماء المستعارة لا تنطبق داخل السكربتات، حيث تحدث الأخطاء المكلفة. اكتب trash-put عمداً بدلاً من ذلك.

الاسترداد الوحيد الذي يعمل في كل مرة

كل ما سبق احتمال. أما النسخة الاحتياطية فليست احتمالاً.

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

باستخدام restic، يتكون الاسترداد من أمرين.

restic -r /srv/restic-repo snapshots
restic -r /srv/restic-repo restore latest --target /mnt/rescue --include /srv/appdata

استعد الملفات إلى مجلد فارغ بدلاً من الكتابة فوق المسار الحي، حتى تتمكن من مقارنة النسختين قبل نقل أي شيء إلى موضعه. يشرح إعداد نسخ restic الاحتياطية على VPS إعداد المستودع وsystemd timer الذي يشغّله.

باستخدام Borg:

borg list /srv/borg-repo
borg extract /srv/borg-repo::daily-2026-08-08 srv/appdata

تُخزَّن المسارات داخل أرشيف Borg من دون الشرطة المائلة في بدايتها، لذلك يطابق srv/appdata، بينما لا يطابق /srv/appdata أي شيء. يكتب borg extract في مجلد العمل الحالي، لذلك نفّذ cd إلى مجلد مؤقت أولاً.

إذا لم تكن قد اخترت بينهما بعد، يشرح مقارنة restic وBorg إزالة التكرار والمستودعات التي تسمح بالإضافة فقط، وهي الخاصية التي تمنع خادماً مخترقاً من حذف سجل النسخ الاحتياطية الخاص به. كلا الأداتين مناسبتان. الإجابة الخاطئة هي ألا تعمل أي منهما.

الخادم الجديد هو أرخص وقت لإعداد ذلك، قبل أن يحتوي على أي شيء يستحق الفقدان. هذا هو موضع الدقائق العشر الأولى على VPS جديد، إلى جانب إعداد SSH والجدار الناري.

ثم أضف إدخالاً متكرراً إلى تقويمك: استعد دليلاً واحداً من المستودع إلى /tmp كل شهر واقرأ الملفات. هذه العادة وحدها تساوي أكثر من كل أداة في هذه الصفحة.

FAQ

هل يمكنني استعادة ملف محذوف على ext4؟

غالباً لا. عند زوال آخر رابط إلى الملف، يمسح ext4 شجرة الامتدادات من inode، لذلك لا يبقى على القرص ما يحدد موضع البيانات. يبحث extundelete وext4magic في journal الخاص بـext4 عن نسخة أقدم من ذلك الـinode، وهذا لا يفيد إلا إذا حدث الحذف قبل دقائق وكان نظام الملفات خاملاً منذ ذلك الحين. لا يجري صيانة أي من المشروعين حالياً. شغّل أيّاً منهما على جهاز غير mounted أو على صورة قرص، ولا تشغّله مطلقاً على نظام ملفات mounted، وتحقق أولاً مما تعمل عليه باستخدام sudo dumpe2fs -h /dev/vdb1 | grep -i journal.

ما زالت خدمة ما تفتح الملف المحذوف. هل يمكنني استعادته؟

نعم، وهذه أفضل حالة. ما دام process يحتفظ بالملف مفتوحاً، يبقى inode وكتل البيانات الخاصة بهما مخصّصين، لذلك تظل البيانات قابلة للقراءة. لا تعِد تشغيل الخدمة، لأن إغلاق آخر descriptor يُكمل عملية الحذف. شغّل sudo lsof +L1 لسرد الملفات المفتوحة التي يبلغ link count فيها 0، وسجّل PID ورقم file descriptor، ثم انسخ الملف باستخدام /proc مع sudo cp /proc/1234/fd/3 /mnt/rescue/events.log. اكتب النسخة إلى نظام ملفات مختلف. الإدخالات التي يظهر فيها mem بدلاً من رقم descriptor هي ملفات memory mapped، ولا تملك مسار /proc/<pid>/fd يمكن النسخ منه.

لماذا ينبغي إنشاء صورة للقرص بدلاً من تشغيل أداة الاسترداد عليه؟

لأن كل أداة تحتاج إلى كتابة مخرجاتها في مكان ما، وقد تؤدي الكتابة إلى نظام الملفات الذي تستعيده إلى الكتابة فوق الكتل الحرة التي ما زالت تحتوي على بياناتك. انسخ القسم إلى جهاز مختلف أولاً باستخدام sudo ddrescue -n /dev/vdb1 /mnt/rescue/vdb1.img /mnt/rescue/vdb1.map، ثم مرّر ملف الصورة إلى photorec. تتيح لك الصورة أيضاً تجربة أداة ثانية لاحقاً على البايتات نفسها تماماً، وهذا مستحيل بعد أن تكتب أي عملية فوق القرص الأصلي.

هل ما زال الأمر rm -rf / يدمّر نظام Linux؟

لا يدمّره الأمر بصورته المجردة. يرفض GNU rm تنفيذه ويطبع rm: it is dangerous to operate recursively on '/'. تكمن الصيغ الخطرة في الأوامر التي تصل إلى ذلك المسار بطريقة أخرى. عند عدم ضبط TARGET، يوسّع rm -rf "$TARGET"/* إلى rm -rf /*، ثم يمرّر shell إلى rm قائمة بالمجلدات العليا الفعلية، ولا يكون أيٌّ منها /، لذلك لا يعمل الحاجز. اكتب "${TARGET:?TARGET is not set}" بدلاً منه، وسيتوقف shell قبل تشغيل rm.

هل تُعدّ لقطة نظام الملفات نسخة احتياطية؟

لا. توجد لقطة btrfs أو ZFS في pool نفسه الذي توجد فيه البيانات التي تحميها، لذلك يؤدي تعطل القرص أو تلف pool إلى فقدانهما معاً. وتواجه لقطة LVM مشكلة إضافية تتمثل في حجم ثابت: عندما تمتلئ، يبطلها kernel وتضيع محتوياتها. تُعدّ اللقطات ممتازة للتراجع عن حذف حدث قبل دقيقتين. أما في جميع الحالات الأخرى، فاحتفظ بـrepository على أجهزة منفصلة.

#linux#rm#data-recovery#نسخ احتياطية#ext4