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