1// RootVolume.cpp
2
3#include "RootVolume.h"
4
5#include <new>
6
7#include <AutoLocker.h>
8
9#include "Compatibility.h"
10#include "DebugSupport.h"
11#include "ExtendedServerInfo.h"
12#include "NetAddress.h"
13#include "netfs_ioctl.h"
14#include "ServerVolume.h"
15#include "TaskManager.h"
16#include "VolumeManager.h"
17#include "VolumeSupport.h"
18
19static const int32 kOptimalIOSize = 64 * 1024;
20static const char* kFSName = "netfs";
21
22// constructor
23RootVolume::RootVolume(VolumeManager* volumeManager)
24	: VirtualVolume(volumeManager)
25{
26}
27
28// destructor
29RootVolume::~RootVolume()
30{
31}
32
33// Init
34status_t
35RootVolume::Init()
36{
37	status_t error = VirtualVolume::Init("Network");
38	if (error != B_OK)
39		return error;
40
41	// create and init the server manager
42	fServerManager = new(std::nothrow) ServerManager(this);
43	if (!fServerManager)
44		RETURN_ERROR(B_NO_MEMORY);
45	error = fServerManager->Init();
46	if (error != B_OK)
47		RETURN_ERROR(error);
48
49	return B_OK;
50}
51
52// Uninit
53void
54RootVolume::Uninit()
55{
56	// delete the server manager
57	delete fServerManager;
58	fServerManager = NULL;
59
60	VirtualVolume::Uninit();
61}
62
63// PrepareToUnmount
64void
65RootVolume::PrepareToUnmount()
66{
67	VirtualVolume::PrepareToUnmount();
68}
69
70
71// #pragma mark -
72// #pragma mark ----- FS -----
73
74// Mount
75status_t
76RootVolume::Mount(const char* device, uint32 flags, const char* parameters,
77	int32 len)
78{
79	status_t error = NewVNode(fRootNode->GetID(), fRootNode);
80	if (error != B_OK)
81		RETURN_ERROR(error);
82
83	// start the server manager
84	fServerManager->Run();
85
86	return B_OK;
87}
88
89// Unmount
90status_t
91RootVolume::Unmount()
92{
93	Uninit();
94	return B_OK;
95}
96
97// Sync
98status_t
99RootVolume::Sync()
100{
101	return B_BAD_VALUE;
102}
103
104// ReadFSStat
105status_t
106RootVolume::ReadFSStat(fs_info* info)
107{
108	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_MIME | B_FS_HAS_ATTR
109		| B_FS_IS_SHARED | B_FS_HAS_QUERY;
110	if (fVolumeManager->GetMountFlags() & B_MOUNT_READ_ONLY)
111		info->flags |= B_FS_IS_READONLY;
112	info->block_size = 1024;
113	info->io_size = kOptimalIOSize;
114	info->total_blocks = 0;	// TODO: We could at least fill this in.
115	info->free_blocks = LONGLONG_MAX / info->block_size;
116		// keep the Tracker happy
117	strcpy(info->device_name, "");
118	strcpy(info->volume_name, GetName());
119	strcpy(info->fsh_name, kFSName);
120	return B_OK;
121}
122
123// WriteFSStat
124status_t
125RootVolume::WriteFSStat(struct fs_info* info, int32 mask)
126{
127	// TODO: Allow editing the volume name.
128	return B_BAD_VALUE;
129}
130
131// #pragma mark -
132// #pragma mark ----- files -----
133
134// IOCtl
135status_t
136RootVolume::IOCtl(Node* node, void* cookie, int cmd, void* buffer,
137	size_t bufferSize)
138{
139	if (node != fRootNode)
140		return B_BAD_VALUE;
141
142	switch (cmd) {
143		case NET_FS_IOCTL_ADD_SERVER:
144		{
145			// check the parameters
146			if (!buffer)
147				return B_BAD_VALUE;
148			netfs_ioctl_add_server* params = (netfs_ioctl_add_server*)buffer;
149			int32 serverNameLen = strnlen(params->serverName,
150				sizeof(params->serverName));
151			if (serverNameLen == 0
152				|| serverNameLen == sizeof(params->serverName)) {
153				return B_BAD_VALUE;
154			}
155			PRINT("RootVolume::IOCtl(): NET_FS_IOCTL_ADD_SERVER: "
156				"`%s'\n", params->serverName);
157
158			// get the server address
159			NetAddress netAddress;
160			NetAddressResolver resolver;
161			status_t error = resolver.GetHostAddress(params->serverName, &netAddress);
162			if (error != B_OK)
163				return error;
164
165			// ask the server manager to add the server
166			return fServerManager->AddServer(netAddress);
167		}
168		case NET_FS_IOCTL_REMOVE_SERVER:
169		{
170			// check the parameters
171			if (!buffer)
172				return B_BAD_VALUE;
173			netfs_ioctl_remove_server* params
174				= (netfs_ioctl_remove_server*)buffer;
175			int32 serverNameLen = strnlen(params->serverName,
176				sizeof(params->serverName));
177			if (serverNameLen == 0
178				|| serverNameLen == sizeof(params->serverName)) {
179				return B_BAD_VALUE;
180			}
181			PRINT("RootVolume::IOCtl(): NET_FS_IOCTL_REMOVE_SERVER:"
182				" `%s'\n", params->serverName);
183
184			// get the server volume
185			ServerVolume* serverVolume = _GetServerVolume(params->serverName);
186			if (!serverVolume)
187				return B_ENTRY_NOT_FOUND;
188			VolumePutter volumePutter(serverVolume);
189
190			// ask the server manager to remove the server
191			fServerManager->RemoveServer(serverVolume->GetServerAddress());
192
193			return B_OK;
194		}
195		default:
196			PRINT("RootVolume::IOCtl(): unknown ioctl: %d\n", cmd);
197			return B_BAD_VALUE;
198			break;
199	}
200	return B_BAD_VALUE;
201}
202
203
204// #pragma mark -
205
206// ServerAdded
207void
208RootVolume::ServerAdded(ExtendedServerInfo* serverInfo)
209{
210	PRINT("RootVolume::ServerAdded(%s)\n", serverInfo->GetServerName());
211	// check, if the server does already exist
212	ServerVolume* serverVolume = _GetServerVolume(serverInfo->GetAddress());
213	if (serverVolume) {
214		WARN("RootVolume::ServerAdded(): WARNING: ServerVolume does "
215			"already exist.\n");
216		serverVolume->PutVolume();
217		return;
218	}
219
220	AutoLocker<Locker> locker(fLock);
221
222	// get a unique name for the server
223	char serverName[B_FILE_NAME_LENGTH];
224	status_t error = GetUniqueEntryName(serverInfo->GetServerName(),
225		serverName);
226	if (error != B_OK)
227		return;
228
229	// create a server volume
230	serverVolume = new(std::nothrow) ServerVolume(fVolumeManager, serverInfo);
231	if (!serverVolume)
232		return;
233	error = serverVolume->Init(serverName);
234	if (error != B_OK) {
235		delete serverVolume;
236		return;
237	}
238
239	// add the volume to the volume manager
240	error = fVolumeManager->AddVolume(serverVolume);
241	if (error != B_OK) {
242		delete serverVolume;
243		return;
244	}
245	VolumePutter volumePutter(serverVolume);
246
247	// add the volume to us
248	locker.Unlock();
249	error = AddChildVolume(serverVolume);
250	if (error != B_OK) {
251		serverVolume->SetUnmounting(true);
252		return;
253	}
254}
255
256// ServerUpdated
257void
258RootVolume::ServerUpdated(ExtendedServerInfo* oldInfo,
259	ExtendedServerInfo* newInfo)
260{
261	PRINT("RootVolume::ServerUpdated(%s)\n", newInfo->GetServerName());
262	// get the volume
263	ServerVolume* serverVolume = _GetServerVolume(newInfo->GetAddress());
264	if (!serverVolume)
265		return;
266
267	// set the new server info
268	VolumePutter _(serverVolume);
269	serverVolume->SetServerInfo(newInfo);
270}
271
272// ServerRemoved
273void
274RootVolume::ServerRemoved(ExtendedServerInfo* serverInfo)
275{
276	PRINT("RootVolume::ServerRemoved(%s)\n", serverInfo->GetServerName());
277	// get the volume
278	ServerVolume* serverVolume = _GetServerVolume(serverInfo->GetAddress());
279	if (!serverVolume)
280		return;
281
282	// set it to unmounting
283	VolumePutter _(serverVolume);
284	serverVolume->SetUnmounting(true);
285}
286
287
288// #pragma mark -
289
290// _GetServerVolume
291ServerVolume*
292RootVolume::_GetServerVolume(const char* name)
293{
294	Volume* volume = GetChildVolume(name);
295	if (!volume)
296		return NULL;
297	if (ServerVolume* serverVolume = dynamic_cast<ServerVolume*>(volume))
298		return serverVolume;
299	fVolumeManager->PutVolume(volume);
300	return NULL;
301}
302
303// _GetServerVolume
304ServerVolume*
305RootVolume::_GetServerVolume(const NetAddress& address)
306{
307	AutoLocker<Locker> locker(fLock);
308
309	// init a directory iterator
310	VirtualDirIterator iterator;
311	iterator.SetDirectory(fRootNode, true);
312
313	// iterate through the directory
314	const char* name;
315	Node* node;
316	while (iterator.GetCurrentEntry(&name, &node)) {
317		iterator.NextEntry();
318		ServerVolume* volume = dynamic_cast<ServerVolume*>(node->GetVolume());
319		if (volume && volume->GetServerAddress().GetIP() == address.GetIP()) {
320			return dynamic_cast<ServerVolume*>(
321				fVolumeManager->GetVolume(node->GetID()));
322		}
323	}
324
325	return NULL;
326}
327
328