xref: /haiku/src/add-ons/kernel/file_systems/btrfs/Volume.cpp (revision 4241437c836d96957da142f0263cb7f6a4056b01)
1 /*
2  * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com.
3  * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
4  * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
5  * This file may be used under the terms of the MIT License.
6  */
7 
8 
9 //! Superblock, mounting, etc.
10 
11 
12 #include "Volume.h"
13 #include "BTree.h"
14 #include "CachedBlock.h"
15 #include "Chunk.h"
16 #include "Inode.h"
17 #include "Journal.h"
18 #include "ExtentAllocator.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 #	define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
28 
29 
30 class DeviceOpener {
31 public:
32 								DeviceOpener(int fd, int mode);
33 								DeviceOpener(const char* device, int mode);
34 								~DeviceOpener();
35 
36 			int					Open(const char* device, int mode);
37 			int					Open(int fd, int mode);
38 			void*				InitCache(off_t numBlocks, uint32 blockSize);
39 			void				RemoveCache(bool allowWrites);
40 
41 			void				Keep();
42 
43 			int					Device() const { return fDevice; }
44 			int					Mode() const { return fMode; }
45 			bool				IsReadOnly() const
46 									{ return _IsReadOnly(fMode); }
47 
48 			status_t			GetSize(off_t* _size, uint32* _blockSize = NULL);
49 
50 private:
51 	static	bool				_IsReadOnly(int mode)
52 									{ return (mode & O_RWMASK) == O_RDONLY;}
53 	static	bool				_IsReadWrite(int mode)
54 									{ return (mode & O_RWMASK) == O_RDWR;}
55 
56 			int					fDevice;
57 			int					fMode;
58 			void*				fBlockCache;
59 };
60 
61 
62 DeviceOpener::DeviceOpener(const char* device, int mode)
63 	:
64 	fBlockCache(NULL)
65 {
66 	Open(device, mode);
67 }
68 
69 
70 DeviceOpener::DeviceOpener(int fd, int mode)
71 	:
72 	fBlockCache(NULL)
73 {
74 	Open(fd, mode);
75 }
76 
77 
78 DeviceOpener::~DeviceOpener()
79 {
80 	if (fDevice >= 0) {
81 		RemoveCache(false);
82 		close(fDevice);
83 	}
84 }
85 
86 
87 int
88 DeviceOpener::Open(const char* device, int mode)
89 {
90 	fDevice = open(device, mode | O_NOCACHE);
91 	if (fDevice < 0)
92 		fDevice = errno;
93 
94 	if (fDevice < 0 && _IsReadWrite(mode)) {
95 		// try again to open read-only (don't rely on a specific error code)
96 		return Open(device, O_RDONLY | O_NOCACHE);
97 	}
98 
99 	if (fDevice >= 0) {
100 		// opening succeeded
101 		fMode = mode;
102 		if (_IsReadWrite(mode)) {
103 			// check out if the device really allows for read/write access
104 			device_geometry geometry;
105 			if (!ioctl(fDevice, B_GET_GEOMETRY, &geometry)) {
106 				if (geometry.read_only) {
107 					// reopen device read-only
108 					close(fDevice);
109 					return Open(device, O_RDONLY | O_NOCACHE);
110 				}
111 			}
112 		}
113 	}
114 
115 	return fDevice;
116 }
117 
118 
119 int
120 DeviceOpener::Open(int fd, int mode)
121 {
122 	fDevice = dup(fd);
123 	if (fDevice < 0)
124 		return errno;
125 
126 	fMode = mode;
127 
128 	return fDevice;
129 }
130 
131 
132 void*
133 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
134 {
135 	return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
136 		IsReadOnly());
137 }
138 
139 
140 void
141 DeviceOpener::RemoveCache(bool allowWrites)
142 {
143 	if (fBlockCache == NULL)
144 		return;
145 
146 	block_cache_delete(fBlockCache, allowWrites);
147 	fBlockCache = NULL;
148 }
149 
150 
151 void
152 DeviceOpener::Keep()
153 {
154 	fDevice = -1;
155 }
156 
157 
158 /*!	Returns the size of the device in bytes. It uses B_GET_GEOMETRY
159 	to compute the size, or fstat() if that failed.
160 */
161 status_t
162 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
163 {
164 	device_geometry geometry;
165 	if (ioctl(fDevice, B_GET_GEOMETRY, &geometry) < 0) {
166 		// maybe it's just a file
167 		struct stat stat;
168 		if (fstat(fDevice, &stat) < 0)
169 			return B_ERROR;
170 
171 		if (_size)
172 			*_size = stat.st_size;
173 		if (_blockSize)	// that shouldn't cause us any problems
174 			*_blockSize = 512;
175 
176 		return B_OK;
177 	}
178 
179 	if (_size) {
180 		*_size = 1ULL * geometry.head_count * geometry.cylinder_count
181 			* geometry.sectors_per_track * geometry.bytes_per_sector;
182 	}
183 	if (_blockSize)
184 		*_blockSize = geometry.bytes_per_sector;
185 
186 	return B_OK;
187 }
188 
189 
190 //	#pragma mark -
191 
192 
193 bool
194 btrfs_super_block::IsValid()
195 {
196 	// TODO: check some more values!
197 	if (strncmp(magic, BTRFS_SUPER_BLOCK_MAGIC, sizeof(magic)) != 0)
198 		return false;
199 
200 	return true;
201 }
202 
203 
204 //	#pragma mark -
205 
206 
207 Volume::Volume(fs_volume* volume)
208 	:
209 	fFSVolume(volume),
210 	fFlags(0),
211 	fChunk(NULL),
212 	fChunkTree(NULL)
213 {
214 	mutex_init(&fLock, "btrfs volume");
215 }
216 
217 
218 Volume::~Volume()
219 {
220 	TRACE("Volume destructor.\n");
221 }
222 
223 
224 bool
225 Volume::IsValidSuperBlock()
226 {
227 	return fSuperBlock.IsValid();
228 }
229 
230 
231 const char*
232 Volume::Name() const
233 {
234 	if (fSuperBlock.label[0])
235 		return fSuperBlock.label;
236 
237 	return fName;
238 }
239 
240 
241 status_t
242 Volume::Mount(const char* deviceName, uint32 flags)
243 {
244 	flags |= B_MOUNT_READ_ONLY;
245 		// we only support read-only for now
246 
247 	if ((flags & B_MOUNT_READ_ONLY) != 0) {
248 		TRACE("Volume::Mount(): Read only\n");
249 	} else {
250 		TRACE("Volume::Mount(): Read write\n");
251 	}
252 
253 	DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
254 		? O_RDONLY : O_RDWR);
255 	fDevice = opener.Device();
256 	if (fDevice < B_OK) {
257 		ERROR("Volume::Mount(): couldn't open device\n");
258 		return fDevice;
259 	}
260 
261 	if (opener.IsReadOnly())
262 		fFlags |= VOLUME_READ_ONLY;
263 
264 	// read the superblock
265 	status_t status = Identify(fDevice, &fSuperBlock);
266 	if (status != B_OK) {
267 		ERROR("Volume::Mount(): Identify() failed\n");
268 		return status;
269 	}
270 
271 	fBlockSize = fSuperBlock.BlockSize();
272 	fSectorSize = fSuperBlock.SectorSize();
273 	TRACE("block size %" B_PRIu32 "\n", fBlockSize);
274 	TRACE("sector size %" B_PRIu32 "\n", fSectorSize);
275 
276 	uint8* start = (uint8*)&fSuperBlock.system_chunk_array[0];
277 	uint8* end = (uint8*)&fSuperBlock.system_chunk_array[2048];
278 	while (start < end) {
279 		btrfs_key* key = (btrfs_key*)start;
280 		TRACE("system_chunk_array object_id 0x%" B_PRIx64 " offset 0x%"
281 			B_PRIx64 " type 0x%x\n", key->ObjectID(), key->Offset(),
282 			key->Type());
283 		if (key->Type() != BTRFS_KEY_TYPE_CHUNK_ITEM) {
284 			break;
285 		}
286 
287 		btrfs_chunk* chunk = (btrfs_chunk*)(key + 1);
288 		fChunk = new(std::nothrow) Chunk(chunk, key->Offset());
289 		if (fChunk == NULL)
290 			return B_ERROR;
291 		start += sizeof(btrfs_key) + fChunk->Size();
292 	}
293 
294 	// check if the device size is large enough to hold the file system
295 	off_t diskSize;
296 	status = opener.GetSize(&diskSize);
297 	if (status != B_OK)
298 		return status;
299 	if (diskSize < (off_t)fSuperBlock.TotalSize())
300 		return B_BAD_VALUE;
301 
302 	fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
303 		fBlockSize);
304 	if (fBlockCache == NULL)
305 		return B_ERROR;
306 
307 	TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
308 
309 	fChunkTree = new(std::nothrow) BTree(this);
310 	if (fChunkTree == NULL)
311 		return B_NO_MEMORY;
312 	fChunkTree->SetRoot(fSuperBlock.ChunkRoot(), NULL);
313 	TRACE("Volume::Mount() chunk_root: %" B_PRIu64 " (physical block %" B_PRIu64
314 		")\n", fSuperBlock.ChunkRoot(), fChunkTree->RootBlock());
315 
316 	fRootTree = new(std::nothrow) BTree(this);
317 	if (fRootTree == NULL)
318 		return B_NO_MEMORY;
319 	fRootTree->SetRoot(fSuperBlock.Root(), NULL);
320 	TRACE("Volume::Mount() root: %" B_PRIu64 " (physical block %" B_PRIu64 ")\n",
321 		fSuperBlock.Root(), fRootTree->RootBlock());
322 
323 	BTree::Path path(fRootTree);
324 
325 	TRACE("Volume::Mount(): Searching extent root\n");
326 	btrfs_key search_key;
327 	search_key.SetOffset(0);
328 	search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM);
329 	search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE);
330 	btrfs_root* root;
331 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
332 	if (status != B_OK) {
333 		ERROR("Volume::Mount(): Couldn't find extent root\n");
334 		return status;
335 	}
336 	TRACE("Volume::Mount(): Found extent root: %" B_PRIu64 "\n",
337 		root->LogicalAddress());
338 	fExtentTree = new(std::nothrow) BTree(this);
339 	if (fExtentTree == NULL)
340 		return B_NO_MEMORY;
341 	fExtentTree->SetRoot(root->LogicalAddress(), NULL);
342 	free(root);
343 
344 	TRACE("Volume::Mount(): Searching fs root\n");
345 	search_key.SetOffset(0);
346 	search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE);
347 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
348 	if (status != B_OK) {
349 		ERROR("Volume::Mount(): Couldn't find fs root\n");
350 		return status;
351 	}
352 	TRACE("Volume::Mount(): Found fs root: %" B_PRIu64 "\n",
353 		root->LogicalAddress());
354 	fFSTree = new(std::nothrow) BTree(this);
355 	if (fFSTree == NULL)
356 		return B_NO_MEMORY;
357 	fFSTree->SetRoot(root->LogicalAddress(), NULL);
358 	free(root);
359 
360 	TRACE("Volume::Mount(): Searching dev root\n");
361 	search_key.SetOffset(0);
362 	search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE);
363 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
364 	if (status != B_OK) {
365 		ERROR("Volume::Mount(): Couldn't find dev root\n");
366 		return status;
367 	}
368 	TRACE("Volume::Mount(): Found dev root: %" B_PRIu64 "\n",
369 		root->LogicalAddress());
370 	fDevTree = new(std::nothrow) BTree(this);
371 	if (fDevTree == NULL)
372 		return B_NO_MEMORY;
373 	fDevTree->SetRoot(root->LogicalAddress(), NULL);
374 	free(root);
375 
376 	TRACE("Volume::Mount(): Searching checksum root\n");
377 	search_key.SetOffset(0);
378 	search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE);
379 	status = fRootTree->FindExact(&path, search_key, (void**)&root);
380 	if (status != B_OK) {
381 		ERROR("Volume::Mount(): Couldn't find checksum root\n");
382 		return status;
383 	}
384 	TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64 "\n",
385 		root->LogicalAddress());
386 	fChecksumTree = new(std::nothrow) BTree(this);
387 	if (fChecksumTree == NULL)
388 		return B_NO_MEMORY;
389 	fChecksumTree->SetRoot(root->LogicalAddress(), NULL);
390 	free(root);
391 
392 	search_key.SetObjectID(-1);
393 	search_key.SetType(0);
394 	status = fFSTree->FindPrevious(&path, search_key, NULL);
395 	if (status != B_OK) {
396 		ERROR("Volume::Mount() Couldn't find any inode!!\n");
397 		return status;
398 	}
399 	fLargestInodeID = search_key.ObjectID();
400 	TRACE("Volume::Mount() Find larget inode id % " B_PRIu64 "\n",
401 		fLargestInodeID);
402 
403 	// Initialize Journal
404 	fJournal = new(std::nothrow) Journal(this);
405 	if (fJournal == NULL)
406 		return B_NO_MEMORY;
407 
408 	// Initialize ExtentAllocator;
409 	fExtentAllocator = new(std::nothrow) ExtentAllocator(this);
410 	if (fExtentAllocator == NULL)
411 		return B_NO_MEMORY;
412 	status = fExtentAllocator->Initialize();
413 	if (status != B_OK) {
414 		ERROR("could not initalize extent allocator!\n");
415 		return status;
416 	}
417 
418 	// ready
419 	status = get_vnode(fFSVolume, BTRFS_FIRST_SUBVOLUME,
420 		(void**)&fRootNode);
421 	if (status != B_OK) {
422 		ERROR("could not create root node: get_vnode() failed!\n");
423 		return status;
424 	}
425 
426 	TRACE("Volume::Mount(): Found root node: %" B_PRIu64 " (%s)\n",
427 		fRootNode->ID(), strerror(fRootNode->InitCheck()));
428 
429 	// all went fine
430 	opener.Keep();
431 
432 	if (!fSuperBlock.label[0]) {
433 		// generate a more or less descriptive volume name
434 		off_t divisor = 1ULL << 40;
435 		char unit = 'T';
436 		if (diskSize < divisor) {
437 			divisor = 1UL << 30;
438 			unit = 'G';
439 			if (diskSize < divisor) {
440 				divisor = 1UL << 20;
441 				unit = 'M';
442 			}
443 		}
444 
445 		double size = double((10 * diskSize + divisor - 1) / divisor);
446 			// %g in the kernel does not support precision...
447 
448 		snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume",
449 			size / 10, unit);
450 	}
451 
452 	return B_OK;
453 }
454 
455 
456 status_t
457 Volume::Unmount()
458 {
459 	TRACE("Volume::Unmount()\n");
460 	delete fRootTree;
461 	delete fExtentTree;
462 	delete fChunkTree;
463 	delete fChecksumTree;
464 	delete fFSTree;
465 	delete fDevTree;
466 	delete fJournal;
467 	delete fExtentAllocator;
468 	fRootTree = NULL;
469 	fExtentTree = NULL;
470 	fChunkTree = NULL;
471 	fChecksumTree = NULL;
472 	fFSTree = NULL;
473 	fDevTree = NULL;
474 	fJournal = NULL;
475 	fExtentAllocator = NULL;
476 
477 	TRACE("Volume::Unmount(): Putting root node\n");
478 	put_vnode(fFSVolume, RootNode()->ID());
479 	TRACE("Volume::Unmount(): Deleting the block cache\n");
480 	block_cache_delete(fBlockCache, !IsReadOnly());
481 	TRACE("Volume::Unmount(): Closing device\n");
482 	close(fDevice);
483 
484 	TRACE("Volume::Unmount(): Done\n");
485 	return B_OK;
486 }
487 
488 
489 status_t
490 Volume::LoadSuperBlock()
491 {
492 	CachedBlock cached(this);
493 	const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize);
494 
495 	if (block == NULL)
496 		return B_IO_ERROR;
497 
498 	memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize,
499 		sizeof(fSuperBlock));
500 
501 	return B_OK;
502 }
503 
504 
505 status_t
506 Volume::FindBlock(off_t logical, fsblock_t& physicalBlock)
507 {
508 	off_t physical;
509 	status_t status = FindBlock(logical, physical);
510 	if (status != B_OK)
511 		return status;
512 	physicalBlock = physical / fBlockSize;
513 	return B_OK;
514 }
515 
516 
517 status_t
518 Volume::FindBlock(off_t logical, off_t& physical)
519 {
520 	if (fChunkTree == NULL
521 		|| (logical >= (off_t)fChunk->Offset()
522 			&& logical < (off_t)fChunk->End())) {
523 		// try with fChunk
524 		return fChunk->FindBlock(logical, physical);
525 	}
526 
527 	btrfs_key search_key;
528 	search_key.SetOffset(logical);
529 	search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM);
530 	search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE);
531 	btrfs_chunk* chunk;
532 	BTree::Path path(fChunkTree);
533 	status_t status = fChunkTree->FindPrevious(&path, search_key,
534 		(void**)&chunk);
535 	if (status != B_OK)
536 		return status;
537 
538 	Chunk _chunk(chunk, search_key.Offset());
539 	free(chunk);
540 	status = _chunk.FindBlock(logical, physical);
541 	if (status != B_OK)
542 			return status;
543 	TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF ", physical: %" B_PRIdOFF
544 		"\n", logical, physical);
545 	return B_OK;
546 }
547 
548 
549 /* Wrapper function for allocating new block
550  */
551 status_t
552 Volume::GetNewBlock(uint64& logical, fsblock_t& physical, uint64 start,
553 	uint64 flags)
554 {
555 	status_t status = fExtentAllocator->AllocateTreeBlock(logical, start, flags);
556 	if (status != B_OK)
557 		return status;
558 
559 	return FindBlock(logical, physical);
560 }
561 
562 
563 //	#pragma mark - Disk scanning and initialization
564 
565 
566 /*static*/ status_t
567 Volume::Identify(int fd, btrfs_super_block* superBlock)
568 {
569 	if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock,
570 			sizeof(btrfs_super_block)) != sizeof(btrfs_super_block))
571 		return B_IO_ERROR;
572 
573 	if (!superBlock->IsValid()) {
574 		ERROR("invalid superblock!\n");
575 		return B_BAD_VALUE;
576 	}
577 
578 	return B_OK;
579 }
580 
581