xref: /haiku/src/add-ons/kernel/file_systems/userlandfs/server/fuse/FUSELowLevel.cpp (revision 97f11716bfaa0f385eb0e28a52bf56a5023b9e99)
1 /*
2  * Copyright 2022, Adrien Destugues <pulkomandy@pulkomandy.tk>
3  * Distributed under terms of the MIT license.
4  */
5 
6 #include "FUSELowLevel.h"
7 
8 #include <assert.h>
9 #include <dirent.h>
10 #include <stdio.h>
11 #include <string.h>
12 
13 #include <Errors.h>
14 
15 #define ROUNDDOWN(a, b) (((a) / (b)) * (b))
16 #define ROUNDUP(a, b)   ROUNDDOWN((a) + (b) - 1, b)
17 
18 
19 
20 // Reimplement fuse_req in our own way. In libfuse, the requests are communicated to the kernel,
21 // but in our case, they remain entirely inside userlandfs_server. This means we can use a much
22 // simpler system, by passing pointers directly between userlandfs and the libfuse client code,
23 // and synchronizing them with a semaphore to wait for the replies.
24 struct fuse_req {
fuse_reqfuse_req25 	fuse_req()
26 		: fReplyResult(0),
27 		fReplyBuf(NULL)
28 	{
29 		sem_init(&fSyncSem, 0, 0);
30 	}
31 
~fuse_reqfuse_req32 	~fuse_req() {
33 		sem_destroy(&fSyncSem);
34 	}
35 
Waitfuse_req36 	void Wait() {
37 		sem_wait(&fSyncSem);
38 	}
39 
Notifyfuse_req40 	void Notify() {
41 		sem_post(&fSyncSem);
42 	}
43 
44 	sem_t fSyncSem;
45 	ssize_t fReplyResult;
46 
47 	ReadDirBufferFiller fRequestFiller;
48 	void* fRequestCookie;
49 
50 	// The reply can contain various things, depending on which function was called
51 	union {
52 		struct stat* fReplyAttr;
53 		struct fuse_entry_param fReplyEntry;
54 		struct fuse_file_info* fReplyOpen;
55 		struct statvfs* fReplyStat;
56 
57 		char* fReplyBuf;
58 	};
59 };
60 
61 
62 // #pragma mark - Convenience functions for calling fuse lowlevel filesstems easily
63 
64 
65 void
fuse_ll_init(const fuse_lowlevel_ops * ops,void * userdata,struct fuse_conn_info * conn)66 fuse_ll_init(const fuse_lowlevel_ops* ops, void* userdata, struct fuse_conn_info* conn)
67 {
68 	if (ops->init != NULL) {
69 		ops->init(userdata, conn);
70 	}
71 }
72 
73 
74 void
fuse_ll_destroy(const fuse_lowlevel_ops * ops,void * userdata)75 fuse_ll_destroy(const fuse_lowlevel_ops* ops, void* userdata)
76 {
77 	if (ops->destroy != NULL) {
78 		ops->destroy(userdata);
79 	}
80 }
81 
82 
83 int
fuse_ll_lookup(const fuse_lowlevel_ops * ops,fuse_ino_t parent,const char * name,struct stat * st)84 fuse_ll_lookup(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
85 	struct stat* st)
86 {
87 	if (ops->lookup == NULL)
88 		return B_NOT_SUPPORTED;
89 
90 	fuse_req request;
91 	ops->lookup(&request, parent, name);
92 	request.Wait();
93 	*st = request.fReplyEntry.attr;
94 	st->st_ino = request.fReplyEntry.ino;
95 	return request.fReplyResult;
96 }
97 
98 
99 int
fuse_ll_getattr(const fuse_lowlevel_ops * ops,fuse_ino_t ino,struct stat * st)100 fuse_ll_getattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, struct stat* st)
101 {
102 	if (ops->getattr == NULL)
103 		return B_NOT_SUPPORTED;
104 
105 	fuse_req request;
106 	request.fReplyAttr = st;
107 	ops->getattr(&request, ino, NULL);
108 	request.Wait();
109 	return request.fReplyResult;
110 }
111 
112 
113 int
fuse_ll_setattr(const fuse_lowlevel_ops * ops,fuse_ino_t ino,const struct stat * attr,int to_set)114 fuse_ll_setattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, const struct stat *attr,
115 	int to_set)
116 {
117 	if (ops->setattr == NULL)
118 		return B_NOT_SUPPORTED;
119 
120 	fuse_req request;
121 	//request.fReplyAttr = attr;
122 	ops->setattr(&request, ino, const_cast<struct stat*>(attr), to_set, NULL);
123 	request.Wait();
124 	return request.fReplyResult;
125 }
126 
127 
128 int
fuse_ll_readlink(const fuse_lowlevel_ops * ops,fuse_ino_t ino,char * buffer,size_t size)129 fuse_ll_readlink(const fuse_lowlevel_ops* ops, fuse_ino_t ino, char* buffer, size_t size)
130 {
131 	if (ops->readlink == NULL)
132 		return B_NOT_SUPPORTED;
133 
134 	fuse_req request;
135 	request.fReplyBuf = buffer;
136 	ops->readlink(&request, ino);
137 	request.Wait();
138 	return request.fReplyResult;
139 }
140 
141 
142 int
fuse_ll_mkdir(const fuse_lowlevel_ops * ops,fuse_ino_t parent,const char * name,mode_t mode)143 fuse_ll_mkdir(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
144 	mode_t mode)
145 {
146 	if (ops->mkdir == NULL)
147 		return B_NOT_SUPPORTED;
148 
149 	fuse_req request;
150 	ops->mkdir(&request, parent, name, mode);
151 	request.Wait();
152 	return request.fReplyResult;
153 }
154 
155 
156 int
fuse_ll_unlink(const fuse_lowlevel_ops * ops,fuse_ino_t parent,const char * name)157 fuse_ll_unlink(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name)
158 {
159 	if (ops->unlink == NULL)
160 		return B_NOT_SUPPORTED;
161 
162 	fuse_req request;
163 	ops->unlink(&request, parent, name);
164 	request.Wait();
165 	return request.fReplyResult;
166 }
167 
168 
169 int
fuse_ll_rmdir(const fuse_lowlevel_ops * ops,fuse_ino_t parent,const char * name)170 fuse_ll_rmdir(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name)
171 {
172 	if (ops->rmdir == NULL)
173 		return B_NOT_SUPPORTED;
174 
175 	fuse_req request;
176 	ops->rmdir(&request, parent, name);
177 	request.Wait();
178 	return request.fReplyResult;
179 }
180 
181 
182 int
fuse_ll_symlink(const fuse_lowlevel_ops * ops,const char * link,fuse_ino_t parent,const char * name)183 fuse_ll_symlink(const fuse_lowlevel_ops* ops, const char* link, fuse_ino_t parent,
184 	const char* name)
185 {
186 	if (ops->symlink == NULL)
187 		return B_NOT_SUPPORTED;
188 
189 	fuse_req request;
190 	ops->symlink(&request, link, parent, name);
191 	request.Wait();
192 	return request.fReplyResult;
193 }
194 
195 
196 int
fuse_ll_rename(const fuse_lowlevel_ops * ops,fuse_ino_t parent,const char * name,fuse_ino_t newparent,const char * newname)197 fuse_ll_rename(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
198 	fuse_ino_t newparent, const char *newname)
199 {
200 	if (ops->rename == NULL)
201 		return B_NOT_SUPPORTED;
202 
203 	fuse_req request;
204 	ops->rename(&request, parent, name, newparent, newname);
205 	request.Wait();
206 	return request.fReplyResult;
207 }
208 
209 
210 int
fuse_ll_link(const fuse_lowlevel_ops * ops,fuse_ino_t ino,fuse_ino_t newparent,const char * newname)211 fuse_ll_link(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_ino_t newparent,
212 	const char *newname)
213 {
214 	if (ops->link == NULL)
215 		return B_NOT_SUPPORTED;
216 
217 	fuse_req request;
218 	ops->link(&request, ino, newparent, newname);
219 	request.Wait();
220 	return request.fReplyResult;
221 }
222 
223 
224 int
fuse_ll_open(const fuse_lowlevel_ops * ops,fuse_ino_t ino,fuse_file_info * ffi)225 fuse_ll_open(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_file_info* ffi)
226 {
227 	if (ops->open == NULL)
228 		return 0;
229 
230 	fuse_req request;
231 	request.fReplyOpen = ffi;
232 	ops->open(&request, ino, ffi);
233 	request.Wait();
234 	return request.fReplyResult;
235 }
236 
237 
238 int
fuse_ll_read(const fuse_lowlevel_ops * ops,fuse_ino_t ino,char * buffer,size_t bufferSize,off_t position,fuse_file_info * ffi)239 fuse_ll_read(const fuse_lowlevel_ops* ops, fuse_ino_t ino, char* buffer, size_t bufferSize,
240 	off_t position, fuse_file_info* ffi)
241 {
242 	if (ops->read == NULL)
243 		return B_NOT_SUPPORTED;
244 
245 	fuse_req request;
246 	request.fReplyBuf = buffer;
247 	ops->read(&request, ino, bufferSize, position, ffi);
248 	request.Wait();
249 	return request.fReplyResult;
250 }
251 
252 
253 int
fuse_ll_write(const fuse_lowlevel_ops * ops,fuse_ino_t ino,const char * buf,size_t size,off_t off,struct fuse_file_info * fi)254 fuse_ll_write(const fuse_lowlevel_ops* ops, fuse_ino_t ino, const char *buf,
255 	size_t size, off_t off, struct fuse_file_info *fi)
256 {
257 	if (ops->write == NULL)
258 		return B_NOT_SUPPORTED;
259 
260 	fuse_req request;
261 	ops->write(&request, ino, buf, size, off, fi);
262 	request.Wait();
263 	return request.fReplyResult;
264 }
265 
266 
267 int
fuse_ll_flush(const fuse_lowlevel_ops * ops,fuse_ino_t ino,fuse_file_info * ffi)268 fuse_ll_flush(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_file_info* ffi)
269 {
270 	if (ops->flush == NULL)
271 		return 0;
272 
273 	fuse_req request;
274 	ops->flush(&request, ino, ffi);
275 	request.Wait();
276 	return request.fReplyResult;
277 }
278 
279 
280 int
fuse_ll_release(const fuse_lowlevel_ops * ops,fuse_ino_t ino,fuse_file_info * ffi)281 fuse_ll_release(const fuse_lowlevel_ops* ops, fuse_ino_t ino, fuse_file_info* ffi)
282 {
283 	if (ops->release == NULL)
284 		return 0;
285 
286 	fuse_req request;
287 	ops->release(&request, ino, ffi);
288 	request.Wait();
289 	return request.fReplyResult;
290 }
291 
292 
293 int
fuse_ll_fsync(const fuse_lowlevel_ops * ops,fuse_ino_t ino,int datasync,fuse_file_info * ffi)294 fuse_ll_fsync(const fuse_lowlevel_ops* ops, fuse_ino_t ino, int datasync, fuse_file_info* ffi)
295 {
296 	if (ops->fsync == NULL)
297 		return 0;
298 
299 	fuse_req request;
300 	ops->fsync(&request, ino, datasync, ffi);
301 	request.Wait();
302 	return request.fReplyResult;
303 }
304 
305 
306 int
fuse_ll_opendir(const fuse_lowlevel_ops * ops,fuse_ino_t inode,struct fuse_file_info * ffi)307 fuse_ll_opendir(const fuse_lowlevel_ops* ops, fuse_ino_t inode, struct fuse_file_info* ffi)
308 {
309 	// intentioanlly check for readdir here. Some filesystems do not need an opendir, but still
310 	// implement readdir. However if readdir is not implemented, there is no point in trying to
311 	// open a directory.
312 	if (ops->readdir == NULL)
313 		return B_NOT_SUPPORTED;
314 
315 	if (ops->opendir) {
316 		fuse_req request;
317 		ops->opendir(&request, inode, ffi);
318 		request.Wait();
319 		return request.fReplyResult;
320 	}
321 
322 	return 0;
323 }
324 
325 
326 int
fuse_ll_readdir(const fuse_lowlevel_ops * ops,fuse_ino_t ino,void * cookie,char * buffer,size_t bufferSize,ReadDirBufferFiller filler,off_t pos,fuse_file_info * ffi)327 fuse_ll_readdir(const fuse_lowlevel_ops* ops, fuse_ino_t ino, void* cookie, char* buffer,
328 	size_t bufferSize, ReadDirBufferFiller filler, off_t pos, fuse_file_info* ffi)
329 {
330 	if (ops->readdir == NULL)
331 		return B_NOT_SUPPORTED;
332 
333 	fuse_req request;
334 	request.fReplyBuf = buffer;
335 
336 	request.fRequestFiller = filler;
337 	request.fRequestCookie = cookie;
338 
339 	ops->readdir(&request, ino, bufferSize, pos, ffi);
340 
341 	request.Wait();
342 	return request.fReplyResult;
343 }
344 
345 
346 int
fuse_ll_releasedir(const fuse_lowlevel_ops * ops,fuse_ino_t ino,struct fuse_file_info * fi)347 fuse_ll_releasedir(const fuse_lowlevel_ops* ops, fuse_ino_t ino, struct fuse_file_info *fi)
348 {
349 	if (ops->releasedir == NULL)
350 		return 0;
351 
352 	fuse_req request;
353 	ops->releasedir(&request, ino, fi);
354 	request.Wait();
355 	return request.fReplyResult;
356 }
357 
358 
359 int
fuse_ll_statfs(const fuse_lowlevel_ops * ops,fuse_ino_t inode,struct statvfs * stat)360 fuse_ll_statfs(const fuse_lowlevel_ops* ops, fuse_ino_t inode, struct statvfs* stat)
361 {
362 	if (ops->statfs == NULL)
363 		return B_NOT_SUPPORTED;
364 
365 	fuse_req request;
366 	request.fReplyStat = stat;
367 	ops->statfs(&request, inode);
368 	request.Wait();
369 	return request.fReplyResult;
370 }
371 
372 
373 int
fuse_ll_getxattr(const fuse_lowlevel_ops * ops,fuse_ino_t ino,const char * name,char * buffer,size_t size)374 fuse_ll_getxattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, const char *name,
375 	char* buffer, size_t size)
376 {
377 	if (ops->getxattr == NULL)
378 		return B_NOT_SUPPORTED;
379 
380 	fuse_req request;
381 	request.fReplyBuf = buffer;
382 	ops->getxattr(&request, ino, name, size);
383 	request.Wait();
384 	return request.fReplyResult;
385 }
386 
387 
388 int
fuse_ll_listxattr(const fuse_lowlevel_ops * ops,fuse_ino_t ino,char * buffer,size_t size)389 fuse_ll_listxattr(const fuse_lowlevel_ops* ops, fuse_ino_t ino, char* buffer, size_t size)
390 {
391 	if (ops->listxattr == NULL)
392 		return B_NOT_SUPPORTED;
393 
394 	fuse_req request;
395 	request.fReplyBuf = (char*)buffer;
396 	ops->listxattr(&request, ino, size);
397 	request.Wait();
398 	return request.fReplyResult;
399 }
400 
401 
402 int
fuse_ll_access(const fuse_lowlevel_ops * ops,fuse_ino_t ino,int mask)403 fuse_ll_access(const fuse_lowlevel_ops* ops, fuse_ino_t ino, int mask)
404 {
405 	if (ops->access == NULL)
406 		return B_NOT_SUPPORTED;
407 
408 	fuse_req request;
409 	ops->access(&request, ino, mask);
410 	request.Wait();
411 	return request.fReplyResult;
412 }
413 
414 
415 int
fuse_ll_create(const fuse_lowlevel_ops * ops,fuse_ino_t parent,const char * name,mode_t mode,struct fuse_file_info * fi,fuse_ino_t & ino)416 fuse_ll_create(const fuse_lowlevel_ops* ops, fuse_ino_t parent, const char *name,
417 	mode_t mode, struct fuse_file_info *fi, fuse_ino_t& ino)
418 {
419 	// TODO if the create op is missing, we could try using mknod + open instead
420 	if (ops->create == NULL)
421 		return B_NOT_SUPPORTED;
422 
423 	fuse_req request;
424 	ops->create(&request, parent, name, mode, fi);
425 	request.Wait();
426 	ino = request.fReplyEntry.ino;
427 	return request.fReplyResult;
428 }
429 
430 
431 //#pragma mark - lowlevel replies handling
432 
433 
434 int
fuse_reply_attr(fuse_req_t req,const struct stat * attr,double attr_timeout)435 fuse_reply_attr(fuse_req_t req, const struct stat *attr, double attr_timeout)
436 {
437 	*req->fReplyAttr = *attr;
438 	req->Notify();
439 	return 0;
440 }
441 
442 
443 int
fuse_reply_create(fuse_req_t req,const struct fuse_entry_param * e,const struct fuse_file_info * fi)444 fuse_reply_create(fuse_req_t req, const struct fuse_entry_param* e, const struct fuse_file_info* fi)
445 {
446 	req->fReplyEntry = *e;
447 	req->Notify();
448 	return 0;
449 }
450 
451 
452 int
fuse_reply_readlink(fuse_req_t req,const char * link)453 fuse_reply_readlink(fuse_req_t req, const char* link)
454 {
455 	strlcpy(req->fReplyBuf, link, req->fReplyResult);
456 	req->fReplyResult = strlen(link);
457 	req->Notify();
458 	return 0;
459 }
460 
461 
462 int
fuse_reply_open(fuse_req_t req,const struct fuse_file_info * f)463 fuse_reply_open(fuse_req_t req, const struct fuse_file_info* f)
464 {
465 	*req->fReplyOpen = *f;
466 	req->Notify();
467 	return 0;
468 }
469 
470 
471 int
fuse_reply_buf(fuse_req_t req,const char * buf,size_t size)472 fuse_reply_buf(fuse_req_t req, const char *buf, size_t size)
473 {
474 	if (req->fReplyBuf && req->fReplyBuf != buf)
475 		memcpy(req->fReplyBuf, buf, size);
476 
477 	req->fReplyResult = size;
478 
479 	req->Notify();
480 	return 0;
481 }
482 
483 
484 int
fuse_reply_entry(fuse_req_t req,const struct fuse_entry_param * e)485 fuse_reply_entry(fuse_req_t req, const struct fuse_entry_param *e)
486 {
487 	req->fReplyEntry = *e;
488 	req->Notify();
489 	return 0;
490 }
491 
492 
493 int
fuse_reply_err(fuse_req_t req,int err)494 fuse_reply_err(fuse_req_t req, int err)
495 {
496 	assert(err >= 0);
497 	req->fReplyResult = -err;
498 	req->Notify();
499 	return 0;
500 }
501 
502 
503 int
fuse_reply_statfs(fuse_req_t req,const struct statvfs * stat)504 fuse_reply_statfs(fuse_req_t req, const struct statvfs* stat)
505 {
506 	*req->fReplyStat = *stat;
507 	req->Notify();
508 	return 0;
509 }
510 
511 
512 int
fuse_reply_write(fuse_req_t req,size_t count)513 fuse_reply_write(fuse_req_t req, size_t count)
514 {
515 	req->fReplyResult = count;
516 	req->Notify();
517 	return 0;
518 }
519 
520 
521 // return: size of the entry (no matter if it was added to the buffer or not)
522 // params: pointer to where to store the entry, size
fuse_add_direntry(fuse_req_t req,char * buf,size_t bufsize,const char * name,const struct stat * stbuf,off_t off)523 size_t fuse_add_direntry(fuse_req_t req, char *buf, size_t bufsize, const char *name,
524 	const struct stat *stbuf, off_t off)
525 {
526 	size_t entryLen = offsetof(struct dirent, d_name) + strlen(name) + 1;
527 	// align the entry length, so the next dirent will be aligned
528 	entryLen = ROUNDUP(entryLen, 8);
529 
530 	if (stbuf != NULL) {
531 		req->fRequestFiller(req->fRequestCookie, buf, bufsize, name, stbuf, off);
532 	}
533 
534 	return entryLen;
535 }
536 
537 
538 // #pragma mark - Stubs for FUSE functions called by client code, that we don't need
539 
540 
fuse_session_add_chan(struct fuse_session * se,struct fuse_chan * ch)541 void fuse_session_add_chan(struct fuse_session* se, struct fuse_chan* ch)
542 {
543 }
544 
fuse_session_remove_chan(struct fuse_chan * ch)545 void fuse_session_remove_chan(struct fuse_chan* ch)
546 {
547 }
548 
fuse_session_destroy(struct fuse_session * se)549 void fuse_session_destroy(struct fuse_session* se)
550 {
551 }
552 
fuse_session_exit(struct fuse_session * se)553 void fuse_session_exit(struct fuse_session* se)
554 {
555 }
556