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