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)