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