|
13 | 13 | #include <sys/param.h> |
14 | 14 | #include <sys/stat.h> |
15 | 15 | #include <sys/types.h> |
| 16 | +#include <tuple> |
16 | 17 |
|
17 | 18 | #include <osv/contiguous_alloc.hh> |
18 | 19 | #include <osv/debug.h> |
|
24 | 25 | #include <osv/sched.hh> |
25 | 26 | #include <osv/vnode.h> |
26 | 27 |
|
| 28 | +#include "fuse_kernel.h" |
27 | 29 | #include "virtiofs.hh" |
28 | 30 | #include "virtiofs_dax.hh" |
29 | 31 | #include "virtiofs_i.hh" |
@@ -264,11 +266,81 @@ static int virtiofs_read(struct vnode* vnode, struct file* fp, struct uio* uio, |
264 | 266 | ioflag, *drv, *uio); |
265 | 267 | } |
266 | 268 |
|
| 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 | + |
267 | 285 | static int virtiofs_readdir(struct vnode* vnode, struct file* fp, |
268 | 286 | struct dirent* dir) |
269 | 287 | { |
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; |
272 | 344 | } |
273 | 345 |
|
274 | 346 | static int virtiofs_getattr(struct vnode* vnode, struct vattr* attr) |
|
0 commit comments