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