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.
9 //! Superblock, mounting, etc.
14 #include "CachedBlock.h"
18 #include "ExtentAllocator.h"
23 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
25 # define TRACE(x...) ;
27 # define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
32 DeviceOpener(int fd
, int mode
);
33 DeviceOpener(const char* device
, int mode
);
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
);
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
);
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
;}
62 DeviceOpener::DeviceOpener(const char* device
, int mode
)
70 DeviceOpener::DeviceOpener(int fd
, int mode
)
78 DeviceOpener::~DeviceOpener()
88 DeviceOpener::Open(const char* device
, int mode
)
90 fDevice
= open(device
, mode
| O_NOCACHE
);
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
);
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
109 return Open(device
, O_RDONLY
| O_NOCACHE
);
120 DeviceOpener::Open(int fd
, int mode
)
133 DeviceOpener::InitCache(off_t numBlocks
, uint32 blockSize
)
135 return fBlockCache
= block_cache_create(fDevice
, numBlocks
, blockSize
,
141 DeviceOpener::RemoveCache(bool allowWrites
)
143 if (fBlockCache
== NULL
)
146 block_cache_delete(fBlockCache
, allowWrites
);
158 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
159 to compute the size, or fstat() if that failed.
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
168 if (fstat(fDevice
, &stat
) < 0)
172 *_size
= stat
.st_size
;
173 if (_blockSize
) // that shouldn't cause us any problems
180 *_size
= 1ULL * geometry
.head_count
* geometry
.cylinder_count
181 * geometry
.sectors_per_track
* geometry
.bytes_per_sector
;
184 *_blockSize
= geometry
.bytes_per_sector
;
194 btrfs_super_block::IsValid()
196 // TODO: check some more values!
197 if (strncmp(magic
, BTRFS_SUPER_BLOCK_MAGIC
, sizeof(magic
)) != 0)
207 Volume::Volume(fs_volume
* volume
)
214 mutex_init(&fLock
, "btrfs volume");
220 TRACE("Volume destructor.\n");
225 Volume::IsValidSuperBlock()
227 return fSuperBlock
.IsValid();
234 if (fSuperBlock
.label
[0])
235 return fSuperBlock
.label
;
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");
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");
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");
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(),
283 if (key
->Type() != BTRFS_KEY_TYPE_CHUNK_ITEM
) {
287 btrfs_chunk
* chunk
= (btrfs_chunk
*)(key
+ 1);
288 fChunk
= new(std::nothrow
) Chunk(chunk
, key
->Offset());
291 start
+= sizeof(btrfs_key
) + fChunk
->Size();
294 // check if the device size is large enough to hold the file system
296 status
= opener
.GetSize(&diskSize
);
299 if (diskSize
< (off_t
)fSuperBlock
.TotalSize())
302 fBlockCache
= opener
.InitCache(fSuperBlock
.TotalSize() / fBlockSize
,
304 if (fBlockCache
== NULL
)
307 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache
);
309 fChunkTree
= new(std::nothrow
) BTree(this);
310 if (fChunkTree
== NULL
)
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
)
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
);
331 status
= fRootTree
->FindExact(&path
, search_key
, (void**)&root
);
332 if (status
!= B_OK
) {
333 ERROR("Volume::Mount(): Couldn't find extent root\n");
336 TRACE("Volume::Mount(): Found extent root: %" B_PRIu64
"\n",
337 root
->LogicalAddress());
338 fExtentTree
= new(std::nothrow
) BTree(this);
339 if (fExtentTree
== NULL
)
341 fExtentTree
->SetRoot(root
->LogicalAddress(), NULL
);
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");
352 TRACE("Volume::Mount(): Found fs root: %" B_PRIu64
"\n",
353 root
->LogicalAddress());
354 fFSTree
= new(std::nothrow
) BTree(this);
357 fFSTree
->SetRoot(root
->LogicalAddress(), NULL
);
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");
368 TRACE("Volume::Mount(): Found dev root: %" B_PRIu64
"\n",
369 root
->LogicalAddress());
370 fDevTree
= new(std::nothrow
) BTree(this);
371 if (fDevTree
== NULL
)
373 fDevTree
->SetRoot(root
->LogicalAddress(), NULL
);
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");
384 TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64
"\n",
385 root
->LogicalAddress());
386 fChecksumTree
= new(std::nothrow
) BTree(this);
387 if (fChecksumTree
== NULL
)
389 fChecksumTree
->SetRoot(root
->LogicalAddress(), NULL
);
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");
399 fLargestInodeID
= search_key
.ObjectID();
400 TRACE("Volume::Mount() Find larget inode id % " B_PRIu64
"\n",
403 // Initialize Journal
404 fJournal
= new(std::nothrow
) Journal(this);
405 if (fJournal
== NULL
)
408 // Initialize ExtentAllocator;
409 fExtentAllocator
= new(std::nothrow
) ExtentAllocator(this);
410 if (fExtentAllocator
== NULL
)
412 status
= fExtentAllocator
->Initialize();
413 if (status
!= B_OK
) {
414 ERROR("could not initalize extent allocator!\n");
419 status
= get_vnode(fFSVolume
, BTRFS_FIRST_SUBVOLUME
,
421 if (status
!= B_OK
) {
422 ERROR("could not create root node: get_vnode() failed!\n");
426 TRACE("Volume::Mount(): Found root node: %" B_PRIu64
" (%s)\n",
427 fRootNode
->ID(), strerror(fRootNode
->InitCheck()));
432 if (!fSuperBlock
.label
[0]) {
433 // generate a more or less descriptive volume name
434 off_t divisor
= 1ULL << 40;
436 if (diskSize
< divisor
) {
439 if (diskSize
< divisor
) {
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",
459 TRACE("Volume::Unmount()\n");
463 delete fChecksumTree
;
467 delete fExtentAllocator
;
471 fChecksumTree
= 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");
484 TRACE("Volume::Unmount(): Done\n");
490 Volume::LoadSuperBlock()
492 CachedBlock
cached(this);
493 const uint8
* block
= cached
.SetTo(BTRFS_SUPER_BLOCK_OFFSET
/ fBlockSize
);
498 memcpy(&fSuperBlock
, block
+ BTRFS_SUPER_BLOCK_OFFSET
% fBlockSize
,
499 sizeof(fSuperBlock
));
506 Volume::FindBlock(off_t logical
, fsblock_t
& physicalBlock
)
509 status_t status
= FindBlock(logical
, physical
);
512 physicalBlock
= physical
/ fBlockSize
;
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())) {
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
);
532 BTree::Path
path(fChunkTree
);
533 status_t status
= fChunkTree
->FindPrevious(&path
, search_key
,
538 Chunk
_chunk(chunk
, search_key
.Offset());
540 status
= _chunk
.FindBlock(logical
, physical
);
543 TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF
", physical: %" B_PRIdOFF
544 "\n", logical
, physical
);
549 /* Wrapper function for allocating new block
552 Volume::GetNewBlock(uint64
& logical
, fsblock_t
& physical
, uint64 start
,
555 status_t status
= fExtentAllocator
->AllocateTreeBlock(logical
, start
, flags
);
559 return FindBlock(logical
, physical
);
563 // #pragma mark - Disk scanning and initialization
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
))
573 if (!superBlock
->IsValid()) {
574 ERROR("invalid superblock!\n");