Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement file locking: getlk/setlk/setlkw/flock #199

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion fuse/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,10 @@ type RawFileSystem interface {
Open(input *OpenIn, out *OpenOut) (status 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)
Write(input *WriteIn, data []byte) (written uint32, code Status)
Expand Down
10 changes: 9 additions & 1 deletion fuse/defaultraw.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,15 @@ func (fs *defaultRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta
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
}

Expand Down
14 changes: 12 additions & 2 deletions fuse/lockingfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,19 @@ func (fs *lockingRawFileSystem) Read(input *ReadIn, buf []byte) (ReadResult, Sta
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()()
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) {
Expand Down
10 changes: 9 additions & 1 deletion fuse/nodefs/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ type Node interface {
SetXAttr(attr string, data []byte, flags int, context *fuse.Context) fuse.Status
ListXAttr(context *fuse.Context) (attrs []string, code fuse.Status)

// File locking
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know this wasn't documented well before, but could you add some more wording here? I guess Lkw locks for write and Lk is a concurrent (?) read lock? If you don't want to write docs, add a manpage reference.

GetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, out *fuse.FileLock, context *fuse.Context) (code fuse.Status)
SetLk(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status)
SetLkw(file File, owner uint64, lk *fuse.FileLock, flags uint32, context *fuse.Context) (code fuse.Status)

// Attributes
GetAttr(out *fuse.Attr, file File, context *fuse.Context) (code fuse.Status)
Chmod(file File, perms uint32, context *fuse.Context) (code fuse.Status)
Expand Down Expand Up @@ -130,7 +135,10 @@ type File interface {
Read(dest []byte, off int64) (fuse.ReadResult, 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
// case of duplicated descriptor, it may be called more than
Expand Down
13 changes: 12 additions & 1 deletion fuse/nodefs/defaultfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,18 @@ func (f *defaultFile) Write(data []byte, off int64) (uint32, fuse.Status) {
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 {
return fuse.OK
}
Expand Down
12 changes: 12 additions & 0 deletions fuse/nodefs/defaultnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,18 @@ func (n *defaultNode) GetAttr(out *fuse.Attr, file File, context *fuse.Context)
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) {
return fuse.ENOSYS
}
Expand Down
54 changes: 49 additions & 5 deletions fuse/nodefs/files.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,56 @@ func (f *loopbackFile) Fsync(flags int) (code fuse.Status) {
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()
const (
F_OFD_GETLK = 36
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this looks like a syscall interface. Is this the same on OSX ? I would expect this defs to be a linux-specific file (I assume you wrote and tested this under linux.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We tested these constant on OSX and they seem to be working.

F_OFD_SETLK = 37
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 {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is controlled from a field in the flags, why have the separate argument 'blocking' ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given this signature:

setLock(owner uint64, lk *fuse.FileLock, flags uint32, blocking bool)

we understand your comment to mean that flags will indicate whether the lock call should be blocking, or not.

So we tested this with the loopback mounter and flock, running:

flock -n /tmp/mnt1/foo -c 'echo bar'

and

flock /tmp/mnt1/foo -c 'echo bar'

and in fact

flock -x /tmp/mnt1/foo -c 'echo bar'

In all three cases the lk.Typ and flags were 0x1.

It is entirely possible that we have misunderstood. We also haven't tested with fcntl yet either but maybe this is why @kaoet added a blocking flag. I think we agree that if flags does indicate blocking then it is better to use those than duplicate the intention with an additional boolean but we can't see how it does. What are we missing?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

blocking is determined by SetLk vs SetLkw

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since it is determined by which one of the methods was called (SetLk or SetLkw) and flags do not indicate whether it is blocking we have to pass a separate argument to setLock method. Or we can collapse setLock method into SetLk and SetLkw if that makes it more readable?

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 {
Expand Down
24 changes: 17 additions & 7 deletions fuse/nodefs/fsops.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,15 +455,25 @@ func (c *rawBridge) Read(input *fuse.ReadIn, buf []byte) (fuse.ReadResult, fuse.
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)
func (c *rawBridge) GetLk(input *fuse.LkIn, out *fuse.LkOut) (code fuse.Status) {
n := c.toInode(input.NodeId)
opened := n.mount.getOpenedFile(input.Fh)

if opened != nil {
return opened.WithFlags.File.Flock(flags)
}
return n.fsInode.GetLk(opened, input.Owner, &input.Lk, input.LkFlags, &out.Lk, &input.Context)
}

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 {
Expand Down
16 changes: 14 additions & 2 deletions fuse/nodefs/lockingfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,22 @@ func (f *lockingFile) Flush() fuse.Status {
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()
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() {
Expand Down
25 changes: 24 additions & 1 deletion fuse/opcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ func doInit(server *Server, req *request) {
server.reqMu.Lock()
server.kernelSettings = *input
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 {
server.setSplice()
Expand Down Expand Up @@ -390,6 +390,18 @@ func doFallocate(server *Server, req *request) {
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)
Expand Down Expand Up @@ -457,6 +469,9 @@ func init() {
_OP_READDIR: unsafe.Sizeof(ReadIn{}),
_OP_RELEASEDIR: unsafe.Sizeof(ReleaseIn{}),
_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_CREATE: unsafe.Sizeof(CreateIn{}),
_OP_INTERRUPT: unsafe.Sizeof(InterruptIn{}),
Expand Down Expand Up @@ -484,6 +499,7 @@ func init() {
_OP_LISTXATTR: unsafe.Sizeof(GetXAttrOut{}),
_OP_INIT: unsafe.Sizeof(InitOut{}),
_OP_OPENDIR: unsafe.Sizeof(OpenOut{}),
_OP_GETLK: unsafe.Sizeof(LkOut{}),
_OP_CREATE: unsafe.Sizeof(CreateOut{}),
_OP_BMAP: unsafe.Sizeof(_BmapOut{}),
_OP_IOCTL: unsafe.Sizeof(_IoctlOut{}),
Expand Down Expand Up @@ -572,6 +588,9 @@ func init() {
_OP_FSYNCDIR: doFsyncDir,
_OP_SETXATTR: doSetXAttr,
_OP_REMOVEXATTR: doRemoveXAttr,
_OP_GETLK: doGetLk,
_OP_SETLK: doSetLk,
_OP_SETLKW: doSetLkw,
_OP_ACCESS: doAccess,
_OP_SYMLINK: doSymlink,
_OP_RENAME: doRename,
Expand Down Expand Up @@ -600,6 +619,7 @@ func init() {
_OP_NOTIFY_DELETE: func(ptr unsafe.Pointer) interface{} { return (*NotifyInvalDeleteOut)(ptr) },
_OP_STATFS: func(ptr unsafe.Pointer) interface{} { return (*StatfsOut)(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
}
Expand Down Expand Up @@ -628,6 +648,9 @@ func init() {
_OP_FALLOCATE: func(ptr unsafe.Pointer) interface{} { return (*FallocateIn)(ptr) },
_OP_READDIRPLUS: func(ptr unsafe.Pointer) interface{} { return (*ReadIn)(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
}
Expand Down
22 changes: 22 additions & 0 deletions fuse/pathfs/pathfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,3 +745,25 @@ func (n *pathInode) Write(file nodefs.File, data []byte, off int64, context *fus
}
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
}

Loading