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