稀疏文件:如何查找内容
Sparse files: How to find contents
如果我创建一个文件,使用lseek(2)
跳转到(空)文件的高位,然后在那里写一些有价值的信息,我在Unix系统上创建一个稀疏文件(可能取决于我使用的文件系统,但假设我使用的是典型的 Unix 文件系统,如 ext4 或类似文件系统,就是这种情况。
如果我然后 lseek(2)
到文件中更高的位置,在那里也写一些东西,我最终会得到一个稀疏文件,它在中间某处包含有价值的信息,周围是大量的信息的稀疏文件。我想在文件中找到这些有价值的信息,而不必完全阅读它。
示例:
$ python
f = open('sparse', 'w')
f.seek((1<<40) + 42)
f.write('foo')
f.seek((1<<40) * 2)
f.write('[=10=]')
f.close()
这将创建一个仅使用 8k 磁盘的 2TB 文件space:
$ du -h sparse
8.0K sparse
中间某处(1TB + 42 字节)是有价值的信息 (foo
)。
我当然可以使用 cat sparse
找到它,但这将读取完整的文件并打印大量的零字节。我尝试使用较小的尺寸,发现这种方法大约需要 3 小时才能在我的计算机上打印出这三个字符。
问题是:
有没有办法在不读取所有空块的情况下找到存储在稀疏文件中的信息?我可以使用标准 Unix 方法以某种方式找出稀疏文件中空块的位置吗?
只是根据之前的评论写一个答案:
#!/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()
但是,它可能不会执行您想要的操作,它会准确返回您写入的数据。零可能会包围返回的数据,必须手动修剪。
SEEK_DATA 和 SEEK_HOLE 的当前状态在 https://man7.org/linux/man-pages/man2/lseek.2.html 中描述:
SEEK_DATA and SEEK_HOLE are nonstandard extensions also present in Solaris, FreeBSD, and DragonFly BSD; they are proposed for inclusion in the next POSIX revision (Issue 8).
如果我创建一个文件,使用lseek(2)
跳转到(空)文件的高位,然后在那里写一些有价值的信息,我在Unix系统上创建一个稀疏文件(可能取决于我使用的文件系统,但假设我使用的是典型的 Unix 文件系统,如 ext4 或类似文件系统,就是这种情况。
如果我然后 lseek(2)
到文件中更高的位置,在那里也写一些东西,我最终会得到一个稀疏文件,它在中间某处包含有价值的信息,周围是大量的信息的稀疏文件。我想在文件中找到这些有价值的信息,而不必完全阅读它。
示例:
$ python
f = open('sparse', 'w')
f.seek((1<<40) + 42)
f.write('foo')
f.seek((1<<40) * 2)
f.write('[=10=]')
f.close()
这将创建一个仅使用 8k 磁盘的 2TB 文件space:
$ du -h sparse
8.0K sparse
中间某处(1TB + 42 字节)是有价值的信息 (foo
)。
我当然可以使用 cat sparse
找到它,但这将读取完整的文件并打印大量的零字节。我尝试使用较小的尺寸,发现这种方法大约需要 3 小时才能在我的计算机上打印出这三个字符。
问题是:
有没有办法在不读取所有空块的情况下找到存储在稀疏文件中的信息?我可以使用标准 Unix 方法以某种方式找出稀疏文件中空块的位置吗?
只是根据之前的评论写一个答案:
#!/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()
但是,它可能不会执行您想要的操作,它会准确返回您写入的数据。零可能会包围返回的数据,必须手动修剪。
SEEK_DATA 和 SEEK_HOLE 的当前状态在 https://man7.org/linux/man-pages/man2/lseek.2.html 中描述:
SEEK_DATA and SEEK_HOLE are nonstandard extensions also present in Solaris, FreeBSD, and DragonFly BSD; they are proposed for inclusion in the next POSIX revision (Issue 8).