Skip to content

Commit a16e5c9

Browse files
foxengwkozaczuk
authored andcommitted
virtio-fs: implement readdir
Signed-off-by: Fotis Xenakis <[email protected]> Message-Id: <AM0PR03MB6292510F3FC9656870453A98A67A0@AM0PR03MB6292.eurprd03.prod.outlook.com>
1 parent f9ae1e5 commit a16e5c9

File tree

1 file changed

+74
-2
lines changed

1 file changed

+74
-2
lines changed

fs/virtiofs/virtiofs_vnops.cc

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <sys/param.h>
1414
#include <sys/stat.h>
1515
#include <sys/types.h>
16+
#include <tuple>
1617

1718
#include <osv/contiguous_alloc.hh>
1819
#include <osv/debug.h>
@@ -24,6 +25,7 @@
2425
#include <osv/sched.hh>
2526
#include <osv/vnode.h>
2627

28+
#include "fuse_kernel.h"
2729
#include "virtiofs.hh"
2830
#include "virtiofs_dax.hh"
2931
#include "virtiofs_i.hh"
@@ -264,11 +266,81 @@ static int virtiofs_read(struct vnode* vnode, struct file* fp, struct uio* uio,
264266
ioflag, *drv, *uio);
265267
}
266268

269+
// Checks if @buf (with size @len) points to a valid fuse_dirent (with its name
270+
// not exceeding @name_max) and if so returns @buf. Otherwise, returns nullptr.
271+
static fuse_dirent* parse_fuse_dirent(void* buf, size_t len, size_t name_max)
272+
{
273+
if (len < FUSE_NAME_OFFSET) {
274+
return nullptr;
275+
}
276+
277+
auto* fdir = static_cast<struct fuse_dirent*>(buf);
278+
if (FUSE_DIRENT_SIZE(fdir) > len || fdir->namelen > name_max) {
279+
return nullptr;
280+
}
281+
282+
return fdir;
283+
}
284+
267285
static int virtiofs_readdir(struct vnode* vnode, struct file* fp,
268286
struct dirent* dir)
269287
{
270-
// TODO: Implement
271-
return EPERM;
288+
auto* inode = static_cast<virtiofs_inode*>(vnode->v_data);
289+
auto* file_data = static_cast<virtiofs_file_data*>(fp->f_data);
290+
auto* m_data = static_cast<virtiofs_mount_data*>(vnode->v_mount->m_data);
291+
auto* drv = m_data->drv;
292+
293+
// NOTE: The response consists of a buffer of size <= fuse_read_in.size.
294+
// This contains multiple (whole) fuse_dirent structs, each padded to 64-bit
295+
// boundary. We only parse one such struct at a time, for simplicity. The
296+
// fuse_dirent.name field is _not_ null-terminated, but our dirent.d_name
297+
// is, requiring caution.
298+
std::unique_ptr<fuse_read_in> in_args {new (std::nothrow) fuse_read_in()};
299+
constexpr size_t name_max = sizeof(dir->d_name) - 1; // account for '\0'
300+
// The size of a (padded) fuse_dirent with a name_max-long name
301+
size_t bufsize = FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + name_max);
302+
std::unique_ptr<void, std::function<void(void*)>> buf {
303+
memory::alloc_phys_contiguous_aligned(bufsize,
304+
alignof(std::max_align_t)), memory::free_phys_contiguous_aligned };
305+
if (!in_args || !buf) {
306+
return ENOMEM;
307+
}
308+
in_args->fh = file_data->file_handle;
309+
in_args->offset = fp->f_offset;
310+
in_args->size = bufsize;
311+
in_args->flags = fp->f_flags;
312+
313+
size_t len;
314+
int error;
315+
std::tie(len, error) = fuse_req_send_and_receive_reply(drv, FUSE_READDIR,
316+
inode->nodeid, in_args.get(), sizeof(*in_args), buf.get(), bufsize);
317+
if (error) {
318+
kprintf("[virtiofs] inode %lld, readdir failed\n", inode->nodeid);
319+
return error;
320+
}
321+
322+
if (len == 0) {
323+
return ENOENT;
324+
}
325+
auto* fdir = parse_fuse_dirent(buf.get(), len, name_max);
326+
if (!fdir) {
327+
kprintf("[virtiofs] inode %lld, dirent parsing failed\n",
328+
inode->nodeid);
329+
return EIO;
330+
}
331+
332+
dir->d_ino = fdir->ino;
333+
dir->d_off = fdir->off;
334+
dir->d_type = fdir->type;
335+
// Copy fdir->name (not null-terminated) to dir->d_name (null-terminated)
336+
memcpy(dir->d_name, fdir->name, fdir->namelen);
337+
dir->d_name[fdir->namelen] = '\0';
338+
339+
fp->f_offset = fdir->off;
340+
341+
virtiofs_debug("inode %lld, read dir entry %s\n", inode->nodeid,
342+
dir->d_name);
343+
return 0;
272344
}
273345

274346
static int virtiofs_getattr(struct vnode* vnode, struct vattr* attr)

0 commit comments

Comments
 (0)