public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Wolfgang Bumiller <w.bumiller@proxmox.com>
To: "Max R. Carrara" <m.carrara@proxmox.com>
Cc: pve-devel@lists.proxmox.com
Subject: Re: [PATCH pve-storage v1 48/54] fix #2884: implement nested subdir scanning and support 'iso' vtype
Date: Thu, 2 Jul 2026 16:55:29 +0200	[thread overview]
Message-ID: <cvjtmro3odmdgh574wnfql3nlaezezukokbsuxdnmycadfcuah@4ckwm5hn2bci> (raw)
In-Reply-To: <20260422111322.257380-49-m.carrara@proxmox.com>

On Wed, Apr 22, 2026 at 01:13:14PM +0200, Max R. Carrara wrote:
> Introduce a new 'max-scan-depth' property for directory-based
> storages. This property makes it so that files for specific volume
> types may also be put into (nested) subdirectories inside the volume
> type's directory.
> 
> The maximum allowed depth is between 0 and 50, with 0 being the
> default, which corresponds to the current behavior.
> 
> Support nested subdirectories for ISOs (the 'iso' vtype) only for now.
> 
> Achieve all of this by calling the `get_subdir_files()` helper in
> `PVE::Storage::Plugin` recursively. Note that the default recursion
> limit in Perl appears to be exactly 100 [0], way above the maximum
> value of 50 for the 'max-scan-depth' property. Should we need deeper
> nesting, we can always make the subroutine iterative instead of
> recursive later.
> 
> Add additional test cases wherever applicable to account for nested
> subdirectories, including cases that check whether the limit set by
> the property is upheld, and also cases that check for the existence of
> parent directory references ('..') in volume names.
> 
> [0]: https://perldoc.perl.org/perl5101delta#Deep-recursion-on-subroutine-%22%25s%22
> 
> Originally-by: Noel Ullreich <n.ullreich@proxmox.com>
> Signed-off-by: Max R. Carrara <m.carrara@proxmox.com>
> ---
>  src/PVE/Storage/BTRFSPlugin.pm              |   1 +
>  src/PVE/Storage/CephFSPlugin.pm             |   1 +
>  src/PVE/Storage/Common/Parse.pm             |  14 +++
>  src/PVE/Storage/Common/test/parser_tests.pl |  50 ++++++++
>  src/PVE/Storage/DirPlugin.pm                |   1 +
>  src/PVE/Storage/Plugin.pm                   |  41 ++++++-
>  src/test/filesystem_path_test.pm            |   8 ++
>  src/test/list_volumes_test.pm               | 127 ++++++++++++++++++++
>  src/test/parse_volname_test.pm              |  56 +++++++++
>  src/test/path_to_volume_id_test.pm          |   7 ++
>  10 files changed, 302 insertions(+), 4 deletions(-)
> 
> diff --git a/src/PVE/Storage/BTRFSPlugin.pm b/src/PVE/Storage/BTRFSPlugin.pm
> index 67b442f6..0a29fd31 100644
> --- a/src/PVE/Storage/BTRFSPlugin.pm
> +++ b/src/PVE/Storage/BTRFSPlugin.pm
> @@ -68,6 +68,7 @@ sub properties {
>  sub options {
>      return {
>          path => { fixed => 1 },
> +        'max-scan-depth' => { optional => 1 },
>          nodes => { optional => 1 },
>          shared => { optional => 1 },
>          disable => { optional => 1 },
> diff --git a/src/PVE/Storage/CephFSPlugin.pm b/src/PVE/Storage/CephFSPlugin.pm
> index fbc97113..b31f53d9 100644
> --- a/src/PVE/Storage/CephFSPlugin.pm
> +++ b/src/PVE/Storage/CephFSPlugin.pm
> @@ -140,6 +140,7 @@ sub options {
>      return {
>          path => { fixed => 1 },
>          'content-dirs' => { optional => 1 },
> +        'max-scan-depth' => { optional => 1 },
>          monhost => { optional => 1 },
>          nodes => { optional => 1 },
>          subdir => { optional => 1 },
> diff --git a/src/PVE/Storage/Common/Parse.pm b/src/PVE/Storage/Common/Parse.pm
> index 9fb45b0c..f98b702d 100644
> --- a/src/PVE/Storage/Common/Parse.pm
> +++ b/src/PVE/Storage/Common/Parse.pm
> @@ -57,6 +57,10 @@ my $RE_SAFE_CHAR_CLASS = qr/[a-zA-Z0-9\-\.\+\=\_]/;
> 
>  my $RE_SAFE_CHAR_WITH_WHITESPACE_CLASS = qr/[ a-zA-Z0-9\-\.\+\=\_]/;
> 
> +my $RE_DIRECTORY_COMPONENTS = qr!
> +    ( ($RE_SAFE_CHAR_WITH_WHITESPACE_CLASS)+ / )*?  # NOTE: non-greedy matching

↑ Greedy should work fine (and is usually faster).
This regex always ends in a slash and all uses of this regex are
immediately followed by a `[^/]` (not-a-slash), so the boundary should
always be the same.

> +!xn;
> +
>  my $RE_PARENT_DIR = quotemeta('..');
>  my $RE_CONTAINS_PARENT_DIR = qr!
>      ( ^$RE_PARENT_DIR/ )  #  ../ --> Beginning of path
> @@ -73,6 +77,7 @@ my $RE_VMID = qr![1-9][0-9]{2,8}!;
> 
>  my $RE_ISO_FILE_PATH = qr!
>      (?<path>
> +        (?<dir> $RE_DIRECTORY_COMPONENTS )?
>          (?<file> [^/]+ \. (?<ext> (?i: iso|img) ) )
>      )
>  !xn;
> @@ -206,6 +211,15 @@ my sub format_named_groups(%groups) {
> 
>      my @disk_path_components = ();
> 
> +    if (defined($result->{dir})) {
> +        if ($result->{dir} eq '') {
> +            delete $result->{dir};
> +        } else {
> +            $result->{dir} = strip_trailing_path_separators($result->{dir});
> +            push(@disk_path_components, $result->{dir});
> +        }
> +    }
> +
>      if (defined($result->{file})) {
>          $result->{file} = strip_leading_path_separators($result->{file});
>          push(@disk_path_components, $result->{file});
> diff --git a/src/PVE/Storage/Common/test/parser_tests.pl b/src/PVE/Storage/Common/test/parser_tests.pl
> index b6e42307..ad122cd2 100755
> --- a/src/PVE/Storage/Common/test/parser_tests.pl
> +++ b/src/PVE/Storage/Common/test/parser_tests.pl
> @@ -73,6 +73,32 @@ my $volname_cases_iso_valid = [
>              volname => 'iso/Fedora.Img',
>          },
>      },
> +
> +    # subdirectories
> +    {
> +        path => 'subdir/custom-debian.iso',
> +        expected => {
> +            file => 'custom-debian.iso',
> +            ext => 'iso',
> +            dir => 'subdir',
> +            'disk-path' => 'subdir/custom-debian.iso',
> +            path => 'subdir/custom-debian.iso',
> +            vtype => 'iso',
> +            volname => 'iso/subdir/custom-debian.iso',
> +        },
> +    },
> +    {
> +        path => 'deeply/nested/dir/hannah-montana-linux.IMG',
> +        expected => {
> +            file => 'hannah-montana-linux.IMG',
> +            ext => 'IMG',
> +            dir => 'deeply/nested/dir',
> +            'disk-path' => 'deeply/nested/dir/hannah-montana-linux.IMG',
> +            path => 'deeply/nested/dir/hannah-montana-linux.IMG',
> +            vtype => 'iso',
> +            volname => 'iso/deeply/nested/dir/hannah-montana-linux.IMG',
> +        },
> +    },
>  ];
> 
>  my $volname_cases_iso_invalid = [
> @@ -84,6 +110,30 @@ my $volname_cases_iso_invalid = [
>          },
>          expected => undef,
>      },
> +    {
> +        description => "Parent dir reference in path (beginning) (iso)",
> +        args => {
> +            path => '../custom-debian.iso',
> +            vtype => 'iso',
> +        },
> +        expected => undef,
> +    },
> +    {
> +        description => "Parent dir reference in path (middle) (iso)",
> +        args => {
> +            path => 'subdir/../custom-debian.iso',
> +            vtype => 'iso',
> +        },
> +        expected => undef,
> +    },
> +    {
> +        description => "Parent dir reference in path (end) (iso)",
> +        args => {
> +            path => 'subdir/custom-debian.iso/..',
> +            vtype => 'iso',
> +        },
> +        expected => undef,
> +    },
>  ];
> 
>  my $volname_cases_vztmpl_valid = [
> diff --git a/src/PVE/Storage/DirPlugin.pm b/src/PVE/Storage/DirPlugin.pm
> index 80c4a031..58b942e7 100644
> --- a/src/PVE/Storage/DirPlugin.pm
> +++ b/src/PVE/Storage/DirPlugin.pm
> @@ -81,6 +81,7 @@ sub options {
>      return {
>          path => { fixed => 1 },
>          'content-dirs' => { optional => 1 },
> +        'max-scan-depth' => { optional => 1 },
>          nodes => { optional => 1 },
>          shared => { optional => 1 },
>          disable => { optional => 1 },
> diff --git a/src/PVE/Storage/Plugin.pm b/src/PVE/Storage/Plugin.pm
> index 101e0b6d..0fbcbd4b 100644
> --- a/src/PVE/Storage/Plugin.pm
> +++ b/src/PVE/Storage/Plugin.pm
> @@ -233,6 +233,15 @@ my $defaultData = {
>              format => "pve-dir-override-list",
>              optional => 1,
>          },
> +        'max-scan-depth' => {
> +            description => "Maximum depth of subdirectories to traverse when searching for"
> +                . " ISOs in directories.",
> +            type => 'integer',
> +            default => 0,
> +            minimum => 0,
> +            maximum => 50,
> +            optional => 1,
> +        },
>          options => {
>              description => "NFS/CIFS mount options (see 'man nfs' or 'man mount.cifs')",
>              type => 'string',
> @@ -1669,10 +1678,16 @@ sub list_images {
> 
>  # $vtype = <iso|vztmpl|backup|snippets|import>
>  my sub get_subdir_files {
> -    my ($storeid, $scfg, $vtype, $vmid) = @_;
> +    use feature 'current_sub'; # Needed for the __SUB__ token further below
> +
> +    my ($storeid, $scfg, $vtype, $vmid, $remaining_depth, $current_path) = @_;
> 
>      my $vtype_subdir = plugin_get_vtype_subdir($scfg, $vtype);
> 
> +    if (!defined($current_path)) {
> +        $current_path = $vtype_subdir;
> +    }
> +
>      my $res = [];
> 
>      my $get_subdir_file_info = sub {
> @@ -1752,10 +1767,24 @@ my sub get_subdir_files {
>          return;
>      };
> 
> -    for my $path (<$vtype_subdir/*>) {
> +    for my $path (<$current_path/*>) {
>          my $st = File::stat::stat($path);
> 
> -        next if (!$st || S_ISDIR($st->mode));
> +        next if !$st;
> +
> +        if (S_ISDIR($st->mode)) {
> +            if (defined($remaining_depth) && $remaining_depth > 0) {
> +                # Note: Have to call the subroutine via __SUB__->(...) because
> +                # lexical subroutines otherwise cannot reference themselves

↑ You can drop this comment.

> +                my $inner_res = __SUB__->(
> +                    $storeid, $scfg, $vtype, $vmid, $remaining_depth - 1, $path,
> +                );
> +
> +                push($res->@*, $inner_res->@*);
> +            }
> +
> +            next;
> +        }
> 
>          if (defined(my $info = $get_subdir_file_info->($path, $st))) {
>              $info->{size} = $st->size;
> @@ -1773,6 +1802,10 @@ my sub get_subdir_files {
>  sub list_volumes {
>      my ($class, $storeid, $scfg, $vmid, $content_types) = @_;
> 
> +    my $depth = $scfg->{'max-scan-depth'} // 0;
> +    $depth = 0 if $depth < 0;
> +    $depth = 50 if $depth > 50;
> +
>      my $res = [];
>      my $vmlist = PVE::Cluster::get_vmlist();
> 
> @@ -1786,7 +1819,7 @@ sub list_volumes {
>          return if !$scfg->{path};
> 
>          if ($type eq 'iso' && !defined($vmid)) {
> -            return get_subdir_files($storeid, $scfg, 'iso', undef);
> +            return get_subdir_files($storeid, $scfg, 'iso', undef, $depth);
>          }
> 
>          if ($type eq 'vztmpl' && !defined($vmid)) {
> diff --git a/src/test/filesystem_path_test.pm b/src/test/filesystem_path_test.pm
> index 5a715ce9..26a74a0d 100644
> --- a/src/test/filesystem_path_test.pm
> +++ b/src/test/filesystem_path_test.pm
> @@ -48,6 +48,14 @@ my $tests = [
>              "$DEFAULT_STORAGE_DIR/template/iso/my-awesome-proxmox.iso", undef, 'iso',
>          ],
>      },
> +    {
> +        volname => 'iso/foo/bar/baz/my-awesome-proxmox.iso',
> +        snapname => undef,
> +        expected => [
> +            "$DEFAULT_STORAGE_DIR/template/iso/foo/bar/baz/my-awesome-proxmox.iso", undef,
> +            'iso',
> +        ],
> +    },
>      {
>          volname => "backup/vzdump-qemu-1234-2020_03_30-21_12_40.vma",
>          snapname => undef,
> diff --git a/src/test/list_volumes_test.pm b/src/test/list_volumes_test.pm
> index ce35c782..0daaba94 100644
> --- a/src/test/list_volumes_test.pm
> +++ b/src/test/list_volumes_test.pm
> @@ -1296,6 +1296,133 @@ my $test_param_list = [
>              },
>          ],
>      },
> +    {
> +        description => "VMID: none, no nested subdirectories when using defaults",
> +        storeid => $DEFAULT_STOREID,
> +        scfg => $DEFAULT_SCFG,
> +        vmid => undef,
> +        vtypes => ['iso', 'vztmpl', 'snippets', 'import'],
> +        cases => [
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/some-installer.iso",
> +                expected => {
> +                    content => 'iso',
> +                    ctime => $DEFAULT_CTIME,
> +                    format => 'iso',
> +                    size => $DEFAULT_SIZE,
> +                    volid => 'local:iso/some-installer.iso',
> +                },
> +            },
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/1/some-installer.iso",
> +                expected => undef,
> +            },
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/1/2/3/4/5/some-installer.iso",
> +                expected => undef,
> +            },
> +        ],
> +    },
> +    {
> +        description => "VMID: none, nested subdirectories allowed, max-scan-depth = 1",
> +        storeid => $DEFAULT_STOREID,
> +        scfg => {
> +            type => 'dir',
> +            path => $DEFAULT_STORAGE_PATH,
> +            shared => 0,
> +            'max-scan-depth' => 1,
> +            content => {
> +                iso => 1,
> +                vztmpl => 1,
> +                snippets => 1,
> +                import => 1,
> +            },
> +        },
> +        vmid => undef,
> +        vtypes => ['iso', 'vztmpl', 'snippets', 'import'],
> +        cases => [
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/some-installer.iso",
> +                expected => {
> +                    content => 'iso',
> +                    ctime => $DEFAULT_CTIME,
> +                    format => 'iso',
> +                    size => $DEFAULT_SIZE,
> +                    volid => 'local:iso/some-installer.iso',
> +                },
> +            },
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/1/some-installer.iso",
> +                expected => {
> +                    content => 'iso',
> +                    ctime => $DEFAULT_CTIME,
> +                    format => 'iso',
> +                    size => $DEFAULT_SIZE,
> +                    volid => 'local:iso/1/some-installer.iso',
> +                },
> +            },
> +            {
> +                # Exceeds max-scan-depth
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/1/2/some-installer.iso",
> +                expected => undef,
> +            },
> +        ],
> +    },
> +    {
> +        description => "VMID: none, nested subdirectories allowed, max-scan-depth = 5",
> +        storeid => $DEFAULT_STOREID,
> +        scfg => {
> +            type => 'dir',
> +            path => $DEFAULT_STORAGE_PATH,
> +            shared => 0,
> +            'max-scan-depth' => 5,
> +            content => {
> +                iso => 1,
> +                vztmpl => 1,
> +                snippets => 1,
> +                import => 1,
> +            },
> +        },
> +        vmid => undef,
> +        vtypes => ['iso', 'vztmpl', 'snippets', 'import'],
> +        cases => [
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/some-installer.iso",
> +                expected => {
> +                    content => 'iso',
> +                    ctime => $DEFAULT_CTIME,
> +                    format => 'iso',
> +                    size => $DEFAULT_SIZE,
> +                    volid => 'local:iso/some-installer.iso',
> +                },
> +            },
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/1/some-installer.iso",
> +                expected => {
> +                    content => 'iso',
> +                    ctime => $DEFAULT_CTIME,
> +                    format => 'iso',
> +                    size => $DEFAULT_SIZE,
> +                    volid => 'local:iso/1/some-installer.iso',
> +                },
> +            },
> +            {
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/1/2/3/4/5/some-installer.iso",
> +                expected => {
> +                    content => 'iso',
> +                    ctime => $DEFAULT_CTIME,
> +                    format => 'iso',
> +                    size => $DEFAULT_SIZE,
> +                    volid => 'local:iso/1/2/3/4/5/some-installer.iso',
> +                },
> +            },
> +            {
> +                # Exceeds max-scan-depth
> +                file => "$DEFAULT_STORAGE_PATH/template/iso/1/2/3/4/5/6/some-installer.iso",
> +                expected => undef,
> +            },
> +        ],
> +    },
>  ];
> 
>  # Additional test cases that cannot be constructed within the list above
> diff --git a/src/test/parse_volname_test.pm b/src/test/parse_volname_test.pm
> index 1dcec9d6..24d1ed0e 100644
> --- a/src/test/parse_volname_test.pm
> +++ b/src/test/parse_volname_test.pm
> @@ -167,10 +167,66 @@ my $tests = [
>                  volname => "iso/$file_name",
>                  expected => ['iso', "$file_name", undef, undef, undef, undef, 'raw'],
>              },
> +            {
> +                description => "ISO image, $suffix, subdirectory",
> +                volname => "iso/foo/$file_name",
> +                expected => [
> +                    'iso', "foo/$file_name", undef, undef, undef, undef, 'raw',
> +                ],
> +            },
> +            {
> +                description => "ISO image, $suffix, nested subdirectories",
> +                volname => "iso/foo/bar/baz/$file_name",
> +                expected => [
> +                    'iso', "foo/bar/baz/$file_name", undef, undef, undef, undef, 'raw',
> +                ],
> +            },
> +            {
> +                description => "ISO image, $suffix, subdirectory with same name as file",
> +                volname => "iso/$file_name/$file_name",
> +                expected => [
> +                    'iso', "$file_name/$file_name", undef, undef, undef, undef, 'raw',
> +                ],
> +            },
>          );
> 
>          push($tests->@*, @extra_tests);
>      }
> +
> +    # Failed tests
> +    {
> +        my $file_name = "$prefix.iso";
> +
> +        my @extra_failed_tests = (
> +            {
> +                description =>
> +                    "ISO image, iso, parent directory reference before volume type prefix",
> +                volname => "../iso/$file_name",
> +                expected => "unable to parse directory volume name '../iso/$file_name'\n",
> +            },
> +            {
> +                description =>
> +                    "ISO image, iso, parent directory reference at beginning of volume path",
> +                volname => "iso/../$file_name",
> +                expected => "unable to parse directory volume name 'iso/../$file_name'\n",
> +            },
> +            {
> +                description =>
> +                    "ISO image, iso, parent directory reference at end of volume path",
> +                volname => "iso/$file_name/..",
> +                expected => "unable to parse directory volume name 'iso/$file_name/..'\n",
> +            },
> +            {
> +                description =>
> +                    "ISO image, iso, parent directory reference between dir components of volume path",
> +                volname => "iso/foo/../bar/$file_name",
> +                expected =>
> +                    "unable to parse directory volume name 'iso/foo/../bar/$file_name'\n",
> +            },
> +        );
> +
> +        push($tests->@*, @extra_failed_tests);
> +    }
>  }
> 
>  # Test cases for container templates
> diff --git a/src/test/path_to_volume_id_test.pm b/src/test/path_to_volume_id_test.pm
> index 6c7d0d67..bc87d289 100644
> --- a/src/test/path_to_volume_id_test.pm
> +++ b/src/test/path_to_volume_id_test.pm
> @@ -123,6 +123,13 @@ my $tests = [
>              'iso', 'local:iso/yet-again-a-installation-disk.iso',
>          ],
>      },
> +    {
> +        description => 'ISO file, nested subdirectories',
> +        file => "$DEFAULT_STORAGE_DIR/template/iso/foo/bar/hannah-montana-linux-installer.iso",
> +        expected => [
> +            'iso', 'local:iso/foo/bar/hannah-montana-linux-installer.iso',
> +        ],
> +    },
>      {
>          description => 'CT template, tar.gz',
>          file => "$DEFAULT_STORAGE_DIR/template/cache/debian-10.0-standard_10.0-1_amd64.tar.gz",
> --
> 2.47.3
> 
> 
> 
> 
> 

-- 




  reply	other threads:[~2026-07-02 14:56 UTC|newest]

Thread overview: 69+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-04-22 11:12 [PATCH pve-storage, pve-manager v1 00/54] Fix #2884: Implement Subdirectory Scanning for Dir-Based Storage Types Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 01/54] test: plugin tests: run tests with at most 4 jobs Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 02/54] plugin, common: remove superfluous use of =pod command paragraph Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 03/54] common: add POD headings for groups of helpers Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 04/54] common: use Exporter module for PVE::Storage::Common Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 05/54] plugin: make get_subdir_files a proper subroutine and update style Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 06/54] plugin api: replace helpers w/ standalone subs, bump API version & age Max R. Carrara
2026-04-28 15:01   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 07/54] common: prevent autovivification in plugin_get_vtype_subdir helper Max R. Carrara
2026-04-28 15:02   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 08/54] plugin: break up needless if-elsif chain into separate if-blocks Max R. Carrara
2026-04-28 15:04   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 09/54] plugin: adapt get_subdir_files helper of list_volumes API method Max R. Carrara
2026-04-28 15:07   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 10/54] plugin: update code style of list_volumes plugin " Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 11/54] plugin: use closure for obtaining raw volume data in list_volumes Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 12/54] plugin: use closure for inner loop logic " Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 13/54] storage: update code style in function path_to_volume_id Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 14/54] storage: break up needless if-elsif chain in path_to_volume_id Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 15/54] storage: heave vtype file path parsing logic inside loop into helper Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 16/54] storage: clean up code that was moved into helper in path_to_volume_id Max R. Carrara
2026-04-28 15:08   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 17/54] api: status: move content type assert for up-/downloads into helper Max R. Carrara
2026-04-28 15:10   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 18/54] api: status: use helper from common module to get content directory Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 19/54] api: status: move up-/download file path parsing code into helper Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 20/54] api: status: simplify file content assertion logic for up-/download Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 21/54] test: guest import: add tests for PVE::GuestImport Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 22/54] tree-wide: introduce parsing module and replace usages of ISO_EXT_RE_0 Max R. Carrara
2026-04-28 15:20   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 23/54] common: test: set up parser testing code, add tests for 'iso' vtype Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 24/54] tree-wide: replace usages of VZTMPL_EXT_RE_1 with parsing functions Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 25/54] tree-wide: replace usages of BACKUP_EXT_RE_2 " Max R. Carrara
2026-06-30 14:06   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 26/54] tree-wide: replace usages of inline regexes for snippets with parsers Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 27/54] tree-wide: partially replace usages of regexes for 'import' vtype Max R. Carrara
2026-06-30 14:19   ` Wolfgang Bumiller
2026-06-30 14:44     ` Wolfgang Bumiller
2026-06-30 14:28   ` Wolfgang Bumiller
2026-04-22 11:12 ` [PATCH pve-storage v1 28/54] tree-wide: replace remaining " Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 29/54] plugin: simplify recently refactored logic in parse_volname method Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 30/54] plugin: simplify recently refactored logic in get_subdir_files helper Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 31/54] storage: simplify recently refactored logic in path_to_volume_id sub Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 32/54] api: status: simplify recently added parsing helper for file transfers Max R. Carrara
2026-04-22 11:12 ` [PATCH pve-storage v1 33/54] plugin: use parsing helper in parse_volume_id sub Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 34/54] test: list volumes: reorganize and modernize test running code Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 35/54] test: list volumes: fix broken test checking for vmlist modifications Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 36/54] test: list volumes: introduce new format for test cases Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 37/54] test: list volumes: remove legacy code and migrate cases to new format Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 38/54] test: list volumes: document behavior wrt. undeclared content types Max R. Carrara
2026-07-02 12:46   ` Wolfgang Bumiller
2026-04-22 11:13 ` [PATCH pve-storage v1 39/54] plugin: correct comment in get_subdir_files helper Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 40/54] test: parse volname: modernize code Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 41/54] test: parse volname: adapt tests regarding 'import' volume type Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 42/54] test: parse volname: move VM disk test creation into separate block Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 43/54] test: parse volname: move backup file test creation into sep. block Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 44/54] test: parse volname: parameterize test case creation for some vtypes Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 45/54] test: volume id: modernize code Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 46/54] test: volume id: rename 'volname' test case parameter to 'file' Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 47/54] test: filesystem path: modernize code Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 48/54] fix #2884: implement nested subdir scanning and support 'iso' vtype Max R. Carrara
2026-07-02 14:55   ` Wolfgang Bumiller [this message]
2026-04-22 11:13 ` [PATCH pve-storage v1 49/54] fix #2884: support nested subdir scanning for 'vztmpl' volume type Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 50/54] fix #2884: support nested subdir scanning for 'snippets' vtype Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 51/54] test: add more tests for 'import' vtype & guard against nested subdirs Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 52/54] test: add tests guarding against subdir scanning for vtypes Max R. Carrara
2026-04-22 11:13 ` [PATCH pve-storage v1 53/54] storage api: mark old public regexes for removal, bump APIVER & APIAGE Max R. Carrara
2026-04-28 15:22   ` Wolfgang Bumiller
2026-04-22 11:13 ` [PATCH pve-manager v1 54/54] fix #2884: ui: storage: add field for 'max-scan-depth' property Max R. Carrara

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=cvjtmro3odmdgh574wnfql3nlaezezukokbsuxdnmycadfcuah@4ckwm5hn2bci \
    --to=w.bumiller@proxmox.com \
    --cc=m.carrara@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal