1/*
2 * Copyright 2019, Bharathi Ramana Joshi, joshibharathiramana@gmail.com
3 * Copyright 2019, Les De Ridder, les@lesderid.net
4 * Copyright 2017, Ch��� V�� Gia Hy, cvghy116@gmail.com.
5 * Copyright 2011, J��r��me Duval, korli@users.berlios.de.
6 * Copyright 2008, Axel D��rfler, axeld@pinc-software.de.
7 *
8 * This file may be used under the terms of the MIT License.
9 */
10
11
12#include "Attribute.h"
13#include "AttributeIterator.h"
14#include "btrfs.h"
15#include "btrfs_disk_system.h"
16#include "DirectoryIterator.h"
17#include "Inode.h"
18#include "system_dependencies.h"
19#include "Utility.h"
20
21
22#ifdef FS_SHELL
23#define ERROR(x...) TRACE(x)
24#define INFORM(x...) TRACE(x)
25#define init_debugging()
26#define exit_debugging()
27#define FUNCTION() dprintf("\33[34mbtrfs:\33[0m %s()\n",__PRETTY_FUNCTION__);
28#define REPORT_ERROR(status) \
29	dprintf("btrfs: %s:%d: %s\n", __FUNCTION__, __LINE__, strerror(status));
30#define RETURN_ERROR(err) \
31	{ status_t _status = err; if (_status < B_OK) REPORT_ERROR(_status); return _status;}
32#define PRINT(x) { dprintf("btrfs: "); dprintf x; }
33#else
34#include <DebugSupport.h>
35#endif
36
37
38//#define TRACE_BTRFS
39#ifdef TRACE_BTRFS
40#	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
41#else
42#	define TRACE(x...) ;
43#endif
44
45#define BTRFS_IO_SIZE	65536
46
47
48struct identify_cookie {
49	btrfs_super_block super_block;
50};
51
52
53//!	btrfs_io() callback hook
54static status_t
55iterative_io_get_vecs_hook(void* cookie, io_request* request, off_t offset,
56	size_t size, struct file_io_vec* vecs, size_t* _count)
57{
58	Inode* inode = (Inode*)cookie;
59
60	return file_map_translate(inode->Map(), offset, size, vecs, _count,
61		inode->GetVolume()->BlockSize());
62}
63
64
65//!	btrfs_io() callback hook
66static status_t
67iterative_io_finished_hook(void* cookie, io_request* request, status_t status,
68	bool partialTransfer, size_t bytesTransferred)
69{
70	Inode* inode = (Inode*)cookie;
71	rw_lock_read_unlock(inode->Lock());
72	return B_OK;
73}
74
75
76//	#pragma mark - Scanning
77
78
79static float
80btrfs_identify_partition(int fd, partition_data* partition, void** _cookie)
81{
82	btrfs_super_block superBlock;
83	status_t status = Volume::Identify(fd, &superBlock);
84	if (status != B_OK)
85		return -1;
86
87	identify_cookie* cookie = new identify_cookie;
88	memcpy(&cookie->super_block, &superBlock, sizeof(btrfs_super_block));
89
90	*_cookie = cookie;
91	return 0.8f;
92}
93
94
95static status_t
96btrfs_scan_partition(int fd, partition_data* partition, void* _cookie)
97{
98	identify_cookie* cookie = (identify_cookie*)_cookie;
99
100	partition->status = B_PARTITION_VALID;
101	partition->flags |= B_PARTITION_FILE_SYSTEM;
102	partition->content_size = cookie->super_block.TotalSize();
103	partition->block_size = cookie->super_block.BlockSize();
104	partition->content_name = strdup(cookie->super_block.label);
105	if (partition->content_name == NULL)
106		return B_NO_MEMORY;
107
108	return B_OK;
109}
110
111
112static void
113btrfs_free_identify_partition_cookie(partition_data* partition, void* _cookie)
114{
115	delete (identify_cookie*)_cookie;
116}
117
118
119//	#pragma mark -
120
121
122static status_t
123btrfs_mount(fs_volume* _volume, const char* device, uint32 flags,
124	const char* args, ino_t* _rootID)
125{
126	Volume* volume = new(std::nothrow) Volume(_volume);
127	if (volume == NULL)
128		return B_NO_MEMORY;
129
130	// TODO: this is a bit hacky: we can't use publish_vnode() to publish
131	// the root node, or else its file cache cannot be created (we could
132	// create it later, though). Therefore we're using get_vnode() in Mount(),
133	// but that requires us to export our volume data before calling it.
134	_volume->private_volume = volume;
135	_volume->ops = &gBtrfsVolumeOps;
136
137	status_t status = volume->Mount(device, flags);
138	if (status != B_OK) {
139		ERROR("Failed mounting the volume. Error: %s\n", strerror(status));
140		delete volume;
141		return status;
142	}
143
144	*_rootID = volume->RootNode()->ID();
145	return B_OK;
146}
147
148
149static status_t
150btrfs_unmount(fs_volume* _volume)
151{
152	Volume* volume = (Volume*)_volume->private_volume;
153
154	status_t status = volume->Unmount();
155	delete volume;
156
157	return status;
158}
159
160
161static status_t
162btrfs_read_fs_info(fs_volume* _volume, struct fs_info* info)
163{
164	Volume* volume = (Volume*)_volume->private_volume;
165
166	// File system flags
167	info->flags = B_FS_IS_PERSISTENT | B_FS_HAS_ATTR
168		| (volume->IsReadOnly() ? B_FS_IS_READONLY : 0);
169	info->io_size = BTRFS_IO_SIZE;
170	info->block_size = volume->BlockSize();
171	info->total_blocks = volume->SuperBlock().TotalSize() / volume->BlockSize();
172	info->free_blocks = 0; //volume->NumFreeBlocks();
173
174	// Volume name
175	strlcpy(info->volume_name, volume->Name(), sizeof(info->volume_name));
176
177	// File system name
178	strlcpy(info->fsh_name, "btrfs", sizeof(info->fsh_name));
179
180	return B_OK;
181}
182
183
184static status_t
185btrfs_write_fs_info(fs_volume* _volume, const struct fs_info* info, uint32 mask)
186{
187	Volume* volume = (Volume*)_volume->private_volume;
188	if (volume->IsReadOnly())
189		return B_READ_ONLY_DEVICE;
190
191	if (mask & ~FS_WRITE_FSINFO_NAME != 0)
192		return B_NOT_SUPPORTED;
193
194	MutexLocker locker(volume->GetLock());
195	status_t status = B_BAD_VALUE;
196
197	if (mask & FS_WRITE_FSINFO_NAME) {
198		btrfs_super_block& superBlock = volume->SuperBlock();
199
200		strncpy(superBlock.label, info->volume_name,
201			sizeof(superBlock.label) - 1);
202		superBlock.label[sizeof(superBlock.label) - 1] = '\0';
203
204		status = volume->WriteSuperBlock();
205	}
206
207	return status;
208}
209
210
211//	#pragma mark -
212
213
214static status_t
215btrfs_get_vnode(fs_volume* _volume, ino_t id, fs_vnode* _node, int* _type,
216	uint32* _flags, bool reenter)
217{
218	Volume* volume = (Volume*)_volume->private_volume;
219
220	Inode* inode = new(std::nothrow) Inode(volume, id);
221	if (inode == NULL)
222		return B_NO_MEMORY;
223
224	status_t status = inode->InitCheck();
225	if (status != B_OK) {
226		delete inode;
227		ERROR("get_vnode: InitCheck() failed. Error: %s\n", strerror(status));
228		return status;
229	}
230
231	_node->private_node = inode;
232	_node->ops = &gBtrfsVnodeOps;
233	*_type = inode->Mode();
234	*_flags = 0;
235
236	return B_OK;
237}
238
239
240static status_t
241btrfs_put_vnode(fs_volume* _volume, fs_vnode* _node, bool reenter)
242{
243	delete (Inode*)_node->private_node;
244	return B_OK;
245}
246
247
248static bool
249btrfs_can_page(fs_volume* _volume, fs_vnode* _node, void* _cookie)
250{
251	return true;
252}
253
254
255static status_t
256btrfs_read_pages(fs_volume* _volume, fs_vnode* _node, void* _cookie,
257	off_t pos, const iovec* vecs, size_t count, size_t* _numBytes)
258{
259	Volume* volume = (Volume*)_volume->private_volume;
260	Inode* inode = (Inode*)_node->private_node;
261
262	if (inode->FileCache() == NULL)
263		return B_BAD_VALUE;
264
265	rw_lock_read_lock(inode->Lock());
266
267	uint32 vecIndex = 0;
268	size_t vecOffset = 0;
269	size_t bytesLeft = *_numBytes;
270	status_t status;
271
272	while (true) {
273		file_io_vec fileVecs[8];
274		size_t fileVecCount = 8;
275
276		status = file_map_translate(inode->Map(), pos, bytesLeft, fileVecs,
277			&fileVecCount, 0);
278		if (status != B_OK && status != B_BUFFER_OVERFLOW)
279			break;
280
281		bool bufferOverflow = status == B_BUFFER_OVERFLOW;
282
283		size_t bytes = bytesLeft;
284		status = read_file_io_vec_pages(volume->Device(), fileVecs,
285			fileVecCount, vecs, count, &vecIndex, &vecOffset, &bytes);
286		if (status != B_OK || !bufferOverflow)
287			break;
288
289		pos += bytes;
290		bytesLeft -= bytes;
291	}
292
293	rw_lock_read_unlock(inode->Lock());
294
295	return status;
296}
297
298
299static status_t
300btrfs_io(fs_volume* _volume, fs_vnode* _node, void* _cookie,
301	io_request* request)
302{
303	Volume* volume = (Volume*)_volume->private_volume;
304	Inode* inode = (Inode*)_node->private_node;
305
306#ifndef FS_SHELL
307	if (io_request_is_write(request) && volume->IsReadOnly()) {
308		notify_io_request(request, B_READ_ONLY_DEVICE);
309		return B_READ_ONLY_DEVICE;
310	}
311#endif
312
313	if (inode->FileCache() == NULL) {
314#ifndef FS_SHELL
315		notify_io_request(request, B_BAD_VALUE);
316#endif
317		return B_BAD_VALUE;
318	}
319
320	// We lock the node here and will unlock it in the "finished" hook.
321	rw_lock_read_lock(inode->Lock());
322
323	return do_iterative_fd_io(volume->Device(), request,
324		iterative_io_get_vecs_hook, iterative_io_finished_hook, inode);
325}
326
327
328static status_t
329btrfs_get_file_map(fs_volume* _volume, fs_vnode* _node, off_t offset,
330	size_t size, struct file_io_vec* vecs, size_t* _count)
331{
332	TRACE("btrfs_get_file_map()\n");
333	Inode* inode = (Inode*)_node->private_node;
334	size_t index = 0, max = *_count;
335
336	while (true) {
337		off_t blockOffset;
338		off_t blockLength;
339		status_t status = inode->FindBlock(offset, blockOffset, &blockLength);
340		if (status != B_OK)
341			return status;
342
343		if (index > 0 && (vecs[index - 1].offset
344				== blockOffset - vecs[index - 1].length)) {
345			vecs[index - 1].length += blockLength;
346		} else {
347			if (index >= max) {
348				// we're out of file_io_vecs; let's bail out
349				*_count = index;
350				return B_BUFFER_OVERFLOW;
351			}
352
353			vecs[index].offset = blockOffset;
354			vecs[index].length = blockLength;
355			index++;
356		}
357
358		offset += blockLength;
359		size -= blockLength;
360
361		if ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) {
362			// We're done!
363			*_count = index;
364			TRACE("btrfs_get_file_map for inode %" B_PRIdINO "\n", inode->ID());
365			return B_OK;
366		}
367	}
368
369	// can never get here
370	return B_ERROR;
371}
372
373
374//	#pragma mark -
375
376
377static status_t
378btrfs_lookup(fs_volume* _volume, fs_vnode* _directory, const char* name,
379	ino_t* _vnodeID)
380{
381	TRACE("btrfs_lookup: name address: %p (%s)\n", name, name);
382	Volume* volume = (Volume*)_volume->private_volume;
383	Inode* directory = (Inode*)_directory->private_node;
384
385	// check access permissions
386	status_t status = directory->CheckPermissions(X_OK);
387	if (status < B_OK)
388		return status;
389
390	status = DirectoryIterator(directory).Lookup(name, strlen(name), _vnodeID);
391	if (status != B_OK) {
392		if (status == B_ENTRY_NOT_FOUND)
393			entry_cache_add_missing(volume->ID(), directory->ID(), name);
394		return status;
395	}
396	entry_cache_add(volume->ID(), directory->ID(), name, *_vnodeID);
397
398	return get_vnode(volume->FSVolume(), *_vnodeID, NULL);
399}
400
401
402static status_t
403btrfs_ioctl(fs_volume* _volume, fs_vnode* _node, void* _cookie, uint32 cmd,
404	void* buffer, size_t bufferLength)
405{
406	TRACE("ioctl: %" B_PRIu32 "\n", cmd);
407
408	/*Volume* volume = (Volume*)_volume->private_volume;*/
409	return B_DEV_INVALID_IOCTL;
410}
411
412
413static status_t
414btrfs_read_stat(fs_volume* _volume, fs_vnode* _node, struct stat* stat)
415{
416	Inode* inode = (Inode*)_node->private_node;
417
418	stat->st_dev = inode->GetVolume()->ID();
419	stat->st_ino = inode->ID();
420	stat->st_nlink = 1;
421	stat->st_blksize = BTRFS_IO_SIZE;
422
423	stat->st_uid = inode->UserID();
424	stat->st_gid = inode->GroupID();
425	stat->st_mode = inode->Mode();
426	stat->st_type = 0;
427
428	inode->GetAccessTime(stat->st_atim);
429	inode->GetModificationTime(stat->st_mtim);
430	inode->GetChangeTime(stat->st_ctim);
431	inode->GetCreationTime(stat->st_crtim);
432
433	stat->st_size = inode->Size();
434	stat->st_blocks = (inode->Size() + 511) / 512;
435
436	return B_OK;
437}
438
439
440static status_t
441btrfs_write_stat(fs_volume* _volume, fs_vnode* _node, const struct stat* stat,
442	uint32 mask)
443{
444	FUNCTION();
445
446	Volume* volume = (Volume*)_volume->private_volume;
447	Inode* inode = (Inode*)_node->private_node;
448
449	if (volume->IsReadOnly())
450		return B_READ_ONLY_DEVICE;
451
452	btrfs_inode& node = inode->Node();
453	bool updateTime = false;
454	uid_t uid = geteuid();
455
456	bool isOwnerOrRoot = uid == 0 || uid == (uid_t)node.UserID();
457	bool hasWriteAccess = inode->CheckPermissions(W_OK) == B_OK;
458
459	Transaction transaction(volume);
460
461	if ((mask & B_STAT_SIZE) != 0 && inode->Size() != stat->st_size) {
462		if (inode->IsDirectory())
463			return B_IS_A_DIRECTORY;
464		if (!inode->IsFile())
465			return B_BAD_VALUE;
466		if (!hasWriteAccess)
467			RETURN_ERROR(B_NOT_ALLOWED);
468
469		//TODO: implement file shrinking/growing
470		return B_NOT_SUPPORTED;
471	}
472
473	if ((mask & B_STAT_UID) != 0) {
474		if (uid != 0)
475			RETURN_ERROR(B_NOT_ALLOWED);
476		node.uid = B_HOST_TO_LENDIAN_INT32(stat->st_uid);
477		updateTime = true;
478	}
479
480	if ((mask & B_STAT_GID) != 0) {
481		if (!isOwnerOrRoot)
482			RETURN_ERROR(B_NOT_ALLOWED);
483		node.gid = B_HOST_TO_LENDIAN_INT32(stat->st_gid);
484		updateTime = true;
485	}
486
487	if ((mask & B_STAT_MODE) != 0) {
488		if (!isOwnerOrRoot)
489			RETURN_ERROR(B_NOT_ALLOWED);
490		PRINT(("original mode = %ld, stat->st_mode = %d\n", node.Mode(),
491			stat->st_mode));
492		node.mode = B_HOST_TO_LENDIAN_INT32((node.Mode() & ~S_IUMSK)
493			| (stat->st_mode & S_IUMSK));
494		updateTime = true;
495	}
496
497	if ((mask & B_STAT_CREATION_TIME) != 0) {
498		if (!isOwnerOrRoot && !hasWriteAccess)
499			RETURN_ERROR(B_NOT_ALLOWED);
500		btrfs_inode::SetTime(node.change_time, stat->st_crtim);
501	}
502
503	if ((mask & B_STAT_MODIFICATION_TIME) != 0) {
504		if (!isOwnerOrRoot && !hasWriteAccess)
505			RETURN_ERROR(B_NOT_ALLOWED);
506		btrfs_inode::SetTime(node.change_time, stat->st_mtim);
507	}
508
509	if ((mask & B_STAT_CHANGE_TIME) != 0 || updateTime) {
510		if (!isOwnerOrRoot && !hasWriteAccess)
511			RETURN_ERROR(B_NOT_ALLOWED);
512		if ((mask & B_STAT_CHANGE_TIME) == 0) {
513			uint64_t microseconds = real_time_clock_usecs();
514			struct timespec t;
515			t.tv_sec = microseconds / 1000000;
516			t.tv_nsec = microseconds % 1000000;
517			btrfs_inode::SetTime(node.change_time, t);
518		} else
519			btrfs_inode::SetTime(node.change_time, stat->st_ctim);
520	}
521
522	status_t status = transaction.Done();
523	if (status == B_OK) {
524		ino_t pid;
525		inode->FindParent(&pid);
526		notify_stat_changed(volume->ID(), pid, inode->ID(), mask);
527	}
528
529	return status;
530}
531
532
533static status_t
534btrfs_open(fs_volume* /*_volume*/, fs_vnode* _node, int openMode,
535	void** _cookie)
536{
537	Inode* inode = (Inode*)_node->private_node;
538
539	// opening a directory read-only is allowed, although you can't read
540	// any data from it.
541	if (inode->IsDirectory() && (openMode & O_RWMASK) != 0)
542		return B_IS_A_DIRECTORY;
543
544	status_t status =  inode->CheckPermissions(open_mode_to_access(openMode)
545		| (openMode & O_TRUNC ? W_OK : 0));
546	if (status != B_OK)
547		return status;
548
549	// Prepare the cookie
550	file_cookie* cookie = new(std::nothrow) file_cookie;
551	if (cookie == NULL)
552		return B_NO_MEMORY;
553	ObjectDeleter<file_cookie> cookieDeleter(cookie);
554
555	cookie->open_mode = openMode & BTRFS_OPEN_MODE_USER_MASK;
556	cookie->last_size = inode->Size();
557	cookie->last_notification = system_time();
558
559	if ((openMode & O_NOCACHE) != 0 && inode->FileCache() != NULL) {
560		// Disable the file cache, if requested?
561		status = file_cache_disable(inode->FileCache());
562		if (status != B_OK)
563			return status;
564	}
565
566	cookieDeleter.Detach();
567	*_cookie = cookie;
568
569	return B_OK;
570}
571
572
573status_t
574btrfs_write(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
575		const void* buffer, size_t* _length)
576{
577	Volume* volume = (Volume*)_volume->private_volume;
578	Inode* inode = (Inode*)_node->private_node;
579
580	if (volume->IsReadOnly())
581		return B_READ_ONLY_DEVICE;
582
583	if (pos < 0)
584		return B_BAD_VALUE;
585
586	if (!inode->IsFile())
587		return B_BAD_VALUE;
588
589	return B_NOT_SUPPORTED;
590}
591
592
593static status_t
594btrfs_read(fs_volume* _volume, fs_vnode* _node, void* _cookie, off_t pos,
595	void* buffer, size_t* _length)
596{
597	FUNCTION();
598	Inode* inode = (Inode*)_node->private_node;
599
600	if (!inode->IsFile()) {
601		*_length = 0;
602		return inode->IsDirectory() ? B_IS_A_DIRECTORY : B_BAD_VALUE;
603	}
604
605	return inode->ReadAt(pos, (uint8*)buffer, _length);
606}
607
608
609static status_t
610btrfs_close(fs_volume* _volume, fs_vnode* _node, void* _cookie)
611{
612	return B_OK;
613}
614
615
616static status_t
617btrfs_free_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
618{
619	file_cookie* cookie = (file_cookie*)_cookie;
620	Volume* volume = (Volume*)_volume->private_volume;
621	Inode* inode = (Inode*)_node->private_node;
622
623	if (inode->Size() != cookie->last_size)
624		notify_stat_changed(volume->ID(), -1, inode->ID(), B_STAT_SIZE);
625
626	delete cookie;
627	return B_OK;
628}
629
630
631static status_t
632btrfs_access(fs_volume* _volume, fs_vnode* _node, int accessMode)
633{
634	Inode* inode = (Inode*)_node->private_node;
635	return inode->CheckPermissions(accessMode);
636}
637
638
639static status_t
640btrfs_read_link(fs_volume* _volume, fs_vnode* _node, char* buffer,
641	size_t* _bufferSize)
642{
643	FUNCTION();
644	Inode* inode = (Inode*)_node->private_node;
645
646	if (!inode->IsSymLink())
647		return B_BAD_VALUE;
648
649	status_t result = inode->ReadAt(0, reinterpret_cast<uint8*>(buffer),
650		_bufferSize);
651	if (result != B_OK)
652		return result;
653
654	*_bufferSize = inode->Size();
655	return B_OK;
656}
657
658
659status_t
660btrfs_unlink(fs_volume* _volume, fs_vnode* _directory, const char* name)
661{
662	if (!strcmp(name, "..") || !strcmp(name, "."))
663		return B_NOT_ALLOWED;
664
665	Volume* volume = (Volume*)_volume->private_volume;
666	Inode* directory = (Inode*)_directory->private_node;
667
668	status_t status = directory->CheckPermissions(W_OK);
669	if (status < B_OK)
670		return status;
671
672	Transaction transaction(volume);
673	BTree::Path path(volume->FSTree());
674
675	ino_t id;
676	status = DirectoryIterator(directory).Lookup(name, strlen(name), &id);
677	if (status != B_OK)
678		return status;
679
680	Inode inode(volume, id);
681	status = inode.InitCheck();
682	if (status != B_OK)
683		return status;
684
685	status = inode.Remove(transaction, &path);
686	if (status != B_OK)
687		return status;
688	status = inode.Dereference(transaction, &path, directory->ID(), name);
689	if (status != B_OK)
690		return status;
691
692	entry_cache_remove(volume->ID(), directory->ID(), name);
693
694	status = transaction.Done();
695	if (status == B_OK)
696		notify_entry_removed(volume->ID(), directory->ID(), name, id);
697	else
698		entry_cache_add(volume->ID(), directory->ID(), name, id);
699
700	return status;
701}
702
703
704//	#pragma mark - Directory functions
705
706
707static status_t
708btrfs_create_dir(fs_volume* _volume, fs_vnode* _directory, const char* name,
709	int mode)
710{
711	Volume* volume = (Volume*)_volume->private_volume;
712	Inode* directory = (Inode*)_directory->private_node;
713	BTree::Path path(volume->FSTree());
714
715	if (volume->IsReadOnly())
716		return B_READ_ONLY_DEVICE;
717
718	if (!directory->IsDirectory())
719		return B_NOT_A_DIRECTORY;
720
721	status_t status = directory->CheckPermissions(W_OK);
722	if (status < B_OK)
723		return status;
724
725	Transaction transaction(volume);
726	ino_t id = volume->GetNextInodeID();
727	mode = S_DIRECTORY | (mode & S_IUMSK);
728	Inode* inode = Inode::Create(transaction, id, directory, mode);
729	if (inode == NULL)
730		return B_NO_MEMORY;
731
732	status = inode->Insert(transaction, &path);
733	if (status != B_OK)
734		return status;
735
736	status = inode->MakeReference(transaction, &path, directory, name, mode);
737	if (status != B_OK)
738		return status;
739
740	put_vnode(volume->FSVolume(), inode->ID());
741	entry_cache_add(volume->ID(), directory->ID(), name, inode->ID());
742
743	status = transaction.Done();
744	if (status == B_OK)
745		notify_entry_created(volume->ID(), directory->ID(), name, inode->ID());
746	else
747		entry_cache_remove(volume->ID(), directory->ID(), name);
748
749	return status;
750}
751
752
753static status_t
754btrfs_remove_dir(fs_volume* _volume, fs_vnode* _directory, const char* name)
755{
756	Volume* volume = (Volume*)_volume->private_volume;
757	Inode* directory = (Inode*)_directory->private_node;
758
759	Transaction transaction(volume);
760	BTree::Path path(volume->FSTree());
761
762	ino_t id;
763	status_t status = DirectoryIterator(directory).Lookup(name, strlen(name),
764		&id);
765	if (status != B_OK)
766		return status;
767
768	Inode inode(volume, id);
769	status = inode.InitCheck();
770	if (status != B_OK)
771		return status;
772
773	status = inode.Remove(transaction, &path);
774	if (status != B_OK)
775		return status;
776	status = inode.Dereference(transaction, &path, directory->ID(), name);
777	if (status != B_OK)
778		return status;
779
780	entry_cache_remove(volume->ID(), directory->ID(), name);
781	entry_cache_remove(volume->ID(), id, "..");
782
783	status = transaction.Done();
784	if (status == B_OK)
785		notify_entry_removed(volume->ID(), directory->ID(), name, id);
786	else {
787		entry_cache_add(volume->ID(), directory->ID(), name, id);
788		entry_cache_add(volume->ID(), id, "..", id);
789	}
790
791	return status;
792}
793
794
795static status_t
796btrfs_open_dir(fs_volume* /*_volume*/, fs_vnode* _node, void** _cookie)
797{
798	Inode* inode = (Inode*)_node->private_node;
799	status_t status = inode->CheckPermissions(R_OK);
800	if (status < B_OK)
801		return status;
802
803	if (!inode->IsDirectory())
804		return B_NOT_A_DIRECTORY;
805
806	DirectoryIterator* iterator = new(std::nothrow) DirectoryIterator(inode);
807	if (iterator == NULL || iterator->InitCheck() != B_OK) {
808		delete iterator;
809		return B_NO_MEMORY;
810	}
811
812	*_cookie = iterator;
813	return B_OK;
814}
815
816
817static status_t
818btrfs_read_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie,
819	struct dirent* dirent, size_t bufferSize, uint32* _num)
820{
821	DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
822	Volume* volume = (Volume*)_volume->private_volume;
823
824	uint32 maxCount = *_num;
825	uint32 count = 0;
826
827	while (count < maxCount && bufferSize > sizeof(struct dirent)) {
828		ino_t id;
829		size_t length = bufferSize - offsetof(struct dirent, d_name);
830
831		status_t status = iterator->GetNext(dirent->d_name, &length,
832			&id);
833
834		if (status == B_ENTRY_NOT_FOUND)
835			break;
836
837		if (status == B_BUFFER_OVERFLOW) {
838			// the remaining name buffer length was too small
839			if (count == 0)
840				return B_BUFFER_OVERFLOW;
841			break;
842		}
843
844		if (status != B_OK)
845			return status;
846
847		dirent->d_dev = volume->ID();
848		dirent->d_ino = id;
849		dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
850
851		bufferSize -= dirent->d_reclen;
852		dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
853		count++;
854	}
855
856	*_num = count;
857	return B_OK;
858}
859
860
861static status_t
862btrfs_rewind_dir(fs_volume* /*_volume*/, fs_vnode* /*node*/, void* _cookie)
863{
864	DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
865
866	return iterator->Rewind();
867}
868
869
870static status_t
871btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/,
872	   	void * /*_cookie*/)
873{
874	return B_OK;
875}
876
877
878static status_t
879btrfs_free_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
880{
881	delete (DirectoryIterator*)_cookie;
882	return B_OK;
883}
884
885
886static status_t
887btrfs_open_attr_dir(fs_volume* _volume, fs_vnode* _node, void** _cookie)
888{
889	Inode* inode = (Inode*)_node->private_node;
890	TRACE("%s()\n", __FUNCTION__);
891
892	// on directories too ?
893	if (!inode->IsFile())
894		return EINVAL;
895
896	AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode);
897	if (iterator == NULL || iterator->InitCheck() != B_OK) {
898		delete iterator;
899		return B_NO_MEMORY;
900	}
901
902	*_cookie = iterator;
903	return B_OK;
904}
905
906
907static status_t
908btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
909{
910	TRACE("%s()\n", __FUNCTION__);
911	return B_OK;
912}
913
914
915static status_t
916btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
917{
918	TRACE("%s()\n", __FUNCTION__);
919	delete (AttributeIterator*)_cookie;
920	return B_OK;
921}
922
923
924static status_t
925btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node,
926	void* _cookie, struct dirent* dirent, size_t bufferSize, uint32* _num)
927{
928	TRACE("%s()\n", __FUNCTION__);
929	AttributeIterator* iterator = (AttributeIterator*)_cookie;
930
931	size_t length = bufferSize;
932	status_t status = iterator->GetNext(dirent->d_name, &length);
933	if (status == B_ENTRY_NOT_FOUND) {
934		*_num = 0;
935		return B_OK;
936	}
937
938	if (status != B_OK)
939		return status;
940
941	Volume* volume = (Volume*)_volume->private_volume;
942	dirent->d_dev = volume->ID();
943	dirent->d_reclen = offsetof(struct dirent, d_name) + length + 1;
944	*_num = 1;
945
946	return B_OK;
947}
948
949
950static status_t
951btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
952{
953	AttributeIterator* iterator = (AttributeIterator*)_cookie;
954	return iterator->Rewind();
955}
956
957
958	/* attribute operations */
959static status_t
960btrfs_create_attr(fs_volume* _volume, fs_vnode* _node,
961	const char* name, uint32 type, int openMode, void** _cookie)
962{
963	return EROFS;
964}
965
966
967static status_t
968btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
969	int openMode, void** _cookie)
970{
971	TRACE("%s()\n", __FUNCTION__);
972
973	Inode* inode = (Inode*)_node->private_node;
974	Attribute attribute(inode);
975
976	return attribute.Open(name, openMode, (attr_cookie**)_cookie);
977}
978
979
980static status_t
981btrfs_close_attr(fs_volume* _volume, fs_vnode* _node,
982	void* cookie)
983{
984	return B_OK;
985}
986
987
988static status_t
989btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node,
990	void* cookie)
991{
992	delete (attr_cookie*)cookie;
993	return B_OK;
994}
995
996
997static status_t
998btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
999	off_t pos, void* buffer, size_t* _length)
1000{
1001	TRACE("%s()\n", __FUNCTION__);
1002
1003	attr_cookie* cookie = (attr_cookie*)_cookie;
1004	Inode* inode = (Inode*)_node->private_node;
1005
1006	Attribute attribute(inode, cookie);
1007
1008	return attribute.Read(cookie, pos, (uint8*)buffer, _length);
1009}
1010
1011
1012static status_t
1013btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie,
1014	off_t pos, const void* buffer, size_t* length)
1015{
1016	return EROFS;
1017}
1018
1019
1020static status_t
1021btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node,
1022	void* _cookie, struct stat* stat)
1023{
1024	attr_cookie* cookie = (attr_cookie*)_cookie;
1025	Inode* inode = (Inode*)_node->private_node;
1026
1027	Attribute attribute(inode, cookie);
1028
1029	return attribute.Stat(*stat);
1030}
1031
1032
1033static status_t
1034btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node,
1035	void* cookie, const struct stat* stat, int statMask)
1036{
1037	return EROFS;
1038}
1039
1040
1041static status_t
1042btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode,
1043	const char* fromName, fs_vnode* toVnode, const char* toName)
1044{
1045	return EROFS;
1046}
1047
1048
1049static status_t
1050btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode,
1051	const char* name)
1052{
1053	return EROFS;
1054}
1055
1056static uint32
1057btrfs_get_supported_operations(partition_data* partition, uint32 mask)
1058{
1059	// TODO: We should at least check the partition size.
1060	return B_DISK_SYSTEM_SUPPORTS_INITIALIZING
1061		| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
1062//		| B_DISK_SYSTEM_SUPPORTS_WRITING
1063		;
1064}
1065
1066
1067static status_t
1068btrfs_initialize(int fd, partition_id partitionID, const char* name,
1069	const char* parameterString, off_t partitionSize, disk_job_id job)
1070{
1071	// check name
1072	status_t status = check_volume_name(name);
1073	if (status != B_OK)
1074		return status;
1075
1076	// parse parameters
1077	initialize_parameters parameters;
1078	status = parse_initialize_parameters(parameterString, parameters);
1079	if (status != B_OK)
1080		return status;
1081
1082	update_disk_device_job_progress(job, 0);
1083
1084	// initialize the volume
1085	Volume volume(NULL);
1086	status = volume.Initialize(fd, name, parameters.blockSize,
1087		parameters.sectorSize);
1088	if (status < B_OK) {
1089		INFORM("Initializing volume failed: %s\n", strerror(status));
1090		return status;
1091	}
1092
1093	// rescan partition
1094	status = scan_partition(partitionID);
1095	if (status != B_OK)
1096		return status;
1097
1098	update_disk_device_job_progress(job, 1);
1099
1100	// print some info, if desired
1101	if (parameters.verbose) {
1102		btrfs_super_block super = volume.SuperBlock();
1103
1104		INFORM("Disk was initialized successfully.\n");
1105		INFORM("\tlabel: \"%s\"\n", super.label);
1106		INFORM("\tblock size: %u bytes\n", (unsigned)super.BlockSize());
1107		INFORM("\tsector size: %u bytes\n", (unsigned)super.SectorSize());
1108	}
1109
1110	return B_OK;
1111}
1112
1113
1114static status_t
1115btrfs_uninitialize(int fd, partition_id partitionID, off_t partitionSize,
1116	uint32 blockSize, disk_job_id job)
1117{
1118	if (blockSize == 0)
1119		return B_BAD_VALUE;
1120
1121	update_disk_device_job_progress(job, 0.0);
1122
1123	// just overwrite the superblock
1124	btrfs_super_block superBlock;
1125	memset(&superBlock, 0, sizeof(superBlock));
1126
1127	if (write_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, &superBlock,
1128			sizeof(superBlock)) < 0)
1129		return errno;
1130
1131	update_disk_device_job_progress(job, 1.0);
1132
1133	return B_OK;
1134}
1135
1136//	#pragma mark -
1137
1138
1139static status_t
1140btrfs_std_ops(int32 op, ...)
1141{
1142	switch (op) {
1143		case B_MODULE_INIT:
1144			init_debugging();
1145
1146			return B_OK;
1147		case B_MODULE_UNINIT:
1148			exit_debugging();
1149
1150			return B_OK;
1151
1152		default:
1153			return B_ERROR;
1154	}
1155}
1156
1157
1158fs_volume_ops gBtrfsVolumeOps = {
1159	&btrfs_unmount,
1160	&btrfs_read_fs_info,
1161	&btrfs_write_fs_info,
1162	NULL,	// fs_sync,
1163	&btrfs_get_vnode,
1164};
1165
1166
1167fs_vnode_ops gBtrfsVnodeOps = {
1168	/* vnode operations */
1169	&btrfs_lookup,
1170	NULL, // btrfs_get_vnode_name - optional, and we can't do better than the
1171		// fallback implementation, so leave as NULL.
1172	&btrfs_put_vnode,
1173	NULL,	// btrfs_remove_vnode,
1174
1175	/* VM file access */
1176	&btrfs_can_page,
1177	&btrfs_read_pages,
1178	NULL,	// btrfs_write_pages,
1179
1180	NULL,	// io()
1181	NULL,	// cancel_io()
1182
1183	&btrfs_get_file_map,
1184
1185	&btrfs_ioctl,
1186	NULL,
1187	NULL,	// fs_select
1188	NULL,	// fs_deselect
1189	NULL,	// fs_fsync,
1190
1191	&btrfs_read_link,
1192	NULL,	// fs_create_symlink,
1193
1194	NULL,	// fs_link,
1195	&btrfs_unlink,
1196	NULL,	// fs_rename,
1197
1198	&btrfs_access,
1199	&btrfs_read_stat,
1200	&btrfs_write_stat,
1201	NULL,	// fs_preallocate
1202
1203	/* file operations */
1204	NULL,	// fs_create,
1205	&btrfs_open,
1206	&btrfs_close,
1207	&btrfs_free_cookie,
1208	&btrfs_read,
1209	&btrfs_write,
1210
1211	/* directory operations */
1212	&btrfs_create_dir,
1213	&btrfs_remove_dir,
1214	&btrfs_open_dir,
1215	&btrfs_close_dir,
1216	&btrfs_free_dir_cookie,
1217	&btrfs_read_dir,
1218	&btrfs_rewind_dir,
1219
1220	/* attribute directory operations */
1221	&btrfs_open_attr_dir,
1222	&btrfs_close_attr_dir,
1223	&btrfs_free_attr_dir_cookie,
1224	&btrfs_read_attr_dir,
1225	&btrfs_rewind_attr_dir,
1226
1227	/* attribute operations */
1228	&btrfs_create_attr,
1229	&btrfs_open_attr,
1230	&btrfs_close_attr,
1231	&btrfs_free_attr_cookie,
1232	&btrfs_read_attr,
1233	&btrfs_write_attr,
1234	&btrfs_read_attr_stat,
1235	&btrfs_write_attr_stat,
1236	&btrfs_rename_attr,
1237	&btrfs_remove_attr,
1238};
1239
1240
1241static file_system_module_info sBtrfsFileSystem = {
1242	{
1243		"file_systems/btrfs" B_CURRENT_FS_API_VERSION,
1244		0,
1245		btrfs_std_ops,
1246	},
1247
1248	"btrfs",						// short_name
1249	"Btrfs File System",			// pretty_name
1250
1251	// DDM flags
1252	0
1253	| B_DISK_SYSTEM_SUPPORTS_INITIALIZING
1254	| B_DISK_SYSTEM_SUPPORTS_CONTENT_NAME
1255//	| B_DISK_SYSTEM_SUPPORTS_WRITING
1256	,
1257
1258	// scanning
1259	btrfs_identify_partition,
1260	btrfs_scan_partition,
1261	btrfs_free_identify_partition_cookie,
1262	NULL,	// free_partition_content_cookie()
1263
1264	&btrfs_mount,
1265
1266
1267	/* capability querying operations */
1268	&btrfs_get_supported_operations,
1269
1270	NULL,	// validate_resize
1271	NULL,	// validate_move
1272	NULL,	// validate_set_content_name
1273	NULL,	// validate_set_content_parameters
1274	NULL,	// validate_initialize,
1275
1276	/* shadow partition modification */
1277	NULL,	// shadow_changed
1278
1279	/* writing */
1280	NULL,	// defragment
1281	NULL,	// repair
1282	NULL,	// resize
1283	NULL,	// move
1284	NULL,	// set_content_name
1285	NULL,	// set_content_parameters
1286	btrfs_initialize,
1287	btrfs_uninitialize
1288};
1289
1290
1291module_info* modules[] = {
1292	(module_info*)&sBtrfsFileSystem,
1293	NULL,
1294};
1295