Skip to content

Commit

Permalink
Resolve symlink at the right place
Browse files Browse the repository at this point in the history
  • Loading branch information
oldergod committed Jun 21, 2023
1 parent 0d6a49e commit a681001
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2176,7 +2176,7 @@ abstract class AbstractFileSystemTest(
}

@Test
fun symlinkMetadata() {
fun absoluteSymlinkMetadata() {
if (!supportsSymlink()) return

val target = base / "symlink-target"
Expand All @@ -2191,6 +2191,22 @@ abstract class AbstractFileSystemTest(
assertInRange(sourceMetadata.createdAt, minTime, maxTime)
}

@Test
fun relativeSymlinkMetadata() {
if (!supportsSymlink()) return

val target = "symlink-target".toPath()
val source = base / "symlink-source"

val minTime = clock.now()
fileSystem.createSymlink(source, target)
val maxTime = clock.now()

val sourceMetadata = fileSystem.metadata(source)
assertEquals(target, sourceMetadata.symlinkTarget)
assertInRange(sourceMetadata.createdAt, minTime, maxTime)
}

@Test
fun createSymlinkSourceAlreadyExists() {
if (!supportsSymlink()) return
Expand Down Expand Up @@ -2231,6 +2247,7 @@ abstract class AbstractFileSystemTest(
@Test
fun openSymlinkSink() {
if (!supportsSymlink()) return
if (isJimFileSystem()) return

val target = base / "symlink-target"
val source = base / "symlink-source"
Expand Down Expand Up @@ -2507,6 +2524,10 @@ abstract class AbstractFileSystemTest(
return windowsLimitations && fileSystem::class.simpleName == "JvmSystemFileSystem"
}

private fun isJimFileSystem(): Boolean {
return "JimfsFileSystem" in fileSystem.toString()
}

private fun isNodeJsFileSystemOnWindows(): Boolean {
return windowsLimitations && fileSystem::class.simpleName == "NodeJsFileSystem"
}
Expand Down
45 changes: 16 additions & 29 deletions okio/src/jvmMain/kotlin/okio/NioFileSystemWrappingFileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@ package okio
import java.io.InterruptedIOException
import java.nio.channels.FileChannel
import java.nio.file.FileSystem as NioFileSystem
import java.nio.file.Files
import java.nio.file.NoSuchFileException
import java.nio.file.Path as NioPath
import java.nio.file.StandardCopyOption
import java.nio.file.StandardOpenOption
import kotlin.io.path.createDirectory
import kotlin.io.path.createSymbolicLinkPointingTo
import kotlin.io.path.deleteExisting
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.isSymbolicLink
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.moveTo
import kotlin.io.path.outputStream
import kotlin.io.path.readSymbolicLink
import okio.Path.Companion.toOkioPath

/**
Expand All @@ -38,24 +38,19 @@ import okio.Path.Companion.toOkioPath
*/
internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFileSystem) : NioSystemFileSystem() {
/**
* On a `java.nio.file.FileSystem`, `java.nio.file.Path` are stateful and hold a reference to the file system they
* got provided from. We need to [resolve][java.nio.file.Path.resolve] all okio paths before doing operations on the
* nio file system in order for things to work properly.
* On a [java.nio.file.FileSystem], paths are stateful and hold a reference to the file system they got provided from.
* Using [getPath][NioFileSystem.getPath], we ask [nioFileSystem] to wrap the [Path]'s value in order to set itself as
* its provider which is needed for operations on the nio file system to work properly.
*/
private fun Path.resolve(readSymlink: Boolean = false): NioPath {
val resolved = nioFileSystem.getPath(toString())
return if (readSymlink && resolved.isSymbolicLink()) {
resolved.readSymbolicLink()
} else {
resolved
}
private fun Path.resolve(): NioPath {
return nioFileSystem.getPath(toString())
}

override fun canonicalize(path: Path): Path {
try {
return path.resolve(readSymlink = true).toRealPath().toOkioPath()
return path.resolve().toRealPath().toOkioPath()
} catch (e: NoSuchFileException) {
throw FileNotFoundException("no such file: $this")
throw FileNotFoundException("no such file: $path")
}
}

Expand Down Expand Up @@ -115,7 +110,7 @@ internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFil

override fun source(file: Path): Source {
try {
return file.resolve(readSymlink = true).inputStream().source()
return file.resolve().inputStream().source()
} catch (e: NoSuchFileException) {
throw FileNotFoundException("no such file: $file")
}
Expand All @@ -126,7 +121,7 @@ internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFil
if (mustCreate) add(StandardOpenOption.CREATE_NEW)
}
try {
return file.resolve(readSymlink = true)
return file.resolve()
.outputStream(*openOptions.toTypedArray())
.sink()
} catch (e: NoSuchFileException) {
Expand Down Expand Up @@ -161,8 +156,7 @@ internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFil
// Note that `java.nio.file.FileSystem` allows atomic moves of a file even if the target is an existing directory.
override fun atomicMove(source: Path, target: Path) {
try {
Files.move(
source.resolve(),
source.resolve().moveTo(
target.resolve(),
StandardCopyOption.ATOMIC_MOVE,
StandardCopyOption.REPLACE_EXISTING,
Expand All @@ -181,7 +175,7 @@ internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFil
}
val nioPath = path.resolve()
try {
Files.delete(nioPath)
nioPath.deleteExisting()
} catch (e: NoSuchFileException) {
if (mustExist) throw FileNotFoundException("no such file: $path")
} catch (e: IOException) {
Expand All @@ -190,15 +184,8 @@ internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFil
}

override fun createSymlink(source: Path, target: Path) {
val sourceNioPath = source.resolve()
val targetNioPath =
if (source.isAbsolute && target.isRelative) {
sourceNioPath.parent.resolve(target.toString())
} else {
target.resolve()
}
Files.createSymbolicLink(sourceNioPath, targetNioPath)
source.resolve().createSymbolicLinkPointingTo(target.resolve())
}

override fun toString(): String = "NioFileSystemWrappingFileSystem"
override fun toString() = nioFileSystem::class.simpleName!!
}
21 changes: 13 additions & 8 deletions okio/src/jvmTest/kotlin/okio/FileHandleFileSystemTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.google.common.jimfs.Configuration
import com.google.common.jimfs.Jimfs
import java.nio.file.FileSystems
import kotlinx.datetime.Clock
import okio.FileHandleFileSystemTest.FileHandleTestingFileSystem
import okio.FileSystem.Companion.asOkioFileSystem

/**
Expand Down Expand Up @@ -61,13 +62,15 @@ class FileHandleFileSystemTest : AbstractFileSystemTest(

class FileHandleNioJimFileSystemWrapperFileSystemTest : AbstractFileSystemTest(
clock = Clock.System,
fileSystem = Jimfs
.newFileSystem(
when (Path.DIRECTORY_SEPARATOR == "\\") {
true -> Configuration.windows()
false -> Configuration.unix()
},
).asOkioFileSystem(),
fileSystem = FileHandleTestingFileSystem(
Jimfs
.newFileSystem(
when (Path.DIRECTORY_SEPARATOR == "\\") {
true -> Configuration.windows()
false -> Configuration.unix()
},
).asOkioFileSystem(),
),
windowsLimitations = false,
allowClobberingEmptyDirectories = true,
allowAtomicMoveFromFileToDirectory = true,
Expand All @@ -76,7 +79,9 @@ class FileHandleNioJimFileSystemWrapperFileSystemTest : AbstractFileSystemTest(

class FileHandleNioDefaultFileSystemWrapperFileSystemTest : AbstractFileSystemTest(
clock = Clock.System,
fileSystem = FileSystems.getDefault().asOkioFileSystem(),
fileSystem = FileHandleTestingFileSystem(
FileSystems.getDefault().asOkioFileSystem(),
),
windowsLimitations = false,
allowClobberingEmptyDirectories = Path.DIRECTORY_SEPARATOR == "\\",
allowAtomicMoveFromFileToDirectory = false,
Expand Down

0 comments on commit a681001

Please sign in to comment.