133 lines
3.8 KiB
Go
133 lines
3.8 KiB
Go
package litestream
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"os"
|
|
"sort"
|
|
"syscall"
|
|
|
|
"bazil.org/fuse"
|
|
"bazil.org/fuse/fs"
|
|
)
|
|
|
|
const (
|
|
F_OFD_GETLK = 0x24
|
|
F_OFD_SETLK = 0x25
|
|
F_OFD_SETLKW = 0x26
|
|
)
|
|
|
|
var _ fs.HandleFlockLocker = (*Handle)(nil)
|
|
var _ fs.HandleFlusher = (*Handle)(nil)
|
|
var _ fs.HandleLocker = (*Handle)(nil)
|
|
var _ fs.HandlePOSIXLocker = (*Handle)(nil)
|
|
var _ fs.HandleReadDirAller = (*Handle)(nil)
|
|
var _ fs.HandleReader = (*Handle)(nil)
|
|
var _ fs.HandleReleaser = (*Handle)(nil)
|
|
var _ fs.HandleWriter = (*Handle)(nil)
|
|
|
|
// var _ fs.HandleReadAller = (*Handle)(nil)
|
|
|
|
// Handle represents a FUSE file handle.
|
|
type Handle struct {
|
|
f *os.File
|
|
}
|
|
|
|
// Release closes the underlying file descriptor.
|
|
func (h *Handle) Release(ctx context.Context, req *fuse.ReleaseRequest) (err error) {
|
|
return h.f.Close()
|
|
}
|
|
|
|
// Read reads data from a given offset in the underlying file.
|
|
func (h *Handle) Read(ctx context.Context, req *fuse.ReadRequest, resp *fuse.ReadResponse) (err error) {
|
|
buf := make([]byte, req.Size)
|
|
n, err := h.f.ReadAt(buf, req.Offset)
|
|
if err != nil && err != io.EOF {
|
|
return err
|
|
}
|
|
resp.Data = buf[:n]
|
|
return nil
|
|
}
|
|
|
|
// Write writes data at a given offset to the underlying file.
|
|
func (h *Handle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.WriteResponse) (err error) {
|
|
resp.Size, err = h.f.WriteAt(req.Data, req.Offset)
|
|
return err
|
|
}
|
|
|
|
// Flush is called when a file handle is synced to disk. Implements fs.HandleFlusher.
|
|
func (h *Handle) Flush(ctx context.Context, req *fuse.FlushRequest) (err error) {
|
|
return h.f.Sync()
|
|
}
|
|
|
|
// ReadDirAll returns a list of all entries in a directory. Implements fs.HandleReadDirAller.
|
|
func (h *Handle) ReadDirAll(ctx context.Context) (ents []fuse.Dirent, err error) {
|
|
fis, err := h.f.Readdir(-1)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Convert FileInfo objects to FUSE directory entries.
|
|
ents = make([]fuse.Dirent, len(fis))
|
|
for i, fi := range fis {
|
|
statt := fi.Sys().(*syscall.Stat_t)
|
|
ents[i] = fuse.Dirent{Inode: statt.Ino, Name: fi.Name()}
|
|
}
|
|
|
|
sort.Slice(ents, func(i, j int) bool { return ents[i].Name < ents[j].Name })
|
|
return ents, nil
|
|
}
|
|
|
|
// Lock tries to acquire a lock on a byte range of the node. If a
|
|
// conflicting lock is already held, returns syscall.EAGAIN.
|
|
func (h *Handle) Lock(ctx context.Context, req *fuse.LockRequest) error {
|
|
return h.lock(ctx, req)
|
|
}
|
|
|
|
// LockWait acquires a lock on a byte range of the node, waiting
|
|
// until the lock can be obtained (or context is canceled).
|
|
func (h *Handle) LockWait(ctx context.Context, req *fuse.LockWaitRequest) error {
|
|
return h.lock(ctx, (*fuse.LockRequest)(req))
|
|
}
|
|
|
|
// Unlock releases the lock on a byte range of the node. Locks can
|
|
// be released also implicitly, see HandleFlockLocker and
|
|
// HandlePOSIXLocker.
|
|
func (h *Handle) Unlock(ctx context.Context, req *fuse.UnlockRequest) error {
|
|
return h.lock(ctx, (*fuse.LockRequest)(req))
|
|
|
|
}
|
|
|
|
func (h *Handle) lock(ctx context.Context, req *fuse.LockRequest) error {
|
|
return syscall.FcntlFlock(h.f.Fd(), F_OFD_SETLK, &syscall.Flock_t{
|
|
Type: int16(req.Lock.Type),
|
|
Whence: io.SeekStart,
|
|
Start: int64(req.Lock.Start),
|
|
Len: int64(req.Lock.End) - int64(req.Lock.Start),
|
|
})
|
|
}
|
|
|
|
// QueryLock returns the current state of locks held for the byte
|
|
// range of the node.
|
|
//
|
|
// See QueryLockRequest for details on how to respond.
|
|
//
|
|
// To simplify implementing this method, resp.Lock is prefilled to
|
|
// have Lock.Type F_UNLCK, and the whole struct should be
|
|
// overwritten for in case of conflicting locks.
|
|
func (h *Handle) QueryLock(ctx context.Context, req *fuse.QueryLockRequest, resp *fuse.QueryLockResponse) error {
|
|
// type QueryLockRequest struct {
|
|
// Header
|
|
// Handle HandleID
|
|
// LockOwner LockOwner
|
|
// Lock FileLock
|
|
// LockFlags LockFlags
|
|
// }
|
|
|
|
// type QueryLockResponse struct {
|
|
// Lock FileLock
|
|
// }
|
|
|
|
panic("TODO")
|
|
}
|