Commit 5e3fe891 authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: implement CopyFileRange

parent 9f3ce49d
...@@ -494,3 +494,7 @@ func (c *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Stat ...@@ -494,3 +494,7 @@ func (c *rawBridge) Flush(cancel <-chan struct{}, input *fuse.FlushIn) fuse.Stat
} }
return fuse.OK return fuse.OK
} }
func (c *rawBridge) CopyFileRange(cancel <-chan struct{}, input *fuse.CopyFileRangeIn) (written uint32, code fuse.Status) {
return 0, fuse.ENOSYS
}
...@@ -191,6 +191,11 @@ type FileOperations interface { ...@@ -191,6 +191,11 @@ type FileOperations interface {
// FSetAttr is like SetAttr but provides a file handle if available. // FSetAttr is like SetAttr but provides a file handle if available.
FSetAttr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status FSetAttr(ctx context.Context, f FileHandle, in *fuse.SetAttrIn, out *fuse.AttrOut) fuse.Status
// CopyFileRange copies data between sections of two files.
CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, fuse.Status)
} }
// LockOperations are operations for locking regions of regular files. // LockOperations are operations for locking regions of regular files.
......
...@@ -738,3 +738,11 @@ func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fu ...@@ -738,3 +738,11 @@ func (b *rawBridge) StatFs(cancel <-chan struct{}, input *fuse.InHeader, out *fu
func (b *rawBridge) Init(s *fuse.Server) { func (b *rawBridge) Init(s *fuse.Server) {
b.server = s b.server = s
} }
func (b *rawBridge) CopyFileRange(cancel <-chan struct{}, in *fuse.CopyFileRangeIn) (size uint32, status fuse.Status) {
n1, f1 := b.inode(in.NodeId, in.FhIn)
n2, f2 := b.inode(in.NodeIdOut, in.FhOut)
return n1.fileOps().CopyFileRange(&fuse.Context{Caller: in.Caller, Cancel: cancel},
f1.file, in.OffIn, n2, f2.file, in.OffOut, in.Len, in.Flags)
}
...@@ -201,6 +201,12 @@ func (n *DefaultOperations) Write(ctx context.Context, f FileHandle, data []byte ...@@ -201,6 +201,12 @@ func (n *DefaultOperations) Write(ctx context.Context, f FileHandle, data []byte
return 0, fuse.EROFS return 0, fuse.EROFS
} }
func (n *DefaultOperations) CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, fuse.Status) {
return 0, fuse.EROFS
}
// GetLk delegates to the FileHandlef // GetLk delegates to the FileHandlef
func (n *DefaultOperations) GetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (status fuse.Status) { func (n *DefaultOperations) GetLk(ctx context.Context, f FileHandle, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (status fuse.Status) {
if f != nil { if f != nil {
......
...@@ -279,6 +279,24 @@ func (n *loopbackNode) FGetAttr(ctx context.Context, f FileHandle, out *fuse.Att ...@@ -279,6 +279,24 @@ func (n *loopbackNode) FGetAttr(ctx context.Context, f FileHandle, out *fuse.Att
return fuse.OK return fuse.OK
} }
func (n *loopbackNode) CopyFileRange(ctx context.Context, fhIn FileHandle,
offIn uint64, out *Inode, fhOut FileHandle, offOut uint64,
len uint64, flags uint64) (uint32, fuse.Status) {
lfIn, ok := fhIn.(*loopbackFile)
if !ok {
return 0, fuse.ENOTSUP
}
lfOut, ok := fhOut.(*loopbackFile)
if !ok {
return 0, fuse.ENOTSUP
}
signedOffIn := int64(offIn)
signedOffOut := int64(offOut)
count, err := unix.CopyFileRange(lfIn.fd, &signedOffIn, lfOut.fd, &signedOffOut, int(len), int(flags))
return uint32(count), fuse.ToStatus(err)
}
// NewLoopback returns a root node for a loopback file system whose // NewLoopback returns a root node for a loopback file system whose
// root is at the given root. // root is at the given root.
func NewLoopbackRoot(root string) (DirOperations, error) { func NewLoopbackRoot(root string) (DirOperations, error) {
......
...@@ -682,3 +682,45 @@ func TestGetAttrParallel(t *testing.T) { ...@@ -682,3 +682,45 @@ func TestGetAttrParallel(t *testing.T) {
} }
// XXX test mknod. // XXX test mknod.
func TestCopyFileRange(t *testing.T) {
tc := newTestCase(t, true, true)
defer tc.Clean()
tc.writeOrig("src", "01234567890123456789", 0644)
tc.writeOrig("dst", "abcdefghijabcdefghij", 0644)
f1, err := syscall.Open(tc.mntDir+"/src", syscall.O_RDONLY, 0)
if err != nil {
t.Fatalf("Open src: %v", err)
}
defer syscall.Close(f1)
f2, err := syscall.Open(tc.mntDir+"/dst", syscall.O_RDWR, 0)
if err != nil {
t.Fatalf("Open dst: %v", err)
}
defer syscall.Close(f2)
srcOff := int64(5)
dstOff := int64(7)
if sz, err := unix.CopyFileRange(f1, &srcOff, f2, &dstOff, 3, 0); err != nil || sz != 3 {
t.Fatalf("CopyFileRange: %d,%v", sz, err)
}
if err := syscall.Close(f1); err != nil {
t.Fatalf("Close src: %v", err)
}
if err := syscall.Close(f2); err != nil {
t.Fatalf("Close dst: %v", err)
}
c, err := ioutil.ReadFile(tc.mntDir + "/dst")
if err != nil {
t.Fatalf("ReadFile: %v", err)
}
want := "abcdefg567abcdefghij"
got := string(c)
if got != want {
t.Errorf("got %q want %q", got, want)
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment