| /* |
| * FUSE: Filesystem in Userspace |
| * Copyright (C) 2026 Oracle. |
| * |
| * This program can be distributed under the terms of the GNU GPLv2. |
| * See the file GPL2.txt. |
| * |
| * This file is shared library code for example fuse servers that want to |
| * expose a single regular file that wraps another file in a manner that goes |
| * beyond simple passthrough. It is not itself a fuse server. |
| */ |
| #ifndef FUSE_SINGLE_FILE_H_ |
| #define FUSE_SINGLE_FILE_H_ |
| |
| static inline uint64_t round_up(uint64_t b, unsigned int align) |
| { |
| unsigned int m; |
| |
| if (align == 0) |
| return b; |
| m = b % align; |
| if (m) |
| b += align - m; |
| return b; |
| } |
| |
| static inline uint64_t round_down(uint64_t b, unsigned int align) |
| { |
| unsigned int m; |
| |
| if (align == 0) |
| return b; |
| m = b % align; |
| return b - m; |
| } |
| |
| static inline uint64_t howmany(uint64_t b, unsigned int align) |
| { |
| unsigned int m; |
| |
| if (align == 0) |
| return b; |
| m = (b % align) ? 1 : 0; |
| return (b / align) + m; |
| } |
| |
| struct single_file { |
| int backing_fd; |
| |
| int64_t isize; |
| uint64_t blocks; |
| |
| mode_t mode; |
| |
| bool ro; |
| bool allow_dio; |
| bool sync; |
| bool require_bdev; |
| |
| unsigned int blocksize; |
| |
| struct timespec atime; |
| struct timespec mtime; |
| struct timespec ctime; |
| |
| pthread_mutex_t lock; |
| }; |
| |
| extern struct single_file single_file; |
| |
| static inline uint64_t b_to_fsbt(uint64_t off) |
| { |
| return off / single_file.blocksize; |
| } |
| |
| static inline uint64_t b_to_fsb(uint64_t off) |
| { |
| return (off + single_file.blocksize - 1) / single_file.blocksize; |
| } |
| |
| static inline uint64_t fsb_to_b(uint64_t fsb) |
| { |
| return fsb * single_file.blocksize; |
| } |
| |
| enum single_file_opt_keys { |
| SINGLE_FILE_RO = 171717, /* how many options could we possibly have? */ |
| SINGLE_FILE_RW, |
| SINGLE_FILE_REQUIRE_BDEV, |
| SINGLE_FILE_DIO, |
| SINGLE_FILE_NODIO, |
| SINGLE_FILE_SYNC, |
| SINGLE_FILE_NOSYNC, |
| SINGLE_FILE_SIZE, |
| SINGLE_FILE_BLOCKSIZE, |
| |
| SINGLE_FILE_NR_KEYS, |
| }; |
| |
| #define SINGLE_FILE_OPT_KEYS \ |
| FUSE_OPT_KEY("ro", SINGLE_FILE_RO), \ |
| FUSE_OPT_KEY("rw", SINGLE_FILE_RW), \ |
| FUSE_OPT_KEY("require_bdev", SINGLE_FILE_REQUIRE_BDEV), \ |
| FUSE_OPT_KEY("dio", SINGLE_FILE_DIO), \ |
| FUSE_OPT_KEY("nodio", SINGLE_FILE_NODIO), \ |
| FUSE_OPT_KEY("sync", SINGLE_FILE_SYNC), \ |
| FUSE_OPT_KEY("nosync", SINGLE_FILE_NOSYNC), \ |
| FUSE_OPT_KEY("size=%s", SINGLE_FILE_SIZE), \ |
| FUSE_OPT_KEY("blocksize=%s", SINGLE_FILE_BLOCKSIZE) |
| |
| int single_file_opt_proc(void *data, const char *arg, int key, |
| struct fuse_args *outargs); |
| |
| unsigned long long parse_num_blocks(const char *arg, int log_block_size); |
| |
| struct fuse_service; |
| int single_file_service_open(const struct fuse_service *sf, const char *path); |
| |
| void single_file_check_read(off_t pos, size_t *count); |
| int single_file_check_write(off_t pos, size_t *count); |
| |
| int single_file_configure(const char *device, const char *filename); |
| int single_file_configure_simple(const char *filename); |
| void single_file_close(void); |
| |
| ssize_t single_file_pwrite(const char *buf, size_t count, off_t pos); |
| ssize_t single_file_pread(char *buf, size_t count, off_t pos); |
| |
| /* low-level fuse operation handlers */ |
| |
| #ifdef USE_SINGLE_FILE_LL_API |
| bool is_single_file_child(fuse_ino_t parent, const char *name); |
| bool is_single_file_ino(fuse_ino_t ino); |
| |
| void single_file_ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, |
| off_t off, struct fuse_file_info *fi); |
| |
| void single_file_ll_statfs(fuse_req_t req, fuse_ino_t ino); |
| |
| void single_file_ll_statx(fuse_req_t req, fuse_ino_t ino, int flags, int mask, |
| struct fuse_file_info *fi); |
| |
| void single_file_ll_getattr(fuse_req_t req, fuse_ino_t ino, |
| struct fuse_file_info *fi); |
| void single_file_ll_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, |
| int to_set, struct fuse_file_info *fi); |
| |
| void single_file_ll_lookup(fuse_req_t req, fuse_ino_t parent, const char *name); |
| void single_file_ll_open(fuse_req_t req, fuse_ino_t ino, |
| struct fuse_file_info *fi); |
| |
| void single_file_ll_fsync(fuse_req_t req, fuse_ino_t ino, int datasync, |
| struct fuse_file_info *fi); |
| |
| int reply_buf_limited(fuse_req_t req, const char *buf, size_t bufsize, |
| off_t off, size_t maxsize); |
| #endif |
| |
| /* high-level fuse operation handlers */ |
| |
| #ifdef USE_SINGLE_FILE_HL_API |
| bool is_single_open_file_path(const struct fuse_file_info *fi, const char *name); |
| |
| int single_file_hl_readdir(const char *path, void *buf, fuse_fill_dir_t filler, |
| off_t offset, struct fuse_file_info *fi, |
| enum fuse_readdir_flags flags); |
| |
| int single_file_hl_statfs(const char *path, struct statvfs *buf); |
| |
| int single_file_hl_statx(const char *path, int statx_flags, int statx_mask, |
| struct statx *stx, struct fuse_file_info *fi); |
| |
| int single_file_hl_getattr(const char *path, struct stat *stbuf, |
| struct fuse_file_info *fi); |
| int single_file_hl_chmod(const char *path, mode_t mode, |
| struct fuse_file_info *fi); |
| int single_file_hl_utimens(const char *path, const struct timespec ctv[2], |
| struct fuse_file_info *fi); |
| int single_file_hl_chown(const char *path, uid_t owner, gid_t group, |
| struct fuse_file_info *fi); |
| int single_file_hl_truncate(const char *path, off_t len, |
| struct fuse_file_info *fi); |
| |
| int single_file_hl_opendir(const char *path, struct fuse_file_info *fi); |
| int single_file_hl_open(const char *path, struct fuse_file_info *fi); |
| |
| int single_file_hl_fsync(const char *path, int datasync, |
| struct fuse_file_info *fi); |
| #endif |
| |
| #if !defined(USE_SINGLE_FILE_LL_API) && !defined(USE_SINGLE_FILE_HL_API) |
| # warning USE_SINGLE_FILE_[HL]L_API not defined! |
| #endif |
| |
| #endif /* FUSE_SINGLE_FILE_H_ */ |