BTRFS: Implement BTree::Path and change _Find.
[haiku.git] / src / add-ons / kernel / file_systems / btrfs / Volume.cpp
blobdc2fffed2dbf9b5cad2ae4456ef207ecdcdd4ef4
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 */
8 //! Superblock, mounting, etc.
11 #include "Volume.h"
12 #include "BTree.h"
13 #include "CachedBlock.h"
14 #include "Chunk.h"
15 #include "Inode.h"
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)
27 class DeviceOpener {
28 public:
29 DeviceOpener(int fd, int mode);
30 DeviceOpener(const char* device, int mode);
31 ~DeviceOpener();
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);
38 void Keep();
40 int Device() const { return fDevice; }
41 int Mode() const { return fMode; }
42 bool IsReadOnly() const
43 { return _IsReadOnly(fMode); }
45 status_t GetSize(off_t* _size, uint32* _blockSize = NULL);
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;}
53 int fDevice;
54 int fMode;
55 void* fBlockCache;
59 DeviceOpener::DeviceOpener(const char* device, int mode)
61 fBlockCache(NULL)
63 Open(device, mode);
67 DeviceOpener::DeviceOpener(int fd, int mode)
69 fBlockCache(NULL)
71 Open(fd, mode);
75 DeviceOpener::~DeviceOpener()
77 if (fDevice >= 0) {
78 RemoveCache(false);
79 close(fDevice);
84 int
85 DeviceOpener::Open(const char* device, int mode)
87 fDevice = open(device, mode | O_NOCACHE);
88 if (fDevice < 0)
89 fDevice = errno;
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);
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);
112 return fDevice;
117 DeviceOpener::Open(int fd, int mode)
119 fDevice = dup(fd);
120 if (fDevice < 0)
121 return errno;
123 fMode = mode;
125 return fDevice;
129 void*
130 DeviceOpener::InitCache(off_t numBlocks, uint32 blockSize)
132 return fBlockCache = block_cache_create(fDevice, numBlocks, blockSize,
133 IsReadOnly());
137 void
138 DeviceOpener::RemoveCache(bool allowWrites)
140 if (fBlockCache == NULL)
141 return;
143 block_cache_delete(fBlockCache, allowWrites);
144 fBlockCache = NULL;
148 void
149 DeviceOpener::Keep()
151 fDevice = -1;
155 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
156 to compute the size, or fstat() if that failed.
158 status_t
159 DeviceOpener::GetSize(off_t* _size, uint32* _blockSize)
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;
168 if (_size)
169 *_size = stat.st_size;
170 if (_blockSize) // that shouldn't cause us any problems
171 *_blockSize = 512;
173 return B_OK;
176 if (_size) {
177 *_size = 1ULL * geometry.head_count * geometry.cylinder_count
178 * geometry.sectors_per_track * geometry.bytes_per_sector;
180 if (_blockSize)
181 *_blockSize = geometry.bytes_per_sector;
183 return B_OK;
187 // #pragma mark -
190 bool
191 btrfs_super_block::IsValid()
193 // TODO: check some more values!
194 if (strncmp(magic, BTRFS_SUPER_BLOCK_MAGIC, sizeof(magic)) != 0)
195 return false;
197 return true;
201 // #pragma mark -
204 Volume::Volume(fs_volume* volume)
206 fFSVolume(volume),
207 fFlags(0),
208 fChunk(NULL),
209 fChunkTree(NULL)
211 mutex_init(&fLock, "btrfs volume");
215 Volume::~Volume()
217 TRACE("Volume destructor.\n");
221 bool
222 Volume::IsValidSuperBlock()
224 return fSuperBlock.IsValid();
228 const char*
229 Volume::Name() const
231 if (fSuperBlock.label[0])
232 return fSuperBlock.label;
234 return fName;
238 status_t
239 Volume::Mount(const char* deviceName, uint32 flags)
241 flags |= B_MOUNT_READ_ONLY;
242 // we only support read-only for now
244 if ((flags & B_MOUNT_READ_ONLY) != 0) {
245 TRACE("Volume::Mount(): Read only\n");
246 } else {
247 TRACE("Volume::Mount(): Read write\n");
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;
258 if (opener.IsReadOnly())
259 fFlags |= VOLUME_READ_ONLY;
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;
268 fBlockSize = fSuperBlock.BlockSize();
269 fSectorSize = fSuperBlock.SectorSize();
270 TRACE("block size %" B_PRIu32 "\n", fBlockSize);
271 TRACE("sector size %" B_PRIu32 "\n", fSectorSize);
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;
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();
291 // check if the device size is large enough to hold the file system
292 off_t diskSize;
293 status = opener.GetSize(&diskSize);
294 if (status != B_OK)
295 return status;
296 if (diskSize < (off_t)fSuperBlock.TotalSize())
297 return B_BAD_VALUE;
299 fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
300 fBlockSize);
301 if (fBlockCache == NULL)
302 return B_ERROR;
304 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
306 fChunkTree = new(std::nothrow) BTree(this);
307 if (fChunkTree == NULL)
308 return B_NO_MEMORY;
309 fChunkTree->SetRoot(fSuperBlock.ChunkRoot(), NULL);
310 TRACE("Volume::Mount() chunk_root: %" B_PRIu64 " (physical block %" B_PRIu64
311 ")\n", fSuperBlock.ChunkRoot(), fChunkTree->RootBlock());
313 fRootTree = new(std::nothrow) BTree(this);
314 if (fRootTree == NULL)
315 return B_NO_MEMORY;
316 fRootTree->SetRoot(fSuperBlock.Root(), NULL);
317 TRACE("Volume::Mount() root: %" B_PRIu64 " (physical block %" B_PRIu64 ")\n",
318 fSuperBlock.Root(), fRootTree->RootBlock());
320 BTree::Path path(fRootTree);
322 TRACE("Volume::Mount(): Searching extent root\n");
323 btrfs_key search_key;
324 search_key.SetOffset(0);
325 search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM);
326 search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE);
327 btrfs_root* root;
328 status = fRootTree->FindExact(&path, search_key, (void**)&root);
329 if (status != B_OK) {
330 ERROR("Volume::Mount(): Couldn't find extent root\n");
331 return status;
333 TRACE("Volume::Mount(): Found extent root: %" B_PRIu64 "\n",
334 root->LogicalAddress());
335 fExtentTree = new(std::nothrow) BTree(this);
336 if (fExtentTree == NULL)
337 return B_NO_MEMORY;
338 fExtentTree->SetRoot(root->LogicalAddress(), NULL);
339 free(root);
341 TRACE("Volume::Mount(): Searching fs root\n");
342 search_key.SetOffset(0);
343 search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE);
344 status = fRootTree->FindExact(&path, search_key, (void**)&root);
345 if (status != B_OK) {
346 ERROR("Volume::Mount(): Couldn't find fs root\n");
347 return status;
349 TRACE("Volume::Mount(): Found fs root: %" B_PRIu64 "\n",
350 root->LogicalAddress());
351 fFSTree = new(std::nothrow) BTree(this);
352 if (fFSTree == NULL)
353 return B_NO_MEMORY;
354 fFSTree->SetRoot(root->LogicalAddress(), NULL);
355 free(root);
357 TRACE("Volume::Mount(): Searching dev root\n");
358 search_key.SetOffset(0);
359 search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE);
360 status = fRootTree->FindExact(&path, search_key, (void**)&root);
361 if (status != B_OK) {
362 ERROR("Volume::Mount(): Couldn't find dev root\n");
363 return status;
365 TRACE("Volume::Mount(): Found dev root: %" B_PRIu64 "\n",
366 root->LogicalAddress());
367 fDevTree = new(std::nothrow) BTree(this);
368 if (fDevTree == NULL)
369 return B_NO_MEMORY;
370 fDevTree->SetRoot(root->LogicalAddress(), NULL);
371 free(root);
373 TRACE("Volume::Mount(): Searching checksum root\n");
374 search_key.SetOffset(0);
375 search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE);
376 status = fRootTree->FindExact(&path, search_key, (void**)&root);
377 if (status != B_OK) {
378 ERROR("Volume::Mount(): Couldn't find checksum root\n");
379 return status;
381 TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64 "\n",
382 root->LogicalAddress());
383 fChecksumTree = new(std::nothrow) BTree(this);
384 if (fChecksumTree == NULL)
385 return B_NO_MEMORY;
386 fChecksumTree->SetRoot(root->LogicalAddress(), NULL);
387 free(root);
389 // ready
390 status = get_vnode(fFSVolume, BTRFS_FIRST_SUBVOLUME,
391 (void**)&fRootNode);
392 if (status != B_OK) {
393 ERROR("could not create root node: get_vnode() failed!\n");
394 return status;
397 TRACE("Volume::Mount(): Found root node: %" B_PRIu64 " (%s)\n",
398 fRootNode->ID(), strerror(fRootNode->InitCheck()));
400 // all went fine
401 opener.Keep();
403 if (!fSuperBlock.label[0]) {
404 // generate a more or less descriptive volume name
405 off_t divisor = 1ULL << 40;
406 char unit = 'T';
407 if (diskSize < divisor) {
408 divisor = 1UL << 30;
409 unit = 'G';
410 if (diskSize < divisor) {
411 divisor = 1UL << 20;
412 unit = 'M';
416 double size = double((10 * diskSize + divisor - 1) / divisor);
417 // %g in the kernel does not support precision...
419 snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume",
420 size / 10, unit);
423 return B_OK;
427 status_t
428 Volume::Unmount()
430 TRACE("Volume::Unmount()\n");
431 delete fExtentTree;
432 delete fChecksumTree;
433 delete fFSTree;
434 delete fDevTree;
435 fExtentTree = NULL;
436 fChecksumTree = NULL;
437 fFSTree = NULL;
438 fDevTree = NULL;
440 TRACE("Volume::Unmount(): Putting root node\n");
441 put_vnode(fFSVolume, RootNode()->ID());
442 TRACE("Volume::Unmount(): Deleting the block cache\n");
443 block_cache_delete(fBlockCache, !IsReadOnly());
444 TRACE("Volume::Unmount(): Closing device\n");
445 close(fDevice);
447 TRACE("Volume::Unmount(): Done\n");
448 return B_OK;
452 status_t
453 Volume::LoadSuperBlock()
455 CachedBlock cached(this);
456 const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize);
458 if (block == NULL)
459 return B_IO_ERROR;
461 memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize,
462 sizeof(fSuperBlock));
464 return B_OK;
468 status_t
469 Volume::FindBlock(off_t logical, fsblock_t& physicalBlock)
471 off_t physical;
472 status_t status = FindBlock(logical, physical);
473 if (status != B_OK)
474 return status;
475 physicalBlock = physical / fBlockSize;
476 return B_OK;
480 status_t
481 Volume::FindBlock(off_t logical, off_t& physical)
483 if (fChunkTree == NULL
484 || (logical >= (off_t)fChunk->Offset()
485 && logical < (off_t)fChunk->End())) {
486 // try with fChunk
487 return fChunk->FindBlock(logical, physical);
490 btrfs_key search_key;
491 search_key.SetOffset(logical);
492 search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM);
493 search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE);
494 btrfs_chunk* chunk;
495 BTree::Path path(fChunkTree);
496 status_t status = fChunkTree->FindPrevious(&path, search_key,
497 (void**)&chunk);
498 if (status != B_OK)
499 return status;
501 Chunk _chunk(chunk, search_key.Offset());
502 free(chunk);
503 status = _chunk.FindBlock(logical, physical);
504 if (status != B_OK)
505 return status;
506 TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF ", physical: %" B_PRIdOFF
507 "\n", logical, physical);
508 return B_OK;
512 // #pragma mark - Disk scanning and initialization
515 /*static*/ status_t
516 Volume::Identify(int fd, btrfs_super_block* superBlock)
518 if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock,
519 sizeof(btrfs_super_block)) != sizeof(btrfs_super_block))
520 return B_IO_ERROR;
522 if (!superBlock->IsValid()) {
523 ERROR("invalid superblock!\n");
524 return B_BAD_VALUE;
527 return B_OK;