1/*
2 * Copyright 2001-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "UserlandFSServer.h"
7
8#include <new>
9#include <stdio.h>
10#include <string.h>
11
12#include <Application.h>
13#include <Clipboard.h>
14#include <Entry.h>
15#include <FindDirectory.h>
16#include <fs_interface.h>
17#include <Locker.h>
18#include <Path.h>
19#include <PathFinder.h>
20#include <StringList.h>
21
22#include <image_private.h>
23
24#include "AutoDeleter.h"
25#include "AutoLocker.h"
26#include "Compatibility.h"
27#include "Debug.h"
28#include "FileSystem.h"
29#include "RequestThread.h"
30#include "ServerDefs.h"
31#include "UserlandFSDefs.h"
32
33
34static const int32 kRequestThreadCount = 10;
35
36
37// constructor
38UserlandFSServer::UserlandFSServer(const char* signature)
39	: BApplication(signature),
40	  fAddOnImage(-1),
41	  fFileSystem(NULL),
42	  fNotificationRequestPort(NULL),
43	  fRequestThreads(NULL)
44{
45}
46
47
48// destructor
49UserlandFSServer::~UserlandFSServer()
50{
51	if (fRequestThreads) {
52		for (int32 i = 0; i < kRequestThreadCount; i++)
53			fRequestThreads[i].PrepareTermination();
54		for (int32 i = 0; i < kRequestThreadCount; i++)
55			fRequestThreads[i].Terminate();
56		delete[] fRequestThreads;
57	}
58	delete fNotificationRequestPort;
59	delete fFileSystem;
60	if (fAddOnImage >= 0)
61		unload_add_on(fAddOnImage);
62}
63
64
65// Init
66status_t
67UserlandFSServer::Init(const char* fileSystem, port_id port)
68{
69	// get the add-on path
70	BPathFinder pathFinder;
71	BStringList paths;
72	status_t error = pathFinder.FindPaths(B_FIND_PATH_ADD_ONS_DIRECTORY,
73		"userlandfs", B_FIND_PATH_EXISTING_ONLY, paths);
74	if (error != B_OK)
75		RETURN_ERROR(error);
76
77	BPath addOnPath;
78	for (int index = 0; index < paths.CountStrings(); index++) {
79		error = addOnPath.SetTo(paths.StringAt(index));
80		if (error != B_OK)
81			RETURN_ERROR(error);
82
83		error = addOnPath.Append(fileSystem);
84		if (error != B_OK)
85			RETURN_ERROR(error);
86
87		BEntry entry(addOnPath.Path());
88		if (entry.Exists())
89			break;
90		else
91			addOnPath.Unset();
92	}
93
94	if (addOnPath.InitCheck() != B_OK)
95		RETURN_ERROR(B_BAD_VALUE);
96
97	// load the add-on
98	fAddOnImage = load_add_on(addOnPath.Path());
99	if (fAddOnImage < 0)
100		RETURN_ERROR(fAddOnImage);
101
102	// Get the FS creation function -- the add-on links against one of our
103	// libraries exporting that function, so we search recursively.
104	union {
105		void* address;
106		status_t (*function)(const char*, image_id, FileSystem**);
107	} createFSFunction;
108	error = get_image_symbol_etc(fAddOnImage, "userlandfs_create_file_system",
109		B_SYMBOL_TYPE_TEXT, true, NULL, &createFSFunction.address);
110	if (error != B_OK)
111		RETURN_ERROR(error);
112
113	// create the FileSystem interface
114	error = createFSFunction.function(fileSystem, fAddOnImage, &fFileSystem);
115	if (error != B_OK)
116		RETURN_ERROR(error);
117
118	// create the notification request port
119	fNotificationRequestPort = new(std::nothrow) RequestPort(kRequestPortSize);
120	if (!fNotificationRequestPort)
121		RETURN_ERROR(B_NO_MEMORY);
122	error = fNotificationRequestPort->InitCheck();
123	if (error != B_OK)
124		RETURN_ERROR(error);
125
126	// now create the request threads
127	fRequestThreads = new(std::nothrow) RequestThread[kRequestThreadCount];
128	if (!fRequestThreads)
129		RETURN_ERROR(B_NO_MEMORY);
130	for (int32 i = 0; i < kRequestThreadCount; i++) {
131		error = fRequestThreads[i].Init(fFileSystem);
132		if (error != B_OK)
133			RETURN_ERROR(error);
134	}
135
136	// run the threads
137	for (int32 i = 0; i < kRequestThreadCount; i++)
138		fRequestThreads[i].Run();
139
140	// enter the debugger here, if desired
141	if (gServerSettings.ShallEnterDebugger())
142		debugger("File system ready to use.");
143
144	// finally announce our existence
145	error = _Announce(fileSystem, port);
146	RETURN_ERROR(error);
147}
148
149
150// GetNotificationRequestPort
151RequestPort*
152UserlandFSServer::GetNotificationRequestPort()
153{
154	if (UserlandFSServer* server = dynamic_cast<UserlandFSServer*>(be_app))
155		return server->fNotificationRequestPort;
156	return NULL;
157}
158
159
160// GetFileSystem
161FileSystem*
162UserlandFSServer::GetFileSystem()
163{
164	if (UserlandFSServer* server = dynamic_cast<UserlandFSServer*>(be_app))
165		return server->fFileSystem;
166	return NULL;
167}
168
169
170// _Announce
171status_t
172UserlandFSServer::_Announce(const char* fsName, port_id port)
173{
174	// if not given, create a port
175	if (port < 0) {
176		char portName[B_OS_NAME_LENGTH];
177		snprintf(portName, sizeof(portName), "_userlandfs_%s", fsName);
178
179		port = create_port(1, portName);
180		if (port < 0)
181			RETURN_ERROR(port);
182	}
183
184	// allocate stack space for the FS initialization info
185	const size_t bufferSize = sizeof(fs_init_info)
186		+ sizeof(Port::Info) * (kRequestThreadCount + 1);
187	char buffer[bufferSize];
188	fs_init_info* info = (fs_init_info*)buffer;
189
190	// get the port infos
191	info->portInfoCount = kRequestThreadCount + 1;
192	info->portInfos[0] = *fNotificationRequestPort->GetPortInfo();
193	for (int32 i = 0; i < kRequestThreadCount; i++)
194		info->portInfos[i + 1] = *fRequestThreads[i].GetPortInfo();
195
196	// FS capabilities
197	fFileSystem->GetCapabilities(info->capabilities);
198	info->clientFSType = fFileSystem->GetClientFSType();
199
200	// send the info to our port
201	RETURN_ERROR(write_port(port, 0, buffer, bufferSize));
202}
203