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.
8 //! Superblock, mounting, etc.
13 #include "CachedBlock.h"
20 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
22 # define TRACE(x...) ;
24 # define ERROR(x...) dprintf("\33[34mbtrfs:\33[0m " x)
29 DeviceOpener(int fd
, int mode
);
30 DeviceOpener(const char* device
, int mode
);
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
);
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
);
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
;}
59 DeviceOpener::DeviceOpener(const char* device
, int mode
)
67 DeviceOpener::DeviceOpener(int fd
, int mode
)
75 DeviceOpener::~DeviceOpener()
85 DeviceOpener::Open(const char* device
, int mode
)
87 fDevice
= open(device
, mode
| O_NOCACHE
);
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
);
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
106 return Open(device
, O_RDONLY
| O_NOCACHE
);
117 DeviceOpener::Open(int fd
, int mode
)
130 DeviceOpener::InitCache(off_t numBlocks
, uint32 blockSize
)
132 return fBlockCache
= block_cache_create(fDevice
, numBlocks
, blockSize
,
138 DeviceOpener::RemoveCache(bool allowWrites
)
140 if (fBlockCache
== NULL
)
143 block_cache_delete(fBlockCache
, allowWrites
);
155 /*! Returns the size of the device in bytes. It uses B_GET_GEOMETRY
156 to compute the size, or fstat() if that failed.
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
165 if (fstat(fDevice
, &stat
) < 0)
169 *_size
= stat
.st_size
;
170 if (_blockSize
) // that shouldn't cause us any problems
177 *_size
= 1ULL * geometry
.head_count
* geometry
.cylinder_count
178 * geometry
.sectors_per_track
* geometry
.bytes_per_sector
;
181 *_blockSize
= geometry
.bytes_per_sector
;
191 btrfs_super_block::IsValid()
193 // TODO: check some more values!
194 if (strncmp(magic
, BTRFS_SUPER_BLOCK_MAGIC
, sizeof(magic
)) != 0)
204 Volume::Volume(fs_volume
* volume
)
211 mutex_init(&fLock
, "btrfs volume");
217 TRACE("Volume destructor.\n");
222 Volume::IsValidSuperBlock()
224 return fSuperBlock
.IsValid();
231 if (fSuperBlock
.label
[0])
232 return fSuperBlock
.label
;
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");
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");
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");
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(),
280 if (key
->Type() != BTRFS_KEY_TYPE_CHUNK_ITEM
) {
284 btrfs_chunk
* chunk
= (btrfs_chunk
*)(key
+ 1);
285 fChunk
= new(std::nothrow
) Chunk(chunk
, key
->Offset());
288 start
+= sizeof(btrfs_key
) + fChunk
->Size();
291 // check if the device size is large enough to hold the file system
293 status
= opener
.GetSize(&diskSize
);
296 if (diskSize
< (off_t
)fSuperBlock
.TotalSize())
299 fBlockCache
= opener
.InitCache(fSuperBlock
.TotalSize() / fBlockSize
,
301 if (fBlockCache
== NULL
)
304 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache
);
306 fChunkTree
= new(std::nothrow
) BTree(this);
307 if (fChunkTree
== NULL
)
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
)
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
);
328 status
= fRootTree
->FindExact(&path
, search_key
, (void**)&root
);
329 if (status
!= B_OK
) {
330 ERROR("Volume::Mount(): Couldn't find extent root\n");
333 TRACE("Volume::Mount(): Found extent root: %" B_PRIu64
"\n",
334 root
->LogicalAddress());
335 fExtentTree
= new(std::nothrow
) BTree(this);
336 if (fExtentTree
== NULL
)
338 fExtentTree
->SetRoot(root
->LogicalAddress(), NULL
);
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");
349 TRACE("Volume::Mount(): Found fs root: %" B_PRIu64
"\n",
350 root
->LogicalAddress());
351 fFSTree
= new(std::nothrow
) BTree(this);
354 fFSTree
->SetRoot(root
->LogicalAddress(), NULL
);
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");
365 TRACE("Volume::Mount(): Found dev root: %" B_PRIu64
"\n",
366 root
->LogicalAddress());
367 fDevTree
= new(std::nothrow
) BTree(this);
368 if (fDevTree
== NULL
)
370 fDevTree
->SetRoot(root
->LogicalAddress(), NULL
);
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");
381 TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64
"\n",
382 root
->LogicalAddress());
383 fChecksumTree
= new(std::nothrow
) BTree(this);
384 if (fChecksumTree
== NULL
)
386 fChecksumTree
->SetRoot(root
->LogicalAddress(), NULL
);
390 status
= get_vnode(fFSVolume
, BTRFS_FIRST_SUBVOLUME
,
392 if (status
!= B_OK
) {
393 ERROR("could not create root node: get_vnode() failed!\n");
397 TRACE("Volume::Mount(): Found root node: %" B_PRIu64
" (%s)\n",
398 fRootNode
->ID(), strerror(fRootNode
->InitCheck()));
403 if (!fSuperBlock
.label
[0]) {
404 // generate a more or less descriptive volume name
405 off_t divisor
= 1ULL << 40;
407 if (diskSize
< divisor
) {
410 if (diskSize
< divisor
) {
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",
430 TRACE("Volume::Unmount()\n");
432 delete fChecksumTree
;
436 fChecksumTree
= 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");
447 TRACE("Volume::Unmount(): Done\n");
453 Volume::LoadSuperBlock()
455 CachedBlock
cached(this);
456 const uint8
* block
= cached
.SetTo(BTRFS_SUPER_BLOCK_OFFSET
/ fBlockSize
);
461 memcpy(&fSuperBlock
, block
+ BTRFS_SUPER_BLOCK_OFFSET
% fBlockSize
,
462 sizeof(fSuperBlock
));
469 Volume::FindBlock(off_t logical
, fsblock_t
& physicalBlock
)
472 status_t status
= FindBlock(logical
, physical
);
475 physicalBlock
= physical
/ fBlockSize
;
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())) {
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
);
495 BTree::Path
path(fChunkTree
);
496 status_t status
= fChunkTree
->FindPrevious(&path
, search_key
,
501 Chunk
_chunk(chunk
, search_key
.Offset());
503 status
= _chunk
.FindBlock(logical
, physical
);
506 TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF
", physical: %" B_PRIdOFF
507 "\n", logical
, physical
);
512 // #pragma mark - Disk scanning and initialization
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
))
522 if (!superBlock
->IsValid()) {
523 ERROR("invalid superblock!\n");