mirror of https://github.com/ceph/go-ceph
cephfs: add ceph_fallocate() function
Added ceph_fallocate() which preallocates or releases disk space for the file for the given byte range. Fixes: https://github.com/ceph/go-ceph/issues/246 Signed-off-by: Mudit Agarwal muagarwa@redhat.com
This commit is contained in:
parent
80833a6789
commit
1d30b636ff
|
@ -3,7 +3,9 @@ package cephfs
|
||||||
/*
|
/*
|
||||||
#cgo LDFLAGS: -lcephfs
|
#cgo LDFLAGS: -lcephfs
|
||||||
#cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64
|
#cgo CPPFLAGS: -D_FILE_OFFSET_BITS=64
|
||||||
|
#define _GNU_SOURCE
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <cephfs/libcephfs.h>
|
#include <cephfs/libcephfs.h>
|
||||||
*/
|
*/
|
||||||
import "C"
|
import "C"
|
||||||
|
@ -223,3 +225,33 @@ func (f *File) Fstatx(want StatxMask, flags AtFlags) (*CephStatx, error) {
|
||||||
}
|
}
|
||||||
return cStructToCephStatx(stx), nil
|
return cStructToCephStatx(stx), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FallocFlags represent flags which determine the operation to be
|
||||||
|
// performed on the given range.
|
||||||
|
// CephFS supports only following two flags.
|
||||||
|
type FallocFlags int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// FallocNoFlag means default option.
|
||||||
|
FallocNoFlag = FallocFlags(0)
|
||||||
|
// FallocFlKeepSize specifies that the file size will not be changed.
|
||||||
|
FallocFlKeepSize = FallocFlags(C.FALLOC_FL_KEEP_SIZE)
|
||||||
|
// FallocFlPunchHole specifies that the operation is to deallocate
|
||||||
|
// space and zero the byte range.
|
||||||
|
FallocFlPunchHole = FallocFlags(C.FALLOC_FL_PUNCH_HOLE)
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fallocate preallocates or releases disk space for the file for the
|
||||||
|
// given byte range, the flags determine the operation to be performed
|
||||||
|
// on the given range.
|
||||||
|
//
|
||||||
|
// Implements:
|
||||||
|
// int ceph_fallocate(struct ceph_mount_info *cmount, int fd, int mode,
|
||||||
|
// int64_t offset, int64_t length);
|
||||||
|
func (f *File) Fallocate(mode FallocFlags, offset, length int64) error {
|
||||||
|
if err := f.validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
ret := C.ceph_fallocate(f.mount.mount, f.fd, C.int(mode), C.int64_t(offset), C.int64_t(length))
|
||||||
|
return getError(ret)
|
||||||
|
}
|
||||||
|
|
|
@ -475,3 +475,143 @@ func TestFstatx(t *testing.T) {
|
||||||
assert.Error(t, err)
|
assert.Error(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFallocate(t *testing.T) {
|
||||||
|
mount := fsConnect(t)
|
||||||
|
defer fsDisconnect(t, mount)
|
||||||
|
fname := "file1.txt"
|
||||||
|
f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, f)
|
||||||
|
defer func() {
|
||||||
|
assert.NoError(t, f.Close())
|
||||||
|
assert.NoError(t, mount.Unlink(fname))
|
||||||
|
}()
|
||||||
|
|
||||||
|
// assert that negative values will return error.
|
||||||
|
t.Run("NegativeOffsetLength", func(t *testing.T) {
|
||||||
|
err = f.Fallocate(FallocNoFlag, -1, 10)
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
err = f.Fallocate(FallocNoFlag, 10, -1)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Allocate space - default case, mode == 0.
|
||||||
|
t.Run("modeIsZero", func(t *testing.T) {
|
||||||
|
useMount(t)
|
||||||
|
// check file size.
|
||||||
|
s, err := os.Stat(path.Join(CephMountDir, fname))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 0, s.Size())
|
||||||
|
// write 10 bytes at offset 0.
|
||||||
|
err = f.Fallocate(FallocNoFlag, 0, 10)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// check file size again.
|
||||||
|
s, err = os.Stat(path.Join(CephMountDir, fname))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 10, s.Size())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Allocate space - size increases, data remains intact.
|
||||||
|
t.Run("increaseSize", func(t *testing.T) {
|
||||||
|
useMount(t)
|
||||||
|
fname := "file2.txt"
|
||||||
|
f1, err := mount.Open(fname, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, f1)
|
||||||
|
defer func() {
|
||||||
|
assert.NoError(t, f1.Close())
|
||||||
|
assert.NoError(t, mount.Unlink(fname))
|
||||||
|
}()
|
||||||
|
// write to file.
|
||||||
|
n, err := f1.Write([]byte("Ten chars!"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 10, n)
|
||||||
|
// check the file size.
|
||||||
|
s, err := os.Stat(path.Join(CephMountDir, fname))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 10, s.Size())
|
||||||
|
// allocate 10 more bytes from the middle.
|
||||||
|
err = f1.Fallocate(FallocNoFlag, 5, 10)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// check the size, it should increase.
|
||||||
|
s, err = os.Stat(path.Join(CephMountDir, fname))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 15, s.Size())
|
||||||
|
// Read the contents, first ten chars remain intact.
|
||||||
|
buf := make([]byte, 10)
|
||||||
|
n, err = f1.ReadAt(buf, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "Ten chars!", string(buf[:n]))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Allocate space - with FALLOC_FL_KEEP_SIZE.
|
||||||
|
t.Run("allocateSpaceWithFlag", func(t *testing.T) {
|
||||||
|
useMount(t)
|
||||||
|
fname := "file3.txt"
|
||||||
|
f1, err := mount.Open(fname, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, f1)
|
||||||
|
defer func() {
|
||||||
|
assert.NoError(t, f1.Close())
|
||||||
|
assert.NoError(t, mount.Unlink(fname))
|
||||||
|
}()
|
||||||
|
// Write to file.
|
||||||
|
n, err := f1.Write([]byte("tenchars!!"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 10, n)
|
||||||
|
// Allocate 10 more bytes from the middle.
|
||||||
|
err = f1.Fallocate(FallocFlKeepSize, 5, 10)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Check the file size, it should not increase.
|
||||||
|
s, err := os.Stat(path.Join(CephMountDir, fname))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 10, s.Size())
|
||||||
|
})
|
||||||
|
|
||||||
|
// Deallocate space - with only FALLOC_FL_PUNCH_HOLE.
|
||||||
|
t.Run("punchHoleFlagAlone", func(t *testing.T) {
|
||||||
|
err := f.Fallocate(FallocFlPunchHole, 0, 10)
|
||||||
|
// Not supported.
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
|
||||||
|
// De-allocate space - punch holes.
|
||||||
|
t.Run("punchActualHoles", func(t *testing.T) {
|
||||||
|
fname := "file4.txt"
|
||||||
|
f1, err := mount.Open(fname, os.O_RDWR|os.O_CREATE, 0666)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.NotNil(t, f1)
|
||||||
|
defer func() {
|
||||||
|
assert.NoError(t, f1.Close())
|
||||||
|
assert.NoError(t, mount.Unlink(fname))
|
||||||
|
}()
|
||||||
|
// Write some data.
|
||||||
|
n, err := f1.Write([]byte("Ten chars!"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.EqualValues(t, 10, n)
|
||||||
|
// Read it back.
|
||||||
|
buf := make([]byte, 10)
|
||||||
|
n, err = f1.ReadAt(buf, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "Ten chars!", string(buf[:n]))
|
||||||
|
// Punch holes.
|
||||||
|
err = f1.Fallocate(FallocFlPunchHole|FallocFlKeepSize, 0, 5)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
// Read again - first five chars.
|
||||||
|
buf = make([]byte, 5)
|
||||||
|
n, err = f1.ReadAt(buf, 0)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, "\x00\x00\x00\x00\x00", string(buf[:n]))
|
||||||
|
// Read again - last five chars.
|
||||||
|
n, err = f1.ReadAt(buf, 5)
|
||||||
|
assert.Equal(t, "hars!", string(buf[:n]))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("checkValidate", func(t *testing.T) {
|
||||||
|
f1 := &File{}
|
||||||
|
err := f1.Fallocate(FallocNoFlag, 0, 10)
|
||||||
|
assert.Error(t, err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue