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 (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/ceph/go-ceph/internal/cutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -128,6 +130,37 @@ func (f *File) ReadAt(buf []byte, offset int64) (int, error) {
|
||||||
return f.read(buf, offset)
|
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
|
// 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
|
// operation we deviate from the ceph naming and expose Write and WriteAt
|
||||||
// wrappers for external callers of the library.
|
// 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)
|
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.
|
// Seek will reposition the file stream based on the given offset.
|
||||||
//
|
//
|
||||||
// Implements:
|
// Implements:
|
||||||
|
|
|
@ -769,3 +769,121 @@ func TestSync(t *testing.T) {
|
||||||
assert.Error(t, err)
|
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