Skip to content

Commit

Permalink
Replace stat call with NSFileManager API on Apple target (#298)
Browse files Browse the repository at this point in the history
Starting from May 1, 2024 developers have to explicitly
declare why they use APIs allowing to access
file timestamps in a privacy manifest file;
otherwise, an app won't be accepted by the AppStore.
The only restricted API we're currently using is stat,
and since we don't extract any timestamps using it, 
we can safely replace it with NSFileManager::fileAttributesAtPath.

For more details on the restriction imposed on
timestamp-accessing APIs read https://developer.apple.com/documentation/bundleresources/privacy_manifest_files/describing_use_of_required_reason_api

Closes #297
  • Loading branch information
fzhinkin authored Apr 22, 2024
1 parent d164c56 commit e7cf4cf
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 21 deletions.
14 changes: 13 additions & 1 deletion core/apple/src/files/FileSystemApple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import kotlinx.cinterop.cstr
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.toKString
import kotlinx.io.IOException
import platform.Foundation.NSTemporaryDirectory
import platform.Foundation.*
import platform.posix.*


Expand Down Expand Up @@ -55,3 +55,15 @@ internal actual fun realpathImpl(path: String): String {
free(res)
}
}

internal actual fun metadataOrNullImpl(path: Path): FileMetadata? {
val attributes = NSFileManager.defaultManager().fileAttributesAtPath(path.path, traverseLink = true) ?: return null
val fileType = attributes[NSFileType] as String
val isFile = fileType == NSFileTypeRegular
val isDir = fileType == NSFileTypeDirectory
return FileMetadata(
isRegularFile = isFile,
isDirectory = isDir,
size = if (isFile) attributes[NSFileSize] as Long else -1
)
}
20 changes: 3 additions & 17 deletions core/native/src/files/FileSystemNative.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,23 +63,7 @@ public actual val SystemFileSystem: FileSystem = object : SystemFileSystemImpl()
atomicMoveImpl(source, destination)
}

@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
override fun metadataOrNull(path: Path): FileMetadata? {
memScoped {
val struct_stat = alloc<stat>()
if (stat(path.path, struct_stat.ptr) != 0) {
if (errno == ENOENT) return null
throw IOException("stat failed to ${path.path}: ${strerror(errno)?.toKString()}")
}
val mode = struct_stat.st_mode.toInt()
val isFile = (mode and S_IFMT) == S_IFREG
return FileMetadata(
isRegularFile = isFile,
isDirectory = (mode and S_IFMT) == S_IFDIR,
if (isFile) struct_stat.st_size.toLong() else -1L
)
}
}
override fun metadataOrNull(path: Path): FileMetadata? = metadataOrNullImpl(path)

override fun resolve(path: Path): Path {
if (!exists(path)) throw FileNotFoundException(path.path)
Expand All @@ -104,6 +88,8 @@ public actual val SystemFileSystem: FileSystem = object : SystemFileSystemImpl()
}
}

internal expect fun metadataOrNullImpl(path: Path): FileMetadata?

internal expect fun atomicMoveImpl(source: Path, destination: Path)

internal expect fun mkdirImpl(path: String)
Expand Down
24 changes: 21 additions & 3 deletions core/nonApple/src/files/FileSystemNonApple.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,28 @@

package kotlinx.io.files

import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.toKString
import platform.posix.getenv
import kotlinx.cinterop.*
import kotlinx.io.IOException
import platform.posix.*

@OptIn(ExperimentalForeignApi::class)
public actual val SystemTemporaryDirectory: Path
get() = Path(getenv("TMPDIR")?.toKString() ?: getenv("TMP")?.toKString() ?: "")

@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
internal actual fun metadataOrNullImpl(path: Path): FileMetadata? {
memScoped {
val struct_stat = alloc<stat>()
if (stat(path.path, struct_stat.ptr) != 0) {
if (errno == ENOENT) return null
throw IOException("stat failed to ${path.path}: ${strerror(errno)?.toKString()}")
}
val mode = struct_stat.st_mode.toInt()
val isFile = (mode and S_IFMT) == S_IFREG
return FileMetadata(
isRegularFile = isFile,
isDirectory = (mode and S_IFMT) == S_IFDIR,
if (isFile) struct_stat.st_size.toLong() else -1L
)
}
}

0 comments on commit e7cf4cf

Please sign in to comment.