Browse Source

Use pip fallocate package rather than hand-rolling

tags/nilmdb-2.0.0
Jim Paris 4 years ago
parent
commit
f3833d9b20
2 changed files with 25 additions and 37 deletions
  1. +7
    -37
      nilmdb/utils/fallocate.py
  2. +18
    -0
      tests/test_misc.py

+ 7
- 37
nilmdb/utils/fallocate.py View File

@@ -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

+ 18
- 0
tests/test_misc.py View File

@@ -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)

Loading…
Cancel
Save