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