1 /* 2 * Copyright 2012 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Paweł Dziepak, pdziepak@quarnos.org 7 */ 8 9 10 #include "FileSystem.h" 11 12 #include <string.h> 13 14 #include <AutoDeleter.h> 15 #include <lock.h> 16 #include <util/Random.h> 17 18 #include "Request.h" 19 #include "RootInode.h" 20 21 22 extern RPC::ServerManager* gRPCServerManager; 23 extern RPC::ProgramData* CreateNFS4Server(RPC::Server* serv); 24 25 26 FileSystem::FileSystem(const MountConfiguration& configuration) 27 : 28 fOpenCount(0), 29 fOpenOwnerSequence(0), 30 fNamedAttrs(true), 31 fPath(NULL), 32 fRoot(NULL), 33 fServer(NULL), 34 fId(1), 35 fConfiguration(configuration) 36 { 37 fOpenOwner = get_random<uint64>(); 38 39 mutex_init(&fOpenOwnerLock, NULL); 40 mutex_init(&fOpenLock, NULL); 41 mutex_init(&fDelegationLock, NULL); 42 mutex_init(&fCreateFileLock, NULL); 43 } 44 45 46 FileSystem::~FileSystem() 47 { 48 if (fServer != NULL) { 49 NFS4Server* server 50 = reinterpret_cast<NFS4Server*>(fServer->PrivateData()); 51 if (server != NULL) 52 server->RemoveFileSystem(this); 53 } 54 55 mutex_destroy(&fDelegationLock); 56 mutex_destroy(&fOpenLock); 57 mutex_destroy(&fOpenOwnerLock); 58 mutex_destroy(&fCreateFileLock); 59 60 if (fPath != NULL) { 61 for (uint32 i = 0; fPath[i] != NULL; i++) 62 free(const_cast<char*>(fPath[i])); 63 } 64 delete[] fPath; 65 66 delete fRoot; 67 } 68 69 70 static InodeNames* 71 GetInodeNames(const char** root, const char* _path) 72 { 73 ASSERT(_path != NULL); 74 75 int i; 76 char* path = strdup(_path); 77 if (path == NULL) 78 return NULL; 79 MemoryDeleter _(path); 80 81 if (root != NULL) { 82 for (i = 0; root[i] != NULL; i++) { 83 char* pathEnd = strchr(path, '/'); 84 if (pathEnd == path) { 85 path++; 86 i--; 87 continue; 88 } 89 90 if (pathEnd == NULL) { 91 path = NULL; 92 break; 93 } else 94 path = pathEnd + 1; 95 } 96 } 97 98 InodeNames* names = NULL; 99 if (path == NULL) { 100 names = new InodeNames; 101 if (names == NULL) 102 return NULL; 103 104 names->AddName(NULL, ""); 105 return names; 106 } 107 108 do { 109 char* pathEnd = strchr(path, '/'); 110 if (pathEnd != NULL) 111 *pathEnd = '\0'; 112 113 InodeNames* name = new InodeNames; 114 if (name == NULL) { 115 delete names; 116 return NULL; 117 } 118 119 name->AddName(names, path); 120 names = name; 121 if (pathEnd == NULL) 122 break; 123 124 path = pathEnd + 1; 125 } while (*path != '\0'); 126 127 return names; 128 } 129 130 131 status_t 132 FileSystem::Mount(FileSystem** _fs, RPC::Server* serv, const char* serverName, 133 const char* fsPath, dev_t id, const MountConfiguration& configuration) 134 { 135 ASSERT(_fs != NULL); 136 ASSERT(serv != NULL); 137 ASSERT(fsPath != NULL); 138 139 FileSystem* fs = new(std::nothrow) FileSystem(configuration); 140 if (fs == NULL) 141 return B_NO_MEMORY; 142 ObjectDeleter<FileSystem> fsDeleter(fs); 143 144 Request request(serv, fs); 145 RequestBuilder& req = request.Builder(); 146 147 req.PutRootFH(); 148 149 uint32 lookupCount = 0; 150 status_t result = _ParsePath(req, lookupCount, fsPath); 151 if (result != B_OK) 152 return result; 153 154 req.GetFH(); 155 req.Access(); 156 157 Attribute attr[] = { FATTR4_SUPPORTED_ATTRS, FATTR4_FH_EXPIRE_TYPE, 158 FATTR4_FSID, FATTR4_FS_LOCATIONS }; 159 req.GetAttr(attr, sizeof(attr) / sizeof(Attribute)); 160 161 result = request.Send(); 162 if (result != B_OK) 163 return result; 164 165 ReplyInterpreter& reply = request.Reply(); 166 167 reply.PutRootFH(); 168 169 for (uint32 i = 0; i < lookupCount; i++) 170 reply.LookUp(); 171 172 FileHandle fh; 173 reply.GetFH(&fh); 174 175 uint32 allowed; 176 result = reply.Access(NULL, &allowed); 177 if (result != B_OK) 178 return result; 179 else if ((allowed & (ACCESS4_READ | ACCESS4_LOOKUP)) 180 != (ACCESS4_READ | ACCESS4_LOOKUP)) 181 return B_PERMISSION_DENIED; 182 183 AttrValue* values; 184 uint32 count; 185 result = reply.GetAttr(&values, &count); 186 if (result != B_OK || count < 2) 187 return result; 188 189 // FATTR4_SUPPORTED_ATTRS is mandatory 190 memcpy(fs->fSupAttrs, &values[0].fData.fValue64, sizeof(fs->fSupAttrs)); 191 192 // FATTR4_FH_EXPIRE_TYPE is mandatory 193 fs->fExpireType = values[1].fData.fValue32; 194 195 // FATTR4_FSID is mandatory 196 FileSystemId* fsid 197 = reinterpret_cast<FileSystemId*>(values[2].fData.fPointer); 198 199 if (count == 4 && values[3].fAttribute == FATTR4_FS_LOCATIONS) { 200 FSLocations* locs 201 = reinterpret_cast<FSLocations*>(values[3].fData.fLocations); 202 203 fs->fPath = locs->fRootPath; 204 locs->fRootPath = NULL; 205 } else 206 fs->fPath = NULL; 207 208 FileInfo fi; 209 210 fs->fServer = serv; 211 fs->fDevId = id; 212 fs->fFsId = *fsid; 213 214 fi.fHandle = fh; 215 216 fi.fNames = GetInodeNames(fs->fPath, fsPath); 217 if (fi.fNames == NULL) { 218 delete[] values; 219 return B_NO_MEMORY; 220 } 221 fi.fNames->fHandle = fh; 222 223 delete[] values; 224 225 Inode* inode; 226 result = Inode::CreateInode(fs, fi, &inode); 227 if (result != B_OK) 228 return result; 229 RootInode* rootInode = reinterpret_cast<RootInode*>(inode); 230 fs->fRoot = rootInode; 231 232 char* fsName = strdup(fsPath); 233 if (fsName == NULL) 234 return B_NO_MEMORY; 235 for (int i = strlen(fsName) - 1; i >= 0 && fsName[i] == '/'; i--) 236 fsName[i] = '\0'; 237 238 char* name = strrchr(fsName, '/'); 239 if (name != NULL) 240 rootInode->SetName(name + 1); 241 else if (fsName[0] != '\0') 242 rootInode->SetName(fsName); 243 else 244 rootInode->SetName(serverName); 245 free(fsName); 246 247 fs->NFSServer()->AddFileSystem(fs); 248 *_fs = fs; 249 250 fsDeleter.Detach(); 251 return B_OK; 252 } 253 254 255 status_t 256 FileSystem::GetInode(ino_t id, Inode** _inode) 257 { 258 ASSERT(_inode != NULL); 259 260 FileInfo fi; 261 status_t result = fInoIdMap.GetFileInfo(&fi, id); 262 ASSERT(result != B_ENTRY_NOT_FOUND); 263 264 if (result != B_OK) 265 return result; 266 267 Inode* inode; 268 result = Inode::CreateInode(this, fi, &inode); 269 if (result != B_OK) 270 return result; 271 272 *_inode = inode; 273 return B_OK; 274 } 275 276 277 status_t 278 FileSystem::Migrate(const RPC::Server* serv) 279 { 280 ASSERT(serv != NULL); 281 282 MutexLocker _(fOpenLock); 283 if (serv != fServer) 284 return B_OK; 285 286 if (!fRoot->ProbeMigration()) 287 return B_OK; 288 289 AttrValue* values; 290 status_t result = fRoot->GetLocations(&values); 291 if (result != B_OK) 292 return result; 293 294 FSLocations* locs 295 = reinterpret_cast<FSLocations*>(values[0].fData.fLocations); 296 297 RPC::Server* server = fServer; 298 for (uint32 i = 0; i < locs->fCount; i++) { 299 for (uint32 j = 0; j < locs->fLocations[i].fCount; j++) { 300 AddressResolver resolver(locs->fLocations[i].fLocations[j]); 301 302 if (gRPCServerManager->Acquire(&fServer, &resolver, 303 CreateNFS4Server) == B_OK) { 304 305 if (fPath != NULL) { 306 for (uint32 i = 0; fPath[i] != NULL; i++) 307 free(const_cast<char*>(fPath[i])); 308 } 309 delete[] fPath; 310 311 fPath = locs->fLocations[i].fRootPath; 312 locs->fLocations[i].fRootPath = NULL; 313 314 if (fPath == NULL) { 315 gRPCServerManager->Release(fServer); 316 fServer = server; 317 318 delete[] values; 319 return B_NO_MEMORY; 320 } 321 322 break; 323 } 324 } 325 } 326 327 delete[] values; 328 329 if (server == fServer) { 330 gRPCServerManager->Release(server); 331 return B_ERROR; 332 } 333 334 NFS4Server* old = reinterpret_cast<NFS4Server*>(server->PrivateData()); 335 old->RemoveFileSystem(this); 336 NFSServer()->AddFileSystem(this); 337 338 gRPCServerManager->Release(server); 339 340 return B_OK; 341 } 342 343 344 DoublyLinkedList<OpenState>& 345 FileSystem::OpenFilesLock() 346 { 347 mutex_lock(&fOpenLock); 348 return fOpenFiles; 349 } 350 351 352 void 353 FileSystem::OpenFilesUnlock() 354 { 355 mutex_unlock(&fOpenLock); 356 } 357 358 359 void 360 FileSystem::AddOpenFile(OpenState* state) 361 { 362 ASSERT(state != NULL); 363 364 MutexLocker _(fOpenLock); 365 366 fOpenFiles.InsertBefore(fOpenFiles.Head(), state); 367 368 NFSServer()->IncUsage(); 369 } 370 371 372 void 373 FileSystem::RemoveOpenFile(OpenState* state) 374 { 375 ASSERT(state != NULL); 376 377 MutexLocker _(fOpenLock); 378 379 fOpenFiles.Remove(state); 380 381 NFSServer()->DecUsage(); 382 } 383 384 385 DoublyLinkedList<Delegation>& 386 FileSystem::DelegationsLock() 387 { 388 mutex_lock(&fDelegationLock); 389 return fDelegationList; 390 } 391 392 393 void 394 FileSystem::DelegationsUnlock() 395 { 396 mutex_unlock(&fDelegationLock); 397 } 398 399 400 void 401 FileSystem::AddDelegation(Delegation* delegation) 402 { 403 ASSERT(delegation != NULL); 404 405 MutexLocker _(fDelegationLock); 406 407 fDelegationList.InsertBefore(fDelegationList.Head(), delegation); 408 409 fHandleToDelegation.Remove(delegation->fInfo.fHandle); 410 fHandleToDelegation.Insert(delegation->fInfo.fHandle, delegation); 411 } 412 413 414 void 415 FileSystem::RemoveDelegation(Delegation* delegation) 416 { 417 ASSERT(delegation != NULL); 418 419 MutexLocker _(fDelegationLock); 420 421 fDelegationList.Remove(delegation); 422 fHandleToDelegation.Remove(delegation->fInfo.fHandle); 423 } 424 425 426 Delegation* 427 FileSystem::GetDelegation(const FileHandle& handle) 428 { 429 MutexLocker _(fDelegationLock); 430 431 AVLTreeMap<FileHandle, Delegation*>::Iterator it; 432 it = fHandleToDelegation.Find(handle); 433 if (!it.HasCurrent()) 434 return NULL; 435 436 return it.Current(); 437 } 438 439 440 status_t 441 FileSystem::_ParsePath(RequestBuilder& req, uint32& count, const char* _path) 442 { 443 ASSERT(_path != NULL); 444 445 char* path = strdup(_path); 446 if (path == NULL) 447 return B_NO_MEMORY; 448 449 char* pathStart = path; 450 char* pathEnd; 451 452 while (pathStart != NULL) { 453 pathEnd = strchr(pathStart, '/'); 454 if (pathEnd != NULL) 455 *pathEnd = '\0'; 456 457 if (pathEnd != pathStart) { 458 if (!strcmp(pathStart, "..")) { 459 req.LookUpUp(); 460 count++; 461 } else if (strcmp(pathStart, ".")) { 462 req.LookUp(pathStart); 463 count++; 464 } 465 } 466 467 if (pathEnd != NULL && pathEnd[1] != '\0') 468 pathStart = pathEnd + 1; 469 else 470 pathStart = NULL; 471 } 472 free(path); 473 474 return B_OK; 475 } 476 477