def test_notify_store(testfs): (mnt_dir, fs_state) = testfs with open(os.path.join(mnt_dir, 'message'), 'r') as fh: pyfuse3.setxattr(mnt_dir, 'command', b'store') fs_state.read_called = False assert fh.read() == 'hello world\n' assert not fs_state.read_called
def main(args=None): '''Control a mounted S3QL File System.''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) path = options.mountpoint ctrlfile = assert_fs_owner(path, mountpoint=True) if options.action == 'flushcache': pyfuse3.setxattr(ctrlfile, 's3ql_flushcache!', b'dummy') elif options.action == 'dropcache': pyfuse3.setxattr(ctrlfile, 's3ql_dropcache!', b'dummy') elif options.action == 'upload-meta': pyfuse3.setxattr(ctrlfile, 'upload-meta', b'dummy') elif options.action == 'log': level = getattr(logging, options.level.upper()) cmd = ('(%r, %r)' % (level, ','.join(options.modules))).encode() pyfuse3.setxattr(ctrlfile, 'logging', cmd) elif options.action == 'cachesize': pyfuse3.setxattr(ctrlfile, 'cachesize', ('%d' % (options.cachesize * 1024, )).encode())
async def setxattr(self, inode: INode, name: bytes, value: bytes, ctx: RequestContext) -> None: try: pyfuse3.setxattr(path=self.paths[inode], name=_bytes2str(name), value=value) except OSError as exc: raise FUSEError(exc.errno) from None
def main(args=None): '''Efficiently copy a directory tree''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) if not os.path.exists(options.source): raise QuietError('Source directory %r does not exist' % options.source) if os.path.exists(options.target): raise QuietError('Target directory %r must not yet exist.' % options.target) parent = os.path.dirname(os.path.abspath(options.target)) if not os.path.exists(parent): raise QuietError('Target parent %r does not exist' % parent) fstat_s = os.stat(options.source) fstat_p = os.stat(parent) if not stat.S_ISDIR(fstat_s.st_mode): raise QuietError('Source %r is not a directory' % options.source) if not stat.S_ISDIR(fstat_p.st_mode): raise QuietError('Target parent %r is not a directory' % parent) if fstat_p.st_dev != fstat_s.st_dev: raise QuietError('Source and target are not on the same file system.') if os.path.ismount(options.source): raise QuietError('%s is a mount point.' % options.source) ctrlfile = assert_fs_owner(options.source) try: os.mkdir(options.target) except PermissionError: raise QuietError('No permission to create target directory') # Make sure that write cache is flushed pyfuse3.syncfs(options.target) # Ensure the inode of the target folder stays in the kernel dentry cache # (We invalidate it during the copy) with os.scandir(options.target) as it: fstat_t = os.stat(options.target) pyfuse3.setxattr(ctrlfile, 'copy', ('(%d, %d)' % (fstat_s.st_ino, fstat_t.st_ino)).encode())
def test_invalidate_inode(testfs): (mnt_dir, fs_state) = testfs with open(os.path.join(mnt_dir, 'message'), 'r') as fh: assert fh.read() == 'hello world\n' assert fs_state.read_called fs_state.read_called = False fh.seek(0) assert fh.read() == 'hello world\n' assert not fs_state.read_called pyfuse3.setxattr(mnt_dir, 'command', b'forget_inode') fh.seek(0) assert fh.read() == 'hello world\n' assert fs_state.read_called
def tst_bug382(self): dirname = self.newname() fullname = self.mnt_dir + "/" + dirname os.mkdir(fullname) assert stat.S_ISDIR(os.stat(fullname).st_mode) assert dirname in pyfuse3.listdir(self.mnt_dir) cmd = ('(%d, %r)' % (pyfuse3.ROOT_INODE, path2bytes(dirname))).encode() pyfuse3.setxattr('%s/%s' % (self.mnt_dir, CTRL_NAME), 'rmtree', cmd) # Invalidation is asynchronous... try: retry(5, lambda: not os.path.exists(fullname)) except RetryTimeoutError: pass # assert_raises should fail assert_raises(FileNotFoundError, os.stat, fullname) assert dirname not in pyfuse3.listdir(self.mnt_dir)
def test_invalidate_entry(testfs): (mnt_dir, fs_state) = testfs path = os.path.join(mnt_dir, 'message') os.stat(path) assert fs_state.lookup_called fs_state.lookup_called = False os.stat(path) assert not fs_state.lookup_called # Hardcoded sleeptimes - sorry! Needed because of the special semantics of # invalidate_entry() pyfuse3.setxattr(mnt_dir, 'command', b'forget_entry') time.sleep(1.1) os.stat(path) assert fs_state.lookup_called
def main(args=None): '''Make directory tree immutable''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) for name in options.path: if os.path.ismount(name): raise QuietError('%s is a mount point.' % name) ctrlfile = assert_fs_owner(name) fstat = os.stat(name) pyfuse3.setxattr(ctrlfile, 'lock', ('%d' % fstat.st_ino).encode())
def test_terminate(tmpdir): mnt_dir = str(tmpdir) mp = get_mp() with mp.Manager() as mgr: fs_state = mgr.Namespace() mount_process = mp.Process(target=run_fs, args=(mnt_dir, fs_state)) mount_process.start() try: wait_for_mount(mount_process, mnt_dir) pyfuse3.setxattr(mnt_dir, 'command', b'terminate') mount_process.join(5) assert mount_process.exitcode is not None except: cleanup(mount_process, mnt_dir) raise
def main(args=None): '''Recursively delete files and directories in an S3QL file system''' if args is None: args = sys.argv[1:] options = parse_args(args) setup_logging(options) for name in options.path: if os.path.ismount(name): raise QuietError('%s is a mount point.' % name) ctrlfile = assert_fs_owner(name) fstat_p = os.stat(os.path.dirname(os.path.abspath(name))) # Make sure that write cache is flushed pyfuse3.syncfs(name) cmd = ('(%d, %r)' % (fstat_p.st_ino, path2bytes(os.path.basename(name)))).encode() pyfuse3.setxattr(ctrlfile, 'rmtree', cmd)
def test_xattr(): with tempfile.NamedTemporaryFile() as fh: key = 'user.new_attribute' assert _getxattr_helper(fh.name, key) is None value = b'a nice little bytestring' try: pyfuse3.setxattr(fh.name, key, value) except OSError as exc: if exc.errno == errno.ENOTSUP: pytest.skip('ACLs not supported for %s' % fh.name) raise assert _getxattr_helper(fh.name, key) == value if not hasattr(os, 'setxattr'): return key = 'user.another_new_attribute' assert _getxattr_helper(fh.name, key) is None value = b'a nice little bytestring, but slightly modified' os.setxattr(fh.name, key, value) assert _getxattr_helper(fh.name, key) == value
def blocking_umount(mountpoint): '''Invoke fusermount and wait for daemon to terminate.''' with open('/dev/null', 'wb') as devnull: if subprocess.call(['fuser', '-m', mountpoint], stdout=devnull, stderr=devnull) == 0: raise MountInUseError(mountpoint) ctrlfile = os.path.join(mountpoint, CTRL_NAME) log.debug('Flushing cache...') pyfuse3.setxattr(ctrlfile, 's3ql_flushcache!', b'dummy') # Get pid log.debug('Trying to get pid') pid = parse_literal(pyfuse3.getxattr(ctrlfile, 's3ql_pid?'), int) log.debug('PID is %d', pid) # Get command line to make race conditions less-likely cmdline = get_cmdline(pid) # Unmount log.debug('Unmounting...') if os.getuid() == 0 or platform.system() == 'Darwin': # MacOS X always uses umount rather than fusermount umount_cmd = ['umount', mountpoint] else: umount_cmd = ['fusermount', '-u', mountpoint] if subprocess.call(umount_cmd) != 0: raise UmountSubError(mountpoint) # Wait for daemon log.debug('Uploading metadata...') step = 0.1 while True: try: os.kill(pid, 0) except OSError: log.debug('Kill failed, assuming daemon has quit.') break # Check that the process did not terminate and the PID # was reused by a different process cmdline2 = get_cmdline(pid) if cmdline2 is None: log.debug('Reading cmdline failed, assuming daemon has quit.') break elif cmdline2 == cmdline: log.debug('PID still alive and commandline unchanged.') else: log.debug('PID still alive, but cmdline changed') break # Process still exists, we wait log.debug('Daemon seems to be alive, waiting...') time.sleep(step) if step < 1: step += 0.1