fs: add fuse primitives

This commit adds fuse structures, requests and functions to fill them

Signed-off-by: Jakub Michalski <jmichalski@antmicro.com>
This commit is contained in:
Jakub Michalski
2025-04-09 10:20:09 +02:00
committed by Anas Nashif
parent 7e9d994aa3
commit b28483a3fe
8 changed files with 931 additions and 0 deletions

View File

@@ -108,6 +108,15 @@ Python Devicetree library test files
* Various yaml files under ``scripts/dts/python-devicetree/tests``
FUSE Interface Definition Header File
--------------------------------------
* *Licensing:* `BSD-2-clause`_
* *Impact:* This header is used in Zephyr build only if :kconfig:option:`CONFIG_FUSE_CLIENT` is enabled.
* *Files*:
* :zephyr_file:`subsys/fs/fuse_client/fuse_abi.h`
.. _Apache 2.0 License:
https://github.com/zephyrproject-rtos/zephyr/blob/main/LICENSE
@@ -120,6 +129,9 @@ Python Devicetree library test files
.. _BSD-3-clause:
https://opensource.org/license/bsd-3-clause
.. _BSD-2-clause:
https://opensource.org/license/bsd-2-clause
.. _Coccinelle:
https://coccinelle.gitlabpages.inria.fr/website/

View File

@@ -17,12 +17,14 @@ if(CONFIG_FILE_SYSTEM_LIB_LINK)
endif()
add_subdirectory_ifdef(CONFIG_FILE_SYSTEM_EXT2 ext2)
add_subdirectory_ifdef(CONFIG_FUSE_CLIENT fuse_client)
zephyr_library_link_libraries(FS)
target_link_libraries_ifdef(CONFIG_FAT_FILESYSTEM_ELM FS INTERFACE ELMFAT)
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_LITTLEFS FS INTERFACE LITTLEFS)
target_link_libraries_ifdef(CONFIG_FILE_SYSTEM_EXT2 FS INTERFACE EXT2)
target_link_libraries_ifdef(CONFIG_FUSE_CLIENT FS INTERFACE FUSE_CLIENT)
endif()
add_subdirectory_ifdef(CONFIG_FCB ./fcb)

View File

@@ -119,6 +119,7 @@ source "subsys/logging/Kconfig.template.log_config"
rsource "Kconfig.fatfs"
rsource "Kconfig.littlefs"
rsource "ext2/Kconfig"
rsource "fuse_client/Kconfig"
endif # FILE_SYSTEM_LIB_LINK

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2025 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: Apache-2.0
# This library provides a set of functions for creating FUSE structures
add_library(FUSE_CLIENT INTERFACE)
target_include_directories(FUSE_CLIENT INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
zephyr_library()
zephyr_library_sources(
fuse_client.c
)
zephyr_library_link_libraries(FUSE_CLIENT)

View File

@@ -0,0 +1,32 @@
# Copyright (c) 2025 Antmicro <www.antmicro.com>
# SPDX-License-Identifier: Apache-2.0
config FUSE_CLIENT
bool "FUSE client-side primitives support"
help
Enable FUSE client-side primitives support.
config FUSE_CLIENT_UID_VALUE
int "FUSE user ID"
default 0
help
Each FUSE request contains user ID, this config allows setting
that value. The result is as if user with given UID accessed the file/resource.
config FUSE_CLIENT_GID_VALUE
int "FUSE group ID"
default 0
help
Each FUSE request contains group ID, this config allows setting
that value. The result is as if user with given GID accessed the file/resource.
config FUSE_CLIENT_PID_VALUE
int "FUSE process ID"
default 0
help
Each FUSE request contains process ID, this config allows setting
that value. The result is as if process with given PID accessed the file/resource.
module = FUSE_CLIENT
module-str = fuse
source "subsys/logging/Kconfig.template.log_config"

View File

@@ -0,0 +1,273 @@
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) */
/*
* This file is based on include/uapi/linux/fuse.h from Linux, and is used
* under the BSD-2-Clause license, as per the dual-license option
*/
/*
* This file defines the kernel interface of FUSE
* This -- and only this -- header file may also be distributed under
* the terms of the BSD Licence as follows:
*
* Copyright (C) 2001-2007 Miklos Szeredi. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#ifndef ZEPHYR_SUBSYS_FS_FUSE_ABI_H_
#define ZEPHYR_SUBSYS_FS_FUSE_ABI_H_
#include <stdint.h>
#define FUSE_MAJOR_VERSION 7
#define FUSE_MINOR_VERSION 31
#define FUSE_LOOKUP 1
#define FUSE_FORGET 2
#define FUSE_SETATTR 4
#define FUSE_MKDIR 9
#define FUSE_UNLINK 10
#define FUSE_RMDIR 11
#define FUSE_RENAME 12
#define FUSE_OPEN 14
#define FUSE_READ 15
#define FUSE_WRITE 16
#define FUSE_STATFS 17
#define FUSE_RELEASE 18
#define FUSE_FSYNC 20
#define FUSE_INIT 26
#define FUSE_OPENDIR 27
#define FUSE_READDIR 28
#define FUSE_RELEASEDIR 29
#define FUSE_CREATE 35
#define FUSE_DESTROY 38
#define FUSE_LSEEK 46
#define FUSE_ROOT_INODE 1
struct fuse_in_header {
uint32_t len;
uint32_t opcode;
uint64_t unique;
uint64_t nodeid;
uint32_t uid;
uint32_t gid;
uint32_t pid;
uint16_t total_extlen;
uint16_t padding;
};
struct fuse_out_header {
uint32_t len;
int32_t error;
uint64_t unique;
};
struct fuse_init_in {
uint32_t major;
uint32_t minor;
uint32_t max_readahead;
uint32_t flags;
uint32_t flags2;
uint32_t unused[11];
};
struct fuse_init_out {
uint32_t major;
uint32_t minor;
uint32_t max_readahead;
uint32_t flags;
uint16_t max_background;
uint16_t congestion_threshold;
uint32_t max_write;
uint32_t time_gran;
uint16_t max_pages;
uint16_t map_alignment;
uint32_t flags2;
uint32_t max_stack_depth;
uint32_t unused[6];
};
struct fuse_open_in {
uint32_t flags;
uint32_t open_flags;
};
struct fuse_open_out {
uint64_t fh;
uint32_t open_flags;
int32_t backing_id;
};
struct fuse_attr {
uint64_t ino;
uint64_t size;
uint64_t blocks;
uint64_t atime;
uint64_t mtime;
uint64_t ctime;
uint32_t atimensec;
uint32_t mtimensec;
uint32_t ctimensec;
uint32_t mode;
uint32_t nlink;
uint32_t uid;
uint32_t gid;
uint32_t rdev;
uint32_t blksize;
uint32_t flags;
};
struct fuse_entry_out {
uint64_t nodeid;
uint64_t generation;
uint64_t entry_valid;
uint64_t attr_valid;
uint32_t entry_valid_nsec;
uint32_t attr_valid_nsec;
struct fuse_attr attr;
};
struct fuse_read_in {
uint64_t fh;
uint64_t offset;
uint32_t size;
uint32_t read_flags;
uint64_t lock_owner;
uint32_t flags;
uint32_t padding;
};
struct fuse_release_in {
uint64_t fh;
uint32_t flags;
uint32_t release_flags;
uint64_t lock_owner;
};
struct fuse_create_in {
uint32_t flags;
uint32_t mode;
uint32_t umask;
uint32_t open_flags;
};
struct fuse_create_out {
struct fuse_entry_out entry_out;
struct fuse_open_out open_out;
};
struct fuse_write_in {
uint64_t fh;
uint64_t offset;
uint32_t size;
uint32_t write_flags;
uint64_t lock_owner;
uint32_t flags;
uint32_t padding;
};
struct fuse_write_out {
uint32_t size;
uint32_t padding;
};
struct fuse_lseek_in {
uint64_t fh;
uint64_t offset;
uint32_t whence;
uint32_t padding;
};
struct fuse_lseek_out {
uint64_t offset;
};
/* mask used to set file size, used in fuse_setattr_in::valid */
#define FATTR_SIZE (1 << 3)
struct fuse_setattr_in {
uint32_t valid;
uint32_t padding;
uint64_t fh;
uint64_t size;
uint64_t lock_owner;
uint64_t atime;
uint64_t mtime;
uint64_t ctime;
uint32_t atimensec;
uint32_t mtimensec;
uint32_t ctimensec;
uint32_t mode;
uint32_t unused4;
uint32_t uid;
uint32_t gid;
uint32_t unused5;
};
struct fuse_attr_out {
uint64_t attr_valid;
uint32_t attr_valid_nsec;
uint32_t dummy;
struct fuse_attr attr;
};
struct fuse_fsync_in {
uint64_t fh;
uint32_t fsync_flags;
uint32_t padding;
};
struct fuse_mkdir_in {
uint32_t mode;
uint32_t umask;
};
struct fuse_rename_in {
uint64_t newdir;
};
struct fuse_kstatfs {
uint64_t blocks;
uint64_t bfree;
uint64_t bavail;
uint64_t files;
uint64_t ffree;
uint32_t bsize;
uint32_t namelen;
uint32_t frsize;
uint32_t padding;
uint32_t spare[6];
};
struct fuse_dirent {
uint64_t ino;
uint64_t off;
uint32_t namelen;
uint32_t type;
char name[];
};
struct fuse_forget_in {
uint64_t nlookup;
};
#endif /* ZEPHYR_SUBSYS_FS_FUSE_ABI_H_ */

View File

@@ -0,0 +1,429 @@
/*
* Copyright (c) 2025 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <fuse_client.h>
#include <zephyr/logging/log.h>
#include <inttypes.h>
LOG_MODULE_REGISTER(fuse, CONFIG_FUSE_CLIENT_LOG_LEVEL);
static uint64_t unique = 1; /* with unique==0 older virtiofsd asserts, so we are starting from 1 */
static uint64_t fuse_get_unique(void)
{
return unique++;
}
void fuse_fill_header(struct fuse_in_header *hdr, uint32_t len, uint32_t opcode, uint64_t nodeid)
{
hdr->len = len;
hdr->opcode = opcode;
hdr->unique = fuse_get_unique();
hdr->nodeid = nodeid;
hdr->uid = CONFIG_FUSE_CLIENT_UID_VALUE;
hdr->gid = CONFIG_FUSE_CLIENT_GID_VALUE;
hdr->pid = CONFIG_FUSE_CLIENT_PID_VALUE;
hdr->total_extlen = 0;
}
void fuse_create_init_req(struct fuse_init_req *req)
{
fuse_fill_header(
&req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_init_in),
FUSE_INIT, 0
);
req->init_in.major = FUSE_MAJOR_VERSION;
req->init_in.minor = FUSE_MINOR_VERSION;
req->init_in.max_readahead = 0;
req->init_in.flags = 0;
req->init_in.flags2 = 0;
}
void fuse_create_open_req(
struct fuse_open_req *req, uint64_t inode, uint32_t flags, enum fuse_object_type type)
{
fuse_fill_header(
&req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_open_in),
type == FUSE_DIR ? FUSE_OPENDIR : FUSE_OPEN, inode
);
req->open_in.flags = flags;
req->open_in.open_flags = 0;
}
void fuse_create_lookup_req(struct fuse_lookup_req *req, uint64_t inode, uint32_t fname_len)
{
fuse_fill_header(
&req->in_header, sizeof(struct fuse_in_header) + fname_len, FUSE_LOOKUP,
inode
);
}
void fuse_create_read_req(
struct fuse_read_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t size,
enum fuse_object_type type)
{
fuse_fill_header(
&req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_read_in),
type == FUSE_FILE ? FUSE_READ : FUSE_READDIR, inode
);
req->read_in.fh = fh;
req->read_in.offset = offset;
req->read_in.size = size;
req->read_in.read_flags = 0;
req->read_in.lock_owner = 0;
req->read_in.flags = 0;
}
void fuse_create_release_req(struct fuse_release_req *req, uint64_t inode, uint64_t fh,
enum fuse_object_type type)
{
fuse_fill_header(
&req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_release_in),
type == FUSE_DIR ? FUSE_RELEASEDIR : FUSE_RELEASE, inode
);
req->release_in.fh = fh;
req->release_in.flags = 0;
req->release_in.release_flags = 0;
req->release_in.lock_owner = 0;
}
void fuse_create_destroy_req(struct fuse_destroy_req *req)
{
fuse_fill_header(&req->in_header, sizeof(struct fuse_in_header), FUSE_DESTROY, 0);
}
void fuse_create_create_req(
struct fuse_create_req *req, uint64_t inode, uint32_t fname_len, uint32_t flags,
uint32_t mode)
{
fuse_fill_header(
&req->in_header, sizeof(req->in_header) + sizeof(req->create_in) + fname_len,
FUSE_CREATE, inode
);
req->create_in.flags = flags;
req->create_in.mode = mode;
req->create_in.open_flags = 0;
req->create_in.umask = 0;
}
void fuse_create_write_req(
struct fuse_write_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t size)
{
fuse_fill_header(
&req->in_header, sizeof(req->in_header) + sizeof(req->write_in) + size, FUSE_WRITE,
inode
);
req->write_in.fh = fh;
req->write_in.offset = offset;
req->write_in.size = size;
req->write_in.write_flags = 0;
req->write_in.lock_owner = 0;
req->write_in.flags = 0;
}
void fuse_create_lseek_req(
struct fuse_lseek_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t whence)
{
fuse_fill_header(
&req->in_header, sizeof(req->in_header) + sizeof(req->lseek_in), FUSE_LSEEK, inode
);
req->lseek_in.fh = fh;
req->lseek_in.offset = offset;
req->lseek_in.whence = whence;
}
void fuse_create_setattr_req(struct fuse_setattr_req *req, uint64_t inode)
{
fuse_fill_header(
&req->in_header, sizeof(req->in_header) + sizeof(struct fuse_setattr_in),
FUSE_SETATTR, inode
);
}
void fuse_create_fsync_req(struct fuse_fsync_req *req, uint64_t inode, uint64_t fh)
{
fuse_fill_header(
&req->in_header, sizeof(req->in_header) + sizeof(req->fsync_in), FUSE_FSYNC,
inode
);
req->fsync_in.fh = fh;
req->fsync_in.fsync_flags = 0;
}
void fuse_create_mkdir_req(
struct fuse_mkdir_req *req, uint64_t inode, uint32_t dirname_len, uint32_t mode)
{
fuse_fill_header(
&req->in_header, sizeof(req->in_header) + sizeof(req->mkdir_in) + dirname_len,
FUSE_MKDIR, inode
);
req->mkdir_in.mode = mode;
req->mkdir_in.umask = 0;
}
void fuse_create_unlink_req(
struct fuse_unlink_req *req, uint32_t fname_len, enum fuse_object_type type)
{
fuse_fill_header(
&req->in_header, sizeof(req->in_header) + fname_len,
type == FUSE_DIR ? FUSE_RMDIR : FUSE_UNLINK, FUSE_ROOT_INODE
);
}
void fuse_create_rename_req(
struct fuse_rename_req *req, uint64_t old_dir_nodeid, uint32_t old_len,
uint64_t new_dir_nodeid, uint32_t new_len)
{
fuse_fill_header(
&req->in_header,
sizeof(req->in_header) + sizeof(req->rename_in) + old_len + new_len,
FUSE_RENAME, old_dir_nodeid
);
req->rename_in.newdir = new_dir_nodeid;
}
const char *fuse_opcode_to_string(uint32_t opcode)
{
switch (opcode) {
case FUSE_LOOKUP:
return "FUSE_LOOKUP";
case FUSE_FORGET:
return "FUSE_FORGET";
case FUSE_SETATTR:
return "FUSE_SETATTR";
case FUSE_MKDIR:
return "FUSE_MKDIR";
case FUSE_UNLINK:
return "FUSE_UNLINK";
case FUSE_RMDIR:
return "FUSE_RMDIR";
case FUSE_RENAME:
return "FUSE_RENAME";
case FUSE_OPEN:
return "FUSE_OPEN";
case FUSE_READ:
return "FUSE_READ";
case FUSE_WRITE:
return "FUSE_WRITE";
case FUSE_STATFS:
return "FUSE_STATFS";
case FUSE_RELEASE:
return "FUSE_RELEASE";
case FUSE_FSYNC:
return "FUSE_FSYNC";
case FUSE_INIT:
return "FUSE_INIT";
case FUSE_OPENDIR:
return "FUSE_OPENDIR";
case FUSE_READDIR:
return "FUSE_READDIR";
case FUSE_RELEASEDIR:
return "FUSE_RELEASEDIR";
case FUSE_CREATE:
return "FUSE_CREATE";
case FUSE_DESTROY:
return "FUSE_DESTROY";
case FUSE_LSEEK:
return "FUSE_LSEEK";
default:
return "";
}
}
void fuse_dump_init_req_out(struct fuse_init_req *req)
{
LOG_INF(
"FUSE_INIT response:\n"
"major=%" PRIu32 "\n"
"minor=%" PRIu32 "\n"
"max_readahead=%" PRIu32 "\n"
"flags=%" PRIu32 "\n"
"max_background=%" PRIu16 "\n"
"congestion_threshold=%" PRIu16 "\n"
"max_write=%" PRIu32 "\n"
"time_gran=%" PRIu32 "\n"
"max_pages=%" PRIu16 "\n"
"map_alignment=%" PRIu16 "\n"
"flags2=%" PRIu32 "\n"
"max_stack_depth=%" PRIu32,
req->init_out.major,
req->init_out.minor,
req->init_out.max_readahead,
req->init_out.flags,
req->init_out.max_background,
req->init_out.congestion_threshold,
req->init_out.max_write,
req->init_out.time_gran,
req->init_out.max_pages,
req->init_out.map_alignment,
req->init_out.flags2,
req->init_out.max_stack_depth
);
}
void fuse_dump_entry_out(struct fuse_entry_out *eo)
{
LOG_INF(
"FUSE LOOKUP response:\n"
"nodeid=%" PRIu64 "\n"
"generation=%" PRIu64 "\n"
"entry_valid=%" PRIu64 "\n"
"attr_valid=%" PRIu64 "\n"
"entry_valid_nsec=%" PRIu32 "\n"
"attr_valid_nsec=%" PRIu32 "\n"
"attr.ino=%" PRIu64 "\n"
"attr.size=%" PRIu64 "\n"
"attr.blocks=%" PRIu64 "\n"
"attr.atime=%" PRIu64 "\n"
"attr.mtime=%" PRIu64 "\n"
"attr.ctime=%" PRIu64 "\n"
"attr.atimensec=%" PRIu32 "\n"
"attr.mtimensec=%" PRIu32 "\n"
"attr.ctimensec=%" PRIu32 "\n"
"attr.mode=%" PRIu32 "\n"
"attr.nlink=%" PRIu32 "\n"
"attr.uid=%" PRIu32 "\n"
"attr.gid=%" PRIu32 "\n"
"attr.rdev=%" PRIu32 "\n"
"attr.blksize=%" PRIu32 "\n"
"attr.flags=%" PRIu32,
eo->nodeid,
eo->generation,
eo->entry_valid,
eo->attr_valid,
eo->entry_valid_nsec,
eo->attr_valid_nsec,
eo->attr.ino,
eo->attr.size,
eo->attr.blocks,
eo->attr.atime,
eo->attr.mtime,
eo->attr.ctime,
eo->attr.atimensec,
eo->attr.mtimensec,
eo->attr.ctimensec,
eo->attr.mode,
eo->attr.nlink,
eo->attr.uid,
eo->attr.gid,
eo->attr.rdev,
eo->attr.blksize,
eo->attr.flags
);
}
void fuse_dump_open_req_out(struct fuse_open_req *req)
{
LOG_INF(
"FUSE OPEN response:\n"
"fh=%" PRIu64 "\n"
"open_flags=%" PRIu32 "\n"
"backing_id=%" PRIi32,
req->open_out.fh,
req->open_out.open_flags,
req->open_out.backing_id
);
}
void fuse_dump_create_req_out(struct fuse_create_out *req)
{
LOG_INF(
"FUSE CREATE response:\n"
"nodeid=%" PRIu64 "\n"
"generation=%" PRIu64 "\n"
"entry_valid=%" PRIu64 "\n"
"attr_valid=%" PRIu64 "\n"
"entry_valid_nsec=%" PRIu32 "\n"
"attr_valid_nsec=%" PRIu32 "\n"
"attr.ino=%" PRIu64 "\n"
"attr.size=%" PRIu64 "\n"
"attr.blocks=%" PRIu64 "\n"
"attr.atime=%" PRIu64 "\n"
"attr.mtime=%" PRIu64 "\n"
"attr.ctime=%" PRIu64 "\n"
"attr.atimensec=%" PRIu32 "\n"
"attr.mtimensec=%" PRIu32 "\n"
"attr.ctimensec=%" PRIu32 "\n"
"attr.mode=%" PRIu32 "\n"
"attr.nlink=%" PRIu32 "\n"
"attr.uid=%" PRIu32 "\n"
"attr.gid=%" PRIu32 "\n"
"attr.rdev=%" PRIu32 "\n"
"attr.blksize=%" PRIu32 "\n"
"attr.flags=%" PRIu32 "\n"
"fh=%" PRIu64 "\n"
"open_flags=%" PRIu32 "\n"
"backing_id=%" PRIi32,
req->entry_out.nodeid,
req->entry_out.generation,
req->entry_out.entry_valid,
req->entry_out.attr_valid,
req->entry_out.entry_valid_nsec,
req->entry_out.attr_valid_nsec,
req->entry_out.attr.ino,
req->entry_out.attr.size,
req->entry_out.attr.blocks,
req->entry_out.attr.atime,
req->entry_out.attr.mtime,
req->entry_out.attr.ctime,
req->entry_out.attr.atimensec,
req->entry_out.attr.mtimensec,
req->entry_out.attr.ctimensec,
req->entry_out.attr.mode,
req->entry_out.attr.nlink,
req->entry_out.attr.uid,
req->entry_out.attr.gid,
req->entry_out.attr.rdev,
req->entry_out.attr.blksize,
req->entry_out.attr.flags,
req->open_out.fh,
req->open_out.open_flags,
req->open_out.backing_id
);
}
void fuse_dump_write_out(struct fuse_write_out *wo)
{
LOG_INF("FUSE WRITE response:\nsize=%" PRIu32, wo->size);
}
void fuse_dump_lseek_out(struct fuse_lseek_out *lo)
{
LOG_INF("FUSE WRITE response:\noffset=%" PRIu64, lo->offset);
}
void fuse_dump_attr_out(struct fuse_attr_out *ao)
{
LOG_INF(
"attr_valid=%" PRIu64 "\n"
"attr_valid_nsec=%" PRIu32,
ao->attr_valid,
ao->attr_valid_nsec
);
}
void fuse_dump_kstafs(struct fuse_kstatfs *ks)
{
LOG_INF(
"blocks=%" PRIu64 "\n"
"bfree=%" PRIu64 "\n"
"bavail=%" PRIu64 "\n"
"files=%" PRIu64 "\n"
"ffree=%" PRIu64 "\n"
"bsize=%" PRIu32 "\n"
"namelen=%" PRIu32 "\n"
"frsize=%" PRIu32,
ks->blocks,
ks->bfree,
ks->bavail,
ks->files,
ks->ffree,
ks->bsize,
ks->namelen,
ks->frsize
);
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright (c) 2025 Antmicro <www.antmicro.com>
*
* SPDX-License-Identifier: Apache-2.0
*/
/*
* This header provides helper functions to pack FUSE client requests. Note the client is the
* side which initiates these requests, and that in a typical FUSE usage the client would be
* the Linux kernel. While in Zephyr's case this is to enable functionality like the embedded
* virtiofs client connecting to a virtiofsd daemon running in the host.
*/
#ifndef ZEPHYR_SUBSYS_FS_FUSE_CLIENT_H_
#define ZEPHYR_SUBSYS_FS_FUSE_CLIENT_H_
#include <stdint.h>
#include "fuse_abi.h"
/*
* requests are put into structs to leverage the fact that they are contiguous in memory and can
* be passed to virtqueue as smaller amount of buffers, e.g. in_header + init_in can be sent as
* a single buffer containing both of them instead of two separate buffers
*/
struct fuse_init_req {
struct fuse_in_header in_header;
struct fuse_init_in init_in;
struct fuse_out_header out_header;
struct fuse_init_out init_out;
};
struct fuse_open_req {
struct fuse_in_header in_header;
struct fuse_open_in open_in;
struct fuse_out_header out_header;
struct fuse_open_out open_out;
};
struct fuse_create_req {
struct fuse_in_header in_header;
struct fuse_create_in create_in;
struct fuse_out_header out_header;
struct fuse_create_out create_out;
};
struct fuse_write_req {
struct fuse_in_header in_header;
struct fuse_write_in write_in;
struct fuse_out_header out_header;
struct fuse_write_out write_out;
};
struct fuse_lseek_req {
struct fuse_in_header in_header;
struct fuse_lseek_in lseek_in;
struct fuse_out_header out_header;
struct fuse_lseek_out lseek_out;
};
struct fuse_mkdir_req {
struct fuse_in_header in_header;
struct fuse_mkdir_in mkdir_in;
struct fuse_out_header out_header;
struct fuse_entry_out entry_out;
};
struct fuse_lookup_req {
struct fuse_in_header in_header;
struct fuse_out_header out_header;
struct fuse_entry_out entry_out;
};
struct fuse_read_req {
struct fuse_in_header in_header;
struct fuse_read_in read_in;
struct fuse_out_header out_header;
};
struct fuse_release_req {
struct fuse_in_header in_header;
struct fuse_release_in release_in;
struct fuse_out_header out_header;
};
struct fuse_destroy_req {
struct fuse_in_header in_header;
struct fuse_out_header out_header;
};
struct fuse_setattr_req {
struct fuse_in_header in_header;
struct fuse_out_header out_header;
};
struct fuse_fsync_req {
struct fuse_in_header in_header;
struct fuse_fsync_in fsync_in;
struct fuse_out_header out_header;
};
struct fuse_unlink_req {
struct fuse_in_header in_header;
struct fuse_out_header out_header;
};
struct fuse_rename_req {
struct fuse_in_header in_header;
struct fuse_rename_in rename_in;
struct fuse_out_header out_header;
};
struct fuse_kstatfs_req {
struct fuse_in_header in_header;
struct fuse_out_header out_header;
struct fuse_kstatfs kstatfs_out;
};
struct fuse_forget_req {
struct fuse_in_header in_header;
struct fuse_forget_in forget_in;
};
enum fuse_object_type {
FUSE_FILE,
FUSE_DIR
};
void fuse_fill_header(struct fuse_in_header *hdr, uint32_t len, uint32_t opcode, uint64_t nodeid);
void fuse_create_init_req(struct fuse_init_req *req);
void fuse_create_open_req(struct fuse_open_req *req, uint64_t inode, uint32_t flags,
enum fuse_object_type type);
void fuse_create_lookup_req(struct fuse_lookup_req *req, uint64_t inode, uint32_t fname_len);
void fuse_create_read_req(
struct fuse_read_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t size,
enum fuse_object_type type);
void fuse_create_release_req(struct fuse_release_req *req, uint64_t inode, uint64_t fh,
enum fuse_object_type type);
void fuse_create_destroy_req(struct fuse_destroy_req *req);
void fuse_create_create_req(
struct fuse_create_req *req, uint64_t inode, uint32_t fname_len, uint32_t flags,
uint32_t mode);
void fuse_create_write_req(
struct fuse_write_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t size);
void fuse_create_lseek_req(
struct fuse_lseek_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t whence);
void fuse_create_setattr_req(struct fuse_setattr_req *req, uint64_t inode);
void fuse_create_fsync_req(struct fuse_fsync_req *req, uint64_t inode, uint64_t fh);
void fuse_create_mkdir_req(
struct fuse_mkdir_req *req, uint64_t inode, uint32_t dirname_len, uint32_t mode);
void fuse_create_unlink_req(
struct fuse_unlink_req *req, uint32_t fname_len, enum fuse_object_type type);
void fuse_create_rename_req(
struct fuse_rename_req *req, uint64_t old_dir_nodeid, uint32_t old_len,
uint64_t new_dir_nodeid, uint32_t new_len);
const char *fuse_opcode_to_string(uint32_t opcode);
void fuse_dump_init_req_out(struct fuse_init_req *req);
void fuse_dump_entry_out(struct fuse_entry_out *eo);
void fuse_dump_open_req_out(struct fuse_open_req *req);
void fuse_dump_create_req_out(struct fuse_create_out *req);
void fuse_dump_write_out(struct fuse_write_out *wo);
void fuse_dump_lseek_out(struct fuse_lseek_out *lo);
void fuse_dump_attr_out(struct fuse_attr_out *ao);
void fuse_dump_kstafs(struct fuse_kstatfs *ks);
#endif /* ZEPHYR_SUBSYS_FS_FUSE_CLIENT_H_ */