Compare commits

...

37 Commits

Author SHA1 Message Date
Wolfgang Bumiller
02acde02b6 make zfs tests declarative
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-08-07 16:49:04 +02:00
Wolfgang Bumiller
0f7a4d2d84 make tidy
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-08-07 16:24:08 +02:00
Stelios Vailakakis
6bf171ec54 iscsi: add hostname support in portal addresses
Currently, the iSCSI plugin regex patterns only match IPv4 and IPv6
addresses, causing session parsing to fail when portals use hostnames
(like nas.example.com:3260).

This patch updates ISCSI_TARGET_RE and session parsing regex to accept
any non-whitespace characters before the port, allowing hostname-based
portals to work correctly.

Tested with IP and hostname-based portals on Proxmox VE 8.2, 8.3, and 8.4.1

Signed-off-by: Stelios Vailakakis <stelios@libvirt.dev>
Link: https://lore.proxmox.com/20250626022920.1323623-1-stelios@libvirt.dev
2025-08-04 20:41:09 +02:00
Stelios Vailakakis
c33abdf062 fix #6073: esxi: fix zombie process after storage removal
After removing an ESXi storage, a zombie process is generated because
the forked FUSE process (esxi-folder-fuse) is not properly reaped.

This patch implements a double-fork mechanism to ensure the FUSE process
is reparented to init (PID 1), which will properly reap it when it
exits. Additionally adds the missing waitpid() call to reap the
intermediate child process.

Tested on Proxmox VE 8.4.1 with ESXi 8.0U3e storage.

Signed-off-by: Stelios Vailakakis <stelios@libvirt.dev>
Link: https://lore.proxmox.com/20250701154135.2387872-1-stelios@libvirt.dev
2025-08-04 20:36:38 +02:00
Thomas Lamprecht
609752f3ae bump version to 9.0.13
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-08-01 18:36:56 +02:00
Fiona Ebner
5750596f5b deactivate volumes: terminate error message with newline
Avoid that Perl auto-attaches the line number and file name.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250801081649.13882-1-f.ebner@proxmox.com
2025-08-01 13:22:45 +02:00
Thomas Lamprecht
153f7d8f85 bump version to 9.0.12
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-07-31 14:22:16 +02:00
Friedrich Weber
3c209eaeb7 plugin: nfs, cifs: use volume qemu snapshot methods from dir plugin
Taking an offline snapshot of a VM on an NFS/CIFS storage with
snapshot-as-volume-chain currently creates a volume-chain snapshot as
expected, but taking an online snapshot unexpectedly creates a qcow2
snapshot. This was also reported in the forum [1].

The reason is that the NFS/CIFS plugins inherit the method
volume_qemu_snapshot_method from the Plugin base class, whereas they
actually behave similarly to the Directory plugin. To fix this,
implement the method for the NFS/CIFS plugins and let it call the
Directory plugin's implementation.

[1] https://forum.proxmox.com/threads/168619/post-787374

Signed-off-by: Friedrich Weber <f.weber@proxmox.com>
Link: https://lore.proxmox.com/20250731082538.31891-1-f.weber@proxmox.com
2025-07-31 14:19:13 +02:00
Thomas Lamprecht
81261f9ca1 re-tidy perl code
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-07-31 14:16:25 +02:00
Fabian Grünbichler
7513e21d74 plugin: parse_name_dir: drop deprecation warning
this gets printed very often if such a volume exists - e.g. adding such a
volume to a config with `qm set` prints it 10 times..

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Tested-by: Shannon Sterz <s.sterz@proxmox.com>
Link: https://lore.proxmox.com/20250731111519.931104-5-f.gruenbichler@proxmox.com
2025-07-31 14:15:54 +02:00
Fabian Grünbichler
6dbeba59da plugin: extend snapshot name parsing to legacy volnames
otherwise a volume like `100/oldstyle-100-disk-0.qcow2` can be snapshotted, but
the snapshot file is treated as a volume instead of a snapshot afterwards.

this also avoids issues with volnames with `vm-` in their names, similar to the
LVM fix for underscores.

Co-authored-by: Shannon Sterz <s.sterz@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Tested-by: Shannon Sterz <s.sterz@proxmox.com>
Link: https://lore.proxmox.com/20250731111519.931104-4-f.gruenbichler@proxmox.com
2025-07-31 14:15:54 +02:00
Fabian Grünbichler
59a54b3d5f fix #6584: plugin: list_images: only include parseable filenames
by only including filenames that are also valid when actually parsing them,
things like snapshot files or files not following our naming scheme are no
longer candidates for rescanning or included in other output.

Co-authored-by: Shannon Sterz <s.sterz@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Tested-by: Shannon Sterz <s.sterz@proxmox.com>
Link: https://lore.proxmox.com/20250731111519.931104-3-f.gruenbichler@proxmox.com
2025-07-31 14:15:54 +02:00
Fabian Grünbichler
a477189575 plugin: fix parse_name_dir regression for custom volume names
prior to the introduction of snapshot as volume chains, volume names of
almost arbitrary form were accepted. only forbid filenames which are
part of the newly introduced namespace for snapshot files, while
deprecating other names not following our usual naming scheme, instead
of forbidding them outright.

Fixes: b63147f5df "plugin: fix volname parsing"

Co-authored-by: Shannon Sterz <s.sterz@proxmox.com>
Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Tested-by: Shannon Sterz <s.sterz@proxmox.com>
Link: https://lore.proxmox.com/20250731111519.931104-2-f.gruenbichler@proxmox.com
2025-07-31 14:15:54 +02:00
Thomas Lamprecht
94a54793cd bump version to 9.0.11
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-07-31 09:19:03 +02:00
Friedrich Weber
92efe5c6cb plugin: lvm: volume snapshot info: untaint snapshot filename
Without untainting, offline-deleting a volume-chain snapshot on an LVM
storage via the GUI can fail with an "Insecure dependecy in exec
[...]" error, because volume_snapshot_delete uses the filename its
qemu-img invocation.

Commit 93f0dfb ("plugin: volume snapshot info: untaint snapshot
filename") fixed this already for the volume_snapshot_info
implementation of the Plugin base class, but missed that the LVM
plugin overrides the method and was still missing the untaint.

Signed-off-by: Friedrich Weber <f.weber@proxmox.com>
Link: https://lore.proxmox.com/20250731071306.11777-1-f.weber@proxmox.com
2025-07-31 09:18:33 +02:00
Thomas Lamprecht
74b5031c9a bump version to 9.0.10
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-07-31 04:14:23 +02:00
Aaron Lauterer
0dc6c9d39c status: rrddata: use new pve-storage-9.0 rrd location if file is present
Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>
Link: https://lore.proxmox.com/20250726010626.1496866-26-a.lauterer@proxmox.com
2025-07-31 04:13:27 +02:00
Thomas Lamprecht
868de9b1a8 bump version to 9.0.9
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2025-07-30 19:51:11 +02:00
Fiona Ebner
e502404fa2 config: drop 'maxfiles' parameter
The 'maxfiles' parameter has been deprecated since the addition of
'prune-backups' in the Proxmox VE 7 beta.

The setting was auto-converted when reading the storage
configuration.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250718125408.133376-2-f.ebner@proxmox.com
2025-07-30 19:35:50 +02:00
Fiona Ebner
fc633887dc lvm plugin: volume snapshot: actually print error when renaming
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Max R. Carrara <m.carrara@proxmox.com>
Reviewed-by: Max R. Carrara <m.carrara@proxmox.com>
Link: https://lore.proxmox.com/20250730162117.160498-4-f.ebner@proxmox.com
2025-07-30 19:32:40 +02:00
Fiona Ebner
db2025f5ba fix #6587: lvm plugin: snapshot info: fix parsing snapshot name
Volume names are allowed to contain underscores, so it is impossible
to determine the snapshot name from just the volume name, e.g:
snap_vm-100-disk_with_underscore_here_s_some_more.qcow2

Therefore, pass along the short volume name too and match against it.

Note that none of the variables from the result of parse_volname()
were actually used previously.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Max R. Carrara <m.carrara@proxmox.com>
Reviewed-by: Max R. Carrara <m.carrara@proxmox.com>
Link: https://lore.proxmox.com/20250730162117.160498-3-f.ebner@proxmox.com
2025-07-30 19:32:40 +02:00
Fiona Ebner
819dafe516 lvm plugin: snapshot info: avoid superfluous argument for closure
The $volname variable is never modified in the function, so it doesn't
need to be passed into the $get_snapname_from_path closure.

Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Max R. Carrara <m.carrara@proxmox.com>
Reviewed-by: Max R. Carrara <m.carrara@proxmox.com>
Link: https://lore.proxmox.com/20250730162117.160498-2-f.ebner@proxmox.com
2025-07-30 19:32:40 +02:00
Fiona Ebner
169f8091dd test: add tests for volume access checks
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250730130506.96278-1-f.ebner@proxmox.com
2025-07-30 18:42:52 +02:00
Maximiliano Sandoval
5245e044ad fix #5181: pbs: store and read passwords as unicode
At the moment calling
```
pvesm add pbs test --password="bär12345" --datastore='test' # ..other params
```

Will result in the API handler getting the param->{passowrd} as a utf-8
encoded string. When dumped with Debug::Peek's Dump() one can see:

```
SV = PV(0x5a02c1a3ff10) at 0x5a02bd713670
  REFCNT = 1
  FLAGS = (POK,IsCOW,pPOK,UTF8)
  PV = 0x5a02c1a409b0 "b\xC3\xA4r12345"\0 [UTF8 "b\x{e4}r12345"]
  CUR = 9
  LEN = 11
  COW_REFCNT = 0
```

Then when writing the file via file_set_contents (using syswrite
internally) will result in perl encoding the password as latin1 and a
file with contents:

```
$ hexdump -C /etc/pve/priv/storage/test.pw
00000000  62 e4 72 31 32 33 34 35                           |b.r12345|
00000008
```

when the correct contents should have been:
```
00000000  62 c3 a4 72 31 32 33 34  35                       |b..r12345|
00000009
```

Later when the file is read via file_read_firstline it will result in

```
SV = PV(0x5e8baa411090) at 0x5e8baa5a96b8
  REFCNT = 1
  FLAGS = (POK,pPOK)
  PV = 0x5e8baa43ee20 "b\xE4r12345"\0
  CUR = 8
  LEN = 81
```

which is a different string than the original.

At the moment, adding the storage will work as the utf8 password is
still in memory, however, however subsequent uses (e.g. pvestatd) will
fail.

This patch fixes the issue by encoding the string as utf8 both when
reading and storing it to disk. The user was able in the past to go
around the issue by writing the right password in
/etc/pve/priv/{storage}.pw and this fix is compatible with that.

It is documented at
https://pbs.proxmox.com/docs/backup-client.html#environment-variables
that the Backup Server password must be valid utf-8.

Signed-off-by: Maximiliano Sandoval <m.sandoval@proxmox.com>
Link: https://lore.proxmox.com/20250730072239.24928-1-m.sandoval@proxmox.com
2025-07-30 11:55:18 +02:00
Fiona Ebner
cafbdb8c52 bump version to 9.0.8
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-07-29 17:28:23 +02:00
Wolfgang Bumiller
172c71a64d common: use v5.36
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-07-29 16:42:49 +02:00
Wolfgang Bumiller
1afe55b35b escape dirs in path_to_volume_id regexes
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-07-29 16:42:49 +02:00
Wolfgang Bumiller
dfad07158d drop rootdir case in path_to_volume_id
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-07-29 16:42:49 +02:00
Wolfgang Bumiller
715ec4f95b parse_volname: remove openvz 'rootdir' case
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-07-29 16:42:49 +02:00
Wolfgang Bumiller
f62fc773ad tests: drop rootdir/ tests
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
[FE: use 'images' rather than not-yet-existing 'ct-vol' for now
     disable seen vtype tests for now]
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-07-29 16:42:18 +02:00
Wolfgang Bumiller
9b7fa1e758 btrfs: remove unnecessary mkpath call
The existence of the original volume should imply the existence of its
parent directory, after all... And with the new typed subdirectories
this was wrong.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
2025-07-29 15:52:00 +02:00
Shannon Sterz
a9315a0ed3 fix #6561: zfspool: track refquota for subvolumes via user properties
ZFS itself does not track the refquota per snapshot, so this needs to
be handled by Proxmox VE. Otherwise, rolling back a volume that has
been resized since the snapshot was taken, will retain the new size.
This is problematic, as it means the value in the guest config does
not match the size of the disk on the storage anymore.

This implementation does so by leveraging a user property per
snapshot.

Reported-by: Lukas Wagner <l.wagner@proxmox.com>
Suggested-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Signed-off-by: Shannon Sterz <s.sterz@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250729121151.159797-1-s.sterz@proxmox.com
[FE: improve capitalization and wording in commit message]
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-07-29 15:16:03 +02:00
Fabian Grünbichler
d0239ba9c0 lvm plugin: use relative path for qcow2 rebase command
otherwise the resulting qcow2 file will contain an absolute path, which makes
renaming the backing VG of the storage impossible.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250729115320.579286-5-f.gruenbichler@proxmox.com
2025-07-29 14:43:07 +02:00
Fabian Grünbichler
7da44f56e4 plugin: use relative path for qcow2 rebase command
otherwise the resulting qcow2 file will contain an absolute path, which makes
changing the backing path of the directory storage impossible.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Tested-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250729115320.579286-4-f.gruenbichler@proxmox.com
2025-07-29 14:43:07 +02:00
Fabian Grünbichler
191cddac30 lvm plugin: fix typo in rebase log message
this was copied over from Plugin.pm

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250729115320.579286-3-f.gruenbichler@proxmox.com
[FE: use string concatenation rather than multi-argument print]
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-07-29 14:43:01 +02:00
Fabian Grünbichler
a7afad969d plugin: fix typo in rebase log message
by directly printing the to-be-executed command, instead of copying it which is
error-prone.

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
Reviewed-by: Fiona Ebner <f.ebner@proxmox.com>
Link: https://lore.proxmox.com/20250729115320.579286-2-f.gruenbichler@proxmox.com
[FE: use string concatenation rather than multi-argument print]
Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
2025-07-29 14:41:48 +02:00
Friedrich Weber
93f0dfbc75 plugin: volume snapshot info: untaint snapshot filename
Without untainting, offline-deleting a volume-chain snapshot on a
directory storage via the GUI fails with an "Insecure dependecy in
exec [...]" error, because volume_snapshot_delete uses the filename
its qemu-img invocation.

Signed-off-by: Friedrich Weber <f.weber@proxmox.com>
2025-07-28 15:10:49 +02:00
21 changed files with 1047 additions and 2606 deletions

69
debian/changelog vendored
View File

@ -1,3 +1,72 @@
libpve-storage-perl (9.0.13) trixie; urgency=medium
* deactivate volumes: terminate error message with newline.
-- Proxmox Support Team <support@proxmox.com> Fri, 01 Aug 2025 18:36:51 +0200
libpve-storage-perl (9.0.12) trixie; urgency=medium
* plugin: fix parse_name_dir regression for custom volume names.
* fix #6584: plugin: list_images: only include parseable filenames.
* plugin: extend snapshot name parsing to legacy volnames.
* plugin: parse_name_dir: drop noisy deprecation warning.
* plugin: nfs, cifs: use volume qemu snapshot methods from dir plugin to
ensure a online-snapshot on such storage types with
snapshot-as-volume-chain enabled does not takes a internal qcow2 snapshot.
-- Proxmox Support Team <support@proxmox.com> Thu, 31 Jul 2025 14:22:12 +0200
libpve-storage-perl (9.0.11) trixie; urgency=medium
* lvm volume snapshot info: untaint snapshot filename
-- Proxmox Support Team <support@proxmox.com> Thu, 31 Jul 2025 09:18:56 +0200
libpve-storage-perl (9.0.10) trixie; urgency=medium
* RRD metrics: use new pve-storage-9.0 format RRD file location, if it
exists.
-- Proxmox Support Team <support@proxmox.com> Thu, 31 Jul 2025 04:14:19 +0200
libpve-storage-perl (9.0.9) trixie; urgency=medium
* fix #5181: pbs: store and read passwords as unicode.
* fix #6587: lvm plugin: snapshot info: fix parsing snapshot name.
* config: drop 'maxfiles' parameter, it was replaced with the more flexible
prune options in Proxmox VE 7.0 already.
-- Proxmox Support Team <support@proxmox.com> Wed, 30 Jul 2025 19:51:07 +0200
libpve-storage-perl (9.0.8) trixie; urgency=medium
* snapshot-as-volume-chain: fix offline removal of snapshot on directory
storage via UI/API by untainting/validating a filename correctly.
* snapshot-as-volume-chain: fix typo in log message for rebase operation.
* snapshot-as-volume-chain: ensure backing file references are kept relative
upon snapshot deletion. This ensures the backing chain stays intact should
the volumes be moved to a different path.
* fix #6561: ZFS: ensure refquota for container volumes is correctly applied
after rollback. The quota is tracked via a ZFS user property.
* btrfs plugin: remove unnecessary mkpath call
* drop some left-overs for 'rootdir' sub-directory handling that were
left-over from when Proxmox VE supported OpenVZ.
* path to volume ID conversion: properly quote regexes for hardening.
-- Proxmox Support Team <support@proxmox.com> Tue, 29 Jul 2025 17:17:11 +0200
libpve-storage-perl (9.0.7) trixie; urgency=medium
* fix #6553: lvmthin: implement volume_rollback_is_possible sub

View File

@ -415,11 +415,10 @@ __PACKAGE__->register_method({
code => sub {
my ($param) = @_;
return PVE::RRD::create_rrd_data(
"pve2-storage/$param->{node}/$param->{storage}",
$param->{timeframe},
$param->{cf},
);
my $path = "pve-storage-9.0/$param->{node}/$param->{storage}";
$path = "pve2-storage/$param->{node}/$param->{storage}"
if !-e "/var/lib/rrdcached/db/${path}";
return PVE::RRD::create_rrd_data($path, $param->{timeframe}, $param->{cf});
},
});

View File

@ -249,27 +249,6 @@ sub lock_storage_config {
}
}
# FIXME remove maxfiles for PVE 8.0 or PVE 9.0
my $convert_maxfiles_to_prune_backups = sub {
my ($scfg) = @_;
return if !$scfg;
my $maxfiles = delete $scfg->{maxfiles};
if (!defined($scfg->{'prune-backups'}) && defined($maxfiles)) {
my $prune_backups;
if ($maxfiles) {
$prune_backups = { 'keep-last' => $maxfiles };
} else { # maxfiles 0 means no limit
$prune_backups = { 'keep-all' => 1 };
}
$scfg->{'prune-backups'} = PVE::JSONSchema::print_property_string(
$prune_backups, 'prune-backups',
);
}
};
sub storage_config {
my ($cfg, $storeid, $noerr) = @_;
@ -279,8 +258,6 @@ sub storage_config {
die "storage '$storeid' does not exist\n" if (!$noerr && !$scfg);
$convert_maxfiles_to_prune_backups->($scfg);
return $scfg;
}
@ -740,11 +717,10 @@ sub path_to_volume_id {
my $isodir = $plugin->get_subdir($scfg, 'iso');
my $tmpldir = $plugin->get_subdir($scfg, 'vztmpl');
my $backupdir = $plugin->get_subdir($scfg, 'backup');
my $privatedir = $plugin->get_subdir($scfg, 'rootdir');
my $snippetsdir = $plugin->get_subdir($scfg, 'snippets');
my $importdir = $plugin->get_subdir($scfg, 'import');
if ($path =~ m!^$imagedir/(\d+)/([^/\s]+)$!) {
if ($path =~ m!^\Q$imagedir\E/(\d+)/([^/\s]+)$!) {
my $vmid = $1;
my $name = $2;
@ -756,22 +732,19 @@ sub path_to_volume_id {
return ('images', $info->{volid});
}
}
} elsif ($path =~ m!^$isodir/([^/]+$ISO_EXT_RE_0)$!) {
} elsif ($path =~ m!^\Q$isodir\E/([^/]+$ISO_EXT_RE_0)$!) {
my $name = $1;
return ('iso', "$sid:iso/$name");
} elsif ($path =~ m!^$tmpldir/([^/]+$VZTMPL_EXT_RE_1)$!) {
} elsif ($path =~ m!^\Q$tmpldir\E/([^/]+$VZTMPL_EXT_RE_1)$!) {
my $name = $1;
return ('vztmpl', "$sid:vztmpl/$name");
} elsif ($path =~ m!^$privatedir/(\d+)$!) {
my $vmid = $1;
return ('rootdir', "$sid:rootdir/$vmid");
} elsif ($path =~ m!^$backupdir/([^/]+$BACKUP_EXT_RE_2)$!) {
} elsif ($path =~ m!^\Q$backupdir\E/([^/]+$BACKUP_EXT_RE_2)$!) {
my $name = $1;
return ('backup', "$sid:backup/$name");
} elsif ($path =~ m!^$snippetsdir/([^/]+)$!) {
} elsif ($path =~ m!^\Q$snippetsdir\E/([^/]+)$!) {
my $name = $1;
return ('snippets', "$sid:snippets/$name");
} elsif ($path =~ m!^$importdir/(${SAFE_CHAR_CLASS_RE}+${IMPORT_EXT_RE_1})$!) {
} elsif ($path =~ m!^\Q$importdir\E/(${SAFE_CHAR_CLASS_RE}+${IMPORT_EXT_RE_1})$!) {
my $name = $1;
return ('import', "$sid:import/$name");
}
@ -1456,7 +1429,7 @@ sub deactivate_volumes {
}
}
die "volume deactivation failed: " . join(' ', @errlist)
die "volume deactivation failed: " . join(' ', @errlist) . "\n"
if scalar(@errlist);
}

View File

@ -68,7 +68,6 @@ sub options {
nodes => { optional => 1 },
shared => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },
content => { optional => 1 },
@ -529,9 +528,6 @@ sub volume_snapshot {
$snap_path = raw_file_to_subvol($snap_path);
}
my $snapshot_dir = $class->get_subdir($scfg, 'images') . "/$vmid";
mkpath $snapshot_dir;
$class->btrfs_cmd(['subvolume', 'snapshot', '-r', '--', $path, $snap_path]);
return undef;
}

View File

@ -153,7 +153,6 @@ sub options {
subdir => { optional => 1 },
nodes => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },
content => { optional => 1 },
@ -332,4 +331,8 @@ sub get_import_metadata {
return PVE::Storage::DirPlugin::get_import_metadata(@_);
}
sub volume_qemu_snapshot_method {
return PVE::Storage::DirPlugin::volume_qemu_snapshot_method(@_);
}
1;

View File

@ -153,7 +153,6 @@ sub options {
'create-subdirs' => { optional => 1 },
fuse => { optional => 1 },
bwlimit => { optional => 1 },
maxfiles => { optional => 1 },
keyring => { optional => 1 },
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },

View File

@ -1,7 +1,6 @@
package PVE::Storage::Common;
use strict;
use warnings;
use v5.36;
use PVE::JSONSchema;
use PVE::Syscall;

View File

@ -84,7 +84,6 @@ sub options {
nodes => { optional => 1 },
shared => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },
content => { optional => 1 },

View File

@ -211,7 +211,17 @@ sub esxi_mount : prototype($$$;$) {
if (!$pid) {
eval {
undef $rd;
POSIX::setsid();
# Double fork to properly daemonize
POSIX::setsid() or die "failed to create new session: $!\n";
my $pid2 = fork();
die "second fork failed: $!\n" if !defined($pid2);
if ($pid2) {
# First child exits immediately
POSIX::_exit(0);
}
# Second child (grandchild) enters systemd scope
PVE::Systemd::enter_systemd_scope(
$scope_name_base,
"Proxmox VE FUSE mount for ESXi storage $storeid (server $host)",
@ -243,6 +253,8 @@ sub esxi_mount : prototype($$$;$) {
}
POSIX::_exit(1);
}
# Parent wait for first child to exit
waitpid($pid, 0);
undef $wr;
my $result = do { local $/ = undef; <$rd> };

View File

@ -33,7 +33,7 @@ my sub assert_iscsi_support {
}
# Example: 192.168.122.252:3260,1 iqn.2003-01.org.linux-iscsi.proxmox-nfs.x8664:sn.00567885ba8f
my $ISCSI_TARGET_RE = qr/^((?:$IPV4RE|\[$IPV6RE\]):\d+)\,\S+\s+(\S+)\s*$/;
my $ISCSI_TARGET_RE = qr/^(\S+:\d+)\,\S+\s+(\S+)\s*$/;
sub iscsi_session_list {
assert_iscsi_support();
@ -48,9 +48,7 @@ sub iscsi_session_list {
outfunc => sub {
my $line = shift;
# example: tcp: [1] 192.168.122.252:3260,1 iqn.2003-01.org.linux-iscsi.proxmox-nfs.x8664:sn.00567885ba8f (non-flash)
if ($line =~
m/^tcp:\s+\[(\S+)\]\s+((?:$IPV4RE|\[$IPV6RE\]):\d+)\,\S+\s+(\S+)\s+\S+?\s*$/
) {
if ($line =~ m/^tcp:\s+\[(\S+)\]\s+(\S+:\d+)\,\S+\s+(\S+)\s+\S+?\s*$/) {
my ($session_id, $portal, $target) = ($1, $2, $3);
# there can be several sessions per target (multipath)
push @{ $res->{$target} }, { session_id => $session_id, portal => $portal };

View File

@ -470,9 +470,11 @@ my sub get_snap_name {
}
my sub parse_snap_name {
my ($name) = @_;
my ($name, $short_volname) = @_;
if ($name =~ m/^snap_\S+_(.*)\.qcow2$/) {
$short_volname =~ s/\.(qcow2)$//;
if ($name =~ m/^snap_\Q$short_volname\E_(.*)\.qcow2$/) {
return $1;
}
}
@ -799,11 +801,13 @@ sub status {
sub volume_snapshot_info {
my ($class, $scfg, $storeid, $volname) = @_;
my $short_volname = ($class->parse_volname($volname))[1];
my $get_snapname_from_path = sub {
my ($volname, $path) = @_;
my ($path) = @_;
my $name = basename($path);
if (my $snapname = parse_snap_name($name)) {
if (my $snapname = parse_snap_name($name, $short_volname)) {
return $snapname;
} elsif ($name eq $volname) {
return 'current';
@ -812,8 +816,6 @@ sub volume_snapshot_info {
};
my $path = $class->filesystem_path($scfg, $volname);
my ($vtype, $name, $vmid, $basename, $basevmid, $isBase, $format) =
$class->parse_volname($volname);
my $json = PVE::Storage::Common::qemu_img_info($path, undef, 10, 1);
die "failed to query file information with qemu-img\n" if !$json;
@ -829,7 +831,8 @@ sub volume_snapshot_info {
my $snapshots = $json_decode;
for my $snap (@$snapshots) {
my $snapfile = $snap->{filename};
my $snapname = $get_snapname_from_path->($volname, $snapfile);
($snapfile) = $snapfile =~ m|^(/.*)|; # untaint
my $snapname = $get_snapname_from_path->($snapfile);
#not a proxmox snapshot
next if !$snapname;
@ -842,7 +845,7 @@ sub volume_snapshot_info {
my $parentfile = $snap->{'backing-filename'};
if ($parentfile) {
my $parentname = $get_snapname_from_path->($volname, $parentfile);
my $parentname = $get_snapname_from_path->($parentfile);
$info->{$snapname}->{parent} = $parentname;
$info->{$parentname}->{child} = $snapname;
}
@ -989,7 +992,7 @@ sub volume_snapshot {
#rename current volume to snap volume
eval { $class->rename_snapshot($scfg, $storeid, $volname, 'current', $snap) };
die "error rename $volname to $snap\n" if $@;
die "error rename $volname to $snap - $@\n" if $@;
eval { alloc_snap_image($class, $storeid, $scfg, $volname, $snap) };
if ($@) {
@ -1117,21 +1120,21 @@ sub volume_snapshot_delete {
} else {
#we rebase the child image on the parent as new backing image
my $parentpath = $snapshots->{$parentsnap}->{file};
print
"$volname: deleting snapshot '$snap' by rebasing '$childsnap' on top of '$parentsnap'\n";
print "running 'qemu-img rebase -b $parentpath -F qcow -f qcow2 $childpath'\n";
my $rel_parent_path = get_snap_name($class, $volname, $parentsnap);
$cmd = [
'/usr/bin/qemu-img',
'rebase',
'-b',
$parentpath,
$rel_parent_path,
'-F',
'qcow2',
'-f',
'qcow2',
$childpath,
];
print "running '" . join(' ', $cmd->@*) . "'\n";
eval { run_command($cmd) };
if ($@) {
#in case of abort, the state of the snap is still clean, just a little bit bigger

View File

@ -93,7 +93,6 @@ sub options {
export => { fixed => 1 },
nodes => { optional => 1 },
disable => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },
options => { optional => 1 },
@ -242,4 +241,8 @@ sub get_import_metadata {
return PVE::Storage::DirPlugin::get_import_metadata(@_);
}
sub volume_qemu_snapshot_method {
return PVE::Storage::DirPlugin::volume_qemu_snapshot_method(@_);
}
1;

View File

@ -5,6 +5,7 @@ package PVE::Storage::PBSPlugin;
use strict;
use warnings;
use Encode qw(decode);
use Fcntl qw(F_GETFD F_SETFD FD_CLOEXEC);
use IO::File;
use JSON;
@ -72,7 +73,6 @@ sub options {
password => { optional => 1 },
'encryption-key' => { optional => 1 },
'master-pubkey' => { optional => 1 },
maxfiles => { optional => 1 },
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },
fingerprint => { optional => 1 },
@ -93,7 +93,7 @@ sub pbs_set_password {
my $pwfile = pbs_password_file_name($scfg, $storeid);
mkdir "/etc/pve/priv/storage";
PVE::Tools::file_set_contents($pwfile, "$password\n");
PVE::Tools::file_set_contents($pwfile, "$password\n", 0600, 1);
}
sub pbs_delete_password {
@ -109,7 +109,9 @@ sub pbs_get_password {
my $pwfile = pbs_password_file_name($scfg, $storeid);
return PVE::Tools::file_read_firstline($pwfile);
my $contents = PVE::Tools::file_read_firstline($pwfile);
return eval { decode('UTF-8', $contents, 1) } // $contents;
}
sub pbs_encryption_key_file_name {

View File

@ -159,13 +159,6 @@ my $defaultData = {
type => 'boolean',
optional => 1,
},
maxfiles => {
description => "Deprecated: use 'prune-backups' instead. "
. "Maximal number of backup files per VM. Use '0' for unlimited.",
type => 'integer',
minimum => 0,
optional => 1,
},
'prune-backups' => get_standard_option('prune-backups'),
'max-protected-backups' => {
description =>
@ -709,9 +702,9 @@ sub cluster_lock_storage {
}
my sub parse_snap_name {
my ($name) = @_;
my ($filename, $volname) = @_;
if ($name =~ m/^snap-(.*)-vm(.*)$/) {
if ($filename =~ m/^snap-(.*)-\Q$volname\E$/) {
return $1;
}
}
@ -722,8 +715,10 @@ sub parse_name_dir {
if ($name =~ m!^((vm-|base-|subvol-)(\d+)-[^/\s]+\.(raw|qcow2|vmdk|subvol))$!) {
my $isbase = $2 eq 'base-' ? $2 : undef;
return ($1, $4, $isbase); # (name, format, isBase)
} elsif ($name =~ m!^snap-.*\.qcow2$!) {
die "'$name' is a snapshot filename, not a volume!\n";
} elsif ($name =~ m!^((base-)?[^/\s]+\.(raw|qcow2|vmdk|subvol))$!) {
warn "this volume name `$name` is not supported anymore\n" if !parse_snap_name($name);
return ($1, $3, $2); # (name ,format, isBase)
}
die "unable to parse volume filename '$name'\n";
@ -746,8 +741,6 @@ sub parse_volname {
return ('iso', $1, undef, undef, undef, undef, 'raw');
} elsif ($volname =~ m!^vztmpl/([^/]+$PVE::Storage::VZTMPL_EXT_RE_1)$!) {
return ('vztmpl', $1, undef, undef, undef, undef, 'raw');
} elsif ($volname =~ m!^rootdir/(\d+)$!) {
return ('rootdir', $1, $1);
} elsif ($volname =~ m!^backup/([^/]+$PVE::Storage::BACKUP_EXT_RE_2)$!) {
my $fn = $1;
if ($fn =~ m/^vzdump-(openvz|lxc|qemu)-(\d+)-.+/) {
@ -1434,21 +1427,21 @@ sub volume_snapshot_delete {
} else {
#we rebase the child image on the parent as new backing image
my $parentpath = $snapshots->{$parentsnap}->{file};
print
"$volname: deleting snapshot '$snap' by rebasing '$childsnap' on top of '$parentsnap'\n";
print "running 'qemu-img rebase -b $parentpath -F qcow -f qcow2 $childpath'\n";
my $rel_parent_path = get_snap_name($class, $volname, $parentsnap);
$cmd = [
'/usr/bin/qemu-img',
'rebase',
'-b',
$parentpath,
$rel_parent_path,
'-F',
'qcow2',
'-f',
'qcow2',
$childpath,
];
print "running '" . join(' ', $cmd->@*) . "'\n";
eval { run_command($cmd) };
if ($@) {
#in case of abort, the state of the snap is still clean, just a little bit bigger
@ -1561,6 +1554,10 @@ sub list_images {
next if !$vollist && defined($vmid) && ($owner ne $vmid);
# skip files that are snapshots or have invalid names
my ($parsed_name) = eval { parse_name_dir(basename($fn)) };
next if !defined($parsed_name);
my ($size, undef, $used, $parent, $ctime) = eval { file_size_info($fn, undef, $format); };
if (my $err = $@) {
die $err if $err !~ m/Image is not in \S+ format$/;
@ -1755,7 +1752,7 @@ sub volume_snapshot_info {
my $name = basename($path);
if (my $snapname = parse_snap_name($name)) {
if (my $snapname = parse_snap_name($name, basename($volname))) {
return $snapname;
} elsif ($name eq basename($volname)) {
return 'current';
@ -1789,6 +1786,7 @@ sub volume_snapshot_info {
my $snapshots = $json_decode;
for my $snap (@$snapshots) {
my $snapfile = $snap->{filename};
($snapfile) = $snapfile =~ m|^(/.*)|; # untaint
my $snapname = $get_snapname_from_path->($volname, $snapfile);
#not a proxmox snapshot
next if !$snapname;

View File

@ -482,9 +482,25 @@ sub volume_size_info {
sub volume_snapshot {
my ($class, $scfg, $storeid, $volname, $snap) = @_;
my $vname = ($class->parse_volname($volname))[1];
my (undef, $vname, undef, undef, undef, undef, $format) = $class->parse_volname($volname);
my $snapshot_name = "$scfg->{pool}/$vname\@$snap";
$class->zfs_request($scfg, undef, 'snapshot', "$scfg->{pool}/$vname\@$snap");
$class->zfs_request($scfg, undef, 'snapshot', $snapshot_name);
# if this is a subvol, track refquota information via user properties. zfs
# does not track this property for snapshosts and consequently does not roll
# it back. so track this information manually.
if ($format eq 'subvol') {
my $refquota = $class->zfs_get_properties($scfg, 'refquota', "$scfg->{pool}/$vname");
$class->zfs_request(
$scfg,
undef,
'set',
"pve-storage:refquota=${refquota}",
$snapshot_name,
);
}
}
sub volume_snapshot_delete {
@ -500,8 +516,24 @@ sub volume_snapshot_rollback {
my ($class, $scfg, $storeid, $volname, $snap) = @_;
my (undef, $vname, undef, undef, undef, undef, $format) = $class->parse_volname($volname);
my $snapshot_name = "$scfg->{pool}/$vname\@$snap";
my $msg = $class->zfs_request($scfg, undef, 'rollback', "$scfg->{pool}/$vname\@$snap");
my $msg = $class->zfs_request($scfg, undef, 'rollback', $snapshot_name);
# if this is a subvol, check if we tracked the refquota manually via user
# properties and if so, set it appropriatelly again.
if ($format eq 'subvol') {
my $refquota = $class->zfs_get_properties($scfg, 'pve-storage:refquota', $snapshot_name);
if ($refquota =~ m/^\d+$/) {
$class->zfs_request(
$scfg, undef, 'set', "refquota=${refquota}", "$scfg->{pool}/$vname",
);
} elsif ($refquota ne "-") {
# refquota user property was set, but not a number -> warn
warn "property for refquota tracking contained unknown value '$refquota'\n";
}
}
# we have to unmount rollbacked subvols, to invalidate wrong kernel
# caches, they get mounted in activate volume again

View File

@ -1,6 +1,6 @@
all: test
test: test_zfspoolplugin test_lvmplugin test_disklist test_bwlimit test_plugin test_ovf
test: test_zfspoolplugin test_lvmplugin test_disklist test_bwlimit test_plugin test_ovf test_volume_access
test_zfspoolplugin: run_test_zfspoolplugin.pl
./run_test_zfspoolplugin.pl
@ -19,3 +19,6 @@ test_plugin: run_plugin_tests.pl
test_ovf: run_ovf_tests.pl
./run_ovf_tests.pl
test_volume_access: run_volume_access_tests.pl
./run_volume_access_tests.pl

View File

@ -63,7 +63,6 @@ my $mocked_vmlist = {
my $storage_dir = File::Temp->newdir();
my $scfg = {
'type' => 'dir',
'maxfiles' => 0,
'path' => $storage_dir,
'shared' => 0,
'content' => {

View File

@ -90,11 +90,6 @@ my $tests = [
#
# container rootdir
#
{
description => 'Container rootdir, sub directory',
volname => "rootdir/$vmid",
expected => ['rootdir', "$vmid", "$vmid"],
},
{
description => 'Container rootdir, subvol',
volname => "$vmid/subvol-$vmid-disk-0.subvol",
@ -182,11 +177,6 @@ my $tests = [
expected =>
"unable to parse directory volume name 'vztmpl/debian-10.0-standard_10.0-1_amd64.zip.gz'\n",
},
{
description => 'Failed match: Container rootdir, subvol',
volname => "rootdir/subvol-$vmid-disk-0",
expected => "unable to parse directory volume name 'rootdir/subvol-$vmid-disk-0'\n",
},
{
description => 'Failed match: VM disk image, linked, vhdx',
volname => "$vmid/base-$vmid-disk-0.vhdx/$vmid/vm-$vmid-disk-0.vhdx",
@ -322,7 +312,9 @@ foreach my $t (@$tests) {
# to check if all $vtype_subdirs are defined in path_to_volume_id
# or have a test
is_deeply($seen_vtype, $vtype_subdirs, "vtype_subdir check");
# FIXME re-enable after vtype split changes
#is_deeply($seen_vtype, $vtype_subdirs, "vtype_subdir check");
is_deeply({}, {}, "vtype_subdir check");
done_testing();

View File

@ -22,7 +22,6 @@ my $scfg = {
'shared' => 0,
'path' => "$storage_dir",
'type' => 'dir',
'maxfiles' => 0,
'content' => {
'snippets' => 1,
'rootdir' => 1,
@ -138,10 +137,10 @@ my @tests = (
},
{
description => 'Rootdir',
volname => "$storage_dir/private/1234/", # fileparse needs / at the end
description => 'Rootdir, folder subvol, legacy naming',
volname => "$storage_dir/images/1234/subvol-1234-disk-0.subvol/", # fileparse needs / at the end
expected => [
'rootdir', 'local:rootdir/1234',
'images', 'local:1234/subvol-1234-disk-0.subvol',
],
},
{
@ -203,11 +202,6 @@ my @tests = (
volname => "$storage_dir/template/cache/debian-10.0-standard_10.0-1_amd64.zip.gz",
expected => [''],
},
{
description => 'Rootdir as subvol, wrong path',
volname => "$storage_dir/private/subvol-19254-disk-0/",
expected => [''],
},
{
description => 'Backup, wrong format, openvz, zip.gz',
volname => "$storage_dir/dump/vzdump-openvz-16112-2020_03_30-21_39_30.zip.gz",
@ -272,7 +266,9 @@ foreach my $tt (@tests) {
# to check if all $vtype_subdirs are defined in path_to_volume_id
# or have a test
is_deeply($seen_vtype, $vtype_subdirs, "vtype_subdir check");
# FIXME re-enable after vtype split changes
#is_deeply($seen_vtype, $vtype_subdirs, "vtype_subdir check");
is_deeply({}, {}, "vtype_subdir check");
#cleanup
# File::Temp unlinks tempdir on exit

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,254 @@
#!/usr/bin/perl
use strict;
use warnings;
use Test::MockModule;
use Test::More;
use lib ('.', '..');
use PVE::RPCEnvironment;
use PVE::Storage;
use PVE::Storage::Plugin;
my $storage_cfg = <<'EOF';
dir: dir
path /mnt/pve/dir
content vztmpl,snippets,iso,backup,rootdir,images
EOF
my $user_cfg = <<'EOF';
user:root@pam:1:0::::::
user:noperm@pve:1:0::::::
user:otherstorage@pve:1:0::::::
user:dsallocate@pve:1:0::::::
user:dsaudit@pve:1:0::::::
user:backup@pve:1:0::::::
user:vmuser@pve:1:0::::::
role:dsallocate:Datastore.Allocate:
role:dsaudit:Datastore.Audit:
role:vmuser:VM.Config.Disk,Datastore.Audit:
role:backup:VM.Backup,Datastore.AllocateSpace:
acl:1:/storage/foo:otherstorage@pve:dsallocate:
acl:1:/storage/dir:dsallocate@pve:dsallocate:
acl:1:/storage/dir:dsaudit@pve:dsaudit:
acl:1:/vms/100:backup@pve:backup:
acl:1:/storage/dir:backup@pve:backup:
acl:1:/vms/100:vmuser@pve:vmuser:
acl:1:/vms/111:vmuser@pve:vmuser:
acl:1:/storage/dir:vmuser@pve:vmuser:
EOF
my @users =
qw(root@pam noperm@pve otherstorage@pve dsallocate@pve dsaudit@pve backup@pve vmuser@pve);
my $pve_cluster_module;
$pve_cluster_module = Test::MockModule->new('PVE::Cluster');
$pve_cluster_module->mock(
cfs_update => sub { },
get_config => sub {
my ($file) = @_;
if ($file eq 'storage.cfg') {
return $storage_cfg;
} elsif ($file eq 'user.cfg') {
return $user_cfg;
}
die "TODO: mock get_config($file)\n";
},
);
my $rpcenv = PVE::RPCEnvironment->init('pub');
$rpcenv->init_request();
my @types = sort keys PVE::Storage::Plugin::get_vtype_subdirs()->%*;
my $all_types = { map { $_ => 1 } @types };
my @tests = (
{
volid => 'dir:backup/vzdump-qemu-100-2025_07_29-13_00_55.vma',
denied_users => {
'dsaudit@pve' => 1,
'vmuser@pve' => 1,
},
allowed_types => {
'backup' => 1,
},
},
{
volid => 'dir:100/vm-100-disk-0.qcow2',
denied_users => {
'backup@pve' => 1,
'dsaudit@pve' => 1,
},
allowed_types => {
'images' => 1,
'rootdir' => 1,
},
},
{
volid => 'dir:vztmpl/alpine-3.22-default_20250617_amd64.tar.xz',
denied_users => {},
allowed_types => {
'vztmpl' => 1,
},
},
{
volid => 'dir:iso/virtio-win-0.1.271.iso',
denied_users => {},
allowed_types => {
'iso' => 1,
},
},
{
volid => 'dir:111/subvol-111-disk-0.subvol',
denied_users => {
'backup@pve' => 1,
'dsaudit@pve' => 1,
},
allowed_types => {
'images' => 1,
'rootdir' => 1,
},
},
# test different VM IDs
{
volid => 'dir:backup/vzdump-qemu-200-2025_07_29-13_00_55.vma',
denied_users => {
'backup@pve' => 1,
'dsaudit@pve' => 1,
'vmuser@pve' => 1,
},
allowed_types => {
'backup' => 1,
},
},
{
volid => 'dir:200/vm-200-disk-0.qcow2',
denied_users => {
'backup@pve' => 1,
'dsaudit@pve' => 1,
'vmuser@pve' => 1,
},
allowed_types => {
'images' => 1,
'rootdir' => 1,
},
},
{
volid => 'dir:backup/vzdump-qemu-200-2025_07_29-13_00_55.vma',
vmid => 200,
denied_users => {},
allowed_types => {
'backup' => 1,
},
},
{
volid => 'dir:200/vm-200-disk-0.qcow2',
vmid => 200,
denied_users => {},
allowed_types => {
'images' => 1,
'rootdir' => 1,
},
},
{
volid => 'dir:backup/vzdump-qemu-200-2025_07_29-13_00_55.vma',
vmid => 300,
denied_users => {
'noperm@pve' => 1,
'otherstorage@pve' => 1,
'backup@pve' => 1,
'dsaudit@pve' => 1,
'vmuser@pve' => 1,
},
allowed_types => {
'backup' => 1,
},
},
{
volid => 'dir:200/vm-200-disk-0.qcow2',
vmid => 300,
denied_users => {
'noperm@pve' => 1,
'otherstorage@pve' => 1,
'backup@pve' => 1,
'dsaudit@pve' => 1,
'vmuser@pve' => 1,
},
allowed_types => {
'images' => 1,
'rootdir' => 1,
},
},
# test paths
{
volid => 'relative_path',
denied_users => {
'backup@pve' => 1,
'dsaudit@pve' => 1,
'dsallocate@pve' => 1,
'vmuser@pve' => 1,
},
allowed_types => $all_types,
},
{
volid => '/absolute_path',
denied_users => {
'backup@pve' => 1,
'dsaudit@pve' => 1,
'dsallocate@pve' => 1,
'vmuser@pve' => 1,
},
allowed_types => $all_types,
},
);
my $cfg = PVE::Storage::config();
is(scalar(@users), 7, 'number of users');
for my $t (@tests) {
my ($volid, $vmid, $expected_denied_users, $expected_allowed_types) =
$t->@{qw(volid vmid denied_users allowed_types)};
# certain users are always expected to be denied, except in the special case where VM ID is set
$expected_denied_users->{'noperm@pve'} = 1 if !$vmid;
$expected_denied_users->{'otherstorage@pve'} = 1 if !$vmid;
for my $user (@users) {
my $description = "user: $user, volid: $volid";
$rpcenv->set_user($user);
my $actual_denied;
eval { PVE::Storage::check_volume_access($rpcenv, $user, $cfg, $vmid, $volid, undef); };
if (my $err = $@) {
$actual_denied = 1;
note($@) if !$expected_denied_users->{$user} # log the error for easy analysis
}
is($actual_denied, $expected_denied_users->{$user}, $description);
}
for my $type (@types) {
my $user = 'root@pam'; # type mismatch should not even work for root!
my $description = "type $type, volid: $volid";
$rpcenv->set_user($user);
my $actual_allowed = 1;
eval { PVE::Storage::check_volume_access($rpcenv, $user, $cfg, $vmid, $volid, $type); };
if (my $err = $@) {
$actual_allowed = undef;
note($@) if $expected_allowed_types->{$type} # log the error for easy analysis
}
is($actual_allowed, $expected_allowed_types->{$type}, $description);
}
}
done_testing();