如何创建一个带有文件空洞的文件?

22

文件空洞是文件中的空白区域,它不占用任何磁盘空间且包含 null 字节。因此,文件大小会比实际磁盘上的大小要大。

但我不知道如何创建带有文件空洞的文件以进行实验。


@Zeke:从表面上看是的。但有些文件系统包括一种优化方式,可以存储具有大量连续空字节的文件,并避免物理上存储所有这些零。不要问我详细原理,我猜这可能被压缩在某些文件属性中。 - user395760
你的意思是相反的吗?一个文件在磁盘上的实际大小比它被最优保存时的大小要大? - ypercubeᵀᴹ
如果是这种情况,那么你无法创建稀疏文件。稀疏文件是根据文件中的数据由操作系统创建的。 - Zeke Hansell
2
@Zeke Hansell: 你错了。有一种方法可以创建这样的文件。阅读回答就行 :-) - Omnifarious
1
@Omni - 理解了。我目前面前没有Unix系统。我能做到的最好的就是一个糟糕的模仿 ;-) - Zeke Hansell
显示剩余4条评论
5个回答

42
使用带有“seek”参数的“dd”命令。
dd if=/dev/urandom bs=4096 count=2 of=file_with_holes
dd if=/dev/urandom bs=4096 seek=7 count=2 of=file_with_holes

这将为您创建一个文件,从字节8192到字节28671之间有一个漂亮的空洞。

下面是一个示例,演示该文件确实存在空洞(ls -s命令告诉您一个文件使用了多少磁盘块):

$ dd if=/dev/urandom bs=4096 count=2 of=fwh # fwh = file with holes
2+0 records in
2+0 records out
8192 bytes (8.2 kB) copied, 0.00195565 s, 4.2 MB/s

$ dd if=/dev/urandom seek=7 bs=4096 count=2 of=fwh
2+0 records in
2+0 records out
8192 bytes (8.2 kB) copied, 0.00152742 s, 5.4 MB/s

$ dd if=/dev/zero bs=4096 count=9 of=fwnh # fwnh = file with no holes
9+0 records in
9+0 records out
36864 bytes (37 kB) copied, 0.000510568 s, 72.2 MB/s

$ ls -ls fw*
16 -rw-rw-r-- 1 hopper hopper 36864 Mar 15 10:25 fwh
36 -rw-rw-r-- 1 hopper hopper 36864 Mar 15 10:29 fwnh

正如您所看到的,带有空洞的文件占用的磁盘块较少,尽管它们的大小相同。

如果您需要这样的程序,这里是:

#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>

int main(int argc, const char *argv[])
{
    char random_garbage[8192]; /* Don't even bother to initialize */
    int fd = -1;
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <filename>\n", argv[0]);
        return 1;
    }
    fd = open(argv[1], O_WRONLY | O_CREAT | O_TRUNC, 0666);
    if (fd < 0) {
        perror("Can't open file: ");
        return 2;
    }
    write(fd, random_garbage, 8192);
    lseek(fd, 5 * 4096, SEEK_CUR);
    write(fd, random_garbage, 8192);
    close(fd);
    return 0;
}

以上内容适用于任何Unix系统。有人回复了一个非常特定于Linux的很好的替代方法。我在这里强调一下,因为它是与我提供的两种方法不同的方法,并且可以用于在现有文件中放置空洞。


3
有人对这个进行了“downvote”(踩/反对),我不知道为什么。也许是因为未初始化的内存通常是一件很愚蠢的事情,所以留下了不好的印象。请注意,“downvote”既可以表示“踩”也可以表示“反对”,具体含义需要根据上下文确定。 - Omnifarious

8
  1. 创建一个文件。
  2. 查找到位置N
  3. 写入一些数据。

文件开头(直到排除位置N)将会有一个空洞。类似地,您可以创建中间带有空洞的文件。

以下文档包含一些示例C代码(搜索“Sparse files”):http://www.win.tue.nl/~aeb/linux/lk/lk-6.html


谢谢提供链接。它不仅对这个问题有帮助。 - Amumu
我曾经看到一种避免第三步并创建100%稀疏文件的方法,但我不记得如何完成 :-( - Martin Scharrer
1
找到了:第3步可以被 ftruncate(fileno(outfile), ftell(outfile)); 替换,即在执行 fseek 后截断文件至其当前大小。这将允许文件末尾稀疏。 - Martin Scharrer

8

3

该问题在W.Richard Stevens的著名书籍《UNIX环境高级编程》(简称APUE)的第3.6节中进行了详细讨论。此处使用unistd.h中包含的lseek函数,该函数旨在显式设置已打开文件的偏移量。 lseek函数的原型如下:

off_t lseek(int filedes, off_t offset, int whence);

这里,filedes是文件描述符,offset是我们想要设置的值,whence是在头文件中设置的常量,具体来说是SEEK_SET,意味着偏移量从文件开头开始设置;SEEK_CUR,意味着偏移量设置为当前值加上参数列表中的偏移量;SEEK_END,意味着文件的偏移量设置为文件大小加上参数列表中的偏移量。

在类UNIX操作系统下创建带有空洞的C文件的示例如下:

/*Creating a file with a hole of size 810*/
#include <fcntl.h>

/*Two strings to write to the file*/    
char buf1[] = "abcde";
char buf2[] = "ABCDE";

int main()
{
    int fd; /*file descriptor*/

    if((fd = creat("file_with_hole", FILE_MODE)) < 0)
        err_sys("creat error");
    if(write(fd, buf1, 5) != 5)
        err_sys("buf1 write error");
    /*offset now 5*/

    if(lseek(fd, 815, SEEK_SET) == -1)
        err_sys("lseek error");
    /*offset now 815*/

    if(write(fd, buf2, 5) !=5)
        err_sys("buf2 write error");
    /*offset now 820*/

    return 0;
}

在上面的代码中,err_sys是用于处理与系统调用相关的致命错误的函数。

0
当数据写入偏移量超过当前文件大小或文件大小被截断为大于当前文件大小时,就会创建一个空洞。

1
目前你的回答不够清晰,请编辑并添加更多细节,以帮助其他人理解它如何回答问题。你可以在帮助中心找到有关如何编写好答案的更多信息。 - Community

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接