Skip to content

Commit b855b08

Browse files
committed
Merge remote-tracking branch 'upstream/master' into flared/main
2 parents 1d29dbb + 3150ccf commit b855b08

File tree

11 files changed

+190
-21
lines changed

11 files changed

+190
-21
lines changed

.github/workflows/python.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
strategy:
1010
fail-fast: false
1111
matrix:
12-
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
12+
python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
1313

1414
steps:
1515
- uses: actions/checkout@v4
@@ -23,7 +23,7 @@ jobs:
2323
run: python -c "import sys; print(sys.version)"
2424

2525
- name: Install Python build dependencies
26-
run: python -m pip install --upgrade pip setuptools wheel
26+
run: python -m pip install --upgrade pip setuptools wheel pytest
2727

2828
- name: Install libfuse-dev and pkg-config
2929
run: sudo apt install -y libfuse-dev pkg-config
@@ -34,5 +34,4 @@ jobs:
3434
- name: Test
3535
run: |
3636
sudo modprobe fuse
37-
mkdir /tmp/foo && python example/hello.py /tmp/foo && [ "$(cat /tmp/foo/hello)" = "Hello World!" ]
38-
mkdir /tmp/bar && python example/fioc.py /tmp/bar && [ -f /tmp/bar/fioc ]
37+
python -m pytest

AUTHORS

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Python bindings
44
Csaba Henk <[email protected]>
55
Sebastien Delafond <[email protected]>
66
Steven James
7-
Jeff Epler <[email protected]>
87

98
Contributions
109
=============
@@ -13,3 +12,8 @@ hello.py by Andrew Straw <[email protected]>
1312
threading related code cleaned up by Miklos Szeredi <[email protected]>
1413
IOCTL support by Cédric Carrée
1514
poll support by David Lechner <[email protected]>
15+
16+
Retired
17+
=======
18+
19+
Jeff Epler <[email protected]>

example/fioc.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ def _IOWR(cls,t,nr,size):
7171
FIOC_GET_SIZE = IOCTL._IOR(ord('E'),0, struct.calcsize("L"));
7272
FIOC_SET_SIZE = IOCTL._IOW(ord('E'),1, struct.calcsize("L"));
7373

74+
print(f"{FIOC_GET_SIZE:x} {FIOC_SET_SIZE:x}")
7475
# object type
7576
FIOC_NONE = 0
7677
FIOC_ROOT = 1

example/xattr.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Example by github @vignedev from https://github.com/libfuse/python-fuse/issues/77
2+
3+
import fuse
4+
import stat, errno
5+
from fuse import Fuse, Stat, Direntry
6+
7+
fuse.fuse_python_api = (0, 2)
8+
9+
BROKEN_FILE = '/utf8_attr'
10+
FATTR_NAME = 'user.xdg.comment'
11+
FATTR_VALUE = 'ああ、メッセージは切り取られていない'
12+
13+
class EmptyStat(Stat):
14+
def __init__(self):
15+
self.st_mode = 0
16+
self.st_ino = 0
17+
self.st_dev = 0
18+
self.st_nlink = 0
19+
self.st_uid = 0
20+
self.st_gid = 0
21+
self.st_size = 0
22+
self.st_atime = 0
23+
self.st_mtime = 0
24+
self.st_ctime = 0
25+
26+
class GetAttrBug(Fuse):
27+
def getattr(self, path):
28+
ret_stat = EmptyStat()
29+
if path == '/':
30+
ret_stat.st_mode = stat.S_IFDIR | int(0e755)
31+
return ret_stat
32+
33+
if path == BROKEN_FILE:
34+
ret_stat.st_mode = stat.S_IFREG | int(0e000)
35+
return ret_stat
36+
37+
return -errno.ENOENT
38+
39+
def readdir(self, path, offset):
40+
yield Direntry('.')
41+
yield Direntry('..')
42+
yield Direntry(BROKEN_FILE[1:])
43+
44+
def open(self, path, flags):
45+
return -errno.EACCES
46+
47+
def read(self, path, size, offset):
48+
return
49+
50+
def listxattr(self, path, size):
51+
if size == 0: return 1
52+
else: return [ FATTR_NAME ]
53+
54+
def getxattr(self, path, attr, size):
55+
if size == 0: return len(FATTR_VALUE.encode('utf8'))
56+
else: return FATTR_VALUE
57+
58+
if __name__ == '__main__':
59+
server = GetAttrBug(dash_s_do='setsingle')
60+
server.parse(errex=1)
61+
server.main()

example/xmp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22

3-
# Copyright (C) 2001 Jeff Epler <jepler@unpythonic.dhs.org>
3+
# Copyright (C) 2001 Jeff Epler <jepler@gmail.com>
44
# Copyright (C) 2006 Csaba Henk <[email protected]>
55
#
66
# This program can be distributed under the terms of the GNU LGPL.

fuse.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#
2-
# Copyright (C) 2001 Jeff Epler <jepler@unpythonic.dhs.org>
2+
# Copyright (C) 2001 Jeff Epler <jepler@gmail.com>
33
# Copyright (C) 2006 Csaba Henk <[email protected]>
44
#
55
# This program can be distributed under the terms of the GNU LGPL.

fuseparts/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "1.0.8post1"
1+
__version__ = "1.0.9post1"

fuseparts/_fusemodule.c

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright (C) 2001 Jeff Epler <jepler@unpythonic.dhs.org>
2+
Copyright (C) 2001 Jeff Epler <jepler@gmail.com>
33
44
This program can be distributed under the terms of the GNU LGPL.
55
See the file COPYING.
@@ -94,12 +94,16 @@
9494
PyErr_SetString(PyExc_ValueError, "non-decodable filename");
9595
return NULL;
9696
}
97-
97+
static inline Py_ssize_t PyString_Size(PyObject *s) {
98+
Py_ssize_t result = -1;
99+
(void)PyUnicode_AsUTF8AndSize(s, &result);
100+
return result;
101+
}
98102
#else
99103
#define PyString_AsString PyUnicode_AsUTF8
104+
#define PyString_Size PyUnicode_GET_LENGTH
100105
#endif
101106
#define PyString_Check PyUnicode_Check
102-
#define PyString_Size PyUnicode_GET_LENGTH
103107
#endif
104108

105109
#ifdef FIX_PATH_DECODING
@@ -683,14 +687,14 @@ read_func(const char *path, char *buf, size_t s, off_t off)
683687
if(PyObject_CheckBuffer(v)) {
684688
PyObject_GetBuffer(v, &buffer, PyBUF_SIMPLE);
685689

686-
if(buffer.len <= s) {
690+
if((size_t)buffer.len <= s) {
687691
memcpy(buf, buffer.buf, buffer.len);
688692
ret = buffer.len;
689693
}
690694

691695
PyBuffer_Release(&buffer);
692696
} else if(PyBytes_Check(v)) {
693-
if(PyBytes_Size(v) > s)
697+
if((size_t)PyBytes_Size(v) > s)
694698
goto OUT_DECREF;
695699
memcpy(buf, PyBytes_AsString(v), PyBytes_Size(v));
696700
ret = PyBytes_Size(v);
@@ -919,6 +923,10 @@ flush_func(const char *path)
919923
EPILOGUE
920924
}
921925

926+
#ifdef __APPLE__
927+
static int
928+
getxattr_func(const char *path, const char *name, char *value, size_t size, uint32_t position)
929+
#else
922930
static int
923931
#if __APPLE__
924932
getxattr_func(const char *path, const char *name, char *value, size_t size, u_int32_t position)
@@ -948,7 +956,7 @@ getxattr_func(const char *path, const char *name, char *value, size_t size)
948956
/* If the size of the value buffer is too small to hold the result, errno
949957
* is set to ERANGE.
950958
*/
951-
if (PyString_Size(v) > size) {
959+
if ((size_t)PyString_Size(v) > size) {
952960
ret = -ERANGE;
953961
goto OUT_DECREF;
954962
}
@@ -984,9 +992,9 @@ listxattr_func(const char *path, char *list, size_t size)
984992
}
985993

986994
for (;;) {
987-
int ilen;
995+
size_t ilen;
988996

989-
w = PyIter_Next(iter);
997+
w = PyIter_Next(iter);
990998
if (!w) {
991999
ret = lx - list;
9921000
break;
@@ -1020,7 +1028,11 @@ listxattr_func(const char *path, char *list, size_t size)
10201028

10211029
EPILOGUE
10221030
}
1023-
1031+
#ifdef __APPLE__
1032+
static int
1033+
setxattr_func(const char *path, const char *name,
1034+
const char *value, size_t size, int flags, uint32_t position)
1035+
#else
10241036
static int
10251037
#if __APPLE__
10261038
setxattr_func(const char *path, const char *name, const char *value,
@@ -1277,15 +1289,36 @@ poll_func(const char *path, struct fuse_file_info *fi,
12771289
struct fuse_pollhandle *ph, unsigned *reventsp)
12781290
{
12791291
PyObject *pollhandle = Py_None;
1292+
int ret = -EINVAL;
1293+
PyObject *v;
12801294

1281-
if (ph)
1295+
PYLOCK();
1296+
1297+
if (ph) {
12821298
pollhandle = PyCapsule_New(ph, pollhandle_name, pollhandle_destructor);
1299+
if (!pollhandle) {
1300+
PyErr_Print();
1301+
goto OUT;
1302+
}
1303+
}
12831304

12841305
#ifdef FIX_PATH_DECODING
1285-
PROLOGUE(PYO_CALLWITHFI(fi, poll_cb, O&O, &Path_AsDecodedUnicode, path, pollhandle));
1306+
v = PYO_CALLWITHFI(fi, poll_cb, O&O, &Path_AsDecodedUnicode, path, pollhandle);
12861307
#else
1287-
PROLOGUE(PYO_CALLWITHFI(fi, poll_cb, sO, path, pollhandle));
1308+
v = PYO_CALLWITHFI(fi, poll_cb, sO, path, pollhandle);
12881309
#endif
1310+
if (!v) {
1311+
PyErr_Print();
1312+
goto OUT;
1313+
}
1314+
if (v == Py_None) {
1315+
ret = 0;
1316+
goto OUT_DECREF;
1317+
}
1318+
if (PyInt_Check(v)) {
1319+
ret = PyInt_AsLong(v);
1320+
goto OUT_DECREF;
1321+
}
12891322

12901323
OUT_DECREF:
12911324
Py_DECREF(v);
@@ -1311,7 +1344,9 @@ pyfuse_loop_mt(struct fuse *f)
13111344
#ifdef WITH_THREAD
13121345
PyThreadState *save;
13131346

1347+
#if PY_VERSION_HEX < 0x03070000
13141348
PyEval_InitThreads();
1349+
#endif
13151350
interp = PyThreadState_Get()->interp;
13161351
save = PyEval_SaveThread();
13171352
err = fuse_loop_mt(f);

pytest.ini

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[pytest]
2+
markers =
3+
fstype: Type of filesystem to mount

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ def setup(**kwargs):
103103
package_data={'': ['COPYING', 'AUTHORS', 'FAQ', 'INSTALL',
104104
'README.md', 'README.new_fusepy_api.rst',
105105
'README.package_maintainers.rst']},
106-
author = 'Jeff Epler <[email protected]>, Csaba Henk <[email protected]>, Steven James, Miklos Szeredi <[email protected]>, Sébastien Delafond<[email protected]>',
106+
author = 'Csaba Henk <[email protected]>, Steven James, Miklos Szeredi <[email protected]>, Sébastien Delafond<[email protected]>',
107107
maintainer = 'Sébastien Delafond',
108108
maintainer_email = '[email protected]',
109109
ext_modules = [fusemodule],

tests/test_basic.py

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import os
2+
import time
3+
import sys
4+
import struct
5+
import subprocess
6+
import pathlib
7+
import tempfile
8+
import pytest
9+
import fcntl
10+
11+
topdir = pathlib.Path(__file__).parent.parent
12+
13+
@pytest.fixture
14+
def filesystem(request):
15+
fstype = request.node.get_closest_marker("fstype").args[0]
16+
17+
with tempfile.TemporaryDirectory() as tmpdir:
18+
st_dev = os.stat(tmpdir).st_dev
19+
proc = subprocess.Popen([sys.executable, "-d", topdir / "example" / f"{fstype}.py", tmpdir], stdin=subprocess.DEVNULL)
20+
21+
deadline = time.time() + 1
22+
while time.time() < deadline:
23+
new_st_dev = os.stat(tmpdir).st_dev
24+
if new_st_dev != st_dev:
25+
break
26+
time.sleep(.01)
27+
if new_st_dev == st_dev:
28+
proc.terminate()
29+
raise RuntimeError("Filesystem did not mount within 1s")
30+
31+
32+
yield pathlib.Path(tmpdir)
33+
34+
subprocess.call(["fusermount", "-u", "-q", "-z", tmpdir])
35+
36+
deadline = time.time() + 1
37+
while time.time() < deadline:
38+
result = proc.poll()
39+
if result is not None:
40+
if result != 0:
41+
raise RuntimeError("Filesystem exited with an error: {result}")
42+
return
43+
time.sleep(.01)
44+
45+
proc.terminate()
46+
raise RuntimeError("Filesystem failed to exit within 1s after unmount")
47+
48+
@pytest.mark.fstype("hello")
49+
def test_hello(filesystem):
50+
content = (filesystem / "hello").read_text(encoding="utf-8")
51+
assert content == "Hello World!\n"
52+
53+
@pytest.mark.fstype("fioc")
54+
def test_fioc(filesystem):
55+
FIOC_GET_SIZE, FIOC_SET_SIZE = 0x80084500, 0x40084501
56+
with (filesystem / "fioc").open("rb") as f:
57+
b = struct.pack("L", 42)
58+
fcntl.ioctl(f.fileno(), FIOC_SET_SIZE, b)
59+
60+
b = bytearray(struct.calcsize('l'))
61+
fcntl.ioctl(f.fileno(), FIOC_GET_SIZE, b)
62+
assert struct.unpack("L", b)[0] == 42
63+
64+
@pytest.mark.fstype("xattr")
65+
def test_xattr(filesystem):
66+
assert os.getxattr(filesystem / "utf8_attr", "user.xdg.comment").decode("utf-8") == 'ああ、メッセージは切り取られていない'

0 commit comments

Comments
 (0)