1 /*
2 * Copyright 2019, Les De Ridder, les@lesderid.net
3 * Copyright 2017, Chế Vũ Gia Hy, cvghy116@gmail.com.
4 * Copyright 2011, Jérôme Duval, korli@users.berlios.de.
5 * Copyright 2008-2010, Axel Dörfler, axeld@pinc-software.de.
6 *
7 * This file may be used under the terms of the MIT License.
8 */
9
10
11 //! Superblock, mounting, etc.
12
13
14 #include "Volume.h"
15 #include "BTree.h"
16 #include "CachedBlock.h"
17 #include "Chunk.h"
18 #include "CRCTable.h"
19 #include "DeviceOpener.h"
20 #include "ExtentAllocator.h"
21 #include "Inode.h"
22 #include "Journal.h"
23 #include "Utility.h"
24
25 #ifdef FS_SHELL
26 #define RETURN_ERROR return
27 #else
28 #include "DebugSupport.h"
29 #endif
30
31 //#define TRACE_BTRFS
32 #ifdef TRACE_BTRFS
33 # define TRACE(x...) dprintf("\33[34mbtrfs:\33[0m " x)
34 #else
35 # define TRACE(x...) ;
36 #endif
37
38
39 // #pragma mark -
40
41
42 bool
IsValid() const43 btrfs_super_block::IsValid() const
44 {
45 // TODO: check some more values!
46 if (strncmp(magic, BTRFS_SUPER_BLOCK_MAGIC, sizeof(magic)) != 0)
47 return false;
48
49 return true;
50 }
51
52
53 void
Initialize(const char * name,off_t numBlocks,uint32 blockSize,uint32 sectorSize)54 btrfs_super_block::Initialize(const char* name, off_t numBlocks,
55 uint32 blockSize, uint32 sectorSize)
56 {
57 memset(this, 0, sizeof(btrfs_super_block));
58
59 uuid_generate(fsid);
60 blocknum = B_HOST_TO_LENDIAN_INT64(BTRFS_SUPER_BLOCK_OFFSET);
61 num_devices = B_HOST_TO_LENDIAN_INT64(1);
62 strncpy(magic, BTRFS_SUPER_BLOCK_MAGIC_TEMPORARY, sizeof(magic));
63 generation = B_HOST_TO_LENDIAN_INT64(1);
64 root = B_HOST_TO_LENDIAN_INT64(BTRFS_RESERVED_SPACE_OFFSET + blockSize);
65 chunk_root = B_HOST_TO_LENDIAN_INT64(Root() + blockSize);
66 total_size = B_HOST_TO_LENDIAN_INT64(numBlocks * blockSize);
67 used_size = B_HOST_TO_LENDIAN_INT64(6 * blockSize);
68 sector_size = B_HOST_TO_LENDIAN_INT32(sectorSize);
69 leaf_size = B_HOST_TO_LENDIAN_INT32(blockSize);
70 node_size = B_HOST_TO_LENDIAN_INT32(blockSize);
71 stripe_size = B_HOST_TO_LENDIAN_INT32(blockSize);
72 checksum_type = B_HOST_TO_LENDIAN_INT32(BTRFS_CSUM_TYPE_CRC32);
73 chunk_root_generation = B_HOST_TO_LENDIAN_INT64(1);
74 // TODO(lesderid): Support configurable filesystem features
75 incompat_flags = B_HOST_TO_LENDIAN_INT64(0);
76 strlcpy(label, name, BTRFS_LABEL_SIZE);
77 }
78
79
80 // #pragma mark -
81
82
Volume(fs_volume * volume)83 Volume::Volume(fs_volume* volume)
84 :
85 fFSVolume(volume),
86 fFlags(0),
87 fChunk(NULL),
88 fChunkTree(NULL)
89 {
90 mutex_init(&fLock, "btrfs volume");
91 }
92
93
~Volume()94 Volume::~Volume()
95 {
96 TRACE("Volume destructor.\n");
97 }
98
99
100 bool
IsValidSuperBlock()101 Volume::IsValidSuperBlock()
102 {
103 return fSuperBlock.IsValid();
104 }
105
106
107 const char*
Name() const108 Volume::Name() const
109 {
110 if (fSuperBlock.label[0])
111 return fSuperBlock.label;
112
113 return fName;
114 }
115
116
117 status_t
Mount(const char * deviceName,uint32 flags)118 Volume::Mount(const char* deviceName, uint32 flags)
119 {
120 flags |= B_MOUNT_READ_ONLY;
121 // we only support read-only for now
122
123 if ((flags & B_MOUNT_READ_ONLY) != 0) {
124 TRACE("Volume::Mount(): Read only\n");
125 } else {
126 TRACE("Volume::Mount(): Read write\n");
127 }
128
129 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
130 ? O_RDONLY : O_RDWR);
131 fDevice = opener.Device();
132 if (fDevice < B_OK) {
133 ERROR("Volume::Mount(): couldn't open device\n");
134 return fDevice;
135 }
136
137 if (opener.IsReadOnly())
138 fFlags |= VOLUME_READ_ONLY;
139
140 // read the superblock
141 status_t status = Identify(fDevice, &fSuperBlock);
142 if (status != B_OK) {
143 ERROR("Volume::Mount(): Identify() failed\n");
144 return status;
145 }
146
147 fBlockSize = fSuperBlock.BlockSize();
148 fSectorSize = fSuperBlock.SectorSize();
149 TRACE("block size %" B_PRIu32 "\n", fBlockSize);
150 TRACE("sector size %" B_PRIu32 "\n", fSectorSize);
151
152 uint8* start = (uint8*)&fSuperBlock.system_chunk_array[0];
153 uint8* end = (uint8*)&fSuperBlock.system_chunk_array[2048];
154 while (start < end) {
155 btrfs_key* key = (btrfs_key*)start;
156 TRACE("system_chunk_array object_id 0x%" B_PRIx64 " offset 0x%"
157 B_PRIx64 " type 0x%x\n", key->ObjectID(), key->Offset(),
158 key->Type());
159 if (key->Type() != BTRFS_KEY_TYPE_CHUNK_ITEM) {
160 break;
161 }
162
163 btrfs_chunk* chunk = (btrfs_chunk*)(key + 1);
164 fChunk = new(std::nothrow) Chunk(chunk, key->Offset());
165 if (fChunk == NULL)
166 return B_ERROR;
167 start += sizeof(btrfs_key) + fChunk->Size();
168 }
169
170 // check if the device size is large enough to hold the file system
171 off_t diskSize;
172 status = opener.GetSize(&diskSize);
173 if (status != B_OK)
174 return status;
175 if (diskSize < (off_t)fSuperBlock.TotalSize())
176 return B_BAD_VALUE;
177
178 fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
179 fBlockSize);
180 if (fBlockCache == NULL)
181 return B_ERROR;
182
183 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
184
185 fChunkTree = new(std::nothrow) BTree(this);
186 if (fChunkTree == NULL)
187 return B_NO_MEMORY;
188 fChunkTree->SetRoot(fSuperBlock.ChunkRoot(), NULL);
189 TRACE("Volume::Mount() chunk_root: %" B_PRIu64 " (physical block %" B_PRIu64
190 ")\n", fSuperBlock.ChunkRoot(), fChunkTree->RootBlock());
191
192 fRootTree = new(std::nothrow) BTree(this);
193 if (fRootTree == NULL)
194 return B_NO_MEMORY;
195 fRootTree->SetRoot(fSuperBlock.Root(), NULL);
196 TRACE("Volume::Mount() root: %" B_PRIu64 " (physical block %" B_PRIu64 ")\n",
197 fSuperBlock.Root(), fRootTree->RootBlock());
198
199 BTree::Path path(fRootTree);
200
201 TRACE("Volume::Mount(): Searching extent root\n");
202 btrfs_key search_key;
203 search_key.SetOffset(0);
204 search_key.SetType(BTRFS_KEY_TYPE_ROOT_ITEM);
205 search_key.SetObjectID(BTRFS_OBJECT_ID_EXTENT_TREE);
206 btrfs_root* root;
207 status = fRootTree->FindExact(&path, search_key, (void**)&root);
208 if (status != B_OK) {
209 ERROR("Volume::Mount(): Couldn't find extent root\n");
210 return status;
211 }
212 TRACE("Volume::Mount(): Found extent root: %" B_PRIu64 "\n",
213 root->LogicalAddress());
214 fExtentTree = new(std::nothrow) BTree(this);
215 if (fExtentTree == NULL)
216 return B_NO_MEMORY;
217 fExtentTree->SetRoot(root->LogicalAddress(), NULL);
218 free(root);
219
220 TRACE("Volume::Mount(): Searching fs root\n");
221 search_key.SetOffset(0);
222 search_key.SetObjectID(BTRFS_OBJECT_ID_FS_TREE);
223 status = fRootTree->FindExact(&path, search_key, (void**)&root);
224 if (status != B_OK) {
225 ERROR("Volume::Mount(): Couldn't find fs root\n");
226 return status;
227 }
228 TRACE("Volume::Mount(): Found fs root: %" B_PRIu64 "\n",
229 root->LogicalAddress());
230 fFSTree = new(std::nothrow) BTree(this);
231 if (fFSTree == NULL)
232 return B_NO_MEMORY;
233 fFSTree->SetRoot(root->LogicalAddress(), NULL);
234 free(root);
235
236 TRACE("Volume::Mount(): Searching dev root\n");
237 search_key.SetOffset(0);
238 search_key.SetObjectID(BTRFS_OBJECT_ID_DEV_TREE);
239 status = fRootTree->FindExact(&path, search_key, (void**)&root);
240 if (status != B_OK) {
241 ERROR("Volume::Mount(): Couldn't find dev root\n");
242 return status;
243 }
244 TRACE("Volume::Mount(): Found dev root: %" B_PRIu64 "\n",
245 root->LogicalAddress());
246 fDevTree = new(std::nothrow) BTree(this);
247 if (fDevTree == NULL)
248 return B_NO_MEMORY;
249 fDevTree->SetRoot(root->LogicalAddress(), NULL);
250 free(root);
251
252 TRACE("Volume::Mount(): Searching checksum root\n");
253 search_key.SetOffset(0);
254 search_key.SetObjectID(BTRFS_OBJECT_ID_CHECKSUM_TREE);
255 status = fRootTree->FindExact(&path, search_key, (void**)&root);
256 if (status != B_OK) {
257 ERROR("Volume::Mount(): Couldn't find checksum root\n");
258 return status;
259 }
260 TRACE("Volume::Mount(): Found checksum root: %" B_PRIu64 "\n",
261 root->LogicalAddress());
262 fChecksumTree = new(std::nothrow) BTree(this);
263 if (fChecksumTree == NULL)
264 return B_NO_MEMORY;
265 fChecksumTree->SetRoot(root->LogicalAddress(), NULL);
266 free(root);
267
268 search_key.SetObjectID(-1);
269 search_key.SetType(0);
270 status = fFSTree->FindPrevious(&path, search_key, NULL);
271 if (status != B_OK) {
272 ERROR("Volume::Mount() Couldn't find any inode!!\n");
273 return status;
274 }
275 fLargestInodeID = search_key.ObjectID();
276 TRACE("Volume::Mount() Find larget inode id % " B_PRIu64 "\n",
277 fLargestInodeID);
278
279 if ((flags & B_MOUNT_READ_ONLY) != 0) {
280 fJournal = NULL;
281 fExtentAllocator = NULL;
282 } else {
283 // Initialize Journal
284 fJournal = new(std::nothrow) Journal(this);
285 if (fJournal == NULL)
286 return B_NO_MEMORY;
287
288 // Initialize ExtentAllocator;
289 fExtentAllocator = new(std::nothrow) ExtentAllocator(this);
290 if (fExtentAllocator == NULL)
291 return B_NO_MEMORY;
292 status = fExtentAllocator->Initialize();
293 if (status != B_OK) {
294 ERROR("could not initalize extent allocator!\n");
295 return status;
296 }
297 }
298
299 // ready
300 status = get_vnode(fFSVolume, BTRFS_FIRST_SUBVOLUME,
301 (void**)&fRootNode);
302 if (status != B_OK) {
303 ERROR("could not create root node: get_vnode() failed!\n");
304 return status;
305 }
306
307 TRACE("Volume::Mount(): Found root node: %" B_PRIu64 " (%s)\n",
308 fRootNode->ID(), strerror(fRootNode->InitCheck()));
309
310 // all went fine
311 opener.Keep();
312
313 if (!fSuperBlock.label[0]) {
314 // generate a more or less descriptive volume name
315 off_t divisor = 1ULL << 40;
316 char unit = 'T';
317 if (diskSize < divisor) {
318 divisor = 1UL << 30;
319 unit = 'G';
320 if (diskSize < divisor) {
321 divisor = 1UL << 20;
322 unit = 'M';
323 }
324 }
325
326 double size = double((10 * diskSize + divisor - 1) / divisor);
327 // %g in the kernel does not support precision...
328
329 snprintf(fName, sizeof(fName), "%g %cB Btrfs Volume",
330 size / 10, unit);
331 }
332
333 return B_OK;
334 }
335
336
337 status_t
Initialize(int fd,const char * label,uint32 blockSize,uint32 sectorSize)338 Volume::Initialize(int fd, const char* label, uint32 blockSize,
339 uint32 sectorSize)
340 {
341 TRACE("Volume::Initialize()\n");
342
343 // label must != NULL and may not contain '/' or '\\'
344 if (label == NULL
345 || strchr(label, '/') != NULL || strchr(label, '\\') != NULL) {
346 return B_BAD_VALUE;
347 }
348
349 if ((blockSize != 1024 && blockSize != 2048 && blockSize != 4096
350 && blockSize != 8192 && blockSize != 16384)
351 || blockSize % sectorSize != 0) {
352 return B_BAD_VALUE;
353 }
354
355 DeviceOpener opener(fd, O_RDWR);
356 if (opener.Device() < B_OK)
357 return B_BAD_VALUE;
358
359 if (opener.IsReadOnly())
360 return B_READ_ONLY_DEVICE;
361
362 fDevice = opener.Device();
363
364 uint32 deviceBlockSize;
365 off_t deviceSize;
366 if (opener.GetSize(&deviceSize, &deviceBlockSize) < B_OK)
367 return B_ERROR;
368 off_t numBlocks = deviceSize / sectorSize;
369
370 // create valid superblock
371
372 fSuperBlock.Initialize(label, numBlocks, blockSize, sectorSize);
373
374 fBlockSize = fSuperBlock.BlockSize();
375 fSectorSize = fSuperBlock.SectorSize();
376
377 // TODO(lesderid): Initialize remaining core structures
378 // (extent tree, chunk tree, fs tree, etc.)
379
380 status_t status = WriteSuperBlock();
381 if (status < B_OK)
382 return status;
383
384 fBlockCache = opener.InitCache(fSuperBlock.TotalSize() / fBlockSize,
385 fBlockSize);
386 if (fBlockCache == NULL)
387 return B_ERROR;
388
389 fJournal = new(std::nothrow) Journal(this);
390 if (fJournal == NULL)
391 RETURN_ERROR(B_ERROR);
392
393 // TODO(lesderid): Perform secondary initialization (in transactions)
394 // (add block groups to extent tree, create root dir, etc.)
395 Transaction transaction(this);
396
397 // TODO(lesderid): Write all superblocks when transactions are committed
398 status = transaction.Done();
399 if (status < B_OK)
400 return status;
401
402 opener.RemoveCache(true);
403
404 TRACE("Volume::Initialize(): Done\n");
405 return B_OK;
406 }
407
408
409 status_t
Unmount()410 Volume::Unmount()
411 {
412 TRACE("Volume::Unmount()\n");
413 delete fRootTree;
414 delete fExtentTree;
415 delete fChunkTree;
416 delete fChecksumTree;
417 delete fFSTree;
418 delete fDevTree;
419 delete fJournal;
420 delete fExtentAllocator;
421 fRootTree = NULL;
422 fExtentTree = NULL;
423 fChunkTree = NULL;
424 fChecksumTree = NULL;
425 fFSTree = NULL;
426 fDevTree = NULL;
427 fJournal = NULL;
428 fExtentAllocator = NULL;
429
430 TRACE("Volume::Unmount(): Putting root node\n");
431 put_vnode(fFSVolume, RootNode()->ID());
432 TRACE("Volume::Unmount(): Deleting the block cache\n");
433 block_cache_delete(fBlockCache, !IsReadOnly());
434 TRACE("Volume::Unmount(): Closing device\n");
435 close(fDevice);
436
437 TRACE("Volume::Unmount(): Done\n");
438 return B_OK;
439 }
440
441
442 status_t
LoadSuperBlock()443 Volume::LoadSuperBlock()
444 {
445 CachedBlock cached(this);
446 const uint8* block = cached.SetTo(BTRFS_SUPER_BLOCK_OFFSET / fBlockSize);
447
448 if (block == NULL)
449 return B_IO_ERROR;
450
451 memcpy(&fSuperBlock, block + BTRFS_SUPER_BLOCK_OFFSET % fBlockSize,
452 sizeof(fSuperBlock));
453
454 return B_OK;
455 }
456
457
458 status_t
FindBlock(off_t logical,fsblock_t & physicalBlock)459 Volume::FindBlock(off_t logical, fsblock_t& physicalBlock)
460 {
461 off_t physical;
462 status_t status = FindBlock(logical, physical);
463 if (status != B_OK)
464 return status;
465 physicalBlock = physical / fBlockSize;
466 return B_OK;
467 }
468
469
470 status_t
FindBlock(off_t logical,off_t & physical)471 Volume::FindBlock(off_t logical, off_t& physical)
472 {
473 if (fChunkTree == NULL
474 || (logical >= (off_t)fChunk->Offset()
475 && logical < (off_t)fChunk->End())) {
476 // try with fChunk
477 return fChunk->FindBlock(logical, physical);
478 }
479
480 btrfs_key search_key;
481 search_key.SetOffset(logical);
482 search_key.SetType(BTRFS_KEY_TYPE_CHUNK_ITEM);
483 search_key.SetObjectID(BTRFS_OBJECT_ID_FIRST_CHUNK_TREE);
484 btrfs_chunk* chunk;
485 BTree::Path path(fChunkTree);
486 status_t status = fChunkTree->FindPrevious(&path, search_key,
487 (void**)&chunk);
488 if (status != B_OK)
489 return status;
490
491 Chunk _chunk(chunk, search_key.Offset());
492 free(chunk);
493 status = _chunk.FindBlock(logical, physical);
494 if (status != B_OK)
495 return status;
496 TRACE("Volume::FindBlock(): logical: %" B_PRIdOFF ", physical: %" B_PRIdOFF
497 "\n", logical, physical);
498 return B_OK;
499 }
500
501
502 status_t
WriteSuperBlock()503 Volume::WriteSuperBlock()
504 {
505 uint32 checksum = calculate_crc((uint32)~1,
506 (uint8 *)(&fSuperBlock + sizeof(fSuperBlock.checksum)),
507 sizeof(fSuperBlock) - sizeof(fSuperBlock.checksum));
508
509 fSuperBlock.checksum[0] = (checksum >> 0) & 0xFF;
510 fSuperBlock.checksum[1] = (checksum >> 8) & 0xFF;
511 fSuperBlock.checksum[2] = (checksum >> 16) & 0xFF;
512 fSuperBlock.checksum[3] = (checksum >> 24) & 0xFF;
513
514 if (write_pos(fDevice, BTRFS_SUPER_BLOCK_OFFSET, &fSuperBlock,
515 sizeof(btrfs_super_block))
516 != sizeof(btrfs_super_block))
517 return B_IO_ERROR;
518
519 return B_OK;
520 }
521
522
523 /* Wrapper function for allocating new block
524 */
525 status_t
GetNewBlock(uint64 & logical,fsblock_t & physical,uint64 start,uint64 flags)526 Volume::GetNewBlock(uint64& logical, fsblock_t& physical, uint64 start,
527 uint64 flags)
528 {
529 status_t status = fExtentAllocator->AllocateTreeBlock(logical, start, flags);
530 if (status != B_OK)
531 return status;
532
533 return FindBlock(logical, physical);
534 }
535
536
537 // #pragma mark - Disk scanning and initialization
538
539
540 /*static*/ status_t
Identify(int fd,btrfs_super_block * superBlock)541 Volume::Identify(int fd, btrfs_super_block* superBlock)
542 {
543 if (read_pos(fd, BTRFS_SUPER_BLOCK_OFFSET, superBlock,
544 sizeof(btrfs_super_block)) != sizeof(btrfs_super_block))
545 return B_IO_ERROR;
546
547 if (!superBlock->IsValid()) {
548 ERROR("invalid superblock!\n");
549 return B_BAD_VALUE;
550 }
551
552 return B_OK;
553 }
554
555