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