1// UserlandFSServer.cpp
2
3#include <new>
4#include <stdio.h>
5#include <string.h>
6
7#include <Application.h>
8#include <cache.h>
9#include <Clipboard.h>
10#include <FindDirectory.h>
11#include <fsproto.h>
12#include <image.h>
13#include <Locker.h>
14#include <Path.h>
15
16#include "AutoLocker.h"
17#include "Compatibility.h"
18#include "Debug.h"
19#include "DispatcherDefs.h"
20#include "FSInfo.h"
21#include "KernelUserFileSystem.h"
22#include "RequestThread.h"
23#include "ServerDefs.h"
24#include "UserFileSystem.h"
25#include "UserlandFSServer.h"
26
27static const int32 kRequestThreadCount = 10;
28
29static const int32 kMaxBlockCacheBlocks = 16384;
30
31// constructor
32UserlandFSServer::UserlandFSServer(const char* signature)
33	: BApplication(signature),
34	  fAddOnImage(-1),
35	  fFileSystem(NULL),
36	  fNotificationRequestPort(NULL),
37	  fRequestThreads(NULL),
38	  fBlockCacheInitialized(false)
39{
40}
41
42// destructor
43UserlandFSServer::~UserlandFSServer()
44{
45	if (fRequestThreads) {
46		for (int32 i = 0; i < kRequestThreadCount; i++)
47			fRequestThreads[i].PrepareTermination();
48		for (int32 i = 0; i < kRequestThreadCount; i++)
49			fRequestThreads[i].Terminate();
50		delete[] fRequestThreads;
51	}
52	delete fNotificationRequestPort;
53	delete fFileSystem;
54	if (fBlockCacheInitialized)
55		shutdown_block_cache();
56	if (fAddOnImage >= 0)
57		unload_add_on(fAddOnImage);
58}
59
60// Init
61status_t
62UserlandFSServer::Init(const char* fileSystem)
63{
64	// get the add-on path
65	BPath addOnPath;
66	status_t error = find_directory(B_USER_ADDONS_DIRECTORY, &addOnPath);
67	if (error != B_OK)
68		RETURN_ERROR(error);
69	error = addOnPath.Append("userlandfs");
70	if (error != B_OK)
71		RETURN_ERROR(error);
72	error = addOnPath.Append(fileSystem);
73	if (error != B_OK)
74		RETURN_ERROR(error);
75	// load the add-on
76	fAddOnImage = load_add_on(addOnPath.Path());
77	if (fAddOnImage < 0)
78		RETURN_ERROR(fAddOnImage);
79	// get the symbols "fs_entry" and "api_version"
80	vnode_ops* fsOps;
81	error = get_image_symbol(fAddOnImage, "fs_entry", B_SYMBOL_TYPE_TEXT,
82		(void**)&fsOps);
83	if (error != B_OK)
84		RETURN_ERROR(error);
85	int32* apiVersion;
86	error = get_image_symbol(fAddOnImage, "api_version", B_SYMBOL_TYPE_DATA,
87		(void**)&apiVersion);
88	if (error != B_OK)
89		RETURN_ERROR(error);
90	// check api version
91	if (*apiVersion != B_CUR_FS_API_VERSION)
92		RETURN_ERROR(B_ERROR);
93	// create the file system
94	fFileSystem = new(nothrow) KernelUserFileSystem(fsOps);
95	if (!fileSystem)
96		RETURN_ERROR(B_NO_MEMORY);
97	// init the block cache
98	error = init_block_cache(kMaxBlockCacheBlocks, 0);
99	if (error != B_OK)
100		RETURN_ERROR(error);
101	fBlockCacheInitialized = true;
102	// create the notification request port
103	fNotificationRequestPort = new(nothrow) RequestPort(kRequestPortSize);
104	if (!fNotificationRequestPort)
105		RETURN_ERROR(B_NO_MEMORY);
106	error = fNotificationRequestPort->InitCheck();
107	if (error != B_OK)
108		RETURN_ERROR(error);
109	// now create the request threads
110	fRequestThreads = new(nothrow) RequestThread[kRequestThreadCount];
111	if (!fRequestThreads)
112		RETURN_ERROR(B_NO_MEMORY);
113	for (int32 i = 0; i < kRequestThreadCount; i++) {
114		error = fRequestThreads[i].Init(fFileSystem);
115		if (error != B_OK)
116			RETURN_ERROR(error);
117	}
118	// run the threads
119	for (int32 i = 0; i < kRequestThreadCount; i++)
120		fRequestThreads[i].Run();
121	// enter the debugger here, if desired
122	if (gServerSettings.ShallEnterDebugger())
123		debugger("File system ready to use.");
124	// finally register with the dispatcher
125	error = _RegisterWithDispatcher(fileSystem);
126	RETURN_ERROR(error);
127}
128
129// GetNotificationRequestPort
130RequestPort*
131UserlandFSServer::GetNotificationRequestPort()
132{
133	if (UserlandFSServer* server = dynamic_cast<UserlandFSServer*>(be_app))
134		return server->fNotificationRequestPort;
135	return NULL;
136}
137
138// GetFileSystem
139UserFileSystem*
140UserlandFSServer::GetFileSystem()
141{
142	if (UserlandFSServer* server = dynamic_cast<UserlandFSServer*>(be_app))
143		return server->fFileSystem;
144	return NULL;
145}
146
147// _RegisterWithDispatcher
148status_t
149UserlandFSServer::_RegisterWithDispatcher(const char* fsName)
150{
151	// get the dispatcher messenger from the clipboard
152	BMessenger messenger;
153	BClipboard clipboard(kUserlandFSDispatcherClipboardName);
154	if (AutoLocker<BClipboard> locker = clipboard) {
155		status_t error = B_OK;
156		if (BMessage* data = clipboard.Data()) {
157			error = data->FindMessenger("messenger", &messenger);
158			if (error != B_OK) {
159				ERROR(("No dispatcher messenger in clipboard.\n"));
160				return error;
161			}
162			if (!messenger.IsValid()) {
163				ERROR(("Found dispatcher messenger not valid.\n"));
164				return B_ERROR;
165			}
166		} else {
167			ERROR(("Failed to get clipboard data container\n"));
168			return B_ERROR;
169		}
170	} else {
171		ERROR(("Failed to lock the clipboard.\n"));
172		return B_ERROR;
173	}
174	// get the port infos
175	Port::Info infos[kRequestThreadCount + 1];
176	infos[0] = *fNotificationRequestPort->GetPortInfo();
177	for (int32 i = 0; i < kRequestThreadCount; i++)
178		infos[i + 1] = *fRequestThreads[i].GetPortInfo();
179	// init an FS info
180	FSInfo info;
181	status_t error = info.SetTo(fsName, infos, kRequestThreadCount + 1);
182	// prepare the message
183	BMessage message(UFS_REGISTER_FS);
184	if (error == B_OK)
185		error = message.AddInt32("team", Team());
186	if (error == B_OK)
187		error = info.Archive(&message);
188	// send the message
189	BMessage reply;
190	error = messenger.SendMessage(&message, &reply);
191	if (error == B_OK && reply.what != UFS_REGISTER_FS_ACK) {
192		ERROR(("FS registration failed.\n"));
193		error = B_ERROR;
194	}
195	return error;
196}
197
198