Skip to content

Commit 78799eb

Browse files
authored
Merge pull request #1 from beg0/master
IOCTL support in python-fuse
2 parents 42cf9fb + f8f420f commit 78799eb

3 files changed

Lines changed: 281 additions & 11 deletions

File tree

example/fioc.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
#!/usr/bin/python
2+
3+
# Copyright (C) 2016 Cedric CARREE <beg0@free.fr>
4+
#
5+
# This program can be distributed under the terms of the GNU LGPL.
6+
# See the file COPYING.
7+
#
8+
import os, stat, errno, struct
9+
10+
try:
11+
import _find_fuse_parts
12+
except ImportError:
13+
pass
14+
import fuse
15+
from fuse import Fuse
16+
17+
18+
if not hasattr(fuse, '__version__'):
19+
raise RuntimeError("your fuse-py doesn't know of fuse.__version__, probably it's too old.")
20+
21+
fuse.fuse_python_api = (0, 2)
22+
23+
# this mimics asm-generic/ioctl.h header
24+
# I'm not sure this is really portable, you'd better use ioctl-opt package or something similar
25+
class IOCTL:
26+
_IOC_NRBITS = 8
27+
_IOC_TYPEBITS = 8
28+
_IOC_SIZEBITS = 14
29+
_IOC_DIRBITS = 2
30+
31+
_IOC_NRMASK = (1 << _IOC_NRBITS)-1
32+
_IOC_TYPEMASK = (1 << _IOC_TYPEBITS)-1
33+
_IOC_SIZEMASK = (1 << _IOC_SIZEBITS)-1
34+
_IOC_DIRMASK = (1 << _IOC_DIRBITS)-1
35+
36+
_IOC_NRSHIFT = 0
37+
_IOC_TYPESHIFT = _IOC_NRSHIFT + _IOC_NRBITS
38+
_IOC_SIZESHIFT = _IOC_TYPESHIFT + _IOC_TYPEBITS
39+
_IOC_DIRSHIFT = _IOC_SIZESHIFT + _IOC_SIZEBITS
40+
41+
_IOC_NONE = 0
42+
_IOC_WRITE = 1
43+
_IOC_READ = 2
44+
45+
@classmethod
46+
def _IOC(cls, d,t,nr,size):
47+
return (((d) << cls._IOC_DIRSHIFT) |
48+
((t) << cls._IOC_TYPESHIFT) | \
49+
((nr) << cls._IOC_NRSHIFT) | \
50+
((size) << cls._IOC_SIZESHIFT))
51+
52+
@classmethod
53+
def _IO(cls, t,nr):
54+
return cls._IOC(cls._IOC_NONE, t, nr, 0)
55+
56+
@classmethod
57+
def _IOR(cls, t,nr,size):
58+
return cls._IOC(cls._IOC_READ, t, nr, size)
59+
60+
@classmethod
61+
def _IOW(cls, t,nr,size):
62+
return cls._IOC(cls._IOC_WRITE, t, nr, size)
63+
64+
@classmethod
65+
def _IOWR(cls,t,nr,size):
66+
return cls._IOC(cls._IOC_WRITE|cls._IOC_READ, t, nr, size)
67+
68+
69+
# IOCTL (as defined in fioc.h)
70+
# Note: on my system, size_t is an unsigned long
71+
FIOC_GET_SIZE = IOCTL._IOR(ord('E'),0, struct.calcsize("L"));
72+
FIOC_SET_SIZE = IOCTL._IOW(ord('E'),1, struct.calcsize("L"));
73+
74+
# object type
75+
FIOC_NONE = 0
76+
FIOC_ROOT = 1
77+
FIOC_FILE = 2
78+
79+
FIOC_NAME = "fioc"
80+
81+
class MyStat(fuse.Stat):
82+
def __init__(self):
83+
self.st_mode = 0
84+
self.st_ino = 0
85+
self.st_dev = 0
86+
self.st_nlink = 0
87+
self.st_uid = 0
88+
self.st_gid = 0
89+
self.st_size = 0
90+
self.st_atime = 0
91+
self.st_mtime = 0
92+
self.st_ctime = 0
93+
94+
95+
class FiocFS(Fuse):
96+
97+
def __init__(self, *args, **kw):
98+
Fuse.__init__(self, *args, **kw)
99+
self.buf = ""
100+
101+
def resize(self, new_size):
102+
old_size = len(self.buf)
103+
if new_size == old_size:
104+
return 0
105+
106+
if new_size < old_size:
107+
self.buf = self.buf[0:new_size]
108+
else:
109+
self.buf = self.buf + "\x00" * (new_size - old_size)
110+
111+
return 0
112+
113+
def file_type(self, path):
114+
if not type(path) == str:
115+
return FIOC_NONE
116+
if path == "/":
117+
return FIOC_ROOT
118+
elif path == "/" + FIOC_NAME:
119+
return FIOC_FILE
120+
else:
121+
return FIOC_NONE
122+
123+
def getattr(self, path):
124+
st = MyStat()
125+
ft = self.file_type(path)
126+
if ft == FIOC_ROOT:
127+
st.st_mode = stat.S_IFDIR | 0o755
128+
st.st_nlink = 2
129+
elif ft == FIOC_FILE:
130+
st.st_mode = stat.S_IFREG | 0o666
131+
st.st_nlink = 1
132+
st.st_size = len(self.buf)
133+
else:
134+
return -errno.ENOENT
135+
return st
136+
137+
def open(self, path, flags):
138+
if self.file_type(path) != FIOC_NONE:
139+
return 0
140+
141+
return -errno.ENOENT
142+
143+
def do_read(self, path, size, offset):
144+
145+
if offset >= len(self.buf):
146+
return 0
147+
148+
if size > (len(self.buf) - offset):
149+
size = len(self.buf) - offset
150+
151+
return self.buf[offset:offset+size]
152+
153+
def read(self, path, size, offset):
154+
if self.file_type(path) != FIOC_FILE:
155+
return -errno.EINVAL;
156+
157+
return self.do_read(path, size, offset)
158+
159+
def do_write(self, path, buf, offset):
160+
self.buf = self.buf[0:offset-1] + buf + self.buf[offset+len(buf)+1:len(self.buf)]
161+
return len(buf)
162+
163+
def write(self, path, buf, offset):
164+
if self.file_type(path) != FIOC_FILE:
165+
return -errno.EINVAL;
166+
167+
return self.do_write(path, buf, offset)
168+
169+
def truncate(self, path, size):
170+
if self.file_type(path) != FIOC_FILE:
171+
return -error.EINVAL
172+
173+
return self.resize(size)
174+
175+
def readdir(self, path, offset):
176+
for r in '.', '..', FIOC_NAME:
177+
yield fuse.Direntry(r)
178+
179+
def ioctl(self, path, cmd, arg, flags):
180+
if cmd == FIOC_GET_SIZE:
181+
data = struct.pack("L",len(self.buf))
182+
return data
183+
elif cmd == FIOC_SET_SIZE:
184+
(l,) = struct.unpack("L",arg);
185+
self.resize(l)
186+
return 0
187+
188+
return -errno.EINVAL
189+
190+
def main():
191+
usage="""
192+
Userspace ioctl example
193+
194+
""" + Fuse.fusage
195+
server = FiocFS(version="%prog " + fuse.__version__,
196+
usage=usage,
197+
dash_s_do='setsingle')
198+
199+
server.parse(errex=1)
200+
server.main()
201+
202+
if __name__ == '__main__':
203+
main()
204+

fuse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ class Fuse(object):
645645
'statfs', 'fsync', 'create', 'opendir', 'releasedir', 'fsyncdir',
646646
'flush', 'fgetattr', 'ftruncate', 'getxattr', 'listxattr',
647647
'setxattr', 'removexattr', 'access', 'lock', 'utimens', 'bmap',
648-
'fsinit', 'fsdestroy']
648+
'fsinit', 'fsdestroy', 'ioctl']
649649

650650
fusage = "%prog [mountpoint] [options]"
651651

fuseparts/_fusemodule.c

Lines changed: 76 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,23 @@
2121
* with both PyInt_Check and PyLong_Check.
2222
*/
2323

24+
#ifndef FUSE_USE_VERSION
25+
#define FUSE_USE_VERSION 26
26+
#endif
27+
28+
#include <Python.h>
29+
#include <fuse.h>
30+
#include <sys/ioctl.h>
31+
32+
2433
#ifndef FUSE_VERSION
2534
#ifndef FUSE_MAKE_VERSION
2635
#define FUSE_MAKE_VERSION(maj, min) ((maj) * 10 + (min))
2736
#endif
2837
#define FUSE_VERSION FUSE_MAKE_VERSION(FUSE_MAJOR_VERSION, FUSE_MINOR_VERSION)
2938
#endif
3039

31-
#ifndef FUSE_USE_VERSION
32-
#define FUSE_USE_VERSION 26
33-
#endif
3440

35-
#include <Python.h>
36-
#include <fuse.h>
3741

3842
#if PY_MAJOR_VERSION >= 3
3943
#define PyInt_FromLong PyLong_FromLong
@@ -54,7 +58,8 @@ static PyObject *getattr_cb=NULL, *readlink_cb=NULL, *readdir_cb=NULL,
5458
*releasedir_cb=NULL, *fsyncdir_cb=NULL, *flush_cb=NULL, *ftruncate_cb=NULL,
5559
*fgetattr_cb=NULL, *getxattr_cb=NULL, *listxattr_cb=NULL, *setxattr_cb=NULL,
5660
*removexattr_cb=NULL, *access_cb=NULL, *lock_cb = NULL, *utimens_cb = NULL,
57-
*bmap_cb = NULL, *fsinit_cb=NULL, *fsdestroy_cb = NULL;
61+
*bmap_cb = NULL, *fsinit_cb=NULL, *fsdestroy_cb = NULL, *ioctl_cb = NULL;
62+
5863

5964
static PyObject *Py_FuseError;
6065
static PyInterpreterState *interp;
@@ -930,6 +935,64 @@ bmap_func(const char *path, size_t blocksize, uint64_t *idx)
930935
}
931936
#endif
932937

938+
#if FUSE_VERSION >= 28
939+
static int
940+
ioctl_func(const char *path, int cmd, void *arg,
941+
struct fuse_file_info *fi, unsigned int flags, void *data)
942+
{
943+
char* s;
944+
char* input_data;
945+
int input_data_size, output_data_size;
946+
947+
input_data = (char*) data;
948+
input_data_size = _IOC_SIZE(cmd);
949+
950+
// If not a "write" ioctl, do not send input data
951+
if(!(_IOC_DIR(cmd) & _IOC_WRITE)) {
952+
input_data = NULL;
953+
input_data_size = 0;
954+
}
955+
956+
#if PY_MAJOR_VERSION >= 3
957+
PROLOGUE(PYO_CALLWITHFI(fi, ioctl_cb, sIy#I, path,cmd,(char*)input_data,input_data_size,flags));
958+
#else
959+
PROLOGUE(PYO_CALLWITHFI(fi, ioctl_cb, sIs#I, path,cmd,(char*)input_data,input_data_size,flags));
960+
#endif
961+
962+
// get returned value if this is a "read" ioctl
963+
if(_IOC_DIR(cmd) & _IOC_READ) {
964+
965+
#if PY_MAJOR_VERSION >= 3
966+
if(!PyBytes_Check(v)) {
967+
ret = -EINVAL;
968+
goto OUT_DECREF;
969+
}
970+
971+
output_data_size = PyBytes_Size(v);
972+
973+
s = PyBytes_AsString(v);
974+
#else
975+
976+
if(!PyString_Check(v)) {
977+
ret = -EINVAL;
978+
goto OUT_DECREF;
979+
}
980+
981+
output_data_size = PyString_Size(v);
982+
983+
s = PyString_AsString(v);
984+
#endif
985+
986+
if(output_data_size > _IOC_SIZE(cmd))
987+
output_data_size = _IOC_SIZE(cmd);
988+
989+
memcpy(data,s,output_data_size);
990+
ret = 0;
991+
}
992+
EPILOGUE
993+
}
994+
#endif
995+
933996
static int
934997
pyfuse_loop_mt(struct fuse *f)
935998
{
@@ -973,13 +1036,13 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
9731036
"create", "opendir", "releasedir", "fsyncdir", "flush",
9741037
"ftruncate", "fgetattr", "getxattr", "listxattr", "setxattr",
9751038
"removexattr", "access", "lock", "utimens", "bmap",
976-
"fsinit", "fsdestroy", "fuse_args", "multithreaded", NULL
1039+
"fsinit", "fsdestroy", "ioctl", "fuse_args", "multithreaded", NULL
9771040
};
978-
1041+
9791042
memset(&op, 0, sizeof(op));
9801043

9811044
if (!PyArg_ParseTupleAndKeywords(args, kw,
982-
"|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOi",
1045+
"|OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOi",
9831046
kwlist, &getattr_cb, &readlink_cb,
9841047
&readdir_cb, &mknod_cb, &mkdir_cb,
9851048
&unlink_cb, &rmdir_cb, &symlink_cb,
@@ -994,7 +1057,7 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
9941057
&listxattr_cb, &setxattr_cb,
9951058
&removexattr_cb, &access_cb,
9961059
&lock_cb, &utimens_cb, &bmap_cb,
997-
&fsinit_cb, &fsdestroy_cb,
1060+
&fsinit_cb, &fsdestroy_cb, &ioctl_cb,
9981061
&fargseq, &multithreaded))
9991062
return NULL;
10001063

@@ -1055,6 +1118,9 @@ Fuse_main(PyObject *self, PyObject *args, PyObject *kw)
10551118
DO_ONE_ATTR_AS(init, fsinit);
10561119
DO_ONE_ATTR_AS(destroy, fsdestroy);
10571120
#endif
1121+
#if FUSE_VERSION >= 28
1122+
DO_ONE_ATTR(ioctl);
1123+
#endif
10581124

10591125
#undef DO_ONE_ATTR
10601126
#undef DO_ONE_ATTR_AS

0 commit comments

Comments
 (0)