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