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