diff --git a/fuse.go b/fuse.go index b774a25c..e138bc94 100644 --- a/fuse.go +++ b/fuse.go @@ -232,10 +232,17 @@ func initMount(c *Conn, conf *mountConfig) error { } c.proto = proto + timeGran := uint32(1) + if conf.timeGran > 0 { + timeGran = conf.timeGran + } + s := &InitResponse{ Library: proto, MaxReadahead: conf.maxReadahead, MaxWrite: maxWrite, + TimeGran: timeGran, + MaxPages: maxPages, Flags: InitBigWrites | conf.initFlags, } r.Respond(s) @@ -1249,10 +1256,14 @@ type InitResponse struct { // Maximum size of a single write operation. // Linux enforces a minimum of 4 KiB. MaxWrite uint32 + // Filesystem timestamp granularity in nanoseconds. + TimeGran uint32 + // Max pages of a single FUSE message. + MaxPages uint16 } func (r *InitResponse) String() string { - return fmt.Sprintf("Init %v ra=%d fl=%v w=%d", r.Library, r.MaxReadahead, r.Flags, r.MaxWrite) + return fmt.Sprintf("Init %v ra=%d fl=%v w=%d tg=%d pg=%d", r.Library, r.MaxReadahead, r.Flags, r.MaxWrite, r.TimeGran, r.MaxPages) } // Respond replies to the request with the given response. @@ -1264,13 +1275,24 @@ func (r *InitRequest) Respond(resp *InitResponse) { out.MaxReadahead = resp.MaxReadahead out.Flags = uint32(resp.Flags) out.MaxWrite = resp.MaxWrite + out.TimeGran = resp.TimeGran + + if r.Kernel.GE(Protocol{7, 28}) { + out.Flags |= uint32(InitMaxPages) + out.MaxPages = resp.MaxPages + } // MaxWrite larger than our receive buffer would just lead to // errors on large writes. if out.MaxWrite > maxWrite { out.MaxWrite = maxWrite } - r.respond(buf) + if r.Kernel.GE(Protocol{7, 23}) { + r.respond(buf) + } else { + // Trucate header struct for older protocol versions. + r.respond(buf[:24]) + } } // A StatfsRequest requests information about the mounted file system. diff --git a/fuse_darwin.go b/fuse_darwin.go index b58dca97..1ccc07ff 100644 --- a/fuse_darwin.go +++ b/fuse_darwin.go @@ -1,5 +1,8 @@ package fuse +// Max pages per fuse message. +const maxPages = 32 + // Maximum file write size we are prepared to receive from the kernel. // // This value has to be >=16MB or OSXFUSE (3.4.0 observed) will diff --git a/fuse_freebsd.go b/fuse_freebsd.go index 4aa83a0d..e04a2987 100644 --- a/fuse_freebsd.go +++ b/fuse_freebsd.go @@ -1,6 +1,9 @@ package fuse +// Max pages per fuse message. +const maxPages = 32 + // Maximum file write size we are prepared to receive from the kernel. // // This number is just a guess. -const maxWrite = 128 * 1024 +const maxWrite = maxPages * 4096 diff --git a/fuse_kernel.go b/fuse_kernel.go index 416d32aa..864952ac 100644 --- a/fuse_kernel.go +++ b/fuse_kernel.go @@ -46,7 +46,7 @@ const ( protoVersionMinMajor = 7 protoVersionMinMinor = 8 protoVersionMaxMajor = 7 - protoVersionMaxMinor = 12 + protoVersionMaxMinor = 28 ) const ( @@ -273,6 +273,12 @@ const ( InitAsyncDIO InitFlags = 1 << 15 InitWritebackCache InitFlags = 1 << 16 InitNoOpenSupport InitFlags = 1 << 17 + InitParallelDirops InitFlags = 1 << 18 + InitHandleKillpriv InitFlags = 1 << 19 + InitPosixACL InitFlags = 1 << 20 + InitAbortError InitFlags = 1 << 21 + InitMaxPages InitFlags = 1 << 22 + InitCacheSymlinks InitFlags = 1 << 23 InitCaseSensitive InitFlags = 1 << 29 // OS X only InitVolRename InitFlags = 1 << 30 // OS X only @@ -303,6 +309,12 @@ var initFlagNames = []flagName{ {uint32(InitAsyncDIO), "InitAsyncDIO"}, {uint32(InitWritebackCache), "InitWritebackCache"}, {uint32(InitNoOpenSupport), "InitNoOpenSupport"}, + {uint32(InitParallelDirops), "InitParallelDirops"}, + {uint32(InitHandleKillpriv), "InitHandleKillpriv"}, + {uint32(InitPosixACL), "InitPosixACL"}, + {uint32(InitAbortError), "InitAbortError"}, + {uint32(InitMaxPages), "InitMaxPages"}, + {uint32(InitCacheSymlinks), "InitCacheSymlinks"}, {uint32(InitCaseSensitive), "InitCaseSensitive"}, {uint32(InitVolRename), "InitVolRename"}, @@ -706,8 +718,13 @@ type initOut struct { Minor uint32 MaxReadahead uint32 Flags uint32 - Unused uint32 + _ uint16 + _ uint16 MaxWrite uint32 + TimeGran uint32 + MaxPages uint16 + _ uint16 + _ [8]uint32 } type interruptIn struct { diff --git a/fuse_linux.go b/fuse_linux.go index 5fb96f9a..34a121d9 100644 --- a/fuse_linux.go +++ b/fuse_linux.go @@ -1,7 +1,13 @@ package fuse +// Max pages per fuse message (FUSE_MAX_MAX_PAGES). +const maxPages = 256 + // Maximum file write size we are prepared to receive from the kernel. // // Linux 4.2.0 has been observed to cap this value at 128kB // (FUSE_MAX_PAGES_PER_REQ=32, 4kB pages). -const maxWrite = 128 * 1024 +// +// Linux >= 4.20 allows up to FUSE_MAX_MAX_PAGES=256, but defaults to +// FUSE_DEFAULT_MAX_PAGES_PER_REQ=32. +const maxWrite = maxPages * 4096 diff --git a/options.go b/options.go index f09ffd4e..49c570bb 100644 --- a/options.go +++ b/options.go @@ -14,6 +14,7 @@ func dummyOption(conf *mountConfig) error { type mountConfig struct { options map[string]string maxReadahead uint32 + timeGran uint32 initFlags InitFlags osxfuseLocations []OSXFUSEPaths } @@ -231,6 +232,16 @@ func WritebackCache() MountOption { } } +// TimeGran sets the granularity of timestamps in nanoseconds. +// +// Default is 1 nanosecond, must be power of 10. +func TimeGran(n uint32) MountOption { + return func(conf *mountConfig) error { + conf.timeGran = n + return nil + } +} + // OSXFUSEPaths describes the paths used by an installed OSXFUSE // version. See OSXFUSELocationV3 for typical values. type OSXFUSEPaths struct {