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 { 25 fuse_req() 26 : fReplyResult(0), 27 fReplyBuf(NULL) 28 { 29 sem_init(&fSyncSem, 0, 0); 30 } 31 32 ~fuse_req() { 33 sem_destroy(&fSyncSem); 34 } 35 36 void Wait() { 37 sem_wait(&fSyncSem); 38 } 39 40 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 541 void fuse_session_add_chan(struct fuse_session* se, struct fuse_chan* ch) 542 { 543 } 544 545 void fuse_session_remove_chan(struct fuse_chan* ch) 546 { 547 } 548 549 void fuse_session_destroy(struct fuse_session* se) 550 { 551 } 552 553 void fuse_session_exit(struct fuse_session* se) 554 { 555 } 556