btrfs: [] on the end of a struct field is a variable length array.
[haiku.git] / src / add-ons / kernel / file_systems / btrfs / Volume.cpp
blob0e3879eec4897cc6b1675b216f873a6a51eb6572
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 */
9 //! Superblock, mounting, etc.
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"
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)
30 class DeviceOpener {
31 public:
32 DeviceOpener(int fd, int mode);
33 DeviceOpener(const char* device, int mode);
34 ~DeviceOpener();
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);
41 void Keep();
43 int Device() const { return fDevice; }
44 int Mode() const { return fMode; }
45 bool IsReadOnly() const
46 { return _IsReadOnly(fMode); }
48 status_t GetSize(off_t* _size, uint32* _blockSize = NULL);
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;}
56 int fDevice;
57 int fMode;
58 void* fBlockCache;
62 DeviceOpener::DeviceOpener(const char* device, int mode)
64 fBlockCache(NULL)
66 Open(device, mode);
70 DeviceOpener::DeviceOpener(int fd, int mode)
72 fBlockCache(NULL)
74 Open(fd, mode);
78 DeviceOpener::~DeviceOpener()
80 if (fDevice >= 0) {
81 RemoveCache(false);
82 close(fDevice);
87 int
88 DeviceOpener::Open(const char* device, int mode)
90 fDevice = open(device, mode | O_NOCACHE);
91 if (fDevice < 0)
92 fDevice = errno;
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);
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);
115 return fDevice;
120 DeviceOpener::Open(int fd, int mode)
122 fDevice = dup(fd);
123 if (fDevice < 0)
124 return errno;
126 fMode = mode;
128 return fDevice;
132 void*
133 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
135 return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
136 IsReadOnly());
140 void
141 DeviceOpener::RemoveCache(bool allowWrites)
143 if (fBlockCache == NULL)
144 return;
146 block_cache_delete(fBlockCache, allowWrites);
147 fBlockCache = NULL;
151 void
152 DeviceOpener::Keep()
154 fDevice = -1;
158 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
159 to compute the size, or fstat() if that failed.
161 status_t
162 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
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;
171 if (_size)
172 *_size = stat.st_size;
173 if (_blockSize) // that shouldn't cause us any problems
174 *_blockSize = 512;
176 return B_OK;
179 if (_size) {
180 *_size = 1ULL * geometry.head_count * geometry.cylinder_count
181 * geometry.sectors_per_track * geometry.bytes_per_sector;
183 if (_blockSize)
184 *_blockSize = geometry.bytes_per_sector;
186 return B_OK;
190 // #pragma mark -
193 bool
194 btrfs_super_block::IsValid()
196 // TODO: check some more values!
197 if (strncmp(magic, BTRFS_SUPER_BLOCK_MAGIC, sizeof(magic)) != 0)
198 return false;
200 return true;
204 // #pragma mark -
207 Volume::Volume(fs_volume* volume)
209 fFSVolume(volume),
210 fFlags(0),
211 fChunk(NULL),
212 fChunkTree(NULL)
214 mutex_init(&fLock, "btrfs volume");
218 Volume::~Volume()
220 TRACE("Volume destructor.\n");
224 bool
225 Volume::IsValidSuperBlock()
227 return fSuperBlock.IsValid();
231 const char*
232 Volume::Name() const
234 if (fSuperBlock.label[0])
235 return fSuperBlock.label;
237 return fName;
241 status_t
242 Volume::Mount(const char* deviceName, uint32 flags)
244 flags |= B_MOUNT_READ_ONLY;
245 // we only support read-only for now
247 if ((flags & B_MOUNT_READ_ONLY) != 0) {
248 TRACE("Volume::Mount(): Read only\n");
249 } else {
250 TRACE("Volume::Mount(): Read write\n");
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;
261 if (opener.IsReadOnly())
262 fFlags |= VOLUME_READ_ONLY;
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;
271 fBlockSize = fSuperBlock.BlockSize();
272 fSectorSize = fSuperBlock.SectorSize();
273 TRACE("block size %" B_PRIu32 "\n", fBlockSize);
274 TRACE("sector size %" B_PRIu32 "\n", fSectorSize);
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;
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();
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;
302 fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
303 fBlockSize);
304 if (fBlockCache == NULL)
305 return B_ERROR;
307 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
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());
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());
323 BTree::Path path(fRootTree);
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;
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);
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;
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);
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;
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);
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;
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);
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;
399 fLargestInodeID = search_key.ObjectID();
400 TRACE("Volume::Mount() Find larget inode id % " B_PRIu64 "\n",
401 fLargestInodeID);
403 // Initialize Journal
404 fJournal = new(std::nothrow) Journal(this);
405 if (fJournal == NULL)
406 return B_NO_MEMORY;
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;
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;
426 TRACE("Volume::Mount(): Found root node: %" B_PRIu64 " (%s)\n",
427 fRootNode->ID(), strerror(fRootNode->InitCheck()));
429 // all went fine
430 opener.Keep();
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';
445 double size = double((10 * diskSize + divisor - 1) / divisor);
446 // %g in the kernel does not support precision...
448 snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume",
449 size / 10, unit);
452 return B_OK;
456 status_t
457 Volume::Unmount()
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;
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);
484 TRACE("Volume::Unmount(): Done\n");
485 return B_OK;
489 status_t
490 Volume::LoadSuperBlock()
492 CachedBlock cached(this);
493 const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize);
495 if (block == NULL)
496 return B_IO_ERROR;
498 memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize,
499 sizeof(fSuperBlock));
501 return B_OK;
505 status_t
506 Volume::FindBlock(off_t logical, fsblock_t& physicalBlock)
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;
517 status_t
518 Volume::FindBlock(off_t logical, off_t& physical)
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);
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;
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;
549 /* Wrapper function for allocating new block
551 status_t
552 Volume::GetNewBlock(uint64& logical, fsblock_t& physical, uint64 start,
553 uint64 flags)
555 status_t status = fExtentAllocator->AllocateTreeBlock(logical, start, flags);
556 if (status != B_OK)
557 return status;
559 return FindBlock(logical, physical);
563 // #pragma mark - Disk scanning and initialization
566 /*static*/ status_t
567 Volume::Identify(int fd, btrfs_super_block* superBlock)
569 if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock,
570 sizeof(btrfs_super_block)) != sizeof(btrfs_super_block))
571 return B_IO_ERROR;
573 if (!superBlock->IsValid()) {
574 ERROR("invalid superblock!\n");
575 return B_BAD_VALUE;
578 return B_OK;