You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

979 lines
30 KiB

  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. from __future__ import print_function, absolute_import, division
  16. from ctypes import *
  17. from ctypes.util import find_library
  18. from errno import *
  19. from os import strerror
  20. from platform import machine, system
  21. from signal import signal, SIGINT, SIG_DFL
  22. from stat import S_IFDIR
  23. from traceback import print_exc
  24. import logging
  25. try:
  26. from functools import partial
  27. except ImportError:
  28. # http://docs.python.org/library/functools.html#functools.partial
  29. def partial(func, *args, **keywords):
  30. def newfunc(*fargs, **fkeywords):
  31. newkeywords = keywords.copy()
  32. newkeywords.update(fkeywords)
  33. return func(*(args + fargs), **newkeywords)
  34. newfunc.func = func
  35. newfunc.args = args
  36. newfunc.keywords = keywords
  37. return newfunc
  38. try:
  39. basestring
  40. except NameError:
  41. basestring = str
  42. class c_timespec(Structure):
  43. _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)]
  44. class c_utimbuf(Structure):
  45. _fields_ = [('actime', c_timespec), ('modtime', c_timespec)]
  46. class c_stat(Structure):
  47. pass # Platform dependent
  48. _system = system()
  49. _machine = machine()
  50. if _system == 'Darwin':
  51. _libiconv = CDLL(find_library('iconv'), RTLD_GLOBAL) # libfuse dependency
  52. _libfuse_path = (find_library('fuse4x') or find_library('osxfuse') or
  53. find_library('fuse'))
  54. else:
  55. _libfuse_path = find_library('fuse')
  56. if not _libfuse_path:
  57. raise EnvironmentError('Unable to find libfuse')
  58. else:
  59. _libfuse = CDLL(_libfuse_path)
  60. if _system == 'Darwin' and hasattr(_libfuse, 'macfuse_version'):
  61. _system = 'Darwin-MacFuse'
  62. if _system in ('Darwin', 'Darwin-MacFuse', 'FreeBSD'):
  63. ENOTSUP = 45
  64. c_dev_t = c_int32
  65. c_fsblkcnt_t = c_ulong
  66. c_fsfilcnt_t = c_ulong
  67. c_gid_t = c_uint32
  68. c_mode_t = c_uint16
  69. c_off_t = c_int64
  70. c_pid_t = c_int32
  71. c_uid_t = c_uint32
  72. setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
  73. c_size_t, c_int, c_uint32)
  74. getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
  75. c_size_t, c_uint32)
  76. if _system == 'Darwin':
  77. c_stat._fields_ = [
  78. ('st_dev', c_dev_t),
  79. ('st_mode', c_mode_t),
  80. ('st_nlink', c_uint16),
  81. ('st_ino', c_uint64),
  82. ('st_uid', c_uid_t),
  83. ('st_gid', c_gid_t),
  84. ('st_rdev', c_dev_t),
  85. ('st_atimespec', c_timespec),
  86. ('st_mtimespec', c_timespec),
  87. ('st_ctimespec', c_timespec),
  88. ('st_birthtimespec', c_timespec),
  89. ('st_size', c_off_t),
  90. ('st_blocks', c_int64),
  91. ('st_blksize', c_int32),
  92. ('st_flags', c_int32),
  93. ('st_gen', c_int32),
  94. ('st_lspare', c_int32),
  95. ('st_qspare', c_int64)]
  96. else:
  97. c_stat._fields_ = [
  98. ('st_dev', c_dev_t),
  99. ('st_ino', c_uint32),
  100. ('st_mode', c_mode_t),
  101. ('st_nlink', c_uint16),
  102. ('st_uid', c_uid_t),
  103. ('st_gid', c_gid_t),
  104. ('st_rdev', c_dev_t),
  105. ('st_atimespec', c_timespec),
  106. ('st_mtimespec', c_timespec),
  107. ('st_ctimespec', c_timespec),
  108. ('st_size', c_off_t),
  109. ('st_blocks', c_int64),
  110. ('st_blksize', c_int32)]
  111. elif _system == 'Linux':
  112. ENOTSUP = 95
  113. c_dev_t = c_ulonglong
  114. c_fsblkcnt_t = c_ulonglong
  115. c_fsfilcnt_t = c_ulonglong
  116. c_gid_t = c_uint
  117. c_mode_t = c_uint
  118. c_off_t = c_longlong
  119. c_pid_t = c_int
  120. c_uid_t = c_uint
  121. setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
  122. c_size_t, c_int)
  123. getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
  124. c_size_t)
  125. if _machine == 'x86_64':
  126. c_stat._fields_ = [
  127. ('st_dev', c_dev_t),
  128. ('st_ino', c_ulong),
  129. ('st_nlink', c_ulong),
  130. ('st_mode', c_mode_t),
  131. ('st_uid', c_uid_t),
  132. ('st_gid', c_gid_t),
  133. ('__pad0', c_int),
  134. ('st_rdev', c_dev_t),
  135. ('st_size', c_off_t),
  136. ('st_blksize', c_long),
  137. ('st_blocks', c_long),
  138. ('st_atimespec', c_timespec),
  139. ('st_mtimespec', c_timespec),
  140. ('st_ctimespec', c_timespec)]
  141. elif _machine == 'mips':
  142. c_stat._fields_ = [
  143. ('st_dev', c_dev_t),
  144. ('__pad1_1', c_ulong),
  145. ('__pad1_2', c_ulong),
  146. ('__pad1_3', c_ulong),
  147. ('st_ino', c_ulong),
  148. ('st_mode', c_mode_t),
  149. ('st_nlink', c_ulong),
  150. ('st_uid', c_uid_t),
  151. ('st_gid', c_gid_t),
  152. ('st_rdev', c_dev_t),
  153. ('__pad2_1', c_ulong),
  154. ('__pad2_2', c_ulong),
  155. ('st_size', c_off_t),
  156. ('__pad3', c_ulong),
  157. ('st_atimespec', c_timespec),
  158. ('__pad4', c_ulong),
  159. ('st_mtimespec', c_timespec),
  160. ('__pad5', c_ulong),
  161. ('st_ctimespec', c_timespec),
  162. ('__pad6', c_ulong),
  163. ('st_blksize', c_long),
  164. ('st_blocks', c_long),
  165. ('__pad7_1', c_ulong),
  166. ('__pad7_2', c_ulong),
  167. ('__pad7_3', c_ulong),
  168. ('__pad7_4', c_ulong),
  169. ('__pad7_5', c_ulong),
  170. ('__pad7_6', c_ulong),
  171. ('__pad7_7', c_ulong),
  172. ('__pad7_8', c_ulong),
  173. ('__pad7_9', c_ulong),
  174. ('__pad7_10', c_ulong),
  175. ('__pad7_11', c_ulong),
  176. ('__pad7_12', c_ulong),
  177. ('__pad7_13', c_ulong),
  178. ('__pad7_14', c_ulong)]
  179. elif _machine == 'ppc':
  180. c_stat._fields_ = [
  181. ('st_dev', c_dev_t),
  182. ('st_ino', c_ulonglong),
  183. ('st_mode', c_mode_t),
  184. ('st_nlink', c_uint),
  185. ('st_uid', c_uid_t),
  186. ('st_gid', c_gid_t),
  187. ('st_rdev', c_dev_t),
  188. ('__pad2', c_ushort),
  189. ('st_size', c_off_t),
  190. ('st_blksize', c_long),
  191. ('st_blocks', c_longlong),
  192. ('st_atimespec', c_timespec),
  193. ('st_mtimespec', c_timespec),
  194. ('st_ctimespec', c_timespec)]
  195. elif _machine == 'aarch64':
  196. c_stat._fields_ = [
  197. ('st_dev', c_dev_t),
  198. ('st_ino', c_ulong),
  199. ('st_mode', c_mode_t),
  200. ('st_nlink', c_uint),
  201. ('st_uid', c_uid_t),
  202. ('st_gid', c_gid_t),
  203. ('st_rdev', c_dev_t),
  204. ('__pad1', c_ulong),
  205. ('st_size', c_off_t),
  206. ('st_blksize', c_int),
  207. ('__pad2', c_int),
  208. ('st_blocks', c_long),
  209. ('st_atimespec', c_timespec),
  210. ('st_mtimespec', c_timespec),
  211. ('st_ctimespec', c_timespec)]
  212. else:
  213. # i686, use as fallback for everything else
  214. c_stat._fields_ = [
  215. ('st_dev', c_dev_t),
  216. ('__pad1', c_ushort),
  217. ('__st_ino', c_ulong),
  218. ('st_mode', c_mode_t),
  219. ('st_nlink', c_uint),
  220. ('st_uid', c_uid_t),
  221. ('st_gid', c_gid_t),
  222. ('st_rdev', c_dev_t),
  223. ('__pad2', c_ushort),
  224. ('st_size', c_off_t),
  225. ('st_blksize', c_long),
  226. ('st_blocks', c_longlong),
  227. ('st_atimespec', c_timespec),
  228. ('st_mtimespec', c_timespec),
  229. ('st_ctimespec', c_timespec),
  230. ('st_ino', c_ulonglong)]
  231. else:
  232. raise NotImplementedError('%s is not supported.' % _system)
  233. class c_statvfs(Structure):
  234. _fields_ = [
  235. ('f_bsize', c_ulong),
  236. ('f_frsize', c_ulong),
  237. ('f_blocks', c_fsblkcnt_t),
  238. ('f_bfree', c_fsblkcnt_t),
  239. ('f_bavail', c_fsblkcnt_t),
  240. ('f_files', c_fsfilcnt_t),
  241. ('f_ffree', c_fsfilcnt_t),
  242. ('f_favail', c_fsfilcnt_t),
  243. ('f_fsid', c_ulong),
  244. #('unused', c_int),
  245. ('f_flag', c_ulong),
  246. ('f_namemax', c_ulong)]
  247. if _system == 'FreeBSD':
  248. c_fsblkcnt_t = c_uint64
  249. c_fsfilcnt_t = c_uint64
  250. setxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
  251. c_size_t, c_int)
  252. getxattr_t = CFUNCTYPE(c_int, c_char_p, c_char_p, POINTER(c_byte),
  253. c_size_t)
  254. class c_statvfs(Structure):
  255. _fields_ = [
  256. ('f_bavail', c_fsblkcnt_t),
  257. ('f_bfree', c_fsblkcnt_t),
  258. ('f_blocks', c_fsblkcnt_t),
  259. ('f_favail', c_fsfilcnt_t),
  260. ('f_ffree', c_fsfilcnt_t),
  261. ('f_files', c_fsfilcnt_t),
  262. ('f_bsize', c_ulong),
  263. ('f_flag', c_ulong),
  264. ('f_frsize', c_ulong)]
  265. class fuse_file_info(Structure):
  266. _fields_ = [
  267. ('flags', c_int),
  268. ('fh_old', c_ulong),
  269. ('writepage', c_int),
  270. ('direct_io', c_uint, 1),
  271. ('keep_cache', c_uint, 1),
  272. ('flush', c_uint, 1),
  273. ('padding', c_uint, 29),
  274. ('fh', c_uint64),
  275. ('lock_owner', c_uint64)]
  276. class fuse_context(Structure):
  277. _fields_ = [
  278. ('fuse', c_voidp),
  279. ('uid', c_uid_t),
  280. ('gid', c_gid_t),
  281. ('pid', c_pid_t),
  282. ('private_data', c_voidp)]
  283. _libfuse.fuse_get_context.restype = POINTER(fuse_context)
  284. class fuse_operations(Structure):
  285. _fields_ = [
  286. ('getattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat))),
  287. ('readlink', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
  288. ('getdir', c_voidp), # Deprecated, use readdir
  289. ('mknod', CFUNCTYPE(c_int, c_char_p, c_mode_t, c_dev_t)),
  290. ('mkdir', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
  291. ('unlink', CFUNCTYPE(c_int, c_char_p)),
  292. ('rmdir', CFUNCTYPE(c_int, c_char_p)),
  293. ('symlink', CFUNCTYPE(c_int, c_char_p, c_char_p)),
  294. ('rename', CFUNCTYPE(c_int, c_char_p, c_char_p)),
  295. ('link', CFUNCTYPE(c_int, c_char_p, c_char_p)),
  296. ('chmod', CFUNCTYPE(c_int, c_char_p, c_mode_t)),
  297. ('chown', CFUNCTYPE(c_int, c_char_p, c_uid_t, c_gid_t)),
  298. ('truncate', CFUNCTYPE(c_int, c_char_p, c_off_t)),
  299. ('utime', c_voidp), # Deprecated, use utimens
  300. ('open', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
  301. ('read', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t,
  302. c_off_t, POINTER(fuse_file_info))),
  303. ('write', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t,
  304. c_off_t, POINTER(fuse_file_info))),
  305. ('statfs', CFUNCTYPE(c_int, c_char_p, POINTER(c_statvfs))),
  306. ('flush', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
  307. ('release', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
  308. ('fsync', CFUNCTYPE(c_int, c_char_p, c_int, POINTER(fuse_file_info))),
  309. ('setxattr', setxattr_t),
  310. ('getxattr', getxattr_t),
  311. ('listxattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_byte), c_size_t)),
  312. ('removexattr', CFUNCTYPE(c_int, c_char_p, c_char_p)),
  313. ('opendir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
  314. ('readdir', CFUNCTYPE(c_int, c_char_p, c_voidp,
  315. CFUNCTYPE(c_int, c_voidp, c_char_p,
  316. POINTER(c_stat), c_off_t),
  317. c_off_t, POINTER(fuse_file_info))),
  318. ('releasedir', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info))),
  319. ('fsyncdir', CFUNCTYPE(c_int, c_char_p, c_int,
  320. POINTER(fuse_file_info))),
  321. ('init', CFUNCTYPE(c_voidp, c_voidp)),
  322. ('destroy', CFUNCTYPE(c_voidp, c_voidp)),
  323. ('access', CFUNCTYPE(c_int, c_char_p, c_int)),
  324. ('create', CFUNCTYPE(c_int, c_char_p, c_mode_t,
  325. POINTER(fuse_file_info))),
  326. ('ftruncate', CFUNCTYPE(c_int, c_char_p, c_off_t,
  327. POINTER(fuse_file_info))),
  328. ('fgetattr', CFUNCTYPE(c_int, c_char_p, POINTER(c_stat),
  329. POINTER(fuse_file_info))),
  330. ('lock', CFUNCTYPE(c_int, c_char_p, POINTER(fuse_file_info),
  331. c_int, c_voidp)),
  332. ('utimens', CFUNCTYPE(c_int, c_char_p, POINTER(c_utimbuf))),
  333. ('bmap', CFUNCTYPE(c_int, c_char_p, c_size_t, POINTER(c_ulonglong))),
  334. ('flag_nullpath_ok', c_uint, 1),
  335. ('flag_nopath', c_uint, 1),
  336. ('flag_utime_omit_ok', c_uint, 1),
  337. ('flag_reserved', c_uint, 29),
  338. ]
  339. def time_of_timespec(ts):
  340. return ts.tv_sec + ts.tv_nsec / 10 ** 9
  341. def set_st_attrs(st, attrs):
  342. for key, val in attrs.items():
  343. if key in ('st_atime', 'st_mtime', 'st_ctime', 'st_birthtime'):
  344. timespec = getattr(st, key + 'spec', None)
  345. if timespec is None:
  346. continue
  347. timespec.tv_sec = int(val)
  348. timespec.tv_nsec = int((val - timespec.tv_sec) * 10 ** 9)
  349. elif hasattr(st, key):
  350. setattr(st, key, val)
  351. def fuse_get_context():
  352. 'Returns a (uid, gid, pid) tuple'
  353. ctxp = _libfuse.fuse_get_context()
  354. ctx = ctxp.contents
  355. return ctx.uid, ctx.gid, ctx.pid
  356. class FuseOSError(OSError):
  357. def __init__(self, errno):
  358. super(FuseOSError, self).__init__(errno, strerror(errno))
  359. class FUSE(object):
  360. '''
  361. This class is the lower level interface and should not be subclassed under
  362. normal use. Its methods are called by fuse.
  363. Assumes API version 2.6 or later.
  364. '''
  365. OPTIONS = (
  366. ('foreground', '-f'),
  367. ('debug', '-d'),
  368. ('nothreads', '-s'),
  369. )
  370. def __init__(self, operations, mountpoint, raw_fi=False, encoding='utf-8',
  371. **kwargs):
  372. '''
  373. Setting raw_fi to True will cause FUSE to pass the fuse_file_info
  374. class as is to Operations, instead of just the fh field.
  375. This gives you access to direct_io, keep_cache, etc.
  376. '''
  377. self.operations = operations
  378. self.raw_fi = raw_fi
  379. self.encoding = encoding
  380. args = ['fuse']
  381. args.extend(flag for arg, flag in self.OPTIONS
  382. if kwargs.pop(arg, False))
  383. kwargs.setdefault('fsname', operations.__class__.__name__)
  384. args.append('-o')
  385. args.append(','.join(self._normalize_fuse_options(**kwargs)))
  386. args.append(mountpoint)
  387. args = [arg.encode(encoding) for arg in args]
  388. argv = (c_char_p * len(args))(*args)
  389. fuse_ops = fuse_operations()
  390. for ent in fuse_operations._fields_:
  391. name, prototype = ent[:2]
  392. val = getattr(operations, name, None)
  393. if val is None:
  394. continue
  395. # Function pointer members are tested for using the
  396. # getattr(operations, name) above but are dynamically
  397. # invoked using self.operations(name)
  398. if hasattr(prototype, 'argtypes'):
  399. val = prototype(partial(self._wrapper, getattr(self, name)))
  400. setattr(fuse_ops, name, val)
  401. try:
  402. old_handler = signal(SIGINT, SIG_DFL)
  403. except ValueError:
  404. old_handler = SIG_DFL
  405. err = _libfuse.fuse_main_real(len(args), argv, pointer(fuse_ops),
  406. sizeof(fuse_ops), None)
  407. try:
  408. signal(SIGINT, old_handler)
  409. except ValueError:
  410. pass
  411. del self.operations # Invoke the destructor
  412. if err:
  413. raise RuntimeError(err)
  414. @staticmethod
  415. def _normalize_fuse_options(**kargs):
  416. for key, value in kargs.items():
  417. if isinstance(value, bool):
  418. if value is True: yield key
  419. else:
  420. yield '%s=%s' % (key, value)
  421. @staticmethod
  422. def _wrapper(func, *args, **kwargs):
  423. 'Decorator for the methods that follow'
  424. try:
  425. return func(*args, **kwargs) or 0
  426. except OSError as e:
  427. return -(e.errno or EFAULT)
  428. except:
  429. print_exc()
  430. return -EFAULT
  431. def _decode_optional_path(self, path):
  432. # NB: this method is intended for fuse operations that
  433. # allow the path argument to be NULL,
  434. # *not* as a generic path decoding method
  435. if path is None:
  436. return None
  437. return path.decode(self.encoding)
  438. def getattr(self, path, buf):
  439. return self.fgetattr(path, buf, None)
  440. def readlink(self, path, buf, bufsize):
  441. ret = self.operations('readlink', path.decode(self.encoding)) \
  442. .encode(self.encoding)
  443. # copies a string into the given buffer
  444. # (null terminated and truncated if necessary)
  445. data = create_string_buffer(ret[:bufsize - 1])
  446. memmove(buf, data, len(data))
  447. return 0
  448. def mknod(self, path, mode, dev):
  449. return self.operations('mknod', path.decode(self.encoding), mode, dev)
  450. def mkdir(self, path, mode):
  451. return self.operations('mkdir', path.decode(self.encoding), mode)
  452. def unlink(self, path):
  453. return self.operations('unlink', path.decode(self.encoding))
  454. def rmdir(self, path):
  455. return self.operations('rmdir', path.decode(self.encoding))
  456. def symlink(self, source, target):
  457. 'creates a symlink `target -> source` (e.g. ln -s source target)'
  458. return self.operations('symlink', target.decode(self.encoding),
  459. source.decode(self.encoding))
  460. def rename(self, old, new):
  461. return self.operations('rename', old.decode(self.encoding),
  462. new.decode(self.encoding))
  463. def link(self, source, target):
  464. 'creates a hard link `target -> source` (e.g. ln source target)'
  465. return self.operations('link', target.decode(self.encoding),
  466. source.decode(self.encoding))
  467. def chmod(self, path, mode):
  468. return self.operations('chmod', path.decode(self.encoding), mode)
  469. def chown(self, path, uid, gid):
  470. # Check if any of the arguments is a -1 that has overflowed
  471. if c_uid_t(uid + 1).value == 0:
  472. uid = -1
  473. if c_gid_t(gid + 1).value == 0:
  474. gid = -1
  475. return self.operations('chown', path.decode(self.encoding), uid, gid)
  476. def truncate(self, path, length):
  477. return self.operations('truncate', path.decode(self.encoding), length)
  478. def open(self, path, fip):
  479. fi = fip.contents
  480. if self.raw_fi:
  481. return self.operations('open', path.decode(self.encoding), fi)
  482. else:
  483. fi.fh = self.operations('open', path.decode(self.encoding),
  484. fi.flags)
  485. return 0
  486. def read(self, path, buf, size, offset, fip):
  487. if self.raw_fi:
  488. fh = fip.contents
  489. else:
  490. fh = fip.contents.fh
  491. ret = self.operations('read', self._decode_optional_path(path), size,
  492. offset, fh)
  493. if not ret: return 0
  494. retsize = len(ret)
  495. assert retsize <= size, \
  496. 'actual amount read %d greater than expected %d' % (retsize, size)
  497. data = create_string_buffer(ret, retsize)
  498. memmove(buf, data, retsize)
  499. return retsize
  500. def write(self, path, buf, size, offset, fip):
  501. data = string_at(buf, size)
  502. if self.raw_fi:
  503. fh = fip.contents
  504. else:
  505. fh = fip.contents.fh
  506. return self.operations('write', self._decode_optional_path(path), data,
  507. offset, fh)
  508. def statfs(self, path, buf):
  509. stv = buf.contents
  510. attrs = self.operations('statfs', path.decode(self.encoding))
  511. for key, val in attrs.items():
  512. if hasattr(stv, key):
  513. setattr(stv, key, val)
  514. return 0
  515. def flush(self, path, fip):
  516. if self.raw_fi:
  517. fh = fip.contents
  518. else:
  519. fh = fip.contents.fh
  520. return self.operations('flush', self._decode_optional_path(path), fh)
  521. def release(self, path, fip):
  522. if self.raw_fi:
  523. fh = fip.contents
  524. else:
  525. fh = fip.contents.fh
  526. return self.operations('release', self._decode_optional_path(path), fh)
  527. def fsync(self, path, datasync, fip):
  528. if self.raw_fi:
  529. fh = fip.contents
  530. else:
  531. fh = fip.contents.fh
  532. return self.operations('fsync', self._decode_optional_path(path), datasync,
  533. fh)
  534. def setxattr(self, path, name, value, size, options, *args):
  535. return self.operations('setxattr', path.decode(self.encoding),
  536. name.decode(self.encoding),
  537. string_at(value, size), options, *args)
  538. def getxattr(self, path, name, value, size, *args):
  539. ret = self.operations('getxattr', path.decode(self.encoding),
  540. name.decode(self.encoding), *args)
  541. retsize = len(ret)
  542. # allow size queries
  543. if not value: return retsize
  544. # do not truncate
  545. if retsize > size: return -ERANGE
  546. buf = create_string_buffer(ret, retsize) # Does not add trailing 0
  547. memmove(value, buf, retsize)
  548. return retsize
  549. def listxattr(self, path, namebuf, size):
  550. attrs = self.operations('listxattr', path.decode(self.encoding)) or ''
  551. ret = '\x00'.join(attrs).encode(self.encoding)
  552. if len(ret) > 0:
  553. ret += '\x00'.encode(self.encoding)
  554. retsize = len(ret)
  555. # allow size queries
  556. if not namebuf: return retsize
  557. # do not truncate
  558. if retsize > size: return -ERANGE
  559. buf = create_string_buffer(ret, retsize)
  560. memmove(namebuf, buf, retsize)
  561. return retsize
  562. def removexattr(self, path, name):
  563. return self.operations('removexattr', path.decode(self.encoding),
  564. name.decode(self.encoding))
  565. def opendir(self, path, fip):
  566. # Ignore raw_fi
  567. fip.contents.fh = self.operations('opendir',
  568. path.decode(self.encoding))
  569. return 0
  570. def readdir(self, path, buf, filler, offset, fip):
  571. # Ignore raw_fi
  572. for item in self.operations('readdir', self._decode_optional_path(path),
  573. fip.contents.fh):
  574. if isinstance(item, basestring):
  575. name, st, offset = item, None, 0
  576. else:
  577. name, attrs, offset = item
  578. if attrs:
  579. st = c_stat()
  580. set_st_attrs(st, attrs)
  581. else:
  582. st = None
  583. if filler(buf, name.encode(self.encoding), st, offset) != 0:
  584. break
  585. return 0
  586. def releasedir(self, path, fip):
  587. # Ignore raw_fi
  588. return self.operations('releasedir', self._decode_optional_path(path),
  589. fip.contents.fh)
  590. def fsyncdir(self, path, datasync, fip):
  591. # Ignore raw_fi
  592. return self.operations('fsyncdir', self._decode_optional_path(path),
  593. datasync, fip.contents.fh)
  594. def init(self, conn):
  595. return self.operations('init', '/')
  596. def destroy(self, private_data):
  597. return self.operations('destroy', '/')
  598. def access(self, path, amode):
  599. return self.operations('access', path.decode(self.encoding), amode)
  600. def create(self, path, mode, fip):
  601. fi = fip.contents
  602. path = path.decode(self.encoding)
  603. if self.raw_fi:
  604. return self.operations('create', path, mode, fi)
  605. else:
  606. fi.fh = self.operations('create', path, mode)
  607. return 0
  608. def ftruncate(self, path, length, fip):
  609. if self.raw_fi:
  610. fh = fip.contents
  611. else:
  612. fh = fip.contents.fh
  613. return self.operations('truncate', self._decode_optional_path(path),
  614. length, fh)
  615. def fgetattr(self, path, buf, fip):
  616. memset(buf, 0, sizeof(c_stat))
  617. st = buf.contents
  618. if not fip:
  619. fh = fip
  620. elif self.raw_fi:
  621. fh = fip.contents
  622. else:
  623. fh = fip.contents.fh
  624. attrs = self.operations('getattr', self._decode_optional_path(path), fh)
  625. set_st_attrs(st, attrs)
  626. return 0
  627. def lock(self, path, fip, cmd, lock):
  628. if self.raw_fi:
  629. fh = fip.contents
  630. else:
  631. fh = fip.contents.fh
  632. return self.operations('lock', self._decode_optional_path(path), fh, cmd,
  633. lock)
  634. def utimens(self, path, buf):
  635. if buf:
  636. atime = time_of_timespec(buf.contents.actime)
  637. mtime = time_of_timespec(buf.contents.modtime)
  638. times = (atime, mtime)
  639. else:
  640. times = None
  641. return self.operations('utimens', path.decode(self.encoding), times)
  642. def bmap(self, path, blocksize, idx):
  643. return self.operations('bmap', path.decode(self.encoding), blocksize,
  644. idx)
  645. class Operations(object):
  646. '''
  647. This class should be subclassed and passed as an argument to FUSE on
  648. initialization. All operations should raise a FuseOSError exception on
  649. error.
  650. When in doubt of what an operation should do, check the FUSE header file
  651. or the corresponding system call man page.
  652. '''
  653. def __call__(self, op, *args):
  654. if not hasattr(self, op):
  655. raise FuseOSError(EFAULT)
  656. return getattr(self, op)(*args)
  657. def access(self, path, amode):
  658. return 0
  659. bmap = None
  660. def chmod(self, path, mode):
  661. raise FuseOSError(EROFS)
  662. def chown(self, path, uid, gid):
  663. raise FuseOSError(EROFS)
  664. def create(self, path, mode, fi=None):
  665. '''
  666. When raw_fi is False (default case), fi is None and create should
  667. return a numerical file handle.
  668. When raw_fi is True the file handle should be set directly by create
  669. and return 0.
  670. '''
  671. raise FuseOSError(EROFS)
  672. def destroy(self, path):
  673. 'Called on filesystem destruction. Path is always /'
  674. pass
  675. def flush(self, path, fh):
  676. return 0
  677. def fsync(self, path, datasync, fh):
  678. return 0
  679. def fsyncdir(self, path, datasync, fh):
  680. return 0
  681. def getattr(self, path, fh=None):
  682. '''
  683. Returns a dictionary with keys identical to the stat C structure of
  684. stat(2).
  685. st_atime, st_mtime and st_ctime should be floats.
  686. NOTE: There is an incombatibility between Linux and Mac OS X
  687. concerning st_nlink of directories. Mac OS X counts all files inside
  688. the directory, while Linux counts only the subdirectories.
  689. '''
  690. if path != '/':
  691. raise FuseOSError(ENOENT)
  692. return dict(st_mode=(S_IFDIR | 0o755), st_nlink=2)
  693. def getxattr(self, path, name, position=0):
  694. raise FuseOSError(ENOTSUP)
  695. def init(self, path):
  696. '''
  697. Called on filesystem initialization. (Path is always /)
  698. Use it instead of __init__ if you start threads on initialization.
  699. '''
  700. pass
  701. def link(self, target, source):
  702. 'creates a hard link `target -> source` (e.g. ln source target)'
  703. raise FuseOSError(EROFS)
  704. def listxattr(self, path):
  705. return []
  706. lock = None
  707. def mkdir(self, path, mode):
  708. raise FuseOSError(EROFS)
  709. def mknod(self, path, mode, dev):
  710. raise FuseOSError(EROFS)
  711. def open(self, path, flags):
  712. '''
  713. When raw_fi is False (default case), open should return a numerical
  714. file handle.
  715. When raw_fi is True the signature of open becomes:
  716. open(self, path, fi)
  717. and the file handle should be set directly.
  718. '''
  719. return 0
  720. def opendir(self, path):
  721. 'Returns a numerical file handle.'
  722. return 0
  723. def read(self, path, size, offset, fh):
  724. 'Returns a string containing the data requested.'
  725. raise FuseOSError(EIO)
  726. def readdir(self, path, fh):
  727. '''
  728. Can return either a list of names, or a list of (name, attrs, offset)
  729. tuples. attrs is a dict as in getattr.
  730. '''
  731. return ['.', '..']
  732. def readlink(self, path):
  733. raise FuseOSError(ENOENT)
  734. def release(self, path, fh):
  735. return 0
  736. def releasedir(self, path, fh):
  737. return 0
  738. def removexattr(self, path, name):
  739. raise FuseOSError(ENOTSUP)
  740. def rename(self, old, new):
  741. raise FuseOSError(EROFS)
  742. def rmdir(self, path):
  743. raise FuseOSError(EROFS)
  744. def setxattr(self, path, name, value, options, position=0):
  745. raise FuseOSError(ENOTSUP)
  746. def statfs(self, path):
  747. '''
  748. Returns a dictionary with keys identical to the statvfs C structure of
  749. statvfs(3).
  750. On Mac OS X f_bsize and f_frsize must be a power of 2
  751. (minimum 512).
  752. '''
  753. return {}
  754. def symlink(self, target, source):
  755. 'creates a symlink `target -> source` (e.g. ln -s source target)'
  756. raise FuseOSError(EROFS)
  757. def truncate(self, path, length, fh=None):
  758. raise FuseOSError(EROFS)
  759. def unlink(self, path):
  760. raise FuseOSError(EROFS)
  761. def utimens(self, path, times=None):
  762. 'Times is a (atime, mtime) tuple. If None use current time.'
  763. return 0
  764. def write(self, path, data, offset, fh):
  765. raise FuseOSError(EROFS)
  766. class LoggingMixIn:
  767. log = logging.getLogger('fuse.log-mixin')
  768. def __call__(self, op, path, *args):
  769. self.log.debug('-> %s %s %s', op, path, repr(args))
  770. ret = '[Unhandled Exception]'
  771. try:
  772. ret = getattr(self, op)(path, *args)
  773. return ret
  774. except OSError as e:
  775. ret = str(e)
  776. raise
  777. finally:
  778. self.log.debug('<- %s %s', op, repr(ret))