mirror of
https://github.com/NixOS/mobile-nixos.git
synced 2024-12-15 19:23:01 +03:00
78 lines
2.6 KiB
Ruby
78 lines
2.6 KiB
Ruby
# Automatically resizes the given filesystem.
|
|
class Tasks::AutoResize < Task
|
|
attr_reader :device
|
|
|
|
def initialize(device, type: )
|
|
@device = device
|
|
@type = type
|
|
add_dependency(:Devices, @device)
|
|
add_dependency(:Mount, "/sys")
|
|
end
|
|
|
|
# Computes whether a filesystem needs to be expanded.
|
|
# This way the long-running tasks happen only once.
|
|
# TODO: parse more than ext filesystems
|
|
def needs_resize?()
|
|
# Parse the output of dumpe2fs
|
|
data = `dumpe2fs -h #{@device.shellescape}`
|
|
.lines
|
|
.map(&:strip)
|
|
.map { |line| line.split(/:\s*/, 2) }
|
|
.select { |pair| pair.length == 2 }
|
|
.to_h
|
|
|
|
block_size = data["Block size"].to_f
|
|
block_count = data["Block count"].to_f
|
|
|
|
# In bytes
|
|
filesystem_size = block_count * block_size
|
|
|
|
device_file = File.realpath(@device)
|
|
sys_file = Dir.glob("/sys/block/*/#{device_file.split("/").last}").first
|
|
|
|
# In bytes. From 512 bytes sectors.
|
|
partition_size = 512 * File.read(File.join(sys_file, "size")).to_f
|
|
|
|
# Accounts for a partition size that can't fit a full block, plus some
|
|
# fudge. It's been found that on some "fully resized" devices there was
|
|
# more than the block size left at the end.
|
|
fudge = 2 * block_size
|
|
|
|
# Output the sizes in the log, for later interpretations.
|
|
log("#{@device}: #{filesystem_size}/#{partition_size} in use.")
|
|
|
|
# Resize when the filesystem size is smaller than the available space.
|
|
# (While accounting for some fudge.)
|
|
filesystem_size < (partition_size - fudge)
|
|
end
|
|
|
|
def run()
|
|
if @type.match(/^ext[234]$/)
|
|
if needs_resize?
|
|
log("Resizing #{@device}...")
|
|
Progress.exec_with_message("Verifying #{@device}...") do
|
|
# TODO: Understand the actual underlying issue with e2fsck.
|
|
# It seems `e2fsck` succeeds, according to the output, but has a >0 exit
|
|
# status. Running it again in those situations is a no-op, which is weird
|
|
# to me.
|
|
# This is why we unconditionally run it once, then twice.
|
|
# The second will hopefully abort the boot if it fails too.
|
|
begin
|
|
System.run_long_running("e2fsck", "-fp", @device)
|
|
rescue System::CommandError
|
|
$logger.info("Re-running e2fsc...")
|
|
System.run_long_running("e2fsck", "-fp", @device)
|
|
end
|
|
end
|
|
Progress.exec_with_message("Resizing #{@device}...") do
|
|
System.run_long_running("resize2fs", "-f", @device)
|
|
end
|
|
else
|
|
log("No need to resize #{@device}...")
|
|
end
|
|
else
|
|
$logger.warn("Cannot resize #{@type}... filesystem left untouched.")
|
|
end
|
|
end
|
|
end
|