Commit ae2e1d5c authored by OneOfOne's avatar OneOfOne Committed by Han-Wen Nienhuys

Flock implementation with test.

Fixes #134
parent 51b6e576
...@@ -123,6 +123,8 @@ type RawFileSystem interface { ...@@ -123,6 +123,8 @@ 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)
Release(input *ReleaseIn) Release(input *ReleaseIn)
Write(input *WriteIn, data []byte) (written uint32, code Status) Write(input *WriteIn, data []byte) (written uint32, code Status)
Flush(input *FlushIn) Status Flush(input *FlushIn) Status
......
...@@ -117,6 +117,10 @@ func (fs *defaultRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta ...@@ -117,6 +117,10 @@ 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 {
return ENOSYS
}
func (fs *defaultRawFileSystem) Release(input *ReleaseIn) { func (fs *defaultRawFileSystem) Release(input *ReleaseIn) {
} }
......
...@@ -159,6 +159,11 @@ func (fs *lockingRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta ...@@ -159,6 +159,11 @@ 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 {
defer fs.locked()()
return fs.RawFS.Flock(input, flags)
}
func (fs *lockingRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) { func (fs *lockingRawFileSystem) Write(input *WriteIn, data []byte) (written uint32, code Status) {
defer fs.locked()() defer fs.locked()()
return fs.RawFS.Write(input, data) return fs.RawFS.Write(input, data)
......
...@@ -130,6 +130,8 @@ type File interface { ...@@ -130,6 +130,8 @@ 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
// 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
// once for a file. // once for a file.
......
...@@ -37,6 +37,7 @@ func (f *defaultFile) Write(data []byte, off int64) (uint32, fuse.Status) { ...@@ -37,6 +37,7 @@ 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) Flush() fuse.Status { func (f *defaultFile) Flush() fuse.Status {
return fuse.OK return fuse.OK
} }
......
...@@ -167,6 +167,14 @@ func (f *loopbackFile) Fsync(flags int) (code fuse.Status) { ...@@ -167,6 +167,14 @@ func (f *loopbackFile) Fsync(flags int) (code fuse.Status) {
return r return r
} }
func (f *loopbackFile) Flock(flags int) fuse.Status {
f.lock.Lock()
r := fuse.ToStatus(syscall.Flock(int(f.File.Fd()), flags))
f.lock.Unlock()
return r
}
func (f *loopbackFile) Truncate(size uint64) fuse.Status { func (f *loopbackFile) Truncate(size uint64) fuse.Status {
f.lock.Lock() f.lock.Lock()
r := fuse.ToStatus(syscall.Ftruncate(int(f.File.Fd()), int64(size))) r := fuse.ToStatus(syscall.Ftruncate(int(f.File.Fd()), int64(size)))
......
...@@ -455,6 +455,17 @@ func (c *rawBridge) Read(input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse. ...@@ -455,6 +455,17 @@ 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 {
node := c.toInode(input.NodeId)
opened := node.mount.getOpenedFile(input.Fh)
if opened != nil {
return opened.WithFlags.File.Flock(flags)
}
return fuse.EBADF
}
func (c *rawBridge) StatFs(header *fuse.InHeader, out *fuse.StatfsOut) fuse.Status { func (c *rawBridge) StatFs(header *fuse.InHeader, out *fuse.StatfsOut) fuse.Status {
node := c.toInode(header.NodeId) node := c.toInode(header.NodeId)
s := node.Node().StatFs() s := node.Node().StatFs()
......
...@@ -54,6 +54,12 @@ func (f *lockingFile) Flush() fuse.Status { ...@@ -54,6 +54,12 @@ func (f *lockingFile) Flush() fuse.Status {
return f.file.Flush() return f.file.Flush()
} }
func (f *lockingFile) Flock(flags int) fuse.Status {
f.mu.Lock()
defer f.mu.Unlock()
return f.file.Flock(flags)
}
func (f *lockingFile) Release() { func (f *lockingFile) Release() {
f.mu.Lock() f.mu.Lock()
defer f.mu.Unlock() defer f.mu.Unlock()
......
// 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"
)
func TestFlock(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()
}
...@@ -510,6 +510,7 @@ func TestAccess(t *testing.T) { ...@@ -510,6 +510,7 @@ func TestAccess(t *testing.T) {
contents := []byte{1, 2, 3} contents := []byte{1, 2, 3}
tc.WriteFile(tc.origFile, []byte(contents), 0700) tc.WriteFile(tc.origFile, []byte(contents), 0700)
if err := os.Chmod(tc.origFile, 0); err != nil { if err := os.Chmod(tc.origFile, 0); err != nil {
t.Fatalf("Chmod failed: %v", err) t.Fatalf("Chmod failed: %v", err)
} }
......
...@@ -18,25 +18,28 @@ const ( ...@@ -18,25 +18,28 @@ const (
type Status int32 type Status int32
const ( const (
OK = Status(0) OK = Status(0)
// EACCESS Permission denied // EACCESS Permission denied
EACCES = Status(syscall.EACCES) EACCES = Status(syscall.EACCES)
// EBUSY Device or resource busy // EBUSY Device or resource busy
EBUSY = Status(syscall.EBUSY) EBUSY = Status(syscall.EBUSY)
// EAGAIN Resource temporarily unavailable
EAGAIN = Status(syscall.EAGAIN)
// EINVAL Invalid argument // EINVAL Invalid argument
EINVAL = Status(syscall.EINVAL) EINVAL = Status(syscall.EINVAL)
// EIO I/O error // EIO I/O error
EIO = Status(syscall.EIO) EIO = Status(syscall.EIO)
// ENOENT No such file or directory // ENOENT No such file or directory
ENOENT = Status(syscall.ENOENT) ENOENT = Status(syscall.ENOENT)
// ENOSYS Function not implemented // ENOSYS Function not implemented
ENOSYS = Status(syscall.ENOSYS) ENOSYS = Status(syscall.ENOSYS)
// ENODATA No data available // ENODATA No data available
ENODATA = Status(syscall.ENODATA) ENODATA = Status(syscall.ENODATA)
...@@ -45,22 +48,22 @@ const ( ...@@ -45,22 +48,22 @@ const (
ENOTDIR = Status(syscall.ENOTDIR) ENOTDIR = Status(syscall.ENOTDIR)
// EPERM Operation not permitted // EPERM Operation not permitted
EPERM = Status(syscall.EPERM) EPERM = Status(syscall.EPERM)
// ERANGE Math result not representable // ERANGE Math result not representable
ERANGE = Status(syscall.ERANGE) ERANGE = Status(syscall.ERANGE)
// EXDEV Cross-device link // EXDEV Cross-device link
EXDEV = Status(syscall.EXDEV) EXDEV = Status(syscall.EXDEV)
// EBADF Bad file number // EBADF Bad file number
EBADF = Status(syscall.EBADF) EBADF = Status(syscall.EBADF)
// ENODEV No such device // ENODEV No such device
ENODEV = Status(syscall.ENODEV) ENODEV = Status(syscall.ENODEV)
// EROFS Read-only file system // EROFS Read-only file system
EROFS = Status(syscall.EROFS) EROFS = Status(syscall.EROFS)
) )
type ForgetIn struct { type ForgetIn struct {
...@@ -462,3 +465,8 @@ type FallocateIn struct { ...@@ -462,3 +465,8 @@ type FallocateIn struct {
Mode uint32 Mode uint32
Padding uint32 Padding uint32
} }
type FlockIn struct {
InHeader
Fh uint64
}
...@@ -244,6 +244,15 @@ func (fs *wrappingFS) Read(input *ReadIn, buf []byte) (ReadResult, Status) { ...@@ -244,6 +244,15 @@ 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 {
if s, ok := fs.fs.(interface {
Flock(input *FlockIn, flags int) Status
}); ok {
return s.Flock(input, flags)
}
return ENOSYS
}
func (fs *wrappingFS) Release(input *ReleaseIn) { func (fs *wrappingFS) Release(input *ReleaseIn) {
if s, ok := fs.fs.(interface { if s, ok := fs.fs.(interface {
Release(input *ReleaseIn) Release(input *ReleaseIn)
......
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