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