mirror of https://github.com/ceph/go-ceph
cephfs: implement Preadv and Pwritev File methods
Add Preadv implementing ceph_preadv. Add Pwritev implementing ceph_pwritev. These calls act similarly to ReadAt and WriteAt but take multiple buffers at once. Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
parent
ebea82dda5
commit
9c1ed2dfc2
|
@ -13,6 +13,8 @@ import "C"
|
|||
import (
|
||||
"io"
|
||||
"unsafe"
|
||||
|
||||
"github.com/ceph/go-ceph/internal/cutil"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -128,6 +130,37 @@ func (f *File) ReadAt(buf []byte, offset int64) (int, error) {
|
|||
return f.read(buf, offset)
|
||||
}
|
||||
|
||||
// Preadv will read data from the file, starting at the given offset,
|
||||
// into the byte-slice data buffers sequentially.
|
||||
// The number of bytes read will be returned.
|
||||
// When nothing is left to read from the file the return values will be:
|
||||
// 0, io.EOF.
|
||||
//
|
||||
// Implements:
|
||||
// int ceph_preadv(struct ceph_mount_info *cmount, int fd, const struct iovec *iov, int iovcnt,
|
||||
// int64_t offset);
|
||||
func (f *File) Preadv(data [][]byte, offset int64) (int, error) {
|
||||
if err := f.validate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iov := cutil.ByteSlicesToIovec(data)
|
||||
defer iov.Free()
|
||||
|
||||
ret := C.ceph_preadv(
|
||||
f.mount.mount,
|
||||
f.fd,
|
||||
(*C.struct_iovec)(iov.Pointer()),
|
||||
C.int(iov.Len()),
|
||||
C.int64_t(offset))
|
||||
switch {
|
||||
case ret < 0:
|
||||
return 0, getError(ret)
|
||||
case ret == 0:
|
||||
return 0, io.EOF
|
||||
}
|
||||
return int(ret), nil
|
||||
}
|
||||
|
||||
// write directly wraps the ceph_write call. Because write is such a common
|
||||
// operation we deviate from the ceph naming and expose Write and WriteAt
|
||||
// wrappers for external callers of the library.
|
||||
|
@ -163,6 +196,32 @@ func (f *File) WriteAt(buf []byte, offset int64) (int, error) {
|
|||
return f.write(buf, offset)
|
||||
}
|
||||
|
||||
// Pwritev writes data from the slice of byte-slice buffers to the file at the
|
||||
// specified offset.
|
||||
// The number of bytes written is returned.
|
||||
//
|
||||
// Implements:
|
||||
// int ceph_pwritev(struct ceph_mount_info *cmount, int fd, const struct iovec *iov, int iovcnt,
|
||||
// int64_t offset);
|
||||
func (f *File) Pwritev(data [][]byte, offset int64) (int, error) {
|
||||
if err := f.validate(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
iov := cutil.ByteSlicesToIovec(data)
|
||||
defer iov.Free()
|
||||
|
||||
ret := C.ceph_pwritev(
|
||||
f.mount.mount,
|
||||
f.fd,
|
||||
(*C.struct_iovec)(iov.Pointer()),
|
||||
C.int(iov.Len()),
|
||||
C.int64_t(offset))
|
||||
if ret < 0 {
|
||||
return 0, getError(ret)
|
||||
}
|
||||
return int(ret), nil
|
||||
}
|
||||
|
||||
// Seek will reposition the file stream based on the given offset.
|
||||
//
|
||||
// Implements:
|
||||
|
|
|
@ -769,3 +769,121 @@ func TestSync(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestFilePreadvPwritev(t *testing.T) {
|
||||
mount := fsConnect(t)
|
||||
defer fsDisconnect(t, mount)
|
||||
|
||||
fname := "TestFilePreadvPwritev.txt"
|
||||
defer mount.Unlink(fname)
|
||||
|
||||
t.Run("simple", func(t *testing.T) {
|
||||
f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
assert.NoError(t, err)
|
||||
defer func() { assert.NoError(t, f.Close()) }()
|
||||
|
||||
b1 := []byte("foobarbaz")
|
||||
b2 := []byte("alphabeta")
|
||||
b3 := []byte("superawseomefuntime")
|
||||
n, err := f.Pwritev([][]byte{b1, b2, b3}, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 37, n)
|
||||
|
||||
o := [][]byte{
|
||||
make([]byte, 3),
|
||||
make([]byte, 3),
|
||||
make([]byte, 3),
|
||||
make([]byte, 3),
|
||||
}
|
||||
n, err = f.Preadv(o, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 12, n)
|
||||
assert.Equal(t, "foo", string(o[0]))
|
||||
assert.Equal(t, "bar", string(o[1]))
|
||||
assert.Equal(t, "baz", string(o[2]))
|
||||
assert.Equal(t, "alp", string(o[3]))
|
||||
})
|
||||
|
||||
t.Run("silly", func(t *testing.T) {
|
||||
f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
assert.NoError(t, err)
|
||||
defer func() { assert.NoError(t, f.Close()) }()
|
||||
|
||||
b := []byte("foo")
|
||||
x := make([][]byte, 8)
|
||||
for i := range x {
|
||||
x[i] = b
|
||||
}
|
||||
n, err := f.Pwritev(x, 0)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 24, n)
|
||||
|
||||
for i := range x {
|
||||
x[i] = make([]byte, 6)
|
||||
}
|
||||
n, err = f.Preadv(x, 1)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, 23, n)
|
||||
assert.Equal(t, "oofoof", string(x[0]))
|
||||
assert.Equal(t, "oofoof", string(x[1]))
|
||||
assert.Equal(t, "oofoof", string(x[2]))
|
||||
assert.Equal(t, "oofoo\x00", string(x[3]))
|
||||
})
|
||||
|
||||
t.Run("readEOF", func(t *testing.T) {
|
||||
f, err := mount.Open(fname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
assert.NoError(t, err)
|
||||
defer func() { assert.NoError(t, f.Close()) }()
|
||||
|
||||
x := make([][]byte, 8)
|
||||
for i := range x {
|
||||
x[i] = make([]byte, 6)
|
||||
}
|
||||
_, err = f.Preadv(x, 16)
|
||||
assert.Error(t, err)
|
||||
assert.Equal(t, io.EOF, err)
|
||||
})
|
||||
|
||||
t.Run("openForWriteOnly", func(t *testing.T) {
|
||||
f1, err := mount.Open(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
assert.NoError(t, err)
|
||||
defer func() { assert.NoError(t, f1.Close()) }()
|
||||
|
||||
x := make([][]byte, 8)
|
||||
for i := range x {
|
||||
x[i] = make([]byte, 6)
|
||||
}
|
||||
_, err = f1.Preadv(x, 0)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("openForReadOnly", func(t *testing.T) {
|
||||
// "touch" the file
|
||||
f1, err := mount.Open(fname, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, f1.Close())
|
||||
|
||||
f1, err = mount.Open(fname, os.O_RDONLY, 0644)
|
||||
assert.NoError(t, err)
|
||||
defer func() { assert.NoError(t, f1.Close()) }()
|
||||
|
||||
x := make([][]byte, 8)
|
||||
for i := range x {
|
||||
x[i] = []byte("robble")
|
||||
}
|
||||
_, err = f1.Pwritev(x, 0)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("writeInvalidFile", func(t *testing.T) {
|
||||
f := &File{}
|
||||
_, err := f.Pwritev([][]byte{}, 0)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("readInvalidFile", func(t *testing.T) {
|
||||
f := &File{}
|
||||
_, err := f.Preadv([][]byte{}, 0)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue