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