1
0

Stat.swift 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683
  1. //===----------------------------------------------------------------------===//
  2. //
  3. // This source file is part of the Swift System open source project
  4. //
  5. // Copyright (c) 2025 - 2026 Apple Inc. and the Swift System project authors
  6. // Licensed under Apache License v2.0 with Runtime Library Exception
  7. //
  8. // See https://swift.org/LICENSE.txt for license information
  9. //
  10. //===----------------------------------------------------------------------===//
  11. #if !os(Windows)
  12. // Must import here to use C stat properties in @_alwaysEmitIntoClient APIs.
  13. #if SYSTEM_PACKAGE_DARWIN
  14. import Darwin
  15. #elseif canImport(Glibc)
  16. import CSystem
  17. import Glibc
  18. #elseif canImport(Musl)
  19. import CSystem
  20. import Musl
  21. #elseif canImport(WASILibc)
  22. import WASILibc
  23. #elseif canImport(Android)
  24. import CSystem
  25. import Android
  26. #else
  27. #error("Unsupported Platform")
  28. #endif
  29. // MARK: - Stat
  30. /// A Swift wrapper of the C `stat` struct.
  31. ///
  32. /// - Note: Only available on Unix-like platforms.
  33. @frozen
  34. @available(System 1.7.0, *)
  35. public struct Stat: RawRepresentable, Sendable {
  36. /// The raw C `stat` struct.
  37. @_alwaysEmitIntoClient
  38. public var rawValue: CInterop.Stat
  39. /// Creates a Swift `Stat` from the raw C struct.
  40. @_alwaysEmitIntoClient
  41. public init(rawValue: CInterop.Stat) { self.rawValue = rawValue }
  42. // MARK: Stat.Flags
  43. /// Flags representing those passed to `fstatat()`.
  44. @frozen
  45. public struct Flags: OptionSet, Sendable, Hashable, Codable {
  46. /// The raw C flags.
  47. @_alwaysEmitIntoClient
  48. public let rawValue: CInt
  49. /// Creates a strongly-typed `Stat.Flags` from raw C flags.
  50. @_alwaysEmitIntoClient
  51. public init(rawValue: CInt) { self.rawValue = rawValue }
  52. /// If the path ends with a symbolic link, return information about the link itself.
  53. ///
  54. /// The corresponding C constant is `AT_SYMLINK_NOFOLLOW`.
  55. @_alwaysEmitIntoClient
  56. public static var symlinkNoFollow: Flags { Flags(rawValue: _AT_SYMLINK_NOFOLLOW) }
  57. #if SYSTEM_PACKAGE_DARWIN
  58. /// If the path ends with a symbolic link, return information about the link itself.
  59. /// If _any_ symbolic link is encountered during path resolution, return an error.
  60. ///
  61. /// The corresponding C constant is `AT_SYMLINK_NOFOLLOW_ANY`.
  62. /// - Note: Only available on Darwin.
  63. @_alwaysEmitIntoClient
  64. public static var symlinkNoFollowAny: Flags { Flags(rawValue: _AT_SYMLINK_NOFOLLOW_ANY) }
  65. #endif
  66. #if canImport(Darwin, _version: 346) || os(FreeBSD)
  67. /// If the path does not reside in the hierarchy beneath the starting directory, return an error.
  68. ///
  69. /// The corresponding C constant is `AT_RESOLVE_BENEATH`.
  70. /// - Note: Only available on Darwin and FreeBSD.
  71. @_alwaysEmitIntoClient
  72. public static var resolveBeneath: Flags { Flags(rawValue: _AT_RESOLVE_BENEATH) }
  73. #endif
  74. }
  75. // MARK: Initializers
  76. /// Creates a `Stat` struct from a `FilePath`.
  77. ///
  78. /// `followTargetSymlink` determines the behavior if `path` ends with a symbolic link.
  79. /// By default, `followTargetSymlink` is `true` and this initializer behaves like `stat()`.
  80. /// If `followTargetSymlink` is set to `false`, this initializer behaves like `lstat()` and
  81. /// returns information about the symlink itself.
  82. ///
  83. /// The corresponding C function is `stat()` or `lstat()` as described above.
  84. @_alwaysEmitIntoClient
  85. public init(
  86. _ path: FilePath,
  87. followTargetSymlink: Bool = true,
  88. retryOnInterrupt: Bool = true
  89. ) throws(Errno) {
  90. self.rawValue = try path.withPlatformString {
  91. Self._stat(
  92. $0,
  93. followTargetSymlink: followTargetSymlink,
  94. retryOnInterrupt: retryOnInterrupt
  95. )
  96. }.get()
  97. }
  98. /// Creates a `Stat` struct from an `UnsafePointer<CChar>` path.
  99. ///
  100. /// `followTargetSymlink` determines the behavior if `path` ends with a symbolic link.
  101. /// By default, `followTargetSymlink` is `true` and this initializer behaves like `stat()`.
  102. /// If `followTargetSymlink` is set to `false`, this initializer behaves like `lstat()` and
  103. /// returns information about the symlink itself.
  104. ///
  105. /// The corresponding C function is `stat()` or `lstat()` as described above.
  106. @_alwaysEmitIntoClient
  107. public init(
  108. _ path: UnsafePointer<CChar>,
  109. followTargetSymlink: Bool = true,
  110. retryOnInterrupt: Bool = true
  111. ) throws(Errno) {
  112. self.rawValue = try Self._stat(
  113. path,
  114. followTargetSymlink: followTargetSymlink,
  115. retryOnInterrupt: retryOnInterrupt
  116. ).get()
  117. }
  118. @usableFromInline
  119. internal static func _stat(
  120. _ ptr: UnsafePointer<CChar>,
  121. followTargetSymlink: Bool,
  122. retryOnInterrupt: Bool
  123. ) -> Result<CInterop.Stat, Errno> {
  124. var result = CInterop.Stat()
  125. return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) {
  126. if followTargetSymlink {
  127. system_stat(ptr, &result)
  128. } else {
  129. system_lstat(ptr, &result)
  130. }
  131. }.map { result }
  132. }
  133. /// Creates a `Stat` struct from a `FileDescriptor`.
  134. ///
  135. /// The corresponding C function is `fstat()`.
  136. @_alwaysEmitIntoClient
  137. public init(
  138. _ fd: FileDescriptor,
  139. retryOnInterrupt: Bool = true
  140. ) throws(Errno) {
  141. self.rawValue = try Self._fstat(
  142. fd,
  143. retryOnInterrupt: retryOnInterrupt
  144. ).get()
  145. }
  146. @usableFromInline
  147. internal static func _fstat(
  148. _ fd: FileDescriptor,
  149. retryOnInterrupt: Bool
  150. ) -> Result<CInterop.Stat, Errno> {
  151. var result = CInterop.Stat()
  152. return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) {
  153. system_fstat(fd.rawValue, &result)
  154. }.map { result }
  155. }
  156. /// Creates a `Stat` struct from a `FilePath` and `Flags`.
  157. ///
  158. /// If `path` is relative, it is resolved against the current working directory.
  159. ///
  160. /// The corresponding C function is `fstatat()`.
  161. @_alwaysEmitIntoClient
  162. public init(
  163. _ path: FilePath,
  164. flags: Stat.Flags,
  165. retryOnInterrupt: Bool = true
  166. ) throws(Errno) {
  167. self.rawValue = try path.withPlatformString {
  168. Self._fstatat(
  169. $0,
  170. relativeTo: _AT_FDCWD,
  171. flags: flags,
  172. retryOnInterrupt: retryOnInterrupt
  173. )
  174. }.get()
  175. }
  176. /// Creates a `Stat` struct from a `FilePath` and `Flags`,
  177. /// including a `FileDescriptor` to resolve a relative path.
  178. ///
  179. /// If `path` is absolute (starts with a forward slash), then `fd` is ignored.
  180. /// If `path` is relative, it is resolved against the directory given by `fd`.
  181. ///
  182. /// The corresponding C function is `fstatat()`.
  183. @_alwaysEmitIntoClient
  184. public init(
  185. _ path: FilePath,
  186. relativeTo fd: FileDescriptor,
  187. flags: Stat.Flags,
  188. retryOnInterrupt: Bool = true
  189. ) throws(Errno) {
  190. self.rawValue = try path.withPlatformString {
  191. Self._fstatat(
  192. $0,
  193. relativeTo: fd.rawValue,
  194. flags: flags,
  195. retryOnInterrupt: retryOnInterrupt
  196. )
  197. }.get()
  198. }
  199. /// Creates a `Stat` struct from an `UnsafePointer<CChar>` path and `Flags`.
  200. ///
  201. /// If `path` is relative, it is resolved against the current working directory.
  202. ///
  203. /// The corresponding C function is `fstatat()`.
  204. @_alwaysEmitIntoClient
  205. public init(
  206. _ path: UnsafePointer<CChar>,
  207. flags: Stat.Flags,
  208. retryOnInterrupt: Bool = true
  209. ) throws(Errno) {
  210. self.rawValue = try Self._fstatat(
  211. path,
  212. relativeTo: _AT_FDCWD,
  213. flags: flags,
  214. retryOnInterrupt: retryOnInterrupt
  215. ).get()
  216. }
  217. /// Creates a `Stat` struct from an `UnsafePointer<CChar>` path and `Flags`,
  218. /// including a `FileDescriptor` to resolve a relative path.
  219. ///
  220. /// If `path` is absolute (starts with a forward slash), then `fd` is ignored.
  221. /// If `path` is relative, it is resolved against the directory given by `fd`.
  222. ///
  223. /// The corresponding C function is `fstatat()`.
  224. @_alwaysEmitIntoClient
  225. public init(
  226. _ path: UnsafePointer<CChar>,
  227. relativeTo fd: FileDescriptor,
  228. flags: Stat.Flags,
  229. retryOnInterrupt: Bool = true
  230. ) throws(Errno) {
  231. self.rawValue = try Self._fstatat(
  232. path,
  233. relativeTo: fd.rawValue,
  234. flags: flags,
  235. retryOnInterrupt: retryOnInterrupt
  236. ).get()
  237. }
  238. @usableFromInline
  239. internal static func _fstatat(
  240. _ path: UnsafePointer<CChar>,
  241. relativeTo fd: FileDescriptor.RawValue,
  242. flags: Stat.Flags,
  243. retryOnInterrupt: Bool
  244. ) -> Result<CInterop.Stat, Errno> {
  245. var result = CInterop.Stat()
  246. return nothingOrErrno(retryOnInterrupt: retryOnInterrupt) {
  247. system_fstatat(fd, path, &result, flags.rawValue)
  248. }.map { result }
  249. }
  250. // MARK: Properties
  251. /// ID of device containing file
  252. ///
  253. /// The corresponding C property is `st_dev`.
  254. @_alwaysEmitIntoClient
  255. public var deviceID: DeviceID {
  256. get { DeviceID(rawValue: numericCast(rawValue.st_dev)) }
  257. set { rawValue.st_dev = numericCast(newValue.rawValue) }
  258. }
  259. /// Inode number
  260. ///
  261. /// The corresponding C property is `st_ino`.
  262. @_alwaysEmitIntoClient
  263. public var inode: Inode {
  264. get { Inode(rawValue: numericCast(rawValue.st_ino)) }
  265. set { rawValue.st_ino = numericCast(newValue.rawValue) }
  266. }
  267. /// File mode
  268. ///
  269. /// The corresponding C property is `st_mode`.
  270. @_alwaysEmitIntoClient
  271. public var mode: FileMode {
  272. get { FileMode(rawValue: numericCast(rawValue.st_mode)) }
  273. set { rawValue.st_mode = numericCast(newValue.rawValue) }
  274. }
  275. /// File type for the given mode
  276. ///
  277. /// - Note: This property is equivalent to `mode.type`. Modifying this
  278. /// property will update the underlying `st_mode` accordingly.
  279. @_alwaysEmitIntoClient
  280. public var type: FileType {
  281. get { mode.type }
  282. set {
  283. var newMode = mode
  284. newMode.type = newValue
  285. mode = newMode
  286. }
  287. }
  288. /// File permissions for the given mode
  289. ///
  290. /// - Note: This property is equivalent to `mode.permissions`. Modifying
  291. /// this property will update the underlying `st_mode` accordingly.
  292. @_alwaysEmitIntoClient
  293. public var permissions: FilePermissions {
  294. get { mode.permissions }
  295. set {
  296. var newMode = mode
  297. newMode.permissions = newValue
  298. mode = newMode
  299. }
  300. }
  301. /// Number of hard links
  302. ///
  303. /// The corresponding C property is `st_nlink`.
  304. @_alwaysEmitIntoClient
  305. public var linkCount: Int {
  306. get { Int(rawValue.st_nlink) }
  307. set { rawValue.st_nlink = numericCast(newValue) }
  308. }
  309. /// User ID of owner
  310. ///
  311. /// The corresponding C property is `st_uid`.
  312. @_alwaysEmitIntoClient
  313. public var userID: UserID {
  314. get { UserID(rawValue: rawValue.st_uid) }
  315. set { rawValue.st_uid = newValue.rawValue }
  316. }
  317. /// Group ID of owner
  318. ///
  319. /// The corresponding C property is `st_gid`.
  320. @_alwaysEmitIntoClient
  321. public var groupID: GroupID {
  322. get { GroupID(rawValue: rawValue.st_gid) }
  323. set { rawValue.st_gid = newValue.rawValue }
  324. }
  325. /// Device ID (if special file)
  326. ///
  327. /// For character or block special files, the returned `DeviceID` may have
  328. /// meaningful major and minor values. For non-special files, this
  329. /// property is usually meaningless and often set to 0.
  330. ///
  331. /// The corresponding C property is `st_rdev`.
  332. @_alwaysEmitIntoClient
  333. public var specialDeviceID: DeviceID {
  334. get { DeviceID(rawValue: numericCast(rawValue.st_rdev)) }
  335. set { rawValue.st_rdev = numericCast(newValue.rawValue) }
  336. }
  337. /// Total size, in bytes
  338. ///
  339. /// The semantics of this property are tied to the underlying C `st_size` field,
  340. /// which can have file-system–dependent behavior. For example, this property
  341. /// can return different values for a file's data fork and resource fork, and some
  342. /// file systems report logical size rather than actual disk usage for compressed
  343. /// or cloned files.
  344. ///
  345. /// The corresponding C property is `st_size`.
  346. @_alwaysEmitIntoClient
  347. public var size: Int64 {
  348. get { Int64(rawValue.st_size) }
  349. set { rawValue.st_size = numericCast(newValue) }
  350. }
  351. /// Block size for file system I/O, in bytes
  352. ///
  353. /// The corresponding C property is `st_blksize`.
  354. @_alwaysEmitIntoClient
  355. public var preferredIOBlockSize: Int {
  356. get { Int(rawValue.st_blksize) }
  357. set { rawValue.st_blksize = numericCast(newValue) }
  358. }
  359. /// Number of 512-byte blocks allocated
  360. ///
  361. /// The semantics of this property are tied to the underlying C `st_blocks` field,
  362. /// which can have file-system–dependent behavior.
  363. ///
  364. /// The corresponding C property is `st_blocks`.
  365. @_alwaysEmitIntoClient
  366. public var blocksAllocated: Int64 {
  367. get { Int64(rawValue.st_blocks) }
  368. set { rawValue.st_blocks = numericCast(newValue) }
  369. }
  370. /// Total size allocated, in bytes
  371. ///
  372. /// The semantics of this property are tied to the underlying C `st_blocks` field,
  373. /// which can have file-system–dependent behavior.
  374. ///
  375. /// - Note: Calculated as `512 * blocksAllocated`.
  376. @_alwaysEmitIntoClient
  377. public var sizeAllocated: Int64 {
  378. 512 * blocksAllocated
  379. }
  380. // NOTE: "st_" property names are used for the `timespec` properties so
  381. // we can reserve `accessTime`, `modificationTime`, etc. for potential
  382. // `UTCClock.Instant` properties in the future.
  383. /// Time of last access, given as a C `timespec` since the Epoch.
  384. ///
  385. /// The corresponding C property is `st_atim` (or `st_atimespec` on Darwin).
  386. @_alwaysEmitIntoClient
  387. public var st_atim: timespec {
  388. get {
  389. #if SYSTEM_PACKAGE_DARWIN
  390. rawValue.st_atimespec
  391. #else
  392. rawValue.st_atim
  393. #endif
  394. }
  395. set {
  396. #if SYSTEM_PACKAGE_DARWIN
  397. rawValue.st_atimespec = newValue
  398. #else
  399. rawValue.st_atim = newValue
  400. #endif
  401. }
  402. }
  403. /// Time of last modification, given as a C `timespec` since the Epoch.
  404. ///
  405. /// The corresponding C property is `st_mtim` (or `st_mtimespec` on Darwin).
  406. @_alwaysEmitIntoClient
  407. public var st_mtim: timespec {
  408. get {
  409. #if SYSTEM_PACKAGE_DARWIN
  410. rawValue.st_mtimespec
  411. #else
  412. rawValue.st_mtim
  413. #endif
  414. }
  415. set {
  416. #if SYSTEM_PACKAGE_DARWIN
  417. rawValue.st_mtimespec = newValue
  418. #else
  419. rawValue.st_mtim = newValue
  420. #endif
  421. }
  422. }
  423. /// Time of last status (inode) change, given as a C `timespec` since the Epoch.
  424. ///
  425. /// The corresponding C property is `st_ctim` (or `st_ctimespec` on Darwin).
  426. @_alwaysEmitIntoClient
  427. public var st_ctim: timespec {
  428. get {
  429. #if SYSTEM_PACKAGE_DARWIN
  430. rawValue.st_ctimespec
  431. #else
  432. rawValue.st_ctim
  433. #endif
  434. }
  435. set {
  436. #if SYSTEM_PACKAGE_DARWIN
  437. rawValue.st_ctimespec = newValue
  438. #else
  439. rawValue.st_ctim = newValue
  440. #endif
  441. }
  442. }
  443. #if SYSTEM_PACKAGE_DARWIN || os(FreeBSD)
  444. /// Time of file creation, given as a C `timespec` since the Epoch.
  445. ///
  446. /// The corresponding C property is `st_birthtim` (or `st_birthtimespec` on Darwin).
  447. /// - Note: Only available on Darwin and FreeBSD.
  448. @_alwaysEmitIntoClient
  449. public var st_birthtim: timespec {
  450. get {
  451. #if SYSTEM_PACKAGE_DARWIN
  452. rawValue.st_birthtimespec
  453. #else
  454. rawValue.st_birthtim
  455. #endif
  456. }
  457. set {
  458. #if SYSTEM_PACKAGE_DARWIN
  459. rawValue.st_birthtimespec = newValue
  460. #else
  461. rawValue.st_birthtim = newValue
  462. #endif
  463. }
  464. }
  465. #endif
  466. #if SYSTEM_PACKAGE_DARWIN || os(FreeBSD) || os(OpenBSD)
  467. /// File flags
  468. ///
  469. /// The corresponding C property is `st_flags`.
  470. /// - Note: Only available on Darwin, FreeBSD, and OpenBSD.
  471. @_alwaysEmitIntoClient
  472. public var flags: FileFlags {
  473. get { FileFlags(rawValue: rawValue.st_flags) }
  474. set { rawValue.st_flags = newValue.rawValue }
  475. }
  476. /// File generation number
  477. ///
  478. /// The file generation number may be used to distinguish between different
  479. /// files that have used the same inode over time.
  480. ///
  481. /// The corresponding C property is `st_gen`.
  482. /// - Note: Only available on Darwin, FreeBSD, and OpenBSD. The underlying C
  483. /// field is 32-bit on Darwin and OpenBSD, and 64-bit on FreeBSD.
  484. @_alwaysEmitIntoClient
  485. public var generationNumber: UInt64 {
  486. get { UInt64(rawValue.st_gen) }
  487. set { rawValue.st_gen = numericCast(newValue) }
  488. }
  489. #endif
  490. }
  491. // MARK: - Equatable and Hashable
  492. @available(System 1.7.0, *)
  493. extension Stat: Equatable {
  494. /// Compares the meaningful file-metadata fields of two `Stat` values.
  495. ///
  496. /// Alignment padding and platform reserved/"spare" fields are not compared.
  497. public static func == (lhs: Self, rhs: Self) -> Bool {
  498. guard lhs.rawValue.st_dev == rhs.rawValue.st_dev,
  499. lhs.rawValue.st_ino == rhs.rawValue.st_ino,
  500. lhs.rawValue.st_mode == rhs.rawValue.st_mode,
  501. lhs.rawValue.st_nlink == rhs.rawValue.st_nlink,
  502. lhs.rawValue.st_uid == rhs.rawValue.st_uid,
  503. lhs.rawValue.st_gid == rhs.rawValue.st_gid,
  504. lhs.rawValue.st_rdev == rhs.rawValue.st_rdev,
  505. lhs.rawValue.st_size == rhs.rawValue.st_size,
  506. lhs.rawValue.st_blksize == rhs.rawValue.st_blksize,
  507. lhs.rawValue.st_blocks == rhs.rawValue.st_blocks,
  508. lhs.st_atim.tv_sec == rhs.st_atim.tv_sec,
  509. lhs.st_atim.tv_nsec == rhs.st_atim.tv_nsec,
  510. lhs.st_mtim.tv_sec == rhs.st_mtim.tv_sec,
  511. lhs.st_mtim.tv_nsec == rhs.st_mtim.tv_nsec,
  512. lhs.st_ctim.tv_sec == rhs.st_ctim.tv_sec,
  513. lhs.st_ctim.tv_nsec == rhs.st_ctim.tv_nsec else {
  514. return false
  515. }
  516. #if SYSTEM_PACKAGE_DARWIN || os(FreeBSD)
  517. guard lhs.st_birthtim.tv_sec == rhs.st_birthtim.tv_sec,
  518. lhs.st_birthtim.tv_nsec == rhs.st_birthtim.tv_nsec else {
  519. return false
  520. }
  521. #endif
  522. #if SYSTEM_PACKAGE_DARWIN || os(FreeBSD) || os(OpenBSD)
  523. guard lhs.rawValue.st_flags == rhs.rawValue.st_flags,
  524. lhs.rawValue.st_gen == rhs.rawValue.st_gen else {
  525. return false
  526. }
  527. #endif
  528. return true
  529. }
  530. }
  531. @available(System 1.7.0, *)
  532. extension Stat: Hashable {
  533. /// Hashes the meaningful file-metadata fields of a `Stat` struct.
  534. ///
  535. /// These are the same fields compared by `==`, fed in the same order.
  536. /// Alignment padding and platform reserved/"spare" fields are not hashed.
  537. public func hash(into hasher: inout Hasher) {
  538. hasher.combine(rawValue.st_dev)
  539. hasher.combine(rawValue.st_ino)
  540. hasher.combine(rawValue.st_mode)
  541. hasher.combine(rawValue.st_nlink)
  542. hasher.combine(rawValue.st_uid)
  543. hasher.combine(rawValue.st_gid)
  544. hasher.combine(rawValue.st_rdev)
  545. hasher.combine(rawValue.st_size)
  546. hasher.combine(rawValue.st_blksize)
  547. hasher.combine(rawValue.st_blocks)
  548. hasher.combine(st_atim.tv_sec)
  549. hasher.combine(st_atim.tv_nsec)
  550. hasher.combine(st_mtim.tv_sec)
  551. hasher.combine(st_mtim.tv_nsec)
  552. hasher.combine(st_ctim.tv_sec)
  553. hasher.combine(st_ctim.tv_nsec)
  554. #if SYSTEM_PACKAGE_DARWIN || os(FreeBSD)
  555. hasher.combine(st_birthtim.tv_sec)
  556. hasher.combine(st_birthtim.tv_nsec)
  557. #endif
  558. #if SYSTEM_PACKAGE_DARWIN || os(FreeBSD) || os(OpenBSD)
  559. hasher.combine(rawValue.st_flags)
  560. hasher.combine(rawValue.st_gen)
  561. #endif
  562. }
  563. }
  564. // MARK: - CustomStringConvertible and CustomDebugStringConvertible
  565. // MARK: - FileDescriptor Extensions
  566. @available(System 1.7.0, *)
  567. extension FileDescriptor {
  568. /// Creates a `Stat` struct for the file referenced by this `FileDescriptor`.
  569. ///
  570. /// The corresponding C function is `fstat()`.
  571. @_alwaysEmitIntoClient
  572. public func stat(
  573. retryOnInterrupt: Bool = true
  574. ) throws(Errno) -> Stat {
  575. try Stat(self, retryOnInterrupt: retryOnInterrupt)
  576. }
  577. }
  578. // MARK: - FilePath Extensions
  579. @available(System 1.7.0, *)
  580. extension FilePath {
  581. /// Creates a `Stat` struct for the file referenced by this `FilePath`.
  582. ///
  583. /// `followTargetSymlink` determines the behavior if `path` ends with a symbolic link.
  584. /// By default, `followTargetSymlink` is `true` and this initializer behaves like `stat()`.
  585. /// If `followTargetSymlink` is set to `false`, this initializer behaves like `lstat()` and
  586. /// returns information about the symlink itself.
  587. ///
  588. /// The corresponding C function is `stat()` or `lstat()` as described above.
  589. @_alwaysEmitIntoClient
  590. public func stat(
  591. followTargetSymlink: Bool = true,
  592. retryOnInterrupt: Bool = true
  593. ) throws(Errno) -> Stat {
  594. try Stat(self, followTargetSymlink: followTargetSymlink, retryOnInterrupt: retryOnInterrupt)
  595. }
  596. /// Creates a `Stat` struct for the file referenced by this `FilePath` using the given `Flags`.
  597. ///
  598. /// If `path` is relative, it is resolved against the current working directory.
  599. ///
  600. /// The corresponding C function is `fstatat()`.
  601. @_alwaysEmitIntoClient
  602. public func stat(
  603. flags: Stat.Flags,
  604. retryOnInterrupt: Bool = true
  605. ) throws(Errno) -> Stat {
  606. try Stat(self, flags: flags, retryOnInterrupt: retryOnInterrupt)
  607. }
  608. /// Creates a `Stat` struct for the file referenced by this `FilePath` using the given `Flags`,
  609. /// including a `FileDescriptor` to resolve a relative path.
  610. ///
  611. /// If `path` is absolute (starts with a forward slash), then `fd` is ignored.
  612. /// If `path` is relative, it is resolved against the directory given by `fd`.
  613. ///
  614. /// The corresponding C function is `fstatat()`.
  615. @_alwaysEmitIntoClient
  616. public func stat(
  617. relativeTo fd: FileDescriptor,
  618. flags: Stat.Flags,
  619. retryOnInterrupt: Bool = true
  620. ) throws(Errno) -> Stat {
  621. try Stat(self, relativeTo: fd, flags: flags, retryOnInterrupt: retryOnInterrupt)
  622. }
  623. }
  624. #endif // !os(Windows)