def from_cffi(cls: t.Type[T], struct) -> T: return cls( nodeid=struct.nodeid, generation=struct.generation, entry_valid=Timespec(struct.entry_valid, struct.entry_valid_nsec), attr_valid=Timespec(struct.attr_valid, struct.attr_valid_nsec), attr=FuseAttr.from_cffi(struct.attr), )
def from_cffi(cls: t.Type[T], struct) -> T: return cls( ino=struct.ino, size=struct.size, blocks=struct.blocks, atime=Timespec(sec=struct.atime, nsec=struct.atimensec), mtime=Timespec(sec=struct.mtime, nsec=struct.mtimensec), ctime=Timespec(sec=struct.ctime, nsec=struct.ctimensec), mode=struct.mode, nlink=struct.nlink, uid=struct.uid, gid=struct.gid, rdev=struct.rdev, blksize=struct.blksize, )
def from_bytes(cls: t.Type[T], data: bytes) -> T: struct = ffi.cast('struct stat*', ffi.from_buffer(data)) return cls( dev=struct.st_dev, ino=struct.st_ino, mode=struct.st_mode, nlink=struct.st_nlink, uid=struct.st_uid, gid=struct.st_gid, rdev=struct.st_rdev, size=struct.st_size, blksize=struct.st_blksize, blocks=struct.st_blocks, atime=Timespec.from_cffi(struct.st_atim), mtime=Timespec.from_cffi(struct.st_mtim), ctime=Timespec.from_cffi(struct.st_ctim), )
def test_stat(self) -> None: initial = Stat( dev=0, ino=0, mode=TypeMode(S_IF.REG, Mode(0o777)), nlink=0, uid=0, gid=0, rdev=0, size=0, blksize=0, blocks=0, atime=Timespec(sec=0, nsec=0), mtime=Timespec(sec=0, nsec=0), ctime=Timespec(sec=0, nsec=0), ) output = Stat.from_bytes(initial.to_bytes()) self.assertEqual(initial, output)
async def test_symlink(self) -> None: @self.nursery.start_soon async def open() -> None: async with (await self.child.task.open( await self.process.ptr(self.path / "foo"), O.RDONLY)) as foo: data, _ = await foo.read(await self.process.malloc(bytes, 4096)) await self.fuse.write((await self.assertRead(FuseGetattrOp)).respond( FuseAttrOut(attr_valid=Timespec(10000, 0), attr=FuseAttr(ino=1, size=0, blocks=1, atime=Timespec(0, 0), mtime=Timespec(0, 0), ctime=Timespec(0, 0), mode=TypeMode(S_IF.DIR, Mode(0o777)), nlink=1, uid=self.fuse.uid, gid=self.fuse.gid, rdev=0, blksize=4096)))) await self.fuse.write((await self.assertRead(FuseLookupOp)).respond( FuseEntryOut( nodeid=2, generation=1, entry_valid=Timespec(10000, 0), attr_valid=Timespec(10000, 0), # the size needs to be consistent with the data we'll actually send back on read # the kernel, I guess, handles delivering an eof; # we can just claim a larger size then send back less data attr=FuseAttr(ino=999, size=4096, blocks=1, atime=Timespec(0, 0), mtime=Timespec(0, 0), ctime=Timespec(0, 0), mode=TypeMode(S_IF.LNK, Mode(0o777)), nlink=1, uid=self.fuse.uid, gid=self.fuse.gid, rdev=0, blksize=4096)))) await self.fuse.write( (await self.assertRead(FuseReadlinkOp)).respond("/bin/sh"))
async def test_basic(self) -> None: data_read_from_fuse = b"this is some data read from fuse" @self.nursery.start_soon async def open() -> None: foo = await self.child.task.open( await self.child.ptr(self.path / "foo"), O.RDONLY) data, _ = await foo.read(await self.child.malloc(bytes, 4096)) self.assertEqual(data_read_from_fuse, await data.read()) data, _ = await foo.read(await self.child.malloc(bytes, 4096)) self.assertEqual(data.size(), 0) await foo.close() root = await self.child.task.open(await self.child.ptr(self.path), O.RDONLY) valid, rest = await root.getdents(await self.child.ram.malloc( DirentList, 4096)) await root.close() root_getattr = await self.assertRead(FuseGetattrOp) if root_getattr.hdr.nodeid != 1: raise Exception("expected to get getattr for root node 1, not", root_getattr.hdr.nodeid) await self.fuse.write( root_getattr.respond( FuseAttrOut(attr_valid=Timespec(10000, 0), attr=FuseAttr(ino=1, size=0, blocks=1, atime=Timespec(0, 0), mtime=Timespec(0, 0), ctime=Timespec(0, 0), mode=TypeMode(S_IF.DIR, Mode(0o777)), nlink=1, uid=self.fuse.uid, gid=self.fuse.gid, rdev=0, blksize=4096)))) await self.fuse.write((await self.assertRead(FuseLookupOp)).respond( FuseEntryOut( nodeid=2, generation=1, entry_valid=Timespec(10000, 0), attr_valid=Timespec(10000, 0), # the size needs to be consistent with the data we'll actually send back on read # the kernel, I guess, handles delivering an eof attr=FuseAttr(ino=999, size=len(data_read_from_fuse), blocks=1, atime=Timespec(0, 0), mtime=Timespec(0, 0), ctime=Timespec(0, 0), mode=TypeMode(S_IF.REG, Mode(0o777)), nlink=1, uid=self.fuse.uid, gid=self.fuse.gid, rdev=0, blksize=4096)))) fh = 42 await self.fuse.write((await self.assertRead(FuseOpenOp)).respond( FuseOpenOut(fh=fh, open_flags=FOPEN.NONE))) await self.fuse.write( (await self.assertRead(FuseReadOp)).respond(data_read_from_fuse)) # close file await self.fuse.write((await self.assertRead(FuseFlushOp)).respond()) await self.fuse.write((await self.assertRead(FuseReleaseOp)).respond()) # open root and getdents root_fh = 137 await self.fuse.write((await self.assertRead(FuseOpendirOp)).respond( FuseOpenOut(fh=root_fh, open_flags=FOPEN.NONE))) foobar_ino = 432 await self.fuse.write(( await self.assertRead(FuseReaddirplusOp) ).respond([ FuseDirentplus( FuseEntryOut( nodeid=foobar_ino, generation=1, entry_valid=Timespec(10000, 0), attr_valid=Timespec(10000, 0), # the size needs to be consistent with the data we'll actually send back on read # the kernel, I guess, handles delivering an eof attr=FuseAttr(ino=foobar_ino, size=len(data_read_from_fuse), blocks=1, atime=Timespec(0, 0), mtime=Timespec(0, 0), ctime=Timespec(0, 0), mode=TypeMode(S_IF.REG, Mode(0o777)), nlink=1, uid=self.fuse.uid, gid=self.fuse.gid, rdev=0, blksize=4096)), FuseDirent( ino=foobar_ino, off=1, type=DT.REG, name="foobar", ), ), ])) # close file await self.fuse.write((await self.assertRead(FuseReleasedirOp)).respond())
async def open() -> None: "Every filename in this filesystem is a symlink to stub_path" while True: try: [op] = await fuse.read() except OSError as e: # sure wish Python had a builtin ENODEV exception, that would come in handy if e.errno == errno.ENODEV: # the filesystem has been unmounted, just return cleanly return else: raise sym_ino = 2 fh = 42 if isinstance(op, FuseGetattrOp): await fuse.write( op.respond( FuseAttrOut(attr_valid=Timespec(10000, 0), attr=FuseAttr( ino=1, size=0, blocks=1, atime=Timespec(0, 0), mtime=Timespec(0, 0), ctime=Timespec(0, 0), mode=TypeMode( S_IF.DIR, Mode(0o777)), nlink=1, uid=fuse.uid, gid=fuse.gid, rdev=0, blksize=4096)))) elif isinstance(op, FuseLookupOp): await fuse.write( op.respond( FuseEntryOut( nodeid=sym_ino, generation=1, entry_valid=Timespec(10000, 0), attr_valid=Timespec(10000, 0), # the size needs to be consistent with the data we'll actually send back on read # the kernel, I guess, handles delivering an eof attr=FuseAttr(ino=999, size=16, blocks=1, atime=Timespec(0, 0), mtime=Timespec(0, 0), ctime=Timespec(0, 0), mode=TypeMode( S_IF.LNK, Mode(0o777)), nlink=1, uid=fuse.uid, gid=fuse.gid, rdev=0, blksize=4096)))) elif isinstance(op, FuseOpenOp): await fuse.write( op.respond( FuseOpenOut(fh=fh, open_flags=FOPEN.NONE))) elif isinstance(op, FuseGetxattrOp): await fuse.write(op.error(-errno.ENODATA)) elif isinstance(op, FuseReadOp): await fuse.write(op.respond(bytes(16))) elif isinstance(op, FuseReleaseOp): await fuse.write(op.respond()) elif isinstance(op, FuseReadlinkOp): await fuse.write(op.respond(stub_path)) else: print("unhandled op", op) raise Exception("unhandled op", op)
def from_header(cls: t.Type[T], data: bytes) -> T: struct = ffi.cast('struct fuse_attr_out*', ffi.from_buffer(data)) return cls( attr_valid=Timespec(struct.attr_valid, struct.attr_valid_nsec), attr=FuseAttr.from_cffi(struct.attr), )