xref: /haiku/src/add-ons/kernel/file_systems/btrfs/kernel_interface.cpp (revision 7d6915b4d08ffe728cd38af02843d5e98ddfe0db)
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 		size_t 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 ((off_t)size <= vecs[index - 1].length || offset >= inode->Size()) {
325 			// We're done!
326 			*_count = index;
327 			TRACE("btrfs_get_file_map for inode %" B_PRIdINO "\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: %" B_PRIu32 "\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 	Volume* volume = (Volume*)_volume->private_volume;
524 
525 	uint32 maxCount = *_num;
526 	uint32 count = 0;
527 
528 	while (count < maxCount && bufferSize > sizeof(struct dirent)) {
529 		ino_t id;
530 		size_t length = bufferSize - sizeof(struct dirent) + 1;
531 
532 		status_t status = iterator->GetNext(dirent->d_name, &length,
533 			&id);
534 
535 		if (status == B_ENTRY_NOT_FOUND)
536 			break;
537 
538 		if (status == B_BUFFER_OVERFLOW) {
539 			// the remaining name buffer length was too small
540 			if (count == 0)
541 				return B_BUFFER_OVERFLOW;
542 			break;
543 		}
544 
545 		if (status != B_OK)
546 			return status;
547 
548 		dirent->d_dev = volume->ID();
549 		dirent->d_ino = id;
550 		dirent->d_reclen = sizeof(struct dirent) + length;
551 
552 		bufferSize -= dirent->d_reclen;
553 		dirent = (struct dirent*)((uint8*)dirent + dirent->d_reclen);
554 		count++;
555 	}
556 
557 	*_num = count;
558 	return B_OK;
559 }
560 
561 
562 static status_t
563 btrfs_rewind_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void *_cookie)
564 {
565 	DirectoryIterator* iterator = (DirectoryIterator*)_cookie;
566 
567 	return iterator->Rewind();
568 }
569 
570 
571 static status_t
572 btrfs_close_dir(fs_volume * /*_volume*/, fs_vnode * /*node*/, void * /*_cookie*/)
573 {
574 	return B_OK;
575 }
576 
577 
578 static status_t
579 btrfs_free_dir_cookie(fs_volume *_volume, fs_vnode *_node, void *_cookie)
580 {
581 	delete (DirectoryIterator*)_cookie;
582 	return B_OK;
583 }
584 
585 
586 static status_t
587 btrfs_open_attr_dir(fs_volume *_volume, fs_vnode *_node, void **_cookie)
588 {
589 	Inode* inode = (Inode*)_node->private_node;
590 	TRACE("%s()\n", __FUNCTION__);
591 
592 	// on directories too ?
593 	if (!inode->IsFile())
594 		return EINVAL;
595 
596 	AttributeIterator* iterator = new(std::nothrow) AttributeIterator(inode);
597 	if (iterator == NULL || iterator->InitCheck() != B_OK) {
598 		delete iterator;
599 		return B_NO_MEMORY;
600 	}
601 
602 	*_cookie = iterator;
603 	return B_OK;
604 }
605 
606 
607 static status_t
608 btrfs_close_attr_dir(fs_volume* _volume, fs_vnode* _node, void* cookie)
609 {
610 	TRACE("%s()\n", __FUNCTION__);
611 	return B_OK;
612 }
613 
614 
615 static status_t
616 btrfs_free_attr_dir_cookie(fs_volume* _volume, fs_vnode* _node, void* _cookie)
617 {
618 	TRACE("%s()\n", __FUNCTION__);
619 	delete (AttributeIterator*)_cookie;
620 	return B_OK;
621 }
622 
623 
624 static status_t
625 btrfs_read_attr_dir(fs_volume* _volume, fs_vnode* _node,
626 				void* _cookie, struct dirent* dirent, size_t bufferSize,
627 				uint32* _num)
628 {
629 	TRACE("%s()\n", __FUNCTION__);
630 	AttributeIterator* iterator = (AttributeIterator*)_cookie;
631 
632 	size_t length = bufferSize;
633 	status_t status = iterator->GetNext(dirent->d_name, &length);
634 	if (status == B_ENTRY_NOT_FOUND) {
635 		*_num = 0;
636 		return B_OK;
637 	} else if (status != B_OK)
638 		return status;
639 
640 	Volume* volume = (Volume*)_volume->private_volume;
641 	dirent->d_dev = volume->ID();
642 	dirent->d_reclen = sizeof(struct dirent) + length;
643 	*_num = 1;
644 
645 	return B_OK;
646 }
647 
648 
649 static status_t
650 btrfs_rewind_attr_dir(fs_volume* _volume, fs_vnode* _node, void* _cookie)
651 {
652 	AttributeIterator* iterator = (AttributeIterator*)_cookie;
653 	return iterator->Rewind();
654 }
655 
656 
657 	/* attribute operations */
658 static status_t
659 btrfs_create_attr(fs_volume* _volume, fs_vnode* _node,
660 	const char* name, uint32 type, int openMode, void** _cookie)
661 {
662 	return EROFS;
663 }
664 
665 
666 static status_t
667 btrfs_open_attr(fs_volume* _volume, fs_vnode* _node, const char* name,
668 	int openMode, void** _cookie)
669 {
670 	TRACE("%s()\n", __FUNCTION__);
671 
672 	Inode* inode = (Inode*)_node->private_node;
673 	Attribute attribute(inode);
674 
675 	return attribute.Open(name, openMode, (attr_cookie**)_cookie);
676 }
677 
678 
679 static status_t
680 btrfs_close_attr(fs_volume* _volume, fs_vnode* _node,
681 	void* cookie)
682 {
683 	return B_OK;
684 }
685 
686 
687 static status_t
688 btrfs_free_attr_cookie(fs_volume* _volume, fs_vnode* _node,
689 	void* cookie)
690 {
691 	delete (attr_cookie*)cookie;
692 	return B_OK;
693 }
694 
695 
696 static status_t
697 btrfs_read_attr(fs_volume* _volume, fs_vnode* _node, void* _cookie,
698 	off_t pos, void* buffer, size_t* _length)
699 {
700 	TRACE("%s()\n", __FUNCTION__);
701 
702 	attr_cookie* cookie = (attr_cookie*)_cookie;
703 	Inode* inode = (Inode*)_node->private_node;
704 
705 	Attribute attribute(inode, cookie);
706 
707 	return attribute.Read(cookie, pos, (uint8*)buffer, _length);
708 }
709 
710 
711 static status_t
712 btrfs_write_attr(fs_volume* _volume, fs_vnode* _node, void* cookie,
713 	off_t pos, const void* buffer, size_t* length)
714 {
715 	return EROFS;
716 }
717 
718 
719 
720 static status_t
721 btrfs_read_attr_stat(fs_volume* _volume, fs_vnode* _node,
722 	void* _cookie, struct stat* stat)
723 {
724 	attr_cookie* cookie = (attr_cookie*)_cookie;
725 	Inode* inode = (Inode*)_node->private_node;
726 
727 	Attribute attribute(inode, cookie);
728 
729 	return attribute.Stat(*stat);
730 }
731 
732 
733 static status_t
734 btrfs_write_attr_stat(fs_volume* _volume, fs_vnode* _node,
735 	void* cookie, const struct stat* stat, int statMask)
736 {
737 	return EROFS;
738 }
739 
740 
741 static status_t
742 btrfs_rename_attr(fs_volume* _volume, fs_vnode* fromVnode,
743 	const char* fromName, fs_vnode* toVnode, const char* toName)
744 {
745 	return EROFS;
746 }
747 
748 
749 static status_t
750 btrfs_remove_attr(fs_volume* _volume, fs_vnode* vnode,
751 	const char* name)
752 {
753 	return EROFS;
754 }
755 
756 
757 fs_volume_ops gBtrfsVolumeOps = {
758 	&btrfs_unmount,
759 	&btrfs_read_fs_info,
760 	NULL,	// write_fs_info()
761 	NULL,	// fs_sync,
762 	&btrfs_get_vnode,
763 };
764 
765 
766 fs_vnode_ops gBtrfsVnodeOps = {
767 	/* vnode operations */
768 	&btrfs_lookup,
769 	NULL,
770 	&btrfs_put_vnode,
771 	NULL,	// btrfs_remove_vnode,
772 
773 	/* VM file access */
774 	&btrfs_can_page,
775 	&btrfs_read_pages,
776 	NULL,	// btrfs_write_pages,
777 
778 	NULL,	// io()
779 	NULL,	// cancel_io()
780 
781 	&btrfs_get_file_map,
782 
783 	&btrfs_ioctl,
784 	NULL,
785 	NULL,	// fs_select
786 	NULL,	// fs_deselect
787 	NULL,	// fs_fsync,
788 
789 	&btrfs_read_link,
790 	NULL,	// fs_create_symlink,
791 
792 	NULL,	// fs_link,
793 	NULL,	// fs_unlink,
794 	NULL,	// fs_rename,
795 
796 	&btrfs_access,
797 	&btrfs_read_stat,
798 	NULL,	// fs_write_stat,
799 	NULL,	// fs_preallocate
800 
801 	/* file operations */
802 	NULL,	// fs_create,
803 	&btrfs_open,
804 	&btrfs_close,
805 	&btrfs_free_cookie,
806 	&btrfs_read,
807 	NULL,	//	fs_write,
808 
809 	/* directory operations */
810 	NULL, 	// fs_create_dir,
811 	NULL, 	// fs_remove_dir,
812 	&btrfs_open_dir,
813 	&btrfs_close_dir,
814 	&btrfs_free_dir_cookie,
815 	&btrfs_read_dir,
816 	&btrfs_rewind_dir,
817 
818 	/* attribute directory operations */
819 	&btrfs_open_attr_dir,
820 	&btrfs_close_attr_dir,
821 	&btrfs_free_attr_dir_cookie,
822 	&btrfs_read_attr_dir,
823 	&btrfs_rewind_attr_dir,
824 
825 	/* attribute operations */
826 	&btrfs_create_attr,
827 	&btrfs_open_attr,
828 	&btrfs_close_attr,
829 	&btrfs_free_attr_cookie,
830 	&btrfs_read_attr,
831 	&btrfs_write_attr,
832 	&btrfs_read_attr_stat,
833 	&btrfs_write_attr_stat,
834 	&btrfs_rename_attr,
835 	&btrfs_remove_attr,
836 };
837 
838 
839 static file_system_module_info sBtrfsFileSystem = {
840 	{
841 		"file_systems/btrfs" B_CURRENT_FS_API_VERSION,
842 		0,
843 		NULL,
844 	},
845 
846 	"btrfs",						// short_name
847 	"btrfs File System",			// pretty_name
848 	0,								// DDM flags
849 
850 	// scanning
851 	btrfs_identify_partition,
852 	btrfs_scan_partition,
853 	btrfs_free_identify_partition_cookie,
854 	NULL,	// free_partition_content_cookie()
855 
856 	&btrfs_mount,
857 
858 	NULL,
859 };
860 
861 
862 module_info *modules[] = {
863 	(module_info *)&sBtrfsFileSystem,
864 	NULL,
865 };
866