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