1 /* 2 * Copyright 2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "FUSEFileSystem.h" 7 8 #include <stdlib.h> 9 #include <string.h> 10 11 #include <new> 12 13 #include "fuse_fs.h" 14 #include "FUSEVolume.h" 15 16 #include "../RequestThread.h" 17 18 19 class FUSEFileSystem::ArgumentVector { 20 private: 21 enum { MAX_ARGUMENTS = 128 }; 22 23 public: 24 ArgumentVector() 25 : 26 fBuffer(NULL), 27 fCount(0) 28 { 29 } 30 31 ~ArgumentVector() 32 { 33 free(fBuffer); 34 } 35 36 const char* const* Arguments() const 37 { 38 return fArguments; 39 } 40 41 int ArgumentCount() const 42 { 43 return fCount; 44 } 45 46 status_t Init(const char* firstElement, const char* arguments) 47 { 48 size_t firstElementSize = firstElement != NULL 49 ? strlen(firstElement) + 1 : 0; 50 51 // allocate the buffer 52 fBuffer = (char*)malloc(firstElementSize + strlen(arguments) + 1); 53 if (fBuffer == NULL) 54 return B_NO_MEMORY; 55 56 fCount = 0; 57 58 bool inArgument = false; 59 int bufferIndex = 0; 60 61 // push the first element, if given 62 if (firstElement != NULL) { 63 memcpy(fBuffer, firstElement, firstElementSize); 64 fArguments[fCount++] = fBuffer; 65 bufferIndex = firstElementSize; 66 } 67 68 // parse the given string 69 for (; *arguments != '\0'; arguments++) { 70 char c = *arguments; 71 switch (c) { 72 case ' ': 73 case '\t': 74 case '\r': 75 case '\n': 76 // white-space marks argument boundaries 77 if (inArgument) { 78 // terminate the current argument 79 fBuffer[bufferIndex++] = '\0'; 80 inArgument = false; 81 } 82 break; 83 case '\\': 84 c = *++arguments; 85 if (c == '\0') 86 break; 87 // fall through 88 default: 89 if (!inArgument) { 90 // push a new argument 91 if (fCount == MAX_ARGUMENTS) 92 break; 93 94 fArguments[fCount++] = fBuffer + bufferIndex; 95 inArgument = true; 96 } 97 98 fBuffer[bufferIndex++] = c; 99 break; 100 } 101 } 102 103 // terminate the last argument 104 if (inArgument) 105 fBuffer[bufferIndex++] = '\0'; 106 107 // NULL terminate the argument array 108 fArguments[fCount] = NULL; 109 110 return B_OK; 111 } 112 113 private: 114 char* fBuffer; 115 const char* fArguments[MAX_ARGUMENTS + 1]; 116 int fCount; 117 }; 118 119 120 FUSEFileSystem::FUSEFileSystem(const char* fsName, 121 int (*mainFunction)(int, const char* const*)) 122 : 123 FileSystem(fsName), 124 fMainFunction(mainFunction), 125 fInitThread(-1), 126 fInitStatus(B_NO_INIT), 127 fInitSemaphore(-1), 128 fExitSemaphore(-1), 129 fInitParameters(NULL), 130 fFS(NULL) 131 { 132 fClientFSType = CLIENT_FS_FUSE; 133 134 // FS capabilities 135 fCapabilities.ClearAll(); 136 fCapabilities.Set(FS_CAPABILITY_MOUNT, true); 137 } 138 139 140 FUSEFileSystem::~FUSEFileSystem() 141 { 142 if (fInitSemaphore >= 0) 143 delete_sem(fInitSemaphore); 144 145 if (fExitSemaphore >= 0) 146 delete_sem(fExitSemaphore); 147 148 if (fInitThread >= 0) 149 wait_for_thread(fInitThread, NULL); 150 } 151 152 153 status_t 154 FUSEFileSystem::CreateVolume(Volume** _volume, dev_t id) 155 { 156 printf("FUSEFileSystem::CreateVolume()\n"); 157 // Only one volume is possible 158 if (!fVolumes.IsEmpty()) 159 RETURN_ERROR(B_BUSY); 160 161 // create the volume 162 FUSEVolume* volume = new(std::nothrow) FUSEVolume(this, id); 163 if (volume == NULL) 164 return B_NO_MEMORY; 165 166 status_t error = volume->Init(); 167 if (error != B_OK) { 168 delete volume; 169 return error; 170 } 171 172 *_volume = volume; 173 return B_OK; 174 } 175 176 177 status_t 178 FUSEFileSystem::DeleteVolume(Volume* volume) 179 { 180 delete volume; 181 return B_OK; 182 } 183 184 185 void 186 FUSEFileSystem::InitRequestThreadContext(RequestThreadContext* context) 187 { 188 // Statically assert that fuse_context fits in the RequestThreadContext 189 // FS data. We can't include <Debug.h> as it clashes with our "Debug.h". 190 do { 191 static const int staticAssertHolds 192 = sizeof(fuse_context) <= REQUEST_THREAD_CONTEXT_FS_DATA_SIZE; 193 struct __staticAssertStruct__ { 194 char __static_assert_failed__[2 * staticAssertHolds - 1]; 195 }; 196 } while (false); 197 198 // init a fuse_context 199 KernelRequest* request = context->GetRequest(); 200 fuse_context* fuseContext = (fuse_context*)context->GetFSData(); 201 fuseContext->fuse = (struct fuse*)this; 202 fuseContext->uid = request->user; 203 fuseContext->gid = request->group; 204 fuseContext->pid = request->team; 205 fuseContext->private_data = fFS != NULL ? fFS->userData : NULL; 206 } 207 208 209 status_t 210 FUSEFileSystem::InitClientFS(const char* parameters) 211 { 212 PRINT(("FUSEFileSystem::InitClientFS()\n")); 213 // create the semaphores we need 214 fInitSemaphore = create_sem(0, "FUSE init sem"); 215 if (fInitSemaphore < 0) 216 RETURN_ERROR(fInitSemaphore); 217 218 fExitSemaphore = create_sem(0, "FUSE exit sem"); 219 if (fExitSemaphore < 0) 220 RETURN_ERROR(fExitSemaphore); 221 222 fInitStatus = 1; 223 fInitParameters = parameters; 224 225 // Start the initialization thread -- it will call main() and won't return 226 // until unmounting. 227 fInitThread = spawn_thread(&_InitializationThreadEntry, 228 "FUSE init", B_NORMAL_PRIORITY, this); 229 if (fInitThread < 0) 230 RETURN_ERROR(fInitThread); 231 232 resume_thread(fInitThread); 233 234 // wait for the initialization to finish 235 PRINT((" waiting for init thread...\n")); 236 while (acquire_sem(fInitSemaphore) == B_INTERRUPTED) { 237 } 238 239 PRINT((" waiting for init thread done\n")); 240 fInitSemaphore = -1; 241 242 if (fInitStatus > 0) 243 RETURN_ERROR(B_ERROR); 244 if (fInitStatus != B_OK) 245 RETURN_ERROR(fInitStatus); 246 247 // initialization went fine 248 return B_OK; 249 } 250 251 252 void 253 FUSEFileSystem::ExitClientFS(status_t status) 254 { 255 // set the exit status and notify the initialization thread 256 fExitStatus = status; 257 if (fExitSemaphore >= 0) 258 delete_sem(fExitSemaphore); 259 260 if (fInitThread >= 0) 261 wait_for_thread(fInitThread, NULL); 262 } 263 264 265 status_t 266 FUSEFileSystem::FinishInitClientFS(fuse_config* config, 267 const fuse_operations* ops, size_t opSize, void* userData) 268 { 269 PRINT(("FUSEFileSystem::FinishInitClientFS()\n")); 270 fExitStatus = B_ERROR; 271 272 fFUSEConfig = *config; 273 274 // do the initialization 275 fInitStatus = _InitClientFS(ops, opSize, userData); 276 return fInitStatus; 277 } 278 279 280 status_t 281 FUSEFileSystem::MainLoop(bool multithreaded) 282 { 283 // TODO: Respect the multithreaded flag! 284 285 PRINT(("FUSEFileSystem::FinishMounting()\n")); 286 // notify the mount thread 287 PRINT((" notifying mount thread\n")); 288 delete_sem(fInitSemaphore); 289 290 // loop until unmounting 291 PRINT((" waiting for unmounting...\n")); 292 while (acquire_sem(fExitSemaphore) == B_INTERRUPTED) { 293 } 294 PRINT((" waiting for unmounting done\n")); 295 296 fExitSemaphore = -1; 297 298 if (fFS != NULL) 299 fuse_fs_destroy(fFS); 300 301 return fExitStatus; 302 } 303 304 305 /*static*/ status_t 306 FUSEFileSystem::_InitializationThreadEntry(void* data) 307 { 308 return ((FUSEFileSystem*)data)->_InitializationThread(); 309 } 310 311 312 status_t 313 FUSEFileSystem::_InitializationThread() 314 { 315 // parse the parameters 316 ArgumentVector args; 317 status_t error = args.Init(GetName(), fInitParameters); 318 if (error != B_OK) { 319 fInitStatus = error; 320 delete_sem(fInitSemaphore); 321 return B_OK; 322 } 323 324 // call main -- should not return until unmounting 325 fMainFunction(args.ArgumentCount(), args.Arguments()); 326 printf("FUSEFileSystem::_InitializationThread(): main() returned!\n"); 327 328 if (fInitStatus > 0 && fInitSemaphore >= 0) { 329 // something went wrong early -- main() returned without calling 330 // fuse_main() 331 fInitStatus = B_ERROR; 332 delete_sem(fInitSemaphore); 333 } 334 335 return B_OK; 336 } 337 338 339 status_t 340 FUSEFileSystem::_InitClientFS(const fuse_operations* ops, size_t opSize, 341 void* userData) 342 { 343 // create a fuse_fs object 344 fFS = fuse_fs_new(ops, opSize, userData); 345 if (fFS == NULL) 346 return B_ERROR; 347 348 _InitCapabilities(); 349 PRINT(("volume capabilities:\n")); 350 fVolumeCapabilities.Dump(); 351 PRINT(("node capabilities:\n")); 352 fNodeCapabilities.Dump(); 353 354 // init connection info 355 fConnectionInfo.proto_major = 0; 356 fConnectionInfo.proto_minor = 0; 357 fConnectionInfo.async_read = false; 358 fConnectionInfo.max_write = 64 * 1024; 359 fConnectionInfo.max_readahead = 64 * 1024; 360 361 fuse_fs_init(fFS, &fConnectionInfo); 362 363 return B_OK; 364 } 365 366 367 void 368 FUSEFileSystem::_InitCapabilities() 369 { 370 fVolumeCapabilities.ClearAll(); 371 fNodeCapabilities.ClearAll(); 372 373 // Volume operations 374 fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_UNMOUNT, true); 375 376 fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_READ_FS_INFO, fFS->ops.statfs); 377 // missing: FS_VOLUME_CAPABILITY_WRITE_FS_INFO 378 fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_SYNC, fFS->ops.fsync); 379 // emulated via fsync() 380 381 fVolumeCapabilities.Set(FS_VOLUME_CAPABILITY_GET_VNODE, true); 382 // emulated 383 384 // index directory & index operations 385 // missing: FS_VOLUME_CAPABILITY_OPEN_INDEX_DIR 386 // missing: FS_VOLUME_CAPABILITY_CLOSE_INDEX_DIR 387 // missing: FS_VOLUME_CAPABILITY_FREE_INDEX_DIR_COOKIE 388 // missing: FS_VOLUME_CAPABILITY_READ_INDEX_DIR 389 // missing: FS_VOLUME_CAPABILITY_REWIND_INDEX_DIR 390 391 // missing: FS_VOLUME_CAPABILITY_CREATE_INDEX 392 // missing: FS_VOLUME_CAPABILITY_REMOVE_INDEX 393 // missing: FS_VOLUME_CAPABILITY_READ_INDEX_STAT 394 395 // query operations 396 // missing: FS_VOLUME_CAPABILITY_OPEN_QUERY 397 // missing: FS_VOLUME_CAPABILITY_CLOSE_QUERY 398 // missing: FS_VOLUME_CAPABILITY_FREE_QUERY_COOKIE 399 // missing: FS_VOLUME_CAPABILITY_READ_QUERY 400 // missing: FS_VOLUME_CAPABILITY_REWIND_QUERY 401 402 // vnode operations 403 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LOOKUP, true); 404 // emulated 405 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_GET_VNODE_NAME, true); 406 // emulated 407 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_PUT_VNODE, true); 408 // emulated 409 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_VNODE, true); 410 // emulated 411 412 // VM file access 413 // missing: FS_VNODE_CAPABILITY_CAN_PAGE 414 // missing: FS_VNODE_CAPABILITY_READ_PAGES 415 // missing: FS_VNODE_CAPABILITY_WRITE_PAGES 416 417 // cache file access 418 // missing: FS_VNODE_CAPABILITY_GET_FILE_MAP 419 420 // common operations 421 // missing: FS_VNODE_CAPABILITY_IOCTL 422 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_SET_FLAGS, true); 423 // emulated 424 // missing: FS_VNODE_CAPABILITY_SELECT 425 // missing: FS_VNODE_CAPABILITY_DESELECT 426 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FSYNC, fFS->ops.fsync); 427 428 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, fFS->ops.readlink); 429 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, fFS->ops.symlink); 430 431 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_LINK, fFS->ops.link); 432 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_UNLINK, fFS->ops.unlink); 433 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME, fFS->ops.rename); 434 435 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_ACCESS, fFS->ops.access); 436 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, fFS->ops.getattr); 437 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT, 438 fFS->ops.chmod != NULL || fFS->ops.chown != NULL 439 || fFS->ops.truncate != NULL || fFS->ops.utimens != NULL 440 || fFS->ops.utime != NULL); 441 442 // file operations 443 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE, fFS->ops.create); 444 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN, fFS->ops.open); 445 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE, fFS->ops.flush); 446 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, fFS->ops.release); 447 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ, fFS->ops.read); 448 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE, fFS->ops.write); 449 450 // directory operations 451 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, fFS->ops.mkdir); 452 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, fFS->ops.rmdir); 453 bool readDirSupport = fFS->ops.opendir != NULL || fFS->ops.readdir != NULL 454 || fFS->ops.getdir; 455 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, readDirSupport); 456 // not needed: FS_VNODE_CAPABILITY_CLOSE_DIR 457 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, readDirSupport); 458 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, readDirSupport); 459 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, readDirSupport); 460 461 // attribute directory operations 462 bool hasAttributes = fFS->ops.listxattr != NULL; 463 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, hasAttributes); 464 // not needed: FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR 465 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE, 466 hasAttributes); 467 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, hasAttributes); 468 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, hasAttributes); 469 470 // attribute operations 471 // // we emulate open_attr() and free_attr_dir_cookie() if either read_attr() 472 // // or write_attr() is present 473 // bool hasAttributes = (fFS->ops.read_attr || fFS->ops.write_attr); 474 // fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, hasAttributes); 475 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, hasAttributes); 476 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, false); 477 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE, hasAttributes); 478 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, fFS->ops.getxattr); 479 // fNodeCapabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, fFS->ops.write_attr); 480 481 fNodeCapabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT, 482 fFS->ops.getxattr); 483 // // missing: FS_VNODE_CAPABILITY_WRITE_ATTR_STAT 484 // fNodeCapabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, fFS->ops.rename_attr); 485 // fNodeCapabilities.Set(FS_VNODE_CAPABILITY_REMOVE_ATTR, fFS->ops.remove_attr); 486 } 487 488 489 // #pragma mark - bootstrapping 490 491 492 status_t 493 userlandfs_create_file_system(const char* fsName, image_id image, 494 FileSystem** _fileSystem) 495 { 496 printf("userlandfs_create_file_system()\n"); 497 // look up the main() function of the add-on 498 int (*mainFunction)(int argc, const char* const* argv); 499 status_t error = get_image_symbol(image, "main", B_SYMBOL_TYPE_TEXT, 500 (void**)&mainFunction); 501 if (error != B_OK) 502 return error; 503 printf("userlandfs_create_file_system(): found main: %p\n", mainFunction); 504 505 // create the file system 506 FUSEFileSystem* fileSystem = new(std::nothrow) FUSEFileSystem(fsName, 507 mainFunction); 508 if (fileSystem == NULL) 509 return B_NO_MEMORY; 510 511 *_fileSystem = fileSystem; 512 return B_OK; 513 } 514