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