pybiolib 1.1.1881__py3-none-any.whl → 1.2.7.dev0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. biolib/__init__.py +11 -4
  2. biolib/_data_record/data_record.py +278 -0
  3. biolib/_internal/data_record/__init__.py +1 -1
  4. biolib/_internal/data_record/data_record.py +97 -151
  5. biolib/_internal/data_record/remote_storage_endpoint.py +18 -7
  6. biolib/_internal/file_utils.py +77 -0
  7. biolib/_internal/fuse_mount/__init__.py +1 -0
  8. biolib/_internal/fuse_mount/experiment_fuse_mount.py +209 -0
  9. biolib/_internal/http_client.py +31 -9
  10. biolib/_internal/lfs/__init__.py +1 -0
  11. biolib/_internal/libs/__init__.py +1 -0
  12. biolib/_internal/libs/fusepy/__init__.py +1257 -0
  13. biolib/_internal/push_application.py +6 -1
  14. biolib/_internal/runtime.py +3 -56
  15. biolib/_internal/types/__init__.py +4 -0
  16. biolib/_internal/types/app.py +9 -0
  17. biolib/_internal/types/data_record.py +40 -0
  18. biolib/_internal/types/experiment.py +10 -0
  19. biolib/_internal/types/resource.py +14 -0
  20. biolib/_internal/types/typing.py +7 -0
  21. biolib/_internal/utils/multinode.py +264 -0
  22. biolib/_runtime/runtime.py +84 -0
  23. biolib/api/__init__.py +1 -0
  24. biolib/api/client.py +39 -17
  25. biolib/app/app.py +34 -71
  26. biolib/biolib_api_client/api_client.py +9 -2
  27. biolib/biolib_api_client/app_types.py +3 -2
  28. biolib/biolib_api_client/biolib_job_api.py +6 -0
  29. biolib/biolib_api_client/job_types.py +4 -4
  30. biolib/biolib_api_client/lfs_types.py +8 -2
  31. biolib/biolib_binary_format/remote_endpoints.py +12 -10
  32. biolib/biolib_binary_format/utils.py +23 -3
  33. biolib/cli/auth.py +1 -1
  34. biolib/cli/data_record.py +45 -6
  35. biolib/cli/lfs.py +10 -6
  36. biolib/compute_node/cloud_utils/cloud_utils.py +13 -16
  37. biolib/compute_node/job_worker/executors/docker_executor.py +127 -108
  38. biolib/compute_node/job_worker/job_storage.py +17 -5
  39. biolib/compute_node/job_worker/job_worker.py +25 -15
  40. biolib/compute_node/remote_host_proxy.py +72 -84
  41. biolib/compute_node/webserver/webserver_types.py +0 -1
  42. biolib/compute_node/webserver/worker_thread.py +42 -39
  43. biolib/experiments/experiment.py +75 -44
  44. biolib/jobs/job.py +98 -19
  45. biolib/jobs/job_result.py +46 -21
  46. biolib/jobs/types.py +1 -1
  47. biolib/runtime/__init__.py +2 -1
  48. biolib/sdk/__init__.py +18 -7
  49. biolib/typing_utils.py +2 -7
  50. biolib/user/sign_in.py +2 -2
  51. biolib/utils/seq_util.py +38 -35
  52. {pybiolib-1.1.1881.dist-info → pybiolib-1.2.7.dev0.dist-info}/METADATA +1 -1
  53. {pybiolib-1.1.1881.dist-info → pybiolib-1.2.7.dev0.dist-info}/RECORD +57 -45
  54. biolib/experiments/types.py +0 -9
  55. biolib/lfs/__init__.py +0 -4
  56. biolib/lfs/utils.py +0 -153
  57. /biolib/{lfs → _internal/lfs}/cache.py +0 -0
  58. {pybiolib-1.1.1881.dist-info → pybiolib-1.2.7.dev0.dist-info}/LICENSE +0 -0
  59. {pybiolib-1.1.1881.dist-info → pybiolib-1.2.7.dev0.dist-info}/WHEEL +0 -0
  60. {pybiolib-1.1.1881.dist-info → pybiolib-1.2.7.dev0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,1257 @@
1
+ # Copyright (c) 2012 Terence Honles <terence@honles.com> (maintainer)
2
+ # Copyright (c) 2008 Giorgos Verigakis <verigak@gmail.com> (author)
3
+ #
4
+ # Permission to use, copy, modify, and distribute this software for any
5
+ # purpose with or without fee is hereby granted, provided that the above
6
+ # copyright notice and this permission notice appear in all copies.
7
+ #
8
+ # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11
+ # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13
+ # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14
+ # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
+
16
+ from __future__ import print_function, absolute_import, division
17
+
18
+ import ctypes
19
+ import errno
20
+ import logging
21
+ import os
22
+ import warnings
23
+
24
+ from ctypes.util import find_library
25
+ from platform import machine, system
26
+ from signal import signal, SIGINT, SIG_DFL
27
+ from stat import S_IFDIR
28
+ from traceback import print_exc
29
+
30
+
31
+ try:
32
+ from functools import partial
33
+ except ImportError:
34
+ # http://docs.python.org/library/functools.html#functools.partial
35
+ def partial(func, *args, **keywords):
36
+ def newfunc(*fargs, **fkeywords):
37
+ newkeywords = keywords.copy()
38
+ newkeywords.update(fkeywords)
39
+ return func(*(args + fargs), **newkeywords)
40
+
41
+ newfunc.func = func
42
+ newfunc.args = args
43
+ newfunc.keywords = keywords
44
+ return newfunc
45
+
46
+ try:
47
+ basestring
48
+ except NameError:
49
+ basestring = str
50
+
51
+ log = logging.getLogger("fuse")
52
+ _system = system()
53
+ _machine = machine()
54
+
55
+ if _system == 'Windows':
56
+ # NOTE:
57
+ #
58
+ # sizeof(long)==4 on Windows 32-bit and 64-bit
59
+ # sizeof(long)==4 on Cygwin 32-bit and ==8 on Cygwin 64-bit
60
+ #
61
+ # We have to fix up c_long and c_ulong so that it matches the
62
+ # Cygwin (and UNIX) sizes when run on Windows.
63
+ import sys
64
+ if sys.maxsize > 0xffffffff:
65
+ c_win_long = ctypes.c_int64
66
+ c_win_ulong = ctypes.c_uint64
67
+ else:
68
+ c_win_long = ctypes.c_int32
69
+ c_win_ulong = ctypes.c_uint32
70
+
71
+ if _system == 'Windows' or _system.startswith('CYGWIN'):
72
+ class c_timespec(ctypes.Structure):
73
+ _fields_ = [('tv_sec', c_win_long), ('tv_nsec', c_win_long)]
74
+ else:
75
+ class c_timespec(ctypes.Structure):
76
+ _fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)]
77
+
78
+ class c_utimbuf(ctypes.Structure):
79
+ _fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
80
+
81
+ class c_stat(ctypes.Structure):
82
+ pass # Platform dependent
83
+
84
+ _libfuse_path = os.environ.get('FUSE_LIBRARY_PATH')
85
+ if not _libfuse_path:
86
+ if _system == 'Darwin':
87
+ # libfuse dependency
88
+ _libiconv = ctypes.CDLL(find_library('iconv'), ctypes.RTLD_GLOBAL)
89
+
90
+ _libfuse_path = (find_library('fuse4x') or find_library('osxfuse') or
91
+ find_library('fuse'))
92
+ elif _system == 'Windows':
93
+ try:
94
+ import _winreg as reg
95
+ except ImportError:
96
+ import winreg as reg
97
+ def Reg32GetValue(rootkey, keyname, valname):
98
+ key, val = None, None
99
+ try:
100
+ key = reg.OpenKey(rootkey, keyname, 0, reg.KEY_READ | reg.KEY_WOW64_32KEY)
101
+ val = str(reg.QueryValueEx(key, valname)[0])
102
+ except WindowsError:
103
+ pass
104
+ finally:
105
+ if key is not None:
106
+ reg.CloseKey(key)
107
+ return val
108
+ _libfuse_path = Reg32GetValue(reg.HKEY_LOCAL_MACHINE, r"SOFTWARE\WinFsp", r"InstallDir")
109
+ if _libfuse_path:
110
+ _libfuse_path += r"bin\winfsp-%s.dll" % ("x64" if sys.maxsize > 0xffffffff else "x86")
111
+ else:
112
+ _libfuse_path = find_library('fuse')
113
+
114
+ if not _libfuse_path:
115
+ raise EnvironmentError('Unable to find libfuse')
116
+ else:
117
+ _libfuse = ctypes.CDLL(_libfuse_path)
118
+
119
+ if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'):
120
+ _system = 'Darwin-MacFuse'
121
+
122
+
123
+ if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'):
124
+ ENOTSUP = 45
125
+
126
+ c_dev_t = ctypes.c_int32
127
+ c_fsblkcnt_t = ctypes.c_ulong
128
+ c_fsfilcnt_t = ctypes.c_ulong
129
+ c_gid_t = ctypes.c_uint32
130
+ c_mode_t = ctypes.c_uint16
131
+ c_off_t = ctypes.c_int64
132
+ c_pid_t = ctypes.c_int32
133
+ c_uid_t = ctypes.c_uint32
134
+ setxattr_t = ctypes.CFUNCTYPE(
135
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
136
+ ctypes.POINTER(ctypes.c_byte), ctypes.c_size_t, ctypes.c_int,
137
+ ctypes.c_uint32)
138
+ getxattr_t = ctypes.CFUNCTYPE(
139
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
140
+ ctypes.POINTER(ctypes.c_byte),
141
+ ctypes.c_size_t, ctypes.c_uint32)
142
+ if _system == 'Darwin':
143
+ c_stat._fields_ = [
144
+ ('st_dev', c_dev_t),
145
+ ('st_mode', c_mode_t),
146
+ ('st_nlink', ctypes.c_uint16),
147
+ ('st_ino', ctypes.c_uint64),
148
+ ('st_uid', c_uid_t),
149
+ ('st_gid', c_gid_t),
150
+ ('st_rdev', c_dev_t),
151
+ ('st_atimespec', c_timespec),
152
+ ('st_mtimespec', c_timespec),
153
+ ('st_ctimespec', c_timespec),
154
+ ('st_birthtimespec', c_timespec),
155
+ ('st_size', c_off_t),
156
+ ('st_blocks', ctypes.c_int64),
157
+ ('st_blksize', ctypes.c_int32),
158
+ ('st_flags', ctypes.c_int32),
159
+ ('st_gen', ctypes.c_int32),
160
+ ('st_lspare', ctypes.c_int32),
161
+ ('st_qspare', ctypes.c_int64)]
162
+ else:
163
+ c_stat._fields_ = [
164
+ ('st_dev', c_dev_t),
165
+ ('st_ino', ctypes.c_uint32),
166
+ ('st_mode', c_mode_t),
167
+ ('st_nlink', ctypes.c_uint16),
168
+ ('st_uid', c_uid_t),
169
+ ('st_gid', c_gid_t),
170
+ ('st_rdev', c_dev_t),
171
+ ('st_atimespec', c_timespec),
172
+ ('st_mtimespec', c_timespec),
173
+ ('st_ctimespec', c_timespec),
174
+ ('st_size', c_off_t),
175
+ ('st_blocks', ctypes.c_int64),
176
+ ('st_blksize', ctypes.c_int32)]
177
+ elif _system == 'Linux':
178
+ ENOTSUP = 95
179
+
180
+ c_dev_t = ctypes.c_ulonglong
181
+ c_fsblkcnt_t = ctypes.c_ulonglong
182
+ c_fsfilcnt_t = ctypes.c_ulonglong
183
+ c_gid_t = ctypes.c_uint
184
+ c_mode_t = ctypes.c_uint
185
+ c_off_t = ctypes.c_longlong
186
+ c_pid_t = ctypes.c_int
187
+ c_uid_t = ctypes.c_uint
188
+ setxattr_t = ctypes.CFUNCTYPE(
189
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
190
+ ctypes.POINTER(ctypes.c_byte), ctypes.c_size_t, ctypes.c_int)
191
+
192
+ getxattr_t = ctypes.CFUNCTYPE(
193
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
194
+ ctypes.POINTER(ctypes.c_byte), ctypes.c_size_t)
195
+
196
+ if _machine == 'x86_64':
197
+ c_stat._fields_ = [
198
+ ('st_dev', c_dev_t),
199
+ ('st_ino', ctypes.c_ulong),
200
+ ('st_nlink', ctypes.c_ulong),
201
+ ('st_mode', c_mode_t),
202
+ ('st_uid', c_uid_t),
203
+ ('st_gid', c_gid_t),
204
+ ('__pad0', ctypes.c_int),
205
+ ('st_rdev', c_dev_t),
206
+ ('st_size', c_off_t),
207
+ ('st_blksize', ctypes.c_long),
208
+ ('st_blocks', ctypes.c_long),
209
+ ('st_atimespec', c_timespec),
210
+ ('st_mtimespec', c_timespec),
211
+ ('st_ctimespec', c_timespec)]
212
+ elif _machine == 'mips':
213
+ c_stat._fields_ = [
214
+ ('st_dev', c_dev_t),
215
+ ('__pad1_1', ctypes.c_ulong),
216
+ ('__pad1_2', ctypes.c_ulong),
217
+ ('__pad1_3', ctypes.c_ulong),
218
+ ('st_ino', ctypes.c_ulong),
219
+ ('st_mode', c_mode_t),
220
+ ('st_nlink', ctypes.c_ulong),
221
+ ('st_uid', c_uid_t),
222
+ ('st_gid', c_gid_t),
223
+ ('st_rdev', c_dev_t),
224
+ ('__pad2_1', ctypes.c_ulong),
225
+ ('__pad2_2', ctypes.c_ulong),
226
+ ('st_size', c_off_t),
227
+ ('__pad3', ctypes.c_ulong),
228
+ ('st_atimespec', c_timespec),
229
+ ('__pad4', ctypes.c_ulong),
230
+ ('st_mtimespec', c_timespec),
231
+ ('__pad5', ctypes.c_ulong),
232
+ ('st_ctimespec', c_timespec),
233
+ ('__pad6', ctypes.c_ulong),
234
+ ('st_blksize', ctypes.c_long),
235
+ ('st_blocks', ctypes.c_long),
236
+ ('__pad7_1', ctypes.c_ulong),
237
+ ('__pad7_2', ctypes.c_ulong),
238
+ ('__pad7_3', ctypes.c_ulong),
239
+ ('__pad7_4', ctypes.c_ulong),
240
+ ('__pad7_5', ctypes.c_ulong),
241
+ ('__pad7_6', ctypes.c_ulong),
242
+ ('__pad7_7', ctypes.c_ulong),
243
+ ('__pad7_8', ctypes.c_ulong),
244
+ ('__pad7_9', ctypes.c_ulong),
245
+ ('__pad7_10', ctypes.c_ulong),
246
+ ('__pad7_11', ctypes.c_ulong),
247
+ ('__pad7_12', ctypes.c_ulong),
248
+ ('__pad7_13', ctypes.c_ulong),
249
+ ('__pad7_14', ctypes.c_ulong)]
250
+ elif _machine == 'ppc':
251
+ c_stat._fields_ = [
252
+ ('st_dev', c_dev_t),
253
+ ('st_ino', ctypes.c_ulonglong),
254
+ ('st_mode', c_mode_t),
255
+ ('st_nlink', ctypes.c_uint),
256
+ ('st_uid', c_uid_t),
257
+ ('st_gid', c_gid_t),
258
+ ('st_rdev', c_dev_t),
259
+ ('__pad2', ctypes.c_ushort),
260
+ ('st_size', c_off_t),
261
+ ('st_blksize', ctypes.c_long),
262
+ ('st_blocks', ctypes.c_longlong),
263
+ ('st_atimespec', c_timespec),
264
+ ('st_mtimespec', c_timespec),
265
+ ('st_ctimespec', c_timespec)]
266
+ elif _machine == 'ppc64' or _machine == 'ppc64le':
267
+ c_stat._fields_ = [
268
+ ('st_dev', c_dev_t),
269
+ ('st_ino', ctypes.c_ulong),
270
+ ('st_nlink', ctypes.c_ulong),
271
+ ('st_mode', c_mode_t),
272
+ ('st_uid', c_uid_t),
273
+ ('st_gid', c_gid_t),
274
+ ('__pad', ctypes.c_uint),
275
+ ('st_rdev', c_dev_t),
276
+ ('st_size', c_off_t),
277
+ ('st_blksize', ctypes.c_long),
278
+ ('st_blocks', ctypes.c_long),
279
+ ('st_atimespec', c_timespec),
280
+ ('st_mtimespec', c_timespec),
281
+ ('st_ctimespec', c_timespec)]
282
+ elif _machine == 'aarch64':
283
+ c_stat._fields_ = [
284
+ ('st_dev', c_dev_t),
285
+ ('st_ino', ctypes.c_ulong),
286
+ ('st_mode', c_mode_t),
287
+ ('st_nlink', ctypes.c_uint),
288
+ ('st_uid', c_uid_t),
289
+ ('st_gid', c_gid_t),
290
+ ('st_rdev', c_dev_t),
291
+ ('__pad1', ctypes.c_ulong),
292
+ ('st_size', c_off_t),
293
+ ('st_blksize', ctypes.c_int),
294
+ ('__pad2', ctypes.c_int),
295
+ ('st_blocks', ctypes.c_long),
296
+ ('st_atimespec', c_timespec),
297
+ ('st_mtimespec', c_timespec),
298
+ ('st_ctimespec', c_timespec)]
299
+ else:
300
+ # i686, use as fallback for everything else
301
+ c_stat._fields_ = [
302
+ ('st_dev', c_dev_t),
303
+ ('__pad1', ctypes.c_ushort),
304
+ ('__st_ino', ctypes.c_ulong),
305
+ ('st_mode', c_mode_t),
306
+ ('st_nlink', ctypes.c_uint),
307
+ ('st_uid', c_uid_t),
308
+ ('st_gid', c_gid_t),
309
+ ('st_rdev', c_dev_t),
310
+ ('__pad2', ctypes.c_ushort),
311
+ ('st_size', c_off_t),
312
+ ('st_blksize', ctypes.c_long),
313
+ ('st_blocks', ctypes.c_longlong),
314
+ ('st_atimespec', c_timespec),
315
+ ('st_mtimespec', c_timespec),
316
+ ('st_ctimespec', c_timespec),
317
+ ('st_ino', ctypes.c_ulonglong)]
318
+ elif _system == 'Windows' or _system.startswith('CYGWIN'):
319
+ ENOTSUP = 129 if _system == 'Windows' else 134
320
+ c_dev_t = ctypes.c_uint
321
+ c_fsblkcnt_t = c_win_ulong
322
+ c_fsfilcnt_t = c_win_ulong
323
+ c_gid_t = ctypes.c_uint
324
+ c_mode_t = ctypes.c_uint
325
+ c_off_t = ctypes.c_longlong
326
+ c_pid_t = ctypes.c_int
327
+ c_uid_t = ctypes.c_uint
328
+ setxattr_t = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
329
+ ctypes.POINTER(ctypes.c_byte), ctypes.c_size_t, ctypes.c_int)
330
+ getxattr_t = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
331
+ ctypes.POINTER(ctypes.c_byte), ctypes.c_size_t)
332
+ c_stat._fields_ = [
333
+ ('st_dev', c_dev_t),
334
+ ('st_ino', ctypes.c_ulonglong),
335
+ ('st_mode', c_mode_t),
336
+ ('st_nlink', ctypes.c_ushort),
337
+ ('st_uid', c_uid_t),
338
+ ('st_gid', c_gid_t),
339
+ ('st_rdev', c_dev_t),
340
+ ('st_size', c_off_t),
341
+ ('st_atimespec', c_timespec),
342
+ ('st_mtimespec', c_timespec),
343
+ ('st_ctimespec', c_timespec),
344
+ ('st_blksize', ctypes.c_int),
345
+ ('st_blocks', ctypes.c_longlong),
346
+ ('st_birthtimespec', c_timespec)]
347
+ else:
348
+ raise NotImplementedError('%s is not supported.' % _system)
349
+
350
+
351
+ if _system == 'FreeBSD':
352
+ c_fsblkcnt_t = ctypes.c_uint64
353
+ c_fsfilcnt_t = ctypes.c_uint64
354
+ setxattr_t = ctypes.CFUNCTYPE(
355
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
356
+ ctypes.POINTER(ctypes.c_byte), ctypes.c_size_t, ctypes.c_int)
357
+
358
+ getxattr_t = ctypes.CFUNCTYPE(
359
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p,
360
+ ctypes.POINTER(ctypes.c_byte), ctypes.c_size_t)
361
+
362
+ class c_statvfs(ctypes.Structure):
363
+ _fields_ = [
364
+ ('f_bavail', c_fsblkcnt_t),
365
+ ('f_bfree', c_fsblkcnt_t),
366
+ ('f_blocks', c_fsblkcnt_t),
367
+ ('f_favail', c_fsfilcnt_t),
368
+ ('f_ffree', c_fsfilcnt_t),
369
+ ('f_files', c_fsfilcnt_t),
370
+ ('f_bsize', ctypes.c_ulong),
371
+ ('f_flag', ctypes.c_ulong),
372
+ ('f_frsize', ctypes.c_ulong)]
373
+ elif _system == 'Windows' or _system.startswith('CYGWIN'):
374
+ class c_statvfs(ctypes.Structure):
375
+ _fields_ = [
376
+ ('f_bsize', c_win_ulong),
377
+ ('f_frsize', c_win_ulong),
378
+ ('f_blocks', c_fsblkcnt_t),
379
+ ('f_bfree', c_fsblkcnt_t),
380
+ ('f_bavail', c_fsblkcnt_t),
381
+ ('f_files', c_fsfilcnt_t),
382
+ ('f_ffree', c_fsfilcnt_t),
383
+ ('f_favail', c_fsfilcnt_t),
384
+ ('f_fsid', c_win_ulong),
385
+ ('f_flag', c_win_ulong),
386
+ ('f_namemax', c_win_ulong)]
387
+ else:
388
+ class c_statvfs(ctypes.Structure):
389
+ _fields_ = [
390
+ ('f_bsize', ctypes.c_ulong),
391
+ ('f_frsize', ctypes.c_ulong),
392
+ ('f_blocks', c_fsblkcnt_t),
393
+ ('f_bfree', c_fsblkcnt_t),
394
+ ('f_bavail', c_fsblkcnt_t),
395
+ ('f_files', c_fsfilcnt_t),
396
+ ('f_ffree', c_fsfilcnt_t),
397
+ ('f_favail', c_fsfilcnt_t),
398
+ ('f_fsid', ctypes.c_ulong),
399
+ # ('unused', ctypes.c_int),
400
+ ('f_flag', ctypes.c_ulong),
401
+ ('f_namemax', ctypes.c_ulong)]
402
+
403
+ if _system == 'Windows' or _system.startswith('CYGWIN'):
404
+ class fuse_file_info(ctypes.Structure):
405
+ _fields_ = [
406
+ ('flags', ctypes.c_int),
407
+ ('fh_old', ctypes.c_int),
408
+ ('writepage', ctypes.c_int),
409
+ ('direct_io', ctypes.c_uint, 1),
410
+ ('keep_cache', ctypes.c_uint, 1),
411
+ ('flush', ctypes.c_uint, 1),
412
+ ('padding', ctypes.c_uint, 29),
413
+ ('fh', ctypes.c_uint64),
414
+ ('lock_owner', ctypes.c_uint64)]
415
+ else:
416
+ class fuse_file_info(ctypes.Structure):
417
+ _fields_ = [
418
+ ('flags', ctypes.c_int),
419
+ ('fh_old', ctypes.c_ulong),
420
+ ('writepage', ctypes.c_int),
421
+ ('direct_io', ctypes.c_uint, 1),
422
+ ('keep_cache', ctypes.c_uint, 1),
423
+ ('flush', ctypes.c_uint, 1),
424
+ ('nonseekable', ctypes.c_uint, 1),
425
+ ('flock_release', ctypes.c_uint, 1),
426
+ ('padding', ctypes.c_uint, 27),
427
+ ('fh', ctypes.c_uint64),
428
+ ('lock_owner', ctypes.c_uint64)]
429
+
430
+ class fuse_context(ctypes.Structure):
431
+ _fields_ = [
432
+ ('fuse', ctypes.c_voidp),
433
+ ('uid', c_uid_t),
434
+ ('gid', c_gid_t),
435
+ ('pid', c_pid_t),
436
+ ('private_data', ctypes.c_voidp)]
437
+
438
+ _libfuse.fuse_get_context.restype = ctypes.POINTER(fuse_context)
439
+
440
+
441
+ class fuse_operations(ctypes.Structure):
442
+ _fields_ = [
443
+ ('getattr', ctypes.CFUNCTYPE(
444
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(c_stat))),
445
+
446
+ ('readlink', ctypes.CFUNCTYPE(
447
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_byte),
448
+ ctypes.c_size_t)),
449
+
450
+ ('getdir', ctypes.c_voidp), # Deprecated, use readdir
451
+
452
+ ('mknod', ctypes.CFUNCTYPE(
453
+ ctypes.c_int, ctypes.c_char_p, c_mode_t, c_dev_t)),
454
+
455
+ ('mkdir', ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, c_mode_t)),
456
+ ('unlink', ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p)),
457
+ ('rmdir', ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p)),
458
+
459
+ ('symlink', ctypes.CFUNCTYPE(
460
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p)),
461
+
462
+ ('rename', ctypes.CFUNCTYPE(
463
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p)),
464
+
465
+ ('link', ctypes.CFUNCTYPE(
466
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p)),
467
+
468
+ ('chmod', ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, c_mode_t)),
469
+
470
+ ('chown', ctypes.CFUNCTYPE(
471
+ ctypes.c_int, ctypes.c_char_p, c_uid_t, c_gid_t)),
472
+
473
+ ('truncate', ctypes.CFUNCTYPE(
474
+ ctypes.c_int, ctypes.c_char_p, c_off_t)),
475
+
476
+ ('utime', ctypes.c_voidp), # Deprecated, use utimens
477
+ ('open', ctypes.CFUNCTYPE(
478
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(fuse_file_info))),
479
+
480
+ ('read', ctypes.CFUNCTYPE(
481
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_byte),
482
+ ctypes.c_size_t, c_off_t, ctypes.POINTER(fuse_file_info))),
483
+
484
+ ('write', ctypes.CFUNCTYPE(
485
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_byte),
486
+ ctypes.c_size_t, c_off_t, ctypes.POINTER(fuse_file_info))),
487
+
488
+ ('statfs', ctypes.CFUNCTYPE(
489
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(c_statvfs))),
490
+
491
+ ('flush', ctypes.CFUNCTYPE(
492
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(fuse_file_info))),
493
+
494
+ ('release', ctypes.CFUNCTYPE(
495
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(fuse_file_info))),
496
+
497
+ ('fsync', ctypes.CFUNCTYPE(
498
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_int,
499
+ ctypes.POINTER(fuse_file_info))),
500
+
501
+ ('setxattr', setxattr_t),
502
+ ('getxattr', getxattr_t),
503
+
504
+ ('listxattr', ctypes.CFUNCTYPE(
505
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(ctypes.c_byte),
506
+ ctypes.c_size_t)),
507
+
508
+ ('removexattr', ctypes.CFUNCTYPE(
509
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p)),
510
+
511
+ ('opendir', ctypes.CFUNCTYPE(
512
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(fuse_file_info))),
513
+
514
+ ('readdir', ctypes.CFUNCTYPE(
515
+ ctypes.c_int,
516
+ ctypes.c_char_p,
517
+ ctypes.c_voidp,
518
+ ctypes.CFUNCTYPE(
519
+ ctypes.c_int, ctypes.c_voidp, ctypes.c_char_p,
520
+ ctypes.POINTER(c_stat), c_off_t),
521
+ c_off_t,
522
+ ctypes.POINTER(fuse_file_info))),
523
+
524
+ ('releasedir', ctypes.CFUNCTYPE(
525
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(fuse_file_info))),
526
+
527
+ ('fsyncdir', ctypes.CFUNCTYPE(
528
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_int,
529
+ ctypes.POINTER(fuse_file_info))),
530
+
531
+ ('init', ctypes.CFUNCTYPE(ctypes.c_voidp, ctypes.c_voidp)),
532
+ ('destroy', ctypes.CFUNCTYPE(ctypes.c_voidp, ctypes.c_voidp)),
533
+
534
+ ('access', ctypes.CFUNCTYPE(
535
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_int)),
536
+
537
+ ('create', ctypes.CFUNCTYPE(
538
+ ctypes.c_int, ctypes.c_char_p, c_mode_t,
539
+ ctypes.POINTER(fuse_file_info))),
540
+
541
+ ('ftruncate', ctypes.CFUNCTYPE(
542
+ ctypes.c_int, ctypes.c_char_p, c_off_t,
543
+ ctypes.POINTER(fuse_file_info))),
544
+
545
+ ('fgetattr', ctypes.CFUNCTYPE(
546
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(c_stat),
547
+ ctypes.POINTER(fuse_file_info))),
548
+
549
+ ('lock', ctypes.CFUNCTYPE(
550
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(fuse_file_info),
551
+ ctypes.c_int, ctypes.c_voidp)),
552
+
553
+ ('utimens', ctypes.CFUNCTYPE(
554
+ ctypes.c_int, ctypes.c_char_p, ctypes.POINTER(c_utimbuf))),
555
+
556
+ ('bmap', ctypes.CFUNCTYPE(
557
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_size_t,
558
+ ctypes.POINTER(ctypes.c_ulonglong))),
559
+
560
+ ('flag_nullpath_ok', ctypes.c_uint, 1),
561
+ ('flag_nopath', ctypes.c_uint, 1),
562
+ ('flag_utime_omit_ok', ctypes.c_uint, 1),
563
+ ('flag_reserved', ctypes.c_uint, 29),
564
+
565
+ ('ioctl', ctypes.CFUNCTYPE(
566
+ ctypes.c_int, ctypes.c_char_p, ctypes.c_uint, ctypes.c_void_p,
567
+ ctypes.POINTER(fuse_file_info), ctypes.c_uint, ctypes.c_void_p)),
568
+ ]
569
+
570
+
571
+ def time_of_timespec(ts, use_ns=False):
572
+ if use_ns:
573
+ return ts.tv_sec * 10 ** 9 + ts.tv_nsec
574
+ else:
575
+ return ts.tv_sec + ts.tv_nsec / 1E9
576
+
577
+ def set_st_attrs(st, attrs, use_ns=False):
578
+ for key, val in attrs.items():
579
+ if key in ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime'):
580
+ timespec = getattr(st, key + 'spec', None)
581
+ if timespec is None:
582
+ continue
583
+
584
+ if use_ns:
585
+ timespec.tv_sec, timespec.tv_nsec = divmod(int(val), 10 ** 9)
586
+ else:
587
+ timespec.tv_sec = int(val)
588
+ timespec.tv_nsec = int((val - timespec.tv_sec) * 1E9)
589
+ elif hasattr(st, key):
590
+ setattr(st, key, val)
591
+
592
+
593
+ def fuse_get_context():
594
+ 'Returns a (uid, gid, pid) tuple'
595
+
596
+ ctxp = _libfuse.fuse_get_context()
597
+ ctx = ctxp.contents
598
+ return ctx.uid, ctx.gid, ctx.pid
599
+
600
+
601
+ def fuse_exit():
602
+ '''
603
+ This will shutdown the FUSE mount and cause the call to FUSE(...) to
604
+ return, similar to sending SIGINT to the process.
605
+
606
+ Flags the native FUSE session as terminated and will cause any running FUSE
607
+ event loops to exit on the next opportunity. (see fuse.c::fuse_exit)
608
+ '''
609
+ fuse_ptr = ctypes.c_void_p(_libfuse.fuse_get_context().contents.fuse)
610
+ _libfuse.fuse_exit(fuse_ptr)
611
+
612
+
613
+ class FuseOSError(OSError):
614
+ def __init__(self, errno):
615
+ super(FuseOSError, self).__init__(errno, os.strerror(errno))
616
+
617
+
618
+ class FUSE(object):
619
+ '''
620
+ This class is the lower level interface and should not be subclassed under
621
+ normal use. Its methods are called by fuse.
622
+
623
+ Assumes API version 2.6 or later.
624
+ '''
625
+
626
+ OPTIONS = (
627
+ ('foreground', '-f'),
628
+ ('debug', '-d'),
629
+ ('nothreads', '-s'),
630
+ )
631
+
632
+ def __init__(self, operations, mountpoint, raw_fi=False, encoding='utf-8',
633
+ **kwargs):
634
+
635
+ '''
636
+ Setting raw_fi to True will cause FUSE to pass the fuse_file_info
637
+ class as is to Operations, instead of just the fh field.
638
+
639
+ This gives you access to direct_io, keep_cache, etc.
640
+ '''
641
+
642
+ self.operations = operations
643
+ self.raw_fi = raw_fi
644
+ self.encoding = encoding
645
+ self.__critical_exception = None
646
+
647
+ self.use_ns = getattr(operations, 'use_ns', False)
648
+ if not self.use_ns:
649
+ warnings.warn(
650
+ 'Time as floating point seconds for utimens is deprecated!\n'
651
+ 'To enable time as nanoseconds set the property "use_ns" to '
652
+ 'True in your operations class or set your fusepy '
653
+ 'requirements to <4.',
654
+ DeprecationWarning)
655
+
656
+ args = ['fuse']
657
+
658
+ args.extend(flag for arg, flag in self.OPTIONS
659
+ if kwargs.pop(arg, False))
660
+
661
+ kwargs.setdefault('fsname', operations.__class__.__name__)
662
+ args.append('-o')
663
+ args.append(','.join(self._normalize_fuse_options(**kwargs)))
664
+ args.append(mountpoint)
665
+
666
+ args = [arg.encode(encoding) for arg in args]
667
+ argv = (ctypes.c_char_p * len(args))(*args)
668
+
669
+ fuse_ops = fuse_operations()
670
+ for ent in fuse_operations._fields_:
671
+ name, prototype = ent[:2]
672
+
673
+ check_name = name
674
+
675
+ # ftruncate()/fgetattr() are implemented in terms of their
676
+ # non-f-prefixed versions in the operations object
677
+ if check_name in ["ftruncate", "fgetattr"]:
678
+ check_name = check_name[1:]
679
+
680
+ val = getattr(operations, check_name, None)
681
+ if val is None:
682
+ continue
683
+
684
+ # Function pointer members are tested for using the
685
+ # getattr(operations, name) above but are dynamically
686
+ # invoked using self.operations(name)
687
+ if hasattr(prototype, 'argtypes'):
688
+ val = prototype(partial(self._wrapper, getattr(self, name)))
689
+
690
+ setattr(fuse_ops, name, val)
691
+
692
+ try:
693
+ old_handler = signal(SIGINT, SIG_DFL)
694
+ except ValueError:
695
+ old_handler = SIG_DFL
696
+
697
+ err = _libfuse.fuse_main_real(
698
+ len(args), argv, ctypes.pointer(fuse_ops),
699
+ ctypes.sizeof(fuse_ops),
700
+ None)
701
+
702
+ try:
703
+ signal(SIGINT, old_handler)
704
+ except ValueError:
705
+ pass
706
+
707
+ del self.operations # Invoke the destructor
708
+ if self.__critical_exception:
709
+ raise self.__critical_exception
710
+ if err:
711
+ raise RuntimeError(err)
712
+
713
+ @staticmethod
714
+ def _normalize_fuse_options(**kargs):
715
+ for key, value in kargs.items():
716
+ if isinstance(value, bool):
717
+ if value is True:
718
+ yield key
719
+ else:
720
+ yield '%s=%s' % (key, value)
721
+
722
+ @staticmethod
723
+ def _wrapper(func, *args, **kwargs):
724
+ 'Decorator for the methods that follow'
725
+
726
+ try:
727
+ if func.__name__ == "init":
728
+ # init may not fail, as its return code is just stored as
729
+ # private_data field of struct fuse_context
730
+ return func(*args, **kwargs) or 0
731
+
732
+ else:
733
+ try:
734
+ return func(*args, **kwargs) or 0
735
+
736
+ except OSError as e:
737
+ if e.errno > 0:
738
+ log.debug(
739
+ "FUSE operation %s raised a %s, returning errno %s.",
740
+ func.__name__, type(e), e.errno, exc_info=True)
741
+ return -e.errno
742
+ else:
743
+ log.error(
744
+ "FUSE operation %s raised an OSError with negative "
745
+ "errno %s, returning errno.EINVAL.",
746
+ func.__name__, e.errno, exc_info=True)
747
+ return -errno.EINVAL
748
+
749
+ except Exception:
750
+ log.error("Uncaught exception from FUSE operation %s, "
751
+ "returning errno.EINVAL.",
752
+ func.__name__, exc_info=True)
753
+ return -errno.EINVAL
754
+
755
+ except BaseException as e:
756
+ # self.__critical_exception = e
757
+ log.critical(
758
+ "Uncaught critical exception from FUSE operation %s, aborting.",
759
+ func.__name__, exc_info=True)
760
+ # the raised exception (even SystemExit) will be caught by FUSE
761
+ # potentially causing SIGSEGV, so tell system to stop/interrupt FUSE
762
+ fuse_exit()
763
+ return -errno.EFAULT
764
+
765
+ def _decode_optional_path(self, path):
766
+ # NB: this method is intended for fuse operations that
767
+ # allow the path argument to be NULL,
768
+ # *not* as a generic path decoding method
769
+ if path is None:
770
+ return None
771
+ return path.decode(self.encoding)
772
+
773
+ def getattr(self, path, buf):
774
+ return self.fgetattr(path, buf, None)
775
+
776
+ def readlink(self, path, buf, bufsize):
777
+ ret = self.operations('readlink', path.decode(self.encoding)) \
778
+ .encode(self.encoding)
779
+
780
+ # copies a string into the given buffer
781
+ # (null terminated and truncated if necessary)
782
+ data = ctypes.create_string_buffer(ret[:bufsize - 1])
783
+ ctypes.memmove(buf, data, len(data))
784
+ return 0
785
+
786
+ def mknod(self, path, mode, dev):
787
+ return self.operations('mknod', path.decode(self.encoding), mode, dev)
788
+
789
+ def mkdir(self, path, mode):
790
+ return self.operations('mkdir', path.decode(self.encoding), mode)
791
+
792
+ def unlink(self, path):
793
+ return self.operations('unlink', path.decode(self.encoding))
794
+
795
+ def rmdir(self, path):
796
+ return self.operations('rmdir', path.decode(self.encoding))
797
+
798
+ def symlink(self, source, target):
799
+ 'creates a symlink `target -> source` (e.g. ln -s source target)'
800
+
801
+ return self.operations('symlink', target.decode(self.encoding),
802
+ source.decode(self.encoding))
803
+
804
+ def rename(self, old, new):
805
+ return self.operations('rename', old.decode(self.encoding),
806
+ new.decode(self.encoding))
807
+
808
+ def link(self, source, target):
809
+ 'creates a hard link `target -> source` (e.g. ln source target)'
810
+
811
+ return self.operations('link', target.decode(self.encoding),
812
+ source.decode(self.encoding))
813
+
814
+ def chmod(self, path, mode):
815
+ return self.operations('chmod', path.decode(self.encoding), mode)
816
+
817
+ def chown(self, path, uid, gid):
818
+ # Check if any of the arguments is a -1 that has overflowed
819
+ if c_uid_t(uid + 1).value == 0:
820
+ uid = -1
821
+ if c_gid_t(gid + 1).value == 0:
822
+ gid = -1
823
+
824
+ return self.operations('chown', path.decode(self.encoding), uid, gid)
825
+
826
+ def truncate(self, path, length):
827
+ return self.operations('truncate', path.decode(self.encoding), length)
828
+
829
+ def open(self, path, fip):
830
+ fi = fip.contents
831
+ if self.raw_fi:
832
+ return self.operations('open', path.decode(self.encoding), fi)
833
+ else:
834
+ fi.fh = self.operations('open', path.decode(self.encoding),
835
+ fi.flags)
836
+
837
+ return 0
838
+
839
+ def read(self, path, buf, size, offset, fip):
840
+ if self.raw_fi:
841
+ fh = fip.contents
842
+ else:
843
+ fh = fip.contents.fh
844
+
845
+ ret = self.operations('read', self._decode_optional_path(path), size,
846
+ offset, fh)
847
+
848
+ if not ret:
849
+ return 0
850
+
851
+ retsize = len(ret)
852
+ assert retsize <= size, \
853
+ 'actual amount read %d greater than expected %d' % (retsize, size)
854
+
855
+ ctypes.memmove(buf, ret, retsize)
856
+ return retsize
857
+
858
+ def write(self, path, buf, size, offset, fip):
859
+ data = ctypes.string_at(buf, size)
860
+
861
+ if self.raw_fi:
862
+ fh = fip.contents
863
+ else:
864
+ fh = fip.contents.fh
865
+
866
+ return self.operations('write', self._decode_optional_path(path), data,
867
+ offset, fh)
868
+
869
+ def statfs(self, path, buf):
870
+ stv = buf.contents
871
+ attrs = self.operations('statfs', path.decode(self.encoding))
872
+ for key, val in attrs.items():
873
+ if hasattr(stv, key):
874
+ setattr(stv, key, val)
875
+
876
+ return 0
877
+
878
+ def flush(self, path, fip):
879
+ if self.raw_fi:
880
+ fh = fip.contents
881
+ else:
882
+ fh = fip.contents.fh
883
+
884
+ return self.operations('flush', self._decode_optional_path(path), fh)
885
+
886
+ def release(self, path, fip):
887
+ if self.raw_fi:
888
+ fh = fip.contents
889
+ else:
890
+ fh = fip.contents.fh
891
+
892
+ return self.operations('release', self._decode_optional_path(path), fh)
893
+
894
+ def fsync(self, path, datasync, fip):
895
+ if self.raw_fi:
896
+ fh = fip.contents
897
+ else:
898
+ fh = fip.contents.fh
899
+
900
+ return self.operations('fsync', self._decode_optional_path(path), datasync,
901
+ fh)
902
+
903
+ def setxattr(self, path, name, value, size, options, *args):
904
+ return self.operations('setxattr', path.decode(self.encoding),
905
+ name.decode(self.encoding),
906
+ ctypes.string_at(value, size), options, *args)
907
+
908
+ def getxattr(self, path, name, value, size, *args):
909
+ ret = self.operations('getxattr', path.decode(self.encoding),
910
+ name.decode(self.encoding), *args)
911
+
912
+ retsize = len(ret)
913
+ # allow size queries
914
+ if not value:
915
+ return retsize
916
+
917
+ # do not truncate
918
+ if retsize > size:
919
+ return -errno.ERANGE
920
+
921
+ # Does not add trailing 0
922
+ buf = ctypes.create_string_buffer(ret, retsize)
923
+ ctypes.memmove(value, buf, retsize)
924
+
925
+ return retsize
926
+
927
+ def listxattr(self, path, namebuf, size):
928
+ attrs = self.operations('listxattr', path.decode(self.encoding)) or ''
929
+ ret = '\x00'.join(attrs).encode(self.encoding)
930
+ if len(ret) > 0:
931
+ ret += '\x00'.encode(self.encoding)
932
+
933
+ retsize = len(ret)
934
+ # allow size queries
935
+ if not namebuf:
936
+ return retsize
937
+
938
+ # do not truncate
939
+ if retsize > size:
940
+ return -errno.ERANGE
941
+
942
+ buf = ctypes.create_string_buffer(ret, retsize)
943
+ ctypes.memmove(namebuf, buf, retsize)
944
+
945
+ return retsize
946
+
947
+ def removexattr(self, path, name):
948
+ return self.operations('removexattr', path.decode(self.encoding),
949
+ name.decode(self.encoding))
950
+
951
+ def opendir(self, path, fip):
952
+ # Ignore raw_fi
953
+ fip.contents.fh = self.operations('opendir',
954
+ path.decode(self.encoding))
955
+
956
+ return 0
957
+
958
+ def readdir(self, path, buf, filler, offset, fip):
959
+ # Ignore raw_fi
960
+ for item in self.operations('readdir', self._decode_optional_path(path),
961
+ fip.contents.fh):
962
+
963
+ if isinstance(item, basestring):
964
+ name, st, offset = item, None, 0
965
+ else:
966
+ name, attrs, offset = item
967
+ if attrs:
968
+ st = c_stat()
969
+ set_st_attrs(st, attrs, use_ns=self.use_ns)
970
+ else:
971
+ st = None
972
+
973
+ if filler(buf, name.encode(self.encoding), st, offset) != 0:
974
+ break
975
+
976
+ return 0
977
+
978
+ def releasedir(self, path, fip):
979
+ # Ignore raw_fi
980
+ return self.operations('releasedir', self._decode_optional_path(path),
981
+ fip.contents.fh)
982
+
983
+ def fsyncdir(self, path, datasync, fip):
984
+ # Ignore raw_fi
985
+ return self.operations('fsyncdir', self._decode_optional_path(path),
986
+ datasync, fip.contents.fh)
987
+
988
+ def init(self, conn):
989
+ return self.operations('init', '/')
990
+
991
+ def destroy(self, private_data):
992
+ return self.operations('destroy', '/')
993
+
994
+ def access(self, path, amode):
995
+ return self.operations('access', path.decode(self.encoding), amode)
996
+
997
+ def create(self, path, mode, fip):
998
+ fi = fip.contents
999
+ path = path.decode(self.encoding)
1000
+
1001
+ if self.raw_fi:
1002
+ return self.operations('create', path, mode, fi)
1003
+ else:
1004
+ fi.fh = self.operations('create', path, mode)
1005
+ return 0
1006
+
1007
+ def ftruncate(self, path, length, fip):
1008
+ if self.raw_fi:
1009
+ fh = fip.contents
1010
+ else:
1011
+ fh = fip.contents.fh
1012
+
1013
+ return self.operations('truncate', self._decode_optional_path(path),
1014
+ length, fh)
1015
+
1016
+ def fgetattr(self, path, buf, fip):
1017
+ ctypes.memset(buf, 0, ctypes.sizeof(c_stat))
1018
+
1019
+ st = buf.contents
1020
+ if not fip:
1021
+ fh = fip
1022
+ elif self.raw_fi:
1023
+ fh = fip.contents
1024
+ else:
1025
+ fh = fip.contents.fh
1026
+
1027
+ attrs = self.operations('getattr', self._decode_optional_path(path), fh)
1028
+ set_st_attrs(st, attrs, use_ns=self.use_ns)
1029
+ return 0
1030
+
1031
+ def lock(self, path, fip, cmd, lock):
1032
+ if self.raw_fi:
1033
+ fh = fip.contents
1034
+ else:
1035
+ fh = fip.contents.fh
1036
+
1037
+ return self.operations('lock', self._decode_optional_path(path), fh, cmd,
1038
+ lock)
1039
+
1040
+ def utimens(self, path, buf):
1041
+ if buf:
1042
+ atime = time_of_timespec(buf.contents.actime, use_ns=self.use_ns)
1043
+ mtime = time_of_timespec(buf.contents.modtime, use_ns=self.use_ns)
1044
+ times = (atime, mtime)
1045
+ else:
1046
+ times = None
1047
+
1048
+ return self.operations('utimens', path.decode(self.encoding), times)
1049
+
1050
+ def bmap(self, path, blocksize, idx):
1051
+ return self.operations('bmap', path.decode(self.encoding), blocksize,
1052
+ idx)
1053
+
1054
+ def ioctl(self, path, cmd, arg, fip, flags, data):
1055
+ if self.raw_fi:
1056
+ fh = fip.contents
1057
+ else:
1058
+ fh = fip.contents.fh
1059
+
1060
+ return self.operations('ioctl', path.decode(self.encoding),
1061
+ cmd, arg, fh, flags, data)
1062
+
1063
+ class Operations(object):
1064
+ '''
1065
+ This class should be subclassed and passed as an argument to FUSE on
1066
+ initialization. All operations should raise a FuseOSError exception on
1067
+ error.
1068
+
1069
+ When in doubt of what an operation should do, check the FUSE header file
1070
+ or the corresponding system call man page.
1071
+ '''
1072
+
1073
+ def __call__(self, op, *args):
1074
+ if not hasattr(self, op):
1075
+ raise FuseOSError(errno.EFAULT)
1076
+ return getattr(self, op)(*args)
1077
+
1078
+ def access(self, path, amode):
1079
+ return 0
1080
+
1081
+ bmap = None
1082
+
1083
+ def chmod(self, path, mode):
1084
+ raise FuseOSError(errno.EROFS)
1085
+
1086
+ def chown(self, path, uid, gid):
1087
+ raise FuseOSError(errno.EROFS)
1088
+
1089
+ def create(self, path, mode, fi=None):
1090
+ '''
1091
+ When raw_fi is False (default case), fi is None and create should
1092
+ return a numerical file handle.
1093
+
1094
+ When raw_fi is True the file handle should be set directly by create
1095
+ and return 0.
1096
+ '''
1097
+
1098
+ raise FuseOSError(errno.EROFS)
1099
+
1100
+ def destroy(self, path):
1101
+ 'Called on filesystem destruction. Path is always /'
1102
+
1103
+ pass
1104
+
1105
+ def flush(self, path, fh):
1106
+ return 0
1107
+
1108
+ def fsync(self, path, datasync, fh):
1109
+ return 0
1110
+
1111
+ def fsyncdir(self, path, datasync, fh):
1112
+ return 0
1113
+
1114
+ def getattr(self, path, fh=None):
1115
+ '''
1116
+ Returns a dictionary with keys identical to the stat C structure of
1117
+ stat(2).
1118
+
1119
+ st_atime, st_mtime and st_ctime should be floats.
1120
+
1121
+ NOTE: There is an incompatibility between Linux and Mac OS X
1122
+ concerning st_nlink of directories. Mac OS X counts all files inside
1123
+ the directory, while Linux counts only the subdirectories.
1124
+ '''
1125
+
1126
+ if path != '/':
1127
+ raise FuseOSError(errno.ENOENT)
1128
+ return dict(st_mode=(S_IFDIR | 0o755), st_nlink=2)
1129
+
1130
+ def getxattr(self, path, name, position=0):
1131
+ raise FuseOSError(ENOTSUP)
1132
+
1133
+ def init(self, path):
1134
+ '''
1135
+ Called on filesystem initialization. (Path is always /)
1136
+
1137
+ Use it instead of __init__ if you start threads on initialization.
1138
+ '''
1139
+
1140
+ pass
1141
+
1142
+ def ioctl(self, path, cmd, arg, fip, flags, data):
1143
+ raise FuseOSError(errno.ENOTTY)
1144
+
1145
+ def link(self, target, source):
1146
+ 'creates a hard link `target -> source` (e.g. ln source target)'
1147
+
1148
+ raise FuseOSError(errno.EROFS)
1149
+
1150
+ def listxattr(self, path):
1151
+ return []
1152
+
1153
+ lock = None
1154
+
1155
+ def mkdir(self, path, mode):
1156
+ raise FuseOSError(errno.EROFS)
1157
+
1158
+ def mknod(self, path, mode, dev):
1159
+ raise FuseOSError(errno.EROFS)
1160
+
1161
+ def open(self, path, flags):
1162
+ '''
1163
+ When raw_fi is False (default case), open should return a numerical
1164
+ file handle.
1165
+
1166
+ When raw_fi is True the signature of open becomes:
1167
+ open(self, path, fi)
1168
+
1169
+ and the file handle should be set directly.
1170
+ '''
1171
+
1172
+ return 0
1173
+
1174
+ def opendir(self, path):
1175
+ 'Returns a numerical file handle.'
1176
+
1177
+ return 0
1178
+
1179
+ def read(self, path, size, offset, fh):
1180
+ 'Returns a string containing the data requested.'
1181
+
1182
+ raise FuseOSError(errno.EIO)
1183
+
1184
+ def readdir(self, path, fh):
1185
+ '''
1186
+ Can return either a list of names, or a list of (name, attrs, offset)
1187
+ tuples. attrs is a dict as in getattr.
1188
+ '''
1189
+
1190
+ return ['.', '..']
1191
+
1192
+ def readlink(self, path):
1193
+ raise FuseOSError(errno.ENOENT)
1194
+
1195
+ def release(self, path, fh):
1196
+ return 0
1197
+
1198
+ def releasedir(self, path, fh):
1199
+ return 0
1200
+
1201
+ def removexattr(self, path, name):
1202
+ raise FuseOSError(ENOTSUP)
1203
+
1204
+ def rename(self, old, new):
1205
+ raise FuseOSError(errno.EROFS)
1206
+
1207
+ def rmdir(self, path):
1208
+ raise FuseOSError(errno.EROFS)
1209
+
1210
+ def setxattr(self, path, name, value, options, position=0):
1211
+ raise FuseOSError(ENOTSUP)
1212
+
1213
+ def statfs(self, path):
1214
+ '''
1215
+ Returns a dictionary with keys identical to the statvfs C structure of
1216
+ statvfs(3).
1217
+
1218
+ On Mac OS X f_bsize and f_frsize must be a power of 2
1219
+ (minimum 512).
1220
+ '''
1221
+
1222
+ return {}
1223
+
1224
+ def symlink(self, target, source):
1225
+ 'creates a symlink `target -> source` (e.g. ln -s source target)'
1226
+
1227
+ raise FuseOSError(errno.EROFS)
1228
+
1229
+ def truncate(self, path, length, fh=None):
1230
+ raise FuseOSError(errno.EROFS)
1231
+
1232
+ def unlink(self, path):
1233
+ raise FuseOSError(errno.EROFS)
1234
+
1235
+ def utimens(self, path, times=None):
1236
+ 'Times is a (atime, mtime) tuple. If None use current time.'
1237
+
1238
+ return 0
1239
+
1240
+ def write(self, path, data, offset, fh):
1241
+ raise FuseOSError(errno.EROFS)
1242
+
1243
+
1244
+ class LoggingMixIn:
1245
+ log = logging.getLogger('fuse.log-mixin')
1246
+
1247
+ def __call__(self, op, path, *args):
1248
+ self.log.debug('-> %s %s %s', op, path, repr(args))
1249
+ ret = '[Unhandled Exception]'
1250
+ try:
1251
+ ret = getattr(self, op)(path, *args)
1252
+ return ret
1253
+ except OSError as e:
1254
+ ret = str(e)
1255
+ raise
1256
+ finally:
1257
+ self.log.debug('<- %s %s', op, repr(ret))