//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)