mirror of https://github.com/digint/btrbk
btrbk: remove filename restrictions
parent
b8370de9de
commit
6a29b08f00
42
btrbk
42
btrbk
|
@ -61,8 +61,6 @@ my $compress_format_alt = join '|', map { $_->{format} } values %compression; #
|
||||||
my $ipv4_addr_match = qr/(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/;
|
my $ipv4_addr_match = qr/(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])/;
|
||||||
my $ipv6_addr_match = qr/[a-fA-F0-9]*:[a-fA-F0-9]*:[a-fA-F0-9:]+/; # simplified (contains at least two colons), matches "::1", "2001:db8::7"
|
my $ipv6_addr_match = qr/[a-fA-F0-9]*:[a-fA-F0-9]*:[a-fA-F0-9:]+/; # simplified (contains at least two colons), matches "::1", "2001:db8::7"
|
||||||
my $host_name_match = qr/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])/;
|
my $host_name_match = qr/(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])/;
|
||||||
my $file_match = qr/[0-9a-zA-Z_@\+\-\.\/]+/; # note: ubuntu uses '@' in the subvolume layout: <https://help.ubuntu.com/community/btrfs>
|
|
||||||
my $glob_match = qr/[0-9a-zA-Z_@\+\-\.\/\*]+/; # file_match plus '*'
|
|
||||||
my $uuid_match = qr/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;
|
my $uuid_match = qr/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;
|
||||||
my $btrbk_timestamp_match = qr/(?<YYYY>[0-9]{4})(?<MM>[0-9]{2})(?<DD>[0-9]{2})(T(?<hh>[0-9]{2})(?<mm>[0-9]{2})((?<ss>[0-9]{2})(?<zz>(Z|[+-][0-9]{4})))?)?(_(?<NN>[0-9]+))?/; # matches "YYYYMMDD[Thhmm[ss+0000]][_NN]"
|
my $btrbk_timestamp_match = qr/(?<YYYY>[0-9]{4})(?<MM>[0-9]{2})(?<DD>[0-9]{2})(T(?<hh>[0-9]{2})(?<mm>[0-9]{2})((?<ss>[0-9]{2})(?<zz>(Z|[+-][0-9]{4})))?)?(_(?<NN>[0-9]+))?/; # matches "YYYYMMDD[Thhmm[ss+0000]][_NN]"
|
||||||
my $raw_postfix_match_DEPRECATED = qr/--(?<received_uuid>$uuid_match)(\@(?<parent_uuid>$uuid_match))?\.btrfs?(\.(?<compress>($compress_format_alt)))?(\.(?<encrypt>gpg))?(\.(?<split>split))?(\.(?<incomplete>part))?/; # matches ".btrfs_<received_uuid>[@<parent_uuid>][.gz|bz2|xz][.gpg][.split][.part]"
|
my $raw_postfix_match_DEPRECATED = qr/--(?<received_uuid>$uuid_match)(\@(?<parent_uuid>$uuid_match))?\.btrfs?(\.(?<compress>($compress_format_alt)))?(\.(?<encrypt>gpg))?(\.(?<split>split))?(\.(?<incomplete>part))?/; # matches ".btrfs_<received_uuid>[@<parent_uuid>][.gz|bz2|xz][.gpg][.split][.part]"
|
||||||
|
@ -1457,7 +1455,7 @@ sub btrfs_subvolume_delete($@)
|
||||||
);
|
);
|
||||||
unless(defined($ret)) {
|
unless(defined($ret)) {
|
||||||
foreach(@stderr) {
|
foreach(@stderr) {
|
||||||
next unless(/^rm: cannot remove '($file_match)':/);
|
next unless(/^rm: cannot remove '(.*?)':/);
|
||||||
my $catch = $1; # make sure $catch matches $vol->{PATH}
|
my $catch = $1; # make sure $catch matches $vol->{PATH}
|
||||||
$catch =~ s/\.info$//;
|
$catch =~ s/\.info$//;
|
||||||
$catch =~ s/\.split_[a-z][a-z]$//;
|
$catch =~ s/\.split_[a-z][a-z]$//;
|
||||||
|
@ -1477,7 +1475,7 @@ sub btrfs_subvolume_delete($@)
|
||||||
);
|
);
|
||||||
unless(defined($ret)) {
|
unless(defined($ret)) {
|
||||||
foreach(@stderr) {
|
foreach(@stderr) {
|
||||||
next unless(/'($file_match)'/ || /: ($file_match)$/ || /($file_match):/);
|
next unless(/'(\/.*?)'/ || /: (\/.*)$/ || /(\/.*?):/);
|
||||||
# NOTE: as of btrfs-progs-4.16, this does not catch anything
|
# NOTE: as of btrfs-progs-4.16, this does not catch anything
|
||||||
$err_catch{$1} //= [];
|
$err_catch{$1} //= [];
|
||||||
push(@{$err_catch{$1}}, $_);
|
push(@{$err_catch{$1}}, $_);
|
||||||
|
@ -2115,10 +2113,6 @@ sub btrfs_mountpoint
|
||||||
DEBUG "Ignoring non-btrfs mount point: $mnt->{mount_source} $mnt->{mount_point} $mnt->{fs_type}";
|
DEBUG "Ignoring non-btrfs mount point: $mnt->{mount_source} $mnt->{mount_point} $mnt->{fs_type}";
|
||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
unless($mnt->{mount_point} =~ /^$file_match$/) {
|
|
||||||
INFO_ONCE "Ignoring non-parseable btrfs mountpoint: $vol->{MACHINE_ID}$mnt->{mount_point}";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
unless($mnt->{MNTOPS}->{subvolid}) {
|
unless($mnt->{MNTOPS}->{subvolid}) {
|
||||||
# kernel <= 4.2 does not have subvolid=NN in /proc/self/mounts, read it with btrfs-progs
|
# kernel <= 4.2 does not have subvolid=NN in /proc/self/mounts, read it with btrfs-progs
|
||||||
DEBUG "No subvolid provided in mounts for: $mnt->{mount_point}";
|
DEBUG "No subvolid provided in mounts for: $mnt->{mount_point}";
|
||||||
|
@ -2224,18 +2218,13 @@ sub system_read_raw_info_dir($)
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
my $deprecated_found = 0;
|
my $deprecated_found = 0;
|
||||||
foreach(@$ret)
|
foreach my $file (@$ret)
|
||||||
{
|
{
|
||||||
unless(/^($file_match)$/) {
|
|
||||||
DEBUG "Skipping non-parseable file: \"$_\"";
|
|
||||||
next;
|
|
||||||
}
|
|
||||||
my $file = $1; # untaint argument
|
|
||||||
unless($file =~ s/^\Q$droot->{PATH}\E\///) {
|
unless($file =~ s/^\Q$droot->{PATH}\E\///) {
|
||||||
ERROR("Unexpected result from 'find': file \"$file\" is not under \"$droot->{PATH}\"");
|
ERROR("Unexpected result from 'find': file \"$file\" is not under \"$droot->{PATH}\"");
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
if($file =~ /^(?<name>$file_match)\.$btrbk_timestamp_match$raw_postfix_match_DEPRECATED$/) {
|
if($file =~ /\.$btrbk_timestamp_match$raw_postfix_match_DEPRECATED$/) {
|
||||||
push @raw_targets, {
|
push @raw_targets, {
|
||||||
# NOTE: if INFO_FILE is not present, this raw target is treated as deprecated format
|
# NOTE: if INFO_FILE is not present, this raw target is treated as deprecated format
|
||||||
TYPE => 'raw',
|
TYPE => 'raw',
|
||||||
|
@ -3026,9 +3015,9 @@ sub add_btrbk_filename_info($;$)
|
||||||
# NOTE: unless long-iso file format is encountered, the timestamp is interpreted in local timezone.
|
# NOTE: unless long-iso file format is encountered, the timestamp is interpreted in local timezone.
|
||||||
|
|
||||||
$name =~ s/^(.*)\///;
|
$name =~ s/^(.*)\///;
|
||||||
if($raw_info && ($name =~ /^(?<name>$file_match)\.$btrbk_timestamp_match$raw_postfix_match$/)) { ; }
|
if($raw_info && ($name =~ /^(?<name>.+)\.$btrbk_timestamp_match$raw_postfix_match$/)) { ; }
|
||||||
elsif($raw_info && $name =~ /^(?<name>$file_match)\.$btrbk_timestamp_match$raw_postfix_match_DEPRECATED$/) { ; } # DEPRECATED raw format
|
elsif($raw_info && $name =~ /^(?<name>.+)\.$btrbk_timestamp_match$raw_postfix_match_DEPRECATED$/) { ; } # DEPRECATED raw format
|
||||||
elsif((not $raw_info) && ($name =~ /^(?<name>$file_match)\.$btrbk_timestamp_match$/)) { ; }
|
elsif((not $raw_info) && ($name =~ /^(?<name>.+)\.$btrbk_timestamp_match$/)) { ; }
|
||||||
else {
|
else {
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
@ -3876,10 +3865,7 @@ sub check_file($$;@)
|
||||||
my %opts = @_;
|
my %opts = @_;
|
||||||
my $sanitize = $opts{sanitize};
|
my $sanitize = $opts{sanitize};
|
||||||
my $error_statement = $opts{error_statement}; # if not defined, no error messages are printed
|
my $error_statement = $opts{error_statement}; # if not defined, no error messages are printed
|
||||||
my $match = $accept->{wildcards} ? $glob_match : $file_match;
|
|
||||||
|
|
||||||
if($file =~ /^($match)$/) {
|
|
||||||
$file = $1;
|
|
||||||
if($accept->{absolute} && $accept->{relative}) {
|
if($accept->{absolute} && $accept->{relative}) {
|
||||||
# accepted, matches either absolute or relative
|
# accepted, matches either absolute or relative
|
||||||
}
|
}
|
||||||
|
@ -3904,11 +3890,7 @@ sub check_file($$;@)
|
||||||
elsif(not $accept->{wildcards}) {
|
elsif(not $accept->{wildcards}) {
|
||||||
die("accept_type must contain either 'relative' or 'absolute'");
|
die("accept_type must contain either 'relative' or 'absolute'");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
ERROR "Ambiguous file ${error_statement}: $file" if(defined($error_statement));
|
|
||||||
return undef;
|
|
||||||
}
|
|
||||||
# check directory traversal
|
# check directory traversal
|
||||||
if(($file =~ /^\.\.$/) || ($file =~ /^\.\.\//) || ($file =~ /\/\.\.\//) || ($file =~ /\/\.\.$/)) {
|
if(($file =~ /^\.\.$/) || ($file =~ /^\.\.\//) || ($file =~ /\/\.\.\//) || ($file =~ /\/\.\.$/)) {
|
||||||
ERROR "Illegal directory traversal ${error_statement}: $file" if(defined($error_statement));
|
ERROR "Illegal directory traversal ${error_statement}: $file" if(defined($error_statement));
|
||||||
|
@ -5428,7 +5410,6 @@ MAIN:
|
||||||
'config|c=s' => \$config_cmdline,
|
'config|c=s' => \$config_cmdline,
|
||||||
'override=s' => \@config_override_cmdline, # e.g. --override=incremental=no
|
'override=s' => \@config_override_cmdline, # e.g. --override=incremental=no
|
||||||
'lockfile=s' => \$lockfile_cmdline,
|
'lockfile=s' => \$lockfile_cmdline,
|
||||||
'unsafe-filenames' => sub { $file_match = $glob_match = qr/.+/ },
|
|
||||||
);
|
);
|
||||||
push @getopt_options, ($program_name eq "lsbtr") ? (
|
push @getopt_options, ($program_name eq "lsbtr") ? (
|
||||||
# "lsbtr" options
|
# "lsbtr" options
|
||||||
|
@ -5692,11 +5673,8 @@ MAIN:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(defined($lockfile_cmdline)) {
|
if(defined($lockfile_cmdline)) {
|
||||||
if($lockfile_cmdline =~ /^($file_match)$/) {
|
unless($lockfile = check_file($lockfile_cmdline, { absolute => 1, relative => 1 },
|
||||||
$lockfile = $1; # untaint argument
|
error_statement => 'for option --lockfile')) {
|
||||||
} else {
|
|
||||||
ERROR "Option \"--lockfile\" is not a valid file name: \"$lockfile_cmdline\"";
|
|
||||||
HELP_MESSAGE(0);
|
|
||||||
exit 2;
|
exit 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue