jcs's openbsd hax
openbsd
at jcs 308 lines 6.4 kB view raw
1/* $OpenBSD: fuse_lowlevel.c,v 1.2 2026/01/29 06:04:27 helg Exp $ */ 2/* 3 * Copyright (c) 2025 Helg Bredow <helg@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18#include <sys/uio.h> 19#include <errno.h> 20#include <stddef.h> 21#include <stdlib.h> 22#include <string.h> 23#include <unistd.h> 24 25#include "debug.h" 26#include "fuse_private.h" 27 28enum { 29 KEY_HELP, 30 KEY_VERSION, 31 KEY_DEBUG 32}; 33 34static const struct fuse_opt fuse_ll_opts[] = { 35 FUSE_OPT_KEY("debug", KEY_DEBUG), 36 FUSE_OPT_KEY("-d", KEY_DEBUG), 37 FUSE_OPT_KEY("-h", KEY_HELP), 38 FUSE_OPT_KEY("--help", KEY_HELP), 39 FUSE_OPT_KEY("-V", KEY_VERSION), 40 FUSE_OPT_KEY("--version", KEY_VERSION), 41 FUSE_OPT_KEY("max_read=", FUSE_OPT_KEY_DISCARD), 42 FUSE_OPT_END 43}; 44 45static void 46dump_version(void) 47{ 48 fprintf(stderr, "FUSE library version: %d.%d\n", FUSE_MAJOR_VERSION, 49 FUSE_MINOR_VERSION); 50} 51 52static void 53dump_help(void) 54{ 55 fprintf(stderr, " -o max_write=N max buffer size for " 56 "write operations\n" 57 ); 58} 59 60static int 61ifuse_ll_opt_proc(void *data, const char *arg, int key, 62 struct fuse_args *outargs) 63{ 64 switch (key) { 65 case KEY_HELP: 66 dump_help(); 67 break; 68 case KEY_VERSION: 69 dump_version(); 70 break; 71 case KEY_DEBUG: 72 ifuse_debug_init(); 73 return (1); 74 default: 75 fprintf(stderr, "fuse: unknown option -- %s\n", arg); 76 } 77 78 return -1; 79} 80 81struct fuse_session * 82fuse_lowlevel_new(struct fuse_args *fargs, 83 const struct fuse_lowlevel_ops *llops, const size_t llops_len, 84 void *userdata) 85{ 86 struct fuse_session *se; 87 88 se = calloc(1, sizeof(*se)); 89 if (se == NULL) 90 return (NULL); 91 92 if (fuse_opt_parse(fargs, NULL, fuse_ll_opts, ifuse_ll_opt_proc) == -1) { 93 free(se); 94 return (NULL); 95 } 96 97 if (llops->create && !llops->mknod) 98 DPRINTF("libfuse: WARNING: filesystem supports creating files " 99 "but does not implement mknod. No new files can be " 100 "created.\n"); 101 102 /* validate size of ops struct */ 103 if (sizeof(se->llops) == llops_len) 104 memcpy(&se->llops, llops, sizeof(se->llops)); 105 else { 106 free(se); 107 return (NULL); 108 } 109 110 se->userdata = userdata; 111 112 return (se); 113} 114DEF(fuse_lowlevel_new); 115 116static int 117ifuse_reply(fuse_req_t req, const char *data, const size_t data_size, int err) 118{ 119 struct fusebuf *fbuf; 120 struct iovec iov[2]; 121 size_t fbuf_size; 122 123 /* check for sanity */ 124 if (data == NULL && data_size > 0) { 125 DPRINTF("\nlibfuse: NULL data with size: %zu\n", data_size); 126 fuse_reply_err(req, EIO); 127 return (-EINVAL); 128 } 129 130 fbuf = req->fbuf; 131 fbuf_size = sizeof(fbuf->fb_hdr) + sizeof(fbuf->FD); 132 133 fbuf->fb_err = err; 134 fbuf->fb_len = data_size; 135 136 iov[0].iov_base = fbuf; 137 iov[0].iov_len = fbuf_size; 138 iov[1].iov_base = (void *)data; 139 iov[1].iov_len = data_size; 140 141 DPRINTF("errno: %d", fbuf->fb_err); 142 143 return fuse_chan_send(req->ch, iov, 2); 144} 145 146static int 147ifuse_reply_ok(fuse_req_t req) 148{ 149 return ifuse_reply(req, NULL, 0, 0); 150} 151 152int 153fuse_reply_err(fuse_req_t req, int err) 154{ 155 return ifuse_reply(req, NULL, 0, err); 156} 157DEF(fuse_reply_err); 158 159int 160fuse_reply_buf(fuse_req_t req, const char *buf, off_t size) 161{ 162 return ifuse_reply(req, buf, size, 0); 163} 164DEF(fuse_reply_buf); 165 166int 167fuse_reply_readlink(fuse_req_t req, char *path) 168{ 169 return ifuse_reply(req, path, strlen(path), 0); 170} 171DEF(fuse_reply_readlink); 172 173int 174fuse_reply_write(fuse_req_t req, size_t size) 175{ 176 struct fusebuf *fbuf; 177 178 fbuf = req->fbuf; 179 fbuf->fb_io_len = size; 180 181 return ifuse_reply_ok(req); 182} 183DEF(fuse_reply_write); 184 185int 186fuse_reply_attr(fuse_req_t req, const struct stat *stbuf, double attr_timeout) 187{ 188 struct fusebuf *fbuf; 189 190 fbuf = req->fbuf; 191 fbuf->fb_attr = *stbuf; 192 193 return ifuse_reply_ok(req); 194} 195DEF(fuse_reply_attr); 196 197int 198fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e) 199{ 200 struct fusebuf *fbuf; 201 202 fbuf = req->fbuf; 203 fbuf->fb_attr = e->attr; 204 fbuf->fb_ino = e->ino; 205 DPRINTF("inode: %llu\t", e->ino); 206 207 return ifuse_reply_ok(req); 208} 209DEF(fuse_reply_entry); 210 211int 212fuse_reply_statfs(fuse_req_t req, const struct statvfs *stbuf) 213{ 214 struct fusebuf *fbuf; 215 216 fbuf = req->fbuf; 217 fbuf->fb_stat = *stbuf; 218 219 return ifuse_reply_ok(req); 220} 221DEF(fuse_reply_statfs); 222 223int 224fuse_reply_bmap(fuse_req_t req, uint64_t idx) 225{ 226 DPRINTF("%s: Unsupported", __func__); 227 return (-EOPNOTSUPP); 228} 229DEF(fuse_reply_bmap); 230 231int 232fuse_reply_create(fuse_req_t req, const struct fuse_entry_param *e, 233 const struct fuse_file_info *ffi) 234{ 235 DPRINTF("%s: Unsupported", __func__); 236 return (-EOPNOTSUPP); 237} 238DEF(fuse_reply_create); 239 240int 241fuse_reply_open(fuse_req_t req, const struct fuse_file_info *ffi) 242{ 243 struct fusebuf *fbuf; 244 245 fbuf = req->fbuf; 246 fbuf->fb_io_fd = ffi->fh; 247 248 return ifuse_reply_ok(req); 249} 250DEF(fuse_reply_open); 251 252void 253fuse_reply_none(fuse_req_t req) 254{ 255 /* no-op */ 256} 257DEF(fuse_reply_none); 258 259#define GENERIC_DIRSIZ(NLEN) \ 260((sizeof (struct dirent) - (MAXNAMLEN+1)) + ((NLEN+1 + 7) &~ 7)) 261 262size_t 263fuse_add_direntry(fuse_req_t req, char *buf, const size_t bufsize, 264 const char *name, const struct stat *stbuf, off_t off) 265{ 266 struct dirent *dir; 267 size_t namelen; 268 size_t len; 269 270 if (name == NULL) 271 return (0); 272 273 namelen = strnlen(name, MAXNAMLEN); 274 len = GENERIC_DIRSIZ(namelen); 275 276 /* NULL buf is used to request size to be calculated */ 277 if (buf == NULL || stbuf == NULL || req == NULL) 278 return (len); 279 280 /* buffer is full */ 281 if (bufsize < len) 282 return (len); 283 284 dir = (struct dirent *)buf; 285 dir->d_fileno = stbuf->st_ino; 286 dir->d_type = IFTODT(stbuf->st_mode); 287 dir->d_reclen = len; 288 dir->d_off = off; 289 strlcpy(dir->d_name, name, sizeof(dir->d_name)); 290 dir->d_namlen = strlen(dir->d_name); 291 292 return (len); 293} 294DEF(fuse_add_direntry); 295 296const struct fuse_ctx * 297fuse_req_ctx(fuse_req_t req) 298{ 299 return (&req->ctx); 300} 301DEF(fuse_req_ctx); 302 303void * 304fuse_req_userdata(fuse_req_t req) 305{ 306 return (req->se->userdata); 307} 308DEF(fuse_req_userdata);