1/*
2 * Copyright 2007-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include "HaikuKernelFileSystem.h"
7
8#include <string.h>
9
10#include <new>
11
12#include <fs_interface.h>
13
14#include <AutoLocker.h>
15
16#include <block_cache.h>
17#include <condition_variable.h>
18#include <file_cache.h>
19
20#include "HaikuKernelIORequest.h"
21#include "HaikuKernelVolume.h"
22
23
24// IORequestHashDefinition
25struct HaikuKernelFileSystem::IORequestHashDefinition {
26	typedef int32					KeyType;
27	typedef	HaikuKernelIORequest	ValueType;
28
29	size_t HashKey(int32 key) const
30		{ return key; }
31	size_t Hash(const HaikuKernelIORequest* value) const
32		{ return value->id; }
33	bool Compare(int32 key, const HaikuKernelIORequest* value) const
34		{ return value->id == key; }
35	HaikuKernelIORequest*& GetLink(HaikuKernelIORequest* value) const
36			{ return value->hashLink; }
37};
38
39
40// IORequestTable
41struct HaikuKernelFileSystem::IORequestTable
42	: public BOpenHashTable<IORequestHashDefinition> {
43	typedef int32					KeyType;
44	typedef	HaikuKernelIORequest	ValueType;
45
46	size_t HashKey(int32 key) const
47		{ return key; }
48	size_t Hash(const HaikuKernelIORequest* value) const
49		{ return value->id; }
50	bool Compare(int32 key, const HaikuKernelIORequest* value) const
51		{ return value->id == key; }
52	HaikuKernelIORequest*& GetLink(HaikuKernelIORequest* value) const
53			{ return value->hashLink; }
54};
55
56
57// NodeCapabilitiesHashDefinition
58struct HaikuKernelFileSystem::NodeCapabilitiesHashDefinition {
59	typedef fs_vnode_ops*					KeyType;
60	typedef	HaikuKernelNode::Capabilities	ValueType;
61
62	size_t HashKey(fs_vnode_ops* key) const
63		{ return (size_t)(addr_t)key; }
64	size_t Hash(const ValueType* value) const
65		{ return HashKey(value->ops); }
66	bool Compare(fs_vnode_ops* key, const ValueType* value) const
67		{ return value->ops == key; }
68	ValueType*& GetLink(ValueType* value) const
69		{ return value->hashLink; }
70};
71
72
73// NodeCapabilitiesTable
74struct HaikuKernelFileSystem::NodeCapabilitiesTable
75	: public BOpenHashTable<NodeCapabilitiesHashDefinition> {
76};
77
78
79// constructor
80HaikuKernelFileSystem::HaikuKernelFileSystem(const char* fsName,
81	file_system_module_info* fsModule)
82	:
83	FileSystem(fsName),
84	fFSModule(fsModule),
85	fIORequests(NULL),
86	fNodeCapabilities(NULL),
87	fLock("HaikuKernelFileSystem")
88{
89	_InitCapabilities();
90}
91
92
93// destructor
94HaikuKernelFileSystem::~HaikuKernelFileSystem()
95{
96	// call the kernel module uninitialization
97	if (fFSModule->info.std_ops)
98		fFSModule->info.std_ops(B_MODULE_UNINIT);
99
100	delete fIORequests;
101	delete fNodeCapabilities;
102
103// TODO: Call the cleanup methods (condition vars, block cache)!
104}
105
106
107// Init
108status_t
109HaikuKernelFileSystem::Init()
110{
111	status_t error = fLock.InitCheck();
112	if (error != B_OK)
113		RETURN_ERROR(error);
114
115	// init condition variables
116	condition_variable_init();
117// TODO: Call the cleanup methods, if something goes wrong!
118
119	// init block cache
120	error = block_cache_init();
121	if (error != B_OK)
122		RETURN_ERROR(error);
123
124	// init file map
125	error = file_map_init();
126	if (error != B_OK)
127		RETURN_ERROR(error);
128
129	// create I/O request map
130	fIORequests = new(std::nothrow) IORequestTable;
131	if (fIORequests == NULL)
132		RETURN_ERROR(B_NO_MEMORY);
133
134	error = fIORequests->Init();
135	if (error != B_OK)
136		RETURN_ERROR(error);
137
138	// create the node capabilites map
139	fNodeCapabilities = new(std::nothrow) NodeCapabilitiesTable;
140	if (fNodeCapabilities == NULL)
141		RETURN_ERROR(B_NO_MEMORY);
142
143	error = fNodeCapabilities->Init();
144	if (error != B_OK)
145		RETURN_ERROR(error);
146
147	// call the kernel module initialization (if any)
148	if (!fFSModule->info.std_ops)
149		return B_OK;
150
151	error = fFSModule->info.std_ops(B_MODULE_INIT);
152	if (error != B_OK)
153		RETURN_ERROR(error);
154
155	return B_OK;
156}
157
158
159// CreateVolume
160status_t
161HaikuKernelFileSystem::CreateVolume(Volume** _volume, dev_t id)
162{
163	// check initialization and parameters
164	if (!fFSModule || !_volume)
165		return B_BAD_VALUE;
166
167	// create and init the volume
168	HaikuKernelVolume* volume
169		= new(std::nothrow) HaikuKernelVolume(this, id, fFSModule);
170	if (!volume)
171		return B_NO_MEMORY;
172
173	status_t error = volume->Init();
174	if (error != B_OK) {
175		delete volume;
176		return error;
177	}
178
179	*_volume = volume;
180	return B_OK;
181}
182
183
184// DeleteVolume
185status_t
186HaikuKernelFileSystem::DeleteVolume(Volume* volume)
187{
188	if (!volume || !dynamic_cast<HaikuKernelVolume*>(volume))
189		return B_BAD_VALUE;
190	delete volume;
191	return B_OK;
192}
193
194
195// AddIORequest
196status_t
197HaikuKernelFileSystem::AddIORequest(HaikuKernelIORequest* request)
198{
199	AutoLocker<Locker> _(fLock);
200
201	// check, if a request with that ID is already in the map
202	if (fIORequests->Lookup(request->id) != NULL)
203		RETURN_ERROR(B_BAD_VALUE);
204
205	fIORequests->Insert(request);
206	return B_OK;
207}
208
209
210// GetIORequest
211HaikuKernelIORequest*
212HaikuKernelFileSystem::GetIORequest(int32 requestID)
213{
214	AutoLocker<Locker> _(fLock);
215
216	HaikuKernelIORequest* request = fIORequests->Lookup(requestID);
217	if (request != NULL)
218		request->refCount++;
219
220	return request;
221}
222
223
224// PutIORequest
225void
226HaikuKernelFileSystem::PutIORequest(HaikuKernelIORequest* request,
227	int32 refCount)
228{
229	AutoLocker<Locker> locker(fLock);
230
231	if ((request->refCount -= refCount) <= 0) {
232		fIORequests->Remove(request);
233		locker.Unlock();
234		delete request;
235	}
236}
237
238
239// GetVNodeCapabilities
240HaikuKernelNode::Capabilities*
241HaikuKernelFileSystem::GetNodeCapabilities(fs_vnode_ops* ops)
242{
243	AutoLocker<Locker> locker(fLock);
244
245	// check whether the ops are already known
246	HaikuKernelNode::Capabilities* capabilities
247		= fNodeCapabilities->Lookup(ops);
248	if (capabilities != NULL) {
249		capabilities->refCount++;
250		return capabilities;
251	}
252
253	// get the capabilities implied by the ops vector
254	FSVNodeCapabilities nodeCapabilities;
255	_InitNodeCapabilities(ops, nodeCapabilities);
256
257	// create a new object
258	capabilities = new(std::nothrow) HaikuKernelNode::Capabilities(ops,
259		nodeCapabilities);
260	if (capabilities == NULL)
261		return NULL;
262
263	fNodeCapabilities->Insert(capabilities);
264
265	return capabilities;
266}
267
268
269// PutVNodeCapabilities
270void
271HaikuKernelFileSystem::PutNodeCapabilities(
272	HaikuKernelNode::Capabilities* capabilities)
273{
274	AutoLocker<Locker> locker(fLock);
275
276	if (--capabilities->refCount == 0) {
277		fNodeCapabilities->Remove(capabilities);
278		delete capabilities;
279	}
280}
281
282
283// _InitCapabilities
284void
285HaikuKernelFileSystem::_InitCapabilities()
286{
287	fCapabilities.ClearAll();
288
289	// FS interface type
290	fClientFSType = CLIENT_FS_HAIKU_KERNEL;
291
292	// FS operations
293	fCapabilities.Set(FS_CAPABILITY_MOUNT, fFSModule->mount);
294}
295
296
297/*static*/ void
298HaikuKernelFileSystem::_InitNodeCapabilities(fs_vnode_ops* ops,
299	FSVNodeCapabilities& capabilities)
300{
301	capabilities.ClearAll();
302
303	// vnode operations
304	capabilities.Set(FS_VNODE_CAPABILITY_LOOKUP, ops->lookup);
305	capabilities.Set(FS_VNODE_CAPABILITY_GET_VNODE_NAME, ops->get_vnode_name);
306	capabilities.Set(FS_VNODE_CAPABILITY_PUT_VNODE, ops->put_vnode);
307	capabilities.Set(FS_VNODE_CAPABILITY_REMOVE_VNODE, ops->remove_vnode);
308
309	// asynchronous I/O
310	capabilities.Set(FS_VNODE_CAPABILITY_IO, ops->io);
311	capabilities.Set(FS_VNODE_CAPABILITY_CANCEL_IO, ops->cancel_io);
312
313	// cache file access
314	capabilities.Set(FS_VNODE_CAPABILITY_GET_FILE_MAP, ops->get_file_map);
315
316	// common operations
317	capabilities.Set(FS_VNODE_CAPABILITY_IOCTL, ops->ioctl);
318	capabilities.Set(FS_VNODE_CAPABILITY_SET_FLAGS, ops->set_flags);
319	capabilities.Set(FS_VNODE_CAPABILITY_SELECT, ops->select);
320	capabilities.Set(FS_VNODE_CAPABILITY_DESELECT, ops->deselect);
321	capabilities.Set(FS_VNODE_CAPABILITY_FSYNC, ops->fsync);
322
323	capabilities.Set(FS_VNODE_CAPABILITY_READ_SYMLINK, ops->read_symlink);
324	capabilities.Set(FS_VNODE_CAPABILITY_CREATE_SYMLINK, ops->create_symlink);
325
326	capabilities.Set(FS_VNODE_CAPABILITY_LINK, ops->link);
327	capabilities.Set(FS_VNODE_CAPABILITY_UNLINK, ops->unlink);
328	capabilities.Set(FS_VNODE_CAPABILITY_RENAME, ops->rename);
329
330	capabilities.Set(FS_VNODE_CAPABILITY_ACCESS, ops->access);
331	capabilities.Set(FS_VNODE_CAPABILITY_READ_STAT, ops->read_stat);
332	capabilities.Set(FS_VNODE_CAPABILITY_WRITE_STAT, ops->write_stat);
333
334	// file operations
335	capabilities.Set(FS_VNODE_CAPABILITY_CREATE, ops->create);
336	capabilities.Set(FS_VNODE_CAPABILITY_OPEN, ops->open);
337	capabilities.Set(FS_VNODE_CAPABILITY_CLOSE, ops->close);
338	capabilities.Set(FS_VNODE_CAPABILITY_FREE_COOKIE, ops->free_cookie);
339	capabilities.Set(FS_VNODE_CAPABILITY_READ, ops->read);
340	capabilities.Set(FS_VNODE_CAPABILITY_WRITE, ops->write);
341
342	// directory operations
343	capabilities.Set(FS_VNODE_CAPABILITY_CREATE_DIR, ops->create_dir);
344	capabilities.Set(FS_VNODE_CAPABILITY_REMOVE_DIR, ops->remove_dir);
345	capabilities.Set(FS_VNODE_CAPABILITY_OPEN_DIR, ops->open_dir);
346	capabilities.Set(FS_VNODE_CAPABILITY_CLOSE_DIR, ops->close_dir);
347	capabilities.Set(FS_VNODE_CAPABILITY_FREE_DIR_COOKIE, ops->free_dir_cookie);
348	capabilities.Set(FS_VNODE_CAPABILITY_READ_DIR, ops->read_dir);
349	capabilities.Set(FS_VNODE_CAPABILITY_REWIND_DIR, ops->rewind_dir);
350
351	// attribute directory operations
352	capabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR_DIR, ops->open_attr_dir);
353	capabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR_DIR, ops->close_attr_dir);
354	capabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_DIR_COOKIE,
355		ops->free_attr_dir_cookie);
356	capabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_DIR, ops->read_attr_dir);
357	capabilities.Set(FS_VNODE_CAPABILITY_REWIND_ATTR_DIR, ops->rewind_attr_dir);
358
359	// attribute operations
360	capabilities.Set(FS_VNODE_CAPABILITY_CREATE_ATTR, ops->create_attr);
361	capabilities.Set(FS_VNODE_CAPABILITY_OPEN_ATTR, ops->open_attr);
362	capabilities.Set(FS_VNODE_CAPABILITY_CLOSE_ATTR, ops->close_attr);
363	capabilities.Set(FS_VNODE_CAPABILITY_FREE_ATTR_COOKIE,
364		ops->free_attr_cookie);
365	capabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR, ops->read_attr);
366	capabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR, ops->write_attr);
367
368	capabilities.Set(FS_VNODE_CAPABILITY_READ_ATTR_STAT, ops->read_attr_stat);
369	capabilities.Set(FS_VNODE_CAPABILITY_WRITE_ATTR_STAT,
370		ops->write_attr_stat);
371	capabilities.Set(FS_VNODE_CAPABILITY_RENAME_ATTR, ops->rename_attr);
372	capabilities.Set(FS_VNODE_CAPABILITY_REMOVE_ATTR, ops->remove_attr);
373
374	// support for node and FS layers
375	capabilities.Set(FS_VNODE_CAPABILITY_CREATE_SPECIAL_NODE,
376		ops->create_special_node);
377	capabilities.Set(FS_VNODE_CAPABILITY_GET_SUPER_VNODE, ops->get_super_vnode);
378}
379
380
381// #pragma mark - bootstrapping
382
383
384status_t
385userlandfs_create_file_system(const char* fsName, image_id image,
386	FileSystem** _fileSystem)
387{
388	// get the modules
389	module_info** modules;
390	status_t error = get_image_symbol(image, "modules", B_SYMBOL_TYPE_DATA,
391		(void**)&modules);
392	if (error != B_OK)
393		RETURN_ERROR(error);
394
395	// module name must match "file_systems/<name>/v1"
396	char moduleName[B_PATH_NAME_LENGTH];
397	snprintf(moduleName, sizeof(moduleName), "file_systems/%s/v1", fsName);
398
399	// find the module
400	file_system_module_info* module = NULL;
401	for (int32 i = 0; modules[i] && modules[i]->name; i++) {
402		if (strcmp(modules[i]->name, moduleName) == 0) {
403			module = (file_system_module_info*)modules[i];
404			break;
405		}
406	}
407	if (!module)
408		RETURN_ERROR(B_ERROR);
409
410	// create the file system
411	HaikuKernelFileSystem* fileSystem
412		= new(std::nothrow) HaikuKernelFileSystem(fsName, module);
413	if (!fileSystem)
414		RETURN_ERROR(B_NO_MEMORY);
415
416	error = fileSystem->Init();
417	if (error != B_OK) {
418		delete fileSystem;
419		return error;
420	}
421
422	*_fileSystem = fileSystem;
423	return B_OK;
424}
425