/* * Copyright (c) 2025 Antmicro * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include 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 ); }