diff --git a/connection.go b/connection.go index 729f161c..3dd17e92 100644 --- a/connection.go +++ b/connection.go @@ -304,6 +304,17 @@ func (c *Connection) finishOp( // LOCKS_EXCLUDED(c.mu) func (c *Connection) handleInterrupt(fuseID uint64) { + // Allow fuse library clients to ignore interupt signals. + // NOTE(bjornleffler): Golang issues lots of SIGURG signals, which are + // interpreted as interuption signals. + // https://github.com/golang/proposal/blob/master/design/24543-non-cooperative-preemption.md + // It would have been preferrable to ignore SIGURG, but I couldn't + // figure out how to do that. From kernel documentation (https://goo.gl/H55Dnr): + // "The userspace filesystem may ignore the INTERRUPT requests entirely" + if c.cfg.IgnoreInterrupts { + return + } + c.mu.Lock() defer c.mu.Unlock() diff --git a/fuseops/filetype.go b/fuseops/filetype.go new file mode 100644 index 00000000..09914381 --- /dev/null +++ b/fuseops/filetype.go @@ -0,0 +1,62 @@ +// Copyright 2023 Google LLC +// +// 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 +// +// https://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 ( + "fmt" +) + +type Filetype int + +const ( + NoFiletype Filetype = 0 + RegularFiletype = 1 + DirectoryFiletype = 2 + SymlinkFiletype = 3 +) + +const ( + // Type values used in GCS metadata. + NoFiletypeString = "none" + RegularFiletypeString = "file" + DirectoryFiletypeString = "directory" + SymlinkFiletypeString = "symlink" +) + +func ParseFiletype(value string) (Filetype, error) { + switch value { + case NoFiletypeString: + return NoFiletype, nil + case RegularFiletypeString: + return RegularFiletype, nil + case DirectoryFiletypeString: + return DirectoryFiletype, nil + case SymlinkFiletypeString: + return SymlinkFiletype, nil + } + return NoFiletype, fmt.Errorf("Failed to parse file type %s", value) +} + +func (filetype Filetype) String() string { + switch filetype { + case RegularFiletype: + return RegularFiletypeString + case DirectoryFiletype: + return DirectoryFiletypeString + case SymlinkFiletype: + return SymlinkFiletypeString + } + return NoFiletypeString +} diff --git a/fuseops/ops.go b/fuseops/ops.go index 0cbb202c..820cf0ac 100644 --- a/fuseops/ops.go +++ b/fuseops/ops.go @@ -595,6 +595,15 @@ type ReadDirOp struct { OpContext OpContext } +// Read all entries from a directory. +type ListDirOp struct { + // The directory inode that we are reading. + Inode InodeID + + // All entries from the directory listing. + Entries []Dirent +} + // Release a previously-minted directory handle. The kernel sends this when // there are no more references to an open directory: all file descriptors are // closed and all memory mappings are unmapped. diff --git a/fuseops/simple_types.go b/fuseops/simple_types.go index 5c6b9cad..24a2ac2c 100644 --- a/fuseops/simple_types.go +++ b/fuseops/simple_types.go @@ -17,6 +17,7 @@ package fuseops import ( "fmt" "os" + "syscall" "time" "github.com/jacobsa/fuse/internal/fusekernel" @@ -59,6 +60,7 @@ func init() { // InodeAttributes contains attributes for a file or directory inode. It // corresponds to struct inode (cf. http://goo.gl/tvYyQt). type InodeAttributes struct { + Type Filetype Size uint64 // The number of incoming hard links to this inode. @@ -222,3 +224,35 @@ type ChildInodeEntry struct { // default. See notes on MountConfig.EnableVnodeCaching for more. EntryExpiration time.Time } + +type DirentType uint32 + +const ( + DT_Unknown DirentType = 0 + DT_Socket DirentType = syscall.DT_SOCK + DT_Link DirentType = syscall.DT_LNK + DT_File DirentType = syscall.DT_REG + DT_Block DirentType = syscall.DT_BLK + DT_Directory DirentType = syscall.DT_DIR + DT_Char DirentType = syscall.DT_CHR + DT_FIFO DirentType = syscall.DT_FIFO +) + +// A struct representing an entry within a directory file, describing a child. +// See notes on ReadDirOp and on WriteDirent for details. +type Dirent struct { + // The (opaque) offset within the directory file of the entry following this + // one. See notes on ReadDirOp.Offset for details. + Offset DirOffset + + // The inode of the child file or directory, and its name within the parent. + Inode InodeID + Name string + + // The type of the child. The zero value (DT_Unknown) is legal, but means + // that the kernel will need to call GetAttr when the type is needed. + Type DirentType + + // Inode attributes for the entry. + Attributes InodeAttributes +} diff --git a/fuseutil/dirent.go b/fuseutil/dirent.go index 2786efc6..89167793 100644 --- a/fuseutil/dirent.go +++ b/fuseutil/dirent.go @@ -15,45 +15,15 @@ package fuseutil import ( - "syscall" "unsafe" "github.com/jacobsa/fuse/fuseops" ) -type DirentType uint32 - -const ( - DT_Unknown DirentType = 0 - DT_Socket DirentType = syscall.DT_SOCK - DT_Link DirentType = syscall.DT_LNK - DT_File DirentType = syscall.DT_REG - DT_Block DirentType = syscall.DT_BLK - DT_Directory DirentType = syscall.DT_DIR - DT_Char DirentType = syscall.DT_CHR - DT_FIFO DirentType = syscall.DT_FIFO -) - -// A struct representing an entry within a directory file, describing a child. -// See notes on fuseops.ReadDirOp and on WriteDirent for details. -type Dirent struct { - // The (opaque) offset within the directory file of the entry following this - // one. See notes on fuseops.ReadDirOp.Offset for details. - Offset fuseops.DirOffset - - // The inode of the child file or directory, and its name within the parent. - Inode fuseops.InodeID - Name string - - // The type of the child. The zero value (DT_Unknown) is legal, but means - // that the kernel will need to call GetAttr when the type is needed. - Type DirentType -} - // Write the supplied directory entry into the given buffer in the format // expected in fuseops.ReadFileOp.Data, returning the number of bytes written. // Return zero if the entry would not fit. -func WriteDirent(buf []byte, d Dirent) (n int) { +func WriteDirent(buf []byte, d fuseops.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 diff --git a/fuseutil/file_system.go b/fuseutil/file_system.go index 5eab68a3..bab06a37 100644 --- a/fuseutil/file_system.go +++ b/fuseutil/file_system.go @@ -51,6 +51,7 @@ type FileSystem interface { OpenDir(context.Context, *fuseops.OpenDirOp) error ReadDir(context.Context, *fuseops.ReadDirOp) error ReleaseDirHandle(context.Context, *fuseops.ReleaseDirHandleOp) error + ListDir(context.Context, *fuseops.ListDirOp) error OpenFile(context.Context, *fuseops.OpenFileOp) error ReadFile(context.Context, *fuseops.ReadFileOp) error WriteFile(context.Context, *fuseops.WriteFileOp) error diff --git a/fuseutil/not_implemented_file_system.go b/fuseutil/not_implemented_file_system.go index cfb116c6..e7c33a22 100644 --- a/fuseutil/not_implemented_file_system.go +++ b/fuseutil/not_implemented_file_system.go @@ -132,6 +132,12 @@ func (fs *NotImplementedFileSystem) ReleaseDirHandle( return fuse.ENOSYS } +func (fs *NotImplementedFileSystem) ListDir( + ctx context.Context, + op *fuseops.ListDirOp) error { + return fuse.ENOSYS +} + func (fs *NotImplementedFileSystem) OpenFile( ctx context.Context, op *fuseops.OpenFileOp) error { diff --git a/mount_config.go b/mount_config.go index 86be126d..3c5b1db0 100644 --- a/mount_config.go +++ b/mount_config.go @@ -185,6 +185,9 @@ type MountConfig struct { // Flag to enable async reads that are received from // the kernel EnableAsyncReads bool + + // Flag to allow fuse library clients to ignore interrupts. + IgnoreInterrupts bool } // Create a map containing all of the key=value mount options to be given to