From 4ee1cf7f62f0f224d1ec7728dabe333577dee3aa Mon Sep 17 00:00:00 2001 From: Ka-Hing Cheung Date: Sat, 26 Oct 2019 10:15:00 +0200 Subject: [PATCH] add support for fallocate (#66) * Fallocate support * use fallocate wrapper that works on darwin --- conversions.go | 18 +++++++++++++++ debug.go | 5 +++++ fuseops/ops.go | 19 ++++++++++++++++ fuseutil/file_system.go | 4 ++++ fuseutil/not_implemented_file_system.go | 7 ++++++ internal/fusekernel/fuse_kernel.go | 9 ++++++++ samples/memfs/inode.go | 16 ++++++++++++++ samples/memfs/memfs.go | 9 ++++++++ samples/memfs/memfs_test.go | 29 +++++++++++++++++++++++++ 9 files changed, 116 insertions(+) diff --git a/conversions.go b/conversions.go index 26c27c1..4e0cb35 100644 --- a/conversions.go +++ b/conversions.go @@ -546,6 +546,21 @@ func convertInMessage( Value: value, Flags: in.Flags, } + case fusekernel.OpFallocate: + type input fusekernel.FallocateIn + in := (*input)(inMsg.Consume(unsafe.Sizeof(input{}))) + if in == nil { + err = errors.New("Corrupt OpFallocate") + return + } + + o = &fuseops.FallocateOp{ + Inode: fuseops.InodeID(inMsg.Header().Nodeid), + Handle: fuseops.HandleID(in.Fh), + Offset: in.Offset, + Length: in.Length, + Mode: in.Mode, + } default: o = &unknownOp{ @@ -793,6 +808,9 @@ func (c *Connection) kernelResponseForOp( case *fuseops.SetXattrOp: // Empty response + case *fuseops.FallocateOp: + // Empty response + case *initOp: out := (*fusekernel.InitOut)(m.Grow(int(unsafe.Sizeof(fusekernel.InitOut{})))) diff --git a/debug.go b/debug.go index d6f9fdc..5ff86b0 100644 --- a/debug.go +++ b/debug.go @@ -98,6 +98,11 @@ func describeRequest(op interface{}) (s string) { case *fuseops.SetXattrOp: addComponent("name %s", typed.Name) + + case *fuseops.FallocateOp: + addComponent("offset %d", typed.Offset) + addComponent("length %d", typed.Length) + addComponent("mode %d", typed.Mode) } // Use just the name if there is no extra info. diff --git a/fuseops/ops.go b/fuseops/ops.go index 56fe89c..d09c690 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -865,3 +865,22 @@ type SetXattrOp struct { // simply replace the value if the attribute exists. Flags uint32 } + +type FallocateOp struct { + // The inode and handle we are fallocating + Inode InodeID + Handle HandleID + + // Start of the byte range + Offset uint64 + + // Length of the byte range + Length uint64 + + // If Mode is 0x0, allocate disk space within the range specified + // If Mode has 0x1, allocate the space but don't increase the file size + // If Mode has 0x2, deallocate space within the range specified + // If Mode has 0x2, it sbould also have 0x1 (deallocate should not increase + // file size) + Mode uint32 +} diff --git a/fuseutil/file_system.go b/fuseutil/file_system.go index 1ac9aac..5eb8bca 100644 --- a/fuseutil/file_system.go +++ b/fuseutil/file_system.go @@ -61,6 +61,7 @@ type FileSystem interface { GetXattr(context.Context, *fuseops.GetXattrOp) error ListXattr(context.Context, *fuseops.ListXattrOp) error SetXattr(context.Context, *fuseops.SetXattrOp) error + Fallocate(context.Context, *fuseops.FallocateOp) error // Regard all inodes (including the root inode) as having their lookup counts // decremented to zero, and clean up any resources associated with the file @@ -215,6 +216,9 @@ func (s *fileSystemServer) handleOp( case *fuseops.SetXattrOp: err = s.fs.SetXattr(ctx, typed) + + case *fuseops.FallocateOp: + err = s.fs.Fallocate(ctx, typed) } c.Reply(ctx, err) diff --git a/fuseutil/not_implemented_file_system.go b/fuseutil/not_implemented_file_system.go index c1a5f73..2e93dfa 100644 --- a/fuseutil/not_implemented_file_system.go +++ b/fuseutil/not_implemented_file_system.go @@ -219,5 +219,12 @@ func (fs *NotImplementedFileSystem) SetXattr( return } +func (fs *NotImplementedFileSystem) Fallocate( + ctx context.Context, + op *fuseops.FallocateOp) (err error) { + err = fuse.ENOSYS + return +} + func (fs *NotImplementedFileSystem) Destroy() { } diff --git a/internal/fusekernel/fuse_kernel.go b/internal/fusekernel/fuse_kernel.go index ef543cb..c716025 100644 --- a/internal/fusekernel/fuse_kernel.go +++ b/internal/fusekernel/fuse_kernel.go @@ -380,6 +380,7 @@ const ( OpDestroy = 38 OpIoctl = 39 // Linux? OpPoll = 40 // Linux? + OpFallocate = 43 // OS X OpSetvolname = 61 @@ -665,6 +666,14 @@ type ListxattrIn struct { Padding uint32 } +type FallocateIn struct { + Fh uint64 + Offset uint64 + Length uint64 + Mode uint32 + Padding uint32 +} + type LkIn struct { Fh uint64 Owner uint64 diff --git a/samples/memfs/inode.go b/samples/memfs/inode.go index b0471cc..19a6db3 100644 --- a/samples/memfs/inode.go +++ b/samples/memfs/inode.go @@ -20,6 +20,7 @@ import ( "os" "time" + "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fuseops" "github.com/jacobsa/fuse/fuseutil" ) @@ -382,3 +383,18 @@ func (in *inode) SetAttributes( in.attrs.Mtime = *mtime } } + +func (in *inode) Fallocate(mode uint32, offset uint64, length uint64) ( + err error) { + if mode == 0 { + newSize := int(offset + length) + if newSize > len(in.contents) { + padding := make([]byte, newSize-len(in.contents)) + in.contents = append(in.contents, padding...) + in.attrs.Size = offset + length + } + } else { + err = fuse.ENOSYS + } + return +} diff --git a/samples/memfs/memfs.go b/samples/memfs/memfs.go index 909b9e0..55a17bd 100644 --- a/samples/memfs/memfs.go +++ b/samples/memfs/memfs.go @@ -753,3 +753,12 @@ func (fs *memFS) SetXattr(ctx context.Context, return } + +func (fs *memFS) Fallocate(ctx context.Context, + op *fuseops.FallocateOp) (err error) { + fs.mu.Lock() + defer fs.mu.Unlock() + inode := fs.getInodeOrDie(op.Inode) + inode.Fallocate(op.Mode, op.Length, op.Length) + return +} diff --git a/samples/memfs/memfs_test.go b/samples/memfs/memfs_test.go index 9abe923..ba6e59c 100644 --- a/samples/memfs/memfs_test.go +++ b/samples/memfs/memfs_test.go @@ -28,6 +28,7 @@ import ( "testing" "time" + fallocate "github.com/detailyang/go-fallocate" "github.com/jacobsa/fuse" "github.com/jacobsa/fuse/fusetesting" "github.com/jacobsa/fuse/samples" @@ -1955,3 +1956,31 @@ func (t *MknodTest) NonExistentParent() { err = syscall.Mknod(p, syscall.S_IFREG|0600, 0) ExpectEq(syscall.ENOENT, err) } + +func (t *MknodTest) Fallocate_Larger() { + var err error + fileName := path.Join(t.Dir, "foo") + + // Create a file. + err = ioutil.WriteFile(fileName, []byte("taco"), 0600) + AssertEq(nil, err) + + // Open it for modification. + f, err := os.OpenFile(fileName, os.O_RDWR, 0) + t.ToClose = append(t.ToClose, f) + AssertEq(nil, err) + + // Truncate it. + err = fallocate.Fallocate(f, 5, 1) + AssertEq(nil, err) + + // Stat it. + fi, err := f.Stat() + AssertEq(nil, err) + ExpectEq(6, fi.Size()) + + // Read the contents. + contents, err := ioutil.ReadFile(fileName) + AssertEq(nil, err) + ExpectEq("taco\x00\x00", string(contents)) +}