1// kernel_interface.cpp
2//
3// Copyright (c) 2003-2010, Ingo Weinhold (bonefish@cs.tu-berlin.de)
4//
5// This program is free software; you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation; either version 2 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program; if not, write to the Free Software
17// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18//
19// You can alternatively use *this file* under the terms of the the MIT
20// license included in this package.
21
22
23#include <new>
24
25#include <ctype.h>
26#include <dirent.h>
27#include <errno.h>
28#include <fcntl.h>
29#include <stdlib.h>
30#include <string.h>
31#include <time.h>
32#include <unistd.h>
33#include <stdio.h>
34#include <sys/stat.h>
35
36#include <fs_cache.h>
37#include <fs_info.h>
38#include <fs_interface.h>
39#include <KernelExport.h>
40
41#include "DirItem.h"
42#include "Iterators.h"
43#include "StatItem.h"
44#include "Tree.h"
45#include "VNode.h"
46#include "Volume.h"
47
48
49using std::nothrow;
50
51static const size_t kOptimalIOSize = 65536;
52
53extern fs_volume_ops gReiserFSVolumeOps;
54extern fs_vnode_ops gReiserFSVnodeOps;
55
56
57inline static bool is_user_in_group(gid_t gid);
58
59
60// #pragma mark - FS
61
62
63// reiserfs_identify_partition
64static float
65reiserfs_identify_partition(int fd, partition_data *partition, void **cookie)
66{
67	Volume* volume = new(nothrow) Volume();
68	if (volume == NULL)
69		return -1.0;
70
71	status_t status = volume->Identify(fd, partition);
72	if (status != B_OK) {
73		delete volume;
74		return -1.0;
75	}
76
77	*cookie = (void*)volume;
78	return 0.8;
79}
80
81
82// reiserfs_scan_partition
83static status_t
84reiserfs_scan_partition(int fd, partition_data *partition, void *_cookie)
85{
86	Volume* volume = (Volume*)_cookie;
87
88	partition->status = B_PARTITION_VALID;
89	partition->flags |= B_PARTITION_FILE_SYSTEM;
90	partition->content_size = volume->CountBlocks()
91		* volume->GetBlockSize();
92	partition->block_size = volume->GetBlockSize();
93
94	volume->UpdateName(partition->id);
95		// must be done after setting the content size
96	partition->content_name = strdup(volume->GetName());
97	if (partition->content_name == NULL)
98		return B_NO_MEMORY;
99
100	return B_OK;
101}
102
103
104// reiserfs_free_identify_partition_cookie
105static void
106reiserfs_free_identify_partition_cookie(partition_data* partition,
107	void* _cookie)
108{
109	delete (Volume*)_cookie;
110}
111
112
113//	#pragma mark -
114
115
116// reiserfs_mount
117static status_t
118reiserfs_mount(fs_volume *_volume, const char *device, uint32 flags,
119	const char *parameters, ino_t *rootID)
120{
121	TOUCH(flags); TOUCH(parameters);
122	FUNCTION_START();
123	// parameters are ignored for now
124	status_t error = B_OK;
125
126	// allocate and init the volume
127	Volume *volume = new(nothrow) Volume;
128	if (!volume)
129		error = B_NO_MEMORY;
130	if (error == B_OK)
131		error = volume->Mount(_volume, device);
132
133	// set the results
134	if (error == B_OK) {
135		*rootID = volume->GetRootVNode()->GetID();
136		_volume->private_volume = volume;
137		_volume->ops = &gReiserFSVolumeOps;
138	}
139
140	// cleanup on failure
141	if (error != B_OK && volume)
142		delete volume;
143	RETURN_ERROR(error);
144}
145
146
147// reiserfs_unmount
148static status_t
149reiserfs_unmount(fs_volume* fs)
150{
151	FUNCTION_START();
152	Volume *volume = (Volume*)fs->private_volume;
153	status_t error = volume->Unmount();
154	if (error == B_OK)
155		delete volume;
156	RETURN_ERROR(error);
157}
158
159// reiserfs_read_fs_info
160static status_t
161reiserfs_read_fs_info(fs_volume* fs, struct fs_info *info)
162{
163	FUNCTION_START();
164	Volume *volume = (Volume*)fs->private_volume;
165	info->flags = B_FS_IS_PERSISTENT | B_FS_IS_READONLY;
166	info->block_size = volume->GetBlockSize();
167	info->io_size = kOptimalIOSize;
168	info->total_blocks = volume->CountBlocks();
169	info->free_blocks = volume->CountFreeBlocks();
170	strlcpy(info->device_name, volume->GetDeviceName(),
171			sizeof(info->device_name));
172	strlcpy(info->volume_name, volume->GetName(), sizeof(info->volume_name));
173	return B_OK;
174}
175
176
177// #pragma mark - VNodes
178
179
180// reiserfs_lookup
181static status_t
182reiserfs_lookup(fs_volume* fs, fs_vnode* _dir, const char *entryName,
183	ino_t *vnid)
184{
185//	FUNCTION_START();
186	Volume *volume = (Volume*)fs->private_volume;
187	VNode *dir = (VNode*)_dir->private_node;
188FUNCTION(("dir: (%Ld: %lu, %lu), entry: `%s'\n", dir->GetID(), dir->GetDirID(),
189		  dir->GetObjectID(), entryName));
190	status_t error = B_OK;
191	VNode *entryNode = NULL;
192
193	// check for non-directories
194	if (!dir->IsDir()) {
195		error = B_ENTRY_NOT_FOUND;
196
197	// special entries: "." and ".."
198	} else if (!strcmp(entryName, ".")) {
199		*vnid = dir->GetID();
200		if (volume->GetVNode(*vnid, &entryNode) != B_OK)
201			error = B_BAD_VALUE;
202	} else if (!strcmp(entryName, "..")) {
203		*vnid = dir->GetParentID();
204		if (volume->GetVNode(*vnid, &entryNode) != B_OK)
205			error = B_BAD_VALUE;
206
207	// ordinary entries
208	} else {
209		// find the entry
210		VNode foundNode;
211		error = volume->FindDirEntry(dir, entryName, &foundNode, true);
212
213		// hide non-file/dir/symlink entries, if the user desires that, and
214		// those entries explicitly set to hidden
215		if (error == B_OK
216			&& ((foundNode.IsEsoteric() && volume->GetHideEsoteric())
217				|| volume->IsNegativeEntry(foundNode.GetID()))) {
218			error = B_ENTRY_NOT_FOUND;
219		}
220		if (error == B_OK) {
221			*vnid = foundNode.GetID();
222			error = volume->GetVNode(*vnid, &entryNode);
223		}
224	}
225
226	// add to the entry cache
227	if (error == B_OK) {
228		entry_cache_add(volume->GetID(), dir->GetID(), entryName,
229			*vnid);
230	}
231
232	RETURN_ERROR(error);
233}
234
235// reiserfs_read_vnode
236static status_t
237reiserfs_read_vnode(fs_volume *fs, ino_t vnid, fs_vnode *node, int *_type,
238	uint32 *_flags, bool reenter)
239{
240	TOUCH(reenter);
241//	FUNCTION_START();
242	FUNCTION(("(%Ld: %lu, %ld)\n", vnid, VNode::GetDirIDFor(vnid),
243			  VNode::GetObjectIDFor(vnid)));
244	Volume *volume = (Volume*)fs->private_volume;
245	status_t error = B_OK;
246	VNode *foundNode = new(nothrow) VNode;
247	if (foundNode) {
248		error = volume->FindVNode(vnid, foundNode);
249		if (error == B_OK) {
250			node->private_node = foundNode;
251			node->ops = &gReiserFSVnodeOps;
252			*_type = foundNode->GetStatData()->GetMode() & S_IFMT;
253			*_flags = 0;
254		} else
255			delete foundNode;
256	} else
257		error = B_NO_MEMORY;
258	RETURN_ERROR(error);
259}
260
261// reiserfs_write_vnode
262static status_t
263reiserfs_write_vnode(fs_volume *fs, fs_vnode *_node, bool reenter)
264{
265	TOUCH(reenter);
266// DANGER: If dbg_printf() is used, this thread will enter another FS and
267// even perform a write operation. The is dangerous here, since this hook
268// may be called out of the other FSs, since, for instance a put_vnode()
269// called from another FS may cause the VFS layer to free vnodes and thus
270// invoke this hook.
271//	FUNCTION_START();
272	Volume *volume = (Volume*)fs->private_volume;
273	VNode *node = (VNode*)_node->private_node;
274	status_t error = B_OK;
275	if (node != volume->GetRootVNode())
276		delete node;
277//	RETURN_ERROR(error);
278	return error;
279}
280
281
282// #pragma mark - Nodes
283
284
285// reiserfs_read_symlink
286static status_t
287reiserfs_read_symlink(fs_volume *fs, fs_vnode *_node, char *buffer,
288	size_t *bufferSize)
289{
290//	FUNCTION_START();
291	Volume *volume = (Volume*)fs->private_volume;
292	VNode *node = (VNode*)_node->private_node;
293FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
294		  node->GetObjectID()));
295	status_t error = B_OK;
296	// read symlinks only
297	if (!node->IsSymlink())
298		error = B_BAD_VALUE;
299	// read
300	if (error == B_OK)
301		error = volume->ReadLink(node, buffer, *bufferSize, bufferSize);
302	RETURN_ERROR(error);
303}
304
305// reiserfs_access
306static status_t
307reiserfs_access(fs_volume *fs, fs_vnode *_node, int mode)
308{
309	TOUCH(fs);
310//	FUNCTION_START();
311//	Volume *volume = (Volume*)fs->private_volume;
312	VNode *node = (VNode*)_node->private_node;
313FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
314		  node->GetObjectID()));
315	// write access requested?
316	if (mode & W_OK)
317		return B_READ_ONLY_DEVICE;
318	// get node permissions
319	StatData *statData = node->GetStatData();
320	int userPermissions = (statData->GetMode() & S_IRWXU) >> 6;
321	int groupPermissions = (statData->GetMode() & S_IRWXG) >> 3;
322	int otherPermissions = statData->GetMode() & S_IRWXO;
323	// get the permissions for this uid/gid
324	int permissions = 0;
325	uid_t uid = geteuid();
326	// user is root
327	if (uid == 0) {
328		// root has always read/write permission, but at least one of the
329		// X bits must be set for execute permission
330		permissions = userPermissions | groupPermissions | otherPermissions
331			| S_IROTH | S_IWOTH;
332	// user is node owner
333	} else if (uid == statData->GetUID())
334		permissions = userPermissions;
335	// user is in owning group
336	else if (is_user_in_group(statData->GetGID()))
337		permissions = groupPermissions;
338	// user is one of the others
339	else
340		permissions = otherPermissions;
341	// do the check
342	if (mode & ~permissions)
343		return B_NOT_ALLOWED;
344	return B_OK;
345}
346
347// reiserfs_read_stat
348static status_t
349reiserfs_read_stat(fs_volume *fs, fs_vnode *_node, struct stat *st)
350{
351//	FUNCTION_START();
352	Volume *volume = (Volume*)fs->private_volume;
353	VNode *node = (VNode*)_node->private_node;
354FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
355		  node->GetObjectID()));
356	status_t error = B_OK;
357	StatData *statData = node->GetStatData();
358	st->st_dev = volume->GetID();
359	st->st_ino = node->GetID();
360	st->st_mode = statData->GetMode();
361	st->st_nlink = statData->GetNLink();
362	st->st_uid = statData->GetUID();
363	st->st_gid = statData->GetGID();
364	st->st_size = statData->GetSize();
365	st->st_blksize = kOptimalIOSize;
366	st->st_atime = statData->GetATime();
367	st->st_mtime = st->st_ctime = statData->GetMTime();
368	st->st_crtime = statData->GetCTime();
369	RETURN_ERROR(error);
370}
371
372
373// #pragma mark - Files
374
375
376// reiserfs_open
377static status_t
378reiserfs_open(fs_volume *fs, fs_vnode *_node, int openMode, void **cookie)
379{
380//	FUNCTION_START();
381	Volume *volume = (Volume*)fs->private_volume;
382	VNode *node = (VNode*)_node->private_node;
383FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
384		  node->GetObjectID()));
385	status_t error = B_OK;
386	// check the open mode
387	if ((openMode & O_RWMASK) == O_WRONLY || (openMode & O_RWMASK) == O_RDWR
388		|| (openMode & (O_TRUNC | O_CREAT))) {
389		error = B_READ_ONLY_DEVICE;
390	}
391	// create a StreamReader
392	if (error == B_OK) {
393		StreamReader *reader = new(nothrow) StreamReader(volume->GetTree(),
394			node->GetDirID(), node->GetObjectID());
395		if (reader) {
396			error = reader->Suspend();
397			if (error == B_OK)
398				*cookie = reader;
399			else
400				delete reader;
401		} else
402			error = B_NO_MEMORY;
403	}
404	RETURN_ERROR(error);
405}
406
407// reiserfs_close
408static status_t
409reiserfs_close(fs_volume *fs, fs_vnode *_node, void *cookie)
410{
411	TOUCH(fs); TOUCH(cookie);
412//	FUNCTION_START();
413	VNode *node = (VNode*)_node->private_node;
414FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
415		  node->GetObjectID()));
416	TOUCH(node);
417	return B_OK;
418}
419
420// reiserfs_free_cookie
421static status_t
422reiserfs_free_cookie(fs_volume *fs, fs_vnode *_node, void *cookie)
423{
424	TOUCH(fs);
425//	FUNCTION_START();
426	VNode *node = (VNode*)_node->private_node;
427FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
428		  node->GetObjectID()));
429	TOUCH(node);
430	StreamReader *reader = (StreamReader*)cookie;
431	delete reader;
432	return B_OK;
433}
434
435// reiserfs_read
436static status_t
437reiserfs_read(fs_volume *fs, fs_vnode *_node, void *cookie, off_t pos,
438	void *buffer, size_t *bufferSize)
439{
440	TOUCH(fs);
441//	FUNCTION_START();
442//	Volume *volume = (Volume*)fs->private_volume;
443	VNode *node = (VNode*)_node->private_node;
444	FUNCTION(("((%Ld: %lu, %lu), %Ld, %p, %lu)\n", node->GetID(),
445			  node->GetDirID(), node->GetObjectID(), pos, buffer,
446			  *bufferSize));
447	status_t error = B_OK;
448	// don't read anything but files
449	if (!node->IsFile()) {
450		if (node->IsDir())
451			error = B_IS_A_DIRECTORY;
452		else
453			error = B_BAD_VALUE;
454	}
455
456	// read
457	StreamReader *reader = (StreamReader*)cookie;
458	if (error == B_OK) {
459		error = reader->Resume();
460		if (error == B_OK) {
461			error = reader->ReadAt(pos, buffer, *bufferSize, bufferSize);
462			reader->Suspend();
463		}
464	}
465	RETURN_ERROR(error);
466}
467
468// is_user_in_group
469inline static bool
470is_user_in_group(gid_t gid)
471{
472// Either I miss something, or we don't have getgroups() in the kernel. :-(
473/*
474	gid_t groups[NGROUPS_MAX];
475	int groupCount = getgroups(NGROUPS_MAX, groups);
476	for (int i = 0; i < groupCount; i++) {
477		if (gid == groups[i])
478			return true;
479	}
480*/
481	return (gid == getegid());
482}
483
484// DirectoryCookie
485class DirectoryCookie : public DirEntryIterator {
486public:
487	DirectoryCookie(Tree *tree, uint32 dirID, uint32 objectID,
488					uint64 startOffset = 0, bool fixedHash = false)
489		: DirEntryIterator(tree, dirID, objectID, startOffset,
490						   fixedHash),
491		  fEncounteredDotDot(false)
492	{
493	}
494
495	bool EncounteredDotDot() const
496	{
497		return fEncounteredDotDot;
498	}
499
500	void SetEncounteredDotDot(bool flag)
501	{
502		fEncounteredDotDot = flag;
503	}
504
505	bool	fEncounteredDotDot;
506};
507
508
509// #pragma mark - Directories
510
511
512// reiserfs_open_dir
513static status_t
514reiserfs_open_dir(fs_volume *fs, fs_vnode *_node, void **cookie)
515{
516//	FUNCTION_START();
517	Volume *volume = (Volume*)fs->private_volume;
518	VNode *node = (VNode*)_node->private_node;
519FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
520		  node->GetObjectID()));
521	status_t error = (node->IsDir() ? B_OK : B_NOT_A_DIRECTORY);
522	if (error == B_OK) {
523		DirectoryCookie *iterator = new(nothrow) DirectoryCookie(
524			volume->GetTree(), node->GetDirID(), node->GetObjectID());
525		if (iterator) {
526			error = iterator->Suspend();
527			if (error == B_OK)
528				*cookie = iterator;
529			else
530				delete iterator;
531		} else
532			error = B_NO_MEMORY;
533	}
534	FUNCTION_END();
535	RETURN_ERROR(error);
536}
537
538// set_dirent_name
539static status_t
540set_dirent_name(struct dirent *buffer, size_t bufferSize,
541						 const char *name, int32 nameLen)
542{
543	size_t length = (buffer->d_name + nameLen + 1) - (char*)buffer;
544	if (length <= bufferSize) {
545		memcpy(buffer->d_name, name, nameLen);
546		buffer->d_name[nameLen] = '\0';
547		buffer->d_reclen = length;
548		return B_OK;
549	} else
550		RETURN_ERROR(B_BUFFER_OVERFLOW);
551}
552
553// reiserfs_close_dir
554static status_t
555reiserfs_close_dir(fs_volume *fs, fs_vnode *_node, void *cookie)
556{
557	TOUCH(fs); TOUCH(cookie);
558//	FUNCTION_START();
559	VNode *node = (VNode*)_node->private_node;
560FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
561		  node->GetObjectID()));
562	TOUCH(node);
563	return B_OK;
564}
565
566// reiserfs_free_dir_cookie
567static status_t
568reiserfs_free_dir_cookie(fs_volume *fs, fs_vnode *_node, void *cookie)
569{
570	TOUCH(fs);
571//	FUNCTION_START();
572	VNode *node = (VNode*)_node->private_node;
573FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
574		  node->GetObjectID()));
575	TOUCH(node);
576	DirectoryCookie *iterator = (DirectoryCookie*)cookie;
577	delete iterator;
578	return B_OK;
579}
580
581// reiserfs_read_dir
582static status_t
583reiserfs_read_dir(fs_volume *fs, fs_vnode *_node, void *cookie,
584	struct dirent *buffer, size_t bufferSize, uint32 *count)
585{
586//	FUNCTION_START();
587	Volume *volume = (Volume*)fs->private_volume;
588	VNode *node = (VNode*)_node->private_node;
589FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
590		  node->GetObjectID()));
591	DirectoryCookie *iterator = (DirectoryCookie*)cookie;
592	status_t error = iterator->Resume();
593	if (error == B_OK) {
594		// read one entry
595		DirItem item;
596		int32 index = 0;
597		DirEntry *entry = NULL;
598		bool done = false;
599		while (error == B_OK && !done
600			   && (error = iterator->GetNext(&item, &index, &entry)) == B_OK) {
601			uint32 dirID = entry->GetDirID();
602			uint32 objectID = entry->GetObjectID();
603			// skip hidden entries and entries the user specified to be hidden
604			if (entry->IsHidden() || volume->IsNegativeEntry(dirID, objectID))
605				continue;
606			// skip entry, if we can't get the stat data, or it is neither a
607			// file, a dir nor a symlink and the user desired to hide those.
608			StatData statData;
609			StatItem statItem;
610			if (volume->GetTree()->FindStatItem(dirID, objectID, &statItem)
611					!= B_OK
612				|| statItem.GetStatData(&statData) != B_OK
613				|| (statData.IsEsoteric() && volume->GetHideEsoteric())) {
614				continue;
615			}
616			if (error == B_OK) {
617				// get the name
618				size_t nameLen = 0;
619				const char *name = item.EntryNameAt(index, &nameLen);
620				if (!name || nameLen == 0)	// bad data: skip it gracefully
621					continue;
622				// fill in the entry name -- checks whether the
623				// entry fits into the buffer
624				error = set_dirent_name(buffer, bufferSize, name,
625										nameLen);
626				if (error == B_OK) {
627					// fill in the other data
628					buffer->d_dev = volume->GetID();
629					buffer->d_ino = VNode::GetIDFor(dirID, objectID);
630					*count = 1;
631PRINT(("Successfully read entry: dir: (%Ld: %ld, %ld), name: `%s', "
632	   "id: (%Ld, %ld, %ld), reclen: %hu\n", node->GetID(), node->GetDirID(),
633	   node->GetObjectID(), buffer->d_name, buffer->d_ino, dirID, objectID,
634	   buffer->d_reclen));
635					if (!strcmp("..", buffer->d_name))
636						iterator->SetEncounteredDotDot(true);
637					done = true;
638				}
639	 		}
640 		}
641 		if (error == B_ENTRY_NOT_FOUND) {
642 			if (iterator->EncounteredDotDot()) {
643	 			error = B_OK;
644				*count = 0;
645			} else {
646				// this is necessary for the root directory
647				// it usually has no ".." entry, so we simulate one
648				// get the name
649				const char *name = "..";
650				size_t nameLen = strlen(name);
651				// fill in the entry name -- checks whether the
652				// entry fits into the buffer
653				error = set_dirent_name(buffer, bufferSize, name,
654										nameLen);
655				if (error == B_OK) {
656					// fill in the other data
657					buffer->d_dev = volume->GetID();
658					buffer->d_ino = node->GetID();
659	// < That's not correct!
660					*count = 1;
661PRINT(("faking `..' entry: dir: (%Ld: %ld, %ld), name: `%s', "
662	   "id: (%Ld, %ld, %ld), reclen: %hu\n", node->GetID(), node->GetDirID(),
663	   node->GetObjectID(), buffer->d_name, buffer->d_ino, node->GetDirID(),
664	   node->GetObjectID(), buffer->d_reclen));
665					iterator->SetEncounteredDotDot(true);
666				}
667			}
668 		}
669 		iterator->Suspend();
670	}
671PRINT(("returning %ld entries\n", *count));
672	RETURN_ERROR(error);
673}
674
675// reiserfs_rewind_dir
676static status_t
677reiserfs_rewind_dir(fs_volume *fs, fs_vnode *_node, void *cookie)
678{
679	TOUCH(fs);
680//	FUNCTION_START();
681	VNode *node = (VNode*)_node->private_node;
682FUNCTION(("node: (%Ld: %lu, %lu)\n", node->GetID(), node->GetDirID(),
683		  node->GetObjectID()));
684	TOUCH(node);
685	DirectoryCookie *iterator = (DirectoryCookie*)cookie;
686	status_t error = iterator->Rewind();	// no need to Resume()
687	if (error == B_OK)
688		error = iterator->Suspend();
689	RETURN_ERROR(error);
690}
691
692
693// #pragma mark - Module Interface
694
695
696// reiserfs_std_ops
697static status_t
698reiserfs_std_ops(int32 op, ...)
699{
700	switch (op) {
701		case B_MODULE_INIT:
702		{
703			init_debugging();
704			PRINT(("reiserfs_std_ops(): B_MODULE_INIT\n"));
705			return B_OK;
706		}
707
708		case B_MODULE_UNINIT:
709			PRINT(("reiserfs_std_ops(): B_MODULE_UNINIT\n"));
710			exit_debugging();
711			return B_OK;
712
713		default:
714			return B_ERROR;
715	}
716}
717
718
719
720
721static file_system_module_info sReiserFSModuleInfo = {
722	{
723		"file_systems/reiserfs" B_CURRENT_FS_API_VERSION,
724		0,
725		reiserfs_std_ops,
726	},
727
728	"reiserfs",					// short_name
729	"Reiser File System",		// pretty_name
730	0,							// DDM flags
731
732
733	// scanning
734	&reiserfs_identify_partition,
735	&reiserfs_scan_partition,
736	&reiserfs_free_identify_partition_cookie,
737	NULL,	// free_partition_content_cookie()
738
739	&reiserfs_mount
740};
741
742
743fs_volume_ops gReiserFSVolumeOps = {
744	&reiserfs_unmount,
745	&reiserfs_read_fs_info,
746	NULL,	// &reiserfs_write_fs_info,
747	NULL,	// &reiserfs_sync,
748
749	&reiserfs_read_vnode
750};
751
752
753fs_vnode_ops gReiserFSVnodeOps = {
754	/* vnode operations */
755	&reiserfs_lookup,
756	NULL,	// &reiserfs_get_vnode_name,
757	&reiserfs_write_vnode,
758	NULL,	// &reiserfs_remove_vnode,
759
760	/* VM file access */
761	NULL,	// &reiserfs_can_page,
762	NULL,	// &reiserfs_read_pages,
763	NULL,	// &reiserfs_write_pages,
764
765	NULL,	// io()
766	NULL,	// cancel_io()
767
768	NULL,	// &reiserfs_get_file_map,
769
770	NULL,	// &reiserfs_ioctl,
771	NULL,	// &reiserfs_set_flags,
772	NULL,	// &reiserfs_select,
773	NULL,	// &reiserfs_deselect,
774	NULL,	// &reiserfs_fsync,
775
776	&reiserfs_read_symlink,
777	NULL,	// &reiserfs_create_symlink,
778
779	NULL,	// &reiserfs_link,
780	NULL,	// &reiserfs_unlink,
781	NULL,	// &reiserfs_rename,
782
783	&reiserfs_access,
784	&reiserfs_read_stat,
785	NULL,	// &reiserfs_write_stat,
786	NULL,	// &reiserfs_preallocate,
787
788	/* file operations */
789	NULL,	// &reiserfs_create,
790	&reiserfs_open,
791	&reiserfs_close,
792	&reiserfs_free_cookie,
793	&reiserfs_read,
794	NULL,	// &reiserfs_write,
795
796	/* directory operations */
797	NULL,	// &reiserfs_create_dir,
798	NULL,	// &reiserfs_remove_dir,
799	&reiserfs_open_dir,
800	&reiserfs_close_dir,
801	&reiserfs_free_dir_cookie,
802	&reiserfs_read_dir,
803	&reiserfs_rewind_dir
804};
805
806
807module_info *modules[] = {
808	(module_info *)&sReiserFSModuleInfo,
809	NULL,
810};
811