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