1 /* 2 * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Distributed under the terms of the MIT License. 4 */ 5 6 #include "Volume.h" 7 8 #include <util/AutoLock.h> 9 10 #include "AutoLocker.h" 11 #include "Compatibility.h" 12 #include "Debug.h" 13 #include "FileSystem.h" 14 #include "HashMap.h" 15 #include "kernel_interface.h" 16 #include "KernelRequestHandler.h" 17 #include "PortReleaser.h" 18 #include "RequestAllocator.h" 19 #include "Requests.h" 20 #include "Settings.h" 21 #include "SingleReplyRequestHandler.h" 22 23 // The time after which the notification thread times out at the port and 24 // restarts the loop. Of interest only when the FS is deleted. It is the 25 // maximal time the destructor has to wait for the thread. 26 static const bigtime_t kNotificationRequestTimeout = 50000; // 50 ms 27 28 // SelectSyncMap 29 struct FileSystem::SelectSyncMap 30 : public SynchronizedHashMap<HashKey32<selectsync*>, int32*> { 31 }; 32 33 // constructor 34 FileSystem::FileSystem() 35 : 36 fVolumes(), 37 fName(), 38 fTeam(-1), 39 fNotificationPort(NULL), 40 fNotificationThread(-1), 41 fPortPool(), 42 fSelectSyncs(NULL), 43 fSettings(NULL), 44 fUserlandServerTeam(-1), 45 fInitialized(false), 46 fTerminating(false) 47 { 48 mutex_init(&fVolumeLock, "userlandfs volumes"); 49 mutex_init(&fVNodeOpsLock, "userlandfs vnode ops"); 50 } 51 52 // destructor 53 FileSystem::~FileSystem() 54 { 55 fTerminating = true; 56 57 // wait for the notification thread to terminate 58 if (fNotificationThread >= 0) { 59 int32 result; 60 wait_for_thread(fNotificationThread, &result); 61 } 62 63 // delete our data structures 64 if (fSelectSyncs) { 65 for (SelectSyncMap::Iterator it = fSelectSyncs->GetIterator(); 66 it.HasNext();) { 67 SelectSyncMap::Entry entry = it.Next(); 68 delete entry.value; 69 } 70 delete fSelectSyncs; 71 } 72 delete fSettings; 73 74 // delete vnode ops vectors -- there shouldn't be any left, though 75 VNodeOps* ops = fVNodeOps.Clear(); 76 int32 count = 0; 77 while (ops != NULL) { 78 count++; 79 VNodeOps* next = ops->hash_link; 80 free(ops); 81 ops = next; 82 } 83 if (count > 0) 84 WARN(("Deleted %ld vnode ops vectors!\n", count)); 85 } 86 87 // Init 88 status_t 89 FileSystem::Init(const char* name, team_id team, Port::Info* infos, int32 count, 90 const FSCapabilities& capabilities) 91 { 92 PRINT(("FileSystem::Init(\"%s\", %p, %ld)\n", name, infos, count)); 93 capabilities.Dump(); 94 95 // check parameters 96 if (!name || !infos || count < 2) 97 RETURN_ERROR(B_BAD_VALUE); 98 99 // set the name 100 if (!fName.SetTo(name)) 101 return B_NO_MEMORY; 102 103 // init VNodeOps map 104 status_t error = fVNodeOps.Init(); 105 if (error != B_OK) 106 return error; 107 108 fTeam = team; 109 fCapabilities = capabilities; 110 111 // create the select sync entry map 112 fSelectSyncs = new(nothrow) SelectSyncMap; 113 if (!fSelectSyncs) 114 return B_NO_MEMORY; 115 116 // create the request ports 117 // the notification port 118 fNotificationPort = new(nothrow) RequestPort(infos); 119 if (!fNotificationPort) 120 RETURN_ERROR(B_NO_MEMORY); 121 error = fNotificationPort->InitCheck(); 122 if (error != B_OK) 123 return error; 124 125 // the other request ports 126 for (int32 i = 1; i < count; i++) { 127 RequestPort* port = new(nothrow) RequestPort(infos + i); 128 if (!port) 129 RETURN_ERROR(B_NO_MEMORY); 130 error = port->InitCheck(); 131 if (error == B_OK) 132 error = fPortPool.AddPort(port); 133 if (error != B_OK) { 134 delete port; 135 RETURN_ERROR(error); 136 } 137 } 138 139 // get the userland team 140 port_info portInfo; 141 error = get_port_info(infos[0].owner_port, &portInfo); 142 if (error != B_OK) 143 RETURN_ERROR(error); 144 fUserlandServerTeam = portInfo.team; 145 146 // print some info about the userland team 147 D( 148 PRINT((" userland team is: %ld\n", fUserlandServerTeam)); 149 int32 cookie = 0; 150 thread_info threadInfo; 151 while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo) 152 == B_OK) { 153 PRINT((" userland thread: %ld: `%s'\n", threadInfo.thread, 154 threadInfo.name)); 155 } 156 ); 157 158 // load the settings 159 fSettings = new(nothrow) Settings; 160 if (fSettings) { 161 status_t settingsError = fSettings->SetTo(fName.GetString()); 162 if (settingsError != B_OK) { 163 PRINT(("Failed to load settings: %s\n", strerror(settingsError))); 164 delete fSettings; 165 fSettings = NULL; 166 } else 167 fSettings->Dump(); 168 } else 169 ERROR(("Failed to allocate settings.\n")); 170 171 // spawn the notification thread 172 #if USER 173 fNotificationThread = spawn_thread(_NotificationThreadEntry, 174 "UFS notification thread", B_NORMAL_PRIORITY, this); 175 #else 176 fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry, 177 "UFS notification thread", B_NORMAL_PRIORITY, this); 178 #endif 179 if (fNotificationThread < 0) 180 RETURN_ERROR(fNotificationThread); 181 resume_thread(fNotificationThread); 182 183 fInitialized = (error == B_OK); 184 RETURN_ERROR(error); 185 } 186 187 // GetName 188 const char* 189 FileSystem::GetName() const 190 { 191 return fName.GetString(); 192 } 193 194 // GetCapabilities 195 const FSCapabilities& 196 FileSystem::GetCapabilities() const 197 { 198 return fCapabilities; 199 } 200 201 // GetPortPool 202 RequestPortPool* 203 FileSystem::GetPortPool() 204 { 205 return &fPortPool; 206 } 207 208 // Mount 209 status_t 210 FileSystem::Mount(fs_volume* fsVolume, const char* device, uint32 flags, 211 const char* parameters, Volume** _volume) 212 { 213 // check initialization and parameters 214 if (!fInitialized || !_volume) 215 return B_BAD_VALUE; 216 217 // create volume 218 Volume* volume = new(nothrow) Volume(this, fsVolume); 219 if (!volume) 220 return B_NO_MEMORY; 221 222 // add volume to the volume list 223 MutexLocker locker(fVolumeLock); 224 status_t error = fVolumes.PushBack(volume); 225 locker.Unlock(); 226 if (error != B_OK) 227 return error; 228 229 // mount volume 230 error = volume->Mount(device, flags, parameters); 231 if (error != B_OK) { 232 MutexLocker locker(fVolumeLock); 233 fVolumes.Remove(volume); 234 locker.Unlock(); 235 volume->RemoveReference(); 236 return error; 237 } 238 239 *_volume = volume; 240 return error; 241 } 242 243 // Initialize 244 /*status_t 245 FileSystem::Initialize(const char* deviceName, const char* parameters, 246 size_t len) 247 { 248 // get a free port 249 RequestPort* port = fPortPool.AcquirePort(); 250 if (!port) 251 return B_ERROR; 252 PortReleaser _(&fPortPool, port); 253 // prepare the request 254 RequestAllocator allocator(port->GetPort()); 255 MountVolumeRequest* request; 256 status_t error = AllocateRequest(allocator, &request); 257 if (error != B_OK) 258 return error; 259 error = allocator.AllocateString(request->device, deviceName); 260 if (error == B_OK) 261 error = allocator.AllocateData(request->parameters, parameters, len, 1); 262 if (error != B_OK) 263 return error; 264 // send the request 265 SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY); 266 InitializeVolumeReply* reply; 267 error = port->SendRequest(&allocator, &handler, (Request**)&reply); 268 if (error != B_OK) 269 return error; 270 RequestReleaser requestReleaser(port, reply); 271 // process the reply 272 if (reply->error != B_OK) 273 return reply->error; 274 return error; 275 }*/ 276 277 // VolumeUnmounted 278 void 279 FileSystem::VolumeUnmounted(Volume* volume) 280 { 281 MutexLocker locker(fVolumeLock); 282 fVolumes.Remove(volume); 283 } 284 285 // GetVolume 286 Volume* 287 FileSystem::GetVolume(dev_t id) 288 { 289 MutexLocker _(fVolumeLock); 290 for (Vector<Volume*>::Iterator it = fVolumes.Begin(); 291 it != fVolumes.End(); 292 it++) { 293 Volume* volume = *it; 294 if (volume->GetID() == id) { 295 volume->AddReference(); 296 return volume; 297 } 298 } 299 return NULL; 300 } 301 302 // GetIOCtlInfo 303 const IOCtlInfo* 304 FileSystem::GetIOCtlInfo(int command) const 305 { 306 return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL); 307 } 308 309 // AddSelectSyncEntry 310 status_t 311 FileSystem::AddSelectSyncEntry(selectsync* sync) 312 { 313 AutoLocker<SelectSyncMap> _(fSelectSyncs); 314 int32* count = fSelectSyncs->Get(sync); 315 if (!count) { 316 count = new(nothrow) int32(0); 317 if (!count) 318 return B_NO_MEMORY; 319 status_t error = fSelectSyncs->Put(sync, count); 320 if (error != B_OK) { 321 delete count; 322 return error; 323 } 324 } 325 (*count)++; 326 return B_OK; 327 } 328 329 // RemoveSelectSyncEntry 330 void 331 FileSystem::RemoveSelectSyncEntry(selectsync* sync) 332 { 333 AutoLocker<SelectSyncMap> _(fSelectSyncs); 334 if (int32* count = fSelectSyncs->Get(sync)) { 335 if (--(*count) <= 0) { 336 fSelectSyncs->Remove(sync); 337 delete count; 338 } 339 } 340 } 341 342 343 // KnowsSelectSyncEntry 344 bool 345 FileSystem::KnowsSelectSyncEntry(selectsync* sync) 346 { 347 return fSelectSyncs->ContainsKey(sync); 348 } 349 350 351 // GetVNodeOps 352 VNodeOps* 353 FileSystem::GetVNodeOps(const FSVNodeCapabilities& capabilities) 354 { 355 MutexLocker locker(fVNodeOpsLock); 356 357 // do we already have ops for those capabilities 358 VNodeOps* ops = fVNodeOps.Lookup(capabilities); 359 if (ops != NULL) { 360 ops->refCount++; 361 return ops; 362 } 363 364 // no, create a new object 365 fs_vnode_ops* opsVector = new(std::nothrow) fs_vnode_ops; 366 if (opsVector == NULL) 367 return NULL; 368 369 // set the operations 370 _InitVNodeOpsVector(opsVector, capabilities); 371 372 // create the VNodeOps object 373 ops = new(std::nothrow) VNodeOps(capabilities, opsVector); 374 if (ops == NULL) { 375 delete opsVector; 376 return NULL; 377 } 378 379 fVNodeOps.Insert(ops); 380 381 return ops; 382 } 383 384 385 // PutVNodeOps 386 void 387 FileSystem::PutVNodeOps(VNodeOps* ops) 388 { 389 MutexLocker locker(fVNodeOpsLock); 390 391 if (--ops->refCount == 0) { 392 fVNodeOps.Remove(ops); 393 delete ops; 394 } 395 } 396 397 398 // IsUserlandServerThread 399 bool 400 FileSystem::IsUserlandServerThread() const 401 { 402 thread_info info; 403 get_thread_info(find_thread(NULL), &info); 404 return (info.team == fUserlandServerTeam); 405 } 406 407 408 // _InitVNodeOpsVector 409 void 410 FileSystem::_InitVNodeOpsVector(fs_vnode_ops* ops, 411 const FSVNodeCapabilities& capabilities) 412 { 413 memcpy(ops, &gUserlandFSVnodeOps, sizeof(fs_vnode_ops)); 414 415 #undef CLEAR_UNSUPPORTED 416 #define CLEAR_UNSUPPORTED(capability, op) \ 417 if (!capabilities.Get(capability)) \ 418 ops->op = NULL 419 420 // vnode operations 421 // FS_VNODE_CAPABILITY_LOOKUP: lookup 422 // FS_VNODE_CAPABILITY_GET_VNODE_NAME: get_vnode_name 423 // emulated in userland 424 // FS_VNODE_CAPABILITY_PUT_VNODE: put_vnode 425 // FS_VNODE_CAPABILITY_REMOVE_VNODE: remove_vnode 426 // needed by Volume to clean up 427 428 // asynchronous I/O 429 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_IO, io); 430 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CANCEL_IO, cancel_io); 431 432 // cache file access 433 ops->get_file_map = NULL; // never used 434 435 // common operations 436 // FS_VNODE_CAPABILITY_IOCTL: ioctl 437 // needed by Volume 438 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SET_FLAGS, set_flags); 439 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_SELECT, select); 440 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_DESELECT, deselect); 441 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_FSYNC, fsync); 442 443 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_SYMLINK, read_symlink); 444 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SYMLINK, create_symlink); 445 446 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_LINK, link); 447 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_UNLINK, unlink); 448 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME, rename); 449 450 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_ACCESS, access); 451 // FS_VNODE_CAPABILITY_READ_STAT: read_stat 452 // needed by Volume 453 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_STAT, write_stat); 454 455 // file operations 456 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE, create); 457 // FS_VNODE_CAPABILITY_OPEN: open 458 // mandatory 459 // FS_VNODE_CAPABILITY_CLOSE: close 460 // needed by Volume 461 // FS_VNODE_CAPABILITY_FREE_COOKIE: free_cookie 462 // needed by Volume 463 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ, read); 464 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE, write); 465 466 // directory operations 467 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_DIR, create_dir); 468 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_DIR, remove_dir); 469 // FS_VNODE_CAPABILITY_OPEN_DIR: open_dir 470 // mandatory 471 // FS_VNODE_CAPABILITY_CLOSE_DIR: close_dir 472 // needed by Volume 473 // FS_VNODE_CAPABILITY_FREE_DIR_COOKIE: free_dir_cookie 474 // needed by Volume 475 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_DIR, read_dir); 476 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_DIR, rewind_dir); 477 478 // attribute directory operations 479 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, open_attr_dir); 480 // FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR: close_attr_dir 481 // needed by Volume 482 // FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE: free_attr_dir_cookie 483 // needed by Volume 484 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_DIR, read_attr_dir); 485 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, rewind_attr_dir); 486 487 // attribute operations 488 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_ATTR, create_attr); 489 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_OPEN_ATTR, open_attr); 490 // FS_VNODE_CAPABILITY_CLOSE_ATTR: close_attr 491 // needed by Volume 492 // FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE: free_attr_cookie 493 // needed by Volume 494 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR, read_attr); 495 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR, write_attr); 496 497 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_READ_ATTR_STAT, read_attr_stat); 498 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_WRITE_ATTR_STAT, write_attr_stat); 499 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_RENAME_ATTR, rename_attr); 500 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_REMOVE_ATTR, remove_attr); 501 502 // support for node and FS layers 503 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_CREATE_SPECIAL_NODE, 504 create_special_node); 505 CLEAR_UNSUPPORTED(FS_VNODE_CAPABILITY_GET_SUPER_VNODE, get_super_vnode); 506 507 #undef CLEAR_UNSUPPORTED 508 } 509 510 511 // _NotificationThreadEntry 512 int32 513 FileSystem::_NotificationThreadEntry(void* data) 514 { 515 return ((FileSystem*)data)->_NotificationThread(); 516 } 517 518 519 // _NotificationThread 520 int32 521 FileSystem::_NotificationThread() 522 { 523 // process the notification requests until the FS is deleted 524 while (!fTerminating) { 525 if (fNotificationPort->InitCheck() != B_OK) 526 return fNotificationPort->InitCheck(); 527 KernelRequestHandler handler(this, NO_REQUEST); 528 fNotificationPort->HandleRequests(&handler, NULL, 529 kNotificationRequestTimeout); 530 } 531 // We eat all remaining notification requests, so that they aren't 532 // presented to the file system, when it is mounted next time. 533 // TODO: We should probably use a special handler that sends an ack reply, 534 // but ignores the requests otherwise. 535 KernelRequestHandler handler(this, NO_REQUEST); 536 fNotificationPort->HandleRequests(&handler, NULL, 0); 537 return 0; 538 } 539 540