Commit a856a74e authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

nodefs: protect against double close

Access file descriptors under lock, and set to -1 on close. This
avoids confusing errors if Close() is doubly called
parent 4f40cf4c
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package nodefs package nodefs
import ( import (
"sync"
"syscall" "syscall"
"unsafe" "unsafe"
...@@ -14,7 +15,10 @@ import ( ...@@ -14,7 +15,10 @@ import (
type loopbackDirStream struct { type loopbackDirStream struct {
buf []byte buf []byte
todo []byte todo []byte
fd int
// Protects fd so we can guard against double close
mu sync.Mutex
fd int
} }
// NewLoopbackDirStream open a directory for reading as a DirStream // NewLoopbackDirStream open a directory for reading as a DirStream
...@@ -37,14 +41,23 @@ func NewLoopbackDirStream(name string) (DirStream, syscall.Errno) { ...@@ -37,14 +41,23 @@ func NewLoopbackDirStream(name string) (DirStream, syscall.Errno) {
} }
func (ds *loopbackDirStream) Close() { func (ds *loopbackDirStream) Close() {
syscall.Close(ds.fd) ds.mu.Lock()
defer ds.mu.Unlock()
if ds.fd != -1 {
syscall.Close(ds.fd)
ds.fd = -1
}
} }
func (ds *loopbackDirStream) HasNext() bool { func (ds *loopbackDirStream) HasNext() bool {
ds.mu.Lock()
defer ds.mu.Unlock()
return len(ds.todo) > 0 return len(ds.todo) > 0
} }
func (ds *loopbackDirStream) Next() (fuse.DirEntry, syscall.Errno) { func (ds *loopbackDirStream) Next() (fuse.DirEntry, syscall.Errno) {
ds.mu.Lock()
defer ds.mu.Unlock()
de := (*syscall.Dirent)(unsafe.Pointer(&ds.todo[0])) de := (*syscall.Dirent)(unsafe.Pointer(&ds.todo[0]))
nameBytes := ds.todo[unsafe.Offsetof(syscall.Dirent{}.Name):de.Reclen] nameBytes := ds.todo[unsafe.Offsetof(syscall.Dirent{}.Name):de.Reclen]
......
...@@ -6,6 +6,8 @@ package nodefs ...@@ -6,6 +6,8 @@ package nodefs
import ( import (
"context" "context"
"sync"
// "time" // "time"
"syscall" "syscall"
...@@ -21,6 +23,7 @@ func NewLoopbackFile(fd int) FileHandle { ...@@ -21,6 +23,7 @@ func NewLoopbackFile(fd int) FileHandle {
} }
type loopbackFile struct { type loopbackFile struct {
mu sync.Mutex
fd int fd int
} }
...@@ -39,21 +42,33 @@ var _ = (FileSetattrer)((*loopbackFile)(nil)) ...@@ -39,21 +42,33 @@ var _ = (FileSetattrer)((*loopbackFile)(nil))
var _ = (FileAllocater)((*loopbackFile)(nil)) var _ = (FileAllocater)((*loopbackFile)(nil))
func (f *loopbackFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) { func (f *loopbackFile) Read(ctx context.Context, buf []byte, off int64) (res fuse.ReadResult, errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
r := fuse.ReadResultFd(uintptr(f.fd), off, len(buf)) r := fuse.ReadResultFd(uintptr(f.fd), off, len(buf))
return r, OK return r, OK
} }
func (f *loopbackFile) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) { func (f *loopbackFile) Write(ctx context.Context, data []byte, off int64) (uint32, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
n, err := syscall.Pwrite(f.fd, data, off) n, err := syscall.Pwrite(f.fd, data, off)
return uint32(n), ToErrno(err) return uint32(n), ToErrno(err)
} }
func (f *loopbackFile) Release(ctx context.Context) syscall.Errno { func (f *loopbackFile) Release(ctx context.Context) syscall.Errno {
err := syscall.Close(f.fd) f.mu.Lock()
return ToErrno(err) defer f.mu.Unlock()
if f.fd != -1 {
err := syscall.Close(f.fd)
f.fd = -1
return ToErrno(err)
}
return syscall.EBADF
} }
func (f *loopbackFile) Flush(ctx context.Context) syscall.Errno { func (f *loopbackFile) Flush(ctx context.Context) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
// Since Flush() may be called for each dup'd fd, we don't // Since Flush() may be called for each dup'd fd, we don't
// want to really close the file, we just want to flush. This // want to really close the file, we just want to flush. This
// is achieved by closing a dup'd fd. // is achieved by closing a dup'd fd.
...@@ -67,6 +82,8 @@ func (f *loopbackFile) Flush(ctx context.Context) syscall.Errno { ...@@ -67,6 +82,8 @@ func (f *loopbackFile) Flush(ctx context.Context) syscall.Errno {
} }
func (f *loopbackFile) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) { func (f *loopbackFile) Fsync(ctx context.Context, flags uint32) (errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
r := ToErrno(syscall.Fsync(f.fd)) r := ToErrno(syscall.Fsync(f.fd))
return r return r
...@@ -79,6 +96,8 @@ const ( ...@@ -79,6 +96,8 @@ const (
) )
func (f *loopbackFile) Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (errno syscall.Errno) { func (f *loopbackFile) Getlk(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
flk := syscall.Flock_t{} flk := syscall.Flock_t{}
lk.ToFlockT(&flk) lk.ToFlockT(&flk)
errno = ToErrno(syscall.FcntlFlock(uintptr(f.fd), _OFD_GETLK, &flk)) errno = ToErrno(syscall.FcntlFlock(uintptr(f.fd), _OFD_GETLK, &flk))
...@@ -95,6 +114,8 @@ func (f *loopbackFile) Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLo ...@@ -95,6 +114,8 @@ func (f *loopbackFile) Setlkw(ctx context.Context, owner uint64, lk *fuse.FileLo
} }
func (f *loopbackFile) setLock(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, blocking bool) (errno syscall.Errno) { func (f *loopbackFile) setLock(ctx context.Context, owner uint64, lk *fuse.FileLock, flags uint32, blocking bool) (errno syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
if (flags & fuse.FUSE_LK_FLOCK) != 0 { if (flags & fuse.FUSE_LK_FLOCK) != 0 {
var op int var op int
switch lk.Typ { switch lk.Typ {
...@@ -133,6 +154,8 @@ func (f *loopbackFile) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fus ...@@ -133,6 +154,8 @@ func (f *loopbackFile) Setattr(ctx context.Context, in *fuse.SetAttrIn, out *fus
} }
func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) syscall.Errno { func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
var errno syscall.Errno var errno syscall.Errno
if mode, ok := in.GetMode(); ok { if mode, ok := in.GetMode(); ok {
errno = ToErrno(syscall.Fchmod(f.fd, mode)) errno = ToErrno(syscall.Fchmod(f.fd, mode))
...@@ -187,6 +210,8 @@ func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) syscall. ...@@ -187,6 +210,8 @@ func (f *loopbackFile) setAttr(ctx context.Context, in *fuse.SetAttrIn) syscall.
} }
func (f *loopbackFile) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno { func (f *loopbackFile) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
st := syscall.Stat_t{} st := syscall.Stat_t{}
err := syscall.Fstat(f.fd, &st) err := syscall.Fstat(f.fd, &st)
if err != nil { if err != nil {
...@@ -198,6 +223,8 @@ func (f *loopbackFile) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Err ...@@ -198,6 +223,8 @@ func (f *loopbackFile) Getattr(ctx context.Context, a *fuse.AttrOut) syscall.Err
} }
func (f *loopbackFile) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) { func (f *loopbackFile) Lseek(ctx context.Context, off uint64, whence uint32) (uint64, syscall.Errno) {
f.mu.Lock()
defer f.mu.Unlock()
n, err := unix.Seek(f.fd, int64(off), int(whence)) n, err := unix.Seek(f.fd, int64(off), int(whence))
return uint64(n), ToErrno(err) return uint64(n), ToErrno(err)
} }
...@@ -13,6 +13,8 @@ import ( ...@@ -13,6 +13,8 @@ import (
) )
func (f *loopbackFile) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32) syscall.Errno { func (f *loopbackFile) Allocate(ctx context.Context, off uint64, sz uint64, mode uint32) syscall.Errno {
f.mu.Lock()
defer f.mu.Unlock()
err := syscall.Fallocate(f.fd, mode, int64(off), int64(sz)) err := syscall.Fallocate(f.fd, mode, int64(off), int64(sz))
if err != nil { if err != nil {
return ToErrno(err) return ToErrno(err)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment