开发项目时,有必要删除应用程序在执行过程中创建的文件。 但是要求不要在会话结束(PC的工作)时删除文件,而是应用户的要求删除文件。
而且似乎没有问题。 Java标准库在java.io.File类中具有delete()方法以删除文件。
File file = new File("path/to/file"); if (file.delete()) { System.out.println(file.getName() + " deleted"); } else { System.out.println(file.getName() + " not deleted"); }
java.io.File类中的delete()方法在后台调用一个本机函数来删除文件,具体取决于操作系统。 并且现代操作系统在删除文件时不会立即删除文件,而只会删除文件名。 该文件的内容将保留,并且已删除的文件所占用的内存将来可能会被重用。 但是都一样,一段时间后似乎已经删除的文件可用。
而且,如果您查看Internet,则有许多用于恢复已删除文件的程序,例如
Recuva 。
但是我希望删除文件,而以后再也无法恢复它们。 开始搜索Internet时,事实证明删除文件而不恢复(覆盖)不是一件容易的事。 并且在执行此类任务时,需要考虑在特定OS中处理文件的功能。 在这种情况下,您需要使用手动编写的本机API或某种本机库。
由于该应用程序是在Ubuntu中开发的,因此该OS以命令行实用程序的形式提供了许多现成的解决方案。 例如,secure-delete实用程序,它使您可以删除文件而无需使用其他方法进行恢复。
$ srm -vz private/*
应用程序应检查该实用程序是否已安装,如果找不到该实用程序,则会显示错误。 但是,如果将应用程序用于其他操作系统,则需要使用类似的实用程序。
这不是很方便,我想摆脱这些问题。 如果您查看安全删除实用程序的源代码,那么它使您可以在不同的操作系统下工作。 它是用C99编写的,并使用了不同的预处理魔术和特定于平台的API。 发生错误时调试此类本机代码非常困难,这就是任务。
如果您了解安全删除实用程序的工作原理,则可以区分以下步骤。
- 首先,它检查文件是否存在以及权限是否正确。
- 根据指定的算法,覆盖文件的内容。
- 将文件大小减小到零字节。
- 重命名文件一个随机的字符序列。
- 删除文件。
secure-delete允许不同的算法覆盖文件的内容:
- 简单算法-用1遍0x00字节覆盖。
- DOE算法-用3个随机,随机的“ DoE”覆盖。
- RCMP算法-用3次遍历0x00、0xFF,“ RCMP”覆盖。
- OPENBSD算法-使用3个通道覆盖0xFF,0x00、0xFF字节。
- DOD算法-覆盖7次传递。
- Gutmann算法-覆盖35次传递。
我希望代码独立于平台并在不同的操作系统下工作。 如果您查看现代C ++,那么可以完成安全删除覆盖文件的所有步骤。
为了检查文件是否存在以及它是否具有正确的权限,可以使用std :: filesystem,它是C ++ 17中添加的。
对于该标准的早期版本,可以使用boost ::文件系统。
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 ++库,因此可以轻松移植到其他平台。 最重要的是,没有预处理器宏魔术,在发生错误的情况下调试起来并不容易。
所有流行的编译器均已支持标准C ++ 17:MVSC,Clang和GCC。
完整的源代码可以在github:
code上查看。