عند تطوير مشروع ، أصبح من الضروري حذف الملفات التي أنشأها التطبيق أثناء تنفيذه. ولكن كان مطلوبًا عدم حذف الملفات في نهاية الجلسة ، وعمل جهاز الكمبيوتر ، ولكن بناءً على طلب المستخدم.
ويبدو أنه لا توجد مشكلة. تحتوي مكتبة Java القياسية على طريقة delete () في فئة java.io.File لحذف ملف.
File file = new File("path/to/file"); if (file.delete()) { System.out.println(file.getName() + " deleted"); } else { System.out.println(file.getName() + " not deleted"); }
تستدعي طريقة delete () في فئة java.io.File تحت الغطاء وظيفة أصلية لحذف ملف ، اعتمادًا على نظام التشغيل. وأنظمة التشغيل الحديثة لا تحذف الملف فورًا عند حذف ملف ، ولكن تحذف فقط اسم الملف. تبقى محتويات الملف ، ويمكن إعادة استخدام الذاكرة التي يشغلها الملف المحذوف بالفعل في المستقبل. ولكن على الرغم من ذلك ، يبدو أن الملف المحذوف متاح بالفعل لبعض الوقت.
وإذا نظرت إلى الإنترنت ، فهناك العديد من البرامج لاستعادة الملفات المحذوفة ، على سبيل المثال
Recuva .
لكنني أريد حذف الملفات دون إمكانية استعادتها في المستقبل. بدء البحث في الإنترنت ، اتضح أن حذف الملف دون استعادة (الكتابة) ليست مهمة تافهة للغاية. وعند تنفيذ هذه المهمة ، يجب مراعاة ميزات العمل مع الملفات في نظام تشغيل معين. وفي هذه الحالة ، تحتاج إلى استخدام واجهة برمجة تطبيقات أصلية مكتوبة يدويًا أو نوعًا من المكتبة المحلية.
نظرًا لأنه تم تطوير التطبيق في Ubuntu ، يوفر نظام التشغيل هذا العديد من الحلول الجاهزة في شكل أدوات مساعدة لسطر الأوامر. على سبيل المثال ، الأداة المساعدة للحذف الآمن ، والتي تسمح لك بحذف الملفات دون الاسترداد باستخدام طرق مختلفة.
$ srm -vz private/*
يجب أن تحقق التطبيقات ما إذا كانت الأداة المساعدة مثبتة وتعرض خطأ إذا لم تعثر عليها. ولكن في حالة استخدام التطبيق لنظام تشغيل آخر ، تحتاج إلى استخدام أداة مساعدة مماثلة.
وهي ليست مريحة للغاية وأريد الابتعاد عن هذه المشاكل. إذا نظرت إلى الكود المصدري للأداة المساعدة للحذف الآمن ، فإنه يسمح لك بالعمل تحت أنظمة تشغيل مختلفة. هو مكتوب في C99 ويستخدم سحر المعالجة المسبقة المختلفة وواجهة برمجة التطبيقات الخاصة بالنظام الأساسي. من الصعب للغاية تصحيح هذه التعليمات البرمجية الأصلية في حالة حدوث خطأ وهذه هي المهمة.
إذا فهمت كيفية عمل الأداة المساعدة للحذف الآمن ، فيمكن تمييز الخطوات التالية.
- أولاً ، يتحقق من وجود الملف وأن الحقوق صحيحة.
- اعتمادًا على الخوارزمية المحددة ، تقوم بالكتابة فوق محتويات الملف.
- يقلل من حجم الملف إلى صفر بايت.
- إعادة تسمية الملف تسلسل عشوائي من الأحرف.
- يحذف الملف.
يسمح الحذف الآمن للخوارزميات المختلفة بالكتابة فوق محتويات الملف:
- خوارزمية بسيطة - الكتابة فوق 1 تمرير 0x00 بايت.
- خوارزمية DOE - الكتابة فوق 3 تمريرات عشوائية ، عشوائية ، "DoE".
- خوارزمية RCMP - الكتابة فوق مع 3 تمريرات 0x00 ، 0xFF ، "RCMP".
- OPENBSD خوارزمية - الكتابة فوق مع 3 يمر 0xFF ، 0x00 ، 0xFF بايت.
- خوارزمية DOD - الكتابة فوق 7 تمريرات.
- جوتمان خوارزمية - الكتابة فوق 35 تمريرة.
أرغب في أن يكون الكود مستقلًا عن النظام الأساسي وأن يعمل في ظل أنظمة تشغيل مختلفة. إذا نظرت إلى الإصدار C ++ الحديث ، فيمكنك تنفيذ جميع الخطوات التي يقوم بها الحذف الآمن بالكتابة فوق الملفات.
من أجل التحقق من وجود ملف وما إذا كان لديه الحقوق الصحيحة ، يمكنك استخدام std :: filesystem ، الذي تمت إضافته في C ++ 17.
بالنسبة للإصدارات السابقة من المعيار ، يمكن استخدام boost :: filesystem.
namespace fs = std::filesystem; if (!fs::exists(file)) { env->ThrowNew(exception_class, "File doesn't exist"); } if (!fs::is_regular_file(file)) { env->ThrowNew(exception_class, "Path doesn't regular file or symlink"); } if (!eraser.check_permision(file, fs::status(file).permissions())) { env->ThrowNew(exception_class, "File hasn't enough permision (maybe not user)"); } bool kl::erase_content::check_permision(const fs::path& entry, fs::perms permision) { try { fs::permissions(entry, fs::perms::owner_read | fs::perms::owner_write, fs::perm_options::add); return true; } catch (fs::filesystem_error& e) { return false; } }
لاستبدال محتويات الملف ، اعتمادًا على الخوارزمية المختارة ، يمكنك ترك التطبيق كما هو في الحذف الآمن.
bool kl::erase_content::overwrite() { switch (entry.get_mode()) { case kl::overwrite_mode::SIMPLE_MODE: if (!overwrite_byte(1, 0x00)) { return false; } break; case kl::overwrite_mode::DOE_MODE: if (!overwrite_random(1)) { return false; } if (!overwrite_random(2)) { return false; } if (!overwrite_bytes(3, "DoE")) { return false; } break; case kl::overwrite_mode::OPENBSD_MODE: break; case kl::overwrite_mode::RCMP_MODE: break; case kl::overwrite_mode::DOD_MODE: break; case kl::overwrite_mode::GUTMAN_MODE: break; default: std::cerr << "overwrite mode doesn't choose" << std::endl; } return true; }
يتم تعبئة مخزن مؤقت بحجم معين بمجموعة بيانات معينة ، اعتمادًا على الخوارزمية ، ويكتب هذا المخزن المؤقت في ملف حتى يصل إلى النهاية.
bool kl::erase_content::overwrite_byte(const int pass, const uint8_t byte) { const auto& [file_name, file_size, buffer_size, mode] = entry; this->buffer = std::make_unique<uint8_t[]>(buffer_size); std::memset(buffer.get(), byte, buffer_size); this->file = kl::fs_util::make_open_file(file_name, "r+b"); if (!overwrite_data(pass)) { return false; } return true; } bool kl::erase_content::overwrite_data(const int pass) { const auto& [file_name, file_size, buffer_size, mode] = entry; const size_t count = file_size / buffer_size; const size_t tail = file_size % buffer_size; size_t writted = 0; if (fseek(file.get(), 0, SEEK_SET) != 0) { std::cerr << "couldn't seek in file" << std::endl; return false; } writted = write_buffer(count, tail); if (writted != file_size) { std::cerr << "couldn't write buffer in file" << std::endl; return false; } fflush(file.get()); if (fseek(file.get(), 0, SEEK_SET) != 0) { std::cerr << "couldn't seek in file" << std::endl; return false; } file.reset(); return true; }
ثم قم بتقليل حجم الملف إلى صفر بايت ، باستخدام الدالة std :: filesystem :: resize_file () لهذا الغرض.
try { fs::resize_file(file, 0); } catch (fs::filesystem_error& e) { env->ThrowNew(exception_class, "truncate file fail"); }
تتمثل الخطوة التالية في إعادة تسمية الملف بتسلسل عشوائي للأحرف ، باستخدام std :: random () و std :: filesystem :: file :: replace_filename () لهذا الغرض.
std::string parent_path = file.parent_path(); std::string file_name = file.filename(); fs::path copy_file = file; file_name = random_text(file_name.size()); copy_file.replace_filename(fs::path(file_name)); try { fs::rename(file, copy_file); } catch (fs::filesystem_error& e) { env->ThrowNew(exception_class, "can't rename file"); } return true;
وفي المرحلة النهائية ، تحتاج فقط إلى حذف الملف باستخدام std :: filesystem :: remove () لهذا الغرض.
try { fs::remove(copy_file); } catch (fs::filesystem_error& e) { env->ThrowNew(exception_class, "can't remove file"); }
حسنًا ، لاستخدامها في Java ، يلزمك الإعلان عن الأساليب الأصلية.
public class EraseFS { static { System.loadLibrary("jefl"); } public static native boolean eraseFile(String path) throws EraseException; public static native boolean eraseFile(String path, OverwrideMode mode) throws EraseException; public static native boolean eraseFiles(String... paths) throws EraseException; public static native boolean eraseFiles(OverwrideMode mode, String... paths) throws EraseException; public static native boolean eraseDirectory(String path, boolean recursived) throws EraseException; public static native boolean eraseDirectory(String path, OverwrideMode mode, boolean recursived) throws EraseException; }
يستخدم التطبيق الأصلي مكتبة C ++ القياسية فقط ، مما يسهل النقل إلى الأنظمة الأساسية الأخرى. والأهم من ذلك ، لا يوجد سحر الماكرو preprocessor ، والذي ليس من السهل تصحيحه في حالة وجود أخطاء.
يدعم المعيار C ++ 17 بالفعل جميع المترجمين المعروفين: MVSC ، Clang ، GCC.
يمكن الاطلاع على شفرة المصدر الكاملة على github:
code .