@@ -671,14 +671,10 @@ extension EXT4 {
671671 let bitmapOffset = self . currentBlock
672672 let bitmapBlocks : UInt32 = blockGroupSize. blockGroups * 2 // each group has two bitmaps - for inodes, and for blocks
673673 let dataBlocks : UInt32 = bitmapOffset + bitmapBlocks // last data block
674- var diskBlocks = dataBlocks
675674 var contentRequiredBlocks = ( blockGroupSize. blockGroups - 1 ) * self . blocksPerGroup + 1
676675 if blockGroupSize. blockGroups == 1 {
677676 contentRequiredBlocks = self . blocksPerGroup // at least 1 block group
678677 }
679- if diskBlocks < contentRequiredBlocks { // for data + metadata
680- diskBlocks = contentRequiredBlocks
681- }
682678 let contentRequiredSize = UInt64 ( contentRequiredBlocks) * self . blockSize
683679 // minDiskSize is usable capacity; the journal is additive on top.
684680 var newSize = self . size + journalByteCount
@@ -697,23 +693,27 @@ extension EXT4 {
697693 var groupDescriptors : [ GroupDescriptor ] = [ ]
698694
699695 let minGroups = ( ( ( self . pos / UInt64( self . blockSize) ) - 1 ) / UInt64( self . blocksPerGroup) ) + 1
700- if newSize < minGroups * blocksPerGroup * blockSize {
701- newSize = UInt64 ( minGroups * blocksPerGroup * blockSize)
702- }
703- let totalGroups = ( ( ( newSize / UInt64( self . blockSize) ) - 1 ) / UInt64( self . blocksPerGroup) ) + 1
704-
705- // If the provided disk size is not aligned to a blockgroup boundary, it needs to
706- // be expanded to the next blockgroup boundary.
707- // Example:
708- // Provided disk size: 2 GB + 100MB: 2148 MB
709- // BlockSize: 4096
710- // Blockgroup size: 32768 blocks: 128MB
711- // Number of blocks: 549888
712- // Number of blockgroups = 549888 / 32768 = 16.78125
713- // Aligned disk size = 557056 blocks = 17 blockgroups: 2176 MB
714- if newSize < totalGroups * blocksPerGroup * blockSize {
715- newSize = UInt64 ( totalGroups * blocksPerGroup * blockSize)
696+ let minBlocksForWrittenMetadata = ( minGroups - 1 ) * UInt64( self . blocksPerGroup) + 1
697+ let minSizeForWrittenMetadata = minBlocksForWrittenMetadata * UInt64( self . blockSize)
698+ if newSize < minSizeForWrittenMetadata {
699+ newSize = minSizeForWrittenMetadata
700+ }
701+
702+ let filesystemBlocks64 = ( newSize + UInt64( self . blockSize) - 1 ) / UInt64( self . blockSize)
703+ guard filesystemBlocks64 <= UInt64 ( UInt32 . max) else {
704+ throw Error . cannotResizeFS ( newSize)
705+ }
706+ let filesystemBlocks = UInt32 ( filesystemBlocks64)
707+ let totalGroups = ( ( filesystemBlocks - 1 ) / self . blocksPerGroup) + 1
708+
709+ func blocksInGroup( _ group: UInt32 ) -> UInt32 {
710+ let groupStart = group * self . blocksPerGroup
711+ guard groupStart < filesystemBlocks else {
712+ return 0
713+ }
714+ return min ( self . blocksPerGroup, filesystemBlocks - groupStart)
716715 }
716+
717717 // Snapshot groupDescriptorBlocks before self.size potentially changes: the bitmap
718718 // loop uses this to identify which GDT slots were physically reserved at init time,
719719 // so it can mark any unused slots as free without accidentally freeing content blocks
@@ -739,15 +739,15 @@ extension EXT4 {
739739 var blocks : UInt32 = 0
740740 // blocks bitmap
741741 var bitmap : [ UInt8 ] = . init( repeating: 0 , count: self . blockSize * 2 ) // 1 for blocks, 1 for inodes
742- if ( group + 1 ) * UInt32( self . blocksPerGroup) <= dataBlocks { // fully allocated group
743- for i in 0 ..< ( self . blockSize) {
744- bitmap [ Int ( i) ] = 0xff // mark as allocated
745- }
746- blocks = UInt32 ( self . blocksPerGroup)
747- } else if group * UInt32( self . blocksPerGroup) < dataBlocks { // partially allocated group
748- for i in 0 ..< dataBlocks - group * UInt32( self . blocksPerGroup) {
742+ let groupStart = group * self . blocksPerGroup
743+ let groupBlocks = blocksInGroup ( group)
744+ if groupStart < dataBlocks {
745+ let usedBlocks = min ( dataBlocks - groupStart, self . blocksPerGroup)
746+ for i in 0 ..< usedBlocks {
749747 bitmap [ Int ( i / 8 ) ] |= 1 << ( i % 8 )
750- blocks += 1
748+ if i < groupBlocks {
749+ blocks += 1
750+ }
751751 }
752752 }
753753
@@ -760,23 +760,21 @@ extension EXT4 {
760760 }
761761 if usedGroupDescriptorBlocks + 1 <= reservedDescriptorBlocks {
762762 for i in usedGroupDescriptorBlocks + 1 ... reservedDescriptorBlocks {
763+ if i < groupBlocks {
764+ blocks -= UInt32 ( ( bitmap [ Int ( i / 8 ) ] >> ( i % 8 ) ) & 1 )
765+ }
763766 bitmap [ Int ( i / 8 ) ] &= ~ ( 1 << ( i % 8 ) )
764- blocks -= 1
765767 }
766768 }
767769 }
768770
769- // last blockGroup if not aligned with total size should be marked as allocated
770- let remainingBlocks = diskBlocks % self . blocksPerGroup
771- if group == totalGroups - 1 && remainingBlocks != 0 && self . size / self . blockSize < self . blocksPerGroup {
772- for i in remainingBlocks..< self . blocksPerGroup {
771+ // The final block group may be shorter than blocksPerGroup. Mark blocks
772+ // outside the filesystem as used in the bitmap, but do not count them as
773+ // allocated filesystem blocks.
774+ if groupBlocks < self . blocksPerGroup {
775+ for i in groupBlocks..< self . blocksPerGroup {
773776 bitmap [ Int ( i / 8 ) ] |= 1 << ( i % 8 )
774777 }
775- if remainingBlocks < self . size / self . blockSize {
776- for i in remainingBlocks..< self . size / self . blockSize {
777- bitmap [ Int ( i / 8 ) ] &= ~ ( 1 << ( i % 8 ) )
778- }
779- }
780778 }
781779
782780 // mark deleted blocks as free
@@ -819,7 +817,7 @@ extension EXT4 {
819817 let blockBitmap = UInt64 ( bitmapOffset + 2 * group)
820818 let inodeBitmap = UInt64 ( bitmapOffset + 2 * group + 1 )
821819 let inodeTable = inodeTableOffset + UInt64( group * inodeTableSizePerGroup)
822- let freeBlocksCount = UInt32 ( self . blocksPerGroup - blocks)
820+ let freeBlocksCount = UInt32 ( groupBlocks - blocks)
823821 let freeInodesCount = UInt32 ( blockGroupSize. inodesPerGroup - inodes)
824822 groupDescriptors. append (
825823 // low bits
@@ -851,13 +849,20 @@ extension EXT4 {
851849 for i in 0 ..< UInt16 ( blockGroupSize. inodesPerGroup) {
852850 inodeBitmap [ Int ( i) / 8 ] &= ~ ( 1 << ( i % 8 ) )
853851 }
854- for group in blockGroupSize. blockGroups..< totalGroups. lo {
855- let blocksInGroup = UInt32 ( self . blocksPerGroup )
852+ for group in blockGroupSize. blockGroups..< totalGroups {
853+ let groupBlocks = blocksInGroup ( group )
856854 let blockBitmapOffset = UInt64 ( group * self . blocksPerGroup + inodeTableSizePerGroup)
857855 let inodeBitmapOffset = UInt64 ( group * self . blocksPerGroup + inodeTableSizePerGroup + 1 )
858856 let inodeTableOffset = UInt64 ( self . blocksPerGroup) * group
859- let freeBlocksCount = UInt32 ( blocksInGroup - inodeTableSizePerGroup - 2 )
857+ let metadataBlocks = min ( groupBlocks, inodeTableSizePerGroup + 2 )
858+ let freeBlocksCount = UInt32 ( groupBlocks - metadataBlocks)
860859 let freeInodesCount = UInt32 ( blockGroupSize. inodesPerGroup)
860+ var groupBlockBitmap = blockBitmap
861+ if groupBlocks < self . blocksPerGroup {
862+ for i in groupBlocks..< self . blocksPerGroup {
863+ groupBlockBitmap [ Int ( i / 8 ) ] |= 1 << ( i % 8 )
864+ }
865+ }
861866 groupDescriptors. append (
862867 // low bits
863868 GroupDescriptor (
@@ -874,9 +879,9 @@ extension EXT4 {
874879 itableUnusedLow: 0x0000 ,
875880 checksum: 0x0000
876881 ) )
877- totalBlocks += ( inodeTableSizePerGroup + 2 )
882+ totalBlocks += metadataBlocks
878883 try self . seek ( block: group * self . blocksPerGroup + inodeTableSizePerGroup)
879- try self . handle. write ( contentsOf: blockBitmap )
884+ try self . handle. write ( contentsOf: groupBlockBitmap )
880885 try self . handle. write ( contentsOf: inodeBitmap)
881886 }
882887
@@ -892,23 +897,20 @@ extension EXT4 {
892897 try self . handle. write ( contentsOf: Array< UInt8> . init( repeating: 0 , count: 1024 ) )
893898
894899 let computedInodes = totalGroups * blockGroupSize. inodesPerGroup
895- var blocksCount = totalGroups * self . blocksPerGroup
896- while blocksCount < totalBlocks {
897- blocksCount = UInt64 ( totalBlocks)
898- }
900+ let blocksCount = UInt64 ( filesystemBlocks)
899901 let totalFreeBlocks : UInt64
900902 if totalBlocks > blocksCount {
901903 totalFreeBlocks = 0
902904 } else {
903- totalFreeBlocks = blocksCount - totalBlocks
905+ totalFreeBlocks = blocksCount - UInt64 ( totalBlocks)
904906 }
905907 var superblock = SuperBlock ( )
906- superblock. inodesCount = computedInodes. lo
908+ superblock. inodesCount = computedInodes
907909 superblock. blocksCountLow = blocksCount. lo
908910 superblock. blocksCountHigh = blocksCount. hi
909911 superblock. freeBlocksCountLow = totalFreeBlocks. lo
910912 superblock. freeBlocksCountHigh = totalFreeBlocks. hi
911- let freeInodesCount = computedInodes. lo - totalInodes
913+ let freeInodesCount = computedInodes - totalInodes
912914 superblock. freeInodesCount = freeInodesCount
913915 superblock. firstDataBlock = 0
914916 superblock. logBlockSize = logBlockSize
0 commit comments