Skip to content

Commit 02fb84e

Browse files
committedApr 21, 2023
Catch trying to create a directory at/under non-directory
1 parent 36500b5 commit 02fb84e

File tree

3 files changed

+40
-28
lines changed

3 files changed

+40
-28
lines changed
 

‎src/main/kotlin/com/coder/gateway/CoderSettingsConfigurable.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package com.coder.gateway
22

33
import com.coder.gateway.sdk.CoderCLIManager
4-
import com.coder.gateway.sdk.canWrite
4+
import com.coder.gateway.sdk.canCreateDirectory
55
import com.coder.gateway.services.CoderSettingsState
66
import com.intellij.openapi.components.service
77
import com.intellij.openapi.options.BoundConfigurable
@@ -45,8 +45,8 @@ class CoderSettingsConfigurable : BoundConfigurable("Coder") {
4545
}
4646

4747
private fun validateBinaryDestination(): ValidationInfoBuilder.(JBTextField) -> ValidationInfo? = {
48-
if (it.text.isNotBlank() && !Path.of(it.text).canWrite()) {
49-
error("Cannot write to this path")
48+
if (it.text.isNotBlank() && !Path.of(it.text).canCreateDirectory()) {
49+
error("Cannot create this directory")
5050
} else {
5151
null
5252
}

‎src/main/kotlin/com/coder/gateway/sdk/PathExtensions.kt

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@ import java.nio.file.Files
44
import java.nio.file.Path
55

66
/**
7-
* Return true if the path can be created.
7+
* Return true if a directory can be created at the specified path or if one
8+
* already exists and we can write into it.
89
*
9-
* Unlike File.canWrite() or Files.isWritable() the file does not need to exist;
10-
* it only needs a writable parent.
10+
* Unlike File.canWrite() or Files.isWritable() the directory does not need to
11+
* exist; it only needs a writable parent and the target needs to be
12+
* non-existent or a directory (not a regular file or nested under one).
1113
*/
12-
fun Path.canWrite(): Boolean {
14+
fun Path.canCreateDirectory(): Boolean {
1315
var current: Path? = this.toAbsolutePath()
1416
while (current != null && !Files.exists(current)) {
1517
current = current.parent
1618
}
1719
// On Windows File.canWrite() only checks read-only while Files.isWritable()
1820
// actually checks permissions.
19-
return current != null && Files.isWritable(current)
21+
return current != null && Files.isWritable(current) && Files.isDirectory(current)
2022
}

‎src/test/groovy/PathExtensionsTest.groovy

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,35 +12,45 @@ class PathExtensionsTest extends Specification {
1212
@Shared
1313
private Path tmpdir = Path.of(System.getProperty("java.io.tmpdir"))
1414
@Shared
15-
private Path unwritable = tmpdir.resolve("coder-gateway-test/path-extensions/unwritable")
15+
private Path unwritableFile = tmpdir.resolve("coder-gateway-test/path-extensions/unwritable/file")
16+
@Shared
17+
private Path writableFile = tmpdir.resolve("coder-gateway-test/path-extensions/writable-file")
1618

1719
void setupSpec() {
18-
if (unwritable.parent.toFile().exists()) {
19-
unwritable.parent.toFile().setWritable(true)
20-
unwritable.parent.toFile().deleteDir()
20+
if (unwritableFile.parent.toFile().exists()) {
21+
unwritableFile.parent.toFile().setWritable(true)
22+
unwritableFile.parent.toFile().deleteDir()
2123
}
22-
Files.createDirectories(unwritable.parent)
23-
unwritable.toFile().write("text")
24-
unwritable.toFile().setWritable(false)
25-
unwritable.parent.toFile().setWritable(false)
24+
Files.createDirectories(unwritableFile.parent)
25+
unwritableFile.toFile().write("text")
26+
writableFile.toFile().write("text")
27+
unwritableFile.toFile().setWritable(false)
28+
unwritableFile.parent.toFile().setWritable(false)
2629
}
2730

28-
def "canWrite"() {
31+
def "canCreateDirectory"() {
2932
expect:
3033
use(PathExtensionsKt) {
31-
path.canWrite() == expected
34+
path.canCreateDirectory() == expected
3235
}
3336

3437
where:
35-
path | expected
36-
unwritable | false
37-
unwritable.resolve("probably/nonexistent") | false
38-
Path.of("relative to project") | true
39-
tmpdir.resolve("./foo/bar/../..") | true
40-
tmpdir | true
41-
tmpdir.resolve("some/nested/non-existent/path") | true
42-
tmpdir.resolve("with space") | true
43-
CoderCLIManager.getConfigDir() | true
44-
CoderCLIManager.getDataDir() | true
38+
path | expected
39+
unwritableFile | false
40+
unwritableFile.resolve("probably/nonexistent") | false
41+
// Unfortunately on Windows directories Java will always tell you the
42+
// directory is writable even if it is not.
43+
unwritableFile.parent.resolve("probably/nonexistent") | System.getProperty("os.name").toLowerCase().contains("windows")
44+
writableFile | false
45+
writableFile.parent | true
46+
writableFile.resolve("nested/under/file") | false
47+
writableFile.parent.resolve("nested/under/dir") | true
48+
Path.of("relative to project") | true
49+
tmpdir.resolve("./foo/bar/../../coder-gateway-test/path-extensions") | true
50+
tmpdir | true
51+
tmpdir.resolve("some/nested/non-existent/path") | true
52+
tmpdir.resolve("with space") | true
53+
CoderCLIManager.getConfigDir() | true
54+
CoderCLIManager.getDataDir() | true
4555
}
4656
}

0 commit comments

Comments
 (0)