X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=PVE%2FStorage%2FBTRFSPlugin.pm;h=63d307df6e8f4b5fa6cbb9239cb8481a4e9cc735;hb=85043c0193962a1fea88c0ec98601f4b6d86a0cf;hp=072dfe0554d4a18f2ed2f054dae7b0098eaa01fa;hpb=a0e3e224ea1bb13f4c1f4d5632c0bd6b37045a34;p=pve-storage.git diff --git a/PVE/Storage/BTRFSPlugin.pm b/PVE/Storage/BTRFSPlugin.pm index 072dfe0..63d307d 100644 --- a/PVE/Storage/BTRFSPlugin.pm +++ b/PVE/Storage/BTRFSPlugin.pm @@ -6,7 +6,7 @@ use warnings; use base qw(PVE::Storage::Plugin); use Fcntl qw(S_ISDIR O_WRONLY O_CREAT O_EXCL); -use File::Basename qw(dirname); +use File::Basename qw(basename dirname); use File::Path qw(mkpath); use IO::Dir; use POSIX qw(EEXIST); @@ -20,6 +20,7 @@ use constant { FS_NOCOW_FL => 0x00800000, FS_IOC_GETFLAGS => 0x40086602, FS_IOC_SETFLAGS => 0x80086601, + BTRFS_MAGIC => 0x9123683e, }; # Configuration (similar to DirPlugin) @@ -42,7 +43,20 @@ sub plugindata { }, { images => 1, rootdir => 1 }, ], - format => [ { raw => 1, qcow2 => 1, vmdk => 1, subvol => 1 }, 'raw', ], + format => [ { raw => 1, subvol => 1 }, 'raw', ], + }; +} + +sub properties { + return { + nocow => { + description => "Set the NOCOW flag on files." + . " Disables data checksumming and causes data errors to be unrecoverable from" + . " while allowing direct I/O. Only use this if data does not need to be any more" + . " safe than on a single ext4 formatted disk with no underlying raid system.", + type => 'boolean', + default => 0, + }, }; } @@ -53,9 +67,13 @@ sub options { shared => { optional => 1 }, disable => { optional => 1 }, maxfiles => { optional => 1 }, + 'prune-backups'=> { optional => 1 }, content => { optional => 1 }, format => { optional => 1 }, is_mountpoint => { optional => 1 }, + nocow => { optional => 1 }, + mkdir => { optional => 1 }, + preallocation => { optional => 1 }, # TODO: The new variant of mkdir with `populate` vs `create`... }; } @@ -75,9 +93,43 @@ sub check_config { return PVE::Storage::DirPlugin::check_config($self, $sectionId, $config, $create, $skipSchemaCheck); } +my sub getfsmagic($) { + my ($path) = @_; + # The field type sizes in `struct statfs` are defined in a rather annoying way, and we only + # need the first field, which is a `long` for our supported platforms. + # Should be moved to pve-rs, so this can be the problem of the `libc` crate ;-) + # Just round up and extract what we need: + my $buf = pack('x160'); + if (0 != syscall(&PVE::Syscall::SYS_statfs, $path, $buf)) { + die "statfs on '$path' failed - $!\n"; + } + + return unpack('L!', $buf); +} + +my sub assert_btrfs($) { + my ($path) = @_; + die "'$path' is not a btrfs file system\n" + if getfsmagic($path) != BTRFS_MAGIC; +} + sub activate_storage { my ($class, $storeid, $scfg, $cache) = @_; - return PVE::Storage::DirPlugin::activate_storage($class, $storeid, $scfg, $cache); + + my $path = $scfg->{path}; + if (!defined($scfg->{mkdir}) || $scfg->{mkdir}) { + mkpath $path; + } + + my $mp = PVE::Storage::DirPlugin::parse_is_mountpoint($scfg); + if (defined($mp) && !PVE::Storage::DirPlugin::path_is_mounted($mp, $cache->{mountdata})) { + die "unable to activate storage '$storeid' - directory is expected to be a mount point but" + ." is not mounted: '$mp'\n"; + } + + assert_btrfs($path); # only assert this stuff now, ensures $path is there and better UX + + $class->SUPER::activate_storage($storeid, $scfg, $cache); } sub status { @@ -129,13 +181,13 @@ sub filesystem_path { $path .= "/$vmid" if $vtype eq 'images'; - if ($format eq 'raw') { + if (defined($format) && $format eq 'raw') { my $dir = raw_name_to_dir($name); if ($snapname) { $dir .= "\@$snapname"; } $path .= "/$dir/disk.raw"; - } elsif ($format eq 'subvol') { + } elsif (defined($format) && $format eq 'subvol') { $path .= "/$name"; if ($snapname) { $path .= "\@$snapname"; @@ -258,7 +310,7 @@ sub alloc_image { my ($class, $storeid, $scfg, $vmid, $fmt, $name, $size) = @_; if ($fmt ne 'raw' && $fmt ne 'subvol') { - return PVE::Storage::DirPlugin::alloc_image(@_); + return $class->SUPER::alloc_image($storeid, $scfg, $vmid, $fmt, $name, $size); } # From Plugin.pm: @@ -311,7 +363,7 @@ sub alloc_image { } elsif ($fmt eq 'raw') { sysopen my $fh, $path, O_WRONLY | O_CREAT | O_EXCL or die "failed to create raw file '$path' - $!\n"; - chattr($fh, ~FS_NOCOW_FL, FS_NOCOW_FL); + chattr($fh, ~FS_NOCOW_FL, FS_NOCOW_FL) if $scfg->{nocow}; truncate($fh, $size * 1024) or die "failed to set file size for '$path' - $!\n"; close($fh); @@ -358,8 +410,8 @@ sub free_image { my (undef, undef, $vmid, undef, undef, undef, $format) = $class->parse_volname($volname); - if ($format ne 'subvol' && $format ne 'raw') { - return PVE::Storage::DirPlugin::free_image(@_); + if (!defined($format) || ($format ne 'subvol' && $format ne 'raw')) { + return $class->SUPER::free_image($storeid, $scfg, $volname, $isBase, $_format); } my $path = $class->filesystem_path($scfg, $volname); @@ -370,9 +422,11 @@ sub free_image { } my $dir = dirname($subvol); + my $basename = basename($subvol); my @snapshot_vols; foreach_subvol($dir, sub { my ($volume, $name, $snapshot) = @_; + return if $name ne $basename; return if !defined $snapshot; push @snapshot_vols, "$dir/$volume"; }); @@ -414,7 +468,7 @@ sub volume_size_info { my $format = ($class->parse_volname($volname))[6]; - if ($format eq 'subvol') { + if (defined($format) && $format eq 'subvol') { my $ctime = (stat($path))[10]; my ($used, $size) = (0, 0); #my ($used, $size) = btrfs_subvol_quota($class, $path); # uses wantarray