Compare commits
2 Commits
8312d62874
...
efe41d860d
Author | SHA1 | Date |
---|---|---|
Vitaliy Filippov | efe41d860d | |
Vitaliy Filippov | a307ab844b |
|
@ -151,6 +151,7 @@ func (c *Connection) Init() error {
|
|||
cacheSymlinks := initOp.Flags&fusekernel.InitCacheSymlinks > 0
|
||||
noOpenSupport := initOp.Flags&fusekernel.InitNoOpenSupport > 0
|
||||
noOpendirSupport := initOp.Flags&fusekernel.InitNoOpendirSupport > 0
|
||||
readdirplusSupport := initOp.Flags&fusekernel.InitDoReaddirplus > 0
|
||||
|
||||
// Respond to the init op.
|
||||
initOp.Library = c.protocol
|
||||
|
@ -193,6 +194,11 @@ func (c *Connection) Init() error {
|
|||
initOp.Flags |= fusekernel.InitNoOpendirSupport
|
||||
}
|
||||
|
||||
// Tell the kernel to do readdirplus (readdir+lookup in one call)
|
||||
if c.cfg.UseReadDirPlus && readdirplusSupport {
|
||||
initOp.Flags |= fusekernel.InitDoReaddirplus
|
||||
}
|
||||
|
||||
c.Reply(ctx, nil)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -341,6 +341,8 @@ func convertInMessage(
|
|||
}
|
||||
o = to
|
||||
|
||||
case fusekernel.OpReaddirplus:
|
||||
fallthrough
|
||||
case fusekernel.OpReaddir:
|
||||
in := (*fusekernel.ReadIn)(inMsg.Consume(fusekernel.ReadInSize(protocol)))
|
||||
if in == nil {
|
||||
|
@ -351,6 +353,7 @@ func convertInMessage(
|
|||
Inode: fuseops.InodeID(inMsg.Header().Nodeid),
|
||||
Handle: fuseops.HandleID(in.Fh),
|
||||
Offset: fuseops.DirOffset(in.Offset),
|
||||
Plus: inMsg.Header().Opcode == fusekernel.OpReaddirplus,
|
||||
OpContext: fuseops.OpContext{Pid: inMsg.Header().Pid},
|
||||
}
|
||||
o = to
|
||||
|
@ -722,37 +725,37 @@ func (c *Connection) kernelResponseForOp(
|
|||
case *fuseops.LookUpInodeOp:
|
||||
size := int(fusekernel.EntryOutSize(c.protocol))
|
||||
out := (*fusekernel.EntryOut)(m.Grow(size))
|
||||
convertChildInodeEntry(&o.Entry, out)
|
||||
fuseops.ConvertChildInodeEntry(&o.Entry, out)
|
||||
|
||||
case *fuseops.GetInodeAttributesOp:
|
||||
size := int(fusekernel.AttrOutSize(c.protocol))
|
||||
out := (*fusekernel.AttrOut)(m.Grow(size))
|
||||
out.AttrValid, out.AttrValidNsec = convertExpirationTime(
|
||||
out.AttrValid, out.AttrValidNsec = fuseops.ConvertExpirationTime(
|
||||
o.AttributesExpiration)
|
||||
convertAttributes(o.Inode, &o.Attributes, &out.Attr)
|
||||
fuseops.ConvertAttributes(o.Inode, &o.Attributes, &out.Attr)
|
||||
|
||||
case *fuseops.SetInodeAttributesOp:
|
||||
size := int(fusekernel.AttrOutSize(c.protocol))
|
||||
out := (*fusekernel.AttrOut)(m.Grow(size))
|
||||
out.AttrValid, out.AttrValidNsec = convertExpirationTime(
|
||||
out.AttrValid, out.AttrValidNsec = fuseops.ConvertExpirationTime(
|
||||
o.AttributesExpiration)
|
||||
convertAttributes(o.Inode, &o.Attributes, &out.Attr)
|
||||
fuseops.ConvertAttributes(o.Inode, &o.Attributes, &out.Attr)
|
||||
|
||||
case *fuseops.MkDirOp:
|
||||
size := int(fusekernel.EntryOutSize(c.protocol))
|
||||
out := (*fusekernel.EntryOut)(m.Grow(size))
|
||||
convertChildInodeEntry(&o.Entry, out)
|
||||
fuseops.ConvertChildInodeEntry(&o.Entry, out)
|
||||
|
||||
case *fuseops.MkNodeOp:
|
||||
size := int(fusekernel.EntryOutSize(c.protocol))
|
||||
out := (*fusekernel.EntryOut)(m.Grow(size))
|
||||
convertChildInodeEntry(&o.Entry, out)
|
||||
fuseops.ConvertChildInodeEntry(&o.Entry, out)
|
||||
|
||||
case *fuseops.CreateFileOp:
|
||||
eSize := int(fusekernel.EntryOutSize(c.protocol))
|
||||
|
||||
e := (*fusekernel.EntryOut)(m.Grow(eSize))
|
||||
convertChildInodeEntry(&o.Entry, e)
|
||||
fuseops.ConvertChildInodeEntry(&o.Entry, e)
|
||||
|
||||
oo := (*fusekernel.OpenOut)(m.Grow(int(unsafe.Sizeof(fusekernel.OpenOut{}))))
|
||||
oo.Fh = uint64(o.Handle)
|
||||
|
@ -760,12 +763,12 @@ func (c *Connection) kernelResponseForOp(
|
|||
case *fuseops.CreateSymlinkOp:
|
||||
size := int(fusekernel.EntryOutSize(c.protocol))
|
||||
out := (*fusekernel.EntryOut)(m.Grow(size))
|
||||
convertChildInodeEntry(&o.Entry, out)
|
||||
fuseops.ConvertChildInodeEntry(&o.Entry, out)
|
||||
|
||||
case *fuseops.CreateLinkOp:
|
||||
size := int(fusekernel.EntryOutSize(c.protocol))
|
||||
out := (*fusekernel.EntryOut)(m.Grow(size))
|
||||
convertChildInodeEntry(&o.Entry, out)
|
||||
fuseops.ConvertChildInodeEntry(&o.Entry, out)
|
||||
|
||||
case *fuseops.RenameOp:
|
||||
// Empty response
|
||||
|
@ -983,64 +986,6 @@ func (c *Connection) kernelNotification(
|
|||
// General conversions
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func convertTime(t time.Time) (secs uint64, nsec uint32) {
|
||||
totalNano := t.UnixNano()
|
||||
secs = uint64(totalNano / 1e9)
|
||||
nsec = uint32(totalNano % 1e9)
|
||||
return secs, nsec
|
||||
}
|
||||
|
||||
func convertAttributes(
|
||||
inodeID fuseops.InodeID,
|
||||
in *fuseops.InodeAttributes,
|
||||
out *fusekernel.Attr) {
|
||||
out.Ino = uint64(inodeID)
|
||||
out.Size = in.Size
|
||||
out.Atime, out.AtimeNsec = convertTime(in.Atime)
|
||||
out.Mtime, out.MtimeNsec = convertTime(in.Mtime)
|
||||
out.Ctime, out.CtimeNsec = convertTime(in.Ctime)
|
||||
out.SetCrtime(convertTime(in.Crtime))
|
||||
out.Nlink = in.Nlink
|
||||
out.Uid = in.Uid
|
||||
out.Gid = in.Gid
|
||||
// round up to the nearest 512 boundary
|
||||
out.Blocks = (in.Size + 512 - 1) / 512
|
||||
|
||||
// Set the mode.
|
||||
out.Mode = fuseops.ConvertGolangMode(in.Mode)
|
||||
|
||||
if out.Mode & (syscall.S_IFCHR | syscall.S_IFBLK) != 0 {
|
||||
out.Rdev = in.Rdev
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an absolute cache expiration time to a relative time from now for
|
||||
// consumption by the fuse kernel module.
|
||||
func convertExpirationTime(t time.Time) (secs uint64, nsecs uint32) {
|
||||
// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
|
||||
// counts of nanoseconds (cf. http://goo.gl/EJupJV). So negative durations
|
||||
// are right out. There is no need to cap the positive magnitude, because
|
||||
// 2^64 seconds is well longer than the 2^63 ns range of time.Duration.
|
||||
d := t.Sub(time.Now())
|
||||
if d > 0 {
|
||||
secs = uint64(d / time.Second)
|
||||
nsecs = uint32((d % time.Second) / time.Nanosecond)
|
||||
}
|
||||
|
||||
return secs, nsecs
|
||||
}
|
||||
|
||||
func convertChildInodeEntry(
|
||||
in *fuseops.ChildInodeEntry,
|
||||
out *fusekernel.EntryOut) {
|
||||
out.Nodeid = uint64(in.Child)
|
||||
out.Generation = uint64(in.Generation)
|
||||
out.EntryValid, out.EntryValidNsec = convertExpirationTime(in.EntryExpiration)
|
||||
out.AttrValid, out.AttrValidNsec = convertExpirationTime(in.AttributesExpiration)
|
||||
|
||||
convertAttributes(in.Child, &in.Attributes, &out.Attr)
|
||||
}
|
||||
|
||||
func writeXattrSize(m *buffer.OutMessage, size uint32) {
|
||||
out := (*fusekernel.GetxattrOut)(m.Grow(int(unsafe.Sizeof(fusekernel.GetxattrOut{}))))
|
||||
out.Size = size
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright 2023 Vitaliy Filippov
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package fuseops
|
||||
|
||||
import (
|
||||
"time"
|
||||
"syscall"
|
||||
|
||||
"github.com/jacobsa/fuse/internal/fusekernel"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// General conversions
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func ConvertTime(t time.Time) (secs uint64, nsec uint32) {
|
||||
totalNano := t.UnixNano()
|
||||
secs = uint64(totalNano / 1e9)
|
||||
nsec = uint32(totalNano % 1e9)
|
||||
return secs, nsec
|
||||
}
|
||||
|
||||
func ConvertAttributes(
|
||||
inodeID InodeID,
|
||||
in *InodeAttributes,
|
||||
out *fusekernel.Attr) {
|
||||
out.Ino = uint64(inodeID)
|
||||
out.Size = in.Size
|
||||
out.Atime, out.AtimeNsec = ConvertTime(in.Atime)
|
||||
out.Mtime, out.MtimeNsec = ConvertTime(in.Mtime)
|
||||
out.Ctime, out.CtimeNsec = ConvertTime(in.Ctime)
|
||||
out.SetCrtime(ConvertTime(in.Crtime))
|
||||
out.Nlink = in.Nlink
|
||||
out.Uid = in.Uid
|
||||
out.Gid = in.Gid
|
||||
// round up to the nearest 512 boundary
|
||||
out.Blocks = (in.Size + 512 - 1) / 512
|
||||
|
||||
// Set the mode.
|
||||
out.Mode = ConvertGolangMode(in.Mode)
|
||||
|
||||
if out.Mode & (syscall.S_IFCHR | syscall.S_IFBLK) != 0 {
|
||||
out.Rdev = in.Rdev
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an absolute cache expiration time to a relative time from now for
|
||||
// consumption by the fuse kernel module.
|
||||
func ConvertExpirationTime(t time.Time) (secs uint64, nsecs uint32) {
|
||||
// Fuse represents durations as unsigned 64-bit counts of seconds and 32-bit
|
||||
// counts of nanoseconds (cf. http://goo.gl/EJupJV). So negative durations
|
||||
// are right out. There is no need to cap the positive magnitude, because
|
||||
// 2^64 seconds is well longer than the 2^63 ns range of time.Duration.
|
||||
d := t.Sub(time.Now())
|
||||
if d > 0 {
|
||||
secs = uint64(d / time.Second)
|
||||
nsecs = uint32((d % time.Second) / time.Nanosecond)
|
||||
}
|
||||
|
||||
return secs, nsecs
|
||||
}
|
||||
|
||||
func ConvertChildInodeEntry(
|
||||
in *ChildInodeEntry,
|
||||
out *fusekernel.EntryOut) {
|
||||
out.Nodeid = uint64(in.Child)
|
||||
out.Generation = uint64(in.Generation)
|
||||
out.EntryValid, out.EntryValidNsec = ConvertExpirationTime(in.EntryExpiration)
|
||||
out.AttrValid, out.AttrValidNsec = ConvertExpirationTime(in.AttributesExpiration)
|
||||
|
||||
ConvertAttributes(in.Child, &in.Attributes, &out.Attr)
|
||||
}
|
|
@ -562,12 +562,18 @@ type ReadDirOp struct {
|
|||
// offset, and return array offsets into that cached listing.
|
||||
Offset DirOffset
|
||||
|
||||
// Whether this operation is a READDIRPLUS
|
||||
//
|
||||
// If true, then the FS must return inode attributes and expiration time
|
||||
// along with each directory entry and increment its reference count.
|
||||
Plus bool
|
||||
|
||||
// The destination buffer, whose length gives the size of the read.
|
||||
//
|
||||
// The output data should consist of a sequence of FUSE directory entries in
|
||||
// the format generated by fuse_add_direntry (http://goo.gl/qCcHCV), which is
|
||||
// consumed by parse_dirfile (http://goo.gl/2WUmD2). Use fuseutil.WriteDirent
|
||||
// to generate this data.
|
||||
// or fuseutil.WriteDirentPlus to generate this data.
|
||||
//
|
||||
// Each entry returned exposes a directory offset to the user that may later
|
||||
// show up in ReadDirRequest.Offset. See notes on that field for more
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"unsafe"
|
||||
|
||||
"github.com/jacobsa/fuse/fuseops"
|
||||
"github.com/jacobsa/fuse/internal/fusekernel"
|
||||
)
|
||||
|
||||
type DirentType uint32
|
||||
|
@ -50,10 +51,18 @@ type Dirent struct {
|
|||
Type DirentType
|
||||
}
|
||||
|
||||
// Write the supplied directory entry intto the given buffer in the format
|
||||
// expected in fuseops.ReadFileOp.Data, returning the number of bytes written.
|
||||
// Write the supplied directory entry into the given buffer in the format
|
||||
// expected in fuseops.ReadDirOp.Data, returning the number of bytes written.
|
||||
// Return zero if the entry would not fit.
|
||||
func WriteDirent(buf []byte, d Dirent) (n int) {
|
||||
return WriteDirentPlus(buf, nil, d)
|
||||
}
|
||||
|
||||
// Write the supplied directory entry and, optionally, inode entry into the
|
||||
// given buffer in the format expected in fuseops.ReadDirOp.Data with enabled
|
||||
// READDIRPLUS capability, returning the number of bytes written.
|
||||
// Returns zero if the entry would not fit.
|
||||
func WriteDirentPlus(buf []byte, e *fuseops.ChildInodeEntry, d Dirent) (n int) {
|
||||
// We want to write bytes with the layout of fuse_dirent
|
||||
// (http://goo.gl/BmFxob) in host order. The struct must be aligned according
|
||||
// to FUSE_DIRENT_ALIGN (http://goo.gl/UziWvH), which dictates 8-byte
|
||||
|
@ -78,10 +87,21 @@ func WriteDirent(buf []byte, d Dirent) (n int) {
|
|||
|
||||
// Do we have enough room?
|
||||
totalLen := direntSize + len(d.Name) + padLen
|
||||
if e != nil {
|
||||
// READDIRPLUS was added in protocol 7.21, entry attributes were added in 7.9
|
||||
// So here EntryOut is always full-length
|
||||
totalLen += int(unsafe.Sizeof(fusekernel.EntryOut{}))
|
||||
}
|
||||
if totalLen > len(buf) {
|
||||
return n
|
||||
}
|
||||
|
||||
if e != nil {
|
||||
out := (*fusekernel.EntryOut)(unsafe.Pointer(&buf[n]))
|
||||
fuseops.ConvertChildInodeEntry(e, out)
|
||||
n += int(unsafe.Sizeof(fusekernel.EntryOut{}))
|
||||
}
|
||||
|
||||
// Write the header.
|
||||
de := fuse_dirent{
|
||||
ino: uint64(d.Inode),
|
||||
|
|
|
@ -417,6 +417,7 @@ const (
|
|||
OpNotifyReply = 41
|
||||
OpBatchForget = 42
|
||||
OpFallocate = 43
|
||||
OpReaddirplus = 44
|
||||
|
||||
// OS X
|
||||
OpSetvolname = 61
|
||||
|
|
|
@ -151,6 +151,11 @@ type MountConfig struct {
|
|||
// OpenDir calls at all (Linux >= 5.1):
|
||||
EnableNoOpendirSupport bool
|
||||
|
||||
// Tell the kernel to use READDIRPLUS.
|
||||
// Note that the implementation may still fall back to READDIR if the running
|
||||
// kernel doesn't have support for READDIRPLUS.
|
||||
UseReadDirPlus bool
|
||||
|
||||
// Disable FUSE default permissions.
|
||||
// This is useful for situations where the backing data store (e.g., S3) doesn't
|
||||
// actually utilise any form of qualifiable UNIX permissions.
|
||||
|
|
Loading…
Reference in New Issue