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