FilePathTempPosix.swift 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /*
  2. This source file is part of the Swift System open source project
  3. Copyright (c) 2024 Apple Inc. and the Swift System project authors
  4. Licensed under Apache License v2.0 with Runtime Library Exception
  5. See https://swift.org/LICENSE.txt for license information
  6. */
  7. #if !os(Windows)
  8. #if SYSTEM_PACKAGE_DARWIN
  9. import Darwin
  10. #elseif canImport(Glibc)
  11. import CSystem
  12. import Glibc
  13. #elseif canImport(Musl)
  14. import CSystem
  15. import Musl
  16. #elseif canImport(WASILibc)
  17. import WASILibc
  18. #elseif canImport(Android)
  19. import CSystem
  20. import Android
  21. #else
  22. #error("Unsupported Platform")
  23. #endif
  24. /// Get the path to the system temporary directory.
  25. internal func _getTemporaryDirectory() throws -> FilePath {
  26. guard let tmp = system_getenv("TMPDIR") else {
  27. return "/tmp"
  28. }
  29. return FilePath(SystemString(platformString: tmp))
  30. }
  31. /// Delete the entire contents of a directory, including its subdirectories.
  32. ///
  33. /// - Parameters:
  34. /// - path: The directory to be deleted.
  35. ///
  36. /// Removes a directory completely, including all of its contents.
  37. internal func _recursiveRemove(
  38. at path: FilePath
  39. ) throws {
  40. let dirfd = try FileDescriptor.open(path, .readOnly, options: .directory)
  41. defer {
  42. try? dirfd.close()
  43. }
  44. let dot: (CInterop.PlatformChar, CInterop.PlatformChar) = (46, 0)
  45. try withUnsafeBytes(of: dot) {
  46. try recursiveRemove(
  47. in: dirfd.rawValue,
  48. name: $0.assumingMemoryBound(to: CInterop.PlatformChar.self).baseAddress!
  49. )
  50. }
  51. try path.withPlatformString {
  52. if system_rmdir($0) != 0 {
  53. throw Errno.current
  54. }
  55. }
  56. }
  57. /// Open a directory by reference to its parent and name.
  58. ///
  59. /// - Parameters:
  60. /// - dirfd: An open file descriptor for the parent directory.
  61. /// - name: The name of the directory to open.
  62. /// - Returns: A pointer to a `DIR` structure.
  63. ///
  64. /// This is like `opendir()`, but instead of taking a path, it uses a
  65. /// file descriptor pointing at the parent, thus avoiding path length
  66. /// limits.
  67. fileprivate func impl_opendirat(
  68. _ dirfd: CInt,
  69. _ name: UnsafePointer<CInterop.PlatformChar>
  70. ) -> system_DIRPtr? {
  71. let fd = system_openat(dirfd, name,
  72. FileDescriptor.AccessMode.readOnly.rawValue
  73. | FileDescriptor.OpenOptions.directory.rawValue)
  74. if fd < 0 {
  75. return nil
  76. }
  77. return system_fdopendir(fd)
  78. }
  79. /// Invoke a closure for each file within a particular directory.
  80. ///
  81. /// - Parameters:
  82. /// - dirfd: The parent of the directory to be enumerated.
  83. /// - subdir: The subdirectory to be enumerated.
  84. /// - body: The closure that will be invoked.
  85. ///
  86. /// We skip the `.` and `..` pseudo-entries.
  87. fileprivate func forEachFile(
  88. in dirfd: CInt,
  89. subdir: UnsafePointer<CInterop.PlatformChar>,
  90. _ body: (system_dirent) throws -> ()
  91. ) throws {
  92. guard let dir = impl_opendirat(dirfd, subdir) else {
  93. throw Errno.current
  94. }
  95. defer {
  96. _ = system_closedir(dir)
  97. }
  98. while let dirent = system_readdir(dir) {
  99. // Skip . and ..
  100. if dirent.pointee.d_name.0 == 46
  101. && (dirent.pointee.d_name.1 == 0
  102. || (dirent.pointee.d_name.1 == 46
  103. && dirent.pointee.d_name.2 == 0)) {
  104. continue
  105. }
  106. try body(dirent.pointee)
  107. }
  108. }
  109. /// Delete the entire contents of a directory, including its subdirectories.
  110. ///
  111. /// - Parameters:
  112. /// - dirfd: The parent of the directory to be removed.
  113. /// - name: The name of the directory to be removed.
  114. ///
  115. /// Removes a directory completely, including all of its contents.
  116. fileprivate func recursiveRemove(
  117. in dirfd: CInt,
  118. name: UnsafePointer<CInterop.PlatformChar>
  119. ) throws {
  120. // First, deal with subdirectories
  121. try forEachFile(in: dirfd, subdir: name) { dirent in
  122. if dirent.d_type == SYSTEM_DT_DIR {
  123. try withUnsafeBytes(of: dirent.d_name) {
  124. try recursiveRemove(
  125. in: dirfd,
  126. name: $0.assumingMemoryBound(to: CInterop.PlatformChar.self)
  127. .baseAddress!
  128. )
  129. }
  130. }
  131. }
  132. // Now delete the contents of this directory
  133. try forEachFile(in: dirfd, subdir: name) { dirent in
  134. let flag: CInt
  135. if dirent.d_type == SYSTEM_DT_DIR {
  136. flag = SYSTEM_AT_REMOVE_DIR
  137. } else {
  138. flag = 0
  139. }
  140. let result = withUnsafeBytes(of: dirent.d_name) {
  141. system_unlinkat(dirfd,
  142. $0.assumingMemoryBound(to: CInterop.PlatformChar.self)
  143. .baseAddress!,
  144. flag)
  145. }
  146. if result != 0 {
  147. throw Errno.current
  148. }
  149. }
  150. }
  151. #endif // !os(Windows)