* [pve-devel] [PATCH pve-storage/pve-manager 1/3 v2] recursively go through subdirs to find files
2023-05-16 14:51 [pve-devel] [PATCH pve-storage/pve-manager 0/3 v2] fix #623: show isos/vztmpl/snippets in subdirs Noel Ullreich
@ 2023-05-16 14:51 ` Noel Ullreich
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 2/3 v2] add `subdir-depth` option to filesystems Noel Ullreich
` (2 subsequent siblings)
3 siblings, 0 replies; 5+ messages in thread
From: Noel Ullreich @ 2023-05-16 14:51 UTC (permalink / raw)
To: pve-devel
This patch allows `get_subdir_files` to recursively call itself, so that
subdirectories of set depth can be searched. We allow searching for
isos, vztmpl and snippets but not backups.
As a security measure, when parsing a given path, parent
directories (`/../`) are forbidden.
The feature is opt-in, i.e. the searchdepth is 0 by default. It can be
changed via the API, the web interface and `pvesm` (see the other
patches).
Signed-off-by: Noel Ullreich <n.ullreich@proxmox.com>
---
PVE/Storage.pm | 7 ++++++
PVE/Storage/Plugin.pm | 54 ++++++++++++++++++++++++++-----------------
2 files changed, 40 insertions(+), 21 deletions(-)
diff --git a/PVE/Storage.pm b/PVE/Storage.pm
index cec3996..0097a53 100755
--- a/PVE/Storage.pm
+++ b/PVE/Storage.pm
@@ -113,6 +113,13 @@ our $VZTMPL_EXT_RE_1 = qr/\.tar\.(gz|xz|zst)/i;
our $BACKUP_EXT_RE_2 = qr/\.(tgz|(?:tar|vma)(?:\.(${\PVE::Storage::Plugin::COMPRESSOR_RE}))?)/;
+# '..' is forbidden at the beginning, between two '/' and at the end
+my $dots = quotemeta('..');
+my $beginning = qr!^$dots/!;
+my $between = qr!/$dots/!;
+my $end = qr!/$dots$!;
+our $forbidden_double_dots_re = qr!(?:$beginning|$between|$end)!;
+
# FIXME remove with PVE 8.0, add versioned breaks for pve-manager
our $vztmpl_extension_re = $VZTMPL_EXT_RE_1;
diff --git a/PVE/Storage/Plugin.pm b/PVE/Storage/Plugin.pm
index c323085..e5b53b0 100644
--- a/PVE/Storage/Plugin.pm
+++ b/PVE/Storage/Plugin.pm
@@ -614,6 +614,8 @@ sub parse_name_dir {
sub parse_volname {
my ($class, $volname) = @_;
+ die "volname must not contain parent directories '/../'\n" if $volname =~ $PVE::Storage::forbidden_double_dots_re;
+
if ($volname =~ m!^(\d+)/(\S+)/(\d+)/(\S+)$!) {
my ($basedvmid, $basename) = ($1, $2);
parse_name_dir($basename);
@@ -624,9 +626,9 @@ sub parse_volname {
my ($vmid, $name) = ($1, $2);
my (undef, $format, $isBase) = parse_name_dir($name);
return ('images', $name, $vmid, undef, undef, $isBase, $format);
- } elsif ($volname =~ m!^iso/([^/]+$PVE::Storage::ISO_EXT_RE_0)$!) {
+ } elsif ($volname =~ m!^iso/((?:[0-9A-z\_\-\.]+\/)*[^\/]+$PVE::Storage::ISO_EXT_RE_0)$!) {
return ('iso', $1);
- } elsif ($volname =~ m!^vztmpl/([^/]+$PVE::Storage::VZTMPL_EXT_RE_1)$!) {
+ } elsif ($volname =~ m!^vztmpl/((?:[0-9A-z\_\-\.]+\/)*[^\/]+$PVE::Storage::VZTMPL_EXT_RE_1)$!) {
return ('vztmpl', $1);
} elsif ($volname =~ m!^rootdir/(\d+)$!) {
return ('rootdir', $1, $1);
@@ -636,7 +638,7 @@ sub parse_volname {
return ('backup', $fn, $2);
}
return ('backup', $fn);
- } elsif ($volname =~ m!^snippets/([^/]+)$!) {
+ } elsif ($volname =~ m!^snippets/((?:[0-9A-z\_\-\.]+\/)*[^\/]+)$!) {
return ('snippets', $1);
}
@@ -1205,28 +1207,33 @@ sub list_images {
}
# list templates ($tt = <iso|vztmpl|backup|snippets>)
-my $get_subdir_files = sub {
- my ($sid, $path, $tt, $vmid) = @_;
+sub get_subdir_files {
+ my ($sid, $path, $tt, $scfg, $vmid, $remaining_depth) = @_;
+ my $storage_path = $scfg->{path};
+ my $content_dir = $scfg->{"content-dirs"}->{$tt} // $vtype_subdirs->{$tt};
my $res = [];
foreach my $fn (<$path/*>) {
- my $st = File::stat::stat($fn);
+ my $st = File::stat::lstat($fn);
+
+ next if (!$st);
- next if (!$st || S_ISDIR($st->mode));
+ if (S_ISDIR($st->mode)) {
+ if ($remaining_depth) {
+ push @$res, get_subdir_files($sid, $fn, $tt, $scfg, $vmid, $remaining_depth-1);
+ }
+ next;
+ }
my $info;
if ($tt eq 'iso') {
- next if $fn !~ m!/([^/]+$PVE::Storage::ISO_EXT_RE_0)$!i;
-
+ next if $fn !~ m/(?:^$storage_path\/$content_dir\/)((?:[0-9A-z\_\-\.]+\/)*[^\/]+$PVE::Storage::ISO_EXT_RE_0)/;
$info = { volid => "$sid:iso/$1", format => 'iso' };
-
} elsif ($tt eq 'vztmpl') {
- next if $fn !~ m!/([^/]+$PVE::Storage::VZTMPL_EXT_RE_1)$!;
-
+ next if $fn !~ m/(?:^$storage_path\/$content_dir\/)((?:[0-9A-z\_\-\.]+\/)*[^\/]+$PVE::Storage::VZTMPL_EXT_RE_1)/;
$info = { volid => "$sid:vztmpl/$1", format => "t$2" };
-
} elsif ($tt eq 'backup') {
next if $fn !~ m!/([^/]+$PVE::Storage::BACKUP_EXT_RE_2)$!;
my $original = $fn;
@@ -1255,7 +1262,7 @@ my $get_subdir_files = sub {
$info->{protected} = 1 if -e PVE::Storage::protection_file_path($original);
} elsif ($tt eq 'snippets') {
-
+ next if $fn !~ m/(?:^$storage_path\/$content_dir\/)((?:[0-9A-z\_\-\.]+\/)*.+)/;
$info = {
volid => "$sid:snippets/". basename($fn),
format => 'snippet',
@@ -1267,14 +1274,18 @@ my $get_subdir_files = sub {
push @$res, $info;
}
-
return $res;
};
+sub flatten {
+ map { ref eq 'ARRAY' ? flatten(@{$_}) : $_ } @_;
+}
+
# If attributes are set on a volume, they should be included in the result.
# See get_volume_attribute for a list of possible attributes.
sub list_volumes {
my ($class, $storeid, $scfg, $vmid, $content_types) = @_;
+ my $max_depth = $scfg->{'subdir-depth'} // 0;
my $res = [];
my $vmlist = PVE::Cluster::get_vmlist();
@@ -1287,17 +1298,19 @@ sub list_volumes {
my $path = $class->get_subdir($scfg, $type);
if ($type eq 'iso' && !defined($vmid)) {
- $data = $get_subdir_files->($storeid, $path, 'iso');
+ $data = get_subdir_files($storeid, $path, 'iso', $scfg, undef, $max_depth);
} elsif ($type eq 'vztmpl'&& !defined($vmid)) {
- $data = $get_subdir_files->($storeid, $path, 'vztmpl');
+ $data = get_subdir_files($storeid, $path , 'vztmpl', $scfg, undef, $max_depth);
} elsif ($type eq 'backup') {
- $data = $get_subdir_files->($storeid, $path, 'backup', $vmid);
+ $data = get_subdir_files($storeid, $path, 'backup', $scfg, $vmid, $max_depth);
} elsif ($type eq 'snippets') {
- $data = $get_subdir_files->($storeid, $path, 'snippets');
+ $data = get_subdir_files($storeid, $path, 'snippets', $scfg, undef, $max_depth);
}
}
- next if !$data;
+ $data = [flatten($data)];
+
+ next if !@$data[0];
foreach my $item (@$data) {
if ($type eq 'images' || $type eq 'rootdir') {
@@ -1315,7 +1328,6 @@ sub list_volumes {
} else {
$item->{content} = $type;
}
-
push @$res, $item;
}
}
--
2.30.2
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pve-devel] [PATCH pve-storage/pve-manager 2/3 v2] add `subdir-depth` option to filesystems
2023-05-16 14:51 [pve-devel] [PATCH pve-storage/pve-manager 0/3 v2] fix #623: show isos/vztmpl/snippets in subdirs Noel Ullreich
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 1/3 v2] recursively go through subdirs to find files Noel Ullreich
@ 2023-05-16 14:51 ` Noel Ullreich
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 3/3 v2] update test for recursive subdir search Noel Ullreich
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 1/1 v2] add field to set subdir-depth in web interface Noel Ullreich
3 siblings, 0 replies; 5+ messages in thread
From: Noel Ullreich @ 2023-05-16 14:51 UTC (permalink / raw)
To: pve-devel
Add the `subdir-depth` to the filesystems that can hold
isos/vztmpl/snippets.
Signed-off-by: Noel Ullreich <n.ullreich@proxmox.com>
---
PVE/Storage/CIFSPlugin.pm | 1 +
PVE/Storage/CephFSPlugin.pm | 1 +
PVE/Storage/DirPlugin.pm | 1 +
PVE/Storage/GlusterfsPlugin.pm | 1 +
PVE/Storage/NFSPlugin.pm | 1 +
PVE/Storage/Plugin.pm | 7 +++++++
6 files changed, 12 insertions(+)
diff --git a/PVE/Storage/CIFSPlugin.pm b/PVE/Storage/CIFSPlugin.pm
index e03226d..77096a2 100644
--- a/PVE/Storage/CIFSPlugin.pm
+++ b/PVE/Storage/CIFSPlugin.pm
@@ -152,6 +152,7 @@ sub options {
mkdir => { optional => 1 },
bwlimit => { optional => 1 },
preallocation => { optional => 1 },
+ 'subdir-depth' => { optional => 1},
};
}
diff --git a/PVE/Storage/CephFSPlugin.pm b/PVE/Storage/CephFSPlugin.pm
index db0c2f6..bf1a046 100644
--- a/PVE/Storage/CephFSPlugin.pm
+++ b/PVE/Storage/CephFSPlugin.pm
@@ -154,6 +154,7 @@ sub options {
'prune-backups' => { optional => 1 },
'max-protected-backups' => { optional => 1 },
'fs-name' => { optional => 1 },
+ 'subdir-depth' => { optional => 1},
};
}
diff --git a/PVE/Storage/DirPlugin.pm b/PVE/Storage/DirPlugin.pm
index 9e305f5..3b9ffba 100644
--- a/PVE/Storage/DirPlugin.pm
+++ b/PVE/Storage/DirPlugin.pm
@@ -67,6 +67,7 @@ sub options {
is_mountpoint => { optional => 1 },
bwlimit => { optional => 1 },
preallocation => { optional => 1 },
+ 'subdir-depth' => { optional => 1},
};
}
diff --git a/PVE/Storage/GlusterfsPlugin.pm b/PVE/Storage/GlusterfsPlugin.pm
index ad386d2..7e774f8 100644
--- a/PVE/Storage/GlusterfsPlugin.pm
+++ b/PVE/Storage/GlusterfsPlugin.pm
@@ -139,6 +139,7 @@ sub options {
mkdir => { optional => 1 },
bwlimit => { optional => 1 },
preallocation => { optional => 1 },
+ 'subdir-depth' => { optional => 1},
};
}
diff --git a/PVE/Storage/NFSPlugin.pm b/PVE/Storage/NFSPlugin.pm
index c2d4176..7c4cc1a 100644
--- a/PVE/Storage/NFSPlugin.pm
+++ b/PVE/Storage/NFSPlugin.pm
@@ -93,6 +93,7 @@ sub options {
mkdir => { optional => 1 },
bwlimit => { optional => 1 },
preallocation => { optional => 1 },
+ 'subdir-depth' => { optional => 1},
};
}
diff --git a/PVE/Storage/Plugin.pm b/PVE/Storage/Plugin.pm
index e5b53b0..885276e 100644
--- a/PVE/Storage/Plugin.pm
+++ b/PVE/Storage/Plugin.pm
@@ -191,6 +191,13 @@ my $defaultData = {
type => "string", format => "pve-dir-override-list",
optional => 1,
},
+ 'subdir-depth' => {
+ description => "Maximal depth of subdirectories to look though when
+ searching for isos/container templates/snippets in a directory",
+ type => 'integer',
+ default => 0,
+ optional => 1,
+ }
},
};
--
2.30.2
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pve-devel] [PATCH pve-storage/pve-manager 3/3 v2] update test for recursive subdir search
2023-05-16 14:51 [pve-devel] [PATCH pve-storage/pve-manager 0/3 v2] fix #623: show isos/vztmpl/snippets in subdirs Noel Ullreich
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 1/3 v2] recursively go through subdirs to find files Noel Ullreich
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 2/3 v2] add `subdir-depth` option to filesystems Noel Ullreich
@ 2023-05-16 14:51 ` Noel Ullreich
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 1/1 v2] add field to set subdir-depth in web interface Noel Ullreich
3 siblings, 0 replies; 5+ messages in thread
From: Noel Ullreich @ 2023-05-16 14:51 UTC (permalink / raw)
To: pve-devel
Tests now also test if traversing subdirectories works. That means
checking if:
- parent directories in paths are caught
- checking that nested files are found
- checking that files below the maximum depth set are not found
Signed-off-by: Noel Ullreich <n.ullreich@proxmox.com>
---
test/filesystem_path_test.pm | 18 ++++++++++
test/list_volumes_test.pm | 68 ++++++++++++++++++++++++++++++++++++
test/parse_volname_test.pm | 40 +++++++++++++++++++++
3 files changed, 126 insertions(+)
diff --git a/test/filesystem_path_test.pm b/test/filesystem_path_test.pm
index c1b6d90..35a1d4e 100644
--- a/test/filesystem_path_test.pm
+++ b/test/filesystem_path_test.pm
@@ -56,6 +56,24 @@ my $tests = [
'iso'
],
},
+ {
+ volname => 'iso/a/b/c/my-awesome-proxmox.iso',
+ snapname => undef,
+ expected => [
+ "$path/template/iso/a/b/c/my-awesome-proxmox.iso",
+ undef,
+ 'iso'
+ ],
+ },
+ {
+ volname => 'vztmpl/a/b/c/my-awesome-template.tar.gz',
+ snapname => undef,
+ expected => [
+ "$path/template/cache/a/b/c/my-awesome-template.tar.gz",
+ undef,
+ 'vztmpl'
+ ],
+ },
{
volname => "backup/vzdump-qemu-1234-2020_03_30-21_12_40.vma",
snapname => undef,
diff --git a/test/list_volumes_test.pm b/test/list_volumes_test.pm
index d155cb9..523639b 100644
--- a/test/list_volumes_test.pm
+++ b/test/list_volumes_test.pm
@@ -74,6 +74,7 @@ my $scfg = {
'snippets' => 1,
'backup' => 1,
},
+ 'subdir-depth' => 10,
};
# The test cases are comprised of an arry of hashes with the following keys:
@@ -448,6 +449,73 @@ my @tests = (
],
expected => [], # returns empty list
},
+ {
+ descripton => 'VMID: none, subdirectory',
+ # find files in subdirectories, test if maximum depth is working
+ vmid => undef,
+ files => [
+ "$storage_dir/template/cache/dir1/dir2/uwuntu-3.10-default_20190626_amd64.tar.xz",
+ "$storage_dir/template/iso/a/b/c/installation-disk.iso",
+ "$storage_dir/template/iso/a/b/c/d.d/yet-again-an-installation-disk.iso",
+ "$storage_dir/template/iso/a/b/template/iso/c/d/another-installation-disk.iso",
+ "$storage_dir/template/iso/1/2/3/4/5/6/7/8/9/10//wunkus.iso", # test depth
+ "$storage_dir/template/iso/1/2/3/4/5/6/7/8/9/10/11/wunkus.iso", # this should not match
+ "$storage_dir/template/iso/a/../somedir/little.iso",
+ "$storage_dir/template/iso/a/../../somedir/little.iso", # this should not match
+ "$storage_dir/snippets/a/b/c/d/userconfig.yaml",
+ ],
+ expected => [
+ {
+ 'content' => 'vztmpl',
+ 'ctime' => DEFAULT_CTIME,
+ 'format' => 'txz',
+ 'size' => DEFAULT_SIZE,
+ 'volid' => 'local:vztmpl/dir1/dir2/uwuntu-3.10-default_20190626_amd64.tar.xz',
+ },
+ {
+ 'content' => 'iso',
+ 'ctime' => DEFAULT_CTIME,
+ 'format' => 'iso',
+ 'size' => DEFAULT_SIZE,
+ 'volid' => 'local:iso/1/2/3/4/5/6/7/8/9/10/wunkus.iso',
+ },
+ {
+ 'content' => 'iso',
+ 'ctime' => DEFAULT_CTIME,
+ 'format' => 'iso',
+ 'size' => DEFAULT_SIZE,
+ 'volid' => 'local:iso/a/b/c/d.d/yet-again-an-installation-disk.iso',
+ },
+ {
+ 'content' => 'iso',
+ 'ctime' => DEFAULT_CTIME,
+ 'format' => 'iso',
+ 'size' => DEFAULT_SIZE,
+ 'volid' => 'local:iso/a/b/c/installation-disk.iso',
+ },
+ {
+ 'content' => 'iso',
+ 'ctime' => DEFAULT_CTIME,
+ 'format' => 'iso',
+ 'size' => DEFAULT_SIZE,
+ 'volid' => 'local:iso/a/b/template/iso/c/d/another-installation-disk.iso',
+ },
+ {
+ 'content' => 'iso',
+ 'ctime' => DEFAULT_CTIME,
+ 'format' => 'iso',
+ 'size' => DEFAULT_SIZE,
+ 'volid' => 'local:iso/somedir/little.iso',
+ },
+ {
+ 'content' => 'snippets',
+ 'ctime' => DEFAULT_CTIME,
+ 'format' => 'snippet',
+ 'size' => DEFAULT_SIZE,
+ 'volid' => 'local:snippets/a/b/c/d/userconfig.yaml',
+ },
+ ],
+ }
);
diff --git a/test/parse_volname_test.pm b/test/parse_volname_test.pm
index d6ac885..737ae9e 100644
--- a/test/parse_volname_test.pm
+++ b/test/parse_volname_test.pm
@@ -31,11 +31,26 @@ my $tests = [
volname => 'iso/some-installation-disk.iso',
expected => ['iso', 'some-installation-disk.iso'],
},
+ {
+ description => 'ISO image, iso, nested',
+ volname => 'iso/a/b/c/some-installation-disk.iso',
+ expected => ['iso', 'a/b/c/some-installation-disk.iso'],
+ },
{
description => 'ISO image, img',
volname => 'iso/some-other-installation-disk.img',
expected => ['iso', 'some-other-installation-disk.img'],
},
+ {
+ description => 'ISO image, img, nested',
+ volname => 'iso/a/b/c/some-other-installation-disk.img',
+ expected => ['iso', 'a/b/c/some-other-installation-disk.img'],
+ },
+ {
+ description => 'ISO image, img, nested with dots',
+ volname => 'iso/a/b.b/c/some-other-installation-disk.img',
+ expected => ['iso', 'a/b.b/c/some-other-installation-disk.img'],
+ },
#
# container templates
#
@@ -44,11 +59,21 @@ my $tests = [
volname => 'vztmpl/debian-10.0-standard_10.0-1_amd64.tar.gz',
expected => ['vztmpl', 'debian-10.0-standard_10.0-1_amd64.tar.gz'],
},
+ {
+ description => 'Container template tar.gz nested',
+ volname => 'vztmpl/a/b/c/debian-10.0-standard_10.0-1_amd64.tar.gz',
+ expected => ['vztmpl', 'a/b/c/debian-10.0-standard_10.0-1_amd64.tar.gz'],
+ },
{
description => 'Container template tar.xz',
volname => 'vztmpl/debian-10.0-standard_10.0-1_amd64.tar.xz',
expected => ['vztmpl', 'debian-10.0-standard_10.0-1_amd64.tar.xz'],
},
+ {
+ description => 'Container template tar.xz nested',
+ volname => 'vztmpl/a/b/c/debian-10.0-standard_10.0-1_amd64.tar.xz',
+ expected => ['vztmpl', 'a/b/c/debian-10.0-standard_10.0-1_amd64.tar.xz'],
+ },
#
# container rootdir
#
@@ -123,6 +148,21 @@ my $tests = [
volname => "$vmid/base-$vmid-disk-0.qcow2/ssss/vm-$vmid-disk-0.qcow2",
expected => "unable to parse volume filename 'base-$vmid-disk-0.qcow2/ssss/vm-$vmid-disk-0.qcow2'\n",
},
+ {
+ description => 'Failed match: ISO image, forbidden double dots between two /',
+ volname => 'iso/a/../c/some-installation-disk.iso',
+ expected => "volname must not contain parent directories '/../'\n",
+ },
+ {
+ description => 'Failed match: ISO image, forbidden double dots in the beginning',
+ volname => '../iso/some-installation-disk.iso',
+ expected => "volname must not contain parent directories '/../'\n",
+ },
+ {
+ description => 'Failed match: ISO image, forbidden double dots in the end',
+ volname => 'iso/some-installation-disk.iso/..',
+ expected => "volname must not contain parent directories '/../'\n",
+ },
];
# create more test cases for VM disk images matches
--
2.30.2
^ permalink raw reply [flat|nested] 5+ messages in thread
* [pve-devel] [PATCH pve-storage/pve-manager 1/1 v2] add field to set subdir-depth in web interface
2023-05-16 14:51 [pve-devel] [PATCH pve-storage/pve-manager 0/3 v2] fix #623: show isos/vztmpl/snippets in subdirs Noel Ullreich
` (2 preceding siblings ...)
2023-05-16 14:51 ` [pve-devel] [PATCH pve-storage/pve-manager 3/3 v2] update test for recursive subdir search Noel Ullreich
@ 2023-05-16 14:51 ` Noel Ullreich
3 siblings, 0 replies; 5+ messages in thread
From: Noel Ullreich @ 2023-05-16 14:51 UTC (permalink / raw)
To: pve-devel
When adding or editing a storage device in Datacenter->Storage in the
web interface, the subdirectory depth can be set in the advanced menu.
Signed-off-by: Noel Ullreich <n.ullreich@proxmox.com>
---
www/manager6/storage/Base.js | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/www/manager6/storage/Base.js b/www/manager6/storage/Base.js
index c8c735f3..c6844426 100644
--- a/www/manager6/storage/Base.js
+++ b/www/manager6/storage/Base.js
@@ -48,6 +48,7 @@ Ext.define('PVE.panel.StorageBase', {
name: 'enable',
checked: true,
uncheckedValue: 0,
+ minValue: 0,
fieldLabel: gettext('Enable'),
},
);
@@ -63,13 +64,23 @@ Ext.define('PVE.panel.StorageBase', {
deleteEmpty: !me.isCreate,
value: '__default__',
};
+ const recursionDepth = {
+ xtype: 'proxmoxintegerfield',
+ name: 'subdir-depth',
+ fieldLabel: gettext('Subdirectory Depth'),
+ allowBlank: false,
+ value: 0,
+ minValue: 0,
+ };
me.advancedColumn1 = me.advancedColumn1 || [];
me.advancedColumn2 = me.advancedColumn2 || [];
if (me.advancedColumn2.length < me.advancedColumn1.length) {
me.advancedColumn2.unshift(preallocSelector);
+ me.advancedColumn2.unshift(recursionDepth);
} else {
me.advancedColumn1.unshift(preallocSelector);
+ me.advancedColumn1.unshift(recursionDepth);
}
}
--
2.30.2
^ permalink raw reply [flat|nested] 5+ messages in thread