From 9d910cff4f2c6a6c2bdffdfd9a390ec1b37b510e Mon Sep 17 00:00:00 2001 From: cjzhou Date: Mon, 23 Mar 2026 12:54:36 +0800 Subject: [PATCH] fuse: remove 1GiB buffer view limit --- fuse/host.go | 41 +++++++++++++++++++++++++--------- fuse/host_buffer_test.go | 48 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 11 deletions(-) create mode 100644 fuse/host_buffer_test.go diff --git a/fuse/host.go b/fuse/host.go index 083c03c..cbeced2 100644 --- a/fuse/host.go +++ b/fuse/host.go @@ -17,6 +17,7 @@ import ( "os" "os/signal" "path/filepath" + "reflect" "runtime" "strings" "sync" @@ -116,6 +117,18 @@ func recoverAsErrno(errc0 *c_int) { } } +func cByteSlice(ptr *c_char, size c_size_t) []byte { + if nil == ptr || 0 == size { + return nil + } + slice := reflect.SliceHeader{ + Data: uintptr(unsafe.Pointer(ptr)), + Len: int(size), + Cap: int(size), + } + return *(*[]byte)(unsafe.Pointer(&slice)) +} + func hostGetattr(path0 *c_char, stat0 *c_fuse_stat_t, fi0 *c_struct_fuse_file_info) (errc0 c_int) { defer recoverAsErrno(&errc0) fsop := hostHandleGet(c_fuse_get_context().private_data).fsop @@ -135,7 +148,10 @@ func hostReadlink(path0 *c_char, buff0 *c_char, size0 c_size_t) (errc0 c_int) { fsop := hostHandleGet(c_fuse_get_context().private_data).fsop path := c_GoString(path0) errc, rslt := fsop.Readlink(path) - buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + buff := cByteSlice(buff0, size0) + if 0 == size0 { + return c_int(errc) + } copy(buff[:size0-1], rslt) rlen := len(rslt) if c_size_t(rlen) < size0 { @@ -284,8 +300,8 @@ func hostRead(path0 *c_char, buff0 *c_char, size0 c_size_t, ofst0 c_fuse_off_t, defer recoverAsErrno(&nbyt0) fsop := hostHandleGet(c_fuse_get_context().private_data).fsop path := c_GoString(path0) - buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) - nbyt := fsop.Read(path, buff[:size0], int64(ofst0), uint64(fi0.fh)) + buff := cByteSlice(buff0, size0) + nbyt := fsop.Read(path, buff, int64(ofst0), uint64(fi0.fh)) return c_int(nbyt) } @@ -294,8 +310,8 @@ func hostWrite(path0 *c_char, buff0 *c_char, size0 c_size_t, ofst0 c_fuse_off_t, defer recoverAsErrno(&nbyt0) fsop := hostHandleGet(c_fuse_get_context().private_data).fsop path := c_GoString(path0) - buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) - nbyt := fsop.Write(path, buff[:size0], int64(ofst0), uint64(fi0.fh)) + buff := cByteSlice(buff0, size0) + nbyt := fsop.Write(path, buff, int64(ofst0), uint64(fi0.fh)) return c_int(nbyt) } @@ -346,8 +362,8 @@ func hostSetxattr(path0 *c_char, name0 *c_char, buff0 *c_char, size0 c_size_t, fsop := hostHandleGet(c_fuse_get_context().private_data).fsop path := c_GoString(path0) name := c_GoString(name0) - buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) - errc := fsop.Setxattr(path, name, buff[:size0], int(flags)) + buff := cByteSlice(buff0, size0) + errc := fsop.Setxattr(path, name, buff, int(flags)) return c_int(errc) } @@ -364,8 +380,8 @@ func hostGetxattr(path0 *c_char, name0 *c_char, buff0 *c_char, size0 c_size_t) ( if len(rslt) > int(size0) { return -c_int(ERANGE) } - buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) - copy(buff[:size0], rslt) + buff := cByteSlice(buff0, size0) + copy(buff, rslt) } return c_int(len(rslt)) } @@ -374,7 +390,7 @@ func hostListxattr(path0 *c_char, buff0 *c_char, size0 c_size_t) (nbyt0 c_int) { defer recoverAsErrno(&nbyt0) fsop := hostHandleGet(c_fuse_get_context().private_data).fsop path := c_GoString(path0) - buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + buff := cByteSlice(buff0, size0) size := int(size0) nbyt := 0 fill := func(name1 string) bool { @@ -599,7 +615,10 @@ func hostGetpath(path0 *c_char, buff0 *c_char, size0 c_size_t, fifh = uint64(fi0.fh) } errc, rslt := intf.Getpath(path, fifh) - buff := (*[1 << 30]byte)(unsafe.Pointer(buff0)) + buff := cByteSlice(buff0, size0) + if 0 == size0 { + return c_int(errc) + } copy(buff[:size0-1], rslt) rlen := len(rslt) if c_size_t(rlen) < size0 { diff --git a/fuse/host_buffer_test.go b/fuse/host_buffer_test.go new file mode 100644 index 0000000..5bbe9fe --- /dev/null +++ b/fuse/host_buffer_test.go @@ -0,0 +1,48 @@ +/* + * host_buffer_test.go + * + * Copyright 2017-2022 Bill Zissimopoulos + */ +/* + * This file is part of Cgofuse. + * + * It is licensed under the MIT license. The full license text can be found + * in the License.txt file at the root of this project. + */ + +package fuse + +import ( + "testing" + "unsafe" +) + +func TestCByteSliceZeroInputs(t *testing.T) { + if got := cByteSlice(nil, 1); nil != got { + t.Fatal("expected nil slice for nil pointer") + } + + buf := []byte{'a'} + if got := cByteSlice((*c_char)(unsafe.Pointer(&buf[0])), 0); nil != got { + t.Fatal("expected nil slice for zero-sized request") + } +} + +func TestCByteSliceSharesBuffer(t *testing.T) { + buf := []byte{'a', 'b', 'c'} + view := cByteSlice((*c_char)(unsafe.Pointer(&buf[0])), c_size_t(len(buf))) + + if len(view) != len(buf) { + t.Fatalf("unexpected slice length: got %d want %d", len(view), len(buf)) + } + + view[1] = 'x' + if buf[1] != 'x' { + t.Fatal("expected view writes to update the original buffer") + } + + buf[2] = 'y' + if view[2] != 'y' { + t.Fatal("expected original buffer writes to update the view") + } +}