151 lines
4.9 KiB
Go
151 lines
4.9 KiB
Go
//go:build linux || (darwin && amd64)
|
|
|
|
package mount2
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"syscall"
|
|
|
|
fusefs "github.com/hanwen/go-fuse/v2/fs"
|
|
"github.com/hanwen/go-fuse/v2/fuse"
|
|
"github.com/rclone/rclone/fs/log"
|
|
"github.com/rclone/rclone/vfs"
|
|
)
|
|
|
|
// FileHandle is a resource identifier for opened files. Usually, a
|
|
// FileHandle should implement some of the FileXxxx interfaces.
|
|
//
|
|
// All of the FileXxxx operations can also be implemented at the
|
|
// InodeEmbedder level, for example, one can implement NodeReader
|
|
// instead of FileReader.
|
|
//
|
|
// FileHandles are useful in two cases: First, if the underlying
|
|
// storage systems needs a handle for reading/writing. This is the
|
|
// case with Unix system calls, which need a file descriptor (See also
|
|
// the function `NewLoopbackFile`). Second, it is useful for
|
|
// implementing files whose contents are not tied to an inode. For
|
|
// example, a file like `/proc/interrupts` has no fixed content, but
|
|
// changes on each open call. This means that each file handle must
|
|
// have its own view of the content; this view can be tied to a
|
|
// FileHandle. Files that have such dynamic content should return the
|
|
// FOPEN_DIRECT_IO flag from their `Open` method. See directio_test.go
|
|
// for an example.
|
|
type FileHandle struct {
|
|
h vfs.Handle
|
|
fsys *FS
|
|
}
|
|
|
|
// Create a new FileHandle
|
|
func newFileHandle(h vfs.Handle, fsys *FS) *FileHandle {
|
|
return &FileHandle{
|
|
h: h,
|
|
fsys: fsys,
|
|
}
|
|
}
|
|
|
|
// Check interface satisfied
|
|
var _ fusefs.FileHandle = (*FileHandle)(nil)
|
|
|
|
// The String method is for debug printing.
|
|
func (f *FileHandle) String() string {
|
|
return fmt.Sprintf("fh=%p(%s)", f, f.h.Node().Path())
|
|
}
|
|
|
|
// Read data from a file. The data should be returned as
|
|
// ReadResult, which may be constructed from the incoming
|
|
// `dest` buffer.
|
|
func (f *FileHandle) Read(ctx context.Context, dest []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) {
|
|
var n int
|
|
var err error
|
|
defer log.Trace(f, "off=%d", off)("n=%d, off=%d, errno=%v", &n, &off, &errno)
|
|
n, err = f.h.ReadAt(dest, off)
|
|
if err == io.EOF {
|
|
err = nil
|
|
}
|
|
return fuse.ReadResultData(dest[:n]), translateError(err)
|
|
}
|
|
|
|
var _ fusefs.FileReader = (*FileHandle)(nil)
|
|
|
|
// Write the data into the file handle at given offset. After
|
|
// returning, the data will be reused and may not referenced.
|
|
func (f *FileHandle) Write(ctx context.Context, data []byte, off int64) (written uint32, errno syscall.Errno) {
|
|
var n int
|
|
var err error
|
|
defer log.Trace(f, "off=%d", off)("n=%d, off=%d, errno=%v", &n, &off, &errno)
|
|
n, err = f.h.WriteAt(data, off)
|
|
return uint32(n), translateError(err)
|
|
}
|
|
|
|
var _ fusefs.FileWriter = (*FileHandle)(nil)
|
|
|
|
// Flush is called for the close(2) call on a file descriptor. In case
|
|
// of a descriptor that was duplicated using dup(2), it may be called
|
|
// more than once for the same FileHandle.
|
|
func (f *FileHandle) Flush(ctx context.Context) syscall.Errno {
|
|
return translateError(f.h.Flush())
|
|
}
|
|
|
|
var _ fusefs.FileFlusher = (*FileHandle)(nil)
|
|
|
|
// Release is called to before a FileHandle is forgotten. The
|
|
// kernel ignores the return value of this method,
|
|
// so any cleanup that requires specific synchronization or
|
|
// could fail with I/O errors should happen in Flush instead.
|
|
func (f *FileHandle) Release(ctx context.Context) syscall.Errno {
|
|
return translateError(f.h.Release())
|
|
}
|
|
|
|
var _ fusefs.FileReleaser = (*FileHandle)(nil)
|
|
|
|
// Fsync is a signal to ensure writes to the Inode are flushed
|
|
// to stable storage.
|
|
func (f *FileHandle) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
|
|
return translateError(f.h.Sync())
|
|
}
|
|
|
|
var _ fusefs.FileFsyncer = (*FileHandle)(nil)
|
|
|
|
// Getattr reads attributes for an Inode. The library will ensure that
|
|
// Mode and Ino are set correctly. For files that are not opened with
|
|
// FOPEN_DIRECTIO, Size should be set so it can be read correctly. If
|
|
// returning zeroed permissions, the default behavior is to change the
|
|
// mode of 0755 (directory) or 0644 (files). This can be switched off
|
|
// with the Options.NullPermissions setting. If blksize is unset, 4096
|
|
// is assumed, and the 'blocks' field is set accordingly.
|
|
func (f *FileHandle) Getattr(ctx context.Context, out *fuse.AttrOut) (errno syscall.Errno) {
|
|
defer log.Trace(f, "")("attr=%v, errno=%v", &out, &errno)
|
|
f.fsys.setAttrOut(f.h.Node(), out)
|
|
return 0
|
|
}
|
|
|
|
var _ fusefs.FileGetattrer = (*FileHandle)(nil)
|
|
|
|
// Setattr sets attributes for an Inode.
|
|
func (f *FileHandle) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fuse.AttrOut) (errno syscall.Errno) {
|
|
defer log.Trace(f, "in=%v", in)("attr=%v, errno=%v", &out, &errno)
|
|
var err error
|
|
f.fsys.setAttrOut(f.h.Node(), out)
|
|
size, ok := in.GetSize()
|
|
if ok {
|
|
err = f.h.Truncate(int64(size))
|
|
if err != nil {
|
|
return translateError(err)
|
|
}
|
|
out.Attr.Size = size
|
|
}
|
|
mtime, ok := in.GetMTime()
|
|
if ok {
|
|
err = f.h.Node().SetModTime(mtime)
|
|
if err != nil {
|
|
return translateError(err)
|
|
}
|
|
out.Attr.Mtime = uint64(mtime.Unix())
|
|
out.Attr.Mtimensec = uint32(mtime.Nanosecond())
|
|
}
|
|
return 0
|
|
}
|
|
|
|
var _ fusefs.FileSetattrer = (*FileHandle)(nil)
|