Skip to content

Commit a8a22c5

Browse files
author
dzsekijo
committed
add support for advisory locking ("lock" filesystem method)
1 parent a3329df commit a8a22c5

3 files changed

Lines changed: 133 additions & 21 deletions

File tree

example/xmp.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import os, sys
1111
from errno import *
1212
from stat import *
13+
import fcntl
1314
# some spaghetti to make it usable without fuse-py being installed
1415
for i in True, False:
1516
try:
@@ -202,6 +203,47 @@ def fgetattr(self):
202203
def ftruncate(self, len):
203204
self.file.truncate(len)
204205

206+
def lock(self, cmd, owner, **kw):
207+
# The code here is much rather just a demonstration of the locking
208+
# API than something which actually was seen to be useful.
209+
210+
# Advisory file locking is pretty messy in Unix, and the Python
211+
# interface to this doesn't make it better.
212+
# We can't do fcntl(2)/F_GETLK from Python in a platfrom independent
213+
# way. The following implementation *might* work under Linux.
214+
#
215+
# if cmd == fcntl.F_GETLK:
216+
# import struct
217+
#
218+
# lockdata = struct.pack('hhQQi', kw['l_type'], os.SEEK_SET,
219+
# kw['l_start'], kw['l_len'], kw['l_pid'])
220+
# ld2 = fcntl.fcntl(self.fd, fcntl.F_GETLK, lockdata)
221+
# flockfields = ('l_type', 'l_whence', 'l_start', 'l_len', 'l_pid')
222+
# uld2 = struct.unpack('hhQQi', ld2)
223+
# res = {}
224+
# for i in xrange(len(uld2)):
225+
# res[flockfields[i]] = uld2[i]
226+
#
227+
# return fuse.Flock(**res)
228+
229+
# Convert fcntl-ish lock parameters to Python's weird
230+
# lockf(3)/flock(2) medley locking API...
231+
op = { fcntl.F_UNLCK : fcntl.LOCK_UN,
232+
fcntl.F_RDLCK : fcntl.LOCK_SH,
233+
fcntl.F_WRLCK : fcntl.LOCK_EX }[kw['l_type']]
234+
if cmd == fcntl.F_GETLK:
235+
return -EOPNOTSUPP
236+
elif cmd == fcntl.F_SETLK:
237+
if op != fcntl.LOCK_UN:
238+
op |= fcntl.LOCK_NB
239+
elif cmd == fcntl.F_SETLKW:
240+
pass
241+
else:
242+
return -EINVAL
243+
244+
fcntl.lockf(self.fd, op, kw['l_start'], kw['l_len'])
245+
246+
205247
def main(self, *a, **kw):
206248

207249
self.file_class = self.XmpFile

fuse.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -381,7 +381,6 @@ class Stat(FuseStruct):
381381
pass
382382

383383

384-
385384
class StatVfs(FuseStruct):
386385
"""
387386
Auxiliary class which can be filled up statvfs attributes.
@@ -439,6 +438,18 @@ def __init__(self, name, **kw):
439438
FuseStruct.__init__(self, **kw)
440439

441440

441+
class Flock(FuseStruct):
442+
"""
443+
Class for representing flock structures (cf. fcntl(3)).
444+
445+
It makes sense to give values to the `l_type`, `l_start`,
446+
`l_len`, `l_pid` attributes (`l_whence` is not used by
447+
FUSE, see ``fuse.h``).
448+
"""
449+
450+
pass
451+
452+
442453
class FuseFileInfo(FuseStruct):
443454

444455
def __init__(self, **kw):
@@ -502,6 +513,7 @@ def feature_needs(*feas):
502513
'has_ftruncate': 25,
503514
'has_fsinit': ('has_init'),
504515
'has_fsdestroy': ('has_destroy'),
516+
'has_lock': 26,
505517
'has_init': 23,
506518
'has_destroy': 23,
507519
'*': '!re:^\*$'}
@@ -594,7 +606,8 @@ class Fuse(object):
594606
'chown', 'truncate', 'utime', 'open', 'read', 'write', 'release',
595607
'statfs', 'fsync', 'create', 'opendir', 'releasedir', 'fsyncdir',
596608
'flush', 'fgetattr', 'ftruncate', 'getxattr', 'listxattr',
597-
'setxattr', 'removexattr', 'access', 'fsinit', 'fsdestroy']
609+
'setxattr', 'removexattr', 'access', 'lock', 'fsinit',
610+
'fsdestroy']
598611

599612
fusage = "%prog [mountpoint] [options]"
600613

@@ -761,8 +774,8 @@ def __init__(self):
761774
class mpx(object):
762775
def __init__(self, name):
763776
self.name = name
764-
def __call__(self, *a):
765-
return getattr(a[-1], self.name)(*(a[1:-1]))
777+
def __call__(self, *a, **kw):
778+
return getattr(a[-1], self.name)(*(a[1:-1]), **kw)
766779

767780
self.proxyclass = mpx
768781
self.mdic = {}
@@ -791,7 +804,7 @@ def setter(self, xcls):
791804

792805
Methproxy._add_class_type('file', ('open', 'create'),
793806
('read', 'write', 'fsync', 'release', 'flush',
794-
'fgetattr', 'ftruncate'))
807+
'fgetattr', 'ftruncate', 'lock'))
795808
Methproxy._add_class_type('dir', ('opendir',),
796809
('readdir', 'fsyncdir', 'releasedir'))
797810

fuseparts/_fusemodule.c

Lines changed: 73 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
Copyright (C) 2006-2007 Csaba Henk <csaba.henk@creo.hu>
1212
*/
1313

14+
/*
15+
* Local Variables:
16+
* indent-tabs-mode: t
17+
* c-basic-offset: 8
18+
* End:
19+
* Changed by David McNab (david@rebirthing.co.nz) to work with recent pythons.
20+
* Namely, replacing PyTuple_* with PySequence_*, and checking numerical values
21+
* with both PyInt_Check and PyLong_Check.
22+
*/
23+
1424
#ifndef FUSE_VERSION
1525
#ifndef FUSE_MAKE_VERSION
1626
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
@@ -33,7 +43,8 @@ static PyObject *getattr_cb=NULL, *readlink_cb=NULL, *readdir_cb=NULL,
3343
*statfs_cb=NULL, *fsync_cb=NULL, *create_cb=NULL, *opendir_cb=NULL,
3444
*releasedir_cb=NULL, *fsyncdir_cb=NULL, *flush_cb=NULL, *ftruncate_cb=NULL,
3545
*fgetattr_cb=NULL, *getxattr_cb=NULL, *listxattr_cb=NULL, *setxattr_cb=NULL,
36-
*removexattr_cb=NULL, *access_cb=NULL, *fsinit_cb=NULL, *fsdestroy_cb = NULL;
46+
*removexattr_cb=NULL, *access_cb=NULL, *lock_cb = NULL, *fsinit_cb=NULL,
47+
*fsdestroy_cb = NULL;
3748

3849
static PyObject *Py_FuseError;
3950
static PyInterpreterState *interp;
@@ -161,16 +172,6 @@ fi_to_py(struct fuse_file_info *fi)
161172
#define fetchattr(st, attr) \
162173
fetchattr_nam(st, attr, #attr)
163174

164-
/*
165-
* Local Variables:
166-
* indent-tabs-mode: t
167-
* c-basic-offset: 8
168-
* End:
169-
* Changed by David McNab (david@rebirthing.co.nz) to work with recent pythons.
170-
* Namely, replacing PyTuple_* with PySequence_*, and checking numerical values
171-
* with both PyInt_Check and PyLong_Check.
172-
*/
173-
174175
#define fetchattr_soft(st, attr) \
175176
pytmp = PyObject_GetAttrString(v, #attr); \
176177
if (pytmp == Py_None) { \
@@ -181,6 +182,11 @@ fi_to_py(struct fuse_file_info *fi)
181182
py2attr(st, attr); \
182183
}
183184

185+
/*
186+
* Following macros are only for getattr-alikes, we undef them after
187+
* the getattr type functions.
188+
*/
189+
184190
#define fetchattr_soft_d(st, attr, defa) \
185191
fetchattr_soft(st, attr) else st->attr = defa
186192

@@ -243,7 +249,6 @@ fgetattr_func(const char *path, struct stat *st, struct fuse_file_info *fi)
243249
}
244250
#endif
245251

246-
#undef fetchattr_soft
247252
#undef fetchattr_soft_d
248253
#undef FETCH_STAT_DATA
249254

@@ -776,6 +781,55 @@ fsdestroy_func(void *param)
776781
}
777782
#endif
778783

784+
static inline PyObject *
785+
lock_func_i(const char *path, struct fuse_file_info *fi, int cmd,
786+
struct flock *lock)
787+
{
788+
PyObject *pyargs, *pykw = NULL, *v = NULL;
789+
790+
pyargs =
791+
fi_to_py(fi) ?
792+
Py_BuildValue("(siKO)", path, cmd, fi->lock_owner, fi_to_py(fi)) :
793+
Py_BuildValue("(siK)", path, cmd, fi->lock_owner);
794+
if (! pyargs)
795+
goto out;
796+
797+
pykw = Py_BuildValue("{sisKsKsi}",
798+
"l_type", lock->l_type,
799+
"l_start", lock->l_start,
800+
"l_len", lock->l_len,
801+
"l_pid", lock->l_pid);
802+
if (! pykw)
803+
goto out;
804+
805+
v = PyObject_Call(lock_cb, pyargs, pykw);
806+
807+
out:
808+
Py_XDECREF(pyargs);
809+
Py_XDECREF(pykw);
810+
811+
return v;
812+
}
813+
814+
static int
815+
lock_func(const char *path, struct fuse_file_info *fi, int cmd,
816+
struct flock *lock)
817+
{
818+
PyObject *pytmp;
819+
unsigned long long ctmp;
820+
821+
PROLOGUE( lock_func_i(path, fi, cmd, lock) )
822+
823+
fetchattr_soft(lock, l_type);
824+
fetchattr_soft(lock, l_start);
825+
fetchattr_soft(lock, l_len);
826+
fetchattr_soft(lock, l_pid);
827+
828+
ret = 0;
829+
830+
EPILOGUE
831+
}
832+
779833
static int
780834
pyfuse_loop_mt(struct fuse *f)
781835
{
@@ -817,14 +871,14 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
817871
"open", "read", "write", "release", "statfs", "fsync",
818872
"create", "opendir", "releasedir", "fsyncdir", "flush",
819873
"ftruncate", "fgetattr", "getxattr", "listxattr", "setxattr",
820-
"removexattr", "access", "fsinit", "fsdestroy", "fuse_args",
821-
"multithreaded", NULL
874+
"removexattr", "access", "lock", "fsinit", "fsdestroy",
875+
"fuse_args", "multithreaded", NULL
822876
};
823877

824878
memset(&op, 0, sizeof(op));
825879

826880
if (!PyArg_ParseTupleAndKeywords(args, kw,
827-
"|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOi",
881+
"|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOi",
828882
kwlist, &getattr_cb, &readlink_cb,
829883
&readdir_cb, &mknod_cb, &mkdir_cb,
830884
&unlink_cb, &rmdir_cb, &symlink_cb,
@@ -837,7 +891,7 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
837891
&flush_cb, &ftruncate_cb,
838892
&fgetattr_cb, &getxattr_cb,
839893
&listxattr_cb, &setxattr_cb,
840-
&removexattr_cb, &access_cb,
894+
&removexattr_cb, &access_cb, &lock_cb,
841895
&fsinit_cb, &fsdestroy_cb,
842896
&fargseq, &multithreaded))
843897
return NULL;
@@ -890,6 +944,9 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
890944
DO_ONE_ATTR(access);
891945
DO_ONE_ATTR(create);
892946
#endif
947+
#if FUSE_VERSION >= 26
948+
DO_ONE_ATTR(lock);
949+
#endif
893950
#if FUSE_VERSION >= 23
894951
DO_ONE_ATTR_AS(init, fsinit);
895952
DO_ONE_ATTR_AS(destroy, fsdestroy);

0 commit comments

Comments
 (0)