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