Commit 1eca9006 authored by Yufeng Cheng's avatar Yufeng Cheng Committed by Han-Wen Nienhuys

Implement file locking: getlk/setlk/setlkw/flock

Co-authored-by: default avatarPaul Warren <paul.warren@emc.com>
Co-authored-by: default avatarMaria Shaldibina <mshaldibina@pivotal.io>
Co-authored-by: default avatarHan-Wen Nienhuys <hanwen@google.com>
parent 291273cb
...@@ -7,10 +7,13 @@ Haitao Li <lihaitao@gmail.com> ...@@ -7,10 +7,13 @@ Haitao Li <lihaitao@gmail.com>
Jakob Unterwurzacher <jakobunt@gmail.com> Jakob Unterwurzacher <jakobunt@gmail.com>
James D. Nurmi <james@abneptis.com> James D. Nurmi <james@abneptis.com>
Jeff <leterip@me.com> Jeff <leterip@me.com>
Kaoet Ibe <kaoet.ibe@outlook.com>
Logan Hanks <logan@bitcasa.com> Logan Hanks <logan@bitcasa.com>
Maria Shaldibina <mshaldibina@pivotal.io>
Nick Cooper <gh@smoogle.org> Nick Cooper <gh@smoogle.org>
Patrick Crosby <pcrosby@gmail.com> Patrick Crosby <pcrosby@gmail.com>
Paul Jolly <paul@myitcv.org.uk> Paul Jolly <paul@myitcv.org.uk>
Paul Warren <paul.warren@emc.com>
Shayan Pooya <shayan@arista.com> Shayan Pooya <shayan@arista.com>
Valient Gough <vgough@pobox.com> Valient Gough <vgough@pobox.com>
Yongwoo Park <nnnlife@gmail.com> Yongwoo Park <nnnlife@gmail.com>
...@@ -134,7 +134,10 @@ type RawFileSystem interface { ...@@ -134,7 +134,10 @@ type RawFileSystem interface {
Open(input *OpenIn, out *OpenOut) (status Status) Open(input *OpenIn, out *OpenOut) (status Status)
Read(input *ReadIn, buf []byte) (ReadResult, Status) Read(input *ReadIn, buf []byte) (ReadResult, Status)
Flock(input *FlockIn, flags int) (code Status) // File locking
GetLk(input *LkIn, out *LkOut) (code Status)
SetLk(input *LkIn) (code Status)
SetLkw(input *LkIn) (code Status)
Release(input *ReleaseIn) Release(input *ReleaseIn)
Write(input *WriteIn, data []byte) (written uint32, code Status) Write(input *WriteIn, data []byte) (written uint32, code Status)
......
...@@ -117,7 +117,15 @@ func (fs *defaultRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta ...@@ -117,7 +117,15 @@ func (fs *defaultRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta
return nil, ENOSYS return nil, ENOSYS
} }
func (fs *defaultRawFileSystem) Flock(input *FlockIn, flags int) Status { func (fs *defaultRawFileSystem) GetLk(in *LkIn, out *LkOut) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) SetLk(in *LkIn) (code Status) {
return ENOSYS
}
func (fs *defaultRawFileSystem) SetLkw(in *LkIn) (code Status) {
return ENOSYS return ENOSYS
} }
......
...@@ -159,9 +159,19 @@ func (fs *lockingRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta ...@@ -159,9 +159,19 @@ func (fs *lockingRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta
return fs.RawFS.Read(input, buf) return fs.RawFS.Read(input, buf)
} }
func (fs *lockingRawFileSystem) Flock(input *FlockIn, flags int) Status { func (fs *lockingRawFileSystem) GetLk(in *LkIn, out *LkOut) (code Status) {
defer fs.locked()() defer fs.locked()()
return fs.RawFS.Flock(input, flags) return fs.RawFS.GetLk(in, out)
}
func (fs *lockingRawFileSystem) SetLk(in *LkIn) (code Status) {
defer fs.locked()()
return fs.RawFS.SetLk(in)
}
func (fs *lockingRawFileSystem) SetLkw(in *LkIn) (code Status) {
defer fs.locked()()
return fs.RawFS.SetLkw(in)
} }
func (fs *lockingRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) { func (fs *lockingRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) {
......
...@@ -103,6 +103,17 @@ type Node interface { ...@@ -103,6 +103,17 @@ type Node interface {
SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status
ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status) ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status)
// File locking
//
// GetLk returns existing lock information for file.
GetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status)
// Sets or clears the lock described by lk on file.
SetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status)
// Sets or clears the lock described by lk. This call blocks until the operation can be completed.
SetLkw(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status)
// Attributes // Attributes
GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status) GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status)
Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status) Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status)
...@@ -131,7 +142,10 @@ type File interface { ...@@ -131,7 +142,10 @@ type File interface {
Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status)
Write(data []byte, off int64) (written uint32, code fuse.Status) Write(data []byte, off int64) (written uint32, code fuse.Status)
Flock(flags int) fuse.Status // File locking
GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (code fuse.Status)
SetLk(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status)
SetLkw(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status)
// Flush is called for close() call on a file descriptor. In // Flush is called for close() call on a file descriptor. In
// case of duplicated descriptor, it may be called more than // case of duplicated descriptor, it may be called more than
......
...@@ -37,7 +37,18 @@ func (f *defaultFile) Write(data []byte, off int64) (uint32, fuse.Status) { ...@@ -37,7 +37,18 @@ func (f *defaultFile) Write(data []byte, off int64) (uint32, fuse.Status) {
return 0, fuse.ENOSYS return 0, fuse.ENOSYS
} }
func (f *defaultFile) Flock(flags int) fuse.Status { return fuse.ENOSYS } func (f *defaultFile) GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *defaultFile) SetLk(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *defaultFile) SetLkw(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status) {
return fuse.ENOSYS
}
func (f *defaultFile) Flush() fuse.Status { func (f *defaultFile) Flush() fuse.Status {
return fuse.OK return fuse.OK
} }
......
...@@ -137,6 +137,18 @@ func (n *defaultNode) GetAttr(out *fuse.Attr, file File, context *fuse.Context) ...@@ -137,6 +137,18 @@ func (n *defaultNode) GetAttr(out *fuse.Attr, file File, context *fuse.Context)
return fuse.OK return fuse.OK
} }
func (n *defaultNode) GetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) SetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) SetLkw(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS
}
func (n *defaultNode) Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status) { func (n *defaultNode) Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status) {
return fuse.ENOSYS return fuse.ENOSYS
} }
......
...@@ -167,12 +167,56 @@ func (f *loopbackFile) Fsync(flags int) (code fuse.Status) { ...@@ -167,12 +167,56 @@ func (f *loopbackFile) Fsync(flags int) (code fuse.Status) {
return r return r
} }
func (f *loopbackFile) Flock(flags int) fuse.Status { const (
f.lock.Lock() F_OFD_GETLK = 36
r := fuse.ToStatus(syscall.Flock(int(f.File.Fd()), flags)) F_OFD_SETLK = 37
f.lock.Unlock() F_OFD_SETLKW = 38
)
return r func (f *loopbackFile) GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (code fuse.Status) {
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
code = fuse.ToStatus(syscall.FcntlFlock(f.File.Fd(), F_OFD_GETLK, &flk))
out.FromFlockT(&flk)
return
}
func (f *loopbackFile) SetLk(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status) {
return f.setLock(owner, lk, flags, false)
}
func (f *loopbackFile) SetLkw(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status) {
return f.setLock(owner, lk, flags, true)
}
func (f *loopbackFile) setLock(owner uint64, lk *fuse.FileLock, flags uint32, blocking bool) (code fuse.Status) {
if (flags & fuse.FUSE_LK_FLOCK) != 0 {
var op int
switch lk.Typ {
case syscall.F_RDLCK:
op = syscall.LOCK_SH
case syscall.F_WRLCK:
op = syscall.LOCK_EX
case syscall.F_UNLCK:
op = syscall.LOCK_UN
default:
return fuse.EINVAL
}
if !blocking {
op |= syscall.LOCK_NB
}
return fuse.ToStatus(syscall.Flock(int(f.File.Fd()), op))
} else {
flk := syscall.Flock_t{}
lk.ToFlockT(&flk)
var op int
if blocking {
op = F_OFD_SETLKW
} else {
op = F_OFD_SETLK
}
return fuse.ToStatus(syscall.FcntlFlock(f.File.Fd(), op, &flk))
}
} }
func (f *loopbackFile) Truncate(size uint64) fuse.Status { func (f *loopbackFile) Truncate(size uint64) fuse.Status {
......
...@@ -452,15 +452,25 @@ func (c *rawBridge) Read(input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse. ...@@ -452,15 +452,25 @@ func (c *rawBridge) Read(input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.
return node.Node().Read(f, buf, int64(input.Offset), &input.Context) return node.Node().Read(f, buf, int64(input.Offset), &input.Context)
} }
func (c *rawBridge) Flock(input *fuse.FlockIn, flags int) fuse.Status { func (c *rawBridge) GetLk(input *fuse.LkIn, out *fuse.LkOut) (code fuse.Status) {
node := c.toInode(input.NodeId) n := c.toInode(input.NodeId)
opened := node.mount.getOpenedFile(input.Fh) opened := n.mount.getOpenedFile(input.Fh)
if opened != nil { return n.fsInode.GetLk(opened, input.Owner, &input.Lk, input.LkFlags, &out.Lk, &input.Context)
return opened.WithFlags.File.Flock(flags) }
}
func (c *rawBridge) SetLk(input *fuse.LkIn) (code fuse.Status) {
n := c.toInode(input.NodeId)
opened := n.mount.getOpenedFile(input.Fh)
return n.fsInode.SetLk(opened, input.Owner, &input.Lk, input.LkFlags, &input.Context)
}
func (c *rawBridge) SetLkw(input *fuse.LkIn) (code fuse.Status) {
n := c.toInode(input.NodeId)
opened := n.mount.getOpenedFile(input.Fh)
return fuse.EBADF return n.fsInode.SetLkw(opened, input.Owner, &input.Lk, input.LkFlags, &input.Context)
} }
func (c *rawBridge) StatFs(header *fuse.InHeader, out *fuse.StatfsOut) fuse.Status { func (c *rawBridge) StatFs(header *fuse.InHeader, out *fuse.StatfsOut) fuse.Status {
......
...@@ -54,10 +54,22 @@ func (f *lockingFile) Flush() fuse.Status { ...@@ -54,10 +54,22 @@ func (f *lockingFile) Flush() fuse.Status {
return f.file.Flush() return f.file.Flush()
} }
func (f *lockingFile) Flock(flags int) fuse.Status { func (f *lockingFile) GetLk(owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock) (code fuse.Status) {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
return f.file.Flock(flags) return f.file.GetLk(owner, lk, flags, out)
}
func (f *lockingFile) SetLk(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.SetLk(owner, lk, flags)
}
func (f *lockingFile) SetLkw(owner uint64, lk *fuse.FileLock, flags uint32) (code fuse.Status) {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.SetLkw(owner, lk, flags)
} }
func (f *lockingFile) Release() { func (f *lockingFile) Release() {
......
...@@ -83,7 +83,7 @@ func doInit(server *Server, req *request) { ...@@ -83,7 +83,7 @@ func doInit(server *Server, req *request) {
server.reqMu.Lock() server.reqMu.Lock()
server.kernelSettings = *input server.kernelSettings = *input
server.kernelSettings.Flags = input.Flags & (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS | server.kernelSettings.Flags = input.Flags & (CAP_ASYNC_READ | CAP_BIG_WRITES | CAP_FILE_OPS |
CAP_AUTO_INVAL_DATA | CAP_READDIRPLUS | CAP_NO_OPEN_SUPPORT) CAP_AUTO_INVAL_DATA | CAP_READDIRPLUS | CAP_NO_OPEN_SUPPORT | CAP_FLOCK_LOCKS | CAP_POSIX_LOCKS)
if input.Minor >= 13 { if input.Minor >= 13 {
server.setSplice() server.setSplice()
...@@ -390,6 +390,18 @@ func doFallocate(server *Server, req *request) { ...@@ -390,6 +390,18 @@ func doFallocate(server *Server, req *request) {
req.status = server.fileSystem.Fallocate((*FallocateIn)(req.inData)) req.status = server.fileSystem.Fallocate((*FallocateIn)(req.inData))
} }
func doGetLk(server *Server, req *request) {
req.status = server.fileSystem.GetLk((*LkIn)(req.inData), (*LkOut)(req.outData()))
}
func doSetLk(server *Server, req *request) {
req.status = server.fileSystem.SetLk((*LkIn)(req.inData))
}
func doSetLkw(server *Server, req *request) {
req.status = server.fileSystem.SetLkw((*LkIn)(req.inData))
}
//////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////
type operationFunc func(*Server, *request) type operationFunc func(*Server, *request)
...@@ -457,6 +469,9 @@ func init() { ...@@ -457,6 +469,9 @@ func init() {
_OP_READDIR: unsafe.Sizeof(ReadIn{}), _OP_READDIR: unsafe.Sizeof(ReadIn{}),
_OP_RELEASEDIR: unsafe.Sizeof(ReleaseIn{}), _OP_RELEASEDIR: unsafe.Sizeof(ReleaseIn{}),
_OP_FSYNCDIR: unsafe.Sizeof(FsyncIn{}), _OP_FSYNCDIR: unsafe.Sizeof(FsyncIn{}),
_OP_GETLK: unsafe.Sizeof(LkIn{}),
_OP_SETLK: unsafe.Sizeof(LkIn{}),
_OP_SETLKW: unsafe.Sizeof(LkIn{}),
_OP_ACCESS: unsafe.Sizeof(AccessIn{}), _OP_ACCESS: unsafe.Sizeof(AccessIn{}),
_OP_CREATE: unsafe.Sizeof(CreateIn{}), _OP_CREATE: unsafe.Sizeof(CreateIn{}),
_OP_INTERRUPT: unsafe.Sizeof(InterruptIn{}), _OP_INTERRUPT: unsafe.Sizeof(InterruptIn{}),
...@@ -484,6 +499,7 @@ func init() { ...@@ -484,6 +499,7 @@ func init() {
_OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}), _OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}),
_OP_INIT: unsafe.Sizeof(InitOut{}), _OP_INIT: unsafe.Sizeof(InitOut{}),
_OP_OPENDIR: unsafe.Sizeof(OpenOut{}), _OP_OPENDIR: unsafe.Sizeof(OpenOut{}),
_OP_GETLK: unsafe.Sizeof(LkOut{}),
_OP_CREATE: unsafe.Sizeof(CreateOut{}), _OP_CREATE: unsafe.Sizeof(CreateOut{}),
_OP_BMAP: unsafe.Sizeof(_BmapOut{}), _OP_BMAP: unsafe.Sizeof(_BmapOut{}),
_OP_IOCTL: unsafe.Sizeof(_IoctlOut{}), _OP_IOCTL: unsafe.Sizeof(_IoctlOut{}),
...@@ -572,6 +588,9 @@ func init() { ...@@ -572,6 +588,9 @@ func init() {
_OP_FSYNCDIR: doFsyncDir, _OP_FSYNCDIR: doFsyncDir,
_OP_SETXATTR: doSetXAttr, _OP_SETXATTR: doSetXAttr,
_OP_REMOVEXATTR: doRemoveXAttr, _OP_REMOVEXATTR: doRemoveXAttr,
_OP_GETLK: doGetLk,
_OP_SETLK: doSetLk,
_OP_SETLKW: doSetLkw,
_OP_ACCESS: doAccess, _OP_ACCESS: doAccess,
_OP_SYMLINK: doSymlink, _OP_SYMLINK: doSymlink,
_OP_RENAME: doRename, _OP_RENAME: doRename,
...@@ -600,6 +619,7 @@ func init() { ...@@ -600,6 +619,7 @@ func init() {
_OP_NOTIFY_DELETE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalDeleteOut)(ptr) }, _OP_NOTIFY_DELETE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalDeleteOut)(ptr) },
_OP_STATFS: func(ptr unsafe.Pointer) interface{} { return (*StatfsOut)(ptr) }, _OP_STATFS: func(ptr unsafe.Pointer) interface{} { return (*StatfsOut)(ptr) },
_OP_SYMLINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) }, _OP_SYMLINK: func(ptr unsafe.Pointer) interface{} { return (*EntryOut)(ptr) },
_OP_GETLK: func(ptr unsafe.Pointer) interface{} { return (*LkOut)(ptr) },
} { } {
operationHandlers[op].DecodeOut = f operationHandlers[op].DecodeOut = f
} }
...@@ -629,6 +649,9 @@ func init() { ...@@ -629,6 +649,9 @@ func init() {
_OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*FallocateIn)(ptr) }, _OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*FallocateIn)(ptr) },
_OP_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) }, _OP_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(ptr) },
_OP_RENAME: func(ptr unsafe.Pointer) interface{} { return (*RenameIn)(ptr) }, _OP_RENAME: func(ptr unsafe.Pointer) interface{} { return (*RenameIn)(ptr) },
_OP_GETLK: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
_OP_SETLK: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
_OP_SETLKW: func(ptr unsafe.Pointer) interface{} { return (*LkIn)(ptr) },
} { } {
operationHandlers[op].DecodeIn = f operationHandlers[op].DecodeIn = f
} }
......
...@@ -752,3 +752,24 @@ func (n *pathInode) Write(file nodefs.File, data []byte, off int64, context *fus ...@@ -752,3 +752,24 @@ func (n *pathInode) Write(file nodefs.File, data []byte, off int64, context *fus
} }
return 0, fuse.ENOSYS return 0, fuse.ENOSYS
} }
func (n *pathInode) GetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status) {
if file != nil {
return file.GetLk(owner, lk, flags, out)
}
return fuse.ENOSYS
}
func (n *pathInode) SetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
if file != nil {
return file.SetLk(owner, lk, flags)
}
return fuse.ENOSYS
}
func (n *pathInode) SetLkw(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
if file != nil {
return file.SetLkw(owner, lk, flags)
}
return fuse.ENOSYS
}
// Copyright 2018 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
package test
import (
"os"
"os/exec"
"path/filepath"
"sync"
"syscall"
"testing"
"github.com/hanwen/go-fuse/fuse"
"github.com/hanwen/go-fuse/fuse/nodefs"
"github.com/hanwen/go-fuse/internal/testutil"
)
func TestFlockExclusive(t *testing.T) {
cmd, err := exec.LookPath("flock")
if err != nil {
t.Skip("flock command not found.")
}
tc := NewTestCase(t)
defer tc.Cleanup()
contents := []byte{1, 2, 3}
tc.WriteFile(tc.origFile, []byte(contents), 0700)
f, err := os.OpenFile(tc.mountFile, os.O_WRONLY, 0)
if err != nil {
t.Fatalf("OpenFile(%q): %v", tc.mountFile, err)
}
defer f.Close()
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
t.Errorf("Flock returned: %v", err)
return
}
_, err = runExternalFlock(cmd, tc.mountFile)
if err == nil {
t.Errorf("Expected flock to fail, but it did not")
}
}
func runExternalFlock(flockPath, fname string) ([]byte, error) {
f, err := os.OpenFile(fname, os.O_WRONLY, 0)
if err != nil {
return nil, err
}
defer f.Close()
// in order to test the lock property we must use cmd.ExtraFiles (instead of passing the actual file)
// if we were to pass the file then this flock command would fail to place the lock (returning a
// 'file busy' error) as it is already opened and locked at this point (see above)
cmd := exec.Command(flockPath, "--exclusive", "--nonblock", "3")
cmd.Env = append(cmd.Env, "LC_ALL=C") // in case the user's shell language is different
cmd.ExtraFiles = []*os.File{f}
return cmd.CombinedOutput()
}
type lockingNode struct {
nodefs.Node
mu sync.Mutex
getLkInvoked bool
setLkInvoked bool
setLkwInvoked bool
}
func (n *lockingNode) GetLkInvoked() bool {
n.mu.Lock()
defer n.mu.Unlock()
return n.getLkInvoked
}
func (n *lockingNode) SetLkInvoked() bool {
n.mu.Lock()
defer n.mu.Unlock()
return n.setLkInvoked
}
func (n *lockingNode) SetLkwInvoked() bool {
n.mu.Lock()
defer n.mu.Unlock()
return n.setLkwInvoked
}
func (n *lockingNode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
return nodefs.NewDataFile([]byte("hello world")), fuse.OK
}
func (n *lockingNode) GetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status) {
n.mu.Lock()
defer n.mu.Unlock()
n.getLkInvoked = true
return fuse.OK
}
func (n *lockingNode) SetLk(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
n.mu.Lock()
defer n.mu.Unlock()
n.setLkInvoked = true
return fuse.OK
}
func (n *lockingNode) SetLkw(file nodefs.File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status) {
n.mu.Lock()
defer n.mu.Unlock()
n.setLkwInvoked = true
return fuse.OK
}
func TestFlockInvoked(t *testing.T) {
flock, err := exec.LookPath("flock")
if err != nil {
t.Skip("flock command not found.")
}
dir := testutil.TempDir()
defer os.RemoveAll(dir)
opts := &nodefs.Options{
Owner: fuse.CurrentOwner(),
Debug: testutil.VerboseTest(),
}
root := nodefs.NewDefaultNode()
s, _, err := nodefs.MountRoot(dir, root, opts)
if err != nil {
t.Fatalf("MountRoot: %v", err)
}
go s.Serve()
if err := s.WaitMount(); err != nil {
t.Fatal("WaitMount", err)
}
defer s.Unmount()
node := &lockingNode{
Node: nodefs.NewDefaultNode(),
}
root.Inode().NewChild("foo", false, node)
realPath := filepath.Join(dir, "foo")
if node.SetLkInvoked() {
t.Fatalf("SetLk is invoked")
}
cmd := exec.Command(flock, "--nonblock", realPath, "echo", "locked")
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("flock %v: %v", err, string(out))
}
if !node.SetLkInvoked() {
t.Fatalf("SetLk is not invoked")
}
if node.SetLkwInvoked() {
t.Fatalf("SetLkw is invoked")
}
cmd = exec.Command(flock, realPath, "echo", "locked")
out, err = cmd.CombinedOutput()
if err != nil {
t.Fatalf("flock %v: %v", err, string(out))
}
if !node.SetLkwInvoked() {
t.Fatalf("SetLkw is not invoked")
}
}
// Copyright 2016 the Go-FUSE Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build linux
package test
import (
"bytes"
"os"
"os/exec"
"syscall"
"testing"
)
// See https://github.com/hanwen/go-fuse/issues/170
func disabledTestFlock(t *testing.T) {
cmd, err := exec.LookPath("flock")
if err != nil {
t.Skip("flock command not found.")
}
tc := NewTestCase(t)
defer tc.Cleanup()
contents := []byte{1, 2, 3}
tc.WriteFile(tc.origFile, []byte(contents), 0700)
f, err := os.OpenFile(tc.mountFile, os.O_WRONLY, 0)
if err != nil {
t.Fatalf("OpenFile(%q): %v", tc.mountFile, err)
}
defer f.Close()
if err = syscall.Flock(int(f.Fd()), syscall.LOCK_EX); err != nil {
t.Errorf("Flock returned: %v", err)
return
}
if out, err := runExternalFlock(cmd, tc.mountFile); !bytes.Contains(out, []byte("failed to get lock")) {
t.Errorf("runExternalFlock(%q): %s (%v)", tc.mountFile, out, err)
}
}
func runExternalFlock(flockPath, fname string) ([]byte, error) {
f, err := os.OpenFile(fname, os.O_WRONLY, 0)
if err != nil {
return nil, err
}
defer f.Close()
cmd := exec.Command(flockPath, "--verbose", "--exclusive", "--nonblock", "3")
cmd.Env = append(cmd.Env, "LC_ALL=C") // in case the user's shell language is different
cmd.ExtraFiles = []*os.File{f}
return cmd.CombinedOutput()
}
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package fuse package fuse
import ( import (
"io"
"syscall" "syscall"
) )
...@@ -306,24 +307,24 @@ type GetXAttrOut struct { ...@@ -306,24 +307,24 @@ type GetXAttrOut struct {
Padding uint32 Padding uint32
} }
type _FileLock struct { type FileLock struct {
Start uint64 Start uint64
End uint64 End uint64
Typ uint32 Typ uint32
Pid uint32 Pid uint32
} }
type _LkIn struct { type LkIn struct {
InHeader InHeader
Fh uint64 Fh uint64
Owner uint64 Owner uint64
Lk _FileLock Lk FileLock
LkFlags uint32 LkFlags uint32
Padding uint32 Padding uint32
} }
type _LkOut struct { type LkOut struct {
Lk _FileLock Lk FileLock
} }
// For AccessIn.Mask. // For AccessIn.Mask.
...@@ -467,7 +468,26 @@ type FallocateIn struct { ...@@ -467,7 +468,26 @@ type FallocateIn struct {
Padding uint32 Padding uint32
} }
type FlockIn struct { func (lk *FileLock) ToFlockT(flockT *syscall.Flock_t) {
InHeader flockT.Start = int64(lk.Start)
Fh uint64 if lk.End == (1<<63)-1 {
flockT.Len = 0
} else {
flockT.Len = int64(lk.End - lk.Start + 1)
}
flockT.Whence = int16(io.SeekStart)
flockT.Type = int16(lk.Typ)
}
func (lk *FileLock) FromFlockT(flockT *syscall.Flock_t) {
lk.Typ = uint32(flockT.Type)
if flockT.Type != syscall.F_UNLCK {
lk.Start = uint64(flockT.Start)
if flockT.Len == 0 {
lk.End = (1 << 63) - 1
} else {
lk.End = uint64(flockT.Start + flockT.Len - 1)
}
}
lk.Pid = uint32(flockT.Pid)
} }
...@@ -244,11 +244,29 @@ func (fs *wrappingFS) Read(input *ReadIn, buf []byte) (ReadResult, Status) { ...@@ -244,11 +244,29 @@ func (fs *wrappingFS) Read(input *ReadIn, buf []byte) (ReadResult, Status) {
return nil, ENOSYS return nil, ENOSYS
} }
func (fs *wrappingFS) Flock(input *FlockIn, flags int) Status { func (fs *wrappingFS) GetLk(in *LkIn, out *LkOut) (code Status) {
if s, ok := fs.fs.(interface { if s, ok := fs.fs.(interface {
Flock(input *FlockIn, flags int) Status GetLk(in *LkIn, out *LkOut) (code Status)
}); ok { }); ok {
return s.Flock(input, flags) return s.GetLk(in, out)
}
return ENOSYS
}
func (fs *wrappingFS) SetLk(in *LkIn) (code Status) {
if s, ok := fs.fs.(interface {
SetLk(in *LkIn) (code Status)
}); ok {
return s.SetLk(in)
}
return ENOSYS
}
func (fs *wrappingFS) SetLkw(in *LkIn) (code Status) {
if s, ok := fs.fs.(interface {
SetLkw(in *LkIn) (code Status)
}); ok {
return s.SetLkw(in)
} }
return ENOSYS return ENOSYS
} }
......
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