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