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 */
6
7
8 //! Superblock, mounting, etc.
9
10
11 #include "Volume.h"
12
13 #include <errno.h>
14 #include <new>
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18
19 #include <fs_cache.h>
20 #include <fs_volume.h>
21
22 #include <util/AutoLock.h>
23
24 #include "CachedBlock.h"
25 #include "CRCTable.h"
26 #include "DeviceOpener.h"
27 #include "Inode.h"
28 #include "InodeJournal.h"
29 #include "NoJournal.h"
30
31
32 //#define TRACE_EXT2
33 #ifdef TRACE_EXT2
34 # define TRACE(x...) dprintf("\33[34mext2:\33[0m " x)
35 #else
36 # define TRACE(x...) ;
37 #endif
38 # define FATAL(x...) dprintf("\33[34mext2:\33[0m " x)
39
40
41 // #pragma mark -
42
43
44 bool
IsValid()45 ext2_super_block::IsValid()
46 {
47 if (Magic() != (uint32)EXT2_SUPER_BLOCK_MAGIC
48 || BlockShift() > 16
49 || BlocksPerGroup() != (1UL << BlockShift()) * 8
50 || InodeSize() > (1UL << BlockShift())
51 || RevisionLevel() > EXT2_MAX_REVISION
52 || ReservedGDTBlocks() > (1UL << BlockShift()) / 4
53 || NumInodes() == 0
54 || InodeSize() == 0
55 || FreeInodes() > NumInodes()) {
56 return false;
57 }
58
59 return true;
60 }
61
62
63 // #pragma mark -
64
65
Volume(fs_volume * volume)66 Volume::Volume(fs_volume* volume)
67 :
68 fFSVolume(volume),
69 fBlockAllocator(NULL),
70 fInodeAllocator(this),
71 fJournalInode(NULL),
72 fFlags(0),
73 fGroupBlocks(NULL),
74 fRootNode(NULL)
75 {
76 mutex_init(&fLock, "ext2 volume");
77 }
78
79
~Volume()80 Volume::~Volume()
81 {
82 TRACE("Volume destructor.\n");
83 delete fBlockAllocator;
84 if (fGroupBlocks != NULL) {
85 uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1)
86 / fGroupsPerBlock;
87 for (uint32 i = 0; i < blockCount; i++)
88 free(fGroupBlocks[i]);
89
90 free(fGroupBlocks);
91 }
92 }
93
94
95 bool
IsValidSuperBlock()96 Volume::IsValidSuperBlock()
97 {
98 return fSuperBlock.IsValid();
99 }
100
101
102 bool
HasExtendedAttributes() const103 Volume::HasExtendedAttributes() const
104 {
105 return (fSuperBlock.CompatibleFeatures() & EXT2_FEATURE_EXT_ATTR) != 0;
106 }
107
108
109 const char*
Name() const110 Volume::Name() const
111 {
112 // The name may be empty, in that case, userspace will generate one.
113 return fSuperBlock.name;
114 }
115
116
117 void
SetName(const char * name)118 Volume::SetName(const char* name)
119 {
120 strlcpy(fSuperBlock.name, name, sizeof(fSuperBlock.name));
121 }
122
123
124 status_t
Mount(const char * deviceName,uint32 flags)125 Volume::Mount(const char* deviceName, uint32 flags)
126 {
127 // flags |= B_MOUNT_READ_ONLY;
128 // we only support read-only for now
129
130 if ((flags & B_MOUNT_READ_ONLY) != 0) {
131 TRACE("Volume::Mount(): Read only\n");
132 } else {
133 TRACE("Volume::Mount(): Read write\n");
134 }
135
136 DeviceOpener opener(deviceName, (flags & B_MOUNT_READ_ONLY) != 0
137 ? O_RDONLY : O_RDWR);
138 fDevice = opener.Device();
139 if (fDevice < B_OK) {
140 FATAL("Volume::Mount(): couldn't open device\n");
141 return fDevice;
142 }
143
144 if (opener.IsReadOnly())
145 fFlags |= VOLUME_READ_ONLY;
146
147 // read the superblock
148 status_t status = Identify(fDevice, &fSuperBlock);
149 if (status != B_OK) {
150 FATAL("Volume::Mount(): Identify() failed\n");
151 return status;
152 }
153
154 TRACE("features %" B_PRIx32 ", incompatible features %" B_PRIx32
155 ", read-only features %" B_PRIx32 "\n",
156 fSuperBlock.CompatibleFeatures(), fSuperBlock.IncompatibleFeatures(),
157 fSuperBlock.ReadOnlyFeatures());
158
159 // check read-only features if mounting read-write
160 if (!IsReadOnly() && _UnsupportedReadOnlyFeatures(fSuperBlock) != 0)
161 return B_UNSUPPORTED;
162
163 if (HasMetaGroupChecksumFeature() && HasChecksumFeature())
164 return B_ERROR;
165
166 if (!_VerifySuperBlock())
167 return B_ERROR;
168
169 if ((fSuperBlock.State() & EXT2_FS_STATE_VALID) == 0
170 || (fSuperBlock.State() & EXT2_FS_STATE_ERROR) != 0) {
171 if (!IsReadOnly()) {
172 FATAL("Volume::Mount(): can't mount R/W, volume not clean\n");
173 return B_NOT_ALLOWED;
174 } else {
175 FATAL("Volume::Mount(): warning: volume not clean\n");
176 }
177 }
178
179 // initialize short hands to the superblock (to save byte swapping)
180 fBlockShift = fSuperBlock.BlockShift();
181 if (fBlockShift < 10 || fBlockShift > 16)
182 return B_ERROR;
183 if (fBlockShift > 12) {
184 FATAL("blocksize not supported!\n");
185 return B_UNSUPPORTED;
186 }
187 fBlockSize = 1UL << fBlockShift;
188 fFirstDataBlock = fSuperBlock.FirstDataBlock();
189
190 fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
191 fFreeInodes = fSuperBlock.FreeInodes();
192
193 if (fFirstDataBlock > fSuperBlock.NumBlocks(Has64bitFeature()))
194 return B_ERROR;
195
196 off_t numBlocks = fSuperBlock.NumBlocks(Has64bitFeature()) - fFirstDataBlock;
197 uint32 blocksPerGroup = fSuperBlock.BlocksPerGroup();
198 fNumGroups = numBlocks / blocksPerGroup;
199 if (numBlocks % blocksPerGroup != 0)
200 fNumGroups++;
201
202 if (blocksPerGroup == 0 || fSuperBlock.FragmentsPerGroup() == 0) {
203 FATAL("zero blocksPerGroup or fragmentsPerGroup!\n");
204 return B_UNSUPPORTED;
205 }
206 if (blocksPerGroup != fSuperBlock.FragmentsPerGroup()) {
207 FATAL("blocksPerGroup is not equal to fragmentsPerGroup!\n");
208 return B_UNSUPPORTED;
209 }
210
211 if (Has64bitFeature()) {
212 TRACE("64bits\n");
213 fGroupDescriptorSize = fSuperBlock.GroupDescriptorSize();
214 if (fGroupDescriptorSize < sizeof(ext2_block_group))
215 return B_ERROR;
216 if (fGroupDescriptorSize != sizeof(ext2_block_group))
217 return B_UNSUPPORTED;
218 } else
219 fGroupDescriptorSize = EXT2_BLOCK_GROUP_NORMAL_SIZE;
220 fGroupsPerBlock = fBlockSize / fGroupDescriptorSize;
221 fNumInodes = fSuperBlock.NumInodes();
222
223 TRACE("block size %" B_PRIu32 ", num groups %" B_PRIu32 ", groups per "
224 "block %" B_PRIu32 ", first %" B_PRIu32 ", group_descriptor_size %"
225 B_PRIu32 "\n", fBlockSize, fNumGroups,
226 fGroupsPerBlock, fFirstDataBlock, fGroupDescriptorSize);
227
228 uint32 blockCount = (fNumGroups + fGroupsPerBlock - 1) / fGroupsPerBlock;
229
230 fGroupBlocks = (uint8**)malloc(blockCount * sizeof(uint8*));
231 if (fGroupBlocks == NULL)
232 return B_NO_MEMORY;
233
234 memset(fGroupBlocks, 0, blockCount * sizeof(uint8*));
235 fInodesPerBlock = fBlockSize / InodeSize();
236
237 _SuperBlockChecksumSeed();
238
239 // check if the device size is large enough to hold the file system
240 off_t diskSize;
241 status = opener.GetSize(&diskSize);
242 if (status != B_OK)
243 return status;
244 if ((diskSize + fBlockSize) <= ((off_t)NumBlocks() << BlockShift())) {
245 FATAL("diskSize is too small for the number of blocks!\n");
246 return B_BAD_VALUE;
247 }
248
249 fBlockCache = opener.InitCache(NumBlocks(), fBlockSize);
250 if (fBlockCache == NULL)
251 return B_ERROR;
252
253 TRACE("Volume::Mount(): Initialized block cache: %p\n", fBlockCache);
254
255 // initialize journal if mounted read-write
256 if (!IsReadOnly() && HasJournalFeature()) {
257 // TODO: There should be a mount option to ignore the existent journal
258 if (fSuperBlock.JournalInode() != 0) {
259 fJournalInode = new(std::nothrow) Inode(this,
260 fSuperBlock.JournalInode());
261
262 if (fJournalInode == NULL)
263 return B_NO_MEMORY;
264
265 TRACE("Opening an on disk, inode mapped journal.\n");
266 fJournal = new(std::nothrow) InodeJournal(fJournalInode);
267 } else {
268 // TODO: external journal
269 TRACE("Can not open an external journal.\n");
270 return B_UNSUPPORTED;
271 }
272 } else {
273 TRACE("Opening a fake journal (NoJournal).\n");
274 fJournal = new(std::nothrow) NoJournal(this);
275 }
276
277 if (fJournal == NULL) {
278 TRACE("No memory to create the journal\n");
279 return B_NO_MEMORY;
280 }
281
282 TRACE("Volume::Mount(): Checking if journal was initialized\n");
283 status = fJournal->InitCheck();
284 if (status != B_OK) {
285 FATAL("could not initialize journal!\n");
286 return status;
287 }
288
289 // TODO: Only recover if asked to
290 TRACE("Volume::Mount(): Asking journal to recover\n");
291 status = fJournal->Recover();
292 if (status != B_OK) {
293 FATAL("could not recover journal!\n");
294 return status;
295 }
296
297 TRACE("Volume::Mount(): Restart journal log\n");
298 status = fJournal->StartLog();
299 if (status != B_OK) {
300 FATAL("could not initialize start journal!\n");
301 return status;
302 }
303
304 if (!IsReadOnly()) {
305 // Initialize allocators
306 fBlockAllocator = new(std::nothrow) BlockAllocator(this);
307 if (fBlockAllocator != NULL) {
308 TRACE("Volume::Mount(): Initialize block allocator\n");
309 status = fBlockAllocator->Initialize();
310 }
311 if (fBlockAllocator == NULL || status != B_OK) {
312 delete fBlockAllocator;
313 fBlockAllocator = NULL;
314 FATAL("could not initialize block allocator, going read-only!\n");
315 fFlags |= VOLUME_READ_ONLY;
316 fJournal->Uninit();
317 delete fJournal;
318 delete fJournalInode;
319 fJournalInode = NULL;
320 fJournal = new(std::nothrow) NoJournal(this);
321 }
322 }
323
324 // ready
325 status = get_vnode(fFSVolume, EXT2_ROOT_NODE, (void**)&fRootNode);
326 if (status != B_OK) {
327 FATAL("could not create root node: get_vnode() failed!\n");
328 return status;
329 }
330
331 // all went fine
332 opener.Keep();
333
334 return B_OK;
335 }
336
337
338 status_t
Unmount()339 Volume::Unmount()
340 {
341 TRACE("Volume::Unmount()\n");
342
343 status_t status = fJournal->Uninit();
344
345 // this will wait on the block notifier/writer thread
346 FlushDevice();
347 delete fJournal;
348 delete fJournalInode;
349
350 TRACE("Volume::Unmount(): Putting root node\n");
351 put_vnode(fFSVolume, RootNode()->ID());
352 TRACE("Volume::Unmount(): Deleting the block cache\n");
353 block_cache_delete(fBlockCache, !IsReadOnly());
354 TRACE("Volume::Unmount(): Closing device\n");
355 close(fDevice);
356
357 TRACE("Volume::Unmount(): Done\n");
358 return status;
359 }
360
361
362 status_t
GetInodeBlock(ino_t id,off_t & block)363 Volume::GetInodeBlock(ino_t id, off_t& block)
364 {
365 ext2_block_group* group;
366 status_t status = GetBlockGroup((id - 1) / fSuperBlock.InodesPerGroup(),
367 &group);
368 if (status != B_OK)
369 return status;
370
371 block = group->InodeTable(Has64bitFeature())
372 + ((id - 1) % fSuperBlock.InodesPerGroup()) / fInodesPerBlock;
373 if (block < 0)
374 return B_BAD_DATA;
375 return B_OK;
376 }
377
378
379 uint32
InodeBlockIndex(ino_t id) const380 Volume::InodeBlockIndex(ino_t id) const
381 {
382 return ((id - 1) % fSuperBlock.InodesPerGroup()) % fInodesPerBlock;
383 }
384
385
386 /*static*/ uint32
_UnsupportedIncompatibleFeatures(ext2_super_block & superBlock)387 Volume::_UnsupportedIncompatibleFeatures(ext2_super_block& superBlock)
388 {
389 uint32 supportedIncompatible = EXT2_INCOMPATIBLE_FEATURE_FILE_TYPE
390 | EXT2_INCOMPATIBLE_FEATURE_RECOVER
391 | EXT2_INCOMPATIBLE_FEATURE_JOURNAL
392 | EXT2_INCOMPATIBLE_FEATURE_EXTENTS
393 | EXT2_INCOMPATIBLE_FEATURE_FLEX_GROUP
394 | EXT2_INCOMPATIBLE_FEATURE_64BIT;
395 /*| EXT2_INCOMPATIBLE_FEATURE_META_GROUP*/;
396 uint32 unsupported = superBlock.IncompatibleFeatures()
397 & ~supportedIncompatible;
398
399 if (unsupported != 0) {
400 FATAL("ext2: incompatible features not supported: %" B_PRIx32
401 " (extents %x)\n", unsupported, EXT2_INCOMPATIBLE_FEATURE_EXTENTS);
402 }
403
404 return unsupported;
405 }
406
407
408 /*static*/ uint32
_UnsupportedReadOnlyFeatures(ext2_super_block & superBlock)409 Volume::_UnsupportedReadOnlyFeatures(ext2_super_block& superBlock)
410 {
411 uint32 supportedReadOnly = EXT2_READ_ONLY_FEATURE_SPARSE_SUPER
412 | EXT2_READ_ONLY_FEATURE_LARGE_FILE
413 | EXT2_READ_ONLY_FEATURE_HUGE_FILE
414 | EXT2_READ_ONLY_FEATURE_EXTRA_ISIZE
415 | EXT2_READ_ONLY_FEATURE_DIR_NLINK
416 | EXT2_READ_ONLY_FEATURE_GDT_CSUM
417 | EXT4_READ_ONLY_FEATURE_METADATA_CSUM;
418 // TODO actually implement EXT2_READ_ONLY_FEATURE_SPARSE_SUPER when
419 // implementing superblock backup copies
420
421 uint32 unsupported = superBlock.ReadOnlyFeatures() & ~supportedReadOnly;
422
423 if (unsupported != 0) {
424 FATAL("ext2: readonly features not supported: %" B_PRIx32 "\n",
425 unsupported);
426 }
427
428 return unsupported;
429 }
430
431
432 uint32
_GroupDescriptorBlock(uint32 blockIndex)433 Volume::_GroupDescriptorBlock(uint32 blockIndex)
434 {
435 if ((fSuperBlock.IncompatibleFeatures()
436 & EXT2_INCOMPATIBLE_FEATURE_META_GROUP) == 0
437 || blockIndex < fSuperBlock.FirstMetaBlockGroup())
438 return fFirstDataBlock + blockIndex + 1;
439
440 panic("meta block");
441 return 0;
442 }
443
444
445 uint16
_GroupCheckSum(ext2_block_group * group,int32 index)446 Volume::_GroupCheckSum(ext2_block_group *group, int32 index)
447 {
448 int32 number = B_HOST_TO_LENDIAN_INT32(index);
449 size_t offset = offsetof(ext2_block_group, checksum);
450 size_t offsetExt4 = offsetof(ext2_block_group, block_bitmap_high);
451 if (HasMetaGroupChecksumFeature()) {
452 uint16 dummy = 0;
453 uint32 checksum = calculate_crc32c(fChecksumSeed, (uint8*)&number,
454 sizeof(number));
455 checksum = calculate_crc32c(checksum, (uint8*)group, offset);
456 checksum = calculate_crc32c(checksum, (uint8*)&dummy, sizeof(dummy));
457 if (fGroupDescriptorSize > offset + sizeof(dummy)) {
458 checksum = calculate_crc32c(checksum, (uint8*)group + offsetExt4,
459 fGroupDescriptorSize - offsetExt4);
460 }
461 return checksum & 0xffff;
462 } else if (HasChecksumFeature()) {
463 uint16 checksum = calculate_crc(0xffff, fSuperBlock.uuid,
464 sizeof(fSuperBlock.uuid));
465 checksum = calculate_crc(checksum, (uint8*)&number, sizeof(number));
466 checksum = calculate_crc(checksum, (uint8*)group, offset);
467 if (Has64bitFeature() && fGroupDescriptorSize > offsetExt4) {
468 checksum = calculate_crc(checksum, (uint8*)group + offsetExt4,
469 fGroupDescriptorSize - offsetExt4);
470 }
471 return checksum;
472 }
473 return 0;
474 }
475
476
477 /*! Makes the requested block group available.
478 The block groups are loaded on demand, but are kept in memory until the
479 volume is unmounted; therefore we don't use the block cache.
480 */
481 status_t
GetBlockGroup(int32 index,ext2_block_group ** _group)482 Volume::GetBlockGroup(int32 index, ext2_block_group** _group)
483 {
484 if (index < 0 || (uint32)index > fNumGroups)
485 return B_BAD_VALUE;
486
487 int32 blockIndex = index / fGroupsPerBlock;
488 int32 blockOffset = index % fGroupsPerBlock;
489
490 MutexLocker _(fLock);
491
492 if (fGroupBlocks[blockIndex] == NULL) {
493 CachedBlock cached(this);
494 const uint8* block = cached.SetTo(_GroupDescriptorBlock(blockIndex));
495 if (block == NULL)
496 return B_IO_ERROR;
497
498 fGroupBlocks[blockIndex] = (uint8*)malloc(fBlockSize);
499 if (fGroupBlocks[blockIndex] == NULL)
500 return B_NO_MEMORY;
501
502 memcpy(fGroupBlocks[blockIndex], block, fBlockSize);
503
504 TRACE("group [%" B_PRId32 "]: inode table %" B_PRIu64 "\n", index,
505 ((ext2_block_group*)(fGroupBlocks[blockIndex] + blockOffset
506 * fGroupDescriptorSize))->InodeTable(Has64bitFeature()));
507 }
508
509 *_group = (ext2_block_group*)(fGroupBlocks[blockIndex]
510 + blockOffset * fGroupDescriptorSize);
511 if (HasChecksumFeature()
512 && (*_group)->checksum != _GroupCheckSum(*_group, index)) {
513 return B_BAD_DATA;
514 }
515 return B_OK;
516 }
517
518
519 status_t
WriteBlockGroup(Transaction & transaction,int32 index)520 Volume::WriteBlockGroup(Transaction& transaction, int32 index)
521 {
522 if (index < 0 || (uint32)index > fNumGroups)
523 return B_BAD_VALUE;
524
525 TRACE("Volume::WriteBlockGroup()\n");
526
527 int32 blockIndex = index / fGroupsPerBlock;
528 int32 blockOffset = index % fGroupsPerBlock;
529
530 MutexLocker _(fLock);
531
532 if (fGroupBlocks[blockIndex] == NULL)
533 return B_BAD_VALUE;
534
535 ext2_block_group *group = (ext2_block_group*)(fGroupBlocks[blockIndex]
536 + blockOffset * fGroupDescriptorSize);
537
538 group->checksum = _GroupCheckSum(group, index);
539 TRACE("Volume::WriteBlockGroup() checksum 0x%x for group %" B_PRId32 " "
540 "(free inodes %" B_PRIu32 ", unused %" B_PRIu32 ")\n", group->checksum,
541 index, group->FreeInodes(Has64bitFeature()),
542 group->UnusedInodes(Has64bitFeature()));
543
544 CachedBlock cached(this);
545 uint8* block = cached.SetToWritable(transaction,
546 _GroupDescriptorBlock(blockIndex));
547 if (block == NULL)
548 return B_IO_ERROR;
549
550 memcpy(block, (const uint8*)fGroupBlocks[blockIndex], fBlockSize);
551
552 // TODO: Write copies
553
554 return B_OK;
555 }
556
557
558 status_t
ActivateLargeFiles(Transaction & transaction)559 Volume::ActivateLargeFiles(Transaction& transaction)
560 {
561 if ((fSuperBlock.ReadOnlyFeatures()
562 & EXT2_READ_ONLY_FEATURE_LARGE_FILE) != 0)
563 return B_OK;
564
565 fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures()
566 | EXT2_READ_ONLY_FEATURE_LARGE_FILE);
567
568 return WriteSuperBlock(transaction);
569 }
570
571
572 status_t
ActivateDirNLink(Transaction & transaction)573 Volume::ActivateDirNLink(Transaction& transaction)
574 {
575 if ((fSuperBlock.ReadOnlyFeatures()
576 & EXT2_READ_ONLY_FEATURE_DIR_NLINK) != 0)
577 return B_OK;
578
579 fSuperBlock.SetReadOnlyFeatures(fSuperBlock.ReadOnlyFeatures()
580 | EXT2_READ_ONLY_FEATURE_DIR_NLINK);
581
582 return WriteSuperBlock(transaction);
583 }
584
585
586 status_t
SaveOrphan(Transaction & transaction,ino_t newID,ino_t & oldID)587 Volume::SaveOrphan(Transaction& transaction, ino_t newID, ino_t& oldID)
588 {
589 oldID = fSuperBlock.LastOrphan();
590 TRACE("Volume::SaveOrphan(): Old: %d, New: %d\n", (int)oldID, (int)newID);
591 fSuperBlock.SetLastOrphan(newID);
592
593 return WriteSuperBlock(transaction);
594 }
595
596
597 status_t
RemoveOrphan(Transaction & transaction,ino_t id)598 Volume::RemoveOrphan(Transaction& transaction, ino_t id)
599 {
600 ino_t currentID = fSuperBlock.LastOrphan();
601 TRACE("Volume::RemoveOrphan(): ID: %d\n", (int)id);
602 if (currentID == 0)
603 return B_OK;
604
605 CachedBlock cached(this);
606
607 off_t blockNum;
608 status_t status = GetInodeBlock(currentID, blockNum);
609 if (status != B_OK)
610 return status;
611
612 uint8* block = cached.SetToWritable(transaction, blockNum);
613 if (block == NULL)
614 return B_IO_ERROR;
615
616 ext2_inode* inode = (ext2_inode*)(block
617 + InodeBlockIndex(currentID) * InodeSize());
618
619 if (currentID == id) {
620 TRACE("Volume::RemoveOrphan(): First entry. Updating head to: %d\n",
621 (int)inode->NextOrphan());
622 fSuperBlock.SetLastOrphan(inode->NextOrphan());
623
624 return WriteSuperBlock(transaction);
625 }
626
627 currentID = inode->NextOrphan();
628 if (currentID == 0)
629 return B_OK;
630
631 do {
632 off_t lastBlockNum = blockNum;
633 status = GetInodeBlock(currentID, blockNum);
634 if (status != B_OK)
635 return status;
636
637 if (blockNum != lastBlockNum) {
638 block = cached.SetToWritable(transaction, blockNum);
639 if (block == NULL)
640 return B_IO_ERROR;
641 }
642
643 ext2_inode* inode = (ext2_inode*)(block
644 + InodeBlockIndex(currentID) * InodeSize());
645
646 currentID = inode->NextOrphan();
647 if (currentID == 0)
648 return B_OK;
649 } while (currentID != id);
650
651 CachedBlock cachedRemoved(this);
652
653 status = GetInodeBlock(id, blockNum);
654 if (status != B_OK)
655 return status;
656
657 uint8* removedBlock = cachedRemoved.SetToWritable(transaction, blockNum);
658 if (removedBlock == NULL)
659 return B_IO_ERROR;
660
661 ext2_inode* removedInode = (ext2_inode*)(removedBlock
662 + InodeBlockIndex(id) * InodeSize());
663
664 // Next orphan is stored inside deletion time
665 inode->deletion_time = removedInode->deletion_time;
666 TRACE("Volume::RemoveOrphan(): Updated pointer to %d\n",
667 (int)inode->NextOrphan());
668
669 return status;
670 }
671
672
673 status_t
AllocateInode(Transaction & transaction,Inode * parent,int32 mode,ino_t & id)674 Volume::AllocateInode(Transaction& transaction, Inode* parent, int32 mode,
675 ino_t& id)
676 {
677 status_t status = fInodeAllocator.New(transaction, parent, mode, id);
678 if (status != B_OK)
679 return status;
680
681 --fFreeInodes;
682
683 return WriteSuperBlock(transaction);
684 }
685
686
687 status_t
FreeInode(Transaction & transaction,ino_t id,bool isDirectory)688 Volume::FreeInode(Transaction& transaction, ino_t id, bool isDirectory)
689 {
690 status_t status = fInodeAllocator.Free(transaction, id, isDirectory);
691 if (status != B_OK)
692 return status;
693
694 ++fFreeInodes;
695
696 return WriteSuperBlock(transaction);
697 }
698
699
700 status_t
AllocateBlocks(Transaction & transaction,uint32 minimum,uint32 maximum,uint32 & blockGroup,fsblock_t & start,uint32 & length)701 Volume::AllocateBlocks(Transaction& transaction, uint32 minimum, uint32 maximum,
702 uint32& blockGroup, fsblock_t& start, uint32& length)
703 {
704 TRACE("Volume::AllocateBlocks()\n");
705 if (IsReadOnly())
706 return B_READ_ONLY_DEVICE;
707
708 TRACE("Volume::AllocateBlocks(): Calling the block allocator\n");
709
710 status_t status = fBlockAllocator->AllocateBlocks(transaction, minimum,
711 maximum, blockGroup, start, length);
712 if (status != B_OK)
713 return status;
714
715 TRACE("Volume::AllocateBlocks(): Allocated %" B_PRIu32 " blocks\n",
716 length);
717
718 fFreeBlocks -= length;
719
720 return WriteSuperBlock(transaction);
721 }
722
723
724 status_t
FreeBlocks(Transaction & transaction,fsblock_t start,uint32 length)725 Volume::FreeBlocks(Transaction& transaction, fsblock_t start, uint32 length)
726 {
727 TRACE("Volume::FreeBlocks(%" B_PRIu64 ", %" B_PRIu32 ")\n", start, length);
728 if (IsReadOnly())
729 return B_READ_ONLY_DEVICE;
730
731 status_t status = fBlockAllocator->Free(transaction, start, length);
732 if (status != B_OK)
733 return status;
734
735 TRACE("Volume::FreeBlocks(): number of free blocks (before): %" B_PRIdOFF
736 "\n", fFreeBlocks);
737 fFreeBlocks += length;
738 TRACE("Volume::FreeBlocks(): number of free blocks (after): %" B_PRIdOFF
739 "\n", fFreeBlocks);
740
741 return WriteSuperBlock(transaction);
742 }
743
744
745 status_t
LoadSuperBlock()746 Volume::LoadSuperBlock()
747 {
748 CachedBlock cached(this);
749 const uint8* block = cached.SetTo(fFirstDataBlock);
750
751 if (block == NULL)
752 return B_IO_ERROR;
753
754 if (fFirstDataBlock == 0)
755 memcpy(&fSuperBlock, block + 1024, sizeof(fSuperBlock));
756 else
757 memcpy(&fSuperBlock, block, sizeof(fSuperBlock));
758
759 fFreeBlocks = fSuperBlock.FreeBlocks(Has64bitFeature());
760 fFreeInodes = fSuperBlock.FreeInodes();
761
762 return B_OK;
763 }
764
765
766 status_t
WriteSuperBlock(Transaction & transaction)767 Volume::WriteSuperBlock(Transaction& transaction)
768 {
769 TRACE("Volume::WriteSuperBlock()\n");
770 fSuperBlock.SetFreeBlocks(fFreeBlocks, Has64bitFeature());
771 fSuperBlock.SetFreeInodes(fFreeInodes);
772 // TODO: Rest of fields that can be modified
773
774 if (HasMetaGroupChecksumFeature()) {
775 fSuperBlock.checksum = calculate_crc32c(0xffffffff, (uint8*)&fSuperBlock,
776 offsetof(struct ext2_super_block, checksum));
777 }
778
779 TRACE("Volume::WriteSuperBlock(): free blocks: %" B_PRIu64 ", free inodes:"
780 " %" B_PRIu32 "\n", fSuperBlock.FreeBlocks(Has64bitFeature()),
781 fSuperBlock.FreeInodes());
782
783 CachedBlock cached(this);
784 uint8* block = cached.SetToWritable(transaction, fFirstDataBlock);
785
786 if (block == NULL)
787 return B_IO_ERROR;
788
789 TRACE("Volume::WriteSuperBlock(): first data block: %" B_PRIu32 ", block:"
790 " %p, superblock: %p\n", fFirstDataBlock, block, &fSuperBlock);
791
792 if (fFirstDataBlock == 0)
793 memcpy(block + 1024, &fSuperBlock, sizeof(fSuperBlock));
794 else
795 memcpy(block, &fSuperBlock, sizeof(fSuperBlock));
796
797 TRACE("Volume::WriteSuperBlock(): Done\n");
798
799 return B_OK;
800 }
801
802
803 void
_SuperBlockChecksumSeed()804 Volume::_SuperBlockChecksumSeed()
805 {
806 if (HasChecksumSeedFeature()) {
807 fChecksumSeed = fSuperBlock.checksum_seed;
808 } else if (HasMetaGroupChecksumFeature()) {
809 fChecksumSeed = calculate_crc32c(0xffffffff, (uint8*)fSuperBlock.uuid,
810 sizeof(fSuperBlock.uuid));
811 } else
812 fChecksumSeed = 0;
813 }
814
815
816 bool
_VerifySuperBlock()817 Volume::_VerifySuperBlock()
818 {
819 if (!HasMetaGroupChecksumFeature())
820 return true;
821
822 uint32 checksum = calculate_crc32c(0xffffffff, (uint8*)&fSuperBlock,
823 offsetof(struct ext2_super_block, checksum));
824 return checksum == fSuperBlock.checksum;
825 }
826
827
828 status_t
FlushDevice()829 Volume::FlushDevice()
830 {
831 TRACE("Volume::FlushDevice(): %p, %p\n", this, fBlockCache);
832 return block_cache_sync(fBlockCache);
833 }
834
835
836 status_t
Sync()837 Volume::Sync()
838 {
839 TRACE("Volume::Sync()\n");
840 return fJournal->Uninit();
841 }
842
843
844 // #pragma mark - Disk scanning and initialization
845
846
847 /*static*/ status_t
Identify(int fd,ext2_super_block * superBlock)848 Volume::Identify(int fd, ext2_super_block* superBlock)
849 {
850 if (read_pos(fd, EXT2_SUPER_BLOCK_OFFSET, superBlock,
851 sizeof(ext2_super_block)) != sizeof(ext2_super_block))
852 return B_IO_ERROR;
853
854 if (!superBlock->IsValid()) {
855 FATAL("invalid superblock!\n");
856 return B_BAD_VALUE;
857 }
858
859 if (_UnsupportedIncompatibleFeatures(*superBlock) != 0)
860 return B_UNSUPPORTED;
861
862 return B_OK;
863 }
864
865
866 void
TransactionDone(bool success)867 Volume::TransactionDone(bool success)
868 {
869 if (!success) {
870 status_t status = LoadSuperBlock();
871 if (status != B_OK)
872 panic("Failed to reload ext2 superblock.\n");
873 }
874 }
875
876
877 void
RemovedFromTransaction()878 Volume::RemovedFromTransaction()
879 {
880 // TODO: Does it make a difference?
881 }
882