/usr/lib/python2.7/dist-packages/llfuse/pyapi.py is in python-llfuse 0.40-2build2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 | '''
pyapi.py
Copyright (C) Nikolaus Rath <Nikolaus@rath.org>
This file is part of LLFUSE (http://python-llfuse.googlecode.com).
LLFUSE can be distributed under the terms of the GNU LGPL.
'''
from __future__ import division, print_function, absolute_import
import os
import errno
import logging
log = logging.getLogger('llfuse')
def strerror(errno):
try:
return os.strerror(errno)
except ValueError:
return 'errno: %d' % errno
class RequestContext:
'''
Instances of this class are passed to some `Operations` methods to
provide information about the caller of the syscall that initiated
the request.
'''
__slots__ = [ 'uid', 'pid', 'gid', 'umask' ]
def __init__(self):
for name in self.__slots__:
setattr(self, name, None)
class EntryAttributes:
'''
Instances of this class store attributes of directory entries.
Most of the attributes correspond to the elements of the ``stat``
C struct as returned by e.g. ``fstat`` and should be
self-explanatory.
The access, modification and creation times may be specified
either in nanoseconds (via the *st_Xtime_ns* attributes) or in
seconds (via the *st_Xtime* attributes). When times are specified
both in seconds and nanoseconds, the nanosecond representation
takes precedence. If times are represented in seconds, floating
point numbers may be used to achieve sub-second
resolution. Nanosecond time stamps must be integers. Note that
using integer nanoseconds is more accurately than using float
seconds.
Request handlers do not need to return objects that inherit from
`EntryAttributes` directly as long as they provide the required
attributes.
'''
# Attributes are documented in rst/data.rst
__slots__ = [ 'st_ino', 'generation', 'entry_timeout',
'attr_timeout', 'st_mode', 'st_nlink', 'st_uid', 'st_gid',
'st_rdev', 'st_size', 'st_blksize', 'st_blocks',
'st_atime', 'st_atime_ns', 'st_mtime', 'st_mtime_ns',
'st_ctime', 'st_ctime_ns' ]
def __init__(self):
for name in self.__slots__:
setattr(self, name, None)
class StatvfsData:
'''
Instances of this class store information about the file system.
The attributes correspond to the elements of the ``statvfs``
struct, see :manpage:`statvfs(2)` for details.
Request handlers do not need to return objects that inherit from
`StatvfsData` directly as long as they provide the required
attributes.
'''
# Attributes are documented in rst/operations.rst
__slots__ = [ 'f_bsize', 'f_frsize', 'f_blocks', 'f_bfree',
'f_bavail', 'f_files', 'f_ffree', 'f_favail' ]
def __init__(self):
for name in self.__slots__:
setattr(self, name, None)
class FUSEError(Exception):
'''
This exception may be raised by request handlers to indicate that
the requested operation could not be carried out. The system call
that resulted in the request (if any) will then fail with error
code *errno_*.
'''
__slots__ = [ 'errno' ]
def __init__(self, errno_):
super(FUSEError, self).__init__()
self.errno = errno_
def __str__(self):
return strerror(self.errno)
class Operations(object):
'''
This class defines the general and request handler methods that an
LLFUSE file system may implement. If a particular request handler
has not been implemented, it must raise `FUSEError` with an errorcode of
`errno.ENOSYS`.
It is recommended that file systems are derived from this class
and only overwrite the handlers that they actually implement. (The
default implementations defined in this class all just raise the
not-implemented exception).
The only exception that request handlers are allowed to raise is
`FUSEError`. This will cause the specified errno to be
returned by the syscall that is being handled.
Note that all character data (directory entry names, extended
attribute names and values, symbolic link targets etc) are passed
as `bytes` and must be returned as `bytes`. This applies to both
running under Python 2.x and 3.x
'''
def init(self):
'''Initialize operations
This function will be called just before the file system
starts handling requests. It must not raise any exceptions
(including `FUSEError`, since this method is not handling a
particular syscall).
'''
pass
def destroy(self):
'''Clean up operations.
This method will be called when `llfuse.close` has been called
and the file system is about to be unmounted.
Since this handler is thus *not* run as part of the main loop,
it is also *not* called with the global lock acquired (unless
the caller of `llfuse.close` already holds the lock).
This method must not raise any exceptions (including
`FUSEError`, since this method is not handling a particular
syscall).
'''
pass
def lookup(self, parent_inode, name):
'''Look up a directory entry by name and get its attributes.
If the entry *name* does not exist in the directory with inode
*parent_inode*, this method must raise `FUSEError` with an
errno of `errno.ENOENT`. Otherwise it must return an
`EntryAttributes` instance.
Once an inode has been returned by `lookup`, `create`,
`symlink`, `link`, `mknod` or `mkdir`, it must be kept by the
file system until it receives a `forget` request for the
inode. If `unlink` or `rmdir` requests are received prior to
the `forget` call, they are expect to remove only the
directory entry for the inode and defer removal of the inode
itself until the `forget` call.
The file system must be able to handle lookups for :file:`.`
and :file:`..`, no matter if these entries are returned by
`readdir` or not.
'''
raise FUSEError(errno.ENOSYS)
def forget(self, inode_list):
'''Notify about inodes being removed from the kernel cache
*inode_list* is a list of ``(inode, nlookup)`` tuples. This
method is called when the kernel removes the listed inodes
from its internal caches. *nlookup* is the number of times
that the inode has been looked up by calling either of the
`lookup`, `create`, `symlink`, `mknod`, `link` or `mkdir`
methods.
The file system is expected to keep track of the number of
times an inode has been looked up and forgotten. No request
handlers other than `lookup` will be called for an inode with
a lookup count of zero.
If the lookup count reaches zero after a call to `forget`, the
file system is expected to check if there are still directory
entries referring to this inode and, if not, delete the inode
itself.
If the file system is unmounted, it will may not receive
`forget` calls for inodes that are still cached. The `destroy`
method may be used to clean up any remaining inodes for which
no `forget` call has been received.
'''
pass
def getattr(self, inode):
'''Get attributes for *inode*
This method should return an `EntryAttributes` instance with
the attributes of *inode*. The
`~EntryAttributes.entry_timeout` attribute is ignored in this
context.
'''
raise FUSEError(errno.ENOSYS)
def setattr(self, inode, attr):
'''Change attributes of *inode*
*attr* is an `EntryAttributes` instance with the new
attributes. Only the attributes `~EntryAttributes.st_size`,
`~EntryAttributes.st_mode`, `~EntryAttributes.st_uid`,
`~EntryAttributes.st_gid`, `~EntryAttributes.st_atime` and
`~EntryAttributes.st_mtime` are relevant. Unchanged attributes
will have a value `None`.
The method should return a new `EntryAttributes` instance
with the updated attributes (i.e., all attributes except for
`~EntryAttributes.entry_timeout` should be set).
'''
raise FUSEError(errno.ENOSYS)
def readlink(self, inode):
'''Return target of symbolic link
The return value must have type `bytes`.
'''
raise FUSEError(errno.ENOSYS)
def mknod(self, parent_inode, name, mode, rdev, ctx):
'''Create (possibly special) file
*ctx* will be a `RequestContext` instance. The method must
return an `EntryAttributes` instance with the attributes of
the newly created directory entry.
Once an inode has been returned by `lookup`, `create`,
`symlink`, `link`, `mknod` or `mkdir`, it must be kept by the
file system until it receives a `forget` request for the
inode. If `unlink` or `rmdir` requests are received prior to
the `forget` call, they are expect to remove only the
directory entry for the inode and defer removal of the inode
itself until the `forget` call.
'''
raise FUSEError(errno.ENOSYS)
def mkdir(self, parent_inode, name, mode, ctx):
'''Create a directory
*ctx* will be a `RequestContext` instance. The method must
return an `EntryAttributes` instance with the attributes of
the newly created directory entry.
Once an inode has been returned by `lookup`, `create`,
`symlink`, `link`, `mknod` or `mkdir`, it must be kept by the
file system until it receives a `forget` request for the
inode. If `unlink` or `rmdir` requests are received prior to
the `forget` call, they are expect to remove only the
directory entry for the inode and defer removal of the inode
itself until the `forget` call.
'''
raise FUSEError(errno.ENOSYS)
def unlink(self, parent_inode, name):
'''Remove a (possibly special) file
If the file system has received a `lookup`, but no `forget`
call for this file yet, `unlink` is expected to remove only
the directory entry and defer removal of the inode with the
actual file contents and metadata until the `forget` call is
received.
Note that an unlinked file may also appear again if it gets a
new directory entry by the `link` method.
'''
raise FUSEError(errno.ENOSYS)
def rmdir(self, inode_parent, name):
'''Remove a directory
If the file system has received a `lookup`, but no `forget`
call for this file yet, `unlink` is expected to remove only
the directory entry and defer removal of the inode with the
actual file contents and metadata until the `forget` call is
received.
'''
raise FUSEError(errno.ENOSYS)
def symlink(self, inode_parent, name, target, ctx):
'''Create a symbolic link
*ctx* will be a `RequestContext` instance. The method must
return an `EntryAttributes` instance with the attributes of
the newly created directory entry.
Once an inode has been returned by `lookup`, `create`,
`symlink`, `link`, `mknod` or `mkdir`, it must be kept by the
file system until it receives a `forget` request for the
inode. If `unlink` or `rmdir` requests are received prior to
the `forget` call, they are expect to remove only the
directory entry for the inode and defer removal of the inode
itself until the `forget` call.
'''
raise FUSEError(errno.ENOSYS)
def rename(self, inode_parent_old, name_old, inode_parent_new, name_new):
'''Rename a directory entry
If *name_new* already exists, it should be overwritten.
If the file system has received a `lookup`, but no `forget`
call for the file that is about to be overwritten, `rename` is
expected to only overwrite the directory entry and defer
removal of the old inode with the its contents and metadata
until the `forget` call is received.
'''
raise FUSEError(errno.ENOSYS)
def link(self, inode, new_parent_inode, new_name):
'''Create a hard link.
The method must return an `EntryAttributes` instance with the
attributes of the newly created directory entry.
Once an inode has been returned by `lookup`, `create`,
`symlink`, `link`, `mknod` or `mkdir`, it must be kept by the
file system until it receives a `forget` request for the
inode. If `unlink` or `rmdir` requests are received prior to
the `forget` call, they are expect to remove only the
directory entry for the inode and defer removal of the inode
itself until the `forget` call.
'''
raise FUSEError(errno.ENOSYS)
def open(self, inode, flags):
'''Open a file.
*flags* will be a bitwise or of the open flags described in
the :manpage:`open(2)` manpage and defined in the `os` module
(with the exception of ``O_CREAT``, ``O_EXCL``, ``O_NOCTTY``
and ``O_TRUNC``)
This method should return an integer file handle. The file
handle will be passed to the `read`, `write`, `flush`, `fsync`
and `release` methods to identify the open file.
'''
raise FUSEError(errno.ENOSYS)
def read(self, fh, off, size):
'''Read *size* bytes from *fh* at position *off*
This function should return exactly the number of bytes
requested except on EOF or error, otherwise the rest of the
data will be substituted with zeroes.
'''
raise FUSEError(errno.ENOSYS)
def write(self, fh, off, buf):
'''Write *buf* into *fh* at *off*
This method should return the number of bytes written. If no
error occured, this should be exactly :samp:`len(buf)`.
'''
raise FUSEError(errno.ENOSYS)
def flush(self, fh):
'''Handle close() syscall.
This method is called whenever a file descriptor is closed. It
may be called multiple times for the same open file (e.g. if
the file handle has been duplicated).
If the file system implements locking, this method must clear
all locks belonging to the file handle's owner.
'''
raise FUSEError(errno.ENOSYS)
def release(self, fh):
'''Release open file
This method will be called when the last file descriptor of
*fh* has been closed. Therefore it will be called exactly once
for each `open` call.
'''
raise FUSEError(errno.ENOSYS)
def fsync(self, fh, datasync):
'''Flush buffers for open file *fh*
If *datasync* is true, only the file contents should be
flushed (in contrast to the metadata about the file).
'''
raise FUSEError(errno.ENOSYS)
def opendir(self, inode):
'''Open a directory.
This method should return an integer file handle. The file
handle will be passed to the `readdir`, `fsyncdir`
and `releasedir` methods to identify the directory.
'''
raise FUSEError(errno.ENOSYS)
def readdir(self, fh, off):
'''Read directory entries
This method should return an iterator over the contents of
directory *fh*, starting at the entry identified by *off*.
Directory entries must be of type `bytes`.
The iterator must yield tuples of the form :samp:`({name}, {attr},
{next_})`, where *attr* is an `EntryAttributes` instance and
*next_* gives an offset that can be passed as *off* to start
a successive `readdir` call at the right position.
Iteration may be stopped as soon as enough elements have been
retrieved. The method should be prepared for this case.
If entries are added or removed during a `readdir` cycle, they
may or may not be returned. However, they must not cause other
entries to be skipped or returned more than once.
:file:`.` and :file:`..` entries may be included but are not
required.
'''
raise FUSEError(errno.ENOSYS)
def releasedir(self, fh):
'''Release open directory
This method must be called exactly once for each `opendir` call.
'''
raise FUSEError(errno.ENOSYS)
def fsyncdir(self, fh, datasync):
'''Flush buffers for open directory *fh*
If *datasync* is true, only the directory contents should be
flushed (in contrast to metadata about the directory itself).
'''
raise FUSEError(errno.ENOSYS)
def statfs(self):
'''Get file system statistics
The method is expected to return an appropriately filled
`StatvfsData` instance.
'''
raise FUSEError(errno.ENOSYS)
def stacktrace(self):
'''Asynchronous debugging
This method will be called when the ``fuse_stacktrace`` extended
attribute is set on the mountpoint. It will be called without
holding the global lock. The default implementation logs the
current stack trace of every running Python thread. This can be
quite useful to debug file system deadlocks.
'''
import sys
import traceback
code = list()
for threadId, frame in sys._current_frames().items():
code.append("\n# ThreadID: %s" % threadId)
for filename, lineno, name, line in traceback.extract_stack(frame):
code.append('%s:%d, in %s' % (os.path.basename(filename), lineno, name))
if line:
code.append(" %s" % (line.strip()))
log.error("\n".join(code))
def setxattr(self, inode, name, value):
'''Set an extended attribute.
The attribute may or may not exist already.
'''
raise FUSEError(errno.ENOSYS)
def getxattr(self, inode, name):
'''Return extended attribute value
If the attribute does not exist, the method must raise
`FUSEError` with an error code of `ENOATTR`.
'''
raise FUSEError(errno.ENOSYS)
def listxattr(self, inode):
'''Get list of extended attribute names'''
raise FUSEError(errno.ENOSYS)
def removexattr(self, inode, name):
'''Remove extended attribute
If the attribute does not exist, the method must raise
`FUSEError` with an error code of `ENOATTR`.
'''
raise FUSEError(errno.ENOSYS)
def access(self, inode, mode, ctx):
'''Check if requesting process has *mode* rights on *inode*.
*ctx* will be a `RequestContext` instance. The method
must return a boolean value.
If the ``default_permissions`` mount option is given, this method is not
called.
'''
raise FUSEError(errno.ENOSYS)
def create(self, inode_parent, name, mode, flags, ctx):
'''Create a file with permissions *mode* and open it with *flags*
*ctx* will be a `RequestContext` instance.
The method must return a tuple of the form *(fh, attr)*,
where *fh* is a file handle like the one returned by `open`
and *attr* is an `EntryAttributes` instance with the
attributes of the newly created directory entry.
Once an inode has been returned by `lookup`, `create`,
`symlink`, `link`, `mknod` or `mkdir`, it must be kept by the
file system until it receives a `forget` request for the
inode. If `unlink` or `rmdir` requests are received prior to
the `forget` call, they are expect to remove only the
directory entry for the inode and defer removal of the inode
itself until the `forget` call.
'''
raise FUSEError(errno.ENOSYS)
|