You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
151 lines
3.8 KiB
151 lines
3.8 KiB
package NGCP::Panel::Utils::DeviceFirmware;
|
|
|
|
use warnings;
|
|
use strict;
|
|
|
|
use English;
|
|
use POSIX;
|
|
|
|
my $chunk_size = 10*1024*1024; # 10MB
|
|
|
|
sub insert_firmware_data {
|
|
my (%args) = @_;
|
|
my $c = $args{c};
|
|
my $fw_id = $args{fw_id};
|
|
# either data_ref or data_fh are expected
|
|
my $data_ref = $args{data_ref};
|
|
my $data_fh = $args{data_fh};
|
|
|
|
# do not set it larger than 10M
|
|
|
|
$c->model('DB')->resultset('autoprov_firmwares')->find({id => $fw_id},{for => 'update'});
|
|
|
|
my $rs = $c->model('DB')->resultset('autoprov_firmwares_data')->search({
|
|
fw_id => $fw_id
|
|
});
|
|
if ($rs->count > 0) {
|
|
$rs->delete;
|
|
}
|
|
|
|
my $fh;
|
|
if ($data_fh) {
|
|
$fh = $data_fh;
|
|
seek($fh, 0, 0);
|
|
} else {
|
|
open($fh, "<:raw", $data_ref)
|
|
or die "Cannot open firmware_data filehandler: ".$ERRNO;
|
|
}
|
|
|
|
my $buffer;
|
|
my $offset = 0;
|
|
binmode $fh;
|
|
while (read($fh, $buffer, $chunk_size, 0)) {
|
|
$c->model('DB')->resultset('autoprov_firmwares_data')->create(
|
|
{ fw_id => $fw_id, data => $buffer }
|
|
);
|
|
$offset += $chunk_size;
|
|
seek($fh, $offset, 0);
|
|
}
|
|
|
|
close $fh;
|
|
|
|
return;
|
|
}
|
|
|
|
sub get_firmware_data {
|
|
my (%args) = @_;
|
|
my $c = $args{c};
|
|
my $fw_id = $args{fw_id};
|
|
|
|
my $rs = $c->model('DB')->resultset('autoprov_firmwares_data')->search({
|
|
fw_id => $fw_id
|
|
},
|
|
{
|
|
order_by => { -asc => 'id' }
|
|
});
|
|
|
|
my $data = '';
|
|
if ($rs->count) {
|
|
foreach my $fw_data ($rs->all) {
|
|
$data = $data . $fw_data->data;
|
|
}
|
|
}
|
|
|
|
return $data;
|
|
}
|
|
|
|
sub get_firmware_data_into_body {
|
|
my (%args) = @_;
|
|
my $c = $args{c};
|
|
my $fw_id = $args{fw_id};
|
|
|
|
my ($range_from, $range_to, $range_len);
|
|
if ($c->request->headers->header('Range')) {
|
|
my $range = $c->request->headers->header('Range');
|
|
if ($range =~ /^bytes=(\d+)\-(\d+)$/) {
|
|
$range_from = int($1);
|
|
$range_to = int($2);
|
|
$range_len = $range_to - $range_from + 1;
|
|
if ($range_to < $range_from) {
|
|
$range_from = $range_to = undef;
|
|
}
|
|
}
|
|
}
|
|
|
|
# TODO: for now, if the requested range fits into the first chunk, request that range
|
|
# directly from db, otherwise we fetch all and extract the requested part
|
|
# later, due to the chunking in the db;
|
|
# this should be optimized to only request the range needed directly from db, but
|
|
# it must be ensured that we also handle cross-chunk ranges
|
|
my ($col, $limit);
|
|
if (defined $range_from && defined $range_to &&
|
|
$range_from < $chunk_size && ($range_from + $range_len) < $chunk_size) {
|
|
my $mysql_range_from = $range_from + 1;
|
|
$col = \"SUBSTRING(data, $mysql_range_from, $range_len)";
|
|
$limit = 1;
|
|
} else {
|
|
$col = 'data';
|
|
$limit = 0;
|
|
}
|
|
|
|
my $rs = $c->model('DB')->resultset('autoprov_firmwares_data')->search({
|
|
fw_id => $fw_id
|
|
},
|
|
{
|
|
select => [$col, \"OCTET_LENGTH(data)"],
|
|
as => [qw/body data_length/],
|
|
order_by => { -asc => 'id' }
|
|
});
|
|
|
|
my $data = '';
|
|
my $full_len = 0;
|
|
my $use_chunk = 1;
|
|
if ($rs->count) {
|
|
foreach my $fw_data ($rs->all) {
|
|
if ($use_chunk) {
|
|
$data = $data . $fw_data->get_column('body');
|
|
if ($limit) {
|
|
$use_chunk = 0;
|
|
}
|
|
}
|
|
$full_len += $fw_data->get_column('data_length');
|
|
}
|
|
}
|
|
|
|
if (defined $range_from && defined $range_to) {
|
|
unless ($limit) {
|
|
$data = substr($data, $range_from, $range_len);
|
|
}
|
|
$c->response->status(206);
|
|
$c->response->header("Content-Range" => "bytes $range_from-$range_to/$full_len");
|
|
}
|
|
$c->response->body($data);
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
1;
|
|
|
|
# vim: set tabstop=4 expandtab:
|