1// FileSystem.cpp 2 3#include "AutoLocker.h" 4#include "Compatibility.h" 5#include "Debug.h" 6#include "FileSystem.h" 7#include "HashMap.h" 8#include "KernelRequestHandler.h" 9#include "PortReleaser.h" 10#include "RequestAllocator.h" 11#include "RequestPort.h" 12#include "Requests.h" 13#include "Settings.h" 14#include "SingleReplyRequestHandler.h" 15#include "Volume.h" 16 17// The time after which the notification thread times out at the port and 18// restarts the loop. Of interest only when the FS is deleted. It is the 19// maximal time the destructor has to wait for the thread. 20static const bigtime_t kNotificationRequestTimeout = 50000; // 50 ms 21 22// SelectSyncMap 23struct FileSystem::SelectSyncMap 24 : public SynchronizedHashMap<HashKey32<selectsync*>, int32*> { 25}; 26 27// constructor 28FileSystem::FileSystem(const char* name, RequestPort* initPort, status_t* error) 29 : LazyInitializable(), 30 Referencable(), 31 fVolumes(), 32 fVolumeLock(), 33 fName(name), 34 fInitPort(initPort), 35 fNotificationPort(NULL), 36 fNotificationThread(-1), 37 fPortPool(), 38 fSelectSyncs(NULL), 39 fSettings(NULL), 40 fUserlandServerTeam(-1), 41 fTerminating(false) 42{ 43 if (error) 44 *error = (fName.GetLength() == 0 ? B_NO_MEMORY : B_OK); 45} 46 47// destructor 48FileSystem::~FileSystem() 49{ 50 fTerminating = true; 51 // wait for the notification thread to terminate 52 if (fNotificationThread >= 0) { 53 int32 result; 54 wait_for_thread(fNotificationThread, &result); 55 } 56 // delete our data structures 57 if (fSelectSyncs) { 58 for (SelectSyncMap::Iterator it = fSelectSyncs->GetIterator(); 59 it.HasNext();) { 60 SelectSyncMap::Entry entry = it.Next(); 61 delete entry.value; 62 } 63 delete fSelectSyncs; 64 } 65 delete fSettings; 66} 67 68// GetName 69const char* 70FileSystem::GetName() const 71{ 72 return fName.GetString(); 73} 74 75// GetPortPool 76RequestPortPool* 77FileSystem::GetPortPool() 78{ 79 return &fPortPool; 80} 81 82// Mount 83status_t 84FileSystem::Mount(nspace_id id, const char* device, ulong flags, 85 const char* parameters, int32 len, Volume** _volume) 86{ 87 // check initialization and parameters 88 if (InitCheck() != B_OK) 89 return InitCheck(); 90 if (!_volume) 91 return B_BAD_VALUE; 92 // create volume 93 Volume* volume = new(nothrow) Volume(this, id); 94 if (!volume) 95 return B_NO_MEMORY; 96 // add volume to the volume list 97 fVolumeLock.Lock(); 98 status_t error = fVolumes.PushBack(volume); 99 fVolumeLock.Unlock(); 100 if (error != B_OK) 101 return error; 102 // mount volume 103 error = volume->Mount(device, flags, parameters, len); 104 if (error != B_OK) { 105 fVolumeLock.Lock(); 106 fVolumes.Remove(volume); 107 fVolumeLock.Unlock(); 108 volume->RemoveReference(); 109 return error; 110 } 111 *_volume = volume; 112 return error; 113} 114 115// Initialize 116status_t 117FileSystem::Initialize(const char* deviceName, const char* parameters, 118 size_t len) 119{ 120 // get a free port 121 RequestPort* port = fPortPool.AcquirePort(); 122 if (!port) 123 return B_ERROR; 124 PortReleaser _(&fPortPool, port); 125 // prepare the request 126 RequestAllocator allocator(port->GetPort()); 127 MountVolumeRequest* request; 128 status_t error = AllocateRequest(allocator, &request); 129 if (error != B_OK) 130 return error; 131 error = allocator.AllocateString(request->device, deviceName); 132 if (error == B_OK) 133 error = allocator.AllocateData(request->parameters, parameters, len, 1); 134 if (error != B_OK) 135 return error; 136 // send the request 137 SingleReplyRequestHandler handler(MOUNT_VOLUME_REPLY); 138 InitializeVolumeReply* reply; 139 error = port->SendRequest(&allocator, &handler, (Request**)&reply); 140 if (error != B_OK) 141 return error; 142 RequestReleaser requestReleaser(port, reply); 143 // process the reply 144 if (reply->error != B_OK) 145 return reply->error; 146 return error; 147} 148 149// VolumeUnmounted 150void 151FileSystem::VolumeUnmounted(Volume* volume) 152{ 153 fVolumeLock.Lock(); 154 fVolumes.Remove(volume); 155 fVolumeLock.Unlock(); 156} 157 158// GetVolume 159Volume* 160FileSystem::GetVolume(nspace_id id) 161{ 162 AutoLocker<Locker> _(fVolumeLock); 163 for (Vector<Volume*>::Iterator it = fVolumes.Begin(); 164 it != fVolumes.End(); 165 it++) { 166 Volume* volume = *it; 167 if (volume->GetID() == id) { 168 volume->AddReference(); 169 return volume; 170 } 171 } 172 return NULL; 173} 174 175// GetIOCtlInfo 176const IOCtlInfo* 177FileSystem::GetIOCtlInfo(int command) const 178{ 179 return (fSettings ? fSettings->GetIOCtlInfo(command) : NULL); 180} 181 182// AddSelectSyncEntry 183status_t 184FileSystem::AddSelectSyncEntry(selectsync* sync) 185{ 186 AutoLocker<SelectSyncMap> _(fSelectSyncs); 187 int32* count = fSelectSyncs->Get(sync); 188 if (!count) { 189 count = new(nothrow) int32(0); 190 if (!count) 191 return B_NO_MEMORY; 192 status_t error = fSelectSyncs->Put(sync, count); 193 if (error != B_OK) { 194 delete count; 195 return error; 196 } 197 } 198 (*count)++; 199 return B_OK; 200} 201 202// RemoveSelectSyncEntry 203void 204FileSystem::RemoveSelectSyncEntry(selectsync* sync) 205{ 206 AutoLocker<SelectSyncMap> _(fSelectSyncs); 207 if (int32* count = fSelectSyncs->Get(sync)) { 208 if (--(*count) <= 0) { 209 fSelectSyncs->Remove(sync); 210 delete count; 211 } 212 } 213} 214 215// KnowsSelectSyncEntry 216bool 217FileSystem::KnowsSelectSyncEntry(selectsync* sync) 218{ 219 return fSelectSyncs->ContainsKey(sync); 220} 221 222// IsUserlandServerThread 223bool 224FileSystem::IsUserlandServerThread() const 225{ 226 thread_info info; 227 get_thread_info(find_thread(NULL), &info); 228 return (info.team == fUserlandServerTeam); 229} 230 231// FirstTimeInit 232status_t 233FileSystem::FirstTimeInit() 234{ 235 if (fName.GetLength() == 0) 236 RETURN_ERROR(B_NO_MEMORY); 237 PRINT(("FileSystem::FirstTimeInit(): %s\n", fName.GetString())); 238 // create the select sync entry map 239 fSelectSyncs = new(nothrow) SelectSyncMap; 240 if (!fSelectSyncs) 241 return B_NO_MEMORY; 242 // prepare the request 243 RequestAllocator allocator(fInitPort->GetPort()); 244 FSConnectRequest* request; 245 status_t error = AllocateRequest(allocator, &request); 246 if (error != B_OK) 247 RETURN_ERROR(error); 248 error = allocator.AllocateString(request->fsName, fName.GetString()); 249 if (error != B_OK) 250 RETURN_ERROR(error); 251 // send the request 252 SingleReplyRequestHandler handler(FS_CONNECT_REPLY); 253 FSConnectReply* reply; 254 error = fInitPort->SendRequest(&allocator, &handler, (Request**)&reply); 255 if (error != B_OK) 256 RETURN_ERROR(error); 257 RequestReleaser requestReleaser(fInitPort, reply); 258 // process the reply 259 if (reply->error != B_OK) 260 RETURN_ERROR(reply->error); 261 // get the port infos 262 int32 count = reply->portInfoCount; 263 if (count < 2) 264 RETURN_ERROR(B_BAD_DATA); 265 if (reply->portInfos.GetSize() != count * (int32)sizeof(Port::Info)) 266 RETURN_ERROR(B_BAD_DATA); 267 Port::Info* infos = (Port::Info*)reply->portInfos.GetData(); 268 // create the request ports 269 // the notification port 270 fNotificationPort = new(nothrow) RequestPort(infos); 271 if (!fNotificationPort) 272 RETURN_ERROR(B_NO_MEMORY); 273 error = fNotificationPort->InitCheck(); 274 if (error != B_OK) 275 return error; 276 // the other request ports 277 for (int32 i = 1; i < count; i++) { 278 RequestPort* port = new(nothrow) RequestPort(infos + i); 279 if (!port) 280 RETURN_ERROR(B_NO_MEMORY); 281 error = port->InitCheck(); 282 if (error == B_OK) 283 error = fPortPool.AddPort(port); 284 if (error != B_OK) { 285 delete port; 286 RETURN_ERROR(error); 287 } 288 } 289 // get the userland team 290 port_info portInfo; 291 error = get_port_info(infos[0].owner_port, &portInfo); 292 if (error != B_OK) 293 RETURN_ERROR(error); 294 fUserlandServerTeam = portInfo.team; 295 // print some info about the userland team 296 D( 297 PRINT((" userland team is: %ld\n", fUserlandServerTeam)); 298 int32 cookie = 0; 299 thread_info threadInfo; 300 while (get_next_thread_info(fUserlandServerTeam, &cookie, &threadInfo) 301 == B_OK) { 302 PRINT((" userland thread: %ld: `%s'\n", threadInfo.thread, 303 threadInfo.name)); 304 } 305 ); 306 // load the settings 307 fSettings = new(nothrow) Settings; 308 if (fSettings) { 309 status_t settingsError = fSettings->SetTo(fName.GetString()); 310 if (settingsError != B_OK) { 311 PRINT(("Failed to load settings: %s\n", strerror(settingsError))); 312 delete fSettings; 313 fSettings = NULL; 314 } else 315 fSettings->Dump(); 316 } else 317 ERROR(("Failed to allocate settings.\n")); 318 // spawn the notification thread 319 #if USER 320 fNotificationThread = spawn_thread(_NotificationThreadEntry, 321 "UFS notification thread", B_NORMAL_PRIORITY, this); 322 #else 323 fNotificationThread = spawn_kernel_thread(_NotificationThreadEntry, 324 "UFS notification thread", B_NORMAL_PRIORITY, this); 325 #endif 326 if (fNotificationThread < 0) 327 RETURN_ERROR(fNotificationThread); 328 resume_thread(fNotificationThread); 329 RETURN_ERROR(error); 330} 331 332// _NotificationThreadEntry 333int32 334FileSystem::_NotificationThreadEntry(void* data) 335{ 336 return ((FileSystem*)data)->_NotificationThread(); 337} 338 339// _NotificationThread 340int32 341FileSystem::_NotificationThread() 342{ 343 // process the notification requests until the FS is deleted 344 while (!fTerminating) { 345 if (fNotificationPort->InitCheck() != B_OK) 346 return fNotificationPort->InitCheck(); 347 KernelRequestHandler handler(this, NO_REQUEST); 348 fNotificationPort->HandleRequests(&handler, NULL, 349 kNotificationRequestTimeout); 350 } 351 // We eat all remaining notification requests, so that they aren't 352 // presented to the file system, when it is mounted next time. 353 // TODO: We should probably use a special handler that sends an ack reply, 354 // but ignores the requests otherwise. 355 KernelRequestHandler handler(this, NO_REQUEST); 356 fNotificationPort->HandleRequests(&handler, NULL, 0); 357 return 0; 358} 359 360