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