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