FileOperations.swift 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672
  1. /*
  2. This source file is part of the Swift System open source project
  3. Copyright (c) 2020 - 2026 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. @available(System 0.0.1, *)
  8. extension FileDescriptor {
  9. /// Opens or creates a file for reading or writing.
  10. ///
  11. /// - Parameters:
  12. /// - path: The location of the file to open.
  13. /// - mode: The read and write access to use.
  14. /// - options: The behavior for opening the file.
  15. /// - permissions: The file permissions to use for created files.
  16. /// This value must not be `nil` when `options` contains `.create`;
  17. /// passing `nil` in that case is a programmer error and traps at runtime.
  18. /// - retryOnInterrupt: Whether to retry the open operation
  19. /// if it throws ``Errno/interrupted``.
  20. /// The default is `true`.
  21. /// Pass `false` to try only once and throw an error upon interruption.
  22. /// - Returns: A file descriptor for the open file
  23. ///
  24. /// The corresponding C function is `open`.
  25. @_alwaysEmitIntoClient
  26. public static func open(
  27. _ path: FilePath,
  28. _ mode: FileDescriptor.AccessMode,
  29. options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(),
  30. permissions: FilePermissions? = nil,
  31. retryOnInterrupt: Bool = true
  32. ) throws -> FileDescriptor {
  33. #if !os(Windows)
  34. return try path.withCString {
  35. try FileDescriptor.open(
  36. $0, mode, options: options, permissions: permissions, retryOnInterrupt: retryOnInterrupt)
  37. }
  38. #else
  39. return try path.withPlatformString {
  40. try FileDescriptor.open(
  41. $0, mode, options: options, permissions: permissions, retryOnInterrupt: retryOnInterrupt)
  42. }
  43. #endif
  44. }
  45. #if !os(Windows)
  46. // On Darwin, `CInterop.PlatformChar` is less available than
  47. // `FileDescriptor.open`, so we need to use `CChar` instead.
  48. /// Opens or creates a file for reading or writing.
  49. ///
  50. /// - Parameters:
  51. /// - path: The location of the file to open.
  52. /// - mode: The read and write access to use.
  53. /// - options: The behavior for opening the file.
  54. /// - permissions: The file permissions to use for created files.
  55. /// This value must not be `nil` when `options` contains `.create`;
  56. /// passing `nil` in that case is a programmer error and traps at runtime.
  57. /// - retryOnInterrupt: Whether to retry the open operation
  58. /// if it throws ``Errno/interrupted``.
  59. /// The default is `true`.
  60. /// Pass `false` to try only once and throw an error upon interruption.
  61. /// - Returns: A file descriptor for the open file
  62. ///
  63. /// The corresponding C function is `open`.
  64. @_alwaysEmitIntoClient
  65. public static func open(
  66. _ path: UnsafePointer<CChar>,
  67. _ mode: FileDescriptor.AccessMode,
  68. options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(),
  69. permissions: FilePermissions? = nil,
  70. retryOnInterrupt: Bool = true
  71. ) throws -> FileDescriptor {
  72. try FileDescriptor._open(
  73. path, mode, options: options, permissions: permissions, retryOnInterrupt: retryOnInterrupt
  74. ).get()
  75. }
  76. @usableFromInline
  77. internal static func _open(
  78. _ path: UnsafePointer<CChar>,
  79. _ mode: FileDescriptor.AccessMode,
  80. options: FileDescriptor.OpenOptions,
  81. permissions: FilePermissions?,
  82. retryOnInterrupt: Bool
  83. ) -> Result<FileDescriptor, Errno> {
  84. let oFlag = mode.rawValue | options.rawValue
  85. let descOrError: Result<CInt, Errno> = valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  86. if let permissions = permissions {
  87. return system_open(path, oFlag, permissions.rawValue)
  88. }
  89. if options.contains(.create) {
  90. fatalError(
  91. "FileDescriptor.open: 'permissions' must not be nil when 'options' contains '.create'")
  92. }
  93. return system_open(path, oFlag)
  94. }
  95. return descOrError.map { FileDescriptor(rawValue: $0) }
  96. }
  97. #else
  98. /// Opens or creates a file for reading or writing.
  99. ///
  100. /// - Parameters:
  101. /// - path: The location of the file to open.
  102. /// - mode: The read and write access to use.
  103. /// - options: The behavior for opening the file.
  104. /// - permissions: The file permissions to use for created files.
  105. /// This value must not be `nil` when `options` contains `.create`;
  106. /// passing `nil` in that case is a programmer error and traps at runtime.
  107. /// - retryOnInterrupt: Whether to retry the open operation
  108. /// if it throws ``Errno/interrupted``.
  109. /// The default is `true`.
  110. /// Pass `false` to try only once and throw an error upon interruption.
  111. /// - Returns: A file descriptor for the open file
  112. ///
  113. /// The corresponding C function is `open`.
  114. @_alwaysEmitIntoClient
  115. public static func open(
  116. _ path: UnsafePointer<CInterop.PlatformChar>,
  117. _ mode: FileDescriptor.AccessMode,
  118. options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(),
  119. permissions: FilePermissions? = nil,
  120. retryOnInterrupt: Bool = true
  121. ) throws -> FileDescriptor {
  122. try FileDescriptor._open(
  123. path, mode, options: options, permissions: permissions, retryOnInterrupt: retryOnInterrupt
  124. ).get()
  125. }
  126. @usableFromInline
  127. internal static func _open(
  128. _ path: UnsafePointer<CInterop.PlatformChar>,
  129. _ mode: FileDescriptor.AccessMode,
  130. options: FileDescriptor.OpenOptions,
  131. permissions: FilePermissions?,
  132. retryOnInterrupt: Bool
  133. ) -> Result<FileDescriptor, Errno> {
  134. let oFlag = mode.rawValue | options.rawValue
  135. let descOrError: Result<CInt, Errno> = valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  136. if let permissions = permissions {
  137. return system_open(path, oFlag, permissions.rawValue)
  138. }
  139. return system_open(path, oFlag)
  140. }
  141. return descOrError.map { FileDescriptor(rawValue: $0) }
  142. }
  143. #endif
  144. /// Deletes a file descriptor.
  145. ///
  146. /// Deletes the file descriptor from the per-process object reference table.
  147. /// If this is the last reference to the underlying object,
  148. /// the object will be deactivated.
  149. ///
  150. /// The corresponding C function is `close`.
  151. @_alwaysEmitIntoClient
  152. public func close() throws { try _close().get() }
  153. @usableFromInline
  154. internal func _close() -> Result<(), Errno> {
  155. nothingOrErrno(retryOnInterrupt: false) { system_close(self.rawValue) }
  156. }
  157. /// Repositions the offset for the given file descriptor.
  158. ///
  159. /// - Parameters:
  160. /// - offset: The new offset for the file descriptor.
  161. /// - whence: The origin of the new offset.
  162. /// - Returns: The file's offset location,
  163. /// in bytes from the beginning of the file.
  164. ///
  165. /// The corresponding C function is `lseek`.
  166. @_alwaysEmitIntoClient
  167. @discardableResult
  168. public func seek(
  169. offset: Int64, from whence: FileDescriptor.SeekOrigin
  170. ) throws -> Int64 {
  171. try _seek(offset: offset, from: whence).get()
  172. }
  173. @usableFromInline
  174. internal func _seek(
  175. offset: Int64, from whence: FileDescriptor.SeekOrigin
  176. ) -> Result<Int64, Errno> {
  177. valueOrErrno(retryOnInterrupt: false) {
  178. Int64(system_lseek(self.rawValue, _COffT(offset), whence.rawValue))
  179. }
  180. }
  181. @_alwaysEmitIntoClient
  182. @available(*, unavailable, renamed: "seek")
  183. public func lseek(
  184. offset: Int64, from whence: FileDescriptor.SeekOrigin
  185. ) throws -> Int64 {
  186. try seek(offset: offset, from: whence)
  187. }
  188. /// Reads bytes at the current file offset into a buffer.
  189. ///
  190. /// - Parameters:
  191. /// - buffer: The region of memory to read into.
  192. /// - retryOnInterrupt: Whether to retry the read operation
  193. /// if it throws ``Errno/interrupted``.
  194. /// The default is `true`.
  195. /// Pass `false` to try only once and throw an error upon interruption.
  196. /// - Returns: The number of bytes that were read.
  197. ///
  198. /// The <doc://com.apple.documentation/documentation/swift/unsafemutablerawbufferpointer/count-95usp> property of `buffer`
  199. /// determines the maximum number of bytes that are read into that buffer.
  200. ///
  201. /// After reading,
  202. /// this method increments the file's offset by the number of bytes read.
  203. /// To change the file's offset,
  204. /// call the ``seek(offset:from:)`` method.
  205. ///
  206. /// The corresponding C function is `read`.
  207. @_alwaysEmitIntoClient
  208. public func read(
  209. into buffer: UnsafeMutableRawBufferPointer,
  210. retryOnInterrupt: Bool = true
  211. ) throws -> Int {
  212. try _read(into: buffer, retryOnInterrupt: retryOnInterrupt).get()
  213. }
  214. @usableFromInline
  215. internal func _read(
  216. into buffer: UnsafeMutableRawBufferPointer,
  217. retryOnInterrupt: Bool
  218. ) -> Result<Int, Errno> {
  219. valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  220. system_read(self.rawValue, buffer.baseAddress, buffer.count)
  221. }
  222. }
  223. /// Reads bytes at the specified offset into a buffer.
  224. ///
  225. /// - Parameters:
  226. /// - offset: The file offset where reading begins.
  227. /// - buffer: The region of memory to read into.
  228. /// - retryOnInterrupt: Whether to retry the read operation
  229. /// if it throws ``Errno/interrupted``.
  230. /// The default is `true`.
  231. /// Pass `false` to try only once and throw an error upon interruption.
  232. /// - Returns: The number of bytes that were read.
  233. ///
  234. /// The <doc://com.apple.documentation/documentation/swift/unsafemutablerawbufferpointer/count-95usp> property of `buffer`
  235. /// determines the maximum number of bytes that are read into that buffer.
  236. ///
  237. /// Unlike <doc:FileDescriptor/read(into:retryOnInterrupt:)>,
  238. /// this method leaves the file's existing offset unchanged.
  239. ///
  240. /// The corresponding C function is `pread`.
  241. @_alwaysEmitIntoClient
  242. public func read(
  243. fromAbsoluteOffset offset: Int64,
  244. into buffer: UnsafeMutableRawBufferPointer,
  245. retryOnInterrupt: Bool = true
  246. ) throws -> Int {
  247. try _read(
  248. fromAbsoluteOffset: offset,
  249. into: buffer,
  250. retryOnInterrupt: retryOnInterrupt
  251. ).get()
  252. }
  253. @usableFromInline
  254. internal func _read(
  255. fromAbsoluteOffset offset: Int64,
  256. into buffer: UnsafeMutableRawBufferPointer,
  257. retryOnInterrupt: Bool
  258. ) -> Result<Int, Errno> {
  259. valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  260. system_pread(self.rawValue, buffer.baseAddress, buffer.count, _COffT(offset))
  261. }
  262. }
  263. @_alwaysEmitIntoClient
  264. @available(*, unavailable, renamed: "read")
  265. public func pread(
  266. fromAbsoluteOffset offset: Int64,
  267. into buffer: UnsafeMutableRawBufferPointer,
  268. retryOnInterrupt: Bool = true
  269. ) throws -> Int {
  270. try read(
  271. fromAbsoluteOffset: offset,
  272. into: buffer,
  273. retryOnInterrupt: retryOnInterrupt)
  274. }
  275. /// Writes the contents of a buffer at the current file offset.
  276. ///
  277. /// - Parameters:
  278. /// - buffer: The region of memory that contains the data being written.
  279. /// - retryOnInterrupt: Whether to retry the write operation
  280. /// if it throws ``Errno/interrupted``.
  281. /// The default is `true`.
  282. /// Pass `false` to try only once and throw an error upon interruption.
  283. /// - Returns: The number of bytes that were written.
  284. ///
  285. /// After writing,
  286. /// this method increments the file's offset by the number of bytes written.
  287. /// To change the file's offset,
  288. /// call the ``seek(offset:from:)`` method.
  289. ///
  290. /// The corresponding C function is `write`.
  291. @_alwaysEmitIntoClient
  292. public func write(
  293. _ buffer: UnsafeRawBufferPointer,
  294. retryOnInterrupt: Bool = true
  295. ) throws -> Int {
  296. try _write(buffer, retryOnInterrupt: retryOnInterrupt).get()
  297. }
  298. @usableFromInline
  299. internal func _write(
  300. _ buffer: UnsafeRawBufferPointer,
  301. retryOnInterrupt: Bool
  302. ) -> Result<Int, Errno> {
  303. valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  304. system_write(self.rawValue, buffer.baseAddress, buffer.count)
  305. }
  306. }
  307. /// Writes the contents of a buffer at the specified offset.
  308. ///
  309. /// - Parameters:
  310. /// - offset: The file offset where writing begins.
  311. /// - buffer: The region of memory that contains the data being written.
  312. /// - retryOnInterrupt: Whether to retry the write operation
  313. /// if it throws ``Errno/interrupted``.
  314. /// The default is `true`.
  315. /// Pass `false` to try only once and throw an error upon interruption.
  316. /// - Returns: The number of bytes that were written.
  317. ///
  318. /// Unlike ``write(_:retryOnInterrupt:)``,
  319. /// this method leaves the file's existing offset unchanged.
  320. ///
  321. /// The corresponding C function is `pwrite`.
  322. @_alwaysEmitIntoClient
  323. public func write(
  324. toAbsoluteOffset offset: Int64,
  325. _ buffer: UnsafeRawBufferPointer,
  326. retryOnInterrupt: Bool = true
  327. ) throws -> Int {
  328. try _write(toAbsoluteOffset: offset, buffer, retryOnInterrupt: retryOnInterrupt).get()
  329. }
  330. @usableFromInline
  331. internal func _write(
  332. toAbsoluteOffset offset: Int64,
  333. _ buffer: UnsafeRawBufferPointer,
  334. retryOnInterrupt: Bool
  335. ) -> Result<Int, Errno> {
  336. valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  337. system_pwrite(self.rawValue, buffer.baseAddress, buffer.count, _COffT(offset))
  338. }
  339. }
  340. @_alwaysEmitIntoClient
  341. @available(*, unavailable, renamed: "write")
  342. public func pwrite(
  343. toAbsoluteOffset offset: Int64,
  344. into buffer: UnsafeRawBufferPointer,
  345. retryOnInterrupt: Bool = true
  346. ) throws -> Int {
  347. try write(
  348. toAbsoluteOffset: offset,
  349. buffer,
  350. retryOnInterrupt: retryOnInterrupt)
  351. }
  352. }
  353. #if !os(WASI)
  354. @available(System 0.0.2, *)
  355. extension FileDescriptor {
  356. /// Duplicates this file descriptor and returns the newly created copy.
  357. ///
  358. /// - Parameters:
  359. /// - `target`: The desired target file descriptor, or `nil`, in which case
  360. /// the copy is assigned to the file descriptor with the lowest raw value
  361. /// that is not currently in use by the process.
  362. /// - retryOnInterrupt: Whether to retry the duplicate operation
  363. /// if it throws ``Errno/interrupted``. The default is `true`.
  364. /// Pass `false` to try only once and throw an error upon interruption.
  365. /// - Returns: The new file descriptor.
  366. ///
  367. /// If the `target` descriptor is already in use, then it is first
  368. /// deallocated as if a close(2) call had been done first.
  369. ///
  370. /// File descriptors are merely references to some underlying system resource.
  371. /// The system does not distinguish between the original and the new file
  372. /// descriptor in any way. For example, read, write and seek operations on
  373. /// one of them also affect the logical file position in the other, and
  374. /// append mode, non-blocking I/O and asynchronous I/O options are shared
  375. /// between the references. If a separate pointer into the file is desired,
  376. /// a different object reference to the file must be obtained by issuing an
  377. /// additional call to `open`.
  378. ///
  379. /// However, each file descriptor maintains its own close-on-exec flag.
  380. ///
  381. /// The corresponding C functions are `dup` and `dup2`.
  382. @_alwaysEmitIntoClient
  383. @available(System 0.0.2, *)
  384. public func duplicate(
  385. as target: FileDescriptor? = nil,
  386. retryOnInterrupt: Bool = true
  387. ) throws -> FileDescriptor {
  388. try _duplicate(as: target, retryOnInterrupt: retryOnInterrupt).get()
  389. }
  390. /// Duplicate this file descriptor and return the newly created copy.
  391. ///
  392. /// - Parameters:
  393. /// - target: The desired target file descriptor.
  394. /// - options: The behavior for creating the target file descriptor.
  395. /// - retryOnInterrupt: Whether to retry the operation
  396. /// if it throws ``Errno/interrupted``. The default is `true`.
  397. /// Pass `false` to try only once and throw an error upon interruption.
  398. /// - Returns: The new file descriptor.
  399. ///
  400. /// If the `target` descriptor is the same as `self`, then EINVAL is thrown.
  401. /// If the `target` descriptor is already in use, then it is first
  402. /// deallocated as if a close(2) call had been done first.
  403. ///
  404. /// NOTE: This overload called with an empty option set is not necessarily
  405. /// equivalent to calling the overload with no options, because `dup3` with
  406. /// no set options is not required to behave identically to `dup2`.
  407. ///
  408. /// File descriptors are merely references to some underlying system resource.
  409. /// The system does not distinguish between the original and the new file
  410. /// descriptor in any way. For example, read, write and seek operations on
  411. /// one of them also affect the logical file position in the other, and
  412. /// append mode, non-blocking I/O and asynchronous I/O options are shared
  413. /// between the references. If a separate pointer into the file is desired,
  414. /// a different object reference to the file must be obtained by issuing an
  415. /// additional call to `open`.
  416. ///
  417. /// However, each file descriptor maintains its own close-on-exec and
  418. /// close-on-fork flags.
  419. ///
  420. /// The corresponding C function is `dup3`.
  421. @available(Windows, unavailable)
  422. @available(macOS 27.0, iOS 27.0, watchOS 27.0, tvOS 27.0, visionOS 27.0, *)
  423. @_alwaysEmitIntoClient
  424. @discardableResult
  425. public func duplicate(
  426. as target: FileDescriptor,
  427. options: DuplicateOptions,
  428. retryOnInterrupt: Bool = true
  429. ) throws(Errno) -> FileDescriptor {
  430. let result = _duplicate(
  431. as: target, options: options.rawValue, retryOnInterrupt: retryOnInterrupt
  432. )
  433. return try result.get()
  434. }
  435. @available(System 0.0.2, *)
  436. @usableFromInline
  437. internal func _duplicate(
  438. as target: FileDescriptor?,
  439. retryOnInterrupt: Bool
  440. ) -> Result<FileDescriptor, Errno> {
  441. valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  442. if let target {
  443. system_dup2(self.rawValue, target.rawValue)
  444. } else {
  445. system_dup(self.rawValue)
  446. }
  447. }.map(FileDescriptor.init(rawValue:))
  448. }
  449. @available(Windows, unavailable)
  450. @available(macOS 27.0, iOS 27.0, watchOS 27.0, tvOS 27.0, visionOS 27.0, *)
  451. @usableFromInline
  452. internal func _duplicate(
  453. as target: FileDescriptor,
  454. options: Int32,
  455. retryOnInterrupt: Bool
  456. ) -> Result<FileDescriptor, Errno> {
  457. valueOrErrno(retryOnInterrupt: retryOnInterrupt) {
  458. system_dup3(self.rawValue, target.rawValue, options)
  459. }.map(FileDescriptor.init(rawValue:))
  460. }
  461. @_alwaysEmitIntoClient
  462. @available(*, unavailable, renamed: "duplicate")
  463. public func dup() throws -> FileDescriptor {
  464. fatalError("Not implemented")
  465. }
  466. @_alwaysEmitIntoClient
  467. @available(*, unavailable, renamed: "duplicate")
  468. public func dup2() throws -> FileDescriptor {
  469. fatalError("Not implemented")
  470. }
  471. @_alwaysEmitIntoClient
  472. @available(*, unavailable, renamed: "duplicate")
  473. public func dup3() throws -> FileDescriptor {
  474. fatalError("Not implemented")
  475. }
  476. }
  477. #endif // !os(WASI)
  478. #if !os(WASI)
  479. @available(System 1.1.0, *)
  480. extension FileDescriptor {
  481. /// Creates a unidirectional data channel, which can be used for
  482. /// interprocess communication.
  483. ///
  484. /// - Returns: The pair of file descriptors.
  485. ///
  486. /// The corresponding C function is `pipe`.
  487. @_alwaysEmitIntoClient
  488. @available(System 1.1.0, *)
  489. public static func pipe(
  490. ) throws -> (readEnd: FileDescriptor, writeEnd: FileDescriptor) {
  491. try _pipe().get()
  492. }
  493. /// Creates a unidirectional data channel, which can be used for
  494. /// interprocess communication.
  495. ///
  496. /// NOTE: This overload called with an empty option set is not necessarily
  497. /// equivalent to calling the overload with no options. On Windows, the
  498. /// no-parameter `pipe()` overload enables the `.closeOnExec` behaviour,
  499. /// but this overload disables it when called with an empty option set.
  500. ///
  501. /// - Parameters:
  502. /// - options: The behavior for creating the pipe.
  503. ///
  504. /// - Returns: The pair of file descriptors.
  505. ///
  506. /// The corresponding C function is `pipe2`.
  507. @_alwaysEmitIntoClient
  508. @available(macOS 27.0, iOS 27.0, watchOS 27.0, tvOS 27.0, visionOS 27.0, *)
  509. public static func pipe(
  510. options: PipeOptions
  511. ) throws(Errno) -> (readEnd: FileDescriptor, writeEnd: FileDescriptor) {
  512. try _pipe(options: options.rawValue).get()
  513. }
  514. @available(System 1.1.0, *)
  515. @usableFromInline
  516. internal static func _pipe(
  517. ) -> Result<(readEnd: FileDescriptor, writeEnd: FileDescriptor), Errno> {
  518. var fds: (Int32, Int32) = (-1, -1)
  519. return withUnsafeMutablePointer(to: &fds) { pointer in
  520. pointer.withMemoryRebound(to: Int32.self, capacity: 2) { fds in
  521. valueOrErrno(retryOnInterrupt: false) {
  522. system_pipe(fds)
  523. }.map { _ in (.init(rawValue: fds[0]), .init(rawValue: fds[1])) }
  524. }
  525. }
  526. }
  527. @available(macOS 27.0, iOS 27.0, watchOS 27.0, tvOS 27.0, visionOS 27.0, *)
  528. @usableFromInline
  529. internal static func _pipe(
  530. options: Int32,
  531. ) -> Result<(readEnd: FileDescriptor, writeEnd: FileDescriptor), Errno> {
  532. var fds: (Int32, Int32) = (-1, -1)
  533. return withUnsafeMutablePointer(to: &fds) { pointer in
  534. pointer.withMemoryRebound(to: Int32.self, capacity: 2) { fds in
  535. valueOrErrno(retryOnInterrupt: false) {
  536. system_pipe2(fds, options)
  537. }.map { _ in (.init(rawValue: fds[0]), .init(rawValue: fds[1])) }
  538. }
  539. }
  540. }
  541. @_alwaysEmitIntoClient
  542. @available(*, unavailable, renamed: "pipe")
  543. public static func pipe2() throws -> FileDescriptor {
  544. fatalError("Not implemented")
  545. }
  546. }
  547. #endif // !os(WASI)
  548. @available(System 1.2.0, *)
  549. extension FileDescriptor {
  550. /// Truncates or extends the file referenced by this file descriptor.
  551. ///
  552. /// - Parameters:
  553. /// - newSize: The length in bytes to resize the file to.
  554. /// - retryOnInterrupt: Whether to retry the write operation
  555. /// if it throws ``Errno/interrupted``. The default is `true`.
  556. /// Pass `false` to try only once and throw an error upon interruption.
  557. ///
  558. /// The file referenced by this file descriptor will by truncated (or extended) to `newSize`.
  559. ///
  560. /// If the current size of the file exceeds `newSize`, any extra data is discarded. If the current
  561. /// size of the file is smaller than `newSize`, the file is extended and filled with zeros to the
  562. /// provided size.
  563. ///
  564. /// This function requires that the file has been opened for writing.
  565. ///
  566. /// - Note: This function does not modify the current offset for any open file descriptors
  567. /// associated with the file.
  568. ///
  569. /// The corresponding C function is `ftruncate`.
  570. @available(System 1.2.0, *)
  571. @_alwaysEmitIntoClient
  572. public func resize(
  573. to newSize: Int64,
  574. retryOnInterrupt: Bool = true
  575. ) throws {
  576. try _resize(
  577. to: newSize,
  578. retryOnInterrupt: retryOnInterrupt
  579. ).get()
  580. }
  581. @available(System 1.2.0, *)
  582. @usableFromInline
  583. internal func _resize(
  584. to newSize: Int64,
  585. retryOnInterrupt: Bool
  586. ) -> Result<(), Errno> {
  587. nothingOrErrno(retryOnInterrupt: retryOnInterrupt) {
  588. system_ftruncate(self.rawValue, _COffT(newSize))
  589. }
  590. }
  591. }
  592. #if !os(WASI) // WASI has no umask
  593. extension FilePermissions {
  594. /// The file creation permission mask (aka "umask").
  595. ///
  596. /// Permissions set in this mask will be cleared by functions that create
  597. /// files or directories. Note that this mask is process-wide, and that
  598. /// *getting* it is not thread safe.
  599. internal static var creationMask: FilePermissions {
  600. get {
  601. let oldMask = _umask(0o22)
  602. _ = _umask(oldMask)
  603. return FilePermissions(rawValue: oldMask)
  604. }
  605. set {
  606. _ = _umask(newValue.rawValue)
  607. }
  608. }
  609. /// Change the file creation permission mask, run some code, then
  610. /// restore it to its original value.
  611. ///
  612. /// - Parameters:
  613. /// - permissions: The new permission mask.
  614. ///
  615. /// This is more efficient than reading `creationMask` and restoring it
  616. /// afterwards, because of the way reading the creation mask works.
  617. internal static func withCreationMask<R>(
  618. _ permissions: FilePermissions,
  619. body: () throws -> R
  620. ) rethrows -> R {
  621. let oldMask = _umask(permissions.rawValue)
  622. defer {
  623. _ = _umask(oldMask)
  624. }
  625. return try body()
  626. }
  627. internal static func _umask(_ mode: CModeT) -> CModeT {
  628. return system_umask(mode)
  629. }
  630. }
  631. #endif // !os(WASI)