diff --git a/okio-testing-support/src/commonMain/kotlin/okio/AbstractFileSystemTest.kt b/okio-testing-support/src/commonMain/kotlin/okio/AbstractFileSystemTest.kt index a716133364..063610183b 100644 --- a/okio-testing-support/src/commonMain/kotlin/okio/AbstractFileSystemTest.kt +++ b/okio-testing-support/src/commonMain/kotlin/okio/AbstractFileSystemTest.kt @@ -2183,7 +2183,7 @@ abstract class AbstractFileSystemTest( } @Test - fun symlinkMetadata() { + fun absoluteSymlinkMetadata() { if (!supportsSymlink()) return val target = base / "symlink-target" @@ -2198,6 +2198,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 diff --git a/okio/src/jvmMain/kotlin/okio/NioFileSystemWrappingFileSystem.kt b/okio/src/jvmMain/kotlin/okio/NioFileSystemWrappingFileSystem.kt index cab8ab51b2..b281531348 100644 --- a/okio/src/jvmMain/kotlin/okio/NioFileSystemWrappingFileSystem.kt +++ b/okio/src/jvmMain/kotlin/okio/NioFileSystemWrappingFileSystem.kt @@ -18,16 +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 @@ -38,14 +40,21 @@ 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() + val symlink = resolved.readSymbolicLink() + // We have to resolve the symlink against its source in order to retrieve the directory in which it has been + // originally created. + if (resolved.parent != null && !symlink.isAbsolute) { + resolved.parent.resolve(symlink.toString()) + } else { + symlink + } } else { resolved } @@ -55,7 +64,7 @@ internal class NioFileSystemWrappingFileSystem(private val nioFileSystem: NioFil try { return path.resolve(readSymlink = true).toRealPath().toOkioPath() } catch (e: NoSuchFileException) { - throw FileNotFoundException("no such file: $this") + throw FileNotFoundException("no such file: $path") } } @@ -161,8 +170,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, @@ -181,7 +189,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) { @@ -190,14 +198,7 @@ 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"