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