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