xref: /haiku/src/add-ons/kernel/file_systems/reiserfs/kernel_interface.cpp (revision 220d04022750f40f8bac8f01fa551211e28d04f2)
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 
49 using std::nothrow;
50 
51 static const size_t kOptimalIOSize = 65536;
52 
53 extern fs_volume_ops gReiserFSVolumeOps;
54 extern fs_vnode_ops gReiserFSVnodeOps;
55 
56 
57 inline static bool is_user_in_group(gid_t gid);
58 
59 
60 // #pragma mark - FS
61 
62 
63 // reiserfs_identify_partition
64 static float
65 reiserfs_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
83 static status_t
84 reiserfs_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
105 static void
106 reiserfs_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
117 static status_t
118 reiserfs_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
148 static status_t
149 reiserfs_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
160 static status_t
161 reiserfs_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
181 static status_t
182 reiserfs_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;
188 FUNCTION(("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
236 static status_t
237 reiserfs_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
262 static status_t
263 reiserfs_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
286 static status_t
287 reiserfs_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;
293 FUNCTION(("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
306 static status_t
307 reiserfs_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;
313 FUNCTION(("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
348 static status_t
349 reiserfs_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;
354 FUNCTION(("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
377 static status_t
378 reiserfs_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;
383 FUNCTION(("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
408 static status_t
409 reiserfs_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;
414 FUNCTION(("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
421 static status_t
422 reiserfs_free_cookie(fs_volume *fs, fs_vnode *_node, void *cookie)
423 {
424 	TOUCH(fs);
425 //	FUNCTION_START();
426 	VNode *node = (VNode*)_node->private_node;
427 FUNCTION(("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
436 static status_t
437 reiserfs_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
469 inline static bool
470 is_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
485 class DirectoryCookie : public DirEntryIterator {
486 public:
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
513 static status_t
514 reiserfs_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;
519 FUNCTION(("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
539 static status_t
540 set_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
554 static status_t
555 reiserfs_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;
560 FUNCTION(("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
567 static status_t
568 reiserfs_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;
573 FUNCTION(("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
582 static status_t
583 reiserfs_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;
589 FUNCTION(("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;
631 PRINT(("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;
661 PRINT(("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 	}
671 PRINT(("returning %ld entries\n", *count));
672 	RETURN_ERROR(error);
673 }
674 
675 // reiserfs_rewind_dir
676 static status_t
677 reiserfs_rewind_dir(fs_volume *fs, fs_vnode *_node, void *cookie)
678 {
679 	TOUCH(fs);
680 //	FUNCTION_START();
681 	VNode *node = (VNode*)_node->private_node;
682 FUNCTION(("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
697 static status_t
698 reiserfs_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 
721 static 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 
743 fs_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 
753 fs_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 
807 module_info *modules[] = {
808 	(module_info *)&sReiserFSModuleInfo,
809 	NULL,
810 };
811