Validate sqlite header
This commit is contained in:
115
handle.go
115
handle.go
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"bazil.org/fuse"
|
||||
"bazil.org/fuse/fs"
|
||||
"github.com/benbjohnson/litestream/sqlite"
|
||||
)
|
||||
|
||||
var _ fs.HandleFlusher = (*Handle)(nil)
|
||||
@@ -25,7 +26,13 @@ var _ fs.HandleWriter = (*Handle)(nil)
|
||||
|
||||
// Handle represents a FUSE file handle.
|
||||
type Handle struct {
|
||||
f *os.File
|
||||
node *Node
|
||||
f *os.File
|
||||
}
|
||||
|
||||
// NewHandle returns a new instance of Handle.
|
||||
func NewHandle(n *Node, f *os.File) *Handle {
|
||||
return &Handle{node: n, f: f}
|
||||
}
|
||||
|
||||
// Release closes the underlying file descriptor.
|
||||
@@ -49,8 +56,36 @@ func (h *Handle) Write(ctx context.Context, req *fuse.WriteRequest, resp *fuse.W
|
||||
log.Printf("write: name=%s offset=%d n=%d", h.f.Name(), req.Offset, len(req.Data))
|
||||
println(HexDump(req.Data))
|
||||
|
||||
resp.Size, err = h.f.WriteAt(req.Data, req.Offset)
|
||||
return err
|
||||
if resp.Size, err = h.f.WriteAt(req.Data, req.Offset); err != nil {
|
||||
// TODO: Invalidate node DB state.
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if handle reference a managed database.
|
||||
db := h.node.DB()
|
||||
if db == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// If this is the DB file, update the DB state based on the header.
|
||||
if !sqlite.IsWALPath(h.node.Path()) {
|
||||
// TODO: Header write could theoretically occur anywhere in first 100 bytes.
|
||||
// If updating the header page, first validate it.
|
||||
if req.Offset == 0 {
|
||||
db.SetHeader(req.Data)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ignore if the DB is not in a valid state (header + wal enabled).
|
||||
if !db.Valid() {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Otherwise this is the WAL file so we should append the WAL data.
|
||||
db.AddPendingWALByteN(int64(len(req.Data)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Flush is called when a file handle is synced to disk. Implements fs.HandleFlusher.
|
||||
@@ -75,77 +110,3 @@ func (h *Handle) ReadDirAll(ctx context.Context) (ents []fuse.Dirent, err error)
|
||||
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 {
|
||||
log.Printf("dbg/lock %p %s -- %#v", h, h.f.Name(), req.Lock)
|
||||
if err := syscall.FcntlFlock(h.f.Fd(), unix.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),
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Printf("dbg/lockwait %p %s -- %#v", h, h.f.Name(), req.Lock)
|
||||
return syscall.FcntlFlock(h.f.Fd(), unix.F_OFD_SETLKW, &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),
|
||||
})
|
||||
}
|
||||
|
||||
// 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 {
|
||||
log.Printf("dbg/unlk %p %s -- %#v", h, h.f.Name(), req.Lock)
|
||||
return syscall.FcntlFlock(h.f.Fd(), unix.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 {
|
||||
log.Printf("dbg/querylock %p %s -- %#v", h, h.f.Name(), req.Lock)
|
||||
|
||||
flock_t := 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),
|
||||
}
|
||||
if err := syscall.FcntlFlock(h.f.Fd(), unix.F_OFD_GETLK, &flock_t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp.Lock = fuse.FileLock{
|
||||
Type: fuse.LockType(flock_t.Type),
|
||||
Start: uint64(flock_t.Start),
|
||||
End: uint64(flock_t.Start + flock_t.Len),
|
||||
PID: flock_t.Pid,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user