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