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
33extern RPC::ServerManager* gRPCServerManager;
34extern RPC::ProgramData* CreateNFS4Server(RPC::Server* serv);
35
36
37FileSystem::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
57FileSystem::~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
81static InodeNames*
82GetInodeNames(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
144status_t
145FileSystem::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
270status_t
271FileSystem::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
294status_t
295FileSystem::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
363DoublyLinkedList<OpenState>&
364FileSystem::OpenFilesLock()
365{
366	CALLED();
367
368	mutex_lock(&fOpenLock);
369	return fOpenFiles;
370}
371
372
373void
374FileSystem::OpenFilesUnlock()
375{
376	CALLED();
377
378	mutex_unlock(&fOpenLock);
379}
380
381
382void
383FileSystem::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
397void
398FileSystem::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
412DoublyLinkedList<Delegation>&
413FileSystem::DelegationsLock()
414{
415	CALLED();
416
417	mutex_lock(&fDelegationLock);
418	return fDelegationList;
419}
420
421
422void
423FileSystem::DelegationsUnlock()
424{
425	CALLED();
426
427	mutex_unlock(&fDelegationLock);
428}
429
430
431void
432FileSystem::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
447void
448FileSystem::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
461Delegation*
462FileSystem::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
477status_t
478FileSystem::_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