用 0 重写文件。我究竟做错了什么?

Rewrite file with 0's. What am I doing wrong?

我想用 0 重写文件。它只写几个字节。

我的代码:

int fileSize = boost::filesystem::file_size(filePath);

int zeros[fileSize] = { 0 };

boost::filesystem::path rewriteFilePath{filePath};
boost::filesystem::ofstream rewriteFile{rewriteFilePath, std::ios::trunc};
rewriteFile << zeros;

另外...这足以粉碎文件吗?接下来我应该怎么做才能使文件无法恢复?

编辑: 好的。我为此重写了我的代码。此代码可以执行此操作吗?

int fileSize = boost::filesystem::file_size(filePath);

boost::filesystem::path rewriteFilePath{filePath};
boost::filesystem::ofstream rewriteFile{rewriteFilePath, std::ios::trunc};

for(int i = 0; i < fileSize; i++) {
    rewriteFile << 0;
}

您的代码有几个问题。

  1. int zeros[fileSize] = { 0 };

    您正在创建一个大小为 sizeof(int) * fileSize 字节的数组。对于您正在尝试的操作,您需要一个大小为 fileSize 字节的数组。所以你需要使用 1 字节的数据类型,比如 (unsigned) charuint8_t.

    但是,更重要的是,由于 fileSize 的值直到运行时才知道,因此这种类型的数组被称为 "Variable Length Array" (VLA),它是 C++ 中的非标准 特性。如果您需要动态分配的数组,请改用 std::vector

  2. boost::filesystem::ofstream rewriteFile{rewriteFilePath, std::ios::trunc};

    trunc 标志将现有文件的大小截断为 0。这需要更新文件的元数据以重置其跟踪的字节大小,并将文件使用的所有磁盘扇区标记为可用重复使用。存储在这些扇区中的实际文件字节不会被擦除,直到随着时间的推移被重用而被覆盖。但是您随后写入截断文件的任何字节都不能保证(并且可能不会)覆盖磁盘上的旧字节。所以,根本不要截断文件。

  3. rewriteFile << zeros;

    ofstream 没有采用 int[] 甚至 int* 作为输入的 operator<<。但它确实有一个 operator<<void* 作为输入(输出所指向的内存地址的值)。数组衰减为指向第一个元素的指针,并且 void* 接受 any 指针。这就是为什么只有几个字节被写入的原因。您需要使用 ofstream::write() 而不是将数组写入文件,并确保使用 binary 标志打开文件。

试试这个:

int fileSize = boost::filesystem::file_size(filePath);

std::vector<char> zeros(fileSize, 0);

boost::filesystem::path rewriteFilePath(filePath);
boost::filesystem::ofstream rewriteFile(rewriteFilePath, std::ios::binary);
rewriteFile.write(zeros.data()/*&zeros[0]*/, fileSize);

也就是说,您根本不需要动态分配的数组,更不用说分配给整个文件大小的数组了。那只是堆内存的浪费,尤其是对于大文件。您可以这样做:

int fileSize = boost::filesystem::file_size(filePath);

const char zeros[1024] = {0}; // adjust size as desired...

boost::filesystem::path rewriteFilePath(filePath);
boost::filesystem::ofstream rewriteFile(rewriteFilePath, std::ios::binary);

int loops = fileSize / sizeof(zeros);
for(int i = 0; i < loops; ++i) {
    rewriteFile.write(zeros, sizeof(zeros));
}
rewriteFile.write(zeros, fileSize % sizeof(zeros));

或者,如果您打开文件的内存映射视图(Windows上的MapViewOfFile()、Linux上的mmap()等),那么您可以简单地使用std::copy()std::memset() 直接在磁盘上将整个文件的字节归零,根本不使用数组。

Also... Is this enough to shred the file?

不是,不是。在物理硬件层,仅用零覆盖文件一次仍会在磁盘扇区中留下残余信号,可以使用足够的工具恢复。您应该多次覆盖该文件,使用不同类型的随机数据,而不仅仅是零。这将更彻底地扰乱扇区中的信号。

纯属娱乐,用随机数据覆盖:

Live On Coliru

#include <boost/iostreams/device/mapped_file.hpp>
#include <random>
namespace bio = boost::iostreams;

int main() {
    bio::mapped_file dst("main.cpp");

    std::mt19937 rng { std::random_device{} () };
    std::uniform_int_distribution<char> dist;

    std::generate_n(dst.data(), dst.size(), [&] { return dist(rng); });
}

注意编译后打乱了自己的源文件:)

我怎么强调覆盖文件内容并不能保证任何原始数据都被覆盖的评论的重要性再怎么强调也不为过。因此,此问题的所有其他答案都与任何最新的操作系统无关。

现代文件系统是基于扩展的,这意味着文件存储为分配块的链表。更新一个 chunk 可能会更快,因为文件系统会写一个全新的 chunk 并简单地调整链表,所以他们就是这样做的。实际上,写时复制文件系统总是 写入任何修改后的块的副本并更新其当前有效范围的 B 树。

此外,即使您的文件系统不这样做,您的硬盘驱动器也可能使用完全相同的技术来提高性能,并且由于闪存的工作原理,任何 SSD 几乎肯定总是使用这种技术。所以将数据覆盖到 "erase" 在现代系统上是没有意义的。做不到。隐藏旧数据的唯一安全方法是全盘加密。您在欺骗自己和您的用户的任何其他事情。