+
+sub destroy_disks {
+ my ($storecfg, $vollist) = @_;
+
+ foreach my $volid (@$vollist) {
+ eval { PVE::Storage::vdisk_free($storecfg, $volid); };
+ warn $@ if $@;
+ }
+}
+
+sub create_disks {
+ my ($storecfg, $vmid, $settings, $conf) = @_;
+
+ my $vollist = [];
+
+ eval {
+ my (undef, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
+ my $chown_vollist = [];
+
+ foreach_mountpoint($settings, sub {
+ my ($ms, $mountpoint) = @_;
+
+ my $volid = $mountpoint->{volume};
+ my $mp = $mountpoint->{mp};
+
+ my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
+
+ if ($storage && ($volid =~ m/^([^:\s]+):(\d+(\.\d+)?)$/)) {
+ my ($storeid, $size_gb) = ($1, $2);
+
+ my $size_kb = int(${size_gb}*1024) * 1024;
+
+ my $scfg = PVE::Storage::storage_config($storecfg, $storage);
+ # fixme: use better naming ct-$vmid-disk-X.raw?
+
+ if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs') {
+ if ($size_kb > 0) {
+ $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw',
+ undef, $size_kb);
+ format_disk($storecfg, $volid, $rootuid, $rootgid);
+ } else {
+ $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol',
+ undef, 0);
+ push @$chown_vollist, $volid;
+ }
+ } elsif ($scfg->{type} eq 'zfspool') {
+
+ $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol',
+ undef, $size_kb);
+ push @$chown_vollist, $volid;
+ } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'lvm' || $scfg->{type} eq 'lvmthin') {
+
+ $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
+ format_disk($storecfg, $volid, $rootuid, $rootgid);
+
+ } elsif ($scfg->{type} eq 'rbd') {
+
+ die "krbd option must be enabled on storage type '$scfg->{type}'\n" if !$scfg->{krbd};
+ $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
+ format_disk($storecfg, $volid, $rootuid, $rootgid);
+ } else {
+ die "unable to create containers on storage type '$scfg->{type}'\n";
+ }
+ push @$vollist, $volid;
+ $mountpoint->{volume} = $volid;
+ $mountpoint->{size} = $size_kb * 1024;
+ $conf->{$ms} = print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+ } else {
+ # use specified/existing volid/dir/device
+ $conf->{$ms} = print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
+ }
+ });
+
+ PVE::Storage::activate_volumes($storecfg, $chown_vollist, undef);
+ foreach my $volid (@$chown_vollist) {
+ my $path = PVE::Storage::path($storecfg, $volid, undef);
+ chown($rootuid, $rootgid, $path);
+ }
+ PVE::Storage::deactivate_volumes($storecfg, $chown_vollist, undef);
+ };
+ # free allocated images on error
+ if (my $err = $@) {
+ destroy_disks($storecfg, $vollist);
+ die $err;
+ }
+ return $vollist;
+}
+
+# bash completion helper
+
+sub complete_os_templates {
+ my ($cmdname, $pname, $cvalue) = @_;
+
+ my $cfg = PVE::Storage::config();
+
+ my $storeid;
+
+ if ($cvalue =~ m/^([^:]+):/) {
+ $storeid = $1;
+ }
+
+ my $vtype = $cmdname eq 'restore' ? 'backup' : 'vztmpl';
+ my $data = PVE::Storage::template_list($cfg, $storeid, $vtype);
+
+ my $res = [];
+ foreach my $id (keys %$data) {
+ foreach my $item (@{$data->{$id}}) {
+ push @$res, $item->{volid} if defined($item->{volid});
+ }
+ }
+
+ return $res;
+}
+
+my $complete_ctid_full = sub {
+ my ($running) = @_;
+
+ my $idlist = vmstatus();
+
+ my $active_hash = list_active_containers();
+
+ my $res = [];
+
+ foreach my $id (keys %$idlist) {
+ my $d = $idlist->{$id};
+ if (defined($running)) {
+ next if $d->{template};
+ next if $running && !$active_hash->{$id};
+ next if !$running && $active_hash->{$id};
+ }
+ push @$res, $id;
+
+ }
+ return $res;
+};
+
+sub complete_ctid {
+ return &$complete_ctid_full();
+}
+
+sub complete_ctid_stopped {
+ return &$complete_ctid_full(0);
+}
+
+sub complete_ctid_running {
+ return &$complete_ctid_full(1);
+}
+
+sub parse_id_maps {
+ my ($conf) = @_;
+
+ my $id_map = [];
+ my $rootuid = 0;
+ my $rootgid = 0;
+
+ my $lxc = $conf->{lxc};
+ foreach my $entry (@$lxc) {
+ my ($key, $value) = @$entry;
+ next if $key ne 'lxc.id_map';
+ if ($value =~ /^([ug])\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) {
+ my ($type, $ct, $host, $length) = ($1, $2, $3, $4);
+ push @$id_map, [$type, $ct, $host, $length];
+ if ($ct == 0) {
+ $rootuid = $host if $type eq 'u';
+ $rootgid = $host if $type eq 'g';
+ }
+ } else {
+ die "failed to parse id_map: $value\n";
+ }
+ }
+
+ if (!@$id_map && $conf->{unprivileged}) {
+ # Should we read them from /etc/subuid?
+ $id_map = [ ['u', '0', '100000', '65536'],
+ ['g', '0', '100000', '65536'] ];
+ $rootuid = $rootgid = 100000;
+ }
+
+ return ($id_map, $rootuid, $rootgid);
+}
+
+sub userns_command {
+ my ($id_map) = @_;
+ if (@$id_map) {
+ return ['lxc-usernsexec', (map { ('-m', join(':', @$_)) } @$id_map), '--'];
+ }
+ return [];
+}
+