12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
# File 'lib/meshtastic/admin/firmware/uf2.rb', line 12
public_class_method def self.install(opts = {})
raise ArgumentError, 'protocol must be explicitly :uf2' unless opts[:protocol] == :uf2
raise ArgumentError, 'Unsupported UF2 install options' unless (opts.keys - %i[protocol firmware bytes mount family_id board_id flash_size]).empty?
raise ArgumentError, 'Supply exactly one of firmware or bytes' unless opts.key?(:firmware) ^ opts.key?(:bytes)
bytes = image_bytes(opts.merge({}))
validate_image(opts.merge(bytes: bytes))
mount = opts[:mount]
raise ArgumentError, 'mount must be an explicit canonical absolute bootloader directory' unless mount.is_a?(String) && mount.start_with?('/') && mount != '/' && File.expand_path(mount) == mount && File.directory?(mount) && File.realpath(mount) == mount
raise NotImplementedError, 'Safe UF2 directory pinning requires Linux procfs and O_NOFOLLOW' unless File.directory?('/proc/self/fd') && File.const_defined?(:NOFOLLOW)
destination = File.join(mount, 'FIRMWARE.UF2')
File.open(mount, File::RDONLY | File::NOFOLLOW | File::NONBLOCK) do |directory|
raise ArgumentError, 'mount must be a directory' unless directory.stat.directory?
anchor = "/proc/self/fd/#{directory.fileno}"
validate_target(opts.merge(anchor: anchor))
current = File.stat(mount)
raise IOError, 'UF2 mount changed before submission' unless current.dev == directory.stat.dev && current.ino == directory.stat.ino && File.realpath(mount) == mount
File.open(File.join(anchor, 'FIRMWARE.UF2'), File::WRONLY | File::CREAT | File::EXCL | File::NOFOLLOW, 0o600) do |file|
file.binmode
written = file.write(bytes)
raise IOError, 'Incomplete UF2 submission; do not automatically retry' unless written == bytes.bytesize
file.flush
file.fsync
end
end
{ status: :copied, protocol: :uf2, bytes: bytes.bytesize, sha256: Digest::SHA256.hexdigest(bytes),
family_id: opts.fetch(:family_id), board_id: opts.fetch(:board_id), destination: destination,
flash_verified: false, reboot_verified: false }
end
|