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