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