Skip to content

Commit e7cf4cf

Browse files
authored
Replace stat call with NSFileManager API on Apple target (#298)
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
1 parent d164c56 commit e7cf4cf

File tree

3 files changed

+37
-21
lines changed

3 files changed

+37
-21
lines changed

core/apple/src/files/FileSystemApple.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import kotlinx.cinterop.cstr
1111
import kotlinx.cinterop.memScoped
1212
import kotlinx.cinterop.toKString
1313
import kotlinx.io.IOException
14-
import platform.Foundation.NSTemporaryDirectory
14+
import platform.Foundation.*
1515
import platform.posix.*
1616

1717

@@ -55,3 +55,15 @@ internal actual fun realpathImpl(path: String): String {
5555
free(res)
5656
}
5757
}
58+
59+
internal actual fun metadataOrNullImpl(path: Path): FileMetadata? {
60+
val attributes = NSFileManager.defaultManager().fileAttributesAtPath(path.path, traverseLink = true) ?: return null
61+
val fileType = attributes[NSFileType] as String
62+
val isFile = fileType == NSFileTypeRegular
63+
val isDir = fileType == NSFileTypeDirectory
64+
return FileMetadata(
65+
isRegularFile = isFile,
66+
isDirectory = isDir,
67+
size = if (isFile) attributes[NSFileSize] as Long else -1
68+
)
69+
}

core/native/src/files/FileSystemNative.kt

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,7 @@ public actual val SystemFileSystem: FileSystem = object : SystemFileSystemImpl()
6363
atomicMoveImpl(source, destination)
6464
}
6565

66-
@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
67-
override fun metadataOrNull(path: Path): FileMetadata? {
68-
memScoped {
69-
val struct_stat = alloc<stat>()
70-
if (stat(path.path, struct_stat.ptr) != 0) {
71-
if (errno == ENOENT) return null
72-
throw IOException("stat failed to ${path.path}: ${strerror(errno)?.toKString()}")
73-
}
74-
val mode = struct_stat.st_mode.toInt()
75-
val isFile = (mode and S_IFMT) == S_IFREG
76-
return FileMetadata(
77-
isRegularFile = isFile,
78-
isDirectory = (mode and S_IFMT) == S_IFDIR,
79-
if (isFile) struct_stat.st_size.toLong() else -1L
80-
)
81-
}
82-
}
66+
override fun metadataOrNull(path: Path): FileMetadata? = metadataOrNullImpl(path)
8367

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

91+
internal expect fun metadataOrNullImpl(path: Path): FileMetadata?
92+
10793
internal expect fun atomicMoveImpl(source: Path, destination: Path)
10894

10995
internal expect fun mkdirImpl(path: String)

core/nonApple/src/files/FileSystemNonApple.kt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,28 @@
55

66
package kotlinx.io.files
77

8-
import kotlinx.cinterop.ExperimentalForeignApi
9-
import kotlinx.cinterop.toKString
10-
import platform.posix.getenv
8+
import kotlinx.cinterop.*
9+
import kotlinx.io.IOException
10+
import platform.posix.*
1111

1212
@OptIn(ExperimentalForeignApi::class)
1313
public actual val SystemTemporaryDirectory: Path
1414
get() = Path(getenv("TMPDIR")?.toKString() ?: getenv("TMP")?.toKString() ?: "")
15+
16+
@OptIn(ExperimentalForeignApi::class, UnsafeNumber::class)
17+
internal actual fun metadataOrNullImpl(path: Path): FileMetadata? {
18+
memScoped {
19+
val struct_stat = alloc<stat>()
20+
if (stat(path.path, struct_stat.ptr) != 0) {
21+
if (errno == ENOENT) return null
22+
throw IOException("stat failed to ${path.path}: ${strerror(errno)?.toKString()}")
23+
}
24+
val mode = struct_stat.st_mode.toInt()
25+
val isFile = (mode and S_IFMT) == S_IFREG
26+
return FileMetadata(
27+
isRegularFile = isFile,
28+
isDirectory = (mode and S_IFMT) == S_IFDIR,
29+
if (isFile) struct_stat.st_size.toLong() else -1L
30+
)
31+
}
32+
}

0 commit comments

Comments
 (0)