From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from gate001.proxmox.com (gate001.proxmox.com [IPv6:2a0f:8001:1:32::40]) by lore.proxmox.com (Postfix) with ESMTPS id E38CA1FF138 for ; Mon, 20 Jul 2026 13:29:42 +0200 (CEST) Received: from gate001.proxmox.com (localhost.localdomain [127.0.0.1]) by gate001.proxmox.com (Proxmox) with ESMTP id 203C22156A; Mon, 20 Jul 2026 13:29:12 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1784390785; x=1784995585; darn=lists.proxmox.com; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=jVtUdeVIJRQnzxqgrU9n8db7myeeS4L2G8LtKiTNA9k=; b=mOE91Rh6g6QT1TsUFhLn0wKBAD0duHA6GJUxnIsMaf9fMCyflaBhFK/qYOTIQ7hPMU MNJPdJdSYbZpU6siugXeXq9oPVtxWnIhnFBNUYYDpLvXI4Jjdo2Rd1MEgnHN0GG0UVXH bd8CWyHNsma6XmVZ4d/4fZcBooGwataDw3LQoeA2r49b4irgSCbM5B5iShQWSZn9gAZ5 YJsP0VHBKgGttJediXfO+ZERupb/cO/IrDjXYN8DUgqe+kN03d2dGMFlG5HQSHZqVNU7 MQHEpMpAUdYid6zYXNBS1dmwpSWF/KXu6j4tgO3FESACg20xUjDbMNHQ9Q5Wa0buRciT Go3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1784390785; x=1784995585; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=jVtUdeVIJRQnzxqgrU9n8db7myeeS4L2G8LtKiTNA9k=; b=RoBsfYkjcOHGqHghGehmLn/OQXd/Ps/dwJVNnsf+E7C9X3ry1/QhgxcRGnAKVpr3gF gsbIAcAP/HsypOQEzTX8DSl0ERZSxOly2FkPgEvUGIL1Ke2MCxg1TDB9HPaqAuCD8ct7 CY/6C+zcmvtnEOOezhhFbbKysDAf55CdtRnW7/J6RRmijbaEBd8IL3Gowz4R/yY/2Uiv lpowqKwr6R08XGV7Wxdu5MypWqN5Cqy9Y3m1jRXMMPrhc0cA48qXnWmnZ8ErGmWzIvNo b3jC5kDguaTxetOdSi8LFsH2JOAvNc327wkhEOwXRbPKwZBzvIKeAhnZkOKY3mdxVFXC c5Cw== X-Gm-Message-State: AOJu0Yw02M44rW4D9eVf6He3fIU1gLWQ8XTwYGV+q4oP83woIcRMK+VS XiYZTw4I09BrFhxrmmCBY0n77wi/tt6lT/J+/oU7j7eNOuGu9s2I+ET/SBemYw== X-Gm-Gg: AfdE7cnUZEK8VS2shDXUICikk4ywd2bGOmnMXf79B30X0Wo1MAGEQ2qYZZ8t9L0k7fj Cn1Unhya3pvAdFyQuya8Jz6qqzt+hKv+dxYTE6CgV6w3CF+Bq1+b7t9bARTXd4V6AZ0PY8lZGBb ZzAkFCHgtalqf1c+X2Ipo5ZqCktINlBdFWNC+Ebb5JR/u1XaGXs/9DIoMERzv+haxYFBQAaALHV i6hpNgVpP1VgrN1nFkOHX19GmfLgWdPi6jyFMSDSNUkGfkkYD6/WtrWi7n9Dw99z5ML+aSN38Rm R/5r2weJMOVbw3l5Uk9u+FBzfcbkh7EdWJpk4XVwn6lhJkGrcyhlxWBCLk01ucI5GthiTtjYfmR kw8qI+DzB/y3bLjq2K5MQq7/O7TFNFaJ7B8CTqKuybZ9MoZBywdw+30xGS6iSFCWqp/b5nE645J 1vHva+fpkb8abU1w== X-Received: by 2002:a05:6000:4201:b0:474:e0f2:905c with SMTP id ffacd0b85a97d-47f622ff8camr8126493f8f.26.1784390785139; Sat, 18 Jul 2026 09:06:25 -0700 (PDT) From: copystring To: pve-devel@lists.proxmox.com subject: SPAM: [RFC PATCH v2 1/4] lxc: create: add isolated OCI rootfs preparation Date: Sat, 18 Jul 2026 18:05:59 +0200 Message-ID: <8c46d715887f1e6913a678a38a4c3b45423a3320.1784389742.git.copystring@gmail.com> X-Mailer: git-send-email 2.47.3 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 3 AWL 0.230 Adjusted score from AWL reputation of From: address DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_AU -0.1 Message has a valid DKIM or DK signature from author's domain DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_PASS -0.1 DMARC pass policy FREEMAIL_FROM 0.001 Sender email is commonly abused enduser mail provider RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust RCVD_IN_SBL_CSS 3.558 Received via a relay in Spamhaus SBL-CSS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record X-MailFrom: copystring@gmail.com X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation Message-ID-Hash: WU2ZXAFOKLMQQTUBOFHFZBOA66RAYFKU X-Message-ID-Hash: WU2ZXAFOKLMQQTUBOFHFZBOA66RAYFKU X-Mailman-Approved-At: Mon, 20 Jul 2026 13:29:01 +0200 CC: copystring X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox VE development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: Prepare a new rootfs volume from an OCI archive without mounting any existing mpX mount points. This keeps the active container config untouched until the API layer switches rootfs under config lock. Keep the allocation and temporary mount/populate flow generic: create the new rootfs volume, mount only that volume in a private mount namespace, populate it through a callback, and free the newly allocated volume on prepare or cleanup failure. Signed-off-by: copystring --- src/PVE/LXC/Create.pm | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/src/PVE/LXC/Create.pm b/src/PVE/LXC/Create.pm index 69065b9..9fc01a8 100644 --- a/src/PVE/LXC/Create.pm +++ b/src/PVE/LXC/Create.pm @@ -783,6 +783,121 @@ sub restore_oci_archive { return $conf; # it's a reference anyway, so return mostly for convenience. } +my sub volume_size_to_gb { + my ($size) = @_; + + my $size_mib = int(($size + 1024 * 1024 - 1) / (1024 * 1024)); + my $size_gb = sprintf('%.6f', $size_mib / 1024); + $size_gb =~ s/0+$//; + $size_gb =~ s/\.$//; + + return $size_gb; +} + +my sub idmap_lxc_config { + my ($conf) = @_; + + return [map { [@$_] } grep { $_->[0] eq 'lxc.idmap' } @{ $conf->{lxc} // [] }]; +} + +my sub create_rootfs_volume { + my ($storage_cfg, $vmid, $storage, $size, $conf, $rootdir_base, $rootdir_name, $populate) = + @_; + + my $size_gb = volume_size_to_gb($size); + my $rootfs = PVE::LXC::Config->parse_volume('rootfs', $conf->{rootfs}); + my $new_rootfs_config = { + %$rootfs, + volume => "$storage:$size_gb", + }; + delete $new_rootfs_config->{type}; + delete $new_rootfs_config->{size}; + + my $settings = { + rootfs => PVE::LXC::Config->print_ct_mountpoint($new_rootfs_config, 1), + }; + my $new_conf = { + lxc => idmap_lxc_config($conf), + }; + $new_conf->{unprivileged} = $conf->{unprivileged} if exists($conf->{unprivileged}); + + $rootdir_base //= "/var/lib/lxc/$vmid"; + make_path($rootdir_base); + my $rootdir = "$rootdir_base/$rootdir_name"; + + my $vollist = PVE::LXC::create_disks($storage_cfg, $vmid, $settings, $new_conf); + my $new_rootfs = PVE::LXC::Config->parse_volume('rootfs', $new_conf->{rootfs}); + my $new_volid = $new_rootfs->{volume}; + + eval { + my (undef, $root_uid, $root_gid) = PVE::LXC::parse_id_maps($conf); + + $new_conf = PVE::LXC::run_unshared(sub { + die "temporary rootfs path '$rootdir' already exists\n" if -e $rootdir; + + my @mounted; + eval { + mkdir $rootdir + or die "failed to create temporary rootfs path '$rootdir': $!\n"; + + my $mountpoint = { %$new_rootfs, mp => '/', ro => 0 }; + PVE::LXC::mountpoint_mount( + $mountpoint, $rootdir, $storage_cfg, undef, $root_uid, $root_gid, + ); + push @mounted, $rootdir; + + $populate->($rootdir, $new_conf); + }; + my $err = $@; + my $cleanup_err = ''; + + foreach my $mount (reverse @mounted) { + eval { + PVE::Tools::run_command(['/bin/umount', $mount], errfunc => sub { }); + }; + $cleanup_err .= "can't unmount temporary rootfs path '$mount': $@\n" if $@; + } + + $cleanup_err .= "failed to remove temporary rootfs path '$rootdir': $!\n" + if -d $rootdir && !rmdir $rootdir; + + die $err if $err; + die $cleanup_err if $cleanup_err; + + return $new_conf; + }); + }; + if (my $err = $@) { + PVE::LXC::destroy_disks($storage_cfg, $vollist); + die $err; + } + + return ($new_conf->{rootfs}, $new_volid, $new_conf); +} + +sub create_oci_rootfs { + my ($storage_cfg, $vmid, $archive, $storage, $size, $conf, $rootdir_base) = @_; + + die "archive '$archive' is not an OCI image archive\n" + if !archive_is_oci_format($storage_cfg, $archive); + + return create_rootfs_volume( + $storage_cfg, + $vmid, + $storage, + $size, + $conf, + $rootdir_base, + '.oci-replace-rootfs', + sub { + my ($rootdir, $new_conf) = @_; + + restore_oci_archive($storage_cfg, $archive, $rootdir, $new_conf); + PVE::LXC::Setup->new($new_conf, $rootdir); # detect OS and architecture + }, + ); +} + sub resolve_oci_user { my ($userstr, $rootdir) = @_; -- 2.55.0.windows.3