Requires fallocate(2) support with FALLOC_FL_PUNCH_HOLE, as well as a filesystem that supports it (in Linux 3.7, tmpfs, btrfs, xfs, or ext4)
50 lines
1.5 KiB
Python
50 lines
1.5 KiB
Python
# Implementation of hole punching via fallocate, if the OS
|
|
# and filesystem support it.
|
|
|
|
try:
|
|
import os
|
|
import ctypes
|
|
import ctypes.util
|
|
|
|
def make_fallocate():
|
|
libc_name = ctypes.util.find_library('c')
|
|
libc = ctypes.CDLL(libc_name, use_errno=True)
|
|
|
|
_fallocate = libc.fallocate
|
|
_fallocate.restype = ctypes.c_int
|
|
_fallocate.argtypes = [ ctypes.c_int, ctypes.c_int,
|
|
ctypes.c_int64, ctypes.c_int64 ]
|
|
|
|
del libc
|
|
del libc_name
|
|
|
|
def fallocate(fd, mode, offset, len_):
|
|
res = _fallocate(fd, mode, offset, len_)
|
|
if res != 0: # pragma: no cover
|
|
errno = ctypes.get_errno()
|
|
raise IOError(errno, os.strerror(errno))
|
|
return fallocate
|
|
|
|
fallocate = make_fallocate()
|
|
del make_fallocate
|
|
except Exception: # pragma: no cover
|
|
fallocate = None
|
|
|
|
FALLOC_FL_KEEP_SIZE = 0x01
|
|
FALLOC_FL_PUNCH_HOLE = 0x02
|
|
|
|
def punch_hole(filename, offset, length, ignore_errors = True):
|
|
"""Punch a hole in the file. This isn't well supported, so errors
|
|
are ignored by default."""
|
|
try:
|
|
if fallocate is None: # pragma: no cover
|
|
raise IOError("fallocate not available")
|
|
with open(filename, "r+") as f:
|
|
fallocate(f.fileno(),
|
|
FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE,
|
|
offset, length)
|
|
except IOError: # pragma: no cover
|
|
if ignore_errors:
|
|
return
|
|
raise
|