Skip to content

Commit 327eb11

Browse files
committed
feat: improved Disk free space calculation
1 parent ee69776 commit 327eb11

1 file changed

Lines changed: 33 additions & 38 deletions

File tree

Modules/Disk/readers.swift

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -82,16 +82,18 @@ internal class CapacityReader: Reader<Disks> {
8282
if let d = self.list.first(where: { $0.BSDName == BSDName}), let idx = self.list.index(where: { $0.BSDName == BSDName}) {
8383
if d.removable && !removableState {
8484
if d.parent != 0 { IOObjectRelease(d.parent) }
85+
if let path = d.path { self.purgableSpace.removeValue(forKey: path) }
8586
self.list.remove(at: idx)
8687
continue
8788
}
8889

8990
if driveIdentityChanged(d, url, disk) {
9091
if d.parent != 0 { IOObjectRelease(d.parent) }
92+
if let path = d.path { self.purgableSpace.removeValue(forKey: path) }
9193
self.list.remove(at: idx)
9294
} else {
9395
if let path = d.path {
94-
self.list.updateFreeSize(idx, newValue: self.freeDiskSpaceInBytes(path))
96+
self.list.updateFreeSize(idx, newValue: self.diskSpaceInBytes(path, fileSystem: d.fileSystem).free)
9597
self.list.updateSMARTData(idx, smart: self.getSMARTDetails(for: BSDName))
9698
}
9799
continue
@@ -100,8 +102,9 @@ internal class CapacityReader: Reader<Disks> {
100102

101103
if var d = driveDetails(disk, removableState: removableState) {
102104
if let path = d.path {
103-
d.free = self.freeDiskSpaceInBytes(path)
104-
d.size = self.totalDiskSpaceInBytes(path)
105+
let space = self.diskSpaceInBytes(path, fileSystem: d.fileSystem)
106+
d.free = space.free
107+
d.size = space.total
105108
}
106109
d.smart = self.getSMARTDetails(for: BSDName)
107110
guard d.size != 0 else {
@@ -118,71 +121,63 @@ internal class CapacityReader: Reader<Disks> {
118121

119122
active.difference(from: self.list.map{ $0.BSDName }).forEach { (BSDName: String) in
120123
if let idx = self.list.index(where: { $0.BSDName == BSDName }) {
121-
let parent = self.list.array[idx].parent
122-
if parent != 0 { IOObjectRelease(parent) }
124+
let d = self.list.array[idx]
125+
if d.parent != 0 { IOObjectRelease(d.parent) }
126+
if let path = d.path { self.purgableSpace.removeValue(forKey: path) }
123127
self.list.remove(at: idx)
124128
}
125129
}
126130

127131
self.callback(self.list)
128132

129133
}
130-
private func freeDiskSpaceInBytes(_ path: URL) -> Int64 {
134+
private func diskSpaceInBytes(_ path: URL, fileSystem: String) -> (total: Int64, free: Int64) {
131135
var path = path
132136
path.removeAllCachedResourceValues()
133137

134138
var stat = statfs()
135139
if statfs(path.path, &stat) == 0 {
136140
let total = Int64(stat.f_blocks) * Int64(stat.f_bsize)
137-
let free = Int64(stat.f_bfree) * Int64(stat.f_bsize)
138-
let used = total - free
141+
let available = Int64(stat.f_bavail) * Int64(stat.f_bsize)
142+
let used = total - available
139143

140144
var purgeable: Int64 = 0
141-
if let pair = self.purgableSpace[path], Date().timeIntervalSince(pair.0) <= 30 {
142-
purgeable = pair.1
143-
} else {
144-
let value = CSDiskSpaceGetRecoveryEstimate(path as NSURL)
145-
if used > 0 && value <= UInt64(used) {
146-
purgeable = Int64(value)
145+
if fileSystem == "apfs" {
146+
if let pair = self.purgableSpace[path], Date().timeIntervalSince(pair.0) <= 60 {
147+
purgeable = pair.1
148+
} else {
149+
let value = CSDiskSpaceGetRecoveryEstimate(path as NSURL)
150+
if used > 0 && value <= UInt64(used) {
151+
purgeable = Int64(value)
152+
}
153+
self.purgableSpace[path] = (Date(), purgeable)
147154
}
148-
self.purgableSpace[path] = (Date(), purgeable)
149155
}
150156

151-
return free + purgeable
157+
return (total, available + purgeable)
152158
}
153159

154-
do {
155-
let values = try path.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
156-
if let capacity = values.volumeAvailableCapacityForImportantUsage, capacity != 0 {
157-
return capacity
158-
}
159-
} catch let err {
160-
error("error retrieving free space #1: \(err.localizedDescription)", log: self.log)
161-
}
160+
var total: Int64 = 0
161+
var free: Int64 = 0
162162

163163
do {
164164
let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: path.path)
165-
if let freeSpace = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value {
166-
return freeSpace
167-
}
165+
total = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value ?? 0
166+
free = (systemAttributes[FileAttributeKey.systemFreeSize] as? NSNumber)?.int64Value ?? 0
168167
} catch let err {
169-
error("error retrieving free space: \(err.localizedDescription)", log: self.log)
168+
error("error retrieving disk space: \(err.localizedDescription)", log: self.log)
170169
}
171170

172-
return 0
173-
}
174-
175-
private func totalDiskSpaceInBytes(_ path: URL) -> Int64 {
176171
do {
177-
let systemAttributes = try FileManager.default.attributesOfFileSystem(forPath: path.path)
178-
if let totalSpace = (systemAttributes[FileAttributeKey.systemSize] as? NSNumber)?.int64Value {
179-
return totalSpace
172+
let values = try path.resourceValues(forKeys: [.volumeAvailableCapacityForImportantUsageKey])
173+
if let capacity = values.volumeAvailableCapacityForImportantUsage, capacity != 0 {
174+
free = capacity
180175
}
181176
} catch let err {
182-
error("error retrieving total space: \(err.localizedDescription)", log: self.log)
177+
error("error retrieving free space: \(err.localizedDescription)", log: self.log)
183178
}
184179

185-
return 0
180+
return (total, free)
186181
}
187182

188183
private func getSMARTDetails(for BSDName: String) -> smart_t? {
@@ -402,7 +397,7 @@ internal class CapacityReader: Reader<Disks> {
402397
let totalWritten = deviceWritten ?? self.smartTotals[BSDName]?.written ?? Int64(rawValue(241) ?? 0) * bytesPerLBA
403398

404399
let errorCounts = [rawValue(5), rawValue(197), rawValue(198)].compactMap({ $0 })
405-
400+
406401
return smart_t(
407402
temperature: temperature,
408403
life: life,

0 commit comments

Comments
 (0)