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