add feature flags using apparmor profile generation
[pve-container.git] / src / PVE / LXC.pm
1 package PVE::LXC;
2
3 use strict;
4 use warnings;
5
6 use POSIX qw(EINTR);
7
8 use Socket;
9
10 use File::Path;
11 use File::Spec;
12 use Cwd qw();
13 use Fcntl qw(O_RDONLY O_NOFOLLOW O_DIRECTORY);
14 use Errno qw(ELOOP ENOTDIR EROFS ECONNREFUSED);
15 use IO::Socket::UNIX;
16
17 use PVE::Exception qw(raise_perm_exc);
18 use PVE::Storage;
19 use PVE::SafeSyslog;
20 use PVE::INotify;
21 use PVE::JSONSchema qw(get_standard_option);
22 use PVE::Tools qw($IPV6RE $IPV4RE dir_glob_foreach lock_file lock_file_full O_PATH);
23 use PVE::CpuSet;
24 use PVE::Network;
25 use PVE::AccessControl;
26 use PVE::ProcFSTools;
27 use PVE::Syscall;
28 use PVE::LXC::Config;
29
30 use Time::HiRes qw (gettimeofday);
31
32 my $LXC_CONFIG_PATH = '/usr/share/lxc/config';
33
34 my $nodename = PVE::INotify::nodename();
35
36 my $cpuinfo= PVE::ProcFSTools::read_cpuinfo();
37
38 sub config_list {
39     my $vmlist = PVE::Cluster::get_vmlist();
40     my $res = {};
41     return $res if !$vmlist || !$vmlist->{ids};
42     my $ids = $vmlist->{ids};
43
44     foreach my $vmid (keys %$ids) {
45         next if !$vmid; # skip CT0
46         my $d = $ids->{$vmid};
47         next if !$d->{node} || $d->{node} ne $nodename;
48         next if !$d->{type} || $d->{type} ne 'lxc';
49         $res->{$vmid} = { type => 'lxc', vmid => $vmid };
50     }
51     return $res;
52 }
53
54 sub destroy_config {
55     my ($vmid) = @_;
56
57     unlink PVE::LXC::Config->config_file($vmid, $nodename);
58 }
59
60 # container status helpers
61
62 sub list_active_containers {
63
64     my $filename = "/proc/net/unix";
65
66     # similar test is used by lcxcontainers.c: list_active_containers
67     my $res = {};
68
69     my $fh = IO::File->new ($filename, "r");
70     return $res if !$fh;
71
72     while (defined(my $line = <$fh>)) {
73         if ($line =~ m/^[a-f0-9]+:\s+\S+\s+\S+\s+\S+\s+\S+\s+\S+\s+\d+\s+(\S+)$/) {
74             my $path = $1;
75             if ($path =~ m!^@/var/lib/lxc/(\d+)/command$!) {
76                 $res->{$1} = 1;
77             }
78         }
79     }
80
81     close($fh);
82
83     return $res;
84 }
85
86 # warning: this is slow
87 sub check_running {
88     my ($vmid) = @_;
89
90     my $active_hash = list_active_containers();
91
92     return 1 if defined($active_hash->{$vmid});
93
94     return undef;
95 }
96
97 sub get_container_disk_usage {
98     my ($vmid, $pid) = @_;
99
100     return PVE::Tools::df("/proc/$pid/root/", 1);
101 }
102
103 my $last_proc_vmid_stat;
104
105 my $parse_cpuacct_stat = sub {
106     my ($vmid, $unprivileged) = @_;
107
108     my $raw = read_cgroup_value('cpuacct', $vmid, $unprivileged, 'cpuacct.stat', 1);
109
110     my $stat = {};
111
112     if ($raw =~ m/^user (\d+)\nsystem (\d+)\n/) {
113
114         $stat->{utime} = $1;
115         $stat->{stime} = $2;
116
117     }
118
119     return $stat;
120 };
121
122 our $vmstatus_return_properties = {
123     vmid => get_standard_option('pve-vmid'),
124     status => {
125         description => "LXC Container status.",
126         type => 'string',
127         enum => ['stopped', 'running'],
128     },
129     maxmem => {
130         description => "Maximum memory in bytes.",
131         type => 'integer',
132         optional => 1,
133         renderer => 'bytes',
134     },
135     maxswap => {
136         description => "Maximum SWAP memory in bytes.",
137         type => 'integer',
138         optional => 1,
139         renderer => 'bytes',
140     },
141     maxdisk => {
142         description => "Root disk size in bytes.",
143         type => 'integer',
144         optional => 1,
145         renderer => 'bytes',
146     },
147     name => {
148         description => "Container name.",
149         type => 'string',
150         optional => 1,
151     },
152     uptime => {
153         description => "Uptime.",
154         type => 'integer',
155         optional => 1,
156         renderer => 'duration',
157     },
158     cpus => {
159         description => "Maximum usable CPUs.",
160         type => 'number',
161         optional => 1,
162     },
163 };
164
165 sub vmstatus {
166     my ($opt_vmid) = @_;
167
168     my $list = $opt_vmid ? { $opt_vmid => { type => 'lxc', vmid => $opt_vmid }} : config_list();
169
170     my $active_hash = list_active_containers();
171
172     my $cpucount = $cpuinfo->{cpus} || 1;
173
174     my $cdtime = gettimeofday;
175
176     my $uptime = (PVE::ProcFSTools::read_proc_uptime(1))[0];
177     my $clock_ticks = POSIX::sysconf(&POSIX::_SC_CLK_TCK);
178
179     my $unprivileged = {};
180
181     foreach my $vmid (keys %$list) {
182         my $d = $list->{$vmid};
183
184         eval { $d->{pid} = find_lxc_pid($vmid) if defined($active_hash->{$vmid}); };
185         warn $@ if $@; # ignore errors (consider them stopped)
186
187         $d->{status} = $d->{pid} ? 'running' : 'stopped';
188
189         my $cfspath = PVE::LXC::Config->cfs_config_path($vmid);
190         my $conf = PVE::Cluster::cfs_read_file($cfspath) || {};
191
192         $unprivileged->{$vmid} = $conf->{unprivileged};
193
194         $d->{name} = $conf->{'hostname'} || "CT$vmid";
195         $d->{name} =~ s/[\s]//g;
196
197         $d->{cpus} = $conf->{cores} || $conf->{cpulimit};
198         $d->{cpus} = $cpucount if !$d->{cpus};
199
200         $d->{lock} = $conf->{lock} || '';
201
202         if ($d->{pid}) {
203             my $res = get_container_disk_usage($vmid, $d->{pid});
204             $d->{disk} = $res->{used};
205             $d->{maxdisk} = $res->{total};
206         } else {
207             $d->{disk} = 0;
208             # use 4GB by default ??
209             if (my $rootfs = $conf->{rootfs}) {
210                 my $rootinfo = PVE::LXC::Config->parse_ct_rootfs($rootfs);
211                 $d->{maxdisk} = $rootinfo->{size} || (4*1024*1024*1024);
212             } else {
213                 $d->{maxdisk} = 4*1024*1024*1024;
214             }
215         }
216
217         $d->{mem} = 0;
218         $d->{swap} = 0;
219         $d->{maxmem} = ($conf->{memory}||512)*1024*1024;
220         $d->{maxswap} = ($conf->{swap}//0)*1024*1024;
221
222         $d->{uptime} = 0;
223         $d->{cpu} = 0;
224
225         $d->{netout} = 0;
226         $d->{netin} = 0;
227
228         $d->{diskread} = 0;
229         $d->{diskwrite} = 0;
230
231         $d->{template} = PVE::LXC::Config->is_template($conf);
232     }
233
234     foreach my $vmid (keys %$list) {
235         my $d = $list->{$vmid};
236         my $pid = $d->{pid};
237
238         next if !$pid; # skip stopped CTs
239
240         my $proc_pid_stat = PVE::ProcFSTools::read_proc_pid_stat($pid);
241         $d->{uptime} = int(($uptime - $proc_pid_stat->{starttime}) / $clock_ticks); # the method lxcfs uses
242
243         my $unpriv = $unprivileged->{$vmid};
244
245         if (-d '/sys/fs/cgroup/memory') {
246             my $memory_stat = read_cgroup_list('memory', $vmid, $unpriv, 'memory.stat');
247             my $mem_usage_in_bytes = read_cgroup_value('memory', $vmid, $unpriv, 'memory.usage_in_bytes');
248
249             $d->{mem} = $mem_usage_in_bytes - $memory_stat->{total_cache};
250             $d->{swap} = read_cgroup_value('memory', $vmid, $unpriv, 'memory.memsw.usage_in_bytes') - $mem_usage_in_bytes;
251         } else {
252             $d->{mem} = 0;
253             $d->{swap} = 0;
254         }
255
256         if (-d '/sys/fs/cgroup/blkio') {
257             my $blkio_bytes = read_cgroup_value('blkio', $vmid, $unpriv, 'blkio.throttle.io_service_bytes', 1);
258             my @bytes = split(/\n/, $blkio_bytes);
259             foreach my $byte (@bytes) {
260                 if (my ($key, $value) = $byte =~ /(Read|Write)\s+(\d+)/) {
261                     $d->{diskread} += $2 if $key eq 'Read';
262                     $d->{diskwrite} += $2 if $key eq 'Write';
263                 }
264             }
265         } else {
266             $d->{diskread} = 0;
267             $d->{diskwrite} = 0;
268         }
269
270         if (-d '/sys/fs/cgroup/cpuacct') {
271             my $pstat = $parse_cpuacct_stat->($vmid, $unpriv);
272
273             my $used = $pstat->{utime} + $pstat->{stime};
274
275             my $old = $last_proc_vmid_stat->{$vmid};
276             if (!$old) {
277                 $last_proc_vmid_stat->{$vmid} = {
278                     time => $cdtime,
279                     used => $used,
280                     cpu => 0,
281                 };
282                 next;
283             }
284
285             my $dtime = ($cdtime -  $old->{time}) * $cpucount * $cpuinfo->{user_hz};
286
287             if ($dtime > 1000) {
288                 my $dutime = $used -  $old->{used};
289
290                 $d->{cpu} = (($dutime/$dtime)* $cpucount) / $d->{cpus};
291                 $last_proc_vmid_stat->{$vmid} = {
292                     time => $cdtime,
293                     used => $used,
294                     cpu => $d->{cpu},
295                 };
296             } else {
297                 $d->{cpu} = $old->{cpu};
298             }
299         } else {
300             $d->{cpu} = 0;
301         }
302     }
303
304     my $netdev = PVE::ProcFSTools::read_proc_net_dev();
305
306     foreach my $dev (keys %$netdev) {
307         next if $dev !~ m/^veth([1-9]\d*)i/;
308         my $vmid = $1;
309         my $d = $list->{$vmid};
310
311         next if !$d;
312
313         $d->{netout} += $netdev->{$dev}->{receive};
314         $d->{netin} += $netdev->{$dev}->{transmit};
315
316     }
317
318     return $list;
319 }
320
321 sub read_cgroup_list($$$$) {
322     my ($group, $vmid, $unprivileged, $name) = @_;
323
324     my $content = read_cgroup_value($group, $vmid, $unprivileged, $name, 1);
325
326     return { split(/\s+/, $content) };
327 }
328
329 sub read_cgroup_value($$$$$) {
330     my ($group, $vmid, $unprivileged, $name, $full) = @_;
331
332     my $nsdir = $unprivileged ? '' : 'ns/';
333     my $path = "/sys/fs/cgroup/$group/lxc/$vmid/${nsdir}$name";
334
335     return PVE::Tools::file_get_contents($path) if $full;
336
337     return PVE::Tools::file_read_firstline($path);
338 }
339
340 sub write_cgroup_value {
341    my ($group, $vmid, $name, $value) = @_;
342
343    my $path = "/sys/fs/cgroup/$group/lxc/$vmid/$name";
344    PVE::ProcFSTools::write_proc_entry($path, $value) if -e $path;
345
346 }
347
348 sub find_lxc_console_pids {
349
350     my $res = {};
351
352     PVE::Tools::dir_glob_foreach('/proc', '\d+', sub {
353         my ($pid) = @_;
354
355         my $cmdline = PVE::Tools::file_read_firstline("/proc/$pid/cmdline");
356         return if !$cmdline;
357
358         my @args = split(/\0/, $cmdline);
359
360         # search for lxc-console -n <vmid>
361         return if scalar(@args) != 3;
362         return if $args[1] ne '-n';
363         return if $args[2] !~ m/^\d+$/;
364         return if $args[0] !~ m|^(/usr/bin/)?lxc-console$|;
365
366         my $vmid = $args[2];
367
368         push @{$res->{$vmid}}, $pid;
369     });
370
371     return $res;
372 }
373
374 sub find_lxc_pid {
375     my ($vmid) = @_;
376
377     my $pid = undef;
378     my $parser = sub {
379         my $line = shift;
380         $pid = $1 if $line =~ m/^PID:\s+(\d+)$/;
381     };
382     PVE::Tools::run_command(['lxc-info', '-n', $vmid, '-p'], outfunc => $parser);
383
384     die "unable to get PID for CT $vmid (not running?)\n" if !$pid;
385
386     return $pid;
387 }
388
389 # Note: we cannot use Net:IP, because that only allows strict
390 # CIDR networks
391 sub parse_ipv4_cidr {
392     my ($cidr, $noerr) = @_;
393
394     if ($cidr =~ m!^($IPV4RE)(?:/(\d+))$! && ($2 > 7) &&  ($2 <= 32)) {
395         return { address => $1, netmask => $PVE::Network::ipv4_reverse_mask->[$2] };
396     }
397
398     return undef if $noerr;
399
400     die "unable to parse ipv4 address/mask\n";
401 }
402
403 sub get_cgroup_subsystems {
404         my $v1 = {};
405         my $v2 = 0;
406         my $data = PVE::Tools::file_get_contents('/proc/self/cgroup');
407         while ($data =~ /^\d+:([^:\n]*):.*$/gm) {
408                 my $type = $1;
409                 if (length($type)) {
410                         $v1->{$_} = 1 foreach split(/,/, $type);
411                 } else {
412                         $v2 = 1;
413                 }
414         }
415         return wantarray ? ($v1, $v2) : $v1;
416 }
417
418 # Currently we do not need to create seccomp profile 'files' as the only
419 # choice our configuration actually allows is "with or without keyctl()",
420 # so we distinguish between using lxc's "default" seccomp profile and our
421 # added pve-userns.seccomp file.
422 #
423 # This returns a configuration line added to the raw lxc config.
424 sub make_seccomp_config {
425     my ($conf, $unprivileged, $features) = @_;
426     # User-configured profile has precedence, note that the user's entry would
427     # be written 'after' this line anyway...
428     if (PVE::LXC::Config->has_lxc_entry($conf, 'lxc.seccomp.profile')) {
429         # Warn the user if this conflicts with a feature:
430         if ($features->{keyctl}) {
431             warn "explicitly configured lxc.seccomp.profile overrides the following settings: features:keyctl\n";
432         }
433         return '';
434     }
435
436     # Privileged containers keep using the default (which is already part of
437     # the files included via lxc.include, so we don't need to write it out,
438     # that way it stays admin-configurable via /usr/share/lxc/config/... as
439     # well)
440     return '' if !$unprivileged;
441
442     # Unprivileged containers will get keyctl() disabled by default as a
443     # workaround for systemd-networkd behavior. But we have an option to
444     # explicitly enable it:
445     return '' if $features->{keyctl};
446
447     # Finally we're in an unprivileged container without `keyctl` set
448     # explicitly. We have a file prepared for this:
449     return "lxc.seccomp.profile = $LXC_CONFIG_PATH/pve-userns.seccomp\n";
450 }
451
452 # Since lxc-3.0.2 we can have lxc generate a profile for the container
453 # automatically. The default should be equivalent to the old
454 # `lxc-container-default-cgns` profile.
455 #
456 # Additionally this also added `lxc.apparmor.raw` which can be used to inject
457 # additional lines into the profile. We can use that to allow mounting specific
458 # file systems.
459 sub make_apparmor_config {
460     my ($conf, $unprivileged, $features) = @_;
461
462     # user-configured profile has precedence, but first we go through our own
463     # code to figure out whether we should warn the user:
464
465     my $raw = "lxc.apparmor.profile = generated\n";
466     my @profile_uses;
467
468     # There's lxc.apparmor.allow_nesting now, which will add the necessary
469     # apparmor lines, create an apparmor namespace for the container, but also
470     # adds proc and sysfs mounts to /dev/.lxc/{proc,sys}. These do not have
471     # lxcfs mounted over them, because that would prevent the container from
472     # mounting new instances of them for nested containers.
473     if ($features->{nesting}) {
474         push @profile_uses, 'features:nesting';
475         $raw .= "lxc.apparmor.allow_nesting = 1\n"
476     } else {
477         # In the default profile in /etc/apparmor.d we patch this in because
478         # otherwise a container can for example run `chown` on /sys, breaking
479         # access to it for non-CAP_DAC_OVERRIDE tools on the host:
480         $raw .= "lxc.apparmor.raw = deny mount -> /proc/,\n";
481         $raw .= "lxc.apparmor.raw = deny mount -> /sys/,\n";
482         # Preferably we could use the 'remount' flag but this does not sit well
483         # with apparmor_parser currently:
484         #  mount options=(rw, nosuid, nodev, noexec, remount) -> /sys/,
485     }
486
487     if (my $mount = $features->{mount}) {
488         push @profile_uses, 'features:mount';
489         foreach my $fs (PVE::Tools::split_list($mount)) {
490             $raw .= "lxc.apparmor.raw = mount fstype=$fs,\n";
491         }
492     }
493
494     # More to come?
495
496     if (PVE::LXC::Config->has_lxc_entry($conf, 'lxc.apparmor.profile')) {
497         if (length(my $used = join(', ', @profile_uses))) {
498             warn "explicitly configured lxc.apparmor.profile overrides the following settings: $used\n";
499         }
500         return '';
501     }
502
503     return $raw;
504 }
505
506 sub update_lxc_config {
507     my ($vmid, $conf) = @_;
508
509     my $dir = "/var/lib/lxc/$vmid";
510
511     if ($conf->{template}) {
512
513         unlink "$dir/config";
514
515         return;
516     }
517
518     my $raw = '';
519
520     die "missing 'arch' - internal error" if !$conf->{arch};
521     $raw .= "lxc.arch = $conf->{arch}\n";
522
523     my $custom_idmap = PVE::LXC::Config->has_lxc_entry($conf, 'lxc.idmap');
524     my $unprivileged = $conf->{unprivileged} || $custom_idmap;
525
526     my $ostype = $conf->{ostype} || die "missing 'ostype' - internal error";
527
528     my $cfgpath = '/usr/share/lxc/config';
529     my $inc = "$cfgpath/$ostype.common.conf";
530     $inc ="$cfgpath/common.conf" if !-f $inc;
531     $raw .= "lxc.include = $inc\n";
532     if ($unprivileged) {
533         $inc = "$cfgpath/$ostype.userns.conf";
534         $inc = "$cfgpath/userns.conf" if !-f $inc;
535         $raw .= "lxc.include = $inc\n";
536     }
537
538     my $features = PVE::LXC::Config->parse_features($conf->{features});
539
540     $raw .= make_seccomp_config($conf, $unprivileged, $features);
541     $raw .= make_apparmor_config($conf, $unprivileged, $features);
542
543     # WARNING: DO NOT REMOVE this without making sure that loop device nodes
544     # cannot be exposed to the container with r/w access (cgroup perms).
545     # When this is enabled mounts will still remain in the monitor's namespace
546     # after the container unmounted them and thus will not detach from their
547     # files while the container is running!
548     $raw .= "lxc.monitor.unshare = 1\n";
549
550     my $cgv1 = get_cgroup_subsystems();
551
552     # Should we read them from /etc/subuid?
553     if ($unprivileged && !$custom_idmap) {
554         $raw .= "lxc.idmap = u 0 100000 65536\n";
555         $raw .= "lxc.idmap = g 0 100000 65536\n";
556     }
557
558     if (!PVE::LXC::Config->has_dev_console($conf)) {
559         $raw .= "lxc.console.path = none\n";
560         $raw .= "lxc.cgroup.devices.deny = c 5:1 rwm\n" if $cgv1->{devices};
561     }
562
563     my $ttycount = PVE::LXC::Config->get_tty_count($conf);
564     $raw .= "lxc.tty.max = $ttycount\n";
565
566     # some init scripts expect a linux terminal (turnkey).
567     $raw .= "lxc.environment = TERM=linux\n";
568     
569     my $utsname = $conf->{hostname} || "CT$vmid";
570     $raw .= "lxc.uts.name = $utsname\n";
571
572     if ($cgv1->{memory}) {
573         my $memory = $conf->{memory} || 512;
574         my $swap = $conf->{swap} // 0;
575
576         my $lxcmem = int($memory*1024*1024);
577         $raw .= "lxc.cgroup.memory.limit_in_bytes = $lxcmem\n";
578
579         my $lxcswap = int(($memory + $swap)*1024*1024);
580         $raw .= "lxc.cgroup.memory.memsw.limit_in_bytes = $lxcswap\n";
581     }
582
583     if ($cgv1->{cpu}) {
584         if (my $cpulimit = $conf->{cpulimit}) {
585             $raw .= "lxc.cgroup.cpu.cfs_period_us = 100000\n";
586             my $value = int(100000*$cpulimit);
587             $raw .= "lxc.cgroup.cpu.cfs_quota_us = $value\n";
588         }
589
590         my $shares = $conf->{cpuunits} || 1024;
591         $raw .= "lxc.cgroup.cpu.shares = $shares\n";
592     }
593
594     die "missing 'rootfs' configuration\n"
595         if !defined($conf->{rootfs});
596
597     my $mountpoint = PVE::LXC::Config->parse_ct_rootfs($conf->{rootfs});
598
599     $raw .= "lxc.rootfs.path = $dir/rootfs\n";
600
601     foreach my $k (sort keys %$conf) {
602         next if $k !~ m/^net(\d+)$/;
603         my $ind = $1;
604         my $d = PVE::LXC::Config->parse_lxc_network($conf->{$k});
605         $raw .= "lxc.net.$ind.type = veth\n";
606         $raw .= "lxc.net.$ind.veth.pair = veth${vmid}i${ind}\n";
607         $raw .= "lxc.net.$ind.hwaddr = $d->{hwaddr}\n" if defined($d->{hwaddr});
608         $raw .= "lxc.net.$ind.name = $d->{name}\n" if defined($d->{name});
609         $raw .= "lxc.net.$ind.mtu = $d->{mtu}\n" if defined($d->{mtu});
610     }
611
612     if ($cgv1->{cpuset}) {
613         my $had_cpuset = 0;
614         if (my $lxcconf = $conf->{lxc}) {
615             foreach my $entry (@$lxcconf) {
616                 my ($k, $v) = @$entry;
617                 $had_cpuset = 1 if $k eq 'lxc.cgroup.cpuset.cpus';
618                 $raw .= "$k = $v\n";
619             }
620         }
621
622         my $cores = $conf->{cores};
623         if (!$had_cpuset && $cores) {
624             my $cpuset = eval { PVE::CpuSet->new_from_cgroup('lxc', 'effective_cpus') };
625             $cpuset = PVE::CpuSet->new_from_cgroup('', 'effective_cpus') if !$cpuset;
626             my @members = $cpuset->members();
627             while (scalar(@members) > $cores) {
628                 my $randidx = int(rand(scalar(@members)));
629                 $cpuset->delete($members[$randidx]);
630                 splice(@members, $randidx, 1); # keep track of the changes
631             }
632             $raw .= "lxc.cgroup.cpuset.cpus = ".$cpuset->short_string()."\n";
633         }
634     }
635
636     File::Path::mkpath("$dir/rootfs");
637
638     PVE::Tools::file_set_contents("$dir/config", $raw);
639 }
640
641 # verify and cleanup nameserver list (replace \0 with ' ')
642 sub verify_nameserver_list {
643     my ($nameserver_list) = @_;
644
645     my @list = ();
646     foreach my $server (PVE::Tools::split_list($nameserver_list)) {
647         PVE::JSONSchema::pve_verify_ip($server);
648         push @list, $server;
649     }
650
651     return join(' ', @list);
652 }
653
654 sub verify_searchdomain_list {
655     my ($searchdomain_list) = @_;
656
657     my @list = ();
658     foreach my $server (PVE::Tools::split_list($searchdomain_list)) {
659         # todo: should we add checks for valid dns domains?
660         push @list, $server;
661     }
662
663     return join(' ', @list);
664 }
665
666 sub get_console_command {
667     my ($vmid, $conf, $noescapechar) = @_;
668
669     my $cmode = PVE::LXC::Config->get_cmode($conf);
670
671     my $cmd = [];
672     if ($cmode eq 'console') {
673         push @$cmd, 'lxc-console', '-n',  $vmid, '-t', 0;
674         push @$cmd, '-e', -1 if $noescapechar;
675     } elsif ($cmode eq 'tty') {
676         push @$cmd, 'lxc-console', '-n',  $vmid;
677         push @$cmd, '-e', -1 if $noescapechar;
678     } elsif ($cmode eq 'shell') {
679         push @$cmd, 'lxc-attach', '--clear-env', '-n', $vmid;
680     } else {
681         die "internal error";
682     }
683
684     return $cmd;
685 }
686
687 sub get_primary_ips {
688     my ($conf) = @_;
689
690     # return data from net0
691
692     return undef if !defined($conf->{net0});
693     my $net = PVE::LXC::Config->parse_lxc_network($conf->{net0});
694
695     my $ipv4 = $net->{ip};
696     if ($ipv4) {
697         if ($ipv4 =~ /^(dhcp|manual)$/) {
698             $ipv4 = undef
699         } else {
700             $ipv4 =~ s!/\d+$!!;
701         }
702     }
703     my $ipv6 = $net->{ip6};
704     if ($ipv6) {
705         if ($ipv6 =~ /^(auto|dhcp|manual)$/) {
706             $ipv6 = undef;
707         } else {
708             $ipv6 =~ s!/\d+$!!;
709         }
710     }
711
712     return ($ipv4, $ipv6);
713 }
714
715 sub delete_mountpoint_volume {
716     my ($storage_cfg, $vmid, $volume) = @_;
717
718     return if PVE::LXC::Config->classify_mountpoint($volume) ne 'volume';
719
720     my ($vtype, $name, $owner) = PVE::Storage::parse_volname($storage_cfg, $volume);
721     PVE::Storage::vdisk_free($storage_cfg, $volume) if $vmid == $owner;
722 }
723
724 sub destroy_lxc_container {
725     my ($storage_cfg, $vmid, $conf, $replacement_conf) = @_;
726
727     PVE::LXC::Config->foreach_mountpoint($conf, sub {
728         my ($ms, $mountpoint) = @_;
729         delete_mountpoint_volume($storage_cfg, $vmid, $mountpoint->{volume});
730     });
731
732     rmdir "/var/lib/lxc/$vmid/rootfs";
733     unlink "/var/lib/lxc/$vmid/config";
734     rmdir "/var/lib/lxc/$vmid";
735     if (defined $replacement_conf) {
736         PVE::LXC::Config->write_config($vmid, $replacement_conf);
737     } else {
738         destroy_config($vmid);
739     }
740
741     #my $cmd = ['lxc-destroy', '-n', $vmid ];
742     #PVE::Tools::run_command($cmd);
743 }
744
745 sub vm_stop_cleanup {
746     my ($storage_cfg, $vmid, $conf, $keepActive) = @_;
747     
748     eval {
749         if (!$keepActive) {
750
751             my $vollist = PVE::LXC::Config->get_vm_volumes($conf);
752             PVE::Storage::deactivate_volumes($storage_cfg, $vollist);
753         }
754     };
755     warn $@ if $@; # avoid errors - just warn
756 }
757
758 my $safe_num_ne = sub {
759     my ($a, $b) = @_;
760
761     return 0 if !defined($a) && !defined($b);
762     return 1 if !defined($a);
763     return 1 if !defined($b);
764
765     return $a != $b;
766 };
767
768 my $safe_string_ne = sub {
769     my ($a, $b) = @_;
770
771     return 0 if !defined($a) && !defined($b);
772     return 1 if !defined($a);
773     return 1 if !defined($b);
774
775     return $a ne $b;
776 };
777
778 sub update_net {
779     my ($vmid, $conf, $opt, $newnet, $netid, $rootdir) = @_;
780
781     if ($newnet->{type} ne 'veth') {
782         # for when there are physical interfaces
783         die "cannot update interface of type $newnet->{type}";
784     }
785
786     my $veth = "veth${vmid}i${netid}";
787     my $eth = $newnet->{name};
788
789     if (my $oldnetcfg = $conf->{$opt}) {
790         my $oldnet = PVE::LXC::Config->parse_lxc_network($oldnetcfg);
791
792         if (&$safe_string_ne($oldnet->{hwaddr}, $newnet->{hwaddr}) ||
793             &$safe_string_ne($oldnet->{name}, $newnet->{name})) {
794
795             PVE::Network::veth_delete($veth);
796             delete $conf->{$opt};
797             PVE::LXC::Config->write_config($vmid, $conf);
798
799             hotplug_net($vmid, $conf, $opt, $newnet, $netid);
800
801         } else {
802             if (&$safe_string_ne($oldnet->{bridge}, $newnet->{bridge}) ||
803                 &$safe_num_ne($oldnet->{tag}, $newnet->{tag}) ||
804                 &$safe_num_ne($oldnet->{firewall}, $newnet->{firewall})) {
805
806                 if ($oldnet->{bridge}) {
807                     PVE::Network::tap_unplug($veth);
808                     foreach (qw(bridge tag firewall)) {
809                         delete $oldnet->{$_};
810                     }
811                     $conf->{$opt} = PVE::LXC::Config->print_lxc_network($oldnet);
812                     PVE::LXC::Config->write_config($vmid, $conf);
813                 }
814
815                 PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
816                 # This includes the rate:
817                 foreach (qw(bridge tag firewall rate)) {
818                     $oldnet->{$_} = $newnet->{$_} if $newnet->{$_};
819                 }
820             } elsif (&$safe_string_ne($oldnet->{rate}, $newnet->{rate})) {
821                 # Rate can be applied on its own but any change above needs to
822                 # include the rate in tap_plug since OVS resets everything.
823                 PVE::Network::tap_rate_limit($veth, $newnet->{rate});
824                 $oldnet->{rate} = $newnet->{rate}
825             }
826             $conf->{$opt} = PVE::LXC::Config->print_lxc_network($oldnet);
827             PVE::LXC::Config->write_config($vmid, $conf);
828         }
829     } else {
830         hotplug_net($vmid, $conf, $opt, $newnet, $netid);
831     }
832
833     update_ipconfig($vmid, $conf, $opt, $eth, $newnet, $rootdir);
834 }
835
836 sub hotplug_net {
837     my ($vmid, $conf, $opt, $newnet, $netid) = @_;
838
839     my $veth = "veth${vmid}i${netid}";
840     my $vethpeer = $veth . "p";
841     my $eth = $newnet->{name};
842
843     PVE::Network::veth_create($veth, $vethpeer, $newnet->{bridge}, $newnet->{hwaddr});
844     PVE::Network::tap_plug($veth, $newnet->{bridge}, $newnet->{tag}, $newnet->{firewall}, $newnet->{trunks}, $newnet->{rate});
845
846     # attach peer in container
847     my $cmd = ['lxc-device', '-n', $vmid, 'add', $vethpeer, "$eth" ];
848     PVE::Tools::run_command($cmd);
849
850     # link up peer in container
851     $cmd = ['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', '/sbin/ip', 'link', 'set', $eth ,'up'  ];
852     PVE::Tools::run_command($cmd);
853
854     my $done = { type => 'veth' };
855     foreach (qw(bridge tag firewall hwaddr name)) {
856         $done->{$_} = $newnet->{$_} if $newnet->{$_};
857     }
858     $conf->{$opt} = PVE::LXC::Config->print_lxc_network($done);
859
860     PVE::LXC::Config->write_config($vmid, $conf);
861 }
862
863 sub update_ipconfig {
864     my ($vmid, $conf, $opt, $eth, $newnet, $rootdir) = @_;
865
866     my $lxc_setup = PVE::LXC::Setup->new($conf, $rootdir);
867
868     my $optdata = PVE::LXC::Config->parse_lxc_network($conf->{$opt});
869     my $deleted = [];
870     my $added = [];
871     my $nscmd = sub {
872         my $cmdargs = shift;
873         PVE::Tools::run_command(['lxc-attach', '-n', $vmid, '-s', 'NETWORK', '--', @_], %$cmdargs);
874     };
875     my $ipcmd = sub { &$nscmd({}, '/sbin/ip', @_) };
876
877     my $change_ip_config = sub {
878         my ($ipversion) = @_;
879
880         my $family_opt = "-$ipversion";
881         my $suffix = $ipversion == 4 ? '' : $ipversion;
882         my $gw= "gw$suffix";
883         my $ip= "ip$suffix";
884
885         my $newip = $newnet->{$ip};
886         my $newgw = $newnet->{$gw};
887         my $oldip = $optdata->{$ip};
888         my $oldgw = $optdata->{$gw};
889
890         my $change_ip = &$safe_string_ne($oldip, $newip);
891         my $change_gw = &$safe_string_ne($oldgw, $newgw);
892
893         return if !$change_ip && !$change_gw;
894
895         # step 1: add new IP, if this fails we cancel
896         my $is_real_ip = ($newip && $newip !~ /^(?:auto|dhcp|manual)$/);
897         if ($change_ip && $is_real_ip) {
898             eval { &$ipcmd($family_opt, 'addr', 'add', $newip, 'dev', $eth); };
899             if (my $err = $@) {
900                 warn $err;
901                 return;
902             }
903         }
904
905         # step 2: replace gateway
906         #   If this fails we delete the added IP and cancel.
907         #   If it succeeds we save the config and delete the old IP, ignoring
908         #   errors. The config is then saved.
909         # Note: 'ip route replace' can add
910         if ($change_gw) {
911             if ($newgw) {
912                 eval {
913                     if ($is_real_ip && !PVE::Network::is_ip_in_cidr($newgw, $newip, $ipversion)) {
914                         &$ipcmd($family_opt, 'route', 'add', $newgw, 'dev', $eth);
915                     }
916                     &$ipcmd($family_opt, 'route', 'replace', 'default', 'via', $newgw);
917                 };
918                 if (my $err = $@) {
919                     warn $err;
920                     # the route was not replaced, the old IP is still available
921                     # rollback (delete new IP) and cancel
922                     if ($change_ip) {
923                         eval { &$ipcmd($family_opt, 'addr', 'del', $newip, 'dev', $eth); };
924                         warn $@ if $@; # no need to die here
925                     }
926                     return;
927                 }
928             } else {
929                 eval { &$ipcmd($family_opt, 'route', 'del', 'default'); };
930                 # if the route was not deleted, the guest might have deleted it manually
931                 # warn and continue
932                 warn $@ if $@;
933             }
934             if ($oldgw && $oldip && !PVE::Network::is_ip_in_cidr($oldgw, $oldip)) {
935                 eval { &$ipcmd($family_opt, 'route', 'del', $oldgw, 'dev', $eth); };
936                 # warn if the route was deleted manually
937                 warn $@ if $@;
938             }
939         }
940
941         # from this point on we save the configuration
942         # step 3: delete old IP ignoring errors
943         if ($change_ip && $oldip && $oldip !~ /^(?:auto|dhcp)$/) {
944             # We need to enable promote_secondaries, otherwise our newly added
945             # address will be removed along with the old one.
946             my $promote = 0;
947             eval {
948                 if ($ipversion == 4) {
949                     &$nscmd({ outfunc => sub { $promote = int(shift) } },
950                             'cat', "/proc/sys/net/ipv4/conf/$eth/promote_secondaries");
951                     &$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=1");
952                 }
953                 &$ipcmd($family_opt, 'addr', 'del', $oldip, 'dev', $eth);
954             };
955             warn $@ if $@; # no need to die here
956
957             if ($ipversion == 4) {
958                 &$nscmd({}, 'sysctl', "net.ipv4.conf.$eth.promote_secondaries=$promote");
959             }
960         }
961
962         foreach my $property ($ip, $gw) {
963             if ($newnet->{$property}) {
964                 $optdata->{$property} = $newnet->{$property};
965             } else {
966                 delete $optdata->{$property};
967             }
968         }
969         $conf->{$opt} = PVE::LXC::Config->print_lxc_network($optdata);
970         PVE::LXC::Config->write_config($vmid, $conf);
971         $lxc_setup->setup_network($conf);
972     };
973
974     &$change_ip_config(4);
975     &$change_ip_config(6);
976
977 }
978
979 my $enter_namespace = sub {
980     my ($vmid, $pid, $which, $type) = @_;
981     sysopen my $fd, "/proc/$pid/ns/$which", O_RDONLY
982         or die "failed to open $which namespace of container $vmid: $!\n";
983     PVE::Tools::setns(fileno($fd), $type)
984         or die "failed to enter $which namespace of container $vmid: $!\n";
985     close $fd;
986 };
987
988 my $do_syncfs = sub {
989     my ($vmid, $pid, $socket) = @_;
990
991     &$enter_namespace($vmid, $pid, 'mnt', PVE::Tools::CLONE_NEWNS);
992
993     # Tell the parent process to start reading our /proc/mounts
994     print {$socket} "go\n";
995     $socket->flush();
996
997     # Receive /proc/self/mounts
998     my $mountdata = do { local $/ = undef; <$socket> };
999     close $socket;
1000
1001     # Now sync all mountpoints...
1002     my $mounts = PVE::ProcFSTools::parse_mounts($mountdata);
1003     foreach my $mp (@$mounts) {
1004         my ($what, $dir, $fs) = @$mp;
1005         next if $fs eq 'fuse.lxcfs';
1006         eval { PVE::Tools::sync_mountpoint($dir); };
1007         warn $@ if $@;
1008     }
1009 };
1010
1011 sub sync_container_namespace {
1012     my ($vmid) = @_;
1013     my $pid = find_lxc_pid($vmid);
1014
1015     # SOCK_DGRAM is nicer for barriers but cannot be slurped
1016     socketpair my $pfd, my $cfd, AF_UNIX, SOCK_STREAM, PF_UNSPEC
1017         or die "failed to create socketpair: $!\n";
1018
1019     my $child = fork();
1020     die "fork failed: $!\n" if !defined($child);
1021
1022     if (!$child) {
1023         eval {
1024             close $pfd;
1025             &$do_syncfs($vmid, $pid, $cfd);
1026         };
1027         if (my $err = $@) {
1028             warn $err;
1029             POSIX::_exit(1);
1030         }
1031         POSIX::_exit(0);
1032     }
1033     close $cfd;
1034     my $go = <$pfd>;
1035     die "failed to enter container namespace\n" if $go ne "go\n";
1036
1037     open my $mounts, '<', "/proc/$child/mounts"
1038         or die "failed to open container's /proc/mounts: $!\n";
1039     my $mountdata = do { local $/ = undef; <$mounts> };
1040     close $mounts;
1041     print {$pfd} $mountdata;
1042     close $pfd;
1043
1044     while (waitpid($child, 0) != $child) {}
1045     die "failed to sync container namespace\n" if $? != 0;
1046 }
1047
1048 sub template_create {
1049     my ($vmid, $conf) = @_;
1050
1051     my $storecfg = PVE::Storage::config();
1052
1053     PVE::LXC::Config->foreach_mountpoint($conf, sub {
1054         my ($ms, $mountpoint) = @_;
1055
1056         my $volid = $mountpoint->{volume};
1057
1058         die "Template feature is not available for '$volid'\n"
1059             if !PVE::Storage::volume_has_feature($storecfg, 'template', $volid);
1060     });
1061
1062     PVE::LXC::Config->foreach_mountpoint($conf, sub {
1063         my ($ms, $mountpoint) = @_;
1064
1065         my $volid = $mountpoint->{volume};
1066
1067         PVE::Storage::activate_volumes($storecfg, [$volid]);
1068
1069         my $template_volid = PVE::Storage::vdisk_create_base($storecfg, $volid);
1070         $mountpoint->{volume} = $template_volid;
1071         $conf->{$ms} = PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq "rootfs");
1072     });
1073
1074     PVE::LXC::Config->write_config($vmid, $conf);
1075 }
1076
1077 sub check_ct_modify_config_perm {
1078     my ($rpcenv, $authuser, $vmid, $pool, $newconf, $delete) = @_;
1079
1080     return 1 if $authuser eq 'root@pam';
1081
1082     my $check = sub {
1083         my ($opt, $delete) = @_;
1084         if ($opt eq 'cores' || $opt eq 'cpuunits' || $opt eq 'cpulimit') {
1085             $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.CPU']);
1086         } elsif ($opt eq 'rootfs' || $opt =~ /^mp\d+$/) {
1087             $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Disk']);
1088             return if $delete;
1089             my $data = $opt eq 'rootfs' ? PVE::LXC::Config->parse_ct_rootfs($newconf->{$opt})
1090                                         : PVE::LXC::Config->parse_ct_mountpoint($newconf->{$opt});
1091             raise_perm_exc("mount point type $data->{type} is only allowed for root\@pam")
1092                 if $data->{type} ne 'volume';
1093         } elsif ($opt eq 'memory' || $opt eq 'swap') {
1094             $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Memory']);
1095         } elsif ($opt =~ m/^net\d+$/ || $opt eq 'nameserver' ||
1096                  $opt eq 'searchdomain' || $opt eq 'hostname') {
1097             $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Network']);
1098         } elsif ($opt eq 'features') {
1099             # For now this is restricted to root@pam
1100             raise_perm_exc("changing feature flags is only allowed for root\@pam");
1101         } else {
1102             $rpcenv->check_vm_perm($authuser, $vmid, $pool, ['VM.Config.Options']);
1103         }
1104     };
1105
1106     foreach my $opt (keys %$newconf) {
1107         &$check($opt, 0);
1108     }
1109     foreach my $opt (@$delete) {
1110         &$check($opt, 1);
1111     }
1112
1113     return 1;
1114 }
1115
1116 sub umount_all {
1117     my ($vmid, $storage_cfg, $conf, $noerr) = @_;
1118
1119     my $rootdir = "/var/lib/lxc/$vmid/rootfs";
1120     my $volid_list = PVE::LXC::Config->get_vm_volumes($conf);
1121
1122     PVE::LXC::Config->foreach_mountpoint_reverse($conf, sub {
1123         my ($ms, $mountpoint) = @_;
1124
1125         my $volid = $mountpoint->{volume};
1126         my $mount = $mountpoint->{mp};
1127
1128         return if !$volid || !$mount;
1129
1130         my $mount_path = "$rootdir/$mount";
1131         $mount_path =~ s!/+!/!g;
1132
1133         return if !PVE::ProcFSTools::is_mounted($mount_path);
1134
1135         eval {
1136             PVE::Tools::run_command(['umount', '-d', $mount_path]);
1137         };
1138         if (my $err = $@) {
1139             if ($noerr) {
1140                 warn $err;
1141             } else {
1142                 die $err;
1143             }
1144         }
1145     });
1146 }
1147
1148 sub mount_all {
1149     my ($vmid, $storage_cfg, $conf, $ignore_ro) = @_;
1150
1151     my $rootdir = "/var/lib/lxc/$vmid/rootfs";
1152     File::Path::make_path($rootdir);
1153
1154     my $volid_list = PVE::LXC::Config->get_vm_volumes($conf);
1155     PVE::Storage::activate_volumes($storage_cfg, $volid_list);
1156
1157     eval {
1158         PVE::LXC::Config->foreach_mountpoint($conf, sub {
1159             my ($ms, $mountpoint) = @_;
1160
1161             $mountpoint->{ro} = 0 if $ignore_ro;
1162
1163             mountpoint_mount($mountpoint, $rootdir, $storage_cfg);
1164         });
1165     };
1166     if (my $err = $@) {
1167         warn "mounting container failed\n";
1168         umount_all($vmid, $storage_cfg, $conf, 1);
1169         die $err;
1170     }
1171
1172     return $rootdir;
1173 }
1174
1175
1176 sub mountpoint_mount_path {
1177     my ($mountpoint, $storage_cfg, $snapname) = @_;
1178
1179     return mountpoint_mount($mountpoint, undef, $storage_cfg, $snapname);
1180 }
1181
1182 sub query_loopdev {
1183     my ($path) = @_;
1184     my $found;
1185     my $parser = sub {
1186         my $line = shift;
1187         if ($line =~ m@^(/dev/loop\d+):@) {
1188             $found = $1;
1189         }
1190     };
1191     my $cmd = ['losetup', '--associated', $path];
1192     PVE::Tools::run_command($cmd, outfunc => $parser);
1193     return $found;
1194 }
1195
1196 # Run a function with a file attached to a loop device.
1197 # The loop device is always detached afterwards (or set to autoclear).
1198 # Returns the loop device.
1199 sub run_with_loopdev {
1200     my ($func, $file) = @_;
1201     my $device = query_loopdev($file);
1202     # Try to reuse an existing device
1203     if ($device) {
1204         # We assume that whoever setup the loop device is responsible for
1205         # detaching it.
1206         &$func($device);
1207         return $device;
1208     }
1209
1210     my $parser = sub {
1211         my $line = shift;
1212         if ($line =~ m@^(/dev/loop\d+)$@) {
1213             $device = $1;
1214         }
1215     };
1216     PVE::Tools::run_command(['losetup', '--show', '-f', $file], outfunc => $parser);
1217     die "failed to setup loop device for $file\n" if !$device;
1218     eval { &$func($device); };
1219     my $err = $@;
1220     PVE::Tools::run_command(['losetup', '-d', $device]);
1221     die $err if $err;
1222     return $device;
1223 }
1224
1225 # In scalar mode: returns a file handle to the deepest directory node.
1226 # In list context: returns a list of:
1227 #   * the deepest directory node
1228 #   * the 2nd deepest directory (parent of the above)
1229 #   * directory name of the last directory
1230 # So that the path $2/$3 should lead to $1 afterwards.
1231 sub walk_tree_nofollow($$$) {
1232     my ($start, $subdir, $mkdir) = @_;
1233
1234     # splitdir() returns '' for empty components including the leading /
1235     my @comps = grep { length($_)>0 } File::Spec->splitdir($subdir);
1236
1237     sysopen(my $fd, $start, O_PATH | O_DIRECTORY)
1238         or die "failed to open start directory $start: $!\n";
1239
1240     my $dir = $start;
1241     my $last_component = undef;
1242     my $second = $fd;
1243     foreach my $component (@comps) {
1244         $dir .= "/$component";
1245         my $next = PVE::Tools::openat(fileno($fd), $component, O_NOFOLLOW | O_DIRECTORY);
1246
1247         if (!$next) {
1248             # failed, check for symlinks and try to create the path
1249             die "symlink encountered at: $dir\n" if $! == ELOOP || $! == ENOTDIR;
1250             die "cannot open directory $dir: $!\n" if !$mkdir;
1251
1252             # We don't check for errors on mkdirat() here and just try to
1253             # openat() again, since at least one error (EEXIST) is an
1254             # expected possibility if multiple containers start
1255             # simultaneously. If someone else injects a symlink now then
1256             # the subsequent openat() will fail due to O_NOFOLLOW anyway.
1257             PVE::Tools::mkdirat(fileno($fd), $component, 0755);
1258
1259             $next = PVE::Tools::openat(fileno($fd), $component, O_NOFOLLOW | O_DIRECTORY);
1260             die "failed to create path: $dir: $!\n" if !$next;
1261         }
1262
1263         close $second if defined($last_component);
1264         $last_component = $component;
1265         $second = $fd;
1266         $fd = $next;
1267     }
1268
1269     return ($fd, defined($last_component) && $second, $last_component) if wantarray;
1270     close $second if defined($last_component);
1271     return $fd;
1272 }
1273
1274 # To guard against symlink attack races against other currently running
1275 # containers with shared recursive bind mount hierarchies we prepare a
1276 # directory handle for the directory we're mounting over to verify the
1277 # mountpoint afterwards.
1278 sub __bindmount_prepare {
1279     my ($hostroot, $dir) = @_;
1280     my $srcdh = walk_tree_nofollow($hostroot, $dir, 0);
1281     return $srcdh;
1282 }
1283
1284 # Assuming we mount to rootfs/a/b/c, verify with the directory handle to 'b'
1285 # ($parentfd) that 'b/c' (openat($parentfd, 'c')) really leads to the directory
1286 # we intended to bind mount.
1287 sub __bindmount_verify {
1288     my ($srcdh, $parentfd, $last_dir, $ro) = @_;
1289     my $destdh;
1290     if ($parentfd) {
1291         # Open the mount point path coming from the parent directory since the
1292         # filehandle we would have gotten as first result of walk_tree_nofollow
1293         # earlier is still a handle to the underlying directory instead of the
1294         # mounted path.
1295         $destdh = PVE::Tools::openat(fileno($parentfd), $last_dir, PVE::Tools::O_PATH | O_NOFOLLOW | O_DIRECTORY);
1296         die "failed to open mount point: $!\n" if !$destdh;
1297         if ($ro) {
1298             my $dot = '.';
1299             # no separate function because 99% of the time it's the wrong thing to use.
1300             if (syscall(PVE::Syscall::faccessat, fileno($destdh), $dot, &POSIX::W_OK, 0) != -1) {
1301                 die "failed to mark bind mount read only\n";
1302             }
1303             die "read-only check failed: $!\n" if $! != EROFS;
1304         }
1305     } else {
1306         # For the rootfs we don't have a parentfd so we open the path directly.
1307         # Note that this means bindmounting any prefix of the host's
1308         # /var/lib/lxc/$vmid path into another container is considered a grave
1309         # security error.
1310         sysopen $destdh, $last_dir, O_PATH | O_DIRECTORY;
1311         die "failed to open mount point: $!\n" if !$destdh;
1312     }
1313
1314     my ($srcdev, $srcinode) = stat($srcdh);
1315     my ($dstdev, $dstinode) = stat($destdh);
1316     close $srcdh;
1317     close $destdh;
1318
1319     return ($srcdev == $dstdev && $srcinode == $dstinode);
1320 }
1321
1322 # Perform the actual bind mounting:
1323 sub __bindmount_do {
1324     my ($dir, $dest, $ro, @extra_opts) = @_;
1325     PVE::Tools::run_command(['mount', '-o', 'bind', @extra_opts, $dir, $dest]);
1326     if ($ro) {
1327         eval { PVE::Tools::run_command(['mount', '-o', 'bind,remount,ro', $dest]); };
1328         if (my $err = $@) {
1329             warn "bindmount error\n";
1330             # don't leave writable bind-mounts behind...
1331             PVE::Tools::run_command(['umount', $dest]);
1332             die $err;
1333         }
1334     }
1335 }
1336
1337 sub bindmount {
1338     my ($dir, $parentfd, $last_dir, $dest, $ro, @extra_opts) = @_;
1339
1340     my $srcdh = __bindmount_prepare('/', $dir);
1341
1342     __bindmount_do($dir, $dest, $ro, @extra_opts);
1343
1344     if (!__bindmount_verify($srcdh, $parentfd, $last_dir, $ro)) {
1345         PVE::Tools::run_command(['umount', $dest]);
1346         die "detected mount path change at: $dir\n";
1347     }
1348 }
1349
1350 # Cleanup $rootdir a bit (double and trailing slashes), build the mount path
1351 # from $rootdir and $mount and walk the path from $rootdir to the final
1352 # directory to check for symlinks.
1353 sub __mount_prepare_rootdir {
1354     my ($rootdir, $mount) = @_;
1355     $rootdir =~ s!/+!/!g;
1356     $rootdir =~ s!/+$!!;
1357     my $mount_path = "$rootdir/$mount";
1358     my ($mpfd, $parentfd, $last_dir) = walk_tree_nofollow($rootdir, $mount, 1);
1359     return ($rootdir, $mount_path, $mpfd, $parentfd, $last_dir);
1360 }
1361
1362 # use $rootdir = undef to just return the corresponding mount path
1363 sub mountpoint_mount {
1364     my ($mountpoint, $rootdir, $storage_cfg, $snapname) = @_;
1365
1366     my $volid = $mountpoint->{volume};
1367     my $mount = $mountpoint->{mp};
1368     my $type = $mountpoint->{type};
1369     my $quota = !$snapname && !$mountpoint->{ro} && $mountpoint->{quota};
1370     my $mounted_dev;
1371     
1372     return if !$volid || !$mount;
1373
1374     $mount =~ s!/+!/!g;
1375
1376     my $mount_path;
1377     my ($mpfd, $parentfd, $last_dir);
1378     
1379     if (defined($rootdir)) {
1380         ($rootdir, $mount_path, $mpfd, $parentfd, $last_dir) =
1381             __mount_prepare_rootdir($rootdir, $mount);
1382     }
1383     
1384     my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
1385
1386     die "unknown snapshot path for '$volid'" if !$storage && defined($snapname);
1387
1388     my $optstring = '';
1389     my $acl = $mountpoint->{acl};
1390     if (defined($acl)) {
1391         $optstring .= ($acl ? 'acl' : 'noacl');
1392     }
1393     my $readonly = $mountpoint->{ro};
1394
1395     my @extra_opts;
1396     @extra_opts = ('-o', $optstring) if $optstring;
1397
1398     if ($storage) {
1399
1400         my $scfg = PVE::Storage::storage_config($storage_cfg, $storage);
1401
1402         # early sanity checks:
1403         # we otherwise call realpath on the rbd url
1404         die "containers on rbd storage without krbd are not supported\n"
1405             if $scfg->{type} eq 'rbd' && !$scfg->{krbd};
1406
1407         my $path = PVE::Storage::path($storage_cfg, $volid, $snapname);
1408
1409         my ($vtype, undef, undef, undef, undef, $isBase, $format) =
1410             PVE::Storage::parse_volname($storage_cfg, $volid);
1411
1412         $format = 'iso' if $vtype eq 'iso'; # allow to handle iso files
1413
1414         if ($format eq 'subvol') {
1415             if ($mount_path) {
1416                 if ($snapname) {
1417                     if ($scfg->{type} eq 'zfspool') {
1418                         my $path_arg = $path;
1419                         $path_arg =~ s!^/+!!;
1420                         PVE::Tools::run_command(['mount', '-o', 'ro', @extra_opts, '-t', 'zfs', $path_arg, $mount_path]);
1421                     } else {
1422                         die "cannot mount subvol snapshots for storage type '$scfg->{type}'\n";
1423                     }
1424                 } else {
1425                     if (defined($acl) && $scfg->{type} eq 'zfspool') {
1426                         my $acltype = ($acl ? 'acltype=posixacl' : 'acltype=noacl');
1427                         my (undef, $name) = PVE::Storage::parse_volname($storage_cfg, $volid);
1428                         $name .= "\@$snapname" if defined($snapname);
1429                         PVE::Tools::run_command(['zfs', 'set', $acltype, "$scfg->{pool}/$name"]);
1430                     }
1431                     bindmount($path, $parentfd, $last_dir//$rootdir, $mount_path, $readonly, @extra_opts);
1432                     warn "cannot enable quota control for bind mounted subvolumes\n" if $quota;
1433                 }
1434             }
1435             return wantarray ? ($path, 0, undef) : $path;
1436         } elsif ($format eq 'raw' || $format eq 'iso') {
1437             # NOTE: 'mount' performs canonicalization without the '-c' switch, which for
1438             # device-mapper devices is special-cased to use the /dev/mapper symlinks.
1439             # Our autodev hook expects the /dev/dm-* device currently
1440             # and will create the /dev/mapper symlink accordingly
1441             $path = Cwd::realpath($path);
1442             die "failed to get device path\n" if !$path;
1443             ($path) = ($path =~ /^(.*)$/s); #untaint
1444             my $domount = sub {
1445                 my ($path) = @_;
1446                 if ($mount_path) {
1447                     if ($format eq 'iso') {
1448                         PVE::Tools::run_command(['mount', '-o', 'ro', @extra_opts, $path, $mount_path]);
1449                     } elsif ($isBase || defined($snapname)) {
1450                         PVE::Tools::run_command(['mount', '-o', 'ro,noload', @extra_opts, $path, $mount_path]);
1451                     } else {
1452                         if ($quota) {
1453                             push @extra_opts, '-o', 'usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0';
1454                         }
1455                         push @extra_opts, '-o', 'ro' if $readonly;
1456                         PVE::Tools::run_command(['mount', @extra_opts, $path, $mount_path]);
1457                     }
1458                 }
1459             };
1460             my $use_loopdev = 0;
1461             if ($scfg->{path}) {
1462                 $mounted_dev = run_with_loopdev($domount, $path);
1463                 $use_loopdev = 1;
1464             } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'lvm' ||
1465                      $scfg->{type} eq 'rbd' || $scfg->{type} eq 'lvmthin') {
1466                 $mounted_dev = $path;
1467                 &$domount($path);
1468             } else {
1469                 die "unsupported storage type '$scfg->{type}'\n";
1470             }
1471             return wantarray ? ($path, $use_loopdev, $mounted_dev) : $path;
1472         } else {
1473             die "unsupported image format '$format'\n";
1474         }
1475     } elsif ($type eq 'device') {
1476         push @extra_opts, '-o', 'ro' if $readonly;
1477         push @extra_opts, '-o', 'usrjquota=aquota.user,grpjquota=aquota.group,jqfmt=vfsv0' if $quota;
1478         # See the NOTE above about devicemapper canonicalization
1479         my ($devpath) = (Cwd::realpath($volid) =~ /^(.*)$/s); # realpath() taints
1480         PVE::Tools::run_command(['mount', @extra_opts, $volid, $mount_path]) if $mount_path;
1481         return wantarray ? ($volid, 0, $devpath) : $volid;
1482     } elsif ($type eq 'bind') {
1483         die "directory '$volid' does not exist\n" if ! -d $volid;
1484         bindmount($volid, $parentfd, $last_dir//$rootdir, $mount_path, $readonly, @extra_opts) if $mount_path;
1485         warn "cannot enable quota control for bind mounts\n" if $quota;
1486         return wantarray ? ($volid, 0, undef) : $volid;
1487     }
1488     
1489     die "unsupported storage";
1490 }
1491
1492 sub mkfs {
1493     my ($dev, $rootuid, $rootgid) = @_;
1494
1495     PVE::Tools::run_command(['mkfs.ext4', '-O', 'mmp',
1496                              '-E', "root_owner=$rootuid:$rootgid",
1497                              $dev]);
1498 }
1499
1500 sub format_disk {
1501     my ($storage_cfg, $volid, $rootuid, $rootgid) = @_;
1502
1503     if ($volid =~ m!^/dev/.+!) {
1504         mkfs($volid);
1505         return;
1506     }
1507
1508     my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
1509
1510     die "cannot format volume '$volid' with no storage\n" if !$storage;
1511
1512     PVE::Storage::activate_volumes($storage_cfg, [$volid]);
1513
1514     my $path = PVE::Storage::path($storage_cfg, $volid);
1515
1516     my ($vtype, undef, undef, undef, undef, $isBase, $format) =
1517         PVE::Storage::parse_volname($storage_cfg, $volid);
1518
1519     die "cannot format volume '$volid' (format == $format)\n"
1520         if $format ne 'raw';
1521
1522     mkfs($path, $rootuid, $rootgid);
1523 }
1524
1525 sub destroy_disks {
1526     my ($storecfg, $vollist) = @_;
1527
1528     foreach my $volid (@$vollist) {
1529         eval { PVE::Storage::vdisk_free($storecfg, $volid); };
1530         warn $@ if $@;
1531     }
1532 }
1533
1534 sub alloc_disk {
1535     my ($storecfg, $vmid, $storage, $size_kb, $rootuid, $rootgid) = @_;
1536
1537     my $needs_chown = 0;
1538     my $volid;
1539
1540     my $scfg = PVE::Storage::storage_config($storecfg, $storage);
1541     # fixme: use better naming ct-$vmid-disk-X.raw?
1542
1543     eval {
1544         my $do_format = 0;
1545         if ($scfg->{type} eq 'dir' || $scfg->{type} eq 'nfs' || $scfg->{type} eq 'cifs' ) {
1546             if ($size_kb > 0) {
1547                 $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw',
1548                                                    undef, $size_kb);
1549                 $do_format = 1;
1550             } else {
1551                 $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol',
1552                                                    undef, 0);
1553                 $needs_chown = 1;
1554             }
1555         } elsif ($scfg->{type} eq 'zfspool') {
1556
1557             $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'subvol',
1558                                                undef, $size_kb);
1559             $needs_chown = 1;
1560         } elsif ($scfg->{type} eq 'drbd' || $scfg->{type} eq 'lvm' || $scfg->{type} eq 'lvmthin') {
1561
1562             $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
1563             $do_format = 1;
1564
1565         } elsif ($scfg->{type} eq 'rbd') {
1566
1567             die "krbd option must be enabled on storage type '$scfg->{type}'\n" if !$scfg->{krbd};
1568             $volid = PVE::Storage::vdisk_alloc($storecfg, $storage, $vmid, 'raw', undef, $size_kb);
1569             $do_format = 1;
1570         } else {
1571             die "unable to create containers on storage type '$scfg->{type}'\n";
1572         }
1573         format_disk($storecfg, $volid, $rootuid, $rootgid) if $do_format;
1574     };
1575     if (my $err = $@) {
1576         # in case formatting got interrupted:
1577         if (defined($volid)) {
1578             eval { PVE::Storage::vdisk_free($storecfg, $volid); };
1579             warn $@ if $@;
1580         }
1581         die $err;
1582     }
1583
1584     return ($volid, $needs_chown);
1585 }
1586
1587 our $NEW_DISK_RE = qr/^([^:\s]+):(\d+(\.\d+)?)$/;
1588 sub create_disks {
1589     my ($storecfg, $vmid, $settings, $conf) = @_;
1590
1591     my $vollist = [];
1592
1593     eval {
1594         my (undef, $rootuid, $rootgid) = PVE::LXC::parse_id_maps($conf);
1595         my $chown_vollist = [];
1596
1597         PVE::LXC::Config->foreach_mountpoint($settings, sub {
1598             my ($ms, $mountpoint) = @_;
1599
1600             my $volid = $mountpoint->{volume};
1601             my $mp = $mountpoint->{mp};
1602
1603             my ($storage, $volname) = PVE::Storage::parse_volume_id($volid, 1);
1604
1605             if ($storage && ($volid =~ $NEW_DISK_RE)) {
1606                 my ($storeid, $size_gb) = ($1, $2);
1607
1608                 my $size_kb = int(${size_gb}*1024) * 1024;
1609
1610                 my $needs_chown = 0;
1611                 ($volid, $needs_chown) = alloc_disk($storecfg, $vmid, $storage, $size_kb, $rootuid, $rootgid);
1612                 push @$chown_vollist, $volid if $needs_chown;
1613                 push @$vollist, $volid;
1614                 $mountpoint->{volume} = $volid;
1615                 $mountpoint->{size} = $size_kb * 1024;
1616                 $conf->{$ms} = PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
1617             } else {
1618                 # use specified/existing volid/dir/device
1619                 $conf->{$ms} = PVE::LXC::Config->print_ct_mountpoint($mountpoint, $ms eq 'rootfs');
1620             }
1621         });
1622
1623         PVE::Storage::activate_volumes($storecfg, $chown_vollist, undef);
1624         foreach my $volid (@$chown_vollist) {
1625             my $path = PVE::Storage::path($storecfg, $volid, undef);
1626             chown($rootuid, $rootgid, $path);
1627         }
1628         PVE::Storage::deactivate_volumes($storecfg, $chown_vollist, undef);
1629     };
1630     # free allocated images on error
1631     if (my $err = $@) {
1632         destroy_disks($storecfg, $vollist);
1633         die $err;
1634     }
1635     return $vollist;
1636 }
1637
1638 # bash completion helper
1639
1640 sub complete_os_templates {
1641     my ($cmdname, $pname, $cvalue) = @_;
1642
1643     my $cfg = PVE::Storage::config();
1644
1645     my $storeid;
1646
1647     if ($cvalue =~ m/^([^:]+):/) {
1648         $storeid = $1;
1649     }
1650
1651     my $vtype = $cmdname eq 'restore' ? 'backup' : 'vztmpl';
1652     my $data = PVE::Storage::template_list($cfg, $storeid, $vtype);
1653
1654     my $res = [];
1655     foreach my $id (keys %$data) {
1656         foreach my $item (@{$data->{$id}}) {
1657             push @$res, $item->{volid} if defined($item->{volid});
1658         }
1659     }
1660
1661     return $res;
1662 }
1663
1664 my $complete_ctid_full = sub {
1665     my ($running) = @_;
1666
1667     my $idlist = vmstatus();
1668
1669     my $active_hash = list_active_containers();
1670
1671     my $res = [];
1672
1673     foreach my $id (keys %$idlist) {
1674         my $d = $idlist->{$id};
1675         if (defined($running)) {
1676             next if $d->{template};
1677             next if $running && !$active_hash->{$id};
1678             next if !$running && $active_hash->{$id};
1679         }
1680         push @$res, $id;
1681
1682     }
1683     return $res;
1684 };
1685
1686 sub complete_ctid {
1687     return &$complete_ctid_full();
1688 }
1689
1690 sub complete_ctid_stopped {
1691     return &$complete_ctid_full(0);
1692 }
1693
1694 sub complete_ctid_running {
1695     return &$complete_ctid_full(1);
1696 }
1697
1698 sub parse_id_maps {
1699     my ($conf) = @_;
1700
1701     my $id_map = [];
1702     my $rootuid = 0;
1703     my $rootgid = 0;
1704
1705     my $lxc = $conf->{lxc};
1706     foreach my $entry (@$lxc) {
1707         my ($key, $value) = @$entry;
1708         # FIXME: remove the 'id_map' variant when lxc-3.0 arrives
1709         next if $key ne 'lxc.idmap' && $key ne 'lxc.id_map';
1710         if ($value =~ /^([ug])\s+(\d+)\s+(\d+)\s+(\d+)\s*$/) {
1711             my ($type, $ct, $host, $length) = ($1, $2, $3, $4);
1712             push @$id_map, [$type, $ct, $host, $length];
1713             if ($ct == 0) {
1714                 $rootuid = $host if $type eq 'u';
1715                 $rootgid = $host if $type eq 'g';
1716             }
1717         } else {
1718             die "failed to parse idmap: $value\n";
1719         }
1720     }
1721
1722     if (!@$id_map && $conf->{unprivileged}) {
1723         # Should we read them from /etc/subuid?
1724         $id_map = [ ['u', '0', '100000', '65536'],
1725                     ['g', '0', '100000', '65536'] ];
1726         $rootuid = $rootgid = 100000;
1727     }
1728
1729     return ($id_map, $rootuid, $rootgid);
1730 }
1731
1732 sub userns_command {
1733     my ($id_map) = @_;
1734     if (@$id_map) {
1735         return ['lxc-usernsexec', (map { ('-m', join(':', @$_)) } @$id_map), '--'];
1736     }
1737     return [];
1738 }
1739
1740 sub vm_start {
1741     my ($vmid, $conf, $skiplock) = @_;
1742
1743     update_lxc_config($vmid, $conf);
1744
1745     my $skiplock_flag_fn = "/run/lxc/skiplock-$vmid";
1746
1747     if ($skiplock) {
1748         open(my $fh, '>', $skiplock_flag_fn) || die "failed to open $skiplock_flag_fn for writing: $!\n";
1749         close($fh);
1750     }
1751
1752     my $cmd = ['systemctl', 'start', "pve-container\@$vmid"];
1753
1754     eval { PVE::Tools::run_command($cmd); };
1755     if (my $err = $@) {
1756         unlink $skiplock_flag_fn;
1757         die $err;
1758     }
1759
1760     return;
1761 }
1762
1763 # Helper to stop a container completely and make sure it has stopped completely.
1764 # This is necessary because we want the post-stop hook to have completed its
1765 # unmount-all step, but post-stop happens after lxc puts the container into the
1766 # STOPPED state.
1767 sub vm_stop {
1768     my ($vmid, $kill, $shutdown_timeout, $exit_timeout) = @_;
1769
1770     # Open the container's command socket.
1771     my $path = "\0/var/lib/lxc/$vmid/command";
1772     my $sock = IO::Socket::UNIX->new(
1773         Type => SOCK_STREAM(),
1774         Peer => $path,
1775     );
1776     if (!$sock) {
1777         return if $! == ECONNREFUSED; # The container is not running
1778         die "failed to open container ${vmid}'s command socket: $!\n";
1779     }
1780
1781     # Stop the container:
1782
1783     my $cmd = ['lxc-stop', '-n', $vmid];
1784
1785     if ($kill) {
1786         push @$cmd, '--kill'; # doesn't allow timeouts
1787     } elsif (defined($shutdown_timeout)) {
1788         push @$cmd, '--timeout', $shutdown_timeout;
1789         # Give run_command 5 extra seconds
1790         $shutdown_timeout += 5;
1791     }
1792
1793     eval { PVE::Tools::run_command($cmd, timeout => $shutdown_timeout) };
1794     if (my $err = $@) {
1795         warn $@ if $@;
1796     }
1797
1798     my $result = 1;
1799     my $wait = sub { $result = <$sock>; };
1800     if (defined($exit_timeout)) {
1801         PVE::Tools::run_with_timeout($exit_timeout, $wait);
1802     } else {
1803         $wait->();
1804     }
1805
1806     return if !defined $result; # monitor is gone and the ct has stopped.
1807     die "container did not stop\n";
1808 }
1809
1810 sub run_unshared {
1811     my ($code) = @_;
1812
1813     return PVE::Tools::run_fork(sub {
1814         # Unshare the mount namespace
1815         die "failed to unshare mount namespace: $!\n"
1816             if !PVE::Tools::unshare(PVE::Tools::CLONE_NEWNS);
1817         PVE::Tools::run_command(['mount', '--make-rslave', '/']);
1818         return $code->();
1819     });
1820 }
1821
1822 my $copy_volume = sub {
1823     my ($src_volid, $src, $dst_volid, $dest, $storage_cfg, $snapname) = @_;
1824
1825     my $src_mp = { volume => $src_volid, mp => '/' };
1826     $src_mp->{type} = PVE::LXC::Config->classify_mountpoint($src_volid);
1827
1828     my $dst_mp = { volume => $dst_volid, mp => '/' };
1829     $dst_mp->{type} = PVE::LXC::Config->classify_mountpoint($dst_volid);
1830
1831     my @mounted;
1832     eval {
1833         # mount and copy
1834         mkdir $src;
1835         mountpoint_mount($src_mp, $src, $storage_cfg, $snapname);
1836         push @mounted, $src;
1837         mkdir $dest;
1838         mountpoint_mount($dst_mp, $dest, $storage_cfg);
1839         push @mounted, $dest;
1840
1841         PVE::Tools::run_command(['/usr/bin/rsync', '--stats', '-X', '-A', '--numeric-ids',
1842                                  '-aH', '--whole-file', '--sparse', '--one-file-system',
1843                                  "$src/", $dest]);
1844     };
1845     my $err = $@;
1846     foreach my $mount (reverse @mounted) {
1847         eval { PVE::Tools::run_command(['/bin/umount', '--lazy', $mount], errfunc => sub{})};
1848         warn "Can't umount $mount\n" if $@;
1849     }
1850
1851     # If this fails they're used as mount points in a concurrent operation
1852     # (which should not happen but there's also no real need to get rid of them).
1853     rmdir $dest;
1854     rmdir $src;
1855
1856     die $err if $err;
1857 };
1858
1859 # Should not be called after unsharing the mount namespace!
1860 sub copy_volume {
1861     my ($mp, $vmid, $storage, $storage_cfg, $conf, $snapname) = @_;
1862
1863     die "cannot copy volumes of type $mp->{type}\n" if $mp->{type} ne 'volume';
1864     File::Path::make_path("/var/lib/lxc/$vmid");
1865     my $dest = "/var/lib/lxc/$vmid/.copy-volume-1";
1866     my $src  = "/var/lib/lxc/$vmid/.copy-volume-2";
1867
1868     # get id's for unprivileged container
1869     my (undef, $rootuid, $rootgid) = parse_id_maps($conf);
1870
1871     # Allocate the disk before unsharing in order to make sure zfs subvolumes
1872     # are visible in this namespace, otherwise the host only sees the empty
1873     # (not-mounted) directory.
1874     my $new_volid;
1875     eval {
1876         # Make sure $mp contains a correct size.
1877         $mp->{size} = PVE::Storage::volume_size_info($storage_cfg, $mp->{volume});
1878         my $needs_chown;
1879         ($new_volid, $needs_chown) = alloc_disk($storage_cfg, $vmid, $storage, $mp->{size}/1024, $rootuid, $rootgid);
1880         if ($needs_chown) {
1881             PVE::Storage::activate_volumes($storage_cfg, [$new_volid], undef);
1882             my $path = PVE::Storage::path($storage_cfg, $new_volid, undef);
1883             chown($rootuid, $rootgid, $path);
1884         }
1885
1886         run_unshared(sub {
1887             $copy_volume->($mp->{volume}, $src, $new_volid, $dest, $storage_cfg, $snapname);
1888         });
1889     };
1890     if (my $err = $@) {
1891         PVE::Storage::vdisk_free($storage_cfg, $new_volid)
1892             if defined($new_volid);
1893         die $err;
1894     }
1895
1896     return $new_volid;
1897 }
1898
1899 1;