jcs's openbsd hax
openbsd
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);