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