xref: /haiku/src/add-ons/kernel/file_systems/btrfs/Volume.cpp (revision 088cebb96f8acf912cb13f1d92ce45a1729c25d6)
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-2010, 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 //! Superblock, mounting, etc.
12 
13 
14 #include "Volume.h"
15 #include "BTree.h"
16 #include "CachedBlock.h"
17 #include "Chunk.h"
18 #include "DebugSupport.h"
19 #include "ExtentAllocator.h"
20 #include "Inode.h"
21 #include "Journal.h"
22 #include "Utility.h"
23 
24 
25 //#define TRACE_BTRFS
26 #ifdef TRACE_BTRFS
27 #	define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
28 #else
29 #	define TRACE(x...) ;
30 #endif
31 #	define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
32 
33 
34 class DeviceOpener {
35 public:
36 								DeviceOpener(int fd, int mode);
37 								DeviceOpener(const char* device, int mode);
38 								~DeviceOpener();
39 
40 			int					Open(const char* device, int mode);
41 			int					Open(int fd, int mode);
42 			void*				InitCache(off_t numBlocks, uint32 blockSize);
43 			void				RemoveCache(bool allowWrites);
44 
45 			void				Keep();
46 
47 			int					Device() const { return fDevice; }
48 			int					Mode() const { return fMode; }
49 			bool				IsReadOnly() const
50 									{ return _IsReadOnly(fMode); }
51 
52 			status_t			GetSize(off_t* _size, uint32* _blockSize = NULL);
53 
54 private:
55 	static	bool				_IsReadOnly(int mode)
56 									{ return (mode & O_RWMASK) == O_RDONLY;}
57 	static	bool				_IsReadWrite(int mode)
58 									{ return (mode & O_RWMASK) == O_RDWR;}
59 
60 			int					fDevice;
61 			int					fMode;
62 			void*				fBlockCache;
63 };
64 
65 
66 DeviceOpener::DeviceOpener(const char* device, int mode)
67 	:
68 	fBlockCache(NULL)
69 {
70 	Open(device, mode);
71 }
72 
73 
74 DeviceOpener::DeviceOpener(int fd, int mode)
75 	:
76 	fBlockCache(NULL)
77 {
78 	Open(fd, mode);
79 }
80 
81 
82 DeviceOpener::~DeviceOpener()
83 {
84 	if (fDevice >= 0) {
85 		RemoveCache(false);
86 		close(fDevice);
87 	}
88 }
89 
90 
91 int
92 DeviceOpener::Open(const char* device, int mode)
93 {
94 	fDevice = open(device, mode | O_NOCACHE);
95 	if (fDevice < 0)
96 		fDevice = errno;
97 
98 	if (fDevice < 0 && _IsReadWrite(mode)) {
99 		// try again to open read-only (don't rely on a specific error code)
100 		return Open(device, O_RDONLY | O_NOCACHE);
101 	}
102 
103 	if (fDevice >= 0) {
104 		// opening succeeded
105 		fMode = mode;
106 		if (_IsReadWrite(mode)) {
107 			// check out if the device really allows for read/write access
108 			device_geometry geometry;
109 			if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) {
110 				if (geometry.read_only) {
111 					// reopen device read-only
112 					close(fDevice);
113 					return Open(device, O_RDONLY | O_NOCACHE);
114 				}
115 			}
116 		}
117 	}
118 
119 	return fDevice;
120 }
121 
122 
123 int
124 DeviceOpener::Open(int fd, int mode)
125 {
126 	fDevice = dup(fd);
127 	if (fDevice < 0)
128 		return errno;
129 
130 	fMode = mode;
131 
132 	return fDevice;
133 }
134 
135 
136 void*
137 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
138 {
139 	return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
140 		IsReadOnly());
141 }
142 
143 
144 void
145 DeviceOpener::RemoveCache(bool allowWrites)
146 {
147 	if (fBlockCache == NULL)
148 		return;
149 
150 	block_cache_delete(fBlockCache, allowWrites);
151 	fBlockCache = NULL;
152 }
153 
154 
155 void
156 DeviceOpener::Keep()
157 {
158 	fDevice = -1;
159 }
160 
161 
162 /*!	Returns the size of the device in bytes. It uses B_GET_GEOMETRY
163 	to compute the size, or fstat() if that failed.
164 */
165 status_t
166 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
167 {
168 	device_geometry geometry;
169 	if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) {
170 		// maybe it's just a file
171 		struct stat stat;
172 		if (fstat(fDevice, &stat) < 0)
173 			return B_ERROR;
174 
175 		if (_size)
176 			*_size = stat.st_size;
177 		if (_blockSize)	// that shouldn't cause us any problems
178 			*_blockSize = 512;
179 
180 		return B_OK;
181 	}
182 
183 	if (_size) {
184 		*_size = 1ULL * geometry.head_count * geometry.cylinder_count
185 			* geometry.sectors_per_track * geometry.bytes_per_sector;
186 	}
187 	if (_blockSize)
188 		*_blockSize = geometry.bytes_per_sector;
189 
190 	return B_OK;
191 }
192 
193 
194 //	#pragma mark -
195 
196 
197 bool
198 btrfs_super_block::IsValid() const
199 {
200 	// TODO: check some more values!
201 	if (strncmp(magic, BTRFS_SUPER_BLOCK_MAGIC, sizeof(magic)) != 0)
202 		return false;
203 
204 	return true;
205 }
206 
207 
208 void
209 btrfs_super_block::Initialize(const char* name, off_t numBlocks,
210 		uint32 blockSize, uint32 sectorSize)
211 {
212 	memset(this, 0, sizeof(btrfs_super_block));
213 
214 	uuid_generate(fsid);
215 	blocknum = B_HOST_TO_LENDIAN_INT64(BTRFS_SUPER_BLOCK_OFFSET);
216 	num_devices = B_HOST_TO_LENDIAN_INT64(1);
217 	strncpy(magic, BTRFS_SUPER_BLOCK_MAGIC_TEMPORARY, sizeof(magic));
218 	generation = B_HOST_TO_LENDIAN_INT64(1);
219 	root = B_HOST_TO_LENDIAN_INT64(BTRFS_RESERVED_SPACE_OFFSET + blockSize);
220 	chunk_root = B_HOST_TO_LENDIAN_INT64(Root() + blockSize);
221 	total_size = B_HOST_TO_LENDIAN_INT64(numBlocks * blockSize);
222 	used_size = B_HOST_TO_LENDIAN_INT64(6 * blockSize);
223 	sector_size = B_HOST_TO_LENDIAN_INT32(sectorSize);
224 	leaf_size = B_HOST_TO_LENDIAN_INT32(blockSize);
225 	node_size = B_HOST_TO_LENDIAN_INT32(blockSize);
226 	stripe_size = B_HOST_TO_LENDIAN_INT32(blockSize);
227 	checksum_type = B_HOST_TO_LENDIAN_INT32(BTRFS_CSUM_TYPE_CRC32);
228 	chunk_root_generation = B_HOST_TO_LENDIAN_INT64(1);
229 	// TODO(lesderid): Support configurable filesystem features
230 	incompat_flags = B_HOST_TO_LENDIAN_INT64(0);
231 	strlcpy(label, name, BTRFS_LABEL_SIZE);
232 }
233 
234 
235 //	#pragma mark -
236 
237 
238 Volume::Volume(fs_volume* volume)
239 	:
240 	fFSVolume(volume),
241 	fFlags(0),
242 	fChunk(NULL),
243 	fChunkTree(NULL)
244 {
245 	mutex_init(&fLock, "btrfs volume");
246 }
247 
248 
249 Volume::~Volume()
250 {
251 	TRACE("Volume destructor.\n");
252 }
253 
254 
255 bool
256 Volume::IsValidSuperBlock()
257 {
258 	return fSuperBlock.IsValid();
259 }
260 
261 
262 const char*
263 Volume::Name() const
264 {
265 	if (fSuperBlock.label[0])
266 		return fSuperBlock.label;
267 
268 	return fName;
269 }
270 
271 
272 status_t
273 Volume::Mount(const char* deviceName, uint32 flags)
274 {
275 	flags |= B_MOUNT_READ_ONLY;
276 		// we only support read-only for now
277 
278 	if ((flags & B_MOUNT_READ_ONLY) != 0) {
279 		TRACE("Volume::Mount(): Read only\n");
280 	} else {
281 		TRACE("Volume::Mount(): Read write\n");
282 	}
283 
284 	DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
285 		? O_RDONLY : O_RDWR);
286 	fDevice = opener.Device();
287 	if (fDevice < B_OK) {
288 		ERROR("Volume::Mount(): couldn't open device\n");
289 		return fDevice;
290 	}
291 
292 	if (opener.IsReadOnly())
293 		fFlags |= VOLUME_READ_ONLY;
294 
295 	// read the superblock
296 	status_t status = Identify(fDevice, &fSuperBlock);
297 	if (status != B_OK) {
298 		ERROR("Volume::Mount(): Identify() failed\n");
299 		return status;
300 	}
301 
302 	fBlockSize = fSuperBlock.BlockSize();
303 	fSectorSize = fSuperBlock.SectorSize();
304 	TRACE("block size %" B_PRIu32 "\n", fBlockSize);
305 	TRACE("sector size %" B_PRIu32 "\n", fSectorSize);
306 
307 	uint8* start = (uint8*)&fSuperBlock.system_chunk_array[0];
308 	uint8* end = (uint8*)&fSuperBlock.system_chunk_array[2048];
309 	while (start < end) {
310 		btrfs_key* key = (btrfs_key*)start;
311 		TRACE("system_chunk_array object_id 0x%" B_PRIx64 " offset 0x%"
312 			B_PRIx64 " type 0x%x\n", key->ObjectID(), key->Offset(),
313 			key->Type());
314 		if (key->Type() != BTRFS_KEY_TYPE_CHUNK_ITEM) {
315 			break;
316 		}
317 
318 		btrfs_chunk* chunk = (btrfs_chunk*)(key + 1);
319 		fChunk = new(std::nothrow) Chunk(chunk, key->Offset());
320 		if (fChunk == NULL)
321 			return B_ERROR;
322 		start += sizeof(btrfs_key) + fChunk->Size();
323 	}
324 
325 	// check if the device size is large enough to hold the file system
326 	off_t diskSize;
327 	status = opener.GetSize(&diskSize);
328 	if (status != B_OK)
329 		return status;
330 	if (diskSize < (off_t)fSuperBlock.TotalSize())
331 		return B_BAD_VALUE;
332 
333 	fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
334 		fBlockSize);
335 	if (fBlockCache == NULL)
336 		return B_ERROR;
337 
338 	TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
339 
340 	fChunkTree = new(std::nothrow) BTree(this);
341 	if (fChunkTree == NULL)
342 		return B_NO_MEMORY;
343 	fChunkTree->SetRoot(fSuperBlock.ChunkRoot(), NULL);
344 	TRACE("Volume::Mount() chunk_root: %" B_PRIu64 " (physical block %" B_PRIu64
345 		")\n", fSuperBlock.ChunkRoot(), fChunkTree->RootBlock());
346 
347 	fRootTree = new(std::nothrow) BTree(this);
348 	if (fRootTree == NULL)
349 		return B_NO_MEMORY;
350 	fRootTree->SetRoot(fSuperBlock.Root(), NULL);
351 	TRACE("Volume::Mount() root: %" B_PRIu64 " (physical block %" B_PRIu64 ")\n",
352 		fSuperBlock.Root(), fRootTree->RootBlock());
353 
354 	BTree::Path path(fRootTree);
355 
356 	TRACE("Volume::Mount(): Searching extent root\n");
357 	btrfs_key search_key;
358 	search_key.SetOffset(0);
359 	search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM);
360 	search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE);
361 	btrfs_root* root;
362 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
363 	if (status != B_OK) {
364 		ERROR("Volume::Mount(): Couldn't find extent root\n");
365 		return status;
366 	}
367 	TRACE("Volume::Mount(): Found extent root: %" B_PRIu64 "\n",
368 		root->LogicalAddress());
369 	fExtentTree = new(std::nothrow) BTree(this);
370 	if (fExtentTree == NULL)
371 		return B_NO_MEMORY;
372 	fExtentTree->SetRoot(root->LogicalAddress(), NULL);
373 	free(root);
374 
375 	TRACE("Volume::Mount(): Searching fs root\n");
376 	search_key.SetOffset(0);
377 	search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE);
378 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
379 	if (status != B_OK) {
380 		ERROR("Volume::Mount(): Couldn't find fs root\n");
381 		return status;
382 	}
383 	TRACE("Volume::Mount(): Found fs root: %" B_PRIu64 "\n",
384 		root->LogicalAddress());
385 	fFSTree = new(std::nothrow) BTree(this);
386 	if (fFSTree == NULL)
387 		return B_NO_MEMORY;
388 	fFSTree->SetRoot(root->LogicalAddress(), NULL);
389 	free(root);
390 
391 	TRACE("Volume::Mount(): Searching dev root\n");
392 	search_key.SetOffset(0);
393 	search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE);
394 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
395 	if (status != B_OK) {
396 		ERROR("Volume::Mount(): Couldn't find dev root\n");
397 		return status;
398 	}
399 	TRACE("Volume::Mount(): Found dev root: %" B_PRIu64 "\n",
400 		root->LogicalAddress());
401 	fDevTree = new(std::nothrow) BTree(this);
402 	if (fDevTree == NULL)
403 		return B_NO_MEMORY;
404 	fDevTree->SetRoot(root->LogicalAddress(), NULL);
405 	free(root);
406 
407 	TRACE("Volume::Mount(): Searching checksum root\n");
408 	search_key.SetOffset(0);
409 	search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE);
410 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
411 	if (status != B_OK) {
412 		ERROR("Volume::Mount(): Couldn't find checksum root\n");
413 		return status;
414 	}
415 	TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64 "\n",
416 		root->LogicalAddress());
417 	fChecksumTree = new(std::nothrow) BTree(this);
418 	if (fChecksumTree == NULL)
419 		return B_NO_MEMORY;
420 	fChecksumTree->SetRoot(root->LogicalAddress(), NULL);
421 	free(root);
422 
423 	search_key.SetObjectID(-1);
424 	search_key.SetType(0);
425 	status = fFSTree->FindPrevious(&path, search_key, NULL);
426 	if (status != B_OK) {
427 		ERROR("Volume::Mount() Couldn't find any inode!!\n");
428 		return status;
429 	}
430 	fLargestInodeID = search_key.ObjectID();
431 	TRACE("Volume::Mount() Find larget inode id % " B_PRIu64 "\n",
432 		fLargestInodeID);
433 
434 	// Initialize Journal
435 	fJournal = new(std::nothrow) Journal(this);
436 	if (fJournal == NULL)
437 		return B_NO_MEMORY;
438 
439 	// Initialize ExtentAllocator;
440 	fExtentAllocator = new(std::nothrow) ExtentAllocator(this);
441 	if (fExtentAllocator == NULL)
442 		return B_NO_MEMORY;
443 	status = fExtentAllocator->Initialize();
444 	if (status != B_OK) {
445 		ERROR("could not initalize extent allocator!\n");
446 		return status;
447 	}
448 
449 	// ready
450 	status = get_vnode(fFSVolume, BTRFS_FIRST_SUBVOLUME,
451 		(void**)&fRootNode);
452 	if (status != B_OK) {
453 		ERROR("could not create root node: get_vnode() failed!\n");
454 		return status;
455 	}
456 
457 	TRACE("Volume::Mount(): Found root node: %" B_PRIu64 " (%s)\n",
458 		fRootNode->ID(), strerror(fRootNode->InitCheck()));
459 
460 	// all went fine
461 	opener.Keep();
462 
463 	if (!fSuperBlock.label[0]) {
464 		// generate a more or less descriptive volume name
465 		off_t divisor = 1ULL << 40;
466 		char unit = 'T';
467 		if (diskSize < divisor) {
468 			divisor = 1UL << 30;
469 			unit = 'G';
470 			if (diskSize < divisor) {
471 				divisor = 1UL << 20;
472 				unit = 'M';
473 			}
474 		}
475 
476 		double size = double((10 * diskSize + divisor - 1) / divisor);
477 			// %g in the kernel does not support precision...
478 
479 		snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume",
480 			size / 10, unit);
481 	}
482 
483 	return B_OK;
484 }
485 
486 
487 status_t
488 Volume::Initialize(int fd, const char* label, uint32 blockSize,
489 	uint32 sectorSize)
490 {
491 	TRACE("Volume::Initialize()\n");
492 
493 	// label must != NULL and may not contain '/' or '\\'
494 	if (label == NULL
495 		|| strchr(label, '/') != NULL || strchr(label, '\\') != NULL) {
496 		return B_BAD_VALUE;
497 	}
498 
499 	if ((blockSize != 1024 && blockSize != 2048 && blockSize != 4096
500 		&& blockSize != 8192 && blockSize != 16384)
501 		|| blockSize % sectorSize != 0) {
502 		return B_BAD_VALUE;
503 	}
504 
505 	DeviceOpener opener(fd, O_RDWR);
506 	if (opener.Device() < B_OK)
507 		return B_BAD_VALUE;
508 
509 	if (opener.IsReadOnly())
510 		return B_READ_ONLY_DEVICE;
511 
512 	fDevice = opener.Device();
513 
514 	uint32 deviceBlockSize;
515 	off_t deviceSize;
516 	if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK)
517 		return B_ERROR;
518 	off_t numBlocks = deviceSize / sectorSize;
519 
520 	// create valid superblock
521 
522 	fSuperBlock.Initialize(label, numBlocks, blockSize, sectorSize);
523 
524 	fBlockSize = fSuperBlock.BlockSize();
525 	fSectorSize = fSuperBlock.SectorSize();
526 
527 	// TODO(lesderid):	Initialize remaining core structures
528 	//					(extent tree, chunk tree, fs tree, etc.)
529 
530 	status_t status = WriteSuperBlock();
531 	if (status < B_OK)
532 		return status;
533 
534 	fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
535 		fBlockSize);
536 	if (fBlockCache == NULL)
537 		return B_ERROR;
538 
539 	fJournal = new(std::nothrow) Journal(this);
540 	if (fJournal == NULL)
541 		RETURN_ERROR(B_ERROR);
542 
543 	// TODO(lesderid):	Perform secondary initialization (in transactions)
544 	//					(add block groups to extent tree, create root dir, etc.)
545 	Transaction transaction(this);
546 
547 	// TODO(lesderid): Write all superblocks when transactions are committed
548 	status = transaction.Done();
549 	if (status < B_OK)
550 		return status;
551 
552 	opener.RemoveCache(true);
553 
554 	TRACE("Volume::Initialize(): Done\n");
555 	return B_OK;
556 }
557 
558 
559 status_t
560 Volume::Unmount()
561 {
562 	TRACE("Volume::Unmount()\n");
563 	delete fRootTree;
564 	delete fExtentTree;
565 	delete fChunkTree;
566 	delete fChecksumTree;
567 	delete fFSTree;
568 	delete fDevTree;
569 	delete fJournal;
570 	delete fExtentAllocator;
571 	fRootTree = NULL;
572 	fExtentTree = NULL;
573 	fChunkTree = NULL;
574 	fChecksumTree = NULL;
575 	fFSTree = NULL;
576 	fDevTree = NULL;
577 	fJournal = NULL;
578 	fExtentAllocator = NULL;
579 
580 	TRACE("Volume::Unmount(): Putting root node\n");
581 	put_vnode(fFSVolume, RootNode()->ID());
582 	TRACE("Volume::Unmount(): Deleting the block cache\n");
583 	block_cache_delete(fBlockCache, !IsReadOnly());
584 	TRACE("Volume::Unmount(): Closing device\n");
585 	close(fDevice);
586 
587 	TRACE("Volume::Unmount(): Done\n");
588 	return B_OK;
589 }
590 
591 
592 status_t
593 Volume::LoadSuperBlock()
594 {
595 	CachedBlock cached(this);
596 	const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize);
597 
598 	if (block == NULL)
599 		return B_IO_ERROR;
600 
601 	memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize,
602 		sizeof(fSuperBlock));
603 
604 	return B_OK;
605 }
606 
607 
608 status_t
609 Volume::FindBlock(off_t logical, fsblock_t& physicalBlock)
610 {
611 	off_t physical;
612 	status_t status = FindBlock(logical, physical);
613 	if (status != B_OK)
614 		return status;
615 	physicalBlock = physical / fBlockSize;
616 	return B_OK;
617 }
618 
619 
620 status_t
621 Volume::FindBlock(off_t logical, off_t& physical)
622 {
623 	if (fChunkTree == NULL
624 		|| (logical >= (off_t)fChunk->Offset()
625 			&& logical < (off_t)fChunk->End())) {
626 		// try with fChunk
627 		return fChunk->FindBlock(logical, physical);
628 	}
629 
630 	btrfs_key search_key;
631 	search_key.SetOffset(logical);
632 	search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM);
633 	search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE);
634 	btrfs_chunk* chunk;
635 	BTree::Path path(fChunkTree);
636 	status_t status = fChunkTree->FindPrevious(&path, search_key,
637 		(void**)&chunk);
638 	if (status != B_OK)
639 		return status;
640 
641 	Chunk _chunk(chunk, search_key.Offset());
642 	free(chunk);
643 	status = _chunk.FindBlock(logical, physical);
644 	if (status != B_OK)
645 			return status;
646 	TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF ", physical: %" B_PRIdOFF
647 		"\n", logical, physical);
648 	return B_OK;
649 }
650 
651 
652 status_t
653 Volume::WriteSuperBlock()
654 {
655 	// TODO(lesderid): Calculate checksum
656 
657 	if (write_pos(fDevice, BTRFS_SUPER_BLOCK_OFFSET, &fSuperBlock,
658 			sizeof(btrfs_super_block))
659 		!= sizeof(btrfs_super_block))
660 		return B_IO_ERROR;
661 
662 	return B_OK;
663 }
664 
665 
666 /* Wrapper function for allocating new block
667  */
668 status_t
669 Volume::GetNewBlock(uint64& logical, fsblock_t& physical, uint64 start,
670 	uint64 flags)
671 {
672 	status_t status = fExtentAllocator->AllocateTreeBlock(logical, start, flags);
673 	if (status != B_OK)
674 		return status;
675 
676 	return FindBlock(logical, physical);
677 }
678 
679 
680 //	#pragma mark - Disk scanning and initialization
681 
682 
683 /*static*/ status_t
684 Volume::Identify(int fd, btrfs_super_block* superBlock)
685 {
686 	if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock,
687 			sizeof(btrfs_super_block)) != sizeof(btrfs_super_block))
688 		return B_IO_ERROR;
689 
690 	if (!superBlock->IsValid()) {
691 		ERROR("invalid superblock!\n");
692 		return B_BAD_VALUE;
693 	}
694 
695 	return B_OK;
696 }
697 
698