5

如果我创建一个文件,用于lseek(2)跳转到(空)文件中的较高位置,然后在那里写一些有价值的信息,我会在 Unix 系统上创建一个稀疏文件(可能取决于我使用的文件系统,但假设我' m 使用典型的 Unix 文件系统,如 ext4 或类似文件,就是这种情况)。

如果我然后lseek(2)到文件中更高的位置,也在那里写一些东西,我最终会得到一个稀疏文件,其中包含有价值的信息,其中包含大量的稀疏文件。我想在文件中找到这些有价值的信息,而不必完全阅读它。

例子:

$ python
f = open('sparse', 'w')
f.seek((1<<40) + 42)
f.write('foo')
f.seek((1<<40) * 2)
f.write('\0')
f.close()

这将创建一个仅使用 8k 磁盘空间的 2TB 文件:

$ du -h sparse 
8.0K    sparse

它中间的某个地方(1TB + 42 字节)是有价值的信息(foo)。

我当然可以找到它cat sparse,但这会读取完整的文件并打印大量的零字节。我尝试使用较小的尺寸,发现这种方法在我的计算机上打印三个字符大约需要 3 小时。

问题是:

有没有办法在不读取所有空块的情况下找到存储在稀疏文件中的信息?我可以使用标准 Unix 方法以某种方式找出空块在稀疏文件中的位置吗?

4

1 回答 1

1

只是根据之前的评论写一个答案:

#!/usr/bin/env python3
from errno import ENXIO
from os import lseek
from sys import argv, stderr

SEEK_DATA = 3
SEEK_HOLE = 4

def get_ranges(fobj):
    ranges = []
    end = 0

    while True:
        try:
            start = lseek(fobj.fileno(), end, SEEK_DATA)
            end = lseek(fobj.fileno(), start, SEEK_HOLE)
            ranges.append((start, end))
        except OSError as e:
            if e.errno == ENXIO:
                return ranges

            raise

def main():
    if len(argv) < 2:
        print('Usage: %s <sparse_file>' % argv[0], file=stderr)
        raise SystemExit(1)

    try:
        with open(argv[1], 'rb') as f:
            ranges = get_ranges(f)
            for start, end in ranges:
                print('[%d:%d]' % (start, end))
                size = end-start
                length = min(20, size)
                f.seek(start)
                data = f.read(length)
                print(data)
    except OSError as e:
        print('Error:', e)
        raise SystemExit(1)

if __name__ == '__main__': main()

但是,它可能不会执行您想要的操作,它会准确返回您编写的数据。零可能围绕返回的数据,并且必须手动修剪。

在https://man7.org/linux/man-pages/man2/lseek.2.html中描述了 SEEK_DATA 和 SEEK_HOLE 的当前状态:

SEEK_DATA 和 SEEK_HOLE 是非标准扩展,也出现在 Solaris、FreeBSD 和 DragonFly BSD 中;建议将它们包含在下一个 POSIX 修订版(第 8 期)中。

于 2020-06-10T20:04:16.520 回答