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