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