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