github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/lib/python/fusepy/fusell.py (about) 1 # Copyright (c) 2010 Giorgos Verigakis <verigak@gmail.com> 2 # 3 # Permission to use, copy, modify, and distribute this software for any 4 # purpose with or without fee is hereby granted, provided that the above 5 # copyright notice and this permission notice appear in all copies. 6 # 7 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15 from __future__ import division 16 17 from ctypes import * 18 from ctypes.util import find_library 19 from errno import * 20 from functools import partial, wraps 21 from inspect import getmembers, ismethod 22 from platform import machine, system 23 from stat import S_IFDIR, S_IFREG 24 25 26 _system = system() 27 _machine = machine() 28 29 class LibFUSE(CDLL): 30 def __init__(self): 31 if _system == 'Darwin': 32 self.libiconv = CDLL(find_library('iconv'), RTLD_GLOBAL) 33 super(LibFUSE, self).__init__(find_library('fuse')) 34 35 self.fuse_mount.argtypes = (c_char_p, POINTER(fuse_args)) 36 self.fuse_mount.restype = c_void_p 37 self.fuse_lowlevel_new.argtypes = (POINTER(fuse_args), POINTER(fuse_lowlevel_ops), 38 c_size_t, c_void_p) 39 self.fuse_lowlevel_new.restype = c_void_p 40 self.fuse_set_signal_handlers.argtypes = (c_void_p,) 41 self.fuse_session_add_chan.argtypes = (c_void_p, c_void_p) 42 self.fuse_session_loop.argtypes = (c_void_p,) 43 self.fuse_remove_signal_handlers.argtypes = (c_void_p,) 44 self.fuse_session_remove_chan.argtypes = (c_void_p,) 45 self.fuse_session_destroy.argtypes = (c_void_p,) 46 self.fuse_unmount.argtypes = (c_char_p, c_void_p) 47 48 self.fuse_req_ctx.restype = POINTER(fuse_ctx) 49 self.fuse_req_ctx.argtypes = (fuse_req_t,) 50 51 self.fuse_reply_err.argtypes = (fuse_req_t, c_int) 52 self.fuse_reply_attr.argtypes = (fuse_req_t, c_void_p, c_double) 53 self.fuse_reply_entry.argtypes = (fuse_req_t, c_void_p) 54 self.fuse_reply_open.argtypes = (fuse_req_t, c_void_p) 55 self.fuse_reply_buf.argtypes = (fuse_req_t, c_char_p, c_size_t) 56 self.fuse_reply_write.argtypes = (fuse_req_t, c_size_t) 57 58 self.fuse_add_direntry.argtypes = (c_void_p, c_char_p, c_size_t, c_char_p, 59 c_stat_p, c_off_t) 60 61 class fuse_args(Structure): 62 _fields_ = [('argc', c_int), ('argv', POINTER(c_char_p)), ('allocated', c_int)] 63 64 class c_timespec(Structure): 65 _fields_ = [('tv_sec', c_long), ('tv_nsec', c_long)] 66 67 class c_stat(Structure): 68 pass # Platform dependent 69 70 if _system == 'Darwin': 71 ENOTSUP = 45 72 c_dev_t = c_int32 73 c_fsblkcnt_t = c_ulong 74 c_fsfilcnt_t = c_ulong 75 c_gid_t = c_uint32 76 c_mode_t = c_uint16 77 c_off_t = c_int64 78 c_pid_t = c_int32 79 c_uid_t = c_uint32 80 c_stat._fields_ = [ 81 ('st_dev', c_dev_t), 82 ('st_ino', c_uint32), 83 ('st_mode', c_mode_t), 84 ('st_nlink', c_uint16), 85 ('st_uid', c_uid_t), 86 ('st_gid', c_gid_t), 87 ('st_rdev', c_dev_t), 88 ('st_atimespec', c_timespec), 89 ('st_mtimespec', c_timespec), 90 ('st_ctimespec', c_timespec), 91 ('st_size', c_off_t), 92 ('st_blocks', c_int64), 93 ('st_blksize', c_int32)] 94 elif _system == 'Linux': 95 ENOTSUP = 95 96 c_dev_t = c_ulonglong 97 c_fsblkcnt_t = c_ulonglong 98 c_fsfilcnt_t = c_ulonglong 99 c_gid_t = c_uint 100 c_mode_t = c_uint 101 c_off_t = c_longlong 102 c_pid_t = c_int 103 c_uid_t = c_uint 104 105 if _machine == 'x86_64': 106 c_stat._fields_ = [ 107 ('st_dev', c_dev_t), 108 ('st_ino', c_ulong), 109 ('st_nlink', c_ulong), 110 ('st_mode', c_mode_t), 111 ('st_uid', c_uid_t), 112 ('st_gid', c_gid_t), 113 ('__pad0', c_int), 114 ('st_rdev', c_dev_t), 115 ('st_size', c_off_t), 116 ('st_blksize', c_long), 117 ('st_blocks', c_long), 118 ('st_atimespec', c_timespec), 119 ('st_mtimespec', c_timespec), 120 ('st_ctimespec', c_timespec)] 121 elif _machine == 'ppc': 122 c_stat._fields_ = [ 123 ('st_dev', c_dev_t), 124 ('st_ino', c_ulonglong), 125 ('st_mode', c_mode_t), 126 ('st_nlink', c_uint), 127 ('st_uid', c_uid_t), 128 ('st_gid', c_gid_t), 129 ('st_rdev', c_dev_t), 130 ('__pad2', c_ushort), 131 ('st_size', c_off_t), 132 ('st_blksize', c_long), 133 ('st_blocks', c_longlong), 134 ('st_atimespec', c_timespec), 135 ('st_mtimespec', c_timespec), 136 ('st_ctimespec', c_timespec)] 137 else: 138 # i686, use as fallback for everything else 139 c_stat._fields_ = [ 140 ('st_dev', c_dev_t), 141 ('__pad1', c_ushort), 142 ('__st_ino', c_ulong), 143 ('st_mode', c_mode_t), 144 ('st_nlink', c_uint), 145 ('st_uid', c_uid_t), 146 ('st_gid', c_gid_t), 147 ('st_rdev', c_dev_t), 148 ('__pad2', c_ushort), 149 ('st_size', c_off_t), 150 ('st_blksize', c_long), 151 ('st_blocks', c_longlong), 152 ('st_atimespec', c_timespec), 153 ('st_mtimespec', c_timespec), 154 ('st_ctimespec', c_timespec), 155 ('st_ino', c_ulonglong)] 156 else: 157 raise NotImplementedError('%s is not supported.' % _system) 158 159 class c_statvfs(Structure): 160 _fields_ = [ 161 ('f_bsize', c_ulong), 162 ('f_frsize', c_ulong), 163 ('f_blocks', c_fsblkcnt_t), 164 ('f_bfree', c_fsblkcnt_t), 165 ('f_bavail', c_fsblkcnt_t), 166 ('f_files', c_fsfilcnt_t), 167 ('f_ffree', c_fsfilcnt_t), 168 ('f_favail', c_fsfilcnt_t)] 169 170 class fuse_file_info(Structure): 171 _fields_ = [ 172 ('flags', c_int), 173 ('fh_old', c_ulong), 174 ('writepage', c_int), 175 ('direct_io', c_uint, 1), 176 ('keep_cache', c_uint, 1), 177 ('flush', c_uint, 1), 178 ('padding', c_uint, 29), 179 ('fh', c_uint64), 180 ('lock_owner', c_uint64)] 181 182 class fuse_ctx(Structure): 183 _fields_ = [('uid', c_uid_t), ('gid', c_gid_t), ('pid', c_pid_t)] 184 185 fuse_ino_t = c_ulong 186 fuse_req_t = c_void_p 187 c_stat_p = POINTER(c_stat) 188 fuse_file_info_p = POINTER(fuse_file_info) 189 190 FUSE_SET_ATTR = ('st_mode', 'st_uid', 'st_gid', 'st_size', 'st_atime', 'st_mtime') 191 192 class fuse_entry_param(Structure): 193 _fields_ = [ 194 ('ino', fuse_ino_t), 195 ('generation', c_ulong), 196 ('attr', c_stat), 197 ('attr_timeout', c_double), 198 ('entry_timeout', c_double)] 199 200 class fuse_lowlevel_ops(Structure): 201 _fields_ = [ 202 ('init', CFUNCTYPE(None, c_void_p, c_void_p)), 203 ('destroy', CFUNCTYPE(None, c_void_p)), 204 ('lookup', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)), 205 ('forget', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_ulong)), 206 ('getattr', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)), 207 ('setattr', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_stat_p, c_int, fuse_file_info_p)), 208 ('readlink', CFUNCTYPE(None, fuse_req_t, fuse_ino_t)), 209 ('mknod', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_mode_t, c_dev_t)), 210 ('mkdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_mode_t)), 211 ('unlink', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)), 212 ('rmdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p)), 213 ('symlink', CFUNCTYPE(None, fuse_req_t, c_char_p, fuse_ino_t, c_char_p)), 214 ('rename', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, fuse_ino_t, c_char_p)), 215 ('link', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_ino_t, c_char_p)), 216 ('open', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)), 217 ('read', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_size_t, c_off_t, fuse_file_info_p)), 218 ('write', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_char_p, c_size_t, c_off_t, 219 fuse_file_info_p)), 220 ('flush', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)), 221 ('release', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)), 222 ('fsync', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_int, fuse_file_info_p)), 223 ('opendir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)), 224 ('readdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_size_t, c_off_t, fuse_file_info_p)), 225 ('releasedir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, fuse_file_info_p)), 226 ('fsyncdir', CFUNCTYPE(None, fuse_req_t, fuse_ino_t, c_int, fuse_file_info_p))] 227 228 229 def struct_to_dict(p): 230 try: 231 x = p.contents 232 return dict((key, getattr(x, key)) for key, type in x._fields_) 233 except ValueError: 234 return {} 235 236 def stat_to_dict(p): 237 try: 238 d = {} 239 x = p.contents 240 for key, type in x._fields_: 241 if key in ('st_atimespec', 'st_mtimespec', 'st_ctimespec'): 242 ts = getattr(x, key) 243 key = key[:-4] # Lose the "spec" 244 d[key] = ts.tv_sec + ts.tv_nsec / 10 ** 9 245 else: 246 d[key] = getattr(x, key) 247 return d 248 except ValueError: 249 return {} 250 251 def dict_to_stat(d): 252 for key in ('st_atime', 'st_mtime', 'st_ctime'): 253 if key in d: 254 val = d[key] 255 sec = int(val) 256 nsec = int((val - sec) * 10 ** 9) 257 d[key + 'spec'] = c_timespec(sec, nsec) 258 return c_stat(**d) 259 260 def setattr_mask_to_list(mask): 261 return [FUSE_SET_ATTR[i] for i in range(len(FUSE_SET_ATTR)) if mask & (1 << i)] 262 263 class FUSELL(object): 264 def __init__(self, mountpoint): 265 self.libfuse = LibFUSE() 266 267 fuse_ops = fuse_lowlevel_ops() 268 269 for name, prototype in fuse_lowlevel_ops._fields_: 270 method = getattr(self, 'fuse_' + name, None) or getattr(self, name, None) 271 if method: 272 setattr(fuse_ops, name, prototype(method)) 273 274 args = ['fuse'] 275 argv = fuse_args(len(args), (c_char_p * len(args))(*args), 0) 276 277 # TODO: handle initialization errors 278 279 chan = self.libfuse.fuse_mount(mountpoint, argv) 280 assert chan 281 282 session = self.libfuse.fuse_lowlevel_new(argv, byref(fuse_ops), sizeof(fuse_ops), None) 283 assert session 284 285 err = self.libfuse.fuse_set_signal_handlers(session) 286 assert err == 0 287 288 self.libfuse.fuse_session_add_chan(session, chan) 289 290 err = self.libfuse.fuse_session_loop(session) 291 assert err == 0 292 293 err = self.libfuse.fuse_remove_signal_handlers(session) 294 assert err == 0 295 296 self.libfuse.fuse_session_remove_chan(chan) 297 self.libfuse.fuse_session_destroy(session) 298 self.libfuse.fuse_unmount(mountpoint, chan) 299 300 def reply_err(self, req, err): 301 return self.libfuse.fuse_reply_err(req, err) 302 303 def reply_none(self, req): 304 self.libfuse.fuse_reply_none(req) 305 306 def reply_entry(self, req, entry): 307 entry['attr'] = c_stat(**entry['attr']) 308 e = fuse_entry_param(**entry) 309 self.libfuse.fuse_reply_entry(req, byref(e)) 310 311 def reply_create(self, req, *args): 312 pass # XXX 313 314 def reply_attr(self, req, attr, attr_timeout): 315 st = dict_to_stat(attr) 316 return self.libfuse.fuse_reply_attr(req, byref(st), c_double(attr_timeout)) 317 318 def reply_readlink(self, req, *args): 319 pass # XXX 320 321 def reply_open(self, req, d): 322 fi = fuse_file_info(**d) 323 return self.libfuse.fuse_reply_open(req, byref(fi)) 324 325 def reply_write(self, req, count): 326 return self.libfuse.fuse_reply_write(req, count) 327 328 def reply_buf(self, req, buf): 329 return self.libfuse.fuse_reply_buf(req, buf, len(buf)) 330 331 def reply_readdir(self, req, size, off, entries): 332 bufsize = 0 333 sized_entries = [] 334 for name, attr in entries: 335 entsize = self.libfuse.fuse_add_direntry(req, None, 0, name, None, 0) 336 sized_entries.append((name, attr, entsize)) 337 bufsize += entsize 338 339 next = 0 340 buf = create_string_buffer(bufsize) 341 for name, attr, entsize in sized_entries: 342 entbuf = cast(addressof(buf) + next, c_char_p) 343 st = c_stat(**attr) 344 next += entsize 345 self.libfuse.fuse_add_direntry(req, entbuf, entsize, name, byref(st), next) 346 347 if off < bufsize: 348 buf = cast(addressof(buf) + off, c_char_p) if off else buf 349 return self.libfuse.fuse_reply_buf(req, buf, min(bufsize - off, size)) 350 else: 351 return self.libfuse.fuse_reply_buf(req, None, 0) 352 353 354 # If you override the following methods you should reply directly 355 # with the self.libfuse.fuse_reply_* methods. 356 357 def fuse_getattr(self, req, ino, fi): 358 self.getattr(req, ino, struct_to_dict(fi)) 359 360 def fuse_setattr(self, req, ino, attr, to_set, fi): 361 attr_dict = stat_to_dict(attr) 362 to_set_list = setattr_mask_to_list(to_set) 363 fi_dict = struct_to_dict(fi) 364 self.setattr(req, ino, attr_dict, to_set_list, fi_dict) 365 366 def fuse_open(self, req, ino, fi): 367 self.open(req, ino, struct_to_dict(fi)) 368 369 def fuse_read(self, req, ino, size, off, fi): 370 self.read(req, ino, size, off, fi) 371 372 def fuse_write(self, req, ino, buf, size, off, fi): 373 buf_str = string_at(buf, size) 374 fi_dict = struct_to_dict(fi) 375 self.write(req, ino, buf_str, off, fi_dict) 376 377 def fuse_flush(self, req, ino, fi): 378 self.flush(req, ino, struct_to_dict(fi)) 379 380 def fuse_release(self, req, ino, fi): 381 self.release(req, ino, struct_to_dict(fi)) 382 383 def fuse_fsync(self, req, ino, datasync, fi): 384 self.fsyncdir(req, ino, datasync, struct_to_dict(fi)) 385 386 def fuse_opendir(self, req, ino, fi): 387 self.opendir(req, ino, struct_to_dict(fi)) 388 389 def fuse_readdir(self, req, ino, size, off, fi): 390 self.readdir(req, ino, size, off, struct_to_dict(fi)) 391 392 def fuse_releasedir(self, req, ino, fi): 393 self.releasedir(req, ino, struct_to_dict(fi)) 394 395 def fuse_fsyncdir(self, req, ino, datasync, fi): 396 self.fsyncdir(req, ino, datasync, struct_to_dict(fi)) 397 398 399 # Utility methods 400 401 def req_ctx(self, req): 402 ctx = self.libfuse.fuse_req_ctx(req) 403 return struct_to_dict(ctx) 404 405 406 # Methods to be overridden in subclasses. 407 # Reply with the self.reply_* methods. 408 409 def init(self, userdata, conn): 410 """Initialize filesystem 411 412 There's no reply to this method 413 """ 414 pass 415 416 def destroy(self, userdata): 417 """Clean up filesystem 418 419 There's no reply to this method 420 """ 421 pass 422 423 def lookup(self, req, parent, name): 424 """Look up a directory entry by name and get its attributes. 425 426 Valid replies: 427 reply_entry 428 reply_err 429 """ 430 self.reply_err(req, ENOENT) 431 432 def forget(self, req, ino, nlookup): 433 """Forget about an inode 434 435 Valid replies: 436 reply_none 437 """ 438 self.reply_none(req) 439 440 def getattr(self, req, ino, fi): 441 """Get file attributes 442 443 Valid replies: 444 reply_attr 445 reply_err 446 """ 447 if ino == 1: 448 attr = {'st_ino': 1, 'st_mode': S_IFDIR | 0755, 'st_nlink': 2} 449 self.reply_attr(req, attr, 1.0) 450 else: 451 self.reply_err(req, ENOENT) 452 453 def setattr(self, req, ino, attr, to_set, fi): 454 """Set file attributes 455 456 Valid replies: 457 reply_attr 458 reply_err 459 """ 460 self.reply_err(req, EROFS) 461 462 def readlink(self, req, ino): 463 """Read symbolic link 464 465 Valid replies: 466 reply_readlink 467 reply_err 468 """ 469 self.reply_err(req, ENOENT) 470 471 def mknod(self, req, parent, name, mode, rdev): 472 """Create file node 473 474 Valid replies: 475 reply_entry 476 reply_err 477 """ 478 self.reply_err(req, EROFS) 479 480 def mkdir(self, req, parent, name, mode): 481 """Create a directory 482 483 Valid replies: 484 reply_entry 485 reply_err 486 """ 487 self.reply_err(req, EROFS) 488 489 def unlink(self, req, parent, name): 490 """Remove a file 491 492 Valid replies: 493 reply_err 494 """ 495 self.reply_err(req, EROFS) 496 497 def rmdir(self, req, parent, name): 498 """Remove a directory 499 500 Valid replies: 501 reply_err 502 """ 503 self.reply_err(req, EROFS) 504 505 def symlink(self, req, link, parent, name): 506 """Create a symbolic link 507 508 Valid replies: 509 reply_entry 510 reply_err 511 """ 512 self.reply_err(req, EROFS) 513 514 def rename(self, req, parent, name, newparent, newname): 515 """Rename a file 516 517 Valid replies: 518 reply_err 519 """ 520 self.reply_err(req, EROFS) 521 522 def link(self, req, ino, newparent, newname): 523 """Create a hard link 524 525 Valid replies: 526 reply_entry 527 reply_err 528 """ 529 self.reply_err(req, EROFS) 530 531 def open(self, req, ino, fi): 532 """Open a file 533 534 Valid replies: 535 reply_open 536 reply_err 537 """ 538 self.reply_open(req, fi) 539 540 def read(self, req, ino, size, off, fi): 541 """Read data 542 543 Valid replies: 544 reply_buf 545 reply_err 546 """ 547 self.reply_err(req, EIO) 548 549 def write(self, req, ino, buf, off, fi): 550 """Write data 551 552 Valid replies: 553 reply_write 554 reply_err 555 """ 556 self.reply_err(req, EROFS) 557 558 def flush(self, req, ino, fi): 559 """Flush method 560 561 Valid replies: 562 reply_err 563 """ 564 self.reply_err(req, 0) 565 566 def release(self, req, ino, fi): 567 """Release an open file 568 569 Valid replies: 570 reply_err 571 """ 572 self.reply_err(req, 0) 573 574 def fsync(self, req, ino, datasync, fi): 575 """Synchronize file contents 576 577 Valid replies: 578 reply_err 579 """ 580 self.reply_err(req, 0) 581 582 def opendir(self, req, ino, fi): 583 """Open a directory 584 585 Valid replies: 586 reply_open 587 reply_err 588 """ 589 self.reply_open(req, fi) 590 591 def readdir(self, req, ino, size, off, fi): 592 """Read directory 593 594 Valid replies: 595 reply_readdir 596 reply_err 597 """ 598 if ino == 1: 599 attr = {'st_ino': 1, 'st_mode': S_IFDIR} 600 entries = [('.', attr), ('..', attr)] 601 self.reply_readdir(req, size, off, entries) 602 else: 603 self.reply_err(req, ENOENT) 604 605 def releasedir(self, req, ino, fi): 606 """Release an open directory 607 608 Valid replies: 609 reply_err 610 """ 611 self.reply_err(req, 0) 612 613 def fsyncdir(self, req, ino, datasync, fi): 614 """Synchronize directory contents 615 616 Valid replies: 617 reply_err 618 """ 619 self.reply_err(req, 0)