# 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
