1 /*
2 * Copyright (c) 2025 Antmicro <www.antmicro.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <fuse_client.h>
8 #include <zephyr/logging/log.h>
9 #include <inttypes.h>
10
11 LOG_MODULE_REGISTER(fuse, CONFIG_FUSE_CLIENT_LOG_LEVEL);
12
13 static uint64_t unique = 1; /* with unique==0 older virtiofsd asserts, so we are starting from 1 */
14
fuse_get_unique(void)15 static uint64_t fuse_get_unique(void)
16 {
17 return unique++;
18 }
19
fuse_fill_header(struct fuse_in_header * hdr,uint32_t len,uint32_t opcode,uint64_t nodeid)20 void fuse_fill_header(struct fuse_in_header *hdr, uint32_t len, uint32_t opcode, uint64_t nodeid)
21 {
22 hdr->len = len;
23 hdr->opcode = opcode;
24 hdr->unique = fuse_get_unique();
25 hdr->nodeid = nodeid;
26 hdr->uid = CONFIG_FUSE_CLIENT_UID_VALUE;
27 hdr->gid = CONFIG_FUSE_CLIENT_GID_VALUE;
28 hdr->pid = CONFIG_FUSE_CLIENT_PID_VALUE;
29 hdr->total_extlen = 0;
30 }
31
fuse_create_init_req(struct fuse_init_req * req)32 void fuse_create_init_req(struct fuse_init_req *req)
33 {
34 fuse_fill_header(
35 &req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_init_in),
36 FUSE_INIT, 0
37 );
38 req->init_in.major = FUSE_MAJOR_VERSION;
39 req->init_in.minor = FUSE_MINOR_VERSION;
40 req->init_in.max_readahead = 0;
41 req->init_in.flags = 0;
42 req->init_in.flags2 = 0;
43 }
44
fuse_create_open_req(struct fuse_open_req * req,uint64_t inode,uint32_t flags,enum fuse_object_type type)45 void fuse_create_open_req(
46 struct fuse_open_req *req, uint64_t inode, uint32_t flags, enum fuse_object_type type)
47 {
48 fuse_fill_header(
49 &req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_open_in),
50 type == FUSE_DIR ? FUSE_OPENDIR : FUSE_OPEN, inode
51 );
52 req->open_in.flags = flags;
53 req->open_in.open_flags = 0;
54 }
55
fuse_create_lookup_req(struct fuse_lookup_req * req,uint64_t inode,uint32_t fname_len)56 void fuse_create_lookup_req(struct fuse_lookup_req *req, uint64_t inode, uint32_t fname_len)
57 {
58 fuse_fill_header(
59 &req->in_header, sizeof(struct fuse_in_header) + fname_len, FUSE_LOOKUP,
60 inode
61 );
62 }
63
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)64 void fuse_create_read_req(
65 struct fuse_read_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t size,
66 enum fuse_object_type type)
67 {
68 fuse_fill_header(
69 &req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_read_in),
70 type == FUSE_FILE ? FUSE_READ : FUSE_READDIR, inode
71 );
72 req->read_in.fh = fh;
73 req->read_in.offset = offset;
74 req->read_in.size = size;
75 req->read_in.read_flags = 0;
76 req->read_in.lock_owner = 0;
77 req->read_in.flags = 0;
78 }
79
fuse_create_release_req(struct fuse_release_req * req,uint64_t inode,uint64_t fh,enum fuse_object_type type)80 void fuse_create_release_req(struct fuse_release_req *req, uint64_t inode, uint64_t fh,
81 enum fuse_object_type type)
82 {
83 fuse_fill_header(
84 &req->in_header, sizeof(struct fuse_in_header) + sizeof(struct fuse_release_in),
85 type == FUSE_DIR ? FUSE_RELEASEDIR : FUSE_RELEASE, inode
86 );
87 req->release_in.fh = fh;
88 req->release_in.flags = 0;
89 req->release_in.release_flags = 0;
90 req->release_in.lock_owner = 0;
91 }
92
fuse_create_destroy_req(struct fuse_destroy_req * req)93 void fuse_create_destroy_req(struct fuse_destroy_req *req)
94 {
95 fuse_fill_header(&req->in_header, sizeof(struct fuse_in_header), FUSE_DESTROY, 0);
96 }
97
fuse_create_create_req(struct fuse_create_req * req,uint64_t inode,uint32_t fname_len,uint32_t flags,uint32_t mode)98 void fuse_create_create_req(
99 struct fuse_create_req *req, uint64_t inode, uint32_t fname_len, uint32_t flags,
100 uint32_t mode)
101 {
102 fuse_fill_header(
103 &req->in_header, sizeof(req->in_header) + sizeof(req->create_in) + fname_len,
104 FUSE_CREATE, inode
105 );
106 req->create_in.flags = flags;
107 req->create_in.mode = mode;
108 req->create_in.open_flags = 0;
109 req->create_in.umask = 0;
110 }
111
fuse_create_write_req(struct fuse_write_req * req,uint64_t inode,uint64_t fh,uint64_t offset,uint32_t size)112 void fuse_create_write_req(
113 struct fuse_write_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t size)
114 {
115 fuse_fill_header(
116 &req->in_header, sizeof(req->in_header) + sizeof(req->write_in) + size, FUSE_WRITE,
117 inode
118 );
119 req->write_in.fh = fh;
120 req->write_in.offset = offset;
121 req->write_in.size = size;
122 req->write_in.write_flags = 0;
123 req->write_in.lock_owner = 0;
124 req->write_in.flags = 0;
125 }
126
fuse_create_lseek_req(struct fuse_lseek_req * req,uint64_t inode,uint64_t fh,uint64_t offset,uint32_t whence)127 void fuse_create_lseek_req(
128 struct fuse_lseek_req *req, uint64_t inode, uint64_t fh, uint64_t offset, uint32_t whence)
129 {
130 fuse_fill_header(
131 &req->in_header, sizeof(req->in_header) + sizeof(req->lseek_in), FUSE_LSEEK, inode
132 );
133 req->lseek_in.fh = fh;
134 req->lseek_in.offset = offset;
135 req->lseek_in.whence = whence;
136 }
137
fuse_create_setattr_req(struct fuse_setattr_req * req,uint64_t inode)138 void fuse_create_setattr_req(struct fuse_setattr_req *req, uint64_t inode)
139 {
140 fuse_fill_header(
141 &req->in_header, sizeof(req->in_header) + sizeof(struct fuse_setattr_in),
142 FUSE_SETATTR, inode
143 );
144 }
145
fuse_create_fsync_req(struct fuse_fsync_req * req,uint64_t inode,uint64_t fh)146 void fuse_create_fsync_req(struct fuse_fsync_req *req, uint64_t inode, uint64_t fh)
147 {
148 fuse_fill_header(
149 &req->in_header, sizeof(req->in_header) + sizeof(req->fsync_in), FUSE_FSYNC,
150 inode
151 );
152 req->fsync_in.fh = fh;
153 req->fsync_in.fsync_flags = 0;
154 }
155
fuse_create_mkdir_req(struct fuse_mkdir_req * req,uint64_t inode,uint32_t dirname_len,uint32_t mode)156 void fuse_create_mkdir_req(
157 struct fuse_mkdir_req *req, uint64_t inode, uint32_t dirname_len, uint32_t mode)
158 {
159 fuse_fill_header(
160 &req->in_header, sizeof(req->in_header) + sizeof(req->mkdir_in) + dirname_len,
161 FUSE_MKDIR, inode
162 );
163
164 req->mkdir_in.mode = mode;
165 req->mkdir_in.umask = 0;
166 }
167
fuse_create_unlink_req(struct fuse_unlink_req * req,uint32_t fname_len,enum fuse_object_type type)168 void fuse_create_unlink_req(
169 struct fuse_unlink_req *req, uint32_t fname_len, enum fuse_object_type type)
170 {
171 fuse_fill_header(
172 &req->in_header, sizeof(req->in_header) + fname_len,
173 type == FUSE_DIR ? FUSE_RMDIR : FUSE_UNLINK, FUSE_ROOT_INODE
174 );
175 }
176
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)177 void fuse_create_rename_req(
178 struct fuse_rename_req *req, uint64_t old_dir_nodeid, uint32_t old_len,
179 uint64_t new_dir_nodeid, uint32_t new_len)
180 {
181 fuse_fill_header(
182 &req->in_header,
183 sizeof(req->in_header) + sizeof(req->rename_in) + old_len + new_len,
184 FUSE_RENAME, old_dir_nodeid
185 );
186 req->rename_in.newdir = new_dir_nodeid;
187 }
188
fuse_opcode_to_string(uint32_t opcode)189 const char *fuse_opcode_to_string(uint32_t opcode)
190 {
191 switch (opcode) {
192 case FUSE_LOOKUP:
193 return "FUSE_LOOKUP";
194 case FUSE_FORGET:
195 return "FUSE_FORGET";
196 case FUSE_SETATTR:
197 return "FUSE_SETATTR";
198 case FUSE_MKDIR:
199 return "FUSE_MKDIR";
200 case FUSE_UNLINK:
201 return "FUSE_UNLINK";
202 case FUSE_RMDIR:
203 return "FUSE_RMDIR";
204 case FUSE_RENAME:
205 return "FUSE_RENAME";
206 case FUSE_OPEN:
207 return "FUSE_OPEN";
208 case FUSE_READ:
209 return "FUSE_READ";
210 case FUSE_WRITE:
211 return "FUSE_WRITE";
212 case FUSE_STATFS:
213 return "FUSE_STATFS";
214 case FUSE_RELEASE:
215 return "FUSE_RELEASE";
216 case FUSE_FSYNC:
217 return "FUSE_FSYNC";
218 case FUSE_INIT:
219 return "FUSE_INIT";
220 case FUSE_OPENDIR:
221 return "FUSE_OPENDIR";
222 case FUSE_READDIR:
223 return "FUSE_READDIR";
224 case FUSE_RELEASEDIR:
225 return "FUSE_RELEASEDIR";
226 case FUSE_CREATE:
227 return "FUSE_CREATE";
228 case FUSE_DESTROY:
229 return "FUSE_DESTROY";
230 case FUSE_LSEEK:
231 return "FUSE_LSEEK";
232 default:
233 return "";
234 }
235 }
236
fuse_dump_init_req_out(struct fuse_init_req * req)237 void fuse_dump_init_req_out(struct fuse_init_req *req)
238 {
239 LOG_INF(
240 "FUSE_INIT response:\n"
241 "major=%" PRIu32 "\n"
242 "minor=%" PRIu32 "\n"
243 "max_readahead=%" PRIu32 "\n"
244 "flags=%" PRIu32 "\n"
245 "max_background=%" PRIu16 "\n"
246 "congestion_threshold=%" PRIu16 "\n"
247 "max_write=%" PRIu32 "\n"
248 "time_gran=%" PRIu32 "\n"
249 "max_pages=%" PRIu16 "\n"
250 "map_alignment=%" PRIu16 "\n"
251 "flags2=%" PRIu32 "\n"
252 "max_stack_depth=%" PRIu32,
253 req->init_out.major,
254 req->init_out.minor,
255 req->init_out.max_readahead,
256 req->init_out.flags,
257 req->init_out.max_background,
258 req->init_out.congestion_threshold,
259 req->init_out.max_write,
260 req->init_out.time_gran,
261 req->init_out.max_pages,
262 req->init_out.map_alignment,
263 req->init_out.flags2,
264 req->init_out.max_stack_depth
265 );
266 }
267
fuse_dump_entry_out(struct fuse_entry_out * eo)268 void fuse_dump_entry_out(struct fuse_entry_out *eo)
269 {
270 LOG_INF(
271 "FUSE LOOKUP response:\n"
272 "nodeid=%" PRIu64 "\n"
273 "generation=%" PRIu64 "\n"
274 "entry_valid=%" PRIu64 "\n"
275 "attr_valid=%" PRIu64 "\n"
276 "entry_valid_nsec=%" PRIu32 "\n"
277 "attr_valid_nsec=%" PRIu32 "\n"
278 "attr.ino=%" PRIu64 "\n"
279 "attr.size=%" PRIu64 "\n"
280 "attr.blocks=%" PRIu64 "\n"
281 "attr.atime=%" PRIu64 "\n"
282 "attr.mtime=%" PRIu64 "\n"
283 "attr.ctime=%" PRIu64 "\n"
284 "attr.atimensec=%" PRIu32 "\n"
285 "attr.mtimensec=%" PRIu32 "\n"
286 "attr.ctimensec=%" PRIu32 "\n"
287 "attr.mode=%" PRIu32 "\n"
288 "attr.nlink=%" PRIu32 "\n"
289 "attr.uid=%" PRIu32 "\n"
290 "attr.gid=%" PRIu32 "\n"
291 "attr.rdev=%" PRIu32 "\n"
292 "attr.blksize=%" PRIu32 "\n"
293 "attr.flags=%" PRIu32,
294 eo->nodeid,
295 eo->generation,
296 eo->entry_valid,
297 eo->attr_valid,
298 eo->entry_valid_nsec,
299 eo->attr_valid_nsec,
300 eo->attr.ino,
301 eo->attr.size,
302 eo->attr.blocks,
303 eo->attr.atime,
304 eo->attr.mtime,
305 eo->attr.ctime,
306 eo->attr.atimensec,
307 eo->attr.mtimensec,
308 eo->attr.ctimensec,
309 eo->attr.mode,
310 eo->attr.nlink,
311 eo->attr.uid,
312 eo->attr.gid,
313 eo->attr.rdev,
314 eo->attr.blksize,
315 eo->attr.flags
316 );
317 }
318
fuse_dump_open_req_out(struct fuse_open_req * req)319 void fuse_dump_open_req_out(struct fuse_open_req *req)
320 {
321 LOG_INF(
322 "FUSE OPEN response:\n"
323 "fh=%" PRIu64 "\n"
324 "open_flags=%" PRIu32 "\n"
325 "backing_id=%" PRIi32,
326 req->open_out.fh,
327 req->open_out.open_flags,
328 req->open_out.backing_id
329 );
330 }
331
fuse_dump_create_req_out(struct fuse_create_out * req)332 void fuse_dump_create_req_out(struct fuse_create_out *req)
333 {
334 LOG_INF(
335 "FUSE CREATE response:\n"
336 "nodeid=%" PRIu64 "\n"
337 "generation=%" PRIu64 "\n"
338 "entry_valid=%" PRIu64 "\n"
339 "attr_valid=%" PRIu64 "\n"
340 "entry_valid_nsec=%" PRIu32 "\n"
341 "attr_valid_nsec=%" PRIu32 "\n"
342 "attr.ino=%" PRIu64 "\n"
343 "attr.size=%" PRIu64 "\n"
344 "attr.blocks=%" PRIu64 "\n"
345 "attr.atime=%" PRIu64 "\n"
346 "attr.mtime=%" PRIu64 "\n"
347 "attr.ctime=%" PRIu64 "\n"
348 "attr.atimensec=%" PRIu32 "\n"
349 "attr.mtimensec=%" PRIu32 "\n"
350 "attr.ctimensec=%" PRIu32 "\n"
351 "attr.mode=%" PRIu32 "\n"
352 "attr.nlink=%" PRIu32 "\n"
353 "attr.uid=%" PRIu32 "\n"
354 "attr.gid=%" PRIu32 "\n"
355 "attr.rdev=%" PRIu32 "\n"
356 "attr.blksize=%" PRIu32 "\n"
357 "attr.flags=%" PRIu32 "\n"
358 "fh=%" PRIu64 "\n"
359 "open_flags=%" PRIu32 "\n"
360 "backing_id=%" PRIi32,
361 req->entry_out.nodeid,
362 req->entry_out.generation,
363 req->entry_out.entry_valid,
364 req->entry_out.attr_valid,
365 req->entry_out.entry_valid_nsec,
366 req->entry_out.attr_valid_nsec,
367 req->entry_out.attr.ino,
368 req->entry_out.attr.size,
369 req->entry_out.attr.blocks,
370 req->entry_out.attr.atime,
371 req->entry_out.attr.mtime,
372 req->entry_out.attr.ctime,
373 req->entry_out.attr.atimensec,
374 req->entry_out.attr.mtimensec,
375 req->entry_out.attr.ctimensec,
376 req->entry_out.attr.mode,
377 req->entry_out.attr.nlink,
378 req->entry_out.attr.uid,
379 req->entry_out.attr.gid,
380 req->entry_out.attr.rdev,
381 req->entry_out.attr.blksize,
382 req->entry_out.attr.flags,
383 req->open_out.fh,
384 req->open_out.open_flags,
385 req->open_out.backing_id
386 );
387 }
388
fuse_dump_write_out(struct fuse_write_out * wo)389 void fuse_dump_write_out(struct fuse_write_out *wo)
390 {
391 LOG_INF("FUSE WRITE response:\nsize=%" PRIu32, wo->size);
392 }
393
fuse_dump_lseek_out(struct fuse_lseek_out * lo)394 void fuse_dump_lseek_out(struct fuse_lseek_out *lo)
395 {
396 LOG_INF("FUSE WRITE response:\noffset=%" PRIu64, lo->offset);
397 }
398
fuse_dump_attr_out(struct fuse_attr_out * ao)399 void fuse_dump_attr_out(struct fuse_attr_out *ao)
400 {
401 LOG_INF(
402 "attr_valid=%" PRIu64 "\n"
403 "attr_valid_nsec=%" PRIu32,
404 ao->attr_valid,
405 ao->attr_valid_nsec
406 );
407 }
408
fuse_dump_kstafs(struct fuse_kstatfs * ks)409 void fuse_dump_kstafs(struct fuse_kstatfs *ks)
410 {
411 LOG_INF(
412 "blocks=%" PRIu64 "\n"
413 "bfree=%" PRIu64 "\n"
414 "bavail=%" PRIu64 "\n"
415 "files=%" PRIu64 "\n"
416 "ffree=%" PRIu64 "\n"
417 "bsize=%" PRIu32 "\n"
418 "namelen=%" PRIu32 "\n"
419 "frsize=%" PRIu32,
420 ks->blocks,
421 ks->bfree,
422 ks->bavail,
423 ks->files,
424 ks->ffree,
425 ks->bsize,
426 ks->namelen,
427 ks->frsize
428 );
429 }
430