xref: /haiku/src/add-ons/kernel/file_systems/bfs/kernel_interface.cpp (revision 5412911f7f8ca41340b0f5cb928ed9726322ab44)
1 /* kernel_interface - file system interface to BeOS' vnode layer
2  *
3  * Copyright 2001-2005, Axel Dörfler, axeld@pinc-software.de.
4  * This file may be used under the terms of the MIT License.
5  */
6 
7 
8 #include "Debug.h"
9 #include "Volume.h"
10 #include "Inode.h"
11 #include "Index.h"
12 #include "BPlusTree.h"
13 #include "Query.h"
14 #include "Attribute.h"
15 #include "bfs_control.h"
16 
17 #include <util/kernel_cpp.h>
18 
19 #include <string.h>
20 #include <stdio.h>
21 
22 #include <KernelExport.h>
23 #include <NodeMonitor.h>
24 #include <fs_interface.h>
25 #include <fs_cache.h>
26 
27 #include <fs_attr.h>
28 #include <fs_info.h>
29 #include <fs_index.h>
30 #include <fs_query.h>
31 #include <fs_volume.h>
32 
33 #define BFS_IO_SIZE	65536
34 
35 
36 struct identify_cookie {
37 	disk_super_block super_block;
38 };
39 
40 
41 extern void fill_stat_buffer(Inode *inode, struct stat &stat);
42 
43 
44 void
45 fill_stat_buffer(Inode *inode, struct stat &stat)
46 {
47 	const bfs_inode &node = inode->Node();
48 
49 	stat.st_dev = inode->GetVolume()->ID();
50 	stat.st_ino = inode->ID();
51 	stat.st_nlink = 1;
52 	stat.st_blksize = BFS_IO_SIZE;
53 
54 	stat.st_uid = node.UserID();
55 	stat.st_gid = node.GroupID();
56 	stat.st_mode = node.Mode();
57 	stat.st_type = node.Type();
58 
59 	stat.st_atime = time(NULL);
60 	stat.st_mtime = stat.st_ctime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT);
61 	stat.st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT);
62 
63 	if (inode->IsSymLink() && (node.Flags() & INODE_LONG_SYMLINK) == 0) {
64 		// symlinks report the size of the link here
65 		stat.st_size = strlen(node.short_symlink) + 1;
66 	} else
67 		stat.st_size = inode->Size();
68 }
69 
70 
71 // ToDo: Temporary hack to get it working
72 
73 
74 double
75 strtod(const char */*start*/, char **/*end*/)
76 {
77 	return 0;
78 }
79 
80 
81 //	#pragma mark - Scanning
82 
83 
84 static float
85 bfs_identify_partition(int fd, partition_data *partition, void **_cookie)
86 {
87 	disk_super_block superBlock;
88 	status_t status = Volume::Identify(fd, &superBlock);
89 	if (status != B_OK)
90 		return status;
91 
92 	identify_cookie *cookie = new identify_cookie;
93 	memcpy(&cookie->super_block, &superBlock, sizeof(disk_super_block));
94 
95 	*_cookie = cookie;
96 	return 0.8f;
97 }
98 
99 
100 static status_t
101 bfs_scan_partition(int fd, partition_data *partition, void *_cookie)
102 {
103 	identify_cookie *cookie = (identify_cookie *)_cookie;
104 
105 	partition->status = B_PARTITION_VALID;
106 	partition->flags |= B_PARTITION_FILE_SYSTEM;
107 	partition->content_size = cookie->super_block.NumBlocks() * cookie->super_block.BlockSize();
108 	partition->block_size = cookie->super_block.BlockSize();
109 	partition->content_name = strdup(cookie->super_block.name);
110 	if (partition->content_name == NULL)
111 		return B_NO_MEMORY;
112 
113 	return B_OK;
114 }
115 
116 
117 static void
118 bfs_free_identify_partition_cookie(partition_data *partition, void *_cookie)
119 {
120 	identify_cookie *cookie = (identify_cookie *)_cookie;
121 
122 	delete cookie;
123 }
124 
125 
126 //	#pragma mark -
127 
128 
129 static status_t
130 bfs_mount(mount_id mountID, const char *device, uint32 flags, const char *args,
131 	void **_data, vnode_id *_rootID)
132 {
133 	FUNCTION();
134 
135 	Volume *volume = new Volume(mountID);
136 	if (volume == NULL)
137 		return B_NO_MEMORY;
138 
139 	status_t status;
140 	if ((status = volume->Mount(device, flags)) == B_OK) {
141 		*_data = volume;
142 		*_rootID = volume->ToVnode(volume->Root());
143 		INFORM(("mounted \"%s\" (root node at %Ld, device = %s)\n",
144 			volume->Name(), *_rootID, device));
145 	}
146 	else
147 		delete volume;
148 
149 	RETURN_ERROR(status);
150 }
151 
152 
153 static status_t
154 bfs_unmount(void *ns)
155 {
156 	FUNCTION();
157 	Volume* volume = (Volume *)ns;
158 
159 	status_t status = volume->Unmount();
160 	delete volume;
161 
162 	RETURN_ERROR(status);
163 }
164 
165 
166 /**	Fill in bfs_info struct for device.
167  */
168 
169 static status_t
170 bfs_read_fs_stat(void *_ns, struct fs_info *info)
171 {
172 	FUNCTION();
173 	if (_ns == NULL || info == NULL)
174 		return B_BAD_VALUE;
175 
176 	Volume *volume = (Volume *)_ns;
177 
178 	RecursiveLocker locker(volume->Lock());
179 
180 	// File system flags.
181 	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR | B_FS_HAS_MIME | B_FS_HAS_QUERY |
182 			(volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
183 
184 	info->io_size = BFS_IO_SIZE;
185 		// whatever is appropriate here? Just use the same value as BFS (and iso9660) for now
186 
187 	info->block_size = volume->BlockSize();
188 	info->total_blocks = volume->NumBlocks();
189 	info->free_blocks = volume->FreeBlocks();
190 
191 	// Volume name
192 	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
193 
194 	// File system name
195 	strlcpy(info->fsh_name, "bfs", sizeof(info->fsh_name));
196 
197 	return B_OK;
198 }
199 
200 
201 static status_t
202 bfs_write_fs_stat(void *_ns, const struct fs_info *info, uint32 mask)
203 {
204 	FUNCTION_START(("mask = %ld\n", mask));
205 
206 	Volume *volume = (Volume *)_ns;
207 	disk_super_block &superBlock = volume->SuperBlock();
208 
209 	RecursiveLocker locker(volume->Lock());
210 
211 	status_t status = B_BAD_VALUE;
212 
213 	if (mask & FS_WRITE_FSINFO_NAME) {
214 		strncpy(superBlock.name, info->volume_name, sizeof(superBlock.name) - 1);
215 		superBlock.name[sizeof(superBlock.name) - 1] = '\0';
216 
217 		status = volume->WriteSuperBlock();
218 	}
219 	return status;
220 }
221 
222 #if 0
223 static status_t
224 bfs_initialize(const char *deviceName, void *parms, size_t len)
225 {
226 	FUNCTION_START(("deviceName = %s, parameter len = %ld\n", deviceName, len));
227 
228 	// The backend (to create the file system) is already done; just
229 	// call Volume::Initialize().
230 
231 	return B_ERROR;
232 }
233 #endif
234 
235 static status_t
236 bfs_sync(void *_ns)
237 {
238 	FUNCTION();
239 	if (_ns == NULL)
240 		return B_BAD_VALUE;
241 
242 	Volume *volume = (Volume *)_ns;
243 
244 	return volume->Sync();
245 }
246 
247 
248 //	#pragma mark -
249 
250 
251 /**	Reads in the node from disk and creates an inode object from it.
252  */
253 
254 static status_t
255 bfs_read_vnode(void *_ns, vnode_id id, void **_node, bool reenter)
256 {
257 	//FUNCTION_START(("vnode_id = %Ld\n", id));
258 	Volume *volume = (Volume *)_ns;
259 
260 	// first inode may be after the log area, we don't go through
261 	// the hassle and try to load an earlier block from disk
262 	if (id < volume->ToBlock(volume->Log()) + volume->Log().Length()
263 		|| id > volume->NumBlocks()) {
264 		INFORM(("inode at %Ld requested!\n", id));
265 		return B_ERROR;
266 	}
267 
268 	CachedBlock cached(volume, id);
269 	bfs_inode *node = (bfs_inode *)cached.Block();
270 	if (node == NULL) {
271 		FATAL(("could not read inode: %Ld\n", id));
272 		return B_IO_ERROR;
273 	}
274 
275 	status_t status = node->InitCheck(volume);
276 	if (status < B_OK) {
277 		FATAL(("inode at %Ld is corrupt!\n", id));
278 		return status;
279 	}
280 
281 	Inode *inode = new Inode(volume, id);
282 	if (inode == NULL)
283 		return B_NO_MEMORY;
284 
285 	status = inode->InitCheck(false);
286 	if (status < B_OK)
287 		delete inode;
288 
289 	if (status == B_OK)
290 		*_node = inode;
291 
292 	return status;
293 }
294 
295 
296 static status_t
297 bfs_release_vnode(void *_ns, void *_node, bool reenter)
298 {
299 	//FUNCTION_START(("node = %p\n", _node));
300 
301 	Volume *volume = (Volume *)_ns;
302 	Inode *inode = (Inode *)_node;
303 
304 	// since a directory's size can be changed without having it opened,
305 	// we need to take care about their preallocated blocks here
306 	if (inode->NeedsTrimming()) {
307 		Transaction transaction(volume, inode->BlockNumber());
308 
309 		if (inode->TrimPreallocation(transaction) == B_OK)
310 			transaction.Done();
311 		else if (transaction.HasParent()) {
312 			// ToDo: for now, we don't let sub-transactions fail
313 			transaction.Done();
314 		}
315 	}
316 
317 	delete inode;
318 
319 	return B_NO_ERROR;
320 }
321 
322 
323 static status_t
324 bfs_remove_vnode(void *_ns, void *_node, bool reenter)
325 {
326 	FUNCTION();
327 
328 	if (_ns == NULL || _node == NULL)
329 		return B_BAD_VALUE;
330 
331 	Volume *volume = (Volume *)_ns;
332 	Inode *inode = (Inode *)_node;
333 
334 	// The "chkbfs" functionality uses this flag to prevent the space used
335 	// up by the inode from being freed - this flag is set only in situations
336 	// where this is a good idea... (the block bitmap will get fixed anyway
337 	// in this case).
338 	if (inode->Flags() & INODE_DONT_FREE_SPACE) {
339 		delete inode;
340 		return B_OK;
341 	}
342 
343 	// If the inode isn't in use anymore, we were called before
344 	// bfs_unlink() returns - in this case, we can just use the
345 	// transaction which has already deleted the inode.
346 	Transaction transaction(volume, volume->ToBlock(inode->Parent()));
347 
348 	status_t status = inode->Free(transaction);
349 	if (status == B_OK) {
350 		transaction.Done();
351 
352 		delete inode;
353 	} else if (transaction.HasParent()) {
354 		// ToDo: for now, we don't let sub-transactions fail
355 		transaction.Done();
356 	}
357 
358 	return status;
359 }
360 
361 
362 static bool
363 bfs_can_page(fs_volume _fs, fs_vnode _v, fs_cookie _cookie)
364 {
365 	// ToDo: we're obviously not even asked...
366 	return false;
367 }
368 
369 
370 static status_t
371 bfs_read_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos,
372 	const iovec *vecs, size_t count, size_t *_numBytes)
373 {
374 	Inode *inode = (Inode *)_node;
375 
376 	if (!inode->HasUserAccessableStream())
377 		RETURN_ERROR(B_BAD_VALUE);
378 
379 	ReadLocked locked(inode->Lock());
380 	return file_cache_read_pages(inode->FileCache(), pos, vecs, count, _numBytes);
381 #if 0
382 	for (uint32 i = 0; i < count; i++) {
383 		if (pos >= inode->Size()) {
384 			memset(vecs[i].iov_base, 0, vecs[i].iov_len);
385 			pos += vecs[i].iov_len;
386 			*_numBytes -= vecs[i].iov_len;
387 		} else {
388 			uint32 length = vecs[i].iov_len;
389 			if (length > inode->Size() - pos)
390 				length = inode->Size() - pos;
391 
392 			inode->ReadAt(pos, (uint8 *)vecs[i].iov_base, &length);
393 
394 			if (length < vecs[i].iov_len) {
395 				memset((char *)vecs[i].iov_base + length, 0, vecs[i].iov_len - length);
396 				*_numBytes -= vecs[i].iov_len - length;
397 			}
398 
399 			pos += vecs[i].iov_len;
400 		}
401 	}
402 
403 	return B_OK;
404 #endif
405 }
406 
407 
408 static status_t
409 bfs_write_pages(fs_volume _fs, fs_vnode _node, fs_cookie _cookie, off_t pos,
410 	const iovec *vecs, size_t count, size_t *_numBytes)
411 {
412 	Inode *inode = (Inode *)_node;
413 
414 	if (!inode->HasUserAccessableStream())
415 		RETURN_ERROR(B_BAD_VALUE);
416 
417 	ReadLocked locked(inode->Lock());
418 	return file_cache_write_pages(inode->FileCache(), pos, vecs, count, _numBytes);
419 }
420 
421 
422 static status_t
423 bfs_get_file_map(fs_volume _fs, fs_vnode _node, off_t offset, size_t size,
424 	struct file_io_vec *vecs, size_t *_count)
425 {
426 	Volume *volume = (Volume *)_fs;
427 	Inode *inode = (Inode *)_node;
428 
429 	int32 blockShift = volume->BlockShift();
430 	size_t index = 0, max = *_count;
431 	block_run run;
432 	off_t fileOffset;
433 
434 	//FUNCTION_START(("offset = %Ld, size = %lu\n", offset, size));
435 
436 	while (true) {
437 		status_t status = inode->FindBlockRun(offset, run, fileOffset);
438 		if (status != B_OK)
439 			return status;
440 
441 		vecs[index].offset = volume->ToOffset(run) + offset - fileOffset;
442 		vecs[index].length = (run.Length() << blockShift) - offset + fileOffset;
443 
444 		offset += vecs[index].length;
445 
446 		// are we already done?
447 		if (size <= vecs[index].length
448 			|| offset >= inode->Size()) {
449 			if (offset > inode->Size()) {
450 				// make sure the extent ends with the last official file
451 				// block (without taking any preallocations into account)
452 				vecs[index].length = (inode->Size() - fileOffset + volume->BlockSize() - 1)
453 					& ~(volume->BlockSize() - 1);
454 			}
455 			*_count = index + 1;
456 			return B_OK;
457 		}
458 
459 		size -= vecs[index].length;
460 		index++;
461 
462 		if (index >= max) {
463 			// we're out of file_io_vecs; let's bail out
464 			*_count = index;
465 			return B_BUFFER_OVERFLOW;
466 		}
467 	}
468 
469 	// can never get here
470 	return B_ERROR;
471 }
472 
473 
474 //	#pragma mark -
475 
476 
477 /**	the walk function just "walks" through a directory looking for the
478  *	specified file. It calls get_vnode() on its vnode-id to init it
479  *	for the kernel.
480  */
481 
482 static status_t
483 bfs_lookup(void *_ns, void *_directory, const char *file, vnode_id *_vnodeID, int *_type)
484 {
485 	//FUNCTION_START(("file = %s\n", file));
486 	if (_ns == NULL || _directory == NULL || file == NULL || _vnodeID == NULL)
487 		return B_BAD_VALUE;
488 
489 	Volume *volume = (Volume *)_ns;
490 	Inode *directory = (Inode *)_directory;
491 
492 	// check access permissions
493 	status_t status = directory->CheckPermissions(X_OK);
494 	if (status < B_OK)
495 		RETURN_ERROR(status);
496 
497 	BPlusTree *tree;
498 	if (directory->GetTree(&tree) != B_OK)
499 		RETURN_ERROR(B_BAD_VALUE);
500 
501 	if ((status = tree->Find((uint8 *)file, (uint16)strlen(file), _vnodeID)) < B_OK) {
502 		//PRINT(("bfs_walk() could not find %Ld:\"%s\": %s\n", directory->BlockNumber(), file, strerror(status)));
503 		return status;
504 	}
505 
506 	RecursiveLocker locker(volume->Lock());
507 		// we have to hold the volume lock in order to not
508 		// interfere with new_vnode() here
509 
510 	Inode *inode;
511 	if ((status = get_vnode(volume->ID(), *_vnodeID, (void **)&inode)) != B_OK) {
512 		REPORT_ERROR(status);
513 		return B_ENTRY_NOT_FOUND;
514 	}
515 
516 	*_type = inode->Mode();
517 
518 	return B_OK;
519 }
520 
521 
522 static status_t
523 bfs_get_vnode_name(fs_volume _fs, fs_vnode _node, char *buffer, size_t bufferSize)
524 {
525 	Inode *inode = (Inode *)_node;
526 
527 	return inode->GetName(buffer, bufferSize);
528 }
529 
530 
531 static status_t
532 bfs_ioctl(void *_fs, void *_node, void *_cookie, ulong cmd, void *buffer, size_t bufferLength)
533 {
534 	FUNCTION_START(("node = %p, cmd = %lu, buf = %p, len = %ld\n", _node, cmd, buffer, bufferLength));
535 
536 	Volume *volume = (Volume *)_fs;
537 	Inode *inode = (Inode *)_node;
538 
539 	switch (cmd) {
540 		case BFS_IOCTL_VERSION:
541 		{
542 			uint32 *version = (uint32 *)buffer;
543 
544 			*version = 0x10000;
545 			return B_OK;
546 		}
547 		case BFS_IOCTL_START_CHECKING:
548 		{
549 			// start checking
550 			BlockAllocator &allocator = volume->Allocator();
551 			check_control *control = (check_control *)buffer;
552 
553 			status_t status = allocator.StartChecking(control);
554 			if (status == B_OK && inode != NULL)
555 				inode->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_CHKBFS_RUNNING);
556 
557 			return status;
558 		}
559 		case BFS_IOCTL_STOP_CHECKING:
560 		{
561 			// stop checking
562 			BlockAllocator &allocator = volume->Allocator();
563 			check_control *control = (check_control *)buffer;
564 
565 			status_t status = allocator.StopChecking(control);
566 			if (status == B_OK && inode != NULL)
567 				inode->Node().flags &= HOST_ENDIAN_TO_BFS_INT32(~INODE_CHKBFS_RUNNING);
568 
569 			return status;
570 		}
571 		case BFS_IOCTL_CHECK_NEXT_NODE:
572 		{
573 			// check next
574 			BlockAllocator &allocator = volume->Allocator();
575 			check_control *control = (check_control *)buffer;
576 
577 			return allocator.CheckNextNode(control);
578 		}
579 #ifdef DEBUG
580 		case 56742:
581 		{
582 			// allocate all free blocks and zero them out (a test for the BlockAllocator)!
583 			BlockAllocator &allocator = volume->Allocator();
584 			Transaction transaction(volume, 0);
585 			CachedBlock cached(volume);
586 			block_run run;
587 			while (allocator.AllocateBlocks(transaction, 8, 0, 64, 1, run) == B_OK) {
588 				PRINT(("write block_run(%ld, %d, %d)\n", run.allocation_group, run.start, run.length));
589 				for (int32 i = 0;i < run.length;i++) {
590 					uint8 *block = cached.SetToWritable(transaction, run);
591 					if (block != NULL)
592 						memset(block, 0, volume->BlockSize());
593 				}
594 			}
595 			return B_OK;
596 		}
597 		case 56743:
598 			dump_super_block(&volume->SuperBlock());
599 			return B_OK;
600 		case 56744:
601 			if (inode != NULL)
602 				dump_inode(&inode->Node());
603 			return B_OK;
604 		case 56745:
605 			if (inode != NULL) {
606 				NodeGetter node(volume, inode);
607 				dump_block((const char *)node.Node(), volume->BlockSize());
608 			}
609 			return B_OK;
610 #endif
611 	}
612 	return B_BAD_VALUE;
613 }
614 
615 
616 /** Sets the open-mode flags for the open file cookie - only
617  *	supports O_APPEND currently, but that should be sufficient
618  *	for a file system.
619  */
620 
621 static status_t
622 bfs_set_flags(void *_ns, void *_node, void *_cookie, int flags)
623 {
624 	FUNCTION_START(("node = %p, flags = %d", _node, flags));
625 
626 	file_cookie *cookie = (file_cookie *)_cookie;
627 	cookie->open_mode = (cookie->open_mode & ~O_APPEND) | (flags & O_APPEND);
628 
629 	return B_OK;
630 }
631 
632 
633 #if 0
634 static status_t
635 bfs_select(void *ns, void *node, void *cookie, uint8 event, uint32 ref, selectsync *sync)
636 {
637 	FUNCTION_START(("event = %d, ref = %lu, sync = %p\n", event, ref, sync));
638 	notify_select_event(sync, ref);
639 
640 	return B_OK;
641 }
642 
643 
644 static status_t
645 bfs_deselect(void *ns, void *node, void *cookie, uint8 event, selectsync *sync)
646 {
647 	FUNCTION();
648 	return B_OK;
649 }
650 #endif
651 
652 static status_t
653 bfs_fsync(void *_ns, void *_node)
654 {
655 	FUNCTION();
656 	if (_node == NULL)
657 		return B_BAD_VALUE;
658 
659 	Inode *inode = (Inode *)_node;
660 	return inode->Sync();
661 }
662 
663 
664 /**	Fills in the stat struct for a node
665  */
666 
667 static status_t
668 bfs_read_stat(void *_ns, void *_node, struct stat *stat)
669 {
670 	FUNCTION();
671 
672 	Inode *inode = (Inode *)_node;
673 
674 	fill_stat_buffer(inode, *stat);
675 	return B_OK;
676 }
677 
678 
679 static status_t
680 bfs_write_stat(void *_ns, void *_node, const struct stat *stat, uint32 mask)
681 {
682 	FUNCTION();
683 
684 	if (_ns == NULL || _node == NULL || stat == NULL)
685 		RETURN_ERROR(B_BAD_VALUE);
686 
687 	Volume *volume = (Volume *)_ns;
688 	Inode *inode = (Inode *)_node;
689 
690 	// that may be incorrect here - I don't think we need write access to
691 	// change most of the stat...
692 	// we should definitely check a bit more if the new stats are correct and valid...
693 
694 	status_t status = inode->CheckPermissions(W_OK);
695 	if (status < B_OK)
696 		RETURN_ERROR(status);
697 
698 #ifdef UNSAFE_GET_VNODE
699 	RecursiveLocker locker(volume->Lock());
700 #endif
701 
702 	WriteLocked locked(inode->Lock());
703 	if (locked.IsLocked() < B_OK)
704 		RETURN_ERROR(B_ERROR);
705 
706 	Transaction transaction(volume, inode->BlockNumber());
707 
708 	bfs_inode &node = inode->Node();
709 
710 	if (mask & B_STAT_SIZE) {
711 		// Since WSTAT_SIZE is the only thing that can fail directly, we
712 		// do it first, so that the inode state will still be consistent
713 		// with the on-disk version
714 		if (inode->IsDirectory())
715 			return B_IS_A_DIRECTORY;
716 
717 		if (inode->Size() != stat->st_size) {
718 			status = inode->SetFileSize(transaction, stat->st_size);
719 			if (status < B_OK)
720 				return status;
721 
722 			// fill the new blocks (if any) with zeros
723 			inode->FillGapWithZeros(inode->OldSize(), inode->Size());
724 
725 			if (!inode->IsDeleted()) {
726 				Index index(volume);
727 				index.UpdateSize(transaction, inode);
728 
729 				if ((mask & B_STAT_MODIFICATION_TIME) == 0)
730 					index.UpdateLastModified(transaction, inode);
731 			}
732 		}
733 	}
734 
735 	if (mask & B_STAT_MODE) {
736 		PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(), stat->st_mode));
737 		node.mode = HOST_ENDIAN_TO_BFS_INT32(node.Mode() & ~S_IUMSK | stat->st_mode & S_IUMSK);
738 	}
739 
740 	if (mask & B_STAT_UID)
741 		node.uid = HOST_ENDIAN_TO_BFS_INT32(stat->st_uid);
742 	if (mask & B_STAT_GID)
743 		node.gid = HOST_ENDIAN_TO_BFS_INT32(stat->st_gid);
744 
745 	if (mask & B_STAT_MODIFICATION_TIME) {
746 		if (!inode->IsDeleted()) {
747 			// Index::UpdateLastModified() will set the new time in the inode
748 			Index index(volume);
749 			index.UpdateLastModified(transaction, inode,
750 				(bigtime_t)stat->st_mtime << INODE_TIME_SHIFT);
751 		}
752 	}
753 	if (mask & B_STAT_CREATION_TIME) {
754 		node.create_time = HOST_ENDIAN_TO_BFS_INT64((bigtime_t)stat->st_crtime << INODE_TIME_SHIFT);
755 	}
756 
757 	if ((status = inode->WriteBack(transaction)) == B_OK)
758 		transaction.Done();
759 
760 	notify_stat_changed(volume->ID(), inode->ID(), mask);
761 
762 	return status;
763 }
764 
765 
766 status_t
767 bfs_create(void *_ns, void *_directory, const char *name, int openMode, int mode,
768 	void **_cookie, vnode_id *vnodeID)
769 {
770 	FUNCTION_START(("name = \"%s\", perms = %d, openMode = %d\n", name, mode, openMode));
771 
772 	if (_ns == NULL || _directory == NULL || _cookie == NULL
773 		|| name == NULL || *name == '\0')
774 		RETURN_ERROR(B_BAD_VALUE);
775 
776 	Volume *volume = (Volume *)_ns;
777 	Inode *directory = (Inode *)_directory;
778 
779 	if (!directory->IsDirectory())
780 		RETURN_ERROR(B_BAD_TYPE);
781 
782 	// We are creating the cookie at this point, so that we don't have
783 	// to remove the inode if we don't have enough free memory later...
784 	file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
785 	if (cookie == NULL)
786 		RETURN_ERROR(B_NO_MEMORY);
787 
788 	// initialize the cookie
789 	cookie->open_mode = openMode;
790 	cookie->last_size = 0;
791 	cookie->last_notification = system_time();
792 
793 #ifdef UNSAFE_GET_VNODE
794 	RecursiveLocker locker(volume->Lock());
795 #endif
796 	Transaction transaction(volume, directory->BlockNumber());
797 
798 	status_t status = Inode::Create(transaction, directory, name, S_FILE | (mode & S_IUMSK),
799 		openMode, 0, vnodeID);
800 
801 	if (status >= B_OK) {
802 		transaction.Done();
803 
804 		// register the cookie
805 		*_cookie = cookie;
806 
807 		notify_entry_created(volume->ID(), directory->ID(), name, *vnodeID);
808 	} else
809 		free(cookie);
810 
811 	return status;
812 }
813 
814 
815 static status_t
816 bfs_create_symlink(void *_ns, void *_directory, const char *name, const char *path, int mode)
817 {
818 	FUNCTION_START(("name = \"%s\", path = \"%s\"\n", name, path));
819 
820 	if (_ns == NULL || _directory == NULL || path == NULL
821 		|| name == NULL || *name == '\0')
822 		RETURN_ERROR(B_BAD_VALUE);
823 
824 	Volume *volume = (Volume *)_ns;
825 	Inode *directory = (Inode *)_directory;
826 
827 	if (!directory->IsDirectory())
828 		RETURN_ERROR(B_BAD_TYPE);
829 
830 	status_t status = directory->CheckPermissions(W_OK);
831 	if (status < B_OK)
832 		RETURN_ERROR(status);
833 
834 #ifdef UNSAFE_GET_VNODE
835 	RecursiveLocker locker(volume->Lock());
836 #endif
837 	Transaction transaction(volume, directory->BlockNumber());
838 
839 	Inode *link;
840 	off_t id;
841 	status = Inode::Create(transaction, directory, name, S_SYMLINK | 0777, 0, 0, &id, &link);
842 	if (status < B_OK)
843 		RETURN_ERROR(status);
844 
845 	size_t length = strlen(path);
846 	if (length < SHORT_SYMLINK_NAME_LENGTH) {
847 		strcpy(link->Node().short_symlink, path);
848 	} else {
849 		link->Node().flags |= HOST_ENDIAN_TO_BFS_INT32(INODE_LONG_SYMLINK | INODE_LOGGED);
850 		// The following call will have to write the inode back, so
851 		// we don't have to do that here...
852 		status = link->WriteAt(transaction, 0, (const uint8 *)path, &length);
853 	}
854 
855 	if (status == B_OK)
856 		status = link->WriteBack(transaction);
857 
858 	// Inode::Create() left the inode locked in memory, and also doesn't publish links
859 	publish_vnode(volume->ID(), id, link);
860 	put_vnode(volume->ID(), id);
861 
862 	if (status == B_OK) {
863 		transaction.Done();
864 
865 		notify_entry_created(volume->ID(), directory->ID(), name, id);
866 	}
867 
868 	return status;
869 }
870 
871 
872 status_t
873 bfs_link(void *ns, void *dir, const char *name, void *node)
874 {
875 	FUNCTION_START(("name = \"%s\"\n", name));
876 
877 	// This one won't be implemented in a binary compatible BFS
878 
879 	return B_ERROR;
880 }
881 
882 
883 status_t
884 bfs_unlink(void *_ns, void *_directory, const char *name)
885 {
886 	FUNCTION_START(("name = \"%s\"\n", name));
887 
888 	if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0')
889 		return B_BAD_VALUE;
890 	if (!strcmp(name, "..") || !strcmp(name, "."))
891 		return B_NOT_ALLOWED;
892 
893 	Volume *volume = (Volume *)_ns;
894 	Inode *directory = (Inode *)_directory;
895 
896 	status_t status = directory->CheckPermissions(W_OK);
897 	if (status < B_OK)
898 		return status;
899 
900 #ifdef UNSAFE_GET_VNODE
901 	RecursiveLocker locker(volume->Lock());
902 #endif
903 	Transaction transaction(volume, directory->BlockNumber());
904 
905 	off_t id;
906 	if ((status = directory->Remove(transaction, name, &id)) == B_OK) {
907 		transaction.Done();
908 
909 		notify_entry_removed(volume->ID(), directory->ID(), name, id);
910 	}
911 	return status;
912 }
913 
914 
915 status_t
916 bfs_rename(void *_ns, void *_oldDir, const char *oldName, void *_newDir, const char *newName)
917 {
918 	FUNCTION_START(("oldDir = %p, oldName = \"%s\", newDir = %p, newName = \"%s\"\n", _oldDir, oldName, _newDir, newName));
919 
920 	// there might be some more tests needed?!
921 	if (_ns == NULL || _oldDir == NULL || _newDir == NULL
922 		|| oldName == NULL || *oldName == '\0'
923 		|| newName == NULL || *newName == '\0'
924 		|| !strcmp(oldName, ".") || !strcmp(oldName, "..")
925 		|| !strcmp(newName, ".") || !strcmp(newName, "..")
926 		|| strchr(newName, '/') != NULL)
927 		RETURN_ERROR(B_BAD_VALUE);
928 
929 	Volume *volume = (Volume *)_ns;
930 	Inode *oldDirectory = (Inode *)_oldDir;
931 	Inode *newDirectory = (Inode *)_newDir;
932 
933 	// are we already done?
934 	if (oldDirectory == newDirectory && !strcmp(oldName, newName))
935 		return B_OK;
936 
937 	RecursiveLocker locker(volume->Lock());
938 
939 	// get the directory's tree, and a pointer to the inode which should be changed
940 	BPlusTree *tree;
941 	status_t status = oldDirectory->GetTree(&tree);
942 	if (status < B_OK)
943 		RETURN_ERROR(status);
944 
945 	off_t id;
946 	status = tree->Find((const uint8 *)oldName, strlen(oldName), &id);
947 	if (status < B_OK)
948 		RETURN_ERROR(status);
949 
950 	Vnode vnode(volume, id);
951 	Inode *inode;
952 	if (vnode.Get(&inode) < B_OK)
953 		return B_IO_ERROR;
954 
955 	// Don't move a directory into one of its children - we soar up
956 	// from the newDirectory to either the root node or the old
957 	// directory, whichever comes first.
958 	// If we meet our inode on that way, we have to bail out.
959 
960 	if (oldDirectory != newDirectory) {
961 		vnode_id parent = volume->ToVnode(newDirectory->Parent());
962 		vnode_id root = volume->RootNode()->ID();
963 
964 		while (true) {
965 			if (parent == id)
966 				return B_BAD_VALUE;
967 			else if (parent == root || parent == oldDirectory->ID())
968 				break;
969 
970 			Vnode vnode(volume, parent);
971 			Inode *parentNode;
972 			if (vnode.Get(&parentNode) < B_OK)
973 				return B_ERROR;
974 
975 			parent = volume->ToVnode(parentNode->Parent());
976 		}
977 	}
978 
979 	// Everything okay? Then lets get to work...
980 
981 	Transaction transaction(volume, oldDirectory->BlockNumber());
982 
983 	// First, try to make sure there is nothing that will stop us in
984 	// the target directory - since this is the only non-critical
985 	// failure, we will test this case first
986 	BPlusTree *newTree = tree;
987 	if (newDirectory != oldDirectory) {
988 		status = newDirectory->GetTree(&newTree);
989 		if (status < B_OK)
990 			RETURN_ERROR(status);
991 	}
992 
993 	status = newTree->Insert(transaction, (const uint8 *)newName, strlen(newName), id);
994 	if (status == B_NAME_IN_USE) {
995 		// If there is already a file with that name, we have to remove
996 		// it, as long it's not a directory with files in it
997 		off_t clobber;
998 		if (newTree->Find((const uint8 *)newName, strlen(newName), &clobber) < B_OK)
999 			return B_NAME_IN_USE;
1000 		if (clobber == id)
1001 			return B_BAD_VALUE;
1002 
1003 		Vnode vnode(volume, clobber);
1004 		Inode *other;
1005 		if (vnode.Get(&other) < B_OK)
1006 			return B_NAME_IN_USE;
1007 
1008 		status = newDirectory->Remove(transaction, newName, NULL, other->IsDirectory());
1009 		if (status < B_OK)
1010 			return status;
1011 
1012 		notify_entry_removed(volume->ID(), newDirectory->ID(), newName, clobber);
1013 
1014 		status = newTree->Insert(transaction, (const uint8 *)newName, strlen(newName), id);
1015 	}
1016 	if (status < B_OK)
1017 		return status;
1018 
1019 	// If anything fails now, we have to remove the inode from the
1020 	// new directory in any case to restore the previous state
1021 	status_t bailStatus = B_OK;
1022 
1023 	// update the name only when they differ
1024 	bool nameUpdated = false;
1025 	if (strcmp(oldName, newName)) {
1026 		status = inode->SetName(transaction, newName);
1027 		if (status == B_OK) {
1028 			Index index(volume);
1029 			index.UpdateName(transaction, oldName, newName, inode);
1030 			nameUpdated = true;
1031 		}
1032 	}
1033 
1034 	if (status == B_OK) {
1035 		status = tree->Remove(transaction, (const uint8 *)oldName, strlen(oldName), id);
1036 		if (status == B_OK) {
1037 			inode->Parent() = newDirectory->BlockRun();
1038 
1039 			// if it's a directory, update the parent directory pointer
1040 			// in its tree if necessary
1041 			BPlusTree *movedTree = NULL;
1042 			if (oldDirectory != newDirectory
1043 				&& inode->IsDirectory()
1044 				&& (status = inode->GetTree(&movedTree)) == B_OK)
1045 				status = movedTree->Replace(transaction, (const uint8 *)"..", 2, newDirectory->ID());
1046 
1047 			if (status == B_OK)
1048 				status = inode->WriteBack(transaction);
1049 
1050 			if (status == B_OK) {
1051 				transaction.Done();
1052 
1053 				notify_entry_moved(volume->ID(), oldDirectory->ID(), oldName,
1054 					newDirectory->ID(), newName, id);
1055 				return B_OK;
1056 			}
1057 			// If we get here, something has gone wrong already!
1058 
1059 			// Those better don't fail, or we switch to a read-only
1060 			// device for safety reasons (Volume::Panic() does this
1061 			// for us)
1062 			// Anyway, if we overwrote a file in the target directory
1063 			// this is lost now (only in-memory, not on-disk)...
1064 			bailStatus = tree->Insert(transaction, (const uint8 *)oldName, strlen(oldName), id);
1065 			if (movedTree != NULL)
1066 				movedTree->Replace(transaction, (const uint8 *)"..", 2, oldDirectory->ID());
1067 		}
1068 	}
1069 
1070 	if (bailStatus == B_OK && nameUpdated) {
1071 		bailStatus = inode->SetName(transaction, oldName);
1072 		if (status == B_OK) {
1073 			// update inode and index
1074 			inode->WriteBack(transaction);
1075 
1076 			Index index(volume);
1077 			index.UpdateName(transaction, newName, oldName, inode);
1078 		}
1079 	}
1080 
1081 	if (bailStatus == B_OK)
1082 		bailStatus = newTree->Remove(transaction, (const uint8 *)newName, strlen(newName), id);
1083 
1084 	if (bailStatus < B_OK)
1085 		volume->Panic();
1086 
1087 	return status;
1088 }
1089 
1090 
1091 /**	Opens the file with the specified mode.
1092  */
1093 
1094 static status_t
1095 bfs_open(void *_fs, void *_node, int openMode, void **_cookie)
1096 {
1097 	FUNCTION();
1098 
1099 	Volume *volume = (Volume *)_fs;
1100 	Inode *inode = (Inode *)_node;
1101 
1102 	// opening a directory read-only is allowed, although you can't read
1103 	// any data from it.
1104 	if (inode->IsDirectory() && openMode & O_RWMASK) {
1105 		openMode = openMode & ~O_RWMASK;
1106 		// ToDo: for compatibility reasons, we don't return an error here...
1107 		// e.g. "copyattr" tries to do that
1108 		//return B_IS_A_DIRECTORY;
1109 	}
1110 
1111 	status_t status = inode->CheckPermissions(openModeToAccess(openMode)
1112 		| (openMode & O_TRUNC ? W_OK : 0));
1113 	if (status < B_OK)
1114 		RETURN_ERROR(status);
1115 
1116 	// we could actually use the cookie to keep track of:
1117 	//	- the last block_run
1118 	//	- the location in the data_stream (indirect, double indirect,
1119 	//	  position in block_run array)
1120 	//
1121 	// This could greatly speed up continuous reads of big files, especially
1122 	// in the indirect block section.
1123 
1124 	file_cookie *cookie = (file_cookie *)malloc(sizeof(file_cookie));
1125 	if (cookie == NULL)
1126 		RETURN_ERROR(B_NO_MEMORY);
1127 
1128 	// initialize the cookie
1129 	cookie->open_mode = openMode;
1130 		// needed by e.g. bfs_write() for O_APPEND
1131 	cookie->last_size = inode->Size();
1132 	cookie->last_notification = system_time();
1133 
1134 	// Should we truncate the file?
1135 	if (openMode & O_TRUNC) {
1136 		WriteLocked locked(inode->Lock());
1137 		Transaction transaction(volume, inode->BlockNumber());
1138 
1139 		status_t status = inode->SetFileSize(transaction, 0);
1140 		if (status < B_OK) {
1141 			// bfs_free_cookie() is only called if this function is successful
1142 			free(cookie);
1143 			return status;
1144 		}
1145 
1146 		transaction.Done();
1147 	}
1148 
1149 	*_cookie = cookie;
1150 	return B_OK;
1151 }
1152 
1153 
1154 /**	Read a file specified by node, using information in cookie
1155  *	and at offset specified by pos. read len bytes into buffer buf.
1156  */
1157 
1158 static status_t
1159 bfs_read(void *_ns, void *_node, void *_cookie, off_t pos, void *buffer, size_t *_length)
1160 {
1161 	//FUNCTION();
1162 	Inode *inode = (Inode *)_node;
1163 
1164 	if (!inode->HasUserAccessableStream()) {
1165 		*_length = 0;
1166 		RETURN_ERROR(B_BAD_VALUE);
1167 	}
1168 
1169 	ReadLocked locked(inode->Lock());
1170 	return inode->ReadAt(pos, (uint8 *)buffer, _length);
1171 }
1172 
1173 
1174 static status_t
1175 bfs_write(void *_ns, void *_node, void *_cookie, off_t pos, const void *buffer, size_t *_length)
1176 {
1177 	//FUNCTION();
1178 	// uncomment to be more robust against a buggy vnode layer ;-)
1179 	//if (_ns == NULL || _node == NULL || _cookie == NULL)
1180 	//	return B_BAD_VALUE;
1181 
1182 	Volume *volume = (Volume *)_ns;
1183 	Inode *inode = (Inode *)_node;
1184 
1185 	if (!inode->HasUserAccessableStream()) {
1186 		*_length = 0;
1187 		RETURN_ERROR(B_BAD_VALUE);
1188 	}
1189 
1190 	file_cookie *cookie = (file_cookie *)_cookie;
1191 
1192 	if (cookie->open_mode & O_APPEND)
1193 		pos = inode->Size();
1194 
1195 	WriteLocked locked(inode->Lock());
1196 	if (locked.IsLocked() < B_OK)
1197 		RETURN_ERROR(B_ERROR);
1198 
1199 	Transaction transaction;
1200 		// We are not starting the transaction here, since
1201 		// it might not be needed at all (the contents of
1202 		// regular files aren't logged)
1203 
1204 	status_t status = inode->WriteAt(transaction, pos, (const uint8 *)buffer, _length);
1205 
1206 	if (status == B_OK)
1207 		transaction.Done();
1208 
1209 	if (status == B_OK) {
1210 		// periodically notify if the file size has changed
1211 		// ToDo: should we better test for a change in the last_modified time only?
1212 		if (!inode->IsDeleted() && cookie->last_size != inode->Size()
1213 			&& system_time() > cookie->last_notification + INODE_NOTIFICATION_INTERVAL) {
1214 			notify_stat_changed(volume->ID(), inode->ID(),
1215 				B_STAT_MODIFICATION_TIME | B_STAT_SIZE);
1216 			cookie->last_size = inode->Size();
1217 			cookie->last_notification = system_time();
1218 		}
1219 	}
1220 
1221 	return status;
1222 }
1223 
1224 
1225 /**	Do whatever is necessary to close a file, EXCEPT for freeing
1226  *	the cookie!
1227  */
1228 
1229 static status_t
1230 bfs_close(void *_ns, void *_node, void *_cookie)
1231 {
1232 	FUNCTION();
1233 	if (_ns == NULL || _node == NULL || _cookie == NULL)
1234 		return B_BAD_VALUE;
1235 
1236 	return B_OK;
1237 }
1238 
1239 
1240 static status_t
1241 bfs_free_cookie(void *_ns, void *_node, void *_cookie)
1242 {
1243 	FUNCTION();
1244 
1245 	if (_ns == NULL || _node == NULL || _cookie == NULL)
1246 		return B_BAD_VALUE;
1247 
1248 	file_cookie *cookie = (file_cookie *)_cookie;
1249 
1250 	Volume *volume = (Volume *)_ns;
1251 	Inode *inode = (Inode *)_node;
1252 
1253 	bool needsTrimming = inode->NeedsTrimming();
1254 
1255 	if ((cookie->open_mode & O_RWMASK) != 0
1256 		&& !inode->IsDeleted()
1257 		&& (needsTrimming
1258 			|| inode->OldLastModified() != inode->LastModified()
1259 			|| inode->OldSize() != inode->Size())) {
1260 #ifdef UNSAFE_GET_VNODE
1261 		RecursiveLocker locker(volume->Lock());
1262 #endif
1263 		ReadLocked locked(inode->Lock());
1264 
1265 		// trim the preallocated blocks and update the size,
1266 		// and last_modified indices if needed
1267 
1268 		Transaction transaction(volume, inode->BlockNumber());
1269 		status_t status = B_OK;
1270 		bool changedSize = false, changedTime = false;
1271 		Index index(volume);
1272 
1273 		if (needsTrimming) {
1274 			status = inode->TrimPreallocation(transaction);
1275 			if (status < B_OK) {
1276 				FATAL(("Could not trim preallocated blocks: inode %Ld, transaction %ld: %s!\n",
1277 					inode->ID(), transaction.ID(), strerror(status)));
1278 
1279 				// we still want this transaction to succeed
1280 				status = B_OK;
1281 			}
1282 		}
1283 		if (needsTrimming || inode->OldSize() != inode->Size()) {
1284 			index.UpdateSize(transaction, inode);
1285 			changedSize = true;
1286 		}
1287 		if (inode->OldLastModified() != inode->LastModified()) {
1288 			index.UpdateLastModified(transaction, inode, inode->LastModified());
1289 			changedTime = true;
1290 
1291 			// updating the index doesn't write back the inode
1292 			inode->WriteBack(transaction);
1293 		}
1294 
1295 		if (status == B_OK)
1296 			transaction.Done();
1297 
1298 		if (changedSize || changedTime) {
1299 			notify_stat_changed(volume->ID(), inode->ID(),
1300 				(changedTime ? B_STAT_MODIFICATION_TIME : 0) | (changedSize ? B_STAT_SIZE : 0));
1301 		}
1302 	}
1303 
1304 	if (inode->Flags() & INODE_CHKBFS_RUNNING) {
1305 		// "chkbfs" exited abnormally, so we have to stop it here...
1306 		FATAL(("check process was aborted!\n"));
1307 		volume->Allocator().StopChecking(NULL);
1308 	}
1309 
1310 	return B_OK;
1311 }
1312 
1313 
1314 /**	Checks access permissions, return B_NOT_ALLOWED if the action
1315  *	is not allowed.
1316  */
1317 
1318 static status_t
1319 bfs_access(void *_ns, void *_node, int accessMode)
1320 {
1321 	//FUNCTION();
1322 
1323 	if (_ns == NULL || _node == NULL)
1324 		return B_BAD_VALUE;
1325 
1326 	Inode *inode = (Inode *)_node;
1327 	status_t status = inode->CheckPermissions(accessMode);
1328 	if (status < B_OK)
1329 		RETURN_ERROR(status);
1330 
1331 	return B_OK;
1332 }
1333 
1334 
1335 static status_t
1336 bfs_read_link(void *_ns, void *_node, char *buffer, size_t *_bufferSize)
1337 {
1338 	FUNCTION();
1339 
1340 	Inode *inode = (Inode *)_node;
1341 	size_t bufferSize = *_bufferSize;
1342 
1343 	if (!inode->IsSymLink())
1344 		RETURN_ERROR(B_BAD_VALUE);
1345 
1346 	if (inode->Flags() & INODE_LONG_SYMLINK) {
1347 		// we also need space for the terminating null byte
1348 		if (inode->Size() >= bufferSize) {
1349 			*_bufferSize = inode->Size();
1350 			return B_BUFFER_OVERFLOW;
1351 		}
1352 
1353 		status_t status = inode->ReadAt(0, (uint8 *)buffer, _bufferSize);
1354 		if (status < B_OK)
1355 			RETURN_ERROR(status);
1356 
1357 		buffer[*_bufferSize] = '\0';
1358 		return B_OK;
1359 	}
1360 
1361 	*_bufferSize = strlcpy(buffer, inode->Node().short_symlink, bufferSize) + 1;
1362 	if (*_bufferSize > bufferSize)
1363 		return B_BUFFER_OVERFLOW;
1364 
1365 	return B_OK;
1366 }
1367 
1368 
1369 //	#pragma mark -
1370 //	Directory functions
1371 
1372 
1373 static status_t
1374 bfs_create_dir(void *_ns, void *_directory, const char *name, int mode, vnode_id *_newVnodeID)
1375 {
1376 	FUNCTION_START(("name = \"%s\", perms = %d\n", name, mode));
1377 
1378 	if (_ns == NULL || _directory == NULL
1379 		|| name == NULL || *name == '\0')
1380 		RETURN_ERROR(B_BAD_VALUE);
1381 
1382 	Volume *volume = (Volume *)_ns;
1383 	Inode *directory = (Inode *)_directory;
1384 
1385 	if (!directory->IsDirectory())
1386 		RETURN_ERROR(B_BAD_TYPE);
1387 
1388 	status_t status = directory->CheckPermissions(W_OK);
1389 	if (status < B_OK)
1390 		RETURN_ERROR(status);
1391 
1392 #ifdef UNSAFE_GET_VNODE
1393 	RecursiveLocker locker(volume->Lock());
1394 #endif
1395 	Transaction transaction(volume, directory->BlockNumber());
1396 
1397 	// Inode::Create() locks the inode if we pass the "id" parameter, but we
1398 	// need it anyway
1399 	off_t id;
1400 	status = Inode::Create(transaction, directory, name, S_DIRECTORY | (mode & S_IUMSK), 0, 0, &id);
1401 	if (status == B_OK) {
1402 		*_newVnodeID = id;
1403 		put_vnode(volume->ID(), id);
1404 		transaction.Done();
1405 
1406 		notify_entry_created(volume->ID(), directory->ID(), name, id);
1407 	}
1408 
1409 	return status;
1410 }
1411 
1412 
1413 static status_t
1414 bfs_remove_dir(void *_ns, void *_directory, const char *name)
1415 {
1416 	FUNCTION_START(("name = \"%s\"\n", name));
1417 
1418 	if (_ns == NULL || _directory == NULL || name == NULL || *name == '\0')
1419 		return B_BAD_VALUE;
1420 
1421 	Volume *volume = (Volume *)_ns;
1422 	Inode *directory = (Inode *)_directory;
1423 
1424 #ifdef UNSAFE_GET_VNODE
1425 	RecursiveLocker locker(volume->Lock());
1426 #endif
1427 	Transaction transaction(volume, directory->BlockNumber());
1428 
1429 	off_t id;
1430 	status_t status = directory->Remove(transaction, name, &id, true);
1431 	if (status == B_OK) {
1432 		transaction.Done();
1433 
1434 		notify_entry_removed(volume->ID(), directory->ID(), name, id);
1435 	}
1436 
1437 	return status;
1438 }
1439 
1440 
1441 /**	Opens a directory ready to be traversed.
1442  *	bfs_open_dir() is also used by bfs_open_index_dir().
1443  */
1444 
1445 static status_t
1446 bfs_open_dir(void *_ns, void *_node, void **_cookie)
1447 {
1448 	FUNCTION();
1449 
1450 	if (_ns == NULL || _node == NULL || _cookie == NULL)
1451 		RETURN_ERROR(B_BAD_VALUE);
1452 
1453 	Inode *inode = (Inode *)_node;
1454 
1455 	status_t status = inode->CheckPermissions(R_OK);
1456 	if (status < B_OK)
1457 		RETURN_ERROR(status);
1458 
1459 	// we don't ask here for directories only, because the bfs_open_index_dir()
1460 	// function utilizes us (so we must be able to open indices as well)
1461 	if (!inode->IsContainer())
1462 		RETURN_ERROR(B_BAD_VALUE);
1463 
1464 	BPlusTree *tree;
1465 	if (inode->GetTree(&tree) != B_OK)
1466 		RETURN_ERROR(B_BAD_VALUE);
1467 
1468 	TreeIterator *iterator = new TreeIterator(tree);
1469 	if (iterator == NULL)
1470 		RETURN_ERROR(B_NO_MEMORY);
1471 
1472 	*_cookie = iterator;
1473 	return B_OK;
1474 }
1475 
1476 
1477 static status_t
1478 bfs_read_dir(void *_ns, void *_node, void *_cookie, struct dirent *dirent,
1479 	size_t bufferSize, uint32 *_num)
1480 {
1481 	FUNCTION();
1482 
1483 	TreeIterator *iterator = (TreeIterator *)_cookie;
1484 	if (iterator == NULL)
1485 		RETURN_ERROR(B_BAD_VALUE);
1486 
1487 	uint16 length;
1488 	vnode_id id;
1489 	status_t status = iterator->GetNextEntry(dirent->d_name, &length, bufferSize, &id);
1490 	if (status == B_ENTRY_NOT_FOUND) {
1491 		*_num = 0;
1492 		return B_OK;
1493 	} else if (status != B_OK)
1494 		RETURN_ERROR(status);
1495 
1496 	Volume *volume = (Volume *)_ns;
1497 
1498 	dirent->d_dev = volume->ID();
1499 	dirent->d_ino = id;
1500 
1501 	dirent->d_reclen = sizeof(struct dirent) + length;
1502 
1503 	*_num = 1;
1504 	return B_OK;
1505 }
1506 
1507 
1508 /** Sets the TreeIterator back to the beginning of the directory
1509  */
1510 
1511 static status_t
1512 bfs_rewind_dir(void * /*ns*/, void * /*node*/, void *_cookie)
1513 {
1514 	FUNCTION();
1515 	TreeIterator *iterator = (TreeIterator *)_cookie;
1516 
1517 	if (iterator == NULL)
1518 		RETURN_ERROR(B_BAD_VALUE);
1519 
1520 	return iterator->Rewind();
1521 }
1522 
1523 
1524 static status_t
1525 bfs_close_dir(void * /*ns*/, void * /*node*/, void * /*_cookie*/)
1526 {
1527 	FUNCTION();
1528 	// Do whatever you need to to close a directory, but DON'T free the cookie!
1529 	return B_OK;
1530 }
1531 
1532 
1533 static status_t
1534 bfs_free_dir_cookie(void *ns, void *node, void *_cookie)
1535 {
1536 	TreeIterator *iterator = (TreeIterator *)_cookie;
1537 
1538 	if (iterator == NULL)
1539 		RETURN_ERROR(B_BAD_VALUE);
1540 
1541 	delete iterator;
1542 	return B_OK;
1543 }
1544 
1545 
1546 //	#pragma mark -
1547 //	Attribute functions
1548 
1549 
1550 static status_t
1551 bfs_open_attr_dir(void *_ns, void *_node, void **_cookie)
1552 {
1553 	Inode *inode = (Inode *)_node;
1554 
1555 	FUNCTION();
1556 
1557 	AttributeIterator *iterator = new AttributeIterator(inode);
1558 	if (iterator == NULL)
1559 		RETURN_ERROR(B_NO_MEMORY);
1560 
1561 	*_cookie = iterator;
1562 	return B_OK;
1563 }
1564 
1565 
1566 static status_t
1567 bfs_close_attr_dir(void *ns, void *node, void *cookie)
1568 {
1569 	FUNCTION();
1570 	return B_OK;
1571 }
1572 
1573 
1574 static status_t
1575 bfs_free_attr_dir_cookie(void *ns, void *node, void *_cookie)
1576 {
1577 	FUNCTION();
1578 	AttributeIterator *iterator = (AttributeIterator *)_cookie;
1579 
1580 	if (iterator == NULL)
1581 		RETURN_ERROR(B_BAD_VALUE);
1582 
1583 	delete iterator;
1584 	return B_OK;
1585 }
1586 
1587 
1588 static status_t
1589 bfs_rewind_attr_dir(void *_ns, void *_node, void *_cookie)
1590 {
1591 	FUNCTION();
1592 
1593 	AttributeIterator *iterator = (AttributeIterator *)_cookie;
1594 	if (iterator == NULL)
1595 		RETURN_ERROR(B_BAD_VALUE);
1596 
1597 	RETURN_ERROR(iterator->Rewind());
1598 }
1599 
1600 
1601 static status_t
1602 bfs_read_attr_dir(void *_ns, void *node, void *_cookie, struct dirent *dirent,
1603 	size_t bufferSize, uint32 *_num)
1604 {
1605 	FUNCTION();
1606 	AttributeIterator *iterator = (AttributeIterator *)_cookie;
1607 
1608 	if (iterator == NULL)
1609 		RETURN_ERROR(B_BAD_VALUE);
1610 
1611 	uint32 type;
1612 	size_t length;
1613 	status_t status = iterator->GetNext(dirent->d_name, &length, &type, &dirent->d_ino);
1614 	if (status == B_ENTRY_NOT_FOUND) {
1615 		*_num = 0;
1616 		return B_OK;
1617 	} else if (status != B_OK) {
1618 		RETURN_ERROR(status);
1619 	}
1620 
1621 	Volume *volume = (Volume *)_ns;
1622 
1623 	dirent->d_dev = volume->ID();
1624 	dirent->d_reclen = sizeof(struct dirent) + length;
1625 
1626 	*_num = 1;
1627 	return B_OK;
1628 }
1629 
1630 
1631 static status_t
1632 bfs_create_attr(fs_volume _fs, fs_vnode _node, const char *name, uint32 type,
1633 	int openMode, fs_cookie *_cookie)
1634 {
1635 	FUNCTION();
1636 
1637 	Inode *inode = (Inode *)_node;
1638 	Attribute attribute(inode);
1639 
1640 	return attribute.Create(name, type, openMode, (attr_cookie **)_cookie);
1641 }
1642 
1643 
1644 static status_t
1645 bfs_open_attr(fs_volume _fs, fs_vnode _node, const char *name, int openMode,
1646 	fs_cookie *_cookie)
1647 {
1648 	FUNCTION();
1649 
1650 	Inode *inode = (Inode *)_node;
1651 	Attribute attribute(inode);
1652 
1653 	return attribute.Open(name, openMode, (attr_cookie **)_cookie);
1654 }
1655 
1656 
1657 static status_t
1658 bfs_close_attr(fs_volume _fs, fs_vnode _file, fs_cookie cookie)
1659 {
1660 	return B_OK;
1661 }
1662 
1663 
1664 static status_t
1665 bfs_free_attr_cookie(fs_volume _fs, fs_vnode _file, fs_cookie cookie)
1666 {
1667 	free(cookie);
1668 	return B_OK;
1669 }
1670 
1671 
1672 static status_t
1673 bfs_read_attr(fs_volume _fs, fs_vnode _file, fs_cookie _cookie, off_t pos,
1674 	void *buffer, size_t *_length)
1675 {
1676 	FUNCTION();
1677 
1678 	attr_cookie *cookie = (attr_cookie *)_cookie;
1679 	Inode *inode = (Inode *)_file;
1680 
1681 	Attribute attribute(inode, cookie);
1682 
1683 	return attribute.Read(cookie, pos, (uint8 *)buffer, _length);
1684 }
1685 
1686 
1687 static status_t
1688 bfs_write_attr(fs_volume _fs, fs_vnode _file, fs_cookie _cookie, off_t pos,
1689 	const void *buffer, size_t *_length)
1690 {
1691 	FUNCTION();
1692 
1693 	attr_cookie *cookie = (attr_cookie *)_cookie;
1694 	Volume *volume = (Volume *)_fs;
1695 	Inode *inode = (Inode *)_file;
1696 
1697 	Transaction transaction(volume, inode->BlockNumber());
1698 	Attribute attribute(inode, cookie);
1699 
1700 	status_t status = attribute.Write(transaction, cookie, pos, (const uint8 *)buffer, _length);
1701 	if (status == B_OK) {
1702 		transaction.Done();
1703 
1704 		notify_attribute_changed(volume->ID(), inode->ID(), cookie->name, B_ATTR_CHANGED);
1705 			// ToDo: B_ATTR_CREATED is not yet taken into account
1706 			// (we don't know what Attribute::Write() does exactly)
1707 	}
1708 
1709 	return status;
1710 }
1711 
1712 
1713 static status_t
1714 bfs_read_attr_stat(fs_volume _fs, fs_vnode _file, fs_cookie _cookie, struct stat *stat)
1715 {
1716 	FUNCTION();
1717 
1718 	attr_cookie *cookie = (attr_cookie *)_cookie;
1719 	Inode *inode = (Inode *)_file;
1720 
1721 	Attribute attribute(inode, cookie);
1722 
1723 	return attribute.Stat(*stat);
1724 }
1725 
1726 
1727 static status_t
1728 bfs_write_attr_stat(fs_volume fs, fs_vnode file, fs_cookie cookie,
1729 	const struct stat *stat, int statMask)
1730 {
1731 	return EOPNOTSUPP;
1732 }
1733 
1734 
1735 static status_t
1736 bfs_rename_attr(fs_volume _fs, fs_vnode fromFile, const char *fromName,
1737 	fs_vnode toFile, const char *toName)
1738 {
1739 	FUNCTION_START(("name = \"%s\", to = \"%s\"\n", fromName, toName));
1740 
1741 	// ToDo: implement bfs_rename_attr()!
1742 	// I'll skip the implementation here, and will do it for Haiku - at least
1743 	// there will be an API to move one attribute to another file, making that
1744 	// function much more complicated - oh joy ;-)
1745 
1746 	RETURN_ERROR(B_ENTRY_NOT_FOUND);
1747 }
1748 
1749 
1750 static status_t
1751 bfs_remove_attr(fs_volume _fs, fs_vnode _node, const char *name)
1752 {
1753 	FUNCTION_START(("name = \"%s\"\n", name));
1754 
1755 	if (name == NULL)
1756 		return B_BAD_VALUE;
1757 
1758 	Volume *volume = (Volume *)_fs;
1759 	Inode *inode = (Inode *)_node;
1760 
1761 	status_t status = inode->CheckPermissions(W_OK);
1762 	if (status < B_OK)
1763 		return status;
1764 
1765 	Transaction transaction(volume, inode->BlockNumber());
1766 
1767 	status = inode->RemoveAttribute(transaction, name);
1768 	if (status == B_OK) {
1769 		transaction.Done();
1770 
1771 		notify_attribute_changed(volume->ID(), inode->ID(), name, B_ATTR_REMOVED);
1772 	}
1773 
1774 	RETURN_ERROR(status);
1775 }
1776 
1777 
1778 //	#pragma mark -
1779 //	Index functions
1780 
1781 
1782 static status_t
1783 bfs_open_index_dir(void *_ns, void **_cookie)
1784 {
1785 	FUNCTION();
1786 	if (_ns == NULL || _cookie == NULL)
1787 		RETURN_ERROR(B_BAD_VALUE);
1788 
1789 	Volume *volume = (Volume *)_ns;
1790 
1791 	if (volume->IndicesNode() == NULL)
1792 		RETURN_ERROR(B_ENTRY_NOT_FOUND);
1793 
1794 	// Since the indices root node is just a directory, and we are storing
1795 	// a pointer to it in our Volume object, we can just use the directory
1796 	// traversal functions.
1797 	// In fact we're storing it in the Volume object for that reason.
1798 
1799 	RETURN_ERROR(bfs_open_dir(_ns, volume->IndicesNode(), _cookie));
1800 }
1801 
1802 
1803 static status_t
1804 bfs_close_index_dir(fs_volume _fs, fs_cookie _cookie)
1805 {
1806 	FUNCTION();
1807 	if (_fs == NULL || _cookie == NULL)
1808 		RETURN_ERROR(B_BAD_VALUE);
1809 
1810 	Volume *volume = (Volume *)_fs;
1811 	RETURN_ERROR(bfs_close_dir(_fs, volume->IndicesNode(), _cookie));
1812 }
1813 
1814 
1815 static status_t
1816 bfs_free_index_dir_cookie(fs_volume _fs, fs_cookie _cookie)
1817 {
1818 	FUNCTION();
1819 	if (_fs == NULL || _cookie == NULL)
1820 		RETURN_ERROR(B_BAD_VALUE);
1821 
1822 	Volume *volume = (Volume *)_fs;
1823 	RETURN_ERROR(bfs_free_dir_cookie(_fs, volume->IndicesNode(), _cookie));
1824 }
1825 
1826 
1827 static status_t
1828 bfs_rewind_index_dir(fs_volume _fs, fs_cookie _cookie)
1829 {
1830 	FUNCTION();
1831 	if (_fs == NULL || _cookie == NULL)
1832 		RETURN_ERROR(B_BAD_VALUE);
1833 
1834 	Volume *volume = (Volume *)_fs;
1835 	RETURN_ERROR(bfs_rewind_dir(_fs, volume->IndicesNode(), _cookie));
1836 }
1837 
1838 
1839 static status_t
1840 bfs_read_index_dir(fs_volume _fs, fs_cookie _cookie, struct dirent *dirent,
1841 	size_t bufferSize, uint32 *_num)
1842 {
1843 	FUNCTION();
1844 	if (_fs == NULL || _cookie == NULL)
1845 		RETURN_ERROR(B_BAD_VALUE);
1846 
1847 	Volume *volume = (Volume *)_fs;
1848 	RETURN_ERROR(bfs_read_dir(_fs, volume->IndicesNode(), _cookie, dirent, bufferSize, _num));
1849 }
1850 
1851 
1852 static status_t
1853 bfs_create_index(fs_volume _fs, const char *name, uint32 type, uint32 flags)
1854 {
1855 	FUNCTION_START(("name = \"%s\", type = %ld, flags = %ld\n", name, type, flags));
1856 	if (_fs == NULL || name == NULL || *name == '\0')
1857 		return B_BAD_VALUE;
1858 
1859 	Volume *volume = (Volume *)_fs;
1860 
1861 	if (volume->IsReadOnly())
1862 		return B_READ_ONLY_DEVICE;
1863 
1864 	// only root users are allowed to create indices
1865 	if (geteuid() != 0)
1866 		return B_NOT_ALLOWED;
1867 
1868 #ifdef UNSAFE_GET_VNODE
1869 	RecursiveLocker locker(volume->Lock());
1870 #endif
1871 	Transaction transaction(volume, volume->Indices());
1872 
1873 	Index index(volume);
1874 	status_t status = index.Create(transaction, name, type);
1875 
1876 	if (status == B_OK)
1877 		transaction.Done();
1878 
1879 	RETURN_ERROR(status);
1880 }
1881 
1882 
1883 static status_t
1884 bfs_remove_index(void *_ns, const char *name)
1885 {
1886 	FUNCTION();
1887 	if (_ns == NULL || name == NULL || *name == '\0')
1888 		return B_BAD_VALUE;
1889 
1890 	Volume *volume = (Volume *)_ns;
1891 
1892 	if (volume->IsReadOnly())
1893 		return B_READ_ONLY_DEVICE;
1894 
1895 	// only root users are allowed to remove indices
1896 	if (geteuid() != 0)
1897 		return B_NOT_ALLOWED;
1898 
1899 	Inode *indices;
1900 	if ((indices = volume->IndicesNode()) == NULL)
1901 		return B_ENTRY_NOT_FOUND;
1902 
1903 #ifdef UNSAFE_GET_VNODE
1904 	RecursiveLocker locker(volume->Lock());
1905 #endif
1906 	Transaction transaction(volume, volume->Indices());
1907 
1908 	status_t status = indices->Remove(transaction, name);
1909 	if (status == B_OK)
1910 		transaction.Done();
1911 
1912 	RETURN_ERROR(status);
1913 }
1914 
1915 
1916 static status_t
1917 bfs_stat_index(fs_volume _fs, const char *name, struct stat *stat)
1918 {
1919 	FUNCTION_START(("name = %s\n", name));
1920 	if (_fs == NULL || name == NULL || stat == NULL)
1921 		RETURN_ERROR(B_BAD_VALUE);
1922 
1923 	Volume *volume = (Volume *)_fs;
1924 	Index index(volume);
1925 	status_t status = index.SetTo(name);
1926 	if (status < B_OK)
1927 		RETURN_ERROR(status);
1928 
1929 	bfs_inode &node = index.Node()->Node();
1930 
1931 	stat->st_type = index.Type();
1932 	stat->st_size = node.data.Size();
1933 	stat->st_mode = node.Mode();
1934 
1935 	stat->st_nlink = 1;
1936 	stat->st_blksize = 65536;
1937 
1938 	stat->st_uid = node.UserID();
1939 	stat->st_gid = node.GroupID();
1940 
1941 	stat->st_atime = time(NULL);
1942 	stat->st_mtime = stat->st_ctime = (time_t)(node.LastModifiedTime() >> INODE_TIME_SHIFT);
1943 	stat->st_crtime = (time_t)(node.CreateTime() >> INODE_TIME_SHIFT);
1944 
1945 	return B_OK;
1946 }
1947 
1948 
1949 //	#pragma mark -
1950 //	Query functions
1951 
1952 
1953 static status_t
1954 bfs_open_query(void *_fs, const char *queryString, uint32 flags, port_id port,
1955 	uint32 token, void **_cookie)
1956 {
1957 	FUNCTION_START(("bfs_open_query(\"%s\", flags = %lu, port_id = %ld, token = %ld)\n",
1958 		queryString, flags, port, token));
1959 
1960 	Volume *volume = (Volume *)_fs;
1961 
1962 	Expression *expression = new Expression((char *)queryString);
1963 	if (expression == NULL)
1964 		RETURN_ERROR(B_NO_MEMORY);
1965 
1966 	if (expression->InitCheck() < B_OK) {
1967 		INFORM(("Could not parse query \"%s\", stopped at: \"%s\"\n",
1968 			queryString, expression->Position()));
1969 
1970 		delete expression;
1971 		RETURN_ERROR(B_BAD_VALUE);
1972 	}
1973 
1974 	Query *query = new Query(volume, expression, flags);
1975 	if (query == NULL) {
1976 		delete expression;
1977 		RETURN_ERROR(B_NO_MEMORY);
1978 	}
1979 
1980 	if (flags & B_LIVE_QUERY)
1981 		query->SetLiveMode(port, token);
1982 
1983 	*_cookie = (void *)query;
1984 
1985 	return B_OK;
1986 }
1987 
1988 
1989 static status_t
1990 bfs_close_query(void *fs, void *cookie)
1991 {
1992 	FUNCTION();
1993 	return B_OK;
1994 }
1995 
1996 
1997 static status_t
1998 bfs_free_query_cookie(void *fs, void *cookie)
1999 {
2000 	FUNCTION();
2001 	if (cookie == NULL)
2002 		RETURN_ERROR(B_BAD_VALUE);
2003 
2004 	Query *query = (Query *)cookie;
2005 	Expression *expression = query->GetExpression();
2006 	delete query;
2007 	delete expression;
2008 
2009 	return B_OK;
2010 }
2011 
2012 
2013 static status_t
2014 bfs_read_query(void */*fs*/, void *cookie, struct dirent *dirent, size_t bufferSize, uint32 *_num)
2015 {
2016 	FUNCTION();
2017 	Query *query = (Query *)cookie;
2018 	if (query == NULL)
2019 		RETURN_ERROR(B_BAD_VALUE);
2020 
2021 	status_t status = query->GetNextEntry(dirent, bufferSize);
2022 	if (status == B_OK)
2023 		*_num = 1;
2024 	else if (status == B_ENTRY_NOT_FOUND)
2025 		*_num = 0;
2026 	else
2027 		return status;
2028 
2029 	return B_OK;
2030 }
2031 
2032 
2033 static status_t
2034 bfs_rewind_query(void */*fs*/, void *cookie)
2035 {
2036 	FUNCTION();
2037 	Query *query = (Query *)cookie;
2038 	if (query == NULL)
2039 		RETURN_ERROR(B_BAD_VALUE);
2040 
2041 	return query->Rewind();
2042 }
2043 
2044 
2045 //	#pragma mark -
2046 
2047 
2048 static status_t
2049 bfs_std_ops(int32 op, ...)
2050 {
2051 	switch (op) {
2052 		case B_MODULE_INIT:
2053 #ifdef DEBUG
2054 			add_debugger_commands();
2055 #endif
2056 			return B_OK;
2057 		case B_MODULE_UNINIT:
2058 #ifdef DEBUG
2059 			remove_debugger_commands();
2060 #endif
2061 			return B_OK;
2062 
2063 		default:
2064 			return B_ERROR;
2065 	}
2066 }
2067 
2068 
2069 static file_system_module_info sBeFileSystem = {
2070 	{
2071 		"file_systems/bfs" B_CURRENT_FS_API_VERSION,
2072 		0,
2073 		bfs_std_ops,
2074 	},
2075 
2076 	"Be File System",
2077 
2078 	// scanning
2079 	bfs_identify_partition,
2080 	bfs_scan_partition,
2081 	bfs_free_identify_partition_cookie,
2082 	NULL,	// free_partition_content_cookie()
2083 
2084 	&bfs_mount,
2085 	&bfs_unmount,
2086 	&bfs_read_fs_stat,
2087 	&bfs_write_fs_stat,
2088 	&bfs_sync,
2089 
2090 	/* vnode operations */
2091 	&bfs_lookup,
2092 	&bfs_get_vnode_name,
2093 	&bfs_read_vnode,
2094 	&bfs_release_vnode,
2095 	&bfs_remove_vnode,
2096 
2097 	/* VM file access */
2098 	&bfs_can_page,
2099 	&bfs_read_pages,
2100 	&bfs_write_pages,
2101 
2102 	&bfs_get_file_map,
2103 
2104 	&bfs_ioctl,
2105 	&bfs_set_flags,
2106 	NULL,	// &bfs_select
2107 	NULL,	// &bfs_deselect
2108 	&bfs_fsync,
2109 
2110 	&bfs_read_link,
2111 	NULL,						// write link
2112 	&bfs_create_symlink,
2113 
2114 	&bfs_link,
2115 	&bfs_unlink,
2116 	&bfs_rename,
2117 
2118 	&bfs_access,
2119 	&bfs_read_stat,
2120 	&bfs_write_stat,
2121 
2122 	/* file operations */
2123 	&bfs_create,
2124 	&bfs_open,
2125 	&bfs_close,
2126 	&bfs_free_cookie,
2127 	&bfs_read,
2128 	&bfs_write,
2129 
2130 	/* directory operations */
2131 	&bfs_create_dir,
2132 	&bfs_remove_dir,
2133 	&bfs_open_dir,
2134 	&bfs_close_dir,
2135 	&bfs_free_dir_cookie,
2136 	&bfs_read_dir,
2137 	&bfs_rewind_dir,
2138 
2139 	/* attribute directory operations */
2140 	&bfs_open_attr_dir,
2141 	&bfs_close_attr_dir,
2142 	&bfs_free_attr_dir_cookie,
2143 	&bfs_read_attr_dir,
2144 	&bfs_rewind_attr_dir,
2145 
2146 	/* attribute operations */
2147 	&bfs_create_attr,
2148 	&bfs_open_attr,
2149 	&bfs_close_attr,
2150 	&bfs_free_attr_cookie,
2151 	&bfs_read_attr,
2152 	&bfs_write_attr,
2153 
2154 	&bfs_read_attr_stat,
2155 	&bfs_write_attr_stat,
2156 	&bfs_rename_attr,
2157 	&bfs_remove_attr,
2158 
2159 	/* index directory & index operations */
2160 	&bfs_open_index_dir,
2161 	&bfs_close_index_dir,
2162 	&bfs_free_index_dir_cookie,
2163 	&bfs_read_index_dir,
2164 	&bfs_rewind_index_dir,
2165 
2166 	&bfs_create_index,
2167 	&bfs_remove_index,
2168 	&bfs_stat_index,
2169 
2170 	/* query operations */
2171 	&bfs_open_query,
2172 	&bfs_close_query,
2173 	&bfs_free_query_cookie,
2174 	&bfs_read_query,
2175 	&bfs_rewind_query,
2176 };
2177 
2178 module_info *modules[] = {
2179 	(module_info *)&sBeFileSystem,
2180 	NULL,
2181 };
2182