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