diff --git a/nilmdb/utils/fallocate.py b/nilmdb/utils/fallocate.py index 8ced07c..25ac4f2 100644 --- a/nilmdb/utils/fallocate.py +++ b/nilmdb/utils/fallocate.py @@ -1,49 +1,19 @@ # 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 +import fallocate 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 + fallocate.fallocate( + f.fileno(), + offset, + length, + fallocate.FALLOC_FL_KEEP_SIZE | fallocate.FALLOC_FL_PUNCH_HOLE) + except Exception: if ignore_errors: return raise diff --git a/tests/test_misc.py b/tests/test_misc.py index e57375f..5e2098c 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -52,3 +52,21 @@ class TestMisc(object): nilmdb.utils.atomic.replace_file(fn, b"goodbye, world") with open(fn, "rb") as f: eq_(f.read(), b"goodbye, world") + + def test_punch(self): + fn = b"tests/misc-testdb/punchit" + try: + os.mkdir(os.path.dirname(fn)) + except FileExistsError: + pass + with open(fn, "wb") as f: + f.write(b"hello, world") + nilmdb.utils.fallocate.punch_hole(fn, 3, 5) + with open(fn, "rb") as f: + eq_(f.read(), b"hel\0\0\0\0\0orld") + with assert_raises(OSError): + nilmdb.utils.fallocate.punch_hole(fn, 1, -1, False) + with assert_raises(OSError): + nilmdb.utils.fallocate.punch_hole("/", 1, 1, False) + # no exception because we ignore errors by default + nilmdb.utils.fallocate.punch_hole(fn, 1, -1)