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