async def setattr(self, id_, attr, fields, fh, ctx): """Handles FUSE setattr() requests""" inode = self.inodes[id_] if fh is not None: assert fh == id_ now_ns = time_ns() if self.failsafe or inode.locked: raise FUSEError(errno.EPERM) if fields.update_mode: inode.mode = attr.st_mode if fields.update_uid: inode.uid = attr.st_uid if fields.update_gid: inode.gid = attr.st_gid if fields.update_atime: inode.atime_ns = attr.st_atime_ns if fields.update_mtime: inode.mtime_ns = attr.st_mtime_ns inode.ctime_ns = now_ns # This needs to go last, because the call to cache.remove and cache.get # are asynchronous and may thus evict the *inode* object from the cache. if fields.update_size: len_ = attr.st_size # Determine blocks to delete last_block = len_ // self.max_obj_size cutoff = len_ % self.max_obj_size total_blocks = int(math.ceil(inode.size / self.max_obj_size)) # Adjust file size inode.size = len_ # Delete blocks and truncate last one if required if cutoff == 0: await self.cache.remove(id_, last_block, total_blocks) else: await self.cache.remove(id_, last_block + 1, total_blocks) try: async with self.cache.get(id_, last_block) as fh: fh.truncate(cutoff) except NoSuchObject as exc: log.warning('Backend lost block %d of inode %d (id %s)!', last_block, id_, exc.key) raise except CorruptedObjectError as exc: log.warning( 'Backend returned malformed data for block %d of inode %d (%s)', last_block, id_, exc) self.failsafe = True self.broken_blocks[id_].add(last_block) raise FUSEError(errno.EIO) return inode.entry_attributes()
async def remove_tree(self, id_p0, name0): '''Remove directory tree''' if self.failsafe: raise FUSEError(errno.EPERM) log.debug('started with %d, %s', id_p0, name0) if self.inodes[id_p0].locked: raise FUSEError(errno.EPERM) id0 = self._lookup(id_p0, name0, ctx=None).id queue = [id0] # Directories that we still need to delete batch_size = 200 # Entries to process before checkpointing stamp = time.time() # Time of last checkpoint while queue: # For every directory id_p = queue.pop() is_open = id_p in self.open_inodes # Per https://sqlite.org/isolation.html, results of removing rows # during select are undefined. Therefore, process data in chunks. # This is also a nice opportunity to release the GIL... query_chunk = self.db.get_list( 'SELECT name, name_id, inode FROM contents_v WHERE ' 'parent_inode=? LIMIT %d' % batch_size, (id_p, )) reinserted = False for (name, name_id, id_) in query_chunk: if self.db.has_val( 'SELECT 1 FROM contents WHERE parent_inode=?', (id_, )): # First delete subdirectories if not reinserted: queue.append(id_p) reinserted = True queue.append(id_) else: if is_open: pyfuse3.invalidate_entry_async(id_p, name) await self._remove(id_p, name, id_, force=True) if query_chunk and not reinserted: # Make sure to re-insert the directory to process the remaining # contents and delete the directory itself. queue.append(id_p) dt = time.time() - stamp batch_size = int(batch_size * CHECKPOINT_INTERVAL / dt) batch_size = min(batch_size, 200) # somewhat arbitrary... batch_size = max(batch_size, 20000) log.debug('Adjusting batch_size to %d and yielding', batch_size) await trio.lowlevel.checkpoint() log.debug('re-acquired lock') stamp = time.time() if id_p0 in self.open_inodes: log.debug('invalidate_entry(%d, %r)', id_p0, name0) pyfuse3.invalidate_entry_async(id_p0, name0) await self._remove(id_p0, name0, id0, force=True) await self.forget([(id0, 1)]) log.debug('finished')
async def copy_tree(self, src_id, target_id): '''Efficiently copy directory tree''' if self.failsafe: raise FUSEError(errno.EPERM) log.debug('started with %d, %d', src_id, target_id) # To avoid lookups and make code tidier make_inode = self.inodes.create_inode db = self.db # Copy target attributes # These come from setxattr, so they may have been deleted # without being in open_inodes try: src_inode = self.inodes[src_id] target_inode = self.inodes[target_id] except KeyError: raise FUSEError(errno.ENOENT) for attr in ('atime_ns', 'ctime_ns', 'mtime_ns', 'mode', 'uid', 'gid'): setattr(target_inode, attr, getattr(src_inode, attr)) # We first replicate into a dummy inode, so that we # need to invalidate only once. now_ns = time_ns() tmp = make_inode(mtime_ns=now_ns, ctime_ns=now_ns, atime_ns=now_ns, uid=0, gid=0, mode=0, refcount=0) queue = [(src_id, tmp.id, -1)] id_cache = dict() while queue: (src_id, target_id, off) = queue.pop() log.debug('Processing directory (%d, %d, %d)', src_id, target_id, off) processed = 0 with db.query( 'SELECT name_id, inode FROM contents WHERE parent_inode=? ' 'AND name_id > ? ORDER BY name_id', (src_id, off)) as res: for (name_id, id_) in res: # Make sure that all blocks are in the database if id_ in self.open_inodes: await self.cache.start_flush(id_) if id_ not in id_cache: inode = self.inodes[id_] inode_new = make_inode(refcount=1, mode=inode.mode, size=inode.size, uid=inode.uid, gid=inode.gid, mtime_ns=inode.mtime_ns, atime_ns=inode.atime_ns, ctime_ns=inode.ctime_ns, rdev=inode.rdev) id_new = inode_new.id if inode.refcount != 1: id_cache[id_] = id_new db.execute( 'INSERT INTO symlink_targets (inode, target) ' 'SELECT ?, target FROM symlink_targets WHERE inode=?', (id_new, id_)) db.execute( 'INSERT INTO ext_attributes (inode, name_id, value) ' 'SELECT ?, name_id, value FROM ext_attributes WHERE inode=?', (id_new, id_)) db.execute( 'UPDATE names SET refcount = refcount + 1 WHERE ' 'id IN (SELECT name_id FROM ext_attributes WHERE inode=?)', (id_, )) processed += db.execute( 'INSERT INTO inode_blocks (inode, blockno, block_id) ' 'SELECT ?, blockno, block_id FROM inode_blocks ' 'WHERE inode=?', (id_new, id_)) db.execute( 'REPLACE INTO blocks (id, hash, refcount, size, obj_id) ' 'SELECT id, hash, refcount+COUNT(id), size, obj_id ' 'FROM inode_blocks JOIN blocks ON block_id = id ' 'WHERE inode = ? GROUP BY id', (id_new, )) if db.has_val( 'SELECT 1 FROM contents WHERE parent_inode=?', (id_, )): queue.append((id_, id_new, -1)) else: id_new = id_cache[id_] self.inodes[id_new].refcount += 1 db.execute( 'INSERT INTO contents (name_id, inode, parent_inode) VALUES(?, ?, ?)', (name_id, id_new, target_id)) db.execute( 'UPDATE names SET refcount=refcount+1 WHERE id=?', (name_id, )) # Break every once in a while - note that we can't yield # right here because there's an active DB query. processed += 1 if processed > 200: queue.append((src_id, target_id, name_id)) break await trio.lowlevel.checkpoint() # Make replication visible self.db.execute( 'UPDATE contents SET parent_inode=? WHERE parent_inode=?', (target_inode.id, tmp.id)) del self.inodes[tmp.id] pyfuse3.invalidate_inode(target_inode.id) log.debug('finished')
async def read(self, fh: FileHandle, off: int, size: int) -> bytes: try: os.lseek(fh, off, os.SEEK_SET) return os.read(fh, size) except OSError as exc: raise FUSEError(exc.errno) from None
async def setxattr(self, id_, name, value, ctx): log.debug('started with %d, %r, %r', id_, name, value) # Handle S3QL commands if id_ == CTRL_INODE: if name == b's3ql_flushcache!': self.inodes.flush() await self.cache.flush() elif name == b's3ql_dropcache!': self.inodes.drop() await self.cache.drop() elif name == b'copy': try: tup = parse_literal(value, (int, int)) except ValueError: log.warning('Received malformed command via control inode') raise FUSEError.EINVAL() await self.copy_tree(*tup) elif name == b'upload-meta': if self.upload_task is not None: self.inodes.flush() self.upload_task.event.set() else: raise FUSEError(errno.ENOTTY) elif name == b'lock': try: id_ = parse_literal(value, int) except ValueError: log.warning('Received malformed command via control inode') raise FUSEError.EINVAL() await self.lock_tree(id_) elif name == b'rmtree': try: tup = parse_literal(value, (int, bytes)) except ValueError: log.warning('Received malformed command via control inode') raise FUSEError.EINVAL() await self.remove_tree(*tup) elif name == b'logging': try: (lvl, modules) = parse_literal(value, (int, str)) except (ValueError, KeyError): log.warning('Received malformed command via control inode') raise FUSEError.EINVAL() update_logging(lvl, modules.split(',') if modules else None) elif name == b'cachesize': try: self.cache.cache.max_size = parse_literal(value, int) except ValueError: log.warning('Received malformed command via control inode') raise FUSEError.EINVAL() log.debug('updated cache size to %d bytes', self.cache.cache.max_size) else: log.warning('Received unknown command via control inode') raise FUSEError(errno.EINVAL) # http://code.google.com/p/s3ql/issues/detail?id=385 elif name in (b'system.posix_acl_access', b'system.posix_acl_default'): raise FUSEError(ACL_ERRNO) else: if self.failsafe or self.inodes[id_].locked: raise FUSEError(errno.EPERM) if len(value) > deltadump.MAX_BLOB_SIZE: raise FUSEError(errno.EINVAL) self.db.execute( 'INSERT OR REPLACE INTO ext_attributes (inode, name_id, value) ' 'VALUES(?, ?, ?)', (id_, self._add_name(name), value)) self.inodes[id_].ctime_ns = time_ns()
async def link(self, inode, new_inode_p, new_name, ctx): raise (FUSEError(errno.ENOTSUP))
async def mkdir(self, inode_p, name, mode, ctx): path = os.path.join(self._inode_to_path(inode_p), fsdecode(name)) print("path mkdir: ", path) start = "/sys/class/gpio/" print("start: ", start) relative_path = os.path.relpath(path, start) print("relative path ", relative_path) print("\n") print("listagpio2 utilizzabile per test2: ", listgpio) if path.startswith('/sys/class/gpio/'): start = '/sys/class/gpio/' relative_path = os.path.relpath(path, start) print("relative path ", relative_path) if relative_path in listgpio: print(relative_path, "trovato\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) else: print("questo gpio non puoi usarlo2, puoi utilizzare dall'8 al 15!") attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr elif path.startswith('/sys/devices/platform/soc/3f200000.gpio/gpiochip0/gpio/'): start = '/sys/devices/platform/soc/3f200000.gpio/gpiochip0/gpio/' relative_path = os.path.relpath(path, start) print("relative path ", relative_path) if relative_path in listgpio: print(relative_path, "trovato\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) else: print("questo gpio non puoi usarlo2, puoi utilizzare dall'8 al 15!") attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr else: print("caso non analizzato\n\n\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr
def _fd_to_cryptors(self, fd): v = self._fd2cryptors.get(fd) if v is None: LOG.error('Error finding cryptor for %d: %r', fd, exc) raise FUSEError(errno.EBADF) return v
async def access(self, inode, mode, ctx): # for permissions but eh raise FUSEError(errno.ENOSYS)
async def _not_permitted_func(name, *args, **kwargs): LOG.debug('Function %s not permitted', name) raise FUSEError(errno.EPERM) # not permitted
def _inode_to_path(self, inode): v = self._inode2path.get(inode) if v is None: raise FUSEError(errno.ENOENT) return v
async def write(self, fh: FileHandle, off: int, buf: bytes) -> int: try: os.lseek(fh, off, os.SEEK_SET) return os.write(fh, buf) except OSError as exc: raise FUSEError(exc.errno) from None
async def release(self, fh: FileHandle) -> None: if self.descriptors.release(cast(FileDescriptor, fh)): try: os.close(fh) except OSError as exc: raise FUSEError(exc.errno)
async def readlink(self, inode: INode, ctx: RequestContext) -> bytes: try: return os.fsencode(os.readlink(self.paths[inode])) except OSError as exc: raise FUSEError(exc.errno) from None
async def readlink(self, inode: int, ctx: pyfuse3.RequestContext): raise (FUSEError(errno.ENOTSUP)) # Error: not supported
async def setxattr(self, inode, name, value, ctx): raise FUSEError(errno.ENOSYS)
async def symlink(self, inode_p: int, name: bytes, target: bytes, ctx: pyfuse3.RequestContext): raise (FUSEError(errno.ENOTSUP)) # Error: not supported
async def listxattr(self, inode, ctx): raise FUSEError(errno.ENOSYS)
async def mkdir(self, inode_p, name, mode, ctx): path = os.path.join(self._inode_to_path(inode_p), fsdecode(name)) print("path mkdir: ", path) start = "/sys/class/gpio/" print("start: ", start) relative_path = os.path.relpath(path, start) print("relative path ", relative_path) config = configparser.ConfigParser() config.sections() config.read('example.ini') config.sections() listgpio = ['gpiochip0', 'gpiochip504'] print("\n\n\n") for count in range(1, 27): print(count, "-gpio" + str(count)) print(config['gpiotest3']['gpio' + str(count)]) if config['gpiotest3']['gpio' + str(count)] == 'yes': print("ok2") listgpio.append("gpio" + str(count)) print("\n") print("\n") print("listagpio2 utilizzabile per test3: ", listgpio) if path.startswith('/sys/class/gpio/'): start = '/sys/class/gpio/' relative_path = os.path.relpath(path, start) print("relative path ", relative_path) if relative_path in listgpio: print(relative_path, "trovato\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) else: print( "questo gpio non puoi usarlo2, puoi utilizzare dall'8 al 15!" ) attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr elif path.startswith( '/sys/devices/platform/soc/3f200000.gpio/gpiochip0/gpio/'): start = '/sys/devices/platform/soc/3f200000.gpio/gpiochip0/gpio/' relative_path = os.path.relpath(path, start) print("relative path ", relative_path) if relative_path in listgpio: print(relative_path, "trovato\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) else: print( "questo gpio non puoi usarlo2, puoi utilizzare dall'8 al 15!" ) attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr else: print("caso non analizzato\n\n\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr
async def removexattr(self, inode, name, ctx): raise FUSEError(errno.ENOSYS)
async def setattr(self, inode, attr, fields, fh, ctx): # We use the f* functions if possible so that we can handle # a setattr() call for an inode without associated directory # handle. if fh is None: path_or_fh = self._inode_to_path(inode) truncate = os.truncate chmod = os.chmod chown = os.chown stat = os.lstat else: path_or_fh = fh truncate = os.ftruncate chmod = os.fchmod chown = os.fchown stat = os.fstat try: if fields.update_size: truncate(path_or_fh, attr.st_size) if fields.update_mode: # Under Linux, chmod always resolves symlinks so we should # actually never get a setattr() request for a symbolic # link. assert not stat_m.S_ISLNK(attr.st_mode) chmod(path_or_fh, stat_m.S_IMODE(attr.st_mode)) if fields.update_uid: chown(path_or_fh, attr.st_uid, -1, follow_symlinks=False) if fields.update_gid: chown(path_or_fh, -1, attr.st_gid, follow_symlinks=False) if fields.update_atime and fields.update_mtime: if fh is None: os.utime(path_or_fh, None, follow_symlinks=False, ns=(attr.st_atime_ns, attr.st_mtime_ns)) else: os.utime(path_or_fh, None, ns=(attr.st_atime_ns, attr.st_mtime_ns)) elif fields.update_atime or fields.update_mtime: # We can only set both values, so we first need to retrieve the # one that we shouldn't be changing. oldstat = stat(path_or_fh) if not fields.update_atime: attr.st_atime_ns = oldstat.st_atime_ns else: attr.st_mtime_ns = oldstat.st_mtime_ns if fh is None: os.utime(path_or_fh, None, follow_symlinks=False, ns=(attr.st_atime_ns, attr.st_mtime_ns)) else: os.utime(path_or_fh, None, ns=(attr.st_atime_ns, attr.st_mtime_ns)) except OSError as exc: raise FUSEError(exc.errno) return await self.getattr(inode)
async def mkdir(self, inode_p, name, mode, ctx): path = os.path.join(self._inode_to_path(inode_p), fsdecode(name)) print("path mkdir: ", path) start = "/sys/class/gpio/" print("start: ", start) relative_path = os.path.relpath(path, start) print("relative path ", relative_path) if path.startswith('/sys/class/gpio/'): print("si tratta di gpio") start = "/sys/class/gpio/" print("start: ", start) relative_path = os.path.relpath(path, start) print("relative path ", relative_path, "\n\n") config = configparser.ConfigParser() config.sections() config.read('example.ini') config.sections() listgpio = ["gpiochip0", "gpiochip504", "export", "unexport"] for count in range(1, 27): # print(count, "gpio" + str(count)) # print(config['gpiotest2']['gpio' + str(count)]) if config['gpiotest2']['gpio' + str(count)] == 'yes': print(count, "gpio" + str(count), "ok") # print("ok") listgpio.append("gpio" + str(count)) print("\n") print("la lista di gpio disponibili per test2 è: ", listgpio, "\n") if any(i in relative_path for i in listgpio): print(relative_path, "trovato\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) else: print( "questo gpio non puoi usarlo2, puoi utilizzare dall'8 al 15!" ) attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr elif path.startswith( '/sys/devices/platform/soc/3f200000.gpio/gpiochip0/gpio/'): print("si tratta di gpio indirettamente") start = "/sys/devices/platform/soc/3f200000.gpio/gpiochip0/" print("start: ", start) relative_path = os.path.relpath(path, start) print("relative path ", relative_path, "\n\n") config = configparser.ConfigParser() config.sections() config.read('example.ini') config.sections() listgpio = ["gpiochip0", "gpiochip504", "export", "unexport"] for count in range(1, 27): # print(count, "gpio" + str(count)) # print(config['gpiotest2']['gpio' + str(count)]) if config['gpiotest2']['gpio' + str(count)] == 'yes': print(count, "gpio" + str(count), "ok") # print("ok") listgpio.append("gpio" + str(count)) print("\n") print("la lista di gpio disponibili per test2 è: ", listgpio, "\n") if any(i in relative_path for i in listgpio): print(relative_path, "trovato\n") try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) else: print( "questo gpio non puoi usarlo2, puoi utilizzare dall'8 al 15!" ) attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr else: print("caso non analizzato\n\n") path = os.path.join(self._inode_to_path(inode_p), fsdecode(name)) try: os.mkdir(path, mode=(mode & ~ctx.umask)) os.chown(path, ctx.uid, ctx.gid) except OSError as exc: raise FUSEError(exc.errno) attr = self._getattr(path=path) self._add_path(attr.st_ino, path) return attr