xref: /haiku/src/add-ons/kernel/file_systems/nfs4/FileSystem.cpp (revision 372b901dfeada686207d00bbcce456f748bbda12)
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