Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions okio/src/mingwX64Main/kotlin/okio/Windows.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
*/
package okio

import kotlinx.cinterop.ByteVarOf
import kotlinx.cinterop.UShortVarOf
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import platform.windows.DWORD
import platform.windows.ERROR_FILE_NOT_FOUND
import platform.windows.ERROR_PATH_NOT_FOUND
import platform.windows.FORMAT_MESSAGE_FROM_SYSTEM
import platform.windows.FORMAT_MESSAGE_IGNORE_INSERTS
import platform.windows.FormatMessageA
import platform.windows.FormatMessageW
import platform.windows.GetLastError
import platform.windows.LANG_NEUTRAL
import platform.windows.SUBLANG_DEFAULT
Expand All @@ -39,8 +40,8 @@ internal fun lastErrorToIOException(): IOException {
internal fun lastErrorString(lastError: DWORD): String {
memScoped {
val messageMaxSize = 2048
val message = allocArray<ByteVarOf<Byte>>(messageMaxSize)
FormatMessageA(
val message = allocArray<UShortVarOf<UShort>>(messageMaxSize)
FormatMessageW(
dwFlags = (FORMAT_MESSAGE_FROM_SYSTEM or FORMAT_MESSAGE_IGNORE_INSERTS).toUInt(),
lpSource = null,
dwMessageId = lastError,
Expand All @@ -49,6 +50,6 @@ internal fun lastErrorString(lastError: DWORD): String {
nSize = messageMaxSize.toUInt(),
Arguments = null,
)
return Buffer().writeNullTerminated(message).readUtf8().trim()
return message.toKString()
}
}
92 changes: 66 additions & 26 deletions okio/src/mingwX64Main/kotlin/okio/WindowsPosixVariant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ package okio

import kotlinx.cinterop.CPointer
import kotlinx.cinterop.alloc
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.toKString
import kotlinx.cinterop.wcstr
import okio.Path.Companion.toPath
import platform.posix.EACCES
import platform.posix.ENOENT
Expand All @@ -28,38 +30,44 @@ import platform.posix.PATH_MAX
import platform.posix.S_IFDIR
import platform.posix.S_IFMT
import platform.posix.S_IFREG
import platform.posix._fullpath
import platform.posix._stat64
import platform.posix._wclosedir
import platform.posix._wdirent
import platform.posix._wfopen
import platform.posix._wfullpath
import platform.posix._wgetenv
import platform.posix._wmkdir
import platform.posix._wopendir
import platform.posix._wreaddir
import platform.posix._wremove
import platform.posix._wrmdir
import platform.posix._wstat64
import platform.posix.errno
import platform.posix.fopen
import platform.posix.free
import platform.posix.getenv
import platform.posix.mkdir
import platform.posix.remove
import platform.posix.rmdir
import platform.posix.set_posix_errno
import platform.windows.CREATE_NEW
import platform.windows.CreateFileA
import platform.windows.CreateFileW
import platform.windows.FILE_ATTRIBUTE_NORMAL
import platform.windows.FILE_SHARE_WRITE
import platform.windows.GENERIC_READ
import platform.windows.GENERIC_WRITE
import platform.windows.INVALID_HANDLE_VALUE
import platform.windows.MOVEFILE_REPLACE_EXISTING
import platform.windows.MoveFileExA
import platform.windows.MoveFileExW
import platform.windows.OPEN_ALWAYS
import platform.windows.OPEN_EXISTING

internal actual val PLATFORM_TEMPORARY_DIRECTORY: Path
get() {
// Windows' built-in APIs check the TEMP, TMP, and USERPROFILE environment variables in order.
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-gettemppatha?redirectedfrom=MSDN
val temp = getenv("TEMP")
val temp = _wgetenv("TEMP".wcstr)
if (temp != null) return temp.toKString().toPath()

val tmp = getenv("TMP")
val tmp = _wgetenv("TMP".wcstr)
if (tmp != null) return tmp.toKString().toPath()

val userProfile = getenv("USERPROFILE")
val userProfile = _wgetenv("USERPROFILE".wcstr)
if (userProfile != null) return userProfile.toKString().toPath()

return "\\Windows\\TEMP".toPath()
Expand All @@ -68,13 +76,13 @@ internal actual val PLATFORM_TEMPORARY_DIRECTORY: Path
internal actual val PLATFORM_DIRECTORY_SEPARATOR = "\\"

internal actual fun PosixFileSystem.variantDelete(path: Path, mustExist: Boolean) {
val pathString = path.toString()
val pathString = path.toString().wcstr

if (remove(pathString) == 0) return
if (_wremove(pathString) == 0) return

// If remove failed with EACCES, it might be a directory. Try that.
if (errno == EACCES) {
if (rmdir(pathString) == 0) return
if (_wrmdir(pathString) == 0) return
}
if (errno == ENOENT) {
if (mustExist) {
Expand All @@ -87,20 +95,52 @@ internal actual fun PosixFileSystem.variantDelete(path: Path, mustExist: Boolean
throw errnoToIOException(EACCES)
}

internal actual fun PosixFileSystem.variantList(dir: Path, throwOnFailure: Boolean): List<Path>? {
val opendir = _wopendir(dir.toString().wcstr)
?: if (throwOnFailure) throw errnoToIOException(errno) else return null

try {
val result = mutableListOf<Path>()
set_posix_errno(0) // If readdir() returns null it's either the end or an error.
while (true) {
val dirent: CPointer<_wdirent> = _wreaddir(opendir) ?: break
val childPath = dirent[0].d_name.toKString().toPath()

if (childPath == SELF_DIRECTORY_ENTRY || childPath == PARENT_DIRECTORY_ENTRY) {
continue // exclude '.' and '..' from the results.
}

result += dir / childPath
}

if (errno != 0) {
if (throwOnFailure) {
throw errnoToIOException(errno)
} else {
return null
}
}

result.sort()
return result
} finally {
_wclosedir(opendir) // Ignore errno from closedir.
}
}

internal actual fun PosixFileSystem.variantMkdir(dir: Path): Int {
return mkdir(dir.toString())
return _wmkdir(dir.toString().wcstr)
}

internal actual fun PosixFileSystem.variantCanonicalize(path: Path): Path {
// Note that _fullpath() returns normally if the file doesn't exist.
val fullpath = _fullpath(null, path.toString(), PATH_MAX.toULong())
val fullpath = _wfullpath(null, path.toString().wcstr, PATH_MAX.toULong())
?: throw errnoToIOException(errno)
try {
val pathString = Buffer().writeNullTerminated(fullpath).readUtf8()
if (platform.posix.access(pathString, 0) != 0 && errno == ENOENT) {
if (platform.posix._waccess(fullpath, 0) != 0 && errno == ENOENT) {
throw FileNotFoundException("no such file")
}
return pathString.toPath()
return fullpath.toKString().toPath()
} finally {
free(fullpath)
}
Expand All @@ -109,7 +149,7 @@ internal actual fun PosixFileSystem.variantCanonicalize(path: Path): Path {
internal actual fun PosixFileSystem.variantMetadataOrNull(path: Path): FileMetadata? {
return memScoped {
val stat = alloc<_stat64>()
if (_stat64(path.toString(), stat.ptr) != 0) {
if (_wstat64(path.toString().wcstr, stat.ptr) != 0) {
if (errno == ENOENT) return null
throw errnoToIOException(errno)
}
Expand All @@ -126,21 +166,21 @@ internal actual fun PosixFileSystem.variantMetadataOrNull(path: Path): FileMetad
}

internal actual fun PosixFileSystem.variantMove(source: Path, target: Path) {
if (MoveFileExA(source.toString(), target.toString(), MOVEFILE_REPLACE_EXISTING.toUInt()) == 0) {
if (MoveFileExW(source.toString(), target.toString(), MOVEFILE_REPLACE_EXISTING.toUInt()) == 0) {
throw lastErrorToIOException()
}
}

internal actual fun PosixFileSystem.variantSource(file: Path): Source {
val openFile: CPointer<FILE> = fopen(file.toString(), "rb")
val openFile: CPointer<FILE> = _wfopen(file.toString().wcstr, "rb".wcstr)
?: throw errnoToIOException(errno)
return FileSource(openFile)
}

internal actual fun PosixFileSystem.variantSink(file: Path, mustCreate: Boolean): Sink {
// We're non-atomically checking file existence because Windows errors if we use the `x` flag along with `w`.
if (mustCreate && exists(file)) throw IOException("$file already exists.")
val openFile: CPointer<FILE> = fopen(file.toString(), "wb")
val openFile: CPointer<FILE> = _wfopen(file.toString().wcstr, "wb".wcstr)
?: throw errnoToIOException(errno)
return FileSink(openFile)
}
Expand All @@ -150,13 +190,13 @@ internal actual fun PosixFileSystem.variantAppendingSink(file: Path, mustExist:
// doesn't allow opening for appending, and we don't currently have a way to move the cursor to
// the end of the file. We are then forcing existence non-atomically.
if (mustExist && !exists(file)) throw IOException("$file doesn't exist.")
val openFile: CPointer<FILE> = fopen(file.toString(), "ab")
val openFile: CPointer<FILE> = _wfopen(file.toString().wcstr, "ab".wcstr)
?: throw errnoToIOException(errno)
return FileSink(openFile)
}

internal actual fun PosixFileSystem.variantOpenReadOnly(file: Path): FileHandle {
val openFile = CreateFileA(
val openFile = CreateFileW(
lpFileName = file.toString(),
dwDesiredAccess = GENERIC_READ,
dwShareMode = FILE_SHARE_WRITE.toUInt(),
Expand Down Expand Up @@ -186,7 +226,7 @@ internal actual fun PosixFileSystem.variantOpenReadWrite(
else -> OPEN_ALWAYS.toUInt()
}

val openFile = CreateFileA(
val openFile = CreateFileW(
lpFileName = file.toString(),
dwDesiredAccess = GENERIC_READ or GENERIC_WRITE.toUInt(),
dwShareMode = FILE_SHARE_WRITE.toUInt(),
Expand Down
47 changes: 3 additions & 44 deletions okio/src/nativeMain/kotlin/okio/PosixFileSystem.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,15 @@
*/
package okio

import kotlinx.cinterop.CPointer
import kotlinx.cinterop.get
import okio.Path.Companion.toPath
import okio.internal.toPath
import platform.posix.EEXIST
import platform.posix.closedir
import platform.posix.dirent
import platform.posix.errno
import platform.posix.opendir
import platform.posix.readdir
import platform.posix.set_posix_errno

internal object PosixFileSystem : FileSystem() {
private val SELF_DIRECTORY_ENTRY = ".".toPath()
private val PARENT_DIRECTORY_ENTRY = "..".toPath()
internal val SELF_DIRECTORY_ENTRY = ".".toPath()
internal val PARENT_DIRECTORY_ENTRY = "..".toPath()

override fun canonicalize(path: Path) = variantCanonicalize(path)

Expand All @@ -39,42 +33,7 @@ internal object PosixFileSystem : FileSystem() {

override fun listOrNull(dir: Path): List<Path>? = list(dir, throwOnFailure = false)

private fun list(dir: Path, throwOnFailure: Boolean): List<Path>? {
val opendir = opendir(dir.toString())
?: if (throwOnFailure) throw errnoToIOException(errno) else return null

try {
val result = mutableListOf<Path>()
val buffer = Buffer()

set_posix_errno(0) // If readdir() returns null it's either the end or an error.
while (true) {
val dirent: CPointer<dirent> = readdir(opendir) ?: break
val childPath = buffer.writeNullTerminated(
bytes = dirent[0].d_name,
).toPath(normalize = true)

if (childPath == SELF_DIRECTORY_ENTRY || childPath == PARENT_DIRECTORY_ENTRY) {
continue // exclude '.' and '..' from the results.
}

result += dir / childPath
}

if (errno != 0) {
if (throwOnFailure) {
throw errnoToIOException(errno)
} else {
return null
}
}

result.sort()
return result
} finally {
closedir(opendir) // Ignore errno from closedir.
}
}
private fun list(dir: Path, throwOnFailure: Boolean): List<Path>? = variantList(dir, throwOnFailure)

override fun openReadOnly(file: Path) = variantOpenReadOnly(file)

Expand Down
2 changes: 2 additions & 0 deletions okio/src/nativeMain/kotlin/okio/PosixVariant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ package okio

internal expect val PLATFORM_TEMPORARY_DIRECTORY: Path

internal expect fun PosixFileSystem.variantList(dir: Path, throwOnFailure: Boolean): List<Path>?

internal expect fun PosixFileSystem.variantDelete(path: Path, mustExist: Boolean)

internal expect fun PosixFileSystem.variantMkdir(dir: Path): Int
Expand Down
43 changes: 43 additions & 0 deletions okio/src/unixMain/kotlin/okio/UnixPosixVariant.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import kotlinx.cinterop.CValuesRef
import kotlinx.cinterop.UnsafeNumber
import kotlinx.cinterop.allocArray
import kotlinx.cinterop.convert
import kotlinx.cinterop.get
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import okio.Path.Companion.toPath
Expand All @@ -34,6 +35,8 @@ import platform.posix.O_RDWR
import platform.posix.PATH_MAX
import platform.posix.S_IFLNK
import platform.posix.S_IFMT
import platform.posix.closedir
import platform.posix.dirent
import platform.posix.errno
import platform.posix.fdopen
import platform.posix.fileno
Expand All @@ -42,12 +45,15 @@ import platform.posix.free
import platform.posix.getenv
import platform.posix.mkdir
import platform.posix.open
import platform.posix.opendir
import platform.posix.pread
import platform.posix.pwrite
import platform.posix.readdir
import platform.posix.readlink
import platform.posix.realpath
import platform.posix.remove
import platform.posix.rename
import platform.posix.set_posix_errno
import platform.posix.stat
import platform.posix.symlink
import platform.posix.timespec
Expand Down Expand Up @@ -76,6 +82,43 @@ internal actual fun PosixFileSystem.variantDelete(path: Path, mustExist: Boolean
}
}

internal actual fun PosixFileSystem.variantList(dir: Path, throwOnFailure: Boolean): List<Path>? {
val opendir = opendir(dir.toString())
?: if (throwOnFailure) throw errnoToIOException(errno) else return null

try {
val result = mutableListOf<Path>()
val buffer = Buffer()

set_posix_errno(0) // If readdir() returns null it's either the end or an error.
while (true) {
val dirent: CPointer<dirent> = readdir(opendir) ?: break
val childPath = buffer.writeNullTerminated(
bytes = dirent[0].d_name,
).toPath(normalize = true)

if (childPath == SELF_DIRECTORY_ENTRY || childPath == PARENT_DIRECTORY_ENTRY) {
continue // exclude '.' and '..' from the results.
}

result += dir / childPath
}

if (errno != 0) {
if (throwOnFailure) {
throw errnoToIOException(errno)
} else {
return null
}
}

result.sort()
return result
} finally {
closedir(opendir) // Ignore errno from closedir.
}
}

@OptIn(UnsafeNumber::class)
internal actual fun PosixFileSystem.variantMkdir(dir: Path): Int {
return mkdir(dir.toString(), 0b111111111u.convert() /* octal 777 */)
Expand Down