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