Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
What's new
10
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Open sidebar
cld
systems
cassandra
Commits
0a1e8d16
Commit
0a1e8d16
authored
Jun 12, 2020
by
Sam Tunnicliffe
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'cassandra-2.2' into cassandra-3.0
parents
c092c461
c8c3c269
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
69 additions
and
65 deletions
+69
-65
CHANGES.txt
CHANGES.txt
+1
-0
src/java/org/apache/cassandra/cql3/functions/UDFunction.java
src/java/org/apache/cassandra/cql3/functions/UDFunction.java
+11
-11
src/java/org/apache/cassandra/db/Directories.java
src/java/org/apache/cassandra/db/Directories.java
+14
-14
src/java/org/apache/cassandra/db/DisallowedDirectories.java
src/java/org/apache/cassandra/db/DisallowedDirectories.java
+15
-14
src/java/org/apache/cassandra/db/DisallowedDirectoriesMBean.java
...a/org/apache/cassandra/db/DisallowedDirectoriesMBean.java
+2
-1
src/java/org/apache/cassandra/db/compaction/AbstractCompactionStrategy.java
...e/cassandra/db/compaction/AbstractCompactionStrategy.java
+3
-3
src/java/org/apache/cassandra/db/compaction/LeveledManifest.java
...a/org/apache/cassandra/db/compaction/LeveledManifest.java
+1
-1
src/java/org/apache/cassandra/hints/HintsDispatchExecutor.java
...ava/org/apache/cassandra/hints/HintsDispatchExecutor.java
+1
-1
src/java/org/apache/cassandra/hints/HintsStore.java
src/java/org/apache/cassandra/hints/HintsStore.java
+5
-5
src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
...org/apache/cassandra/io/sstable/format/SSTableReader.java
+1
-1
src/java/org/apache/cassandra/service/DefaultFSErrorHandler.java
...a/org/apache/cassandra/service/DefaultFSErrorHandler.java
+3
-3
test/unit/org/apache/cassandra/Util.java
test/unit/org/apache/cassandra/Util.java
+2
-2
test/unit/org/apache/cassandra/db/DirectoriesTest.java
test/unit/org/apache/cassandra/db/DirectoriesTest.java
+1
-1
test/unit/org/apache/cassandra/db/compaction/CorruptedSSTablesCompactionsTest.java
...andra/db/compaction/CorruptedSSTablesCompactionsTest.java
+9
-8
No files found.
CHANGES.txt
View file @
0a1e8d16
...
...
@@ -14,6 +14,7 @@
* Memtable memory allocations may deadlock (CASSANDRA-15367)
* Run evictFromMembership in GossipStage (CASSANDRA-15592)
Merged from 2.2:
* Fix nomenclature of allow and deny lists (CASSANDRA-15862)
* Remove generated files from source artifact (CASSANDRA-15849)
* Remove duplicated tools binaries from tarballs (CASSANDRA-15768)
* Duplicate results with DISTINCT queries in mixed mode (CASSANDRA-15501)
...
...
src/java/org/apache/cassandra/cql3/functions/UDFunction.java
View file @
0a1e8d16
...
...
@@ -76,19 +76,19 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
protected
final
boolean
calledOnNullInput
;
//
// Access to classes is controlled via a
whitelist and a black
list.
// Access to classes is controlled via a
llow and disallow
list
s
.
//
// When a class is requested (both during compilation and runtime),
// the
whitelist
edPatterns array is searched first, whether the
// the
allow
edPatterns array is searched first, whether the
// requested name matches one of the patterns. If not, nothing is
// returned from the class-loader - meaning ClassNotFoundException
// during runtime and "type could not resolved" during compilation.
//
// If a
whitelist
ed pattern has been found, the
blacklist
edPatterns
// If a
n allow
ed pattern has been found, the
disallow
edPatterns
// array is searched for a match. If a match is found, class-loader
// rejects access. Otherwise the class/resource can be loaded.
//
private
static
final
String
[]
whitelist
edPatterns
=
private
static
final
String
[]
allow
edPatterns
=
{
"com/datastax/driver/core/"
,
"com/google/common/reflect/TypeToken"
,
...
...
@@ -110,8 +110,8 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
"org/apache/cassandra/cql3/functions/JavaUDF.class"
,
"org/apache/cassandra/exceptions/"
,
};
// Only need to
blacklist
a pattern, if it would otherwise be allowed via
whitelist
edPatterns
private
static
final
String
[]
blacklist
edPatterns
=
// Only need to
disallow
a pattern, if it would otherwise be allowed via
allow
edPatterns
private
static
final
String
[]
disallow
edPatterns
=
{
"com/datastax/driver/core/Cluster.class"
,
"com/datastax/driver/core/Metrics.class"
,
...
...
@@ -154,13 +154,13 @@ public abstract class UDFunction extends AbstractFunction implements ScalarFunct
while
(
resource
.
startsWith
(
"/"
))
resource
=
resource
.
substring
(
1
);
for
(
String
white
:
whitelist
edPatterns
)
if
(
resource
.
startsWith
(
white
))
for
(
String
allowed
:
allow
edPatterns
)
if
(
resource
.
startsWith
(
allowed
))
{
// resource is in
whitelist
edPatterns, let's see if it is not explicit
yl blacklist
ed
for
(
String
black
:
blacklist
edPatterns
)
if
(
resource
.
startsWith
(
black
))
// resource is in
allow
edPatterns, let's see if it is not explicit
ly disallow
ed
for
(
String
disallowed
:
disallow
edPatterns
)
if
(
resource
.
startsWith
(
disallowed
))
{
logger
.
trace
(
"access denied: resource {}"
,
resource
);
return
false
;
...
...
src/java/org/apache/cassandra/db/Directories.java
View file @
0a1e8d16
...
...
@@ -310,10 +310,10 @@ public class Directories
/**
* Basically the same as calling {@link #getWriteableLocationAsFile(long)} with an unknown size ({@code -1L}),
* which may return any
non-blacklist
ed directory - even a data directory that has no usable space.
* which may return any
allow
ed directory - even a data directory that has no usable space.
* Do not use this method in production code.
*
* @throws IOError if all directories are bl
a
ck
list
ed.
* @throws IOError if all directories are bl
o
cked.
*/
public
File
getDirectoryForNewSSTables
()
{
...
...
@@ -321,9 +321,9 @@ public class Directories
}
/**
* Returns a
non-blacklisted data
directory that _currently_ has {@code writeSize} bytes as usable space.
* Returns a
n allowed
directory that _currently_ has {@code writeSize} bytes as usable space.
*
* @throws IOError if all directories are
blacklist
ed.
* @throws IOError if all directories are
disallow
ed.
*/
public
File
getWriteableLocationAsFile
(
long
writeSize
)
{
...
...
@@ -331,11 +331,11 @@ public class Directories
}
/**
* Returns a temporary subdirectory on
non-blacklist
ed data directory
* Returns a temporary subdirectory on
allow
ed data directory
* that _currently_ has {@code writeSize} bytes as usable space.
* This method does not create the temporary directory.
*
* @throws IOError if all directories are
blacklist
ed.
* @throws IOError if all directories are
disallow
ed.
*/
public
File
getTemporaryWriteableDirectoryAsFile
(
long
writeSize
)
{
...
...
@@ -359,9 +359,9 @@ public class Directories
}
/**
* Returns a
non-blacklist
ed data directory that _currently_ has {@code writeSize} bytes as usable space.
* Returns a
n allow
ed data directory that _currently_ has {@code writeSize} bytes as usable space.
*
* @throws IOError if all directories are
blacklist
ed.
* @throws IOError if all directories are
disallow
ed.
*/
public
DataDirectory
getWriteableLocation
(
long
writeSize
)
{
...
...
@@ -369,13 +369,13 @@ public class Directories
long
totalAvailable
=
0L
;
// pick directories with enough space and so that resulting sstable dirs aren't
blacklist
ed for writes.
// pick directories with enough space and so that resulting sstable dirs aren't
disallow
ed for writes.
boolean
tooBig
=
false
;
for
(
DataDirectory
dataDir
:
paths
)
{
if
(
Blacklist
edDirectories
.
isUnwritable
(
getLocationForDisk
(
dataDir
)))
if
(
Disallow
edDirectories
.
isUnwritable
(
getLocationForDisk
(
dataDir
)))
{
logger
.
trace
(
"removing
blacklist
ed candidate {}"
,
dataDir
.
location
);
logger
.
trace
(
"removing
disallow
ed candidate {}"
,
dataDir
.
location
);
continue
;
}
DataDirectoryCandidate
candidate
=
new
DataDirectoryCandidate
(
dataDir
);
...
...
@@ -394,7 +394,7 @@ public class Directories
if
(
tooBig
)
throw
new
FSDiskFullWriteError
(
new
IOException
(
"Insufficient disk space to write "
+
writeSize
+
" bytes"
),
""
);
else
throw
new
FSWriteError
(
new
IOException
(
"All configured data directories have been
blacklist
ed as unwritable for erroring out"
),
""
);
throw
new
FSWriteError
(
new
IOException
(
"All configured data directories have been
disallow
ed as unwritable for erroring out"
),
""
);
// shortcut for single data directory systems
if
(
candidates
.
size
()
==
1
)
...
...
@@ -439,7 +439,7 @@ public class Directories
for
(
DataDirectory
dataDir
:
paths
)
{
if
(
Blacklist
edDirectories
.
isUnwritable
(
getLocationForDisk
(
dataDir
)))
if
(
Disallow
edDirectories
.
isUnwritable
(
getLocationForDisk
(
dataDir
)))
continue
;
DataDirectoryCandidate
candidate
=
new
DataDirectoryCandidate
(
dataDir
);
// exclude directory if its total writeSize does not fit to data directory
...
...
@@ -691,7 +691,7 @@ public class Directories
for
(
File
location
:
dataPaths
)
{
if
(
Blacklist
edDirectories
.
isUnreadable
(
location
))
if
(
Disallow
edDirectories
.
isUnreadable
(
location
))
continue
;
if
(
snapshotName
!=
null
)
...
...
src/java/org/apache/cassandra/db/
Blacklist
edDirectories.java
→
src/java/org/apache/cassandra/db/
Disallow
edDirectories.java
View file @
0a1e8d16
...
...
@@ -27,21 +27,22 @@ import java.util.concurrent.CopyOnWriteArraySet;
import
com.google.common.annotations.VisibleForTesting
;
import
org.apache.cassandra.utils.JVMStabilityInspector
;
import
org.apache.cassandra.utils.MBeanWrapper
;
public
class
Blacklist
edDirectories
implements
Blacklist
edDirectoriesMBean
public
class
Disallow
edDirectories
implements
Disallow
edDirectoriesMBean
{
public
static
final
String
MBEAN_NAME
=
"org.apache.cassandra.db:type=BlacklistedDirectories"
;
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
BlacklistedDirectories
.
class
);
private
static
final
BlacklistedDirectories
instance
=
new
BlacklistedDirectories
();
public
static
final
String
DEPRECATED_MBEAN_NAME
=
"org.apache.cassandra.db:type=BlacklistedDirectories"
;
public
static
final
String
MBEAN_NAME
=
"org.apache.cassandra.db:type=DisallowedDirectories"
;
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
DisallowedDirectories
.
class
);
private
static
final
DisallowedDirectories
instance
=
new
DisallowedDirectories
();
private
final
Set
<
File
>
unreadableDirectories
=
new
CopyOnWriteArraySet
<
File
>();
private
final
Set
<
File
>
unwritableDirectories
=
new
CopyOnWriteArraySet
<
File
>();
private
Blacklist
edDirectories
()
private
Disallow
edDirectories
()
{
// Register this instance with JMX
MBeanWrapper
.
instance
.
registerMBean
(
this
,
DEPRECATED_MBEAN_NAME
,
MBeanWrapper
.
OnException
.
LOG
);
MBeanWrapper
.
instance
.
registerMBean
(
this
,
MBEAN_NAME
,
MBeanWrapper
.
OnException
.
LOG
);
}
...
...
@@ -59,14 +60,14 @@ public class BlacklistedDirectories implements BlacklistedDirectoriesMBean
* Adds parent directory of the file (or the file itself, if it is a directory)
* to the set of unreadable directories.
*
* @return the
blacklist
ed directory or null if nothing has been added to the list.
* @return the
disallow
ed directory or null if nothing has been added to the list.
*/
public
static
File
maybeMarkUnreadable
(
File
path
)
{
File
directory
=
getDirectory
(
path
);
if
(
instance
.
unreadableDirectories
.
add
(
directory
))
{
logger
.
warn
(
"
Blacklist
ing {} for reads"
,
directory
);
logger
.
warn
(
"
Disallow
ing {} for reads"
,
directory
);
return
directory
;
}
return
null
;
...
...
@@ -76,14 +77,14 @@ public class BlacklistedDirectories implements BlacklistedDirectoriesMBean
* Adds parent directory of the file (or the file itself, if it is a directory)
* to the set of unwritable directories.
*
* @return the
blacklist
ed directory or null if nothing has been added to the list.
* @return the
disallow
ed directory or null if nothing has been added to the list.
*/
public
static
File
maybeMarkUnwritable
(
File
path
)
{
File
directory
=
getDirectory
(
path
);
if
(
instance
.
unwritableDirectories
.
add
(
directory
))
{
logger
.
warn
(
"
Blacklist
ing {} for writes"
,
directory
);
logger
.
warn
(
"
Disallow
ing {} for writes"
,
directory
);
return
directory
;
}
return
null
;
...
...
@@ -101,8 +102,8 @@ public class BlacklistedDirectories implements BlacklistedDirectoriesMBean
/**
* Tells whether or not the directory is
blacklist
ed for reads.
* @return whether or not the directory is
blacklist
ed for reads.
* Tells whether or not the directory is
disallow
ed for reads.
* @return whether or not the directory is
disallow
ed for reads.
*/
public
static
boolean
isUnreadable
(
File
directory
)
{
...
...
@@ -110,8 +111,8 @@ public class BlacklistedDirectories implements BlacklistedDirectoriesMBean
}
/**
* Tells whether or not the directory is
blacklist
ed for writes.
* @return whether or not the directory is
blacklist
ed for reads.
* Tells whether or not the directory is
disallow
ed for writes.
* @return whether or not the directory is
disallow
ed for reads.
*/
public
static
boolean
isUnwritable
(
File
directory
)
{
...
...
src/java/org/apache/cassandra/db/
Blacklist
edDirectoriesMBean.java
→
src/java/org/apache/cassandra/db/
Disallow
edDirectoriesMBean.java
View file @
0a1e8d16
...
...
@@ -20,7 +20,8 @@ package org.apache.cassandra.db;
import
java.io.File
;
import
java.util.Set
;
public
interface
BlacklistedDirectoriesMBean
{
public
interface
DisallowedDirectoriesMBean
{
public
Set
<
File
>
getUnreadableDirectories
();
...
...
src/java/org/apache/cassandra/db/compaction/AbstractCompactionStrategy.java
View file @
0a1e8d16
...
...
@@ -247,10 +247,10 @@ public abstract class AbstractCompactionStrategy
}
/**
* Filters SSTables that are to be
blacklist
ed from the given collection
* Filters SSTables that are to be
exclud
ed from the given collection
*
* @param originalCandidates The collection to check for
blacklist
ed SSTables
* @return list of the SSTables with
blacklist
ed ones filtered out
* @param originalCandidates The collection to check for
exclud
ed SSTables
* @return list of the SSTables with
exclud
ed ones filtered out
*/
public
static
List
<
SSTableReader
>
filterSuspectSSTables
(
Iterable
<
SSTableReader
>
originalCandidates
)
{
...
...
src/java/org/apache/cassandra/db/compaction/LeveledManifest.java
View file @
0a1e8d16
...
...
@@ -576,7 +576,7 @@ public class LeveledManifest
/**
* @return highest-priority sstables to compact for the given level.
* If no compactions are possible (because of concurrent compactions or because some sstables are
blacklist
ed
* If no compactions are possible (because of concurrent compactions or because some sstables are
exclud
ed
* for prior failure), will return an empty list. Never returns null.
*/
private
Collection
<
SSTableReader
>
getCandidatesFor
(
int
level
)
...
...
src/java/org/apache/cassandra/hints/HintsDispatchExecutor.java
View file @
0a1e8d16
...
...
@@ -242,7 +242,7 @@ final class HintsDispatchExecutor
{
logger
.
error
(
"Failed to dispatch hints file {}: file is corrupted ({})"
,
descriptor
.
fileName
(),
e
);
store
.
cleanUp
(
descriptor
);
store
.
blacklist
(
descriptor
);
store
.
markCorrupted
(
descriptor
);
throw
e
;
}
}
...
...
src/java/org/apache/cassandra/hints/HintsStore.java
View file @
0a1e8d16
...
...
@@ -52,7 +52,7 @@ final class HintsStore
private
final
Map
<
HintsDescriptor
,
InputPosition
>
dispatchPositions
;
private
final
Deque
<
HintsDescriptor
>
dispatchDequeue
;
private
final
Queue
<
HintsDescriptor
>
blacklis
tedFiles
;
private
final
Queue
<
HintsDescriptor
>
corrup
tedFiles
;
// last timestamp used in a descriptor; make sure to not reuse the same timestamp for new descriptors.
private
volatile
long
lastUsedTimestamp
;
...
...
@@ -66,7 +66,7 @@ final class HintsStore
dispatchPositions
=
new
ConcurrentHashMap
<>();
dispatchDequeue
=
new
ConcurrentLinkedDeque
<>(
descriptors
);
blacklis
tedFiles
=
new
ConcurrentLinkedQueue
<>();
corrup
tedFiles
=
new
ConcurrentLinkedQueue
<>();
//noinspection resource
lastUsedTimestamp
=
descriptors
.
stream
().
mapToLong
(
d
->
d
.
timestamp
).
max
().
orElse
(
0L
);
...
...
@@ -119,7 +119,7 @@ final class HintsStore
delete
(
descriptor
);
}
while
((
descriptor
=
blacklis
tedFiles
.
poll
())
!=
null
)
while
((
descriptor
=
corrup
tedFiles
.
poll
())
!=
null
)
{
cleanUp
(
descriptor
);
delete
(
descriptor
);
...
...
@@ -158,9 +158,9 @@ final class HintsStore
dispatchPositions
.
remove
(
descriptor
);
}
void
blacklist
(
HintsDescriptor
descriptor
)
void
markCorrupted
(
HintsDescriptor
descriptor
)
{
blacklis
tedFiles
.
add
(
descriptor
);
corrup
tedFiles
.
add
(
descriptor
);
}
/*
...
...
src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java
View file @
0a1e8d16
...
...
@@ -1687,7 +1687,7 @@ public abstract class SSTableReader extends SSTable implements SelfRefCounted<SS
public
void
markSuspect
()
{
if
(
logger
.
isTraceEnabled
())
logger
.
trace
(
"Marking {} as a suspect
for blacklisting
."
,
getFilename
());
logger
.
trace
(
"Marking {} as a suspect
to be excluded from reads
."
,
getFilename
());
isSuspect
.
getAndSet
(
true
);
}
...
...
src/java/org/apache/cassandra/service/DefaultFSErrorHandler.java
View file @
0a1e8d16
...
...
@@ -24,7 +24,7 @@ import org.slf4j.Logger;
import
org.slf4j.LoggerFactory
;
import
org.apache.cassandra.config.DatabaseDescriptor
;
import
org.apache.cassandra.db.
Blacklist
edDirectories
;
import
org.apache.cassandra.db.
Disallow
edDirectories
;
import
org.apache.cassandra.db.Keyspace
;
import
org.apache.cassandra.io.FSError
;
import
org.apache.cassandra.io.FSErrorHandler
;
...
...
@@ -66,10 +66,10 @@ public class DefaultFSErrorHandler implements FSErrorHandler
break
;
case
best_effort:
// for both read and write errors mark the path as unwritable.
Blacklist
edDirectories
.
maybeMarkUnwritable
(
e
.
path
);
Disallow
edDirectories
.
maybeMarkUnwritable
(
e
.
path
);
if
(
e
instanceof
FSReadError
)
{
File
directory
=
Blacklist
edDirectories
.
maybeMarkUnreadable
(
e
.
path
);
File
directory
=
Disallow
edDirectories
.
maybeMarkUnreadable
(
e
.
path
);
if
(
directory
!=
null
)
Keyspace
.
removeUnreadableSSTables
(
directory
);
}
...
...
test/unit/org/apache/cassandra/Util.java
View file @
0a1e8d16
...
...
@@ -639,13 +639,13 @@ public class Util
for
(
;
;
)
{
DataDirectory
dir
=
cfs
.
getDirectories
().
getWriteableLocation
(
1
);
Blacklist
edDirectories
.
maybeMarkUnwritable
(
cfs
.
getDirectories
().
getLocationForDisk
(
dir
));
Disallow
edDirectories
.
maybeMarkUnwritable
(
cfs
.
getDirectories
().
getLocationForDisk
(
dir
));
}
}
catch
(
IOError
e
)
{
// Expected -- marked all directories as unwritable
}
return
()
->
Blacklist
edDirectories
.
clearUnwritableUnsafe
();
return
()
->
Disallow
edDirectories
.
clearUnwritableUnsafe
();
}
}
test/unit/org/apache/cassandra/db/DirectoriesTest.java
View file @
0a1e8d16
...
...
@@ -338,7 +338,7 @@ public class DirectoriesTest
for
(
DataDirectory
dd
:
Directories
.
dataDirectories
)
{
File
file
=
new
File
(
dd
.
location
,
new
File
(
KS
,
"bad"
).
getPath
());
assertTrue
(
Blacklist
edDirectories
.
isUnwritable
(
file
));
assertTrue
(
Disallow
edDirectories
.
isUnwritable
(
file
));
}
}
finally
...
...
test/unit/org/apache/cassandra/db/compaction/
Blacklisting
CompactionsTest.java
→
test/unit/org/apache/cassandra/db/compaction/
CorruptedSSTables
CompactionsTest.java
View file @
0a1e8d16
...
...
@@ -47,13 +47,13 @@ import org.apache.cassandra.schema.*;
import
static
org
.
junit
.
Assert
.
assertTrue
;
public
class
Blacklisting
CompactionsTest
public
class
CorruptedSSTables
CompactionsTest
{
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
Blacklisting
CompactionsTest
.
class
);
private
static
final
Logger
logger
=
LoggerFactory
.
getLogger
(
CorruptedSSTables
CompactionsTest
.
class
);
private
static
Random
random
;
private
static
final
String
KEYSPACE1
=
"
Blacklisting
CompactionsTest"
;
private
static
final
String
KEYSPACE1
=
"
CorruptedSSTables
CompactionsTest"
;
private
static
final
String
STANDARD_STCS
=
"Standard_STCS"
;
private
static
final
String
STANDARD_LCS
=
"Standard_LCS"
;
private
static
int
maxValueSize
;
...
...
@@ -112,18 +112,19 @@ public class BlacklistingCompactionsTest
}
@Test
public
void
test
Blacklisting
WithSizeTieredCompactionStrategy
()
throws
Exception
public
void
test
CorruptedSSTables
WithSizeTieredCompactionStrategy
()
throws
Exception
{
test
Blacklisting
(
STANDARD_STCS
);
test
CorruptedSSTables
(
STANDARD_STCS
);
}
@Test
public
void
test
Blacklisting
WithLeveledCompactionStrategy
()
throws
Exception
public
void
test
CorruptedSSTables
WithLeveledCompactionStrategy
()
throws
Exception
{
test
Blacklisting
(
STANDARD_LCS
);
test
CorruptedSSTables
(
STANDARD_LCS
);
}
private
void
testBlacklisting
(
String
tableName
)
throws
Exception
public
void
testCorruptedSSTables
(
String
tableName
)
throws
Exception
{
// this test does enough rows to force multiple block indexes to be used
Keyspace
keyspace
=
Keyspace
.
open
(
KEYSPACE1
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment