Skip to content

Commit ed5af12

Browse files
committed
Throw if journal too large for a pathologically small filesystem.
1 parent 696d33f commit ed5af12

2 files changed

Lines changed: 23 additions & 3 deletions

File tree

Sources/ContainerizationEXT4/EXT4+Formatter.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,7 @@ extension EXT4 {
13011301
case invalidBlockSize(_ size: UInt32)
13021302
case journalTooSmall(_ size: UInt64)
13031303
case journalTooLarge(_ size: UInt64)
1304+
case filesystemTooSmallForJournal
13041305
public var description: String {
13051306
switch self {
13061307
case .notDirectory(let path):
@@ -1338,7 +1339,9 @@ extension EXT4 {
13381339
case .journalTooSmall(let size):
13391340
return "requested journal size \(size) bytes is too small; minimum is \(EXT4.MinJournalBlocks) blocks (JBD2_MIN_JOURNAL_BLOCKS)"
13401341
case .journalTooLarge(let size):
1341-
return "requested journal size \(size) bytes exceeds half the filesystem size"
1342+
return "requested journal size \(size) bytes exceeds half the filesystem size; a journal this large is unlikely to be useful"
1343+
case .filesystemTooSmallForJournal:
1344+
return "filesystem is too small to accommodate a minimum-sized journal; increase minDiskSize to at least \(2 * EXT4.MinJournalBlocks) blocks"
13421345
}
13431346
}
13441347
}

Sources/ContainerizationEXT4/EXT4+Journal.swift

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ extension EXT4.Formatter {
5050
guard blocks >= EXT4.MinJournalBlocks else {
5151
throw EXT4.Formatter.Error.journalTooSmall(size)
5252
}
53+
// A journal larger than half the filesystem is unlikely to be useful. In
54+
// writeback or ordered mode only metadata is journaled, so even the 1 GiB
55+
// default ceiling is generous. Only data=journal mode journals data blocks
56+
// too, and even then sizing beyond half the filesystem would be wasteful.
57+
// This is a policy limit, not a kernel hard limit; exceeding it would not
58+
// cause a mount failure.
59+
// Note: totalBlocks derives from minDiskSize; close() may expand the filesystem
60+
// slightly for block group alignment. The check is conservative — the final
61+
// filesystem can only be larger, so this guard never permits an oversized journal.
5362
guard blocks <= UInt64(totalBlocks) / 2 else {
5463
throw EXT4.Formatter.Error.journalTooLarge(size)
5564
}
@@ -61,9 +70,17 @@ extension EXT4.Formatter {
6170
// and 1 GiB for larger filesystems. The larger ceiling was introduced in e2fsprogs 1.43.2:
6271
// https://e2fsprogs.sourceforge.net/e2fsprogs-release.html#1.43.2
6372
let fsBytes = UInt64(totalBlocks) * UInt64(self.blockSize)
64-
let scaledBytes = fsBytes / 64 // 1/64th of the filesystem, matching e2fsprogs defaults
73+
let halfFsBytes = fsBytes / 2
6574
let minBytes: UInt64 = UInt64(EXT4.MinJournalBlocks) * UInt64(self.blockSize)
66-
let maxBytes: UInt64 = fsBytes > 128.gib() ? 1.gib() : 128.mib()
75+
// Note: totalBlocks derives from minDiskSize; close() may expand the filesystem
76+
// substantially if minDiskSize is small relative to content. This check is
77+
// conservative — the final filesystem can only be larger, so false positives
78+
// (rejecting a journal that would have fit) are possible but false negatives are not.
79+
guard minBytes <= halfFsBytes else {
80+
throw EXT4.Formatter.Error.filesystemTooSmallForJournal
81+
}
82+
let scaledBytes = fsBytes / 64 // 1/64th of the filesystem, matching e2fsprogs defaults
83+
let maxBytes: UInt64 = min(fsBytes > 128.gib() ? 1.gib() : 128.mib(), halfFsBytes)
6784
let clampedBytes = min(max(scaledBytes, minBytes), maxBytes)
6885
// Safe: clampedBytes ≤ 1 GiB and blockSize ≥ 1, so the quotient fits in UInt32.
6986
return UInt32(clampedBytes / UInt64(self.blockSize))

0 commit comments

Comments
 (0)