diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..4ed4dbd1 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,9 @@ +By submitting a pull request, you represent that you have the right to license your +contribution to Apple and the community, and agree by submitting the patch that +your contributions are licensed under the [Swift license](https://swift.org/LICENSE.txt). + +--- + +Before submitting the pull request, please make sure you have tested your changes +and that they follow the Swift project [guidelines for contributing +code](https://swift.org/contributing/#contributing-code). diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 00000000..004d67af --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,211 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + + +## Runtime Library Exception to the Apache 2.0 License: ## + + + As an exception, if you use this Software to compile your source code and + portions of this Software are embedded into the binary product as a result, + you may redistribute such product without providing attribution as would + otherwise be required by Sections 4(a), 4(b) and 4(d) of the License. \ No newline at end of file diff --git a/Package.swift b/Package.swift new file mode 100644 index 00000000..379133a5 --- /dev/null +++ b/Package.swift @@ -0,0 +1,39 @@ +// swift-tools-version:5.2 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +import PackageDescription + +let targets: [PackageDescription.Target] = [ + .target( + name: "SystemPackage", + dependencies: ["SystemInternals"], + path: "Sources/System"), + .target( + name: "SystemInternals", + dependencies: ["CSystem"]), + .target( + name: "CSystem", + dependencies: []), + + .testTarget( + name: "SystemTests", + dependencies: ["SystemPackage"]), +] + +let package = Package( + name: "swift-system", + products: [ + .library(name: "SystemPackage", targets: ["SystemPackage"]), + ], + dependencies: [], + targets: targets +) diff --git a/README.md b/README.md new file mode 100644 index 00000000..4b36b9d4 --- /dev/null +++ b/README.md @@ -0,0 +1,59 @@ +# Swift System + +Swift System provides idiomatic interfaces to system calls and low-level currency types. Our vision is for System to act as the single home for low-level system interfaces for all supported Swift platforms. + +## Multi-platform not Cross-platform + +System is a multi-platform library, not a cross-platform one. It provides a separate set of APIs and behaviors on every supported platform, closely reflecting the underlying OS interfaces. A single import will pull in the native platform interfaces specific for the targeted OS. + +Our immediate goal is to simplify building cross-platform libraries and applications such as SwiftNIO and SwiftPM. System does not eliminate the need for `#if os()` conditionals to implement cross-platform abstractions, but it does make it safer and more expressive to fill out the platform-specific parts. + +## Usage + +```swift +import SystemPackage + +let message: String = "Hello, world!" + "\n" +let path: FilePath = "/tmp/log" +let fd = try FileDescriptor.open( + path, .writeOnly, options: [.append, .create], permissions: .ownerReadWrite) +try fd.closeAfter { + _ = try fd.writeAll(message.utf8) +} +``` + +## Adding `SystemPackage` as a Dependency + +To use the `SystemPackage` library in a SwiftPM project, +add the following line to the dependencies in your `Package.swift` file: + +```swift +.package(url: "https://github.com/apple/swift-system", from: "0.0.1"), +``` + +Because `SystemPackage` is under active development, +source-stability is only guaranteed within minor versions (e.g. between `0.0.3` and `0.0.4`). +If you don't want potentially source-breaking package updates, +use this dependency specification instead: + +```swift +.package(url: "https://github.com/apple/swift-system", .upToNextMinor(from: "0.0.1")), +``` + +Finally, include `"SystemPackage"` as a dependency for your executable target: + +```swift +let package = Package( + // name, platforms, products, etc. + dependencies: [ + .package(url: "https://github.com/apple/swift-system", from: "0.0.1"), + // other dependencies + ], + targets: [ + .target(name: "MyTarget", dependencies: [ + .product(name: "SystemPackage", package: "swift-system"), + ]), + // other targets + ] +) +``` diff --git a/Sources/CSystem/include/CSystemLinux.h b/Sources/CSystem/include/CSystemLinux.h new file mode 100644 index 00000000..b172d658 --- /dev/null +++ b/Sources/CSystem/include/CSystemLinux.h @@ -0,0 +1,25 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +#ifdef __linux__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + diff --git a/Sources/CSystem/shims.c b/Sources/CSystem/shims.c new file mode 100644 index 00000000..1f9eb595 --- /dev/null +++ b/Sources/CSystem/shims.c @@ -0,0 +1,14 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +#ifdef __linux__ + +#include + +#endif \ No newline at end of file diff --git a/Sources/System/DarwinPlatformConstants.swift b/Sources/System/DarwinPlatformConstants.swift new file mode 100644 index 00000000..305fd356 --- /dev/null +++ b/Sources/System/DarwinPlatformConstants.swift @@ -0,0 +1,427 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + +// For platform constants redefined in Swift. We redefine them here so that they +// can be @_alwaysEmitIntoClient without depending on Darwin or re-including a +// header (and applying attributes). + +// MARK: errno +@_alwaysEmitIntoClient +internal var _ERRNO_NOT_USED: CInt { 0 } + +@_alwaysEmitIntoClient +internal var _EPERM: CInt { 1 } + +@_alwaysEmitIntoClient +internal var _ENOENT: CInt { 2 } + +@_alwaysEmitIntoClient +internal var _ESRCH: CInt { 3 } + +@_alwaysEmitIntoClient +internal var _EINTR: CInt { 4 } + +@_alwaysEmitIntoClient +internal var _EIO: CInt { 5 } + +@_alwaysEmitIntoClient +internal var _ENXIO: CInt { 6 } + +@_alwaysEmitIntoClient +internal var _E2BIG: CInt { 7 } + +@_alwaysEmitIntoClient +internal var _ENOEXEC: CInt { 8 } + +@_alwaysEmitIntoClient +internal var _EBADF: CInt { 9 } + +@_alwaysEmitIntoClient +internal var _ECHILD: CInt { 10 } + +@_alwaysEmitIntoClient +internal var _EDEADLK: CInt { 11 } + +@_alwaysEmitIntoClient +internal var _ENOMEM: CInt { 12 } + +@_alwaysEmitIntoClient +internal var _EACCES: CInt { 13 } + +@_alwaysEmitIntoClient +internal var _EFAULT: CInt { 14 } + +@_alwaysEmitIntoClient +internal var _ENOTBLK: CInt { 15 } + +@_alwaysEmitIntoClient +internal var _EBUSY: CInt { 16 } + +@_alwaysEmitIntoClient +internal var _EEXIST: CInt { 17 } + +@_alwaysEmitIntoClient +internal var _EXDEV: CInt { 18 } + +@_alwaysEmitIntoClient +internal var _ENODEV: CInt { 19 } + +@_alwaysEmitIntoClient +internal var _ENOTDIR: CInt { 20 } + +@_alwaysEmitIntoClient +internal var _EISDIR: CInt { 21 } + +@_alwaysEmitIntoClient +internal var _EINVAL: CInt { 22 } + +@_alwaysEmitIntoClient +internal var _ENFILE: CInt { 23 } + +@_alwaysEmitIntoClient +internal var _EMFILE: CInt { 24 } + +@_alwaysEmitIntoClient +internal var _ENOTTY: CInt { 25 } + +@_alwaysEmitIntoClient +internal var _ETXTBSY: CInt { 26 } + +@_alwaysEmitIntoClient +internal var _EFBIG: CInt { 27 } + +@_alwaysEmitIntoClient +internal var _ENOSPC: CInt { 28 } + +@_alwaysEmitIntoClient +internal var _ESPIPE: CInt { 29 } + +@_alwaysEmitIntoClient +internal var _EROFS: CInt { 30 } + +@_alwaysEmitIntoClient +internal var _EMLINK: CInt { 31 } + +@_alwaysEmitIntoClient +internal var _EPIPE: CInt { 32 } + +@_alwaysEmitIntoClient +internal var _EDOM: CInt { 33 } + +@_alwaysEmitIntoClient +internal var _ERANGE: CInt { 34 } + +@_alwaysEmitIntoClient +internal var _EAGAIN: CInt { 35 } + +@_alwaysEmitIntoClient +internal var _EWOULDBLOCK: CInt { _EAGAIN } + +@_alwaysEmitIntoClient +internal var _EINPROGRESS: CInt { 36 } + +@_alwaysEmitIntoClient +internal var _EALREADY: CInt { 37 } + +@_alwaysEmitIntoClient +internal var _ENOTSOCK: CInt { 38 } + +@_alwaysEmitIntoClient +internal var _EDESTADDRREQ: CInt { 39 } + +@_alwaysEmitIntoClient +internal var _EMSGSIZE: CInt { 40 } + +@_alwaysEmitIntoClient +internal var _EPROTOTYPE: CInt { 41 } + +@_alwaysEmitIntoClient +internal var _ENOPROTOOPT: CInt { 42 } + +@_alwaysEmitIntoClient +internal var _EPROTONOSUPPORT: CInt { 43 } + +@_alwaysEmitIntoClient +internal var _ESOCKTNOSUPPORT: CInt { 44 } + +@_alwaysEmitIntoClient +internal var _ENOTSUP: CInt { 45 } + +@_alwaysEmitIntoClient +internal var _EPFNOSUPPORT: CInt { 46 } + +@_alwaysEmitIntoClient +internal var _EAFNOSUPPORT: CInt { 47 } + +@_alwaysEmitIntoClient +internal var _EADDRINUSE: CInt { 48 } + +@_alwaysEmitIntoClient +internal var _EADDRNOTAVAIL: CInt { 49 } + +@_alwaysEmitIntoClient +internal var _ENETDOWN: CInt { 50 } + +@_alwaysEmitIntoClient +internal var _ENETUNREACH: CInt { 51 } + +@_alwaysEmitIntoClient +internal var _ENETRESET: CInt { 52 } + +@_alwaysEmitIntoClient +internal var _ECONNABORTED: CInt { 53 } + +@_alwaysEmitIntoClient +internal var _ECONNRESET: CInt { 54 } + +@_alwaysEmitIntoClient +internal var _ENOBUFS: CInt { 55 } + +@_alwaysEmitIntoClient +internal var _EISCONN: CInt { 56 } + +@_alwaysEmitIntoClient +internal var _ENOTCONN: CInt { 57 } + +@_alwaysEmitIntoClient +internal var _ESHUTDOWN: CInt { 58 } + +@_alwaysEmitIntoClient +internal var _ETOOMANYREFS: CInt { 59 } + +@_alwaysEmitIntoClient +internal var _ETIMEDOUT: CInt { 60 } + +@_alwaysEmitIntoClient +internal var _ECONNREFUSED: CInt { 61 } + +@_alwaysEmitIntoClient +internal var _ELOOP: CInt { 62 } + +@_alwaysEmitIntoClient +internal var _ENAMETOOLONG: CInt { 63 } + +@_alwaysEmitIntoClient +internal var _EHOSTDOWN: CInt { 64 } + +@_alwaysEmitIntoClient +internal var _EHOSTUNREACH: CInt { 65 } + +@_alwaysEmitIntoClient +internal var _ENOTEMPTY: CInt { 66 } + +@_alwaysEmitIntoClient +internal var _EPROCLIM: CInt { 67 } + +@_alwaysEmitIntoClient +internal var _EUSERS: CInt { 68 } + +@_alwaysEmitIntoClient +internal var _EDQUOT: CInt { 69 } + +@_alwaysEmitIntoClient +internal var _ESTALE: CInt { 70 } + +@_alwaysEmitIntoClient +internal var _EREMOTE: CInt { 71 } + +@_alwaysEmitIntoClient +internal var _EBADRPC: CInt { 72 } + +@_alwaysEmitIntoClient +internal var _ERPCMISMATCH: CInt { 73 } + +@_alwaysEmitIntoClient +internal var _EPROGUNAVAIL: CInt { 74 } + +@_alwaysEmitIntoClient +internal var _EPROGMISMATCH: CInt { 75 } + +@_alwaysEmitIntoClient +internal var _EPROCUNAVAIL: CInt { 76 } + +@_alwaysEmitIntoClient +internal var _ENOLCK: CInt { 77 } + +@_alwaysEmitIntoClient +internal var _ENOSYS: CInt { 78 } + +@_alwaysEmitIntoClient +internal var _EFTYPE: CInt { 79 } + +@_alwaysEmitIntoClient +internal var _EAUTH: CInt { 80 } + +@_alwaysEmitIntoClient +internal var _ENEEDAUTH: CInt { 81 } + +@_alwaysEmitIntoClient +internal var _EPWROFF: CInt { 82 } + +@_alwaysEmitIntoClient +internal var _EDEVERR: CInt { 83 } + +@_alwaysEmitIntoClient +internal var _EOVERFLOW: CInt { 84 } + +@_alwaysEmitIntoClient +internal var _EBADEXEC: CInt { 85 } + +@_alwaysEmitIntoClient +internal var _EBADARCH: CInt { 86 } + +@_alwaysEmitIntoClient +internal var _ESHLIBVERS: CInt { 87 } + +@_alwaysEmitIntoClient +internal var _EBADMACHO: CInt { 88 } + +@_alwaysEmitIntoClient +internal var _ECANCELED: CInt { 89 } + +@_alwaysEmitIntoClient +internal var _EIDRM: CInt { 90 } + +@_alwaysEmitIntoClient +internal var _ENOMSG: CInt { 91 } + +@_alwaysEmitIntoClient +internal var _EILSEQ: CInt { 92 } + +@_alwaysEmitIntoClient +internal var _ENOATTR: CInt { 93 } + +@_alwaysEmitIntoClient +internal var _EBADMSG: CInt { 94 } + +@_alwaysEmitIntoClient +internal var _EMULTIHOP: CInt { 95 } + +@_alwaysEmitIntoClient +internal var _ENODATA: CInt { 96 } + +@_alwaysEmitIntoClient +internal var _ENOLINK: CInt { 97 } + +@_alwaysEmitIntoClient +internal var _ENOSR: CInt { 98 } + +@_alwaysEmitIntoClient +internal var _ENOSTR: CInt { 99 } + +@_alwaysEmitIntoClient +internal var _EPROTO: CInt { 100 } + +@_alwaysEmitIntoClient +internal var _ETIME: CInt { 101 } + +@_alwaysEmitIntoClient +internal var _EOPNOTSUPP: CInt { 102 } + +@_alwaysEmitIntoClient +internal var _ENOPOLICY: CInt { 103 } + +@_alwaysEmitIntoClient +internal var _ENOTRECOVERABLE: CInt { 104 } + +@_alwaysEmitIntoClient +internal var _EOWNERDEAD: CInt { 105 } + +@_alwaysEmitIntoClient +internal var _EQFULL: CInt { 106 } + +@_alwaysEmitIntoClient +internal var _ELAST: CInt { 106 } + +// MARK: File Operations + +@_alwaysEmitIntoClient +internal var _O_RDONLY: CInt { 0x0000 } + +@_alwaysEmitIntoClient +internal var _O_WRONLY: CInt { 0x0001 } + +@_alwaysEmitIntoClient +internal var _O_RDWR: CInt { 0x0002 } + +// TODO: API? +@_alwaysEmitIntoClient +internal var _O_ACCMODE: CInt { 0x0003 } + +@_alwaysEmitIntoClient +internal var _O_NONBLOCK: CInt { 0x0004 } + +@_alwaysEmitIntoClient +internal var _O_APPEND: CInt { 0x0008 } + +@_alwaysEmitIntoClient +internal var _O_SHLOCK: CInt { 0x0010 } + +@_alwaysEmitIntoClient +internal var _O_EXLOCK: CInt { 0x0020 } + +// TODO: API? +@_alwaysEmitIntoClient +internal var _O_ASYNC: CInt { 0x0040 } + +@_alwaysEmitIntoClient +internal var _O_NOFOLLOW: CInt { 0x0100 } + +@_alwaysEmitIntoClient +internal var _O_CREAT: CInt { 0x0200 } + +@_alwaysEmitIntoClient +internal var _O_TRUNC: CInt { 0x0400 } + +@_alwaysEmitIntoClient +internal var _O_EXCL: CInt { 0x0800 } + +@_alwaysEmitIntoClient +internal var _O_EVTONLY: CInt { 0x8000 } + +// TODO: API? +@_alwaysEmitIntoClient +internal var _O_NOCTTY: CInt { 0x20000 } + +// TODO: API? +@_alwaysEmitIntoClient +internal var _O_DIRECTORY: CInt { 0x100000 } + +@_alwaysEmitIntoClient +internal var _O_SYMLINK: CInt { 0x200000 } + +@_alwaysEmitIntoClient +internal var _O_CLOEXEC: CInt { 0x1000000 } + +// TODO: API? +@_alwaysEmitIntoClient +internal var _O_DP_GETRAWENCRYPTED: CInt { 0x0001 } + +// TODO: API? +@_alwaysEmitIntoClient +internal var _O_DP_GETRAWUNENCRYPTED: CInt { 0x0002 } + +@_alwaysEmitIntoClient +internal var _SEEK_SET: CInt { 0 } + +@_alwaysEmitIntoClient +internal var _SEEK_CUR: CInt { 1 } + +@_alwaysEmitIntoClient +internal var _SEEK_END: CInt { 2 } + +@_alwaysEmitIntoClient +internal var _SEEK_HOLE: CInt { 3 } + +@_alwaysEmitIntoClient +internal var _SEEK_DATA: CInt { 4 } + +#endif diff --git a/Sources/System/Errno.swift b/Sources/System/Errno.swift new file mode 100644 index 00000000..9a6ef687 --- /dev/null +++ b/Sources/System/Errno.swift @@ -0,0 +1,1512 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// An error number used by system calls to communicate what kind of error +/// occurred. +@frozen +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public struct Errno: RawRepresentable, Error, Hashable, Codable { + /// The raw C error number. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Creates a strongly typed error number from a raw C error number. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CInt) { self.init(rawValue: raw) } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Error. Not used. + @_alwaysEmitIntoClient + public static var notUsed: Errno { Errno(_ERRNO_NOT_USED) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notUsed") + public static var ERRNO_NOT_USED: Errno { notUsed } +#endif + + /// Operation not permitted. + /// + /// An attempt was made to perform an operation + /// limited to processes with appropriate privileges + /// or to the owner of a file or other resources. + /// + /// The corresponding C error is `EPERM`. + @_alwaysEmitIntoClient + public static var notPermitted: Errno { Errno(_EPERM) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notPermitted") + public static var EPERM: Errno { notPermitted } + + /// No such file or directory. + /// + /// A component of a specified pathname didn't exist, + /// or the pathname was an empty string. + /// + /// The corresponding C error is `ENOENT`. + @_alwaysEmitIntoClient + public static var noSuchFileOrDirectory: Errno { Errno(_ENOENT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noSuchFileOrDirectory") + public static var ENOENT: Errno { noSuchFileOrDirectory } + + /// No such process. + /// + /// There isn't a process that corresponds to the specified process ID. + /// + /// The corresponding C error is `ESRCH`. + @_alwaysEmitIntoClient + public static var noSuchProcess: Errno { Errno(_ESRCH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noSuchProcess") + public static var ESRCH: Errno { noSuchProcess } + + /// Interrupted function call. + /// + /// The process caught an asynchronous signal (such as `SIGINT` or `SIGQUIT`) + /// during the execution of an interruptible function. + /// If the signal handler performs a normal return, + /// the caller of the interrupted function call receives this error. + /// + /// The corresponding C error is `EINTR`. + @_alwaysEmitIntoClient + public static var interrupted: Errno { Errno(_EINTR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "interrupted") + public static var EINTR: Errno { interrupted } + + /// Input/output error. + /// + /// Some physical input or output error occurred. + /// This error isn't reported until + /// you attempt a subsequent operation on the same file descriptor, + /// and the error may be lost (overwritten) by subsequent errors. + /// + /// The corresponding C error is `EIO`. + @_alwaysEmitIntoClient + public static var ioError: Errno { Errno(_EIO) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "ioError") + public static var EIO: Errno { ioError } + + /// No such device or address. + /// + /// Input or output on a special file referred to a device that didn't exist, + /// or made a request beyond the limits of the device. + /// This error may also occur when, for example, + /// a tape drive isn't online or when there isn't a disk pack loaded on a drive. + /// + /// The corresponding C error is `ENXIO`. + @_alwaysEmitIntoClient + public static var noSuchAddressOrDevice: Errno { Errno(_ENXIO) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noSuchAddressOrDevice") + public static var ENXIO: Errno { noSuchAddressOrDevice } + + /// The argument list is too long. + /// + /// The number of bytes + /// used for the argument and environment list of the new process + /// exceeded the limit `NCARGS`, as defined in ``. + /// + /// The corresponding C error is `E2BIG`. + @_alwaysEmitIntoClient + public static var argListTooLong: Errno { Errno(_E2BIG) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "argListTooLong") + public static var E2BIG: Errno { argListTooLong } + + /// Executable format error. + /// + /// A request was made to execute a file that, + /// although it has the appropriate permissions, + /// isn't in the format required for an executable file. + /// + /// The corresponding C error is `ENOEXEC`. + @_alwaysEmitIntoClient + public static var execFormatError: Errno { Errno(_ENOEXEC) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noExec") + public static var ENOEXEC: Errno { execFormatError } + + /// Bad file descriptor. + /// + /// A file descriptor argument was out of range, + /// referred to no open file, + /// or a read (write) request was made to a file + /// that was only open for writing (reading). + /// + /// The corresponding C error is `EBADF`. + @_alwaysEmitIntoClient + public static var badFileDescriptor: Errno { Errno(_EBADF) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "badFileDescriptor") + public static var EBADF: Errno { badFileDescriptor } + + /// No child processes. + /// + /// A `wait(2)` or `waitpid(2)` function was executed + /// by a process that dosn't have any existing child processes + /// or whose child processes are all already being waited for. + /// + /// The corresponding C error is `ECHILD`. + @_alwaysEmitIntoClient + public static var noChildProcess: Errno { Errno(_ECHILD) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noChildProcess") + public static var ECHILD: Errno { noChildProcess } + + /// Resource deadlock avoided. + /// + /// You attempted to lock a system resource + /// that would have resulted in a deadlock. + /// + /// The corresponding C error is `EDEADLK`. + @_alwaysEmitIntoClient + public static var deadlock: Errno { Errno(_EDEADLK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "deadlock") + public static var EDEADLK: Errno { deadlock } + + /// Can't allocate memory. + /// + /// The new process image required more memory + /// than was allowed by the hardware + /// or by system-imposed memory management constraints. + /// A lack of swap space is normally temporary; + /// however, a lack of core is not. + /// You can increase soft limits up to their corresponding hard limits. + /// + /// The corresponding C error is `ENOMEM`. + @_alwaysEmitIntoClient + public static var noMemory: Errno { Errno(_ENOMEM) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noMemory") + public static var ENOMEM: Errno { noMemory } + + /// Permission denied. + /// + /// You attempted to access a file + /// in a way that's forbidden by the file's access permissions. + /// + /// The corresponding C error is `EACCES`. + @_alwaysEmitIntoClient + public static var permissionDenied: Errno { Errno(_EACCES) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "permissionDenied") + public static var EACCES: Errno { permissionDenied } + + /// Bad address. + /// + /// An address passed as an argument to a system call was invalid. + /// + /// The corresponding C error is `EFAULT`. + @_alwaysEmitIntoClient + public static var badAddress: Errno { Errno(_EFAULT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "badAddress") + public static var EFAULT: Errno { badAddress } + + /// Not a block device. + /// + /// You attempted a block device operation on a nonblock device or file. + /// + /// The corresponding C error is `ENOTBLK`. + @_alwaysEmitIntoClient + public static var notBlockDevice: Errno { Errno(_ENOTBLK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notBlockDevice") + public static var ENOTBLK: Errno { notBlockDevice } + + /// Resource busy. + /// + /// You attempted to use a system resource which was in use at the time, + /// in a manner that would have conflicted with the request. + /// + /// The corresponding C error is `EBUSY`. + @_alwaysEmitIntoClient + public static var resourceBusy: Errno { Errno(_EBUSY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "resourceBusy") + public static var EBUSY: Errno { resourceBusy } + + /// File exists. + /// + /// An existing file was mentioned in an inappropriate context; + /// for example, as the new link name in a link function. + /// + /// The corresponding C error is `EEXIST`. + @_alwaysEmitIntoClient + public static var fileExists: Errno { Errno(_EEXIST) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "fileExists") + public static var EEXIST: Errno { fileExists } + + /// Improper link. + /// + /// You attempted to create a hard link to a file on another file system. + /// + /// The corresponding C error is `EXDEV`. + @_alwaysEmitIntoClient + public static var improperLink: Errno { Errno(_EXDEV) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "improperLink") + public static var EXDEV: Errno { improperLink } + + /// Operation not supported by device. + /// + /// You attempted to apply an inappropriate function to a device; + /// for example, trying to read a write-only device such as a printer. + /// + /// The corresponding C error is `ENODEV`. + @_alwaysEmitIntoClient + public static var operationNotSupportedByDevice: Errno { Errno(_ENODEV) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "operationNotSupportedByDevice") + public static var ENODEV: Errno { operationNotSupportedByDevice } + + /// Not a directory. + /// + /// A component of the specified pathname exists, + /// but it wasn't a directory, + /// when a directory was expected. + /// + /// The corresponding C error is `ENOTDIR`. + @_alwaysEmitIntoClient + public static var notDirectory: Errno { Errno(_ENOTDIR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notDirectory") + public static var ENOTDIR: Errno { notDirectory } + + /// Is a directory. + /// + /// You attempted to open a directory with write mode specified. + /// Directories can be opened only in read mode. + /// + /// The corresponding C error is `EISDIR`. + @_alwaysEmitIntoClient + public static var isDirectory: Errno { Errno(_EISDIR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "isDirectory") + public static var EISDIR: Errno { isDirectory } + + /// Invalid argument. + /// + /// One or more of the specified arguments wasn't valid; + /// for example, specifying an undefined signal to a signal or kill function. + /// + /// The corresponding C error is `EINVAL`. + @_alwaysEmitIntoClient + public static var invalidArgument: Errno { Errno(_EINVAL) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "invalidArgument") + public static var EINVAL: Errno { invalidArgument } + + /// The system has too many open files. + /// + /// The maximum number of file descriptors + /// allowable on the system has been reached; + /// requests to open a file can't be satisfied + /// until you close at least one file descriptor. + /// + /// The corresponding C error is `ENFILE`. + @_alwaysEmitIntoClient + public static var tooManyOpenFilesInSystem: Errno { Errno(_ENFILE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManyOpenFilesInSystem") + public static var ENFILE: Errno { tooManyOpenFilesInSystem } + + /// This process has too many open files. + /// + /// To check the current limit, + /// call the `getdtablesize` function. + /// + /// The corresponding C error is `EMFILE`. + @_alwaysEmitIntoClient + public static var tooManyOpenFiles: Errno { Errno(_EMFILE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManyOpenFiles") + public static var EMFILE: Errno { tooManyOpenFiles } + + /// Inappropriate control function. + /// + /// You attempted a control function + /// that can't be performed on the specified file or device. + /// For information about control functions, see `ioctl(2)`. + /// + /// The corresponding C error is `ENOTTY`. + @_alwaysEmitIntoClient + public static var inappropriateIOCTLForDevice: Errno { Errno(_ENOTTY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "inappropriateIOCTLForDevice") + public static var ENOTTY: Errno { inappropriateIOCTLForDevice } + + /// Text file busy. + /// + /// The new process was a pure procedure (shared text) file, + /// which was already open for writing by another process, + /// or while the pure procedure file was being executed, + /// an open call requested write access. + /// + /// The corresponding C error is `ETXTBSY`. + @_alwaysEmitIntoClient + public static var textFileBusy: Errno { Errno(_ETXTBSY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "textFileBusy") + public static var ETXTBSY: Errno { textFileBusy } + + /// The file is too large. + /// + /// The file exceeds the maximum size allowed by the file system. + /// For example, the maximum size on UFS is about 2.1 gigabytes, + /// and about 9,223 petabytes on HFS-Plus and Apple File System. + /// + /// The corresponding C error is `EFBIG`. + @_alwaysEmitIntoClient + public static var fileTooLarge: Errno { Errno(_EFBIG) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "fileTooLarge") + public static var EFBIG: Errno { fileTooLarge } + + /// Device out of space. + /// + /// A write to an ordinary file, + /// the creation of a directory or symbolic link, + /// or the creation of a directory entry failed + /// because there aren't any available disk blocks on the file system, + /// or the allocation of an inode for a newly created file failed + /// because there aren't any inodes available on the file system. + /// + /// The corresponding C error is `ENOSPC`. + @_alwaysEmitIntoClient + public static var noSpace: Errno { Errno(_ENOSPC) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noSpace") + public static var ENOSPC: Errno { noSpace } + + /// Illegal seek. + /// + /// An `lseek(2)` function was issued on a socket, pipe or FIFO. + /// + /// The corresponding C error is `ESPIPE`. + @_alwaysEmitIntoClient + public static var illegalSeek: Errno { Errno(_ESPIPE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "illegalSeek") + public static var ESPIPE: Errno { illegalSeek } + + /// Read-only file system. + /// + /// You attempted to modify a file or directory + /// on a file system that was read-only at the time. + /// + /// The corresponding C error is `EROFS`. + @_alwaysEmitIntoClient + public static var readOnlyFileSystem: Errno { Errno(_EROFS) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "readOnlyFileSystem") + public static var EROFS: Errno { readOnlyFileSystem } + + /// Too many links. + /// + /// The maximum number of hard links to a single file (32767) + /// has been exceeded. + /// + /// The corresponding C error is `EMLINK`. + @_alwaysEmitIntoClient + public static var tooManyLinks: Errno { Errno(_EMLINK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManyLinks") + public static var EMLINK: Errno { tooManyLinks } + + /// Broken pipe. + /// + /// You attempted to write to a pipe, socket, or FIFO + /// that doesn't have a process reading its data. + /// + /// The corresponding C error is `EPIPE`. + @_alwaysEmitIntoClient + public static var brokenPipe: Errno { Errno(_EPIPE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "brokenPipe") + public static var EPIPE: Errno { brokenPipe } + + /// Numerical argument out of domain. + /// + /// A numerical input argument was outside the defined domain of the + /// mathematical function. + /// + /// The corresponding C error is `EDOM`. + @_alwaysEmitIntoClient + public static var outOfDomain: Errno { Errno(_EDOM) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "outOfDomain") + public static var EDOM: Errno { outOfDomain } + + /// Numerical result out of range. + /// + /// A numerical result of the function + /// was too large to fit in the available space; + /// for example, because it exceeded a floating point number's + /// level of precision. + /// + /// The corresponding C error is `ERANGE`. + @_alwaysEmitIntoClient + public static var outOfRange: Errno { Errno(_ERANGE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "outOfRange") + public static var ERANGE: Errno { outOfRange } + + /// Resource temporarily unavailable. + /// + /// This is a temporary condition; + /// later calls to the same routine may complete normally. + /// Make the same function call again later. + /// + /// The corresponding C error is `EAGAIN`. + @_alwaysEmitIntoClient + public static var resourceTemporarilyUnavailable: Errno { Errno(_EAGAIN) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "resourceTemporarilyUnavailable") + public static var EAGAIN: Errno { resourceTemporarilyUnavailable } + + /// Operation now in progress. + /// + /// You attempted an operation that takes a long time to complete, + /// such as `connect(2)` or `connectx(2)`, + /// on a nonblocking object. + /// See also `fcntl(2)`. + /// + /// The corresponding C error is `EINPROGRESS`. + @_alwaysEmitIntoClient + public static var nowInProgress: Errno { Errno(_EINPROGRESS) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "nowInProcess") + public static var EINPROGRESS: Errno { nowInProgress } + + /// Operation already in progress. + /// + /// You attempted an operation on a nonblocking object + /// that already had an operation in progress. + /// + /// The corresponding C error is `EALREADY`. + @_alwaysEmitIntoClient + public static var alreadyInProcess: Errno { Errno(_EALREADY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "alreadyInProcess") + public static var EALREADY: Errno { alreadyInProcess } + + /// A socket operation was performed on something that isn't a socket. + /// + /// The corresponding C error is `ENOTSOCK`. + @_alwaysEmitIntoClient + public static var notSocket: Errno { Errno(_ENOTSOCK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notSocket") + public static var ENOTSOCK: Errno { notSocket } + + /// Destination address required. + /// + /// A required address was omitted from a socket operation. + /// + /// The corresponding C error is `EDESTADDRREQ`. + @_alwaysEmitIntoClient + public static var addressRequired: Errno { Errno(_EDESTADDRREQ) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "addressRequired") + public static var EDESTADDRREQ: Errno { addressRequired } + + /// Message too long. + /// + /// A message sent on a socket was larger than + /// the internal message buffer or some other network limit. + /// + /// The corresponding C error is `EMSGSIZE`. + @_alwaysEmitIntoClient + public static var messageTooLong: Errno { Errno(_EMSGSIZE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "messageTooLong") + public static var EMSGSIZE: Errno { messageTooLong } + + /// Protocol wrong for socket type. + /// + /// A protocol was specified that doesn't support + /// the semantics of the socket type requested. + /// For example, + /// you can't use the ARPA Internet UDP protocol with type `SOCK_STREAM`. + /// + /// The corresponding C error is `EPROTOTYPE`. + @_alwaysEmitIntoClient + public static var protocolWrongTypeForSocket: Errno { Errno(_EPROTOTYPE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "protocolWrongTypeForSocket") + public static var EPROTOTYPE: Errno { protocolWrongTypeForSocket } + + /// Protocol not available. + /// + /// A bad option or level was specified + /// in a `getsockopt(2)` or `setsockopt(2)` call. + /// + /// The corresponding C error is `ENOPROTOOPT`. + @_alwaysEmitIntoClient + public static var protocolNotAvailable: Errno { Errno(_ENOPROTOOPT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "protocolNotAvailable") + public static var ENOPROTOOPT: Errno { protocolNotAvailable } + + /// Protocol not supported. + /// + /// The protocol hasn't been configured into the system, + /// or no implementation for it exists. + /// + /// The corresponding C error is `EPROTONOSUPPORT`. + @_alwaysEmitIntoClient + public static var protocolNotSupported: Errno { Errno(_EPROTONOSUPPORT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "protocolNotSupported") + public static var EPROTONOSUPPORT: Errno { protocolNotSupported } + + /// Socket type not supported. + /// + /// Support for the socket type hasn't been configured into the system + /// or no implementation for it exists. + /// + /// The corresponding C error is `ESOCKTNOSUPPORT`. + @_alwaysEmitIntoClient + public static var socketTypeNotSupported: Errno { Errno(_ESOCKTNOSUPPORT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "socketTypeNotSupported") + public static var ESOCKTNOSUPPORT: Errno { socketTypeNotSupported } + + /// Not supported. + /// + /// The attempted operation isn't supported + /// for the type of object referenced. + /// + /// The corresponding C error is `ENOTSUP`. + @_alwaysEmitIntoClient + public static var notSupported: Errno { Errno(_ENOTSUP) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notSupported") + public static var ENOTSUP: Errno { notSupported } + + /// Protocol family not supported. + /// + /// The protocol family hasn't been configured into the system + /// or no implementation for it exists. + /// + /// The corresponding C error is `EPFNOSUPPORT`. + @_alwaysEmitIntoClient + public static var protocolFamilyNotSupported: Errno { Errno(_EPFNOSUPPORT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "protocolFamilyNotSupported") + public static var EPFNOSUPPORT: Errno { protocolFamilyNotSupported } + + /// The address family isn't supported by the protocol family. + /// + /// An address incompatible with the requested protocol was used. + /// For example, you shouldn't necessarily expect + /// to be able to use name server addresses with ARPA Internet protocols. + /// + /// The corresponding C error is `EAFNOSUPPORT`. + @_alwaysEmitIntoClient + public static var addressFamilyNotSupported: Errno { Errno(_EAFNOSUPPORT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "addressFamilyNotSupported") + public static var EAFNOSUPPORT: Errno { addressFamilyNotSupported } + + /// Address already in use. + /// + /// Only one use of each address is normally permitted. + /// + /// The corresponding C error is `EADDRINUSE`. + @_alwaysEmitIntoClient + public static var addressInUse: Errno { Errno(_EADDRINUSE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "addressInUse") + public static var EADDRINUSE: Errno { addressInUse } + + /// Can't assign the requested address. + /// + /// This error normally results from + /// an attempt to create a socket with an address that isn't on this machine. + /// + /// The corresponding C error is `EADDRNOTAVAIL`. + @_alwaysEmitIntoClient + public static var addressNotAvailable: Errno { Errno(_EADDRNOTAVAIL) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "addressNotAvailable") + public static var EADDRNOTAVAIL: Errno { addressNotAvailable } + + /// Network is down. + /// + /// A socket operation encountered a dead network. + /// + /// The corresponding C error is `ENETDOWN`. + @_alwaysEmitIntoClient + public static var networkDown: Errno { Errno(_ENETDOWN) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "networkDown") + public static var ENETDOWN: Errno { networkDown } + + /// Network is unreachable. + /// + /// A socket operation was attempted to an unreachable network. + /// + /// The corresponding C error is `ENETUNREACH`. + @_alwaysEmitIntoClient + public static var networkUnreachable: Errno { Errno(_ENETUNREACH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "networkUnreachable") + public static var ENETUNREACH: Errno { networkUnreachable } + + /// Network dropped connection on reset. + /// + /// The host you were connected to crashed and restarted. + /// + /// The corresponding C error is `ENETRESET`. + @_alwaysEmitIntoClient + public static var networkReset: Errno { Errno(_ENETRESET) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "networkReset") + public static var ENETRESET: Errno { networkReset } + + /// Software caused a connection abort. + /// + /// A connection abort was caused internal to your host machine. + /// + /// The corresponding C error is `ECONNABORTED`. + @_alwaysEmitIntoClient + public static var connectionAbort: Errno { Errno(_ECONNABORTED) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "connectionAbort") + public static var ECONNABORTED: Errno { connectionAbort } + + /// Connection reset by peer. + /// + /// A connection was forcibly closed by a peer. + /// This normally results from a loss of the connection + /// on the remote socket due to a timeout or a reboot. + /// + /// The corresponding C error is `ECONNRESET`. + @_alwaysEmitIntoClient + public static var connectionReset: Errno { Errno(_ECONNRESET) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "connectionReset") + public static var ECONNRESET: Errno { connectionReset } + + /// No buffer space available. + /// + /// An operation on a socket or pipe wasn't performed + /// because the system lacked sufficient buffer space + /// or because a queue was full. + /// + /// The corresponding C error is `ENOBUFS`. + @_alwaysEmitIntoClient + public static var noBufferSpace: Errno { Errno(_ENOBUFS) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noBufferSpace") + public static var ENOBUFS: Errno { noBufferSpace } + + /// Socket is already connected. + /// + /// A `connect(2)` or `connectx(2)` request was made + /// on an already connected socket, + /// or a `sendto(2)` or `sendmsg(2)` request was made + /// on a connected socket specified a destination when already connected. + /// + /// The corresponding C error is `EISCONN`. + @_alwaysEmitIntoClient + public static var socketIsConnected: Errno { Errno(_EISCONN) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "socketIsConnected") + public static var EISCONN: Errno { socketIsConnected } + + /// Socket is not connected. + /// + /// A request to send or receive data wasn't permitted + /// because the socket wasn't connected and, + /// when sending on a datagram socket, + /// no address was supplied. + /// + /// The corresponding C error is `ENOTCONN`. + @_alwaysEmitIntoClient + public static var socketNotConnected: Errno { Errno(_ENOTCONN) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "socketNotConnected") + public static var ENOTCONN: Errno { socketNotConnected } + + /// Can't send after socket shutdown. + /// + /// A request to send data wasn't permitted + /// because the socket had already been shut down + /// with a previous `shutdown(2)` call. + /// + /// The corresponding C error is `ESHUTDOWN`. + @_alwaysEmitIntoClient + public static var socketShutdown: Errno { Errno(_ESHUTDOWN) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "socketShutdown") + public static var ESHUTDOWN: Errno { socketShutdown } + + /// Operation timed out. + /// + /// A `connect(2)`, `connectx(2)` or `send(2)` request failed + /// because the connected party didn't properly respond + /// within the required period of time. + /// The timeout period is dependent on the communication protocol. + /// + /// The corresponding C error is `ETIMEDOUT`. + @_alwaysEmitIntoClient + public static var timedOut: Errno { Errno(_ETIMEDOUT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "timedOut") + public static var ETIMEDOUT: Errno { timedOut } + + /// Connection refused. + /// + /// No connection could be made + /// because the target machine actively refused it. + /// This usually results from trying to connect to a service + /// that's inactive on the foreign host. + /// + /// The corresponding C error is `ECONNREFUSED`. + @_alwaysEmitIntoClient + public static var connectionRefused: Errno { Errno(_ECONNREFUSED) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "connectionRefused") + public static var ECONNREFUSED: Errno { connectionRefused } + + /// Too many levels of symbolic links. + /// + /// A pathname lookup involved more than eight symbolic links. + /// + /// The corresponding C error is `ELOOP`. + @_alwaysEmitIntoClient + public static var tooManySymbolicLinkLevels: Errno { Errno(_ELOOP) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManySymbolicLinkLevels") + public static var ELOOP: Errno { tooManySymbolicLinkLevels } + + /// The file name is too long. + /// + /// A component of a pathname exceeded 255 (`MAXNAMELEN`) characters, + /// or an entire pathname exceeded 1023 (`MAXPATHLEN-1`) characters. + /// + /// The corresponding C error is `ENAMETOOLONG`. + @_alwaysEmitIntoClient + public static var fileNameTooLong: Errno { Errno(_ENAMETOOLONG) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "fileNameTooLong") + public static var ENAMETOOLONG: Errno { fileNameTooLong } + + /// The host is down. + /// + /// A socket operation failed because the destination host was down. + /// + /// The corresponding C error is `EHOSTDOWN`. + @_alwaysEmitIntoClient + public static var hostIsDown: Errno { Errno(_EHOSTDOWN) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "hostIsDown") + public static var EHOSTDOWN: Errno { hostIsDown } + + /// No route to host. + /// + /// A socket operation failed because the destination host was unreachable. + /// + /// The corresponding C error is `EHOSTUNREACH`. + @_alwaysEmitIntoClient + public static var noRouteToHost: Errno { Errno(_EHOSTUNREACH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noRouteToHost") + public static var EHOSTUNREACH: Errno { noRouteToHost } + + /// Directory not empty. + /// + /// A directory with entries other than `.` and `..` + /// was supplied to a `remove(2)` directory or `rename(2)` call. + /// + /// The corresponding C error is `ENOTEMPTY`. + @_alwaysEmitIntoClient + public static var directoryNotEmpty: Errno { Errno(_ENOTEMPTY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "directoryNotEmpty") + public static var ENOTEMPTY: Errno { directoryNotEmpty } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Too many processes. + /// + /// The corresponding C error is `EPROCLIM`. + @_alwaysEmitIntoClient + public static var tooManyProcesses: Errno { Errno(_EPROCLIM) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManyProcesses") + public static var EPROCLIM: Errno { tooManyProcesses } +#endif + + /// Too many users. + /// + /// The quota system ran out of table entries. + /// + /// The corresponding C error is `EUSERS`. + @_alwaysEmitIntoClient + public static var tooManyUsers: Errno { Errno(_EUSERS) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManyUsers") + public static var EUSERS: Errno { tooManyUsers } + + /// Disk quota exceeded. + /// + /// A write to an ordinary file, + /// the creation of a directory or symbolic link, + /// or the creation of a directory entry failed + /// because the user's quota of disk blocks was exhausted, + /// or the allocation of an inode for a newly created file failed + /// because the user's quota of inodes was exhausted. + /// + /// The corresponding C error is `EDQUOT`. + @_alwaysEmitIntoClient + public static var diskQuotaExceeded: Errno { Errno(_EDQUOT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "diskQuotaExceeded") + public static var EDQUOT: Errno { diskQuotaExceeded } + + /// Stale NFS file handle. + /// + /// You attempted access an open file on an NFS filesystem, + /// which is now unavailable as referenced by the given file descriptor. + /// This may indicate that the file was deleted on the NFS server + /// or that some other catastrophic event occurred. + /// + /// The corresponding C error is `ESTALE`. + @_alwaysEmitIntoClient + public static var staleNFSFileHandle: Errno { Errno(_ESTALE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "staleNFSFileHandle") + public static var ESTALE: Errno { staleNFSFileHandle } + +// TODO: Add Linux's RPC equivalents +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + + /// The structure of the remote procedure call (RPC) is bad. + /// + /// Exchange of RPC information was unsuccessful. + /// + /// The corresponding C error is `EBADRPC`. + @_alwaysEmitIntoClient + public static var rpcUnsuccessful: Errno { Errno(_EBADRPC) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "rpcUnsuccessful") + public static var EBADRPC: Errno { rpcUnsuccessful } + + /// The version of the remote procedure call (RPC) is incorrect. + /// + /// The version of RPC on the remote peer + /// isn't compatible with the local version. + /// + /// The corresponding C error is `ERPCMISMATCH`. + @_alwaysEmitIntoClient + public static var rpcVersionMismatch: Errno { Errno(_ERPCMISMATCH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "rpcVersionMismatch") + public static var ERPCMISMATCH: Errno { rpcVersionMismatch } + + /// The remote procedure call (RPC) program isn't available. + /// + /// The requested program isn't registered on the remote host. + /// + /// The corresponding C error is `EPROGUNAVAIL`. + @_alwaysEmitIntoClient + public static var rpcProgramUnavailable: Errno { Errno(_EPROGUNAVAIL) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "rpcProgramUnavailable") + public static var EPROGUNAVAIL: Errno { rpcProgramUnavailable } + + /// The version of the remote procedure call (RPC) program is incorrect. + /// + /// The requested version of the program + /// isn't available on the remote host. + /// + /// The corresponding C error is `EPROGMISMATCH`. + @_alwaysEmitIntoClient + public static var rpcProgramVersionMismatch: Errno { Errno(_EPROGMISMATCH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "rpcProgramVersionMismatch") + public static var EPROGMISMATCH: Errno { rpcProgramVersionMismatch } + + /// Bad procedure for program. + /// + /// A remote procedure call was attempted for a procedure + /// that doesn't exist in the remote program. + /// + /// The corresponding C error is `EPROCUNAVAIL`. + @_alwaysEmitIntoClient + public static var rpcProcedureUnavailable: Errno { Errno(_EPROCUNAVAIL) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "rpcProcedureUnavailable") + public static var EPROCUNAVAIL: Errno { rpcProcedureUnavailable } +#endif + + /// No locks available. + /// + /// You have reached the system-imposed limit + /// on the number of simultaneous files. + /// + /// The corresponding C error is `ENOLCK`. + @_alwaysEmitIntoClient + public static var noLocks: Errno { Errno(_ENOLCK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noLocks") + public static var ENOLCK: Errno { noLocks } + + /// Function not implemented. + /// + /// You attempted a system call that isn't available on this system. + /// + /// The corresponding C error is `ENOSYS`. + @_alwaysEmitIntoClient + public static var noFunction: Errno { Errno(_ENOSYS) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noFunction") + public static var ENOSYS: Errno { noFunction } + +// BSD +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Inappropriate file type or format. + /// + /// The file was the wrong type for the operation, + /// or a data file had the wrong format. + /// + /// The corresponding C error is `EFTYPE`. + @_alwaysEmitIntoClient + public static var badFileTypeOrFormat: Errno { Errno(_EFTYPE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "badFileTypeOrFormat") + public static var EFTYPE: Errno { badFileTypeOrFormat } +#endif + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Authentication error. + /// + /// The authentication ticket used to mount an NFS file system was invalid. + /// + /// The corresponding C error is `EAUTH`. + @_alwaysEmitIntoClient + public static var authenticationError: Errno { Errno(_EAUTH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "authenticationError") + public static var EAUTH: Errno { authenticationError } + + /// Need authenticator. + /// + /// Before mounting the given NFS file system, + /// you must obtain an authentication ticket. + /// + /// The corresponding C error is `ENEEDAUTH`. + @_alwaysEmitIntoClient + public static var needAuthenticator: Errno { Errno(_ENEEDAUTH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "needAuthenticator") + public static var ENEEDAUTH: Errno { needAuthenticator } +#endif + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Device power is off. + /// + /// The corresponding C error is `EPWROFF`. + @_alwaysEmitIntoClient + public static var devicePowerIsOff: Errno { Errno(_EPWROFF) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "devicePowerIsOff") + public static var EPWROFF: Errno { devicePowerIsOff } + + /// Device error. + /// + /// A device error has occurred; + /// for example, a printer running out of paper. + /// + /// The corresponding C error is `EDEVERR`. + @_alwaysEmitIntoClient + public static var deviceError: Errno { Errno(_EDEVERR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "deviceError") + public static var EDEVERR: Errno { deviceError } +#endif + + /// Value too large to be stored in data type. + /// + /// A numerical result of the function + /// is too large to be stored in the space that the caller provided. + /// + /// The corresponding C error is `EOVERFLOW`. + @_alwaysEmitIntoClient + public static var overflow: Errno { Errno(_EOVERFLOW) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "overflow") + public static var EOVERFLOW: Errno { overflow } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Bad executable or shared library. + /// + /// The executable or shared library being referenced was malformed. + /// + /// The corresponding C error is `EBADEXEC`. + @_alwaysEmitIntoClient + public static var badExecutable: Errno { Errno(_EBADEXEC) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "badExecutable") + public static var EBADEXEC: Errno { badExecutable } + + /// Bad CPU type in executable. + /// + /// The specified executable doesn't support the current CPU. + /// + /// The corresponding C error is `EBADARCH`. + @_alwaysEmitIntoClient + public static var badCPUType: Errno { Errno(_EBADARCH) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "badCPUType") + public static var EBADARCH: Errno { badCPUType } + + /// Shared library version mismatch. + /// + /// The version of the shared library on the system + /// doesn't match the expected version. + /// + /// The corresponding C error is `ESHLIBVERS`. + @_alwaysEmitIntoClient + public static var sharedLibraryVersionMismatch: Errno { Errno(_ESHLIBVERS) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "sharedLibraryVersionMismatch") + public static var ESHLIBVERS: Errno { sharedLibraryVersionMismatch } + + /// Malformed Mach-O file. + /// + /// The Mach object file is malformed. + /// + /// The corresponding C error is `EBADMACHO`. + @_alwaysEmitIntoClient + public static var malformedMachO: Errno { Errno(_EBADMACHO) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "malformedMachO") + public static var EBADMACHO: Errno { malformedMachO } +#endif + + /// Operation canceled. + /// + /// The scheduled operation was canceled. + /// + /// The corresponding C error is `ECANCELED`. + @_alwaysEmitIntoClient + public static var canceled: Errno { Errno(_ECANCELED) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "canceled") + public static var ECANCELED: Errno { canceled } + + /// Identifier removed. + /// + /// An IPC identifier was removed while the current process was waiting on it. + /// + /// The corresponding C error is `EIDRM`. + @_alwaysEmitIntoClient + public static var identifierRemoved: Errno { Errno(_EIDRM) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "identifierRemoved") + public static var EIDRM: Errno { identifierRemoved } + + /// No message of desired type. + /// + /// An IPC message queue doesn't contain a message of the desired type, + /// or a message catalog doesn't contain the requested message. + /// + /// The corresponding C error is `ENOMSG`. + @_alwaysEmitIntoClient + public static var noMessage: Errno { Errno(_ENOMSG) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noMessage") + public static var ENOMSG: Errno { noMessage } + + /// Illegal byte sequence. + /// + /// While decoding a multibyte character, + /// the function encountered an invalid or incomplete sequence of bytes, + /// or the given wide character is invalid. + /// + /// The corresponding C error is `EILSEQ`. + @_alwaysEmitIntoClient + public static var illegalByteSequence: Errno { Errno(_EILSEQ) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "illegalByteSequence") + public static var EILSEQ: Errno { illegalByteSequence } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Attribute not found. + /// + /// The specified extended attribute doesn't exist. + /// + /// The corresponding C error is `ENOATTR`. + @_alwaysEmitIntoClient + public static var attributeNotFound: Errno { Errno(_ENOATTR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "attributeNotFound") + public static var ENOATTR: Errno { attributeNotFound } +#endif + + /// Bad message. + /// + /// The message to be received is inappropriate + /// for the attempted operation. + /// + /// The corresponding C error is `EBADMSG`. + @_alwaysEmitIntoClient + public static var badMessage: Errno { Errno(_EBADMSG) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "badMessage") + public static var EBADMSG: Errno { badMessage } + + /// Reserved. + /// + /// This error is reserved for future use. + /// + /// The corresponding C error is `EMULTIHOP`. + @_alwaysEmitIntoClient + public static var multiHop: Errno { Errno(_EMULTIHOP) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "multiHop") + public static var EMULTIHOP: Errno { multiHop } + + /// No message available. + /// + /// No message was available to be received by the requested operation. + /// + /// The corresponding C error is `ENODATA`. + @_alwaysEmitIntoClient + public static var noData: Errno { Errno(_ENODATA) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noData") + public static var ENODATA: Errno { noData } + + /// Reserved. + /// + /// This error is reserved for future use. + /// + /// The corresponding C error is `ENOLINK`. + @_alwaysEmitIntoClient + public static var noLink: Errno { Errno(_ENOLINK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noLink") + public static var ENOLINK: Errno { noLink } + + /// Reserved. + /// + /// This error is reserved for future use. + /// + /// The corresponding C error is `ENOSR`. + @_alwaysEmitIntoClient + public static var noStreamResources: Errno { Errno(_ENOSR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noStreamResources") + public static var ENOSR: Errno { noStreamResources } + + /// Reserved. + /// + /// This error is reserved for future use. + /// + /// The corresponding C error is `ENOSTR`. + @_alwaysEmitIntoClient + public static var notStream: Errno { Errno(_ENOSTR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notStream") + public static var ENOSTR: Errno { notStream } + + /// Protocol error. + /// + /// Some protocol error occurred. + /// This error is device-specific, + /// but generally isn't related to a hardware failure. + /// + /// The corresponding C error is `EPROTO`. + @_alwaysEmitIntoClient + public static var protocolError: Errno { Errno(_EPROTO) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "protocolError") + public static var EPROTO: Errno { protocolError } + + /// Reserved. + /// + /// This error is reserved for future use. + /// + /// The corresponding C error is `ETIME`. + @_alwaysEmitIntoClient + public static var timeout: Errno { Errno(_ETIME) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "timeout") + public static var ETIME: Errno { timeout } + + /// Operation not supported on socket. + /// + /// The attempted operation isn't supported for the type of socket referenced; + /// for example, trying to accept a connection on a datagram socket. + /// + /// The corresponding C error is `EOPNOTSUPP`. + @_alwaysEmitIntoClient + public static var notSupportedOnSocket: Errno { Errno(_EOPNOTSUPP) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notSupportedOnSocket") + public static var EOPNOTSUPP: Errno { notSupportedOnSocket } +} + +// Constants defined in header but not man page +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Errno { + + /// Operation would block. + /// + /// The corresponding C error is `EWOULDBLOCK`. + @_alwaysEmitIntoClient + public static var wouldBlock: Errno { Errno(_EWOULDBLOCK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "wouldBlock") + public static var EWOULDBLOCK: Errno { wouldBlock } + + /// Too many references: can't splice. + /// + /// The corresponding C error is `ETOOMANYREFS`. + @_alwaysEmitIntoClient + public static var tooManyReferences: Errno { Errno(_ETOOMANYREFS) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManyReferences") + public static var ETOOMANYREFS: Errno { tooManyReferences } + + /// Too many levels of remote in path. + /// + /// The corresponding C error is `EREMOTE`. + @_alwaysEmitIntoClient + public static var tooManyRemoteLevels: Errno { Errno(_EREMOTE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "tooManyRemoteLevels") + public static var EREMOTE: Errno { tooManyRemoteLevels } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// No such policy registered. + /// + /// The corresponding C error is `ENOPOLICY`. + @_alwaysEmitIntoClient + public static var noSuchPolicy: Errno { Errno(_ENOPOLICY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noSuchPolicy") + public static var ENOPOLICY: Errno { noSuchPolicy } +#endif + + /// State not recoverable. + /// + /// The corresponding C error is `ENOTRECOVERABLE`. + @_alwaysEmitIntoClient + public static var notRecoverable: Errno { Errno(_ENOTRECOVERABLE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "notRecoverable") + public static var ENOTRECOVERABLE: Errno { notRecoverable } + + /// Previous pthread mutex owner died. + /// + /// The corresponding C error is `EOWNERDEAD`. + @_alwaysEmitIntoClient + public static var previousOwnerDied: Errno { Errno(_EOWNERDEAD) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "previousOwnerDied") + public static var EOWNERDEAD: Errno { previousOwnerDied } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Interface output queue is full. + /// + /// The corresponding C error is `EQFULL`. + @_alwaysEmitIntoClient + public static var outputQueueFull: Errno { Errno(_EQFULL) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "outputQueueFull") + public static var EQFULL: Errno { outputQueueFull } + + /// The largest valid error. + /// + /// This value is the largest valid value + /// encountered using the C `errno` global variable. + /// It isn't a valid error. + /// + /// The corresponding C error is `ELAST`. + @_alwaysEmitIntoClient + public static var lastErrnoValue: Errno { Errno(_ELAST) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "lastErrnoValue") + public static var ELAST: Errno { lastErrnoValue } +#endif +} + +@_implementationOnly import SystemInternals +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Errno { + // TODO: We want to provide safe access to `errno`, but we need a + // release-barrier to do so. + + /// The current error value, set by system calls if an error occurs. + /// + /// The corresponding C global variable is `errno`. + internal static var current: Errno { + get { Errno(rawValue: system_errno) } + set { system_errno = newValue.rawValue } + } +} + +// Use "hidden" entry points for `NSError` bridging +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Errno { + public var _code: Int { Int(rawValue) } + + public var _domain: String { "NSPOSIXErrorDomain" } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Errno: CustomStringConvertible, CustomDebugStringConvertible { + /// A textual representation of the most recent error + /// returned by a system call. + /// + /// The corresponding C function is `strerror(3)`. + @inline(never) + public var description: String { + guard let ptr = system_strerror(self.rawValue) else { return "unknown error" } + return String(cString: ptr) + } + + /// A textual representation, + /// suitable for debugging, + /// of the most recent error returned by a system call. + /// + /// The corresponding C function is `strerror(3)`. + public var debugDescription: String { self.description } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Errno { + @_alwaysEmitIntoClient + public static func ~=(_ lhs: Errno, _ rhs: Error) -> Bool { + guard let value = rhs as? Errno else { return false } + return lhs == value + } +} + diff --git a/Sources/System/FileDescriptor.swift b/Sources/System/FileDescriptor.swift new file mode 100644 index 00000000..7bb9febb --- /dev/null +++ b/Sources/System/FileDescriptor.swift @@ -0,0 +1,433 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// An abstract handle to an input or output data resource, +/// such as a file or a socket. +/// +/// You are responsible for managing the lifetime and validity +/// of `FileDescriptor` values, +/// in the same way as you manage a raw C file handle. +@frozen +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public struct FileDescriptor: RawRepresentable, Hashable, Codable { + /// The raw C file handle. + @_alwaysEmitIntoClient + public let rawValue: CInt + + /// Creates a strongly-typed file handle from a raw C file handle. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FileDescriptor { + /// The desired read and write access for a newly opened file. + @frozen + public struct AccessMode: RawRepresentable, Hashable, Codable { + /// The raw C access mode. + @_alwaysEmitIntoClient + public var rawValue: CInt + + /// Creates a strongly-typed access mode from a raw C access mode. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + /// Opens the file for reading only. + /// + /// The corresponding C constant is `O_RDONLY`. + @_alwaysEmitIntoClient + public static var readOnly: AccessMode { AccessMode(rawValue: _O_RDONLY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "readOnly") + public static var O_RDONLY: AccessMode { readOnly } + + /// Opens the file for writing only. + /// + /// The corresponding C constant is `O_WRONLY`. + @_alwaysEmitIntoClient + public static var writeOnly: AccessMode { AccessMode(rawValue: _O_WRONLY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "writeOnly") + public static var O_WRONLY: AccessMode { writeOnly } + + /// Opens the file for reading and writing. + /// + /// The corresponding C constant is `O_RDWR`. + @_alwaysEmitIntoClient + public static var readWrite: AccessMode { AccessMode(rawValue: _O_RDWR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "readWrite") + public static var O_RDWR: AccessMode { readWrite } + } + + /// Options that specify behavior for a newly-opened file. + @frozen + public struct OpenOptions: OptionSet, Hashable, Codable { + /// The raw C options. + @_alwaysEmitIntoClient + public var rawValue: CInt + + /// Create a strongly-typed options value from raw C options. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CInt) { self.init(rawValue: raw) } + + /// Indicates that opening the file doesn't + /// wait for the file or device to become available. + /// + /// If this option is specified, + /// the system doesn't wait for the device or file + /// to be ready or available. + /// If the + /// + /// call would result in the process being blocked for some reason, + /// that method returns immediately. + /// This flag also has the effect of making all + /// subsequent input and output operations on the open file nonblocking. + /// + /// The corresponding C constant is `O_NONBLOCK`. + @_alwaysEmitIntoClient + public static var nonBlocking: OpenOptions { OpenOptions(_O_NONBLOCK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "nonBlocking") + public static var O_NONBLOCK: OpenOptions { nonBlocking } + + /// Indicates that each write operation appends to the file. + /// + /// If this option is specified, + /// each time you write to the file, + /// the new data is written at the end of the file, + /// after all existing file data. + /// + /// The corresponding C constant is `O_APPEND`. + @_alwaysEmitIntoClient + public static var append: OpenOptions { OpenOptions(_O_APPEND) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "append") + public static var O_APPEND: OpenOptions { append } + + /// Indicates that opening the file creates the file if it doesn't exist. + /// + /// The corresponding C constant is `O_CREAT`. + @_alwaysEmitIntoClient + public static var create: OpenOptions { OpenOptions(_O_CREAT) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "create") + public static var O_CREAT: OpenOptions { create } + + /// Indicates that opening the file truncates the file if it exists. + /// + /// If this option is specified and the file exists, + /// the file is truncated to zero bytes + /// before any other operations are performed. + /// + /// The corresponding C constant is `O_TRUNC`. + @_alwaysEmitIntoClient + public static var truncate: OpenOptions { OpenOptions(_O_TRUNC) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "truncate") + public static var O_TRUNC: OpenOptions { truncate } + + /// Indicates that opening the file creates the file, + /// expecting that it doesn't exist. + /// + /// If this option and ``create`` are both specified and the file exists, + /// + /// returns an error instead of creating the file. + /// You can use this, for example, + /// to implement a simple exclusive-access locking mechanism. + /// + /// If this option and ``create`` are both specified + /// and the last component of the file's path is a symbolic link, + /// + /// fails even if the symbolic link points to a nonexistent name. + /// + /// The corresponding C constant is `O_EXCL`. + @_alwaysEmitIntoClient + public static var exclusiveCreate: OpenOptions { OpenOptions(_O_EXCL) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "exclusiveCreate") + public static var O_EXCL: OpenOptions { exclusiveCreate } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Indicates that opening the file + /// atomically obtains a shared lock on the file. + /// + /// Setting this option or the ``exclusiveLock`` option + /// obtains a lock with `flock(2)` semantics. + /// If you're creating a file using the ``create`` option, + /// the request for the lock always succeeds + /// except on file systems that don't support locking. + /// + /// The corresponding C constant is `O_SHLOCK`. + @_alwaysEmitIntoClient + public static var sharedLock: OpenOptions { OpenOptions(_O_SHLOCK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "sharedLock") + public static var O_SHLOCK: OpenOptions { sharedLock } + + /// Indicates that opening the file + /// atomically obtains an exclusive lock. + /// + /// Setting this option or the ``sharedLock`` option. + /// obtains a lock with `flock(2)` semantics. + /// If you're creating a file using the ``create`` option, + /// the request for the lock always succeeds + /// except on file systems that don't support locking. + /// + /// The corresponding C constant is `O_EXLOCK`. + @_alwaysEmitIntoClient + public static var exclusiveLock: OpenOptions { OpenOptions(_O_EXLOCK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "exclusiveLock") + public static var O_EXLOCK: OpenOptions { exclusiveLock } +#endif + + /// Indicates that opening the file doesn't follow symlinks. + /// + /// If you specify this option + /// and the file path you pass to + /// + /// is a symbolic link, + /// then that open operation fails. + /// + /// The corresponding C constant is `O_NOFOLLOW`. + @_alwaysEmitIntoClient + public static var noFollow: OpenOptions { OpenOptions(_O_NOFOLLOW) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "noFollow") + public static var O_NOFOLLOW: OpenOptions { noFollow } + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Indicates that opening the file + /// opens symbolic links instead of following them. + /// + /// If you specify this option + /// and the file path you pass to + /// + /// is a symbolic link, + /// then the link itself is opened instead of what it links to. + /// + /// The corresponding C constant is `O_SYMLINK`. + @_alwaysEmitIntoClient + public static var symlink: OpenOptions { OpenOptions(_O_SYMLINK) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "symlink") + public static var O_SYMLINK: OpenOptions { symlink } + + /// Indicates that opening the file monitors a file for changes. + /// + /// Specify this option when opening a file for event notifications, + /// such as a file handle returned by the `kqueue(2)` function, + /// rather than for reading or writing. + /// Files opened with this option + /// don't prevent their containing volume from being unmounted. + /// + /// The corresponding C constant is `O_EVTONLY`. + @_alwaysEmitIntoClient + public static var eventOnly: OpenOptions { OpenOptions(_O_EVTONLY) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "eventOnly") + public static var O_EVTONLY: OpenOptions { eventOnly } +#endif + + /// Indicates that executing a program closes the file. + /// + /// Normally, file descriptors remain open + /// across calls to the `exec(2)` family of functions. + /// If you specify this option, + /// the file descriptor is closed when replacing this process + /// with another process. + /// + /// The state of the file + /// descriptor flags can be inspected using `F_GETFD`, + /// as described in the `fcntl(2)` man page. + /// + /// The corresponding C constant is `O_CLOEXEC`. + @_alwaysEmitIntoClient + public static var closeOnExec: OpenOptions { OpenOptions(_O_CLOEXEC) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "closeOnExec") + public static var O_CLOEXEC: OpenOptions { closeOnExec } + } + + /// Options for specifying what a file descriptor's offset is relative to. + @frozen + public struct SeekOrigin: RawRepresentable, Hashable, Codable { + /// The raw C value. + @_alwaysEmitIntoClient + public var rawValue: CInt + + /// Create a strongly-typed seek origin from a raw C value. + @_alwaysEmitIntoClient + public init(rawValue: CInt) { self.rawValue = rawValue } + + /// Indicates that the offset should be set to the specified value. + /// + /// The corresponding C constant is `SEEK_SET`. + @_alwaysEmitIntoClient + public static var start: SeekOrigin { SeekOrigin(rawValue: _SEEK_SET) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "start") + public static var SEEK_SET: SeekOrigin { start } + + /// Indicates that the offset should be set + /// to the specified number of bytes after the current location. + /// + /// The corresponding C constant is `SEEK_CUR`. + @_alwaysEmitIntoClient + public static var current: SeekOrigin { SeekOrigin(rawValue: _SEEK_CUR) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "current") + public static var SEEK_CUR: SeekOrigin { current } + + /// Indicates that the offset should be set + /// to the size of the file plus the specified number of bytes. + /// + /// The corresponding C constant is `SEEK_END`. + @_alwaysEmitIntoClient + public static var end: SeekOrigin { SeekOrigin(rawValue: _SEEK_END) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "end") + public static var SEEK_END: SeekOrigin { end } + +// TODO: These are available on some versions of Linux with appropriate +// macro defines. +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + /// Indicates that the offset should be set + /// to the next hole after the specified number of bytes. + /// + /// For information about what is considered a hole, + /// see the `lseek(2)` man page. + /// + /// The corresponding C constant is `SEEK_HOLE`. + @_alwaysEmitIntoClient + public static var nextHole: SeekOrigin { SeekOrigin(rawValue: _SEEK_HOLE) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "nextHole") + public static var SEEK_HOLE: SeekOrigin { nextHole } + + /// Indicates that the offset should be set + /// to the start of the next file region + /// that isn't a hole + /// and is greater than or equal to the supplied offset. + /// + /// The corresponding C constant is `SEEK_DATA`. + @_alwaysEmitIntoClient + public static var nextData: SeekOrigin { SeekOrigin(rawValue: _SEEK_DATA) } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "nextData") + public static var SEEK_DATA: SeekOrigin { nextData } +#endif + + } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FileDescriptor.AccessMode + : CustomStringConvertible, CustomDebugStringConvertible +{ + /// A textual representation of the access mode. + @inline(never) + public var description: String { + switch self { + case .readOnly: return "readOnly" + case .writeOnly: return "writeOnly" + case .readWrite: return "readWrite" + default: return "\(Self.self)(rawValue: \(self.rawValue))" + } + } + + /// A textual representation of the access mode, suitable for debugging + public var debugDescription: String { self.description } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FileDescriptor.SeekOrigin + : CustomStringConvertible, CustomDebugStringConvertible +{ + /// A textual representation of the seek origin. + @inline(never) + public var description: String { + switch self { + case .start: return "start" + case .current: return "current" + case .end: return "end" +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + case .nextHole: return "nextHole" + case .nextData: return "nextData" +#endif + default: return "\(Self.self)(rawValue: \(self.rawValue))" + } + } + + /// A textual representation of the seek origin, suitable for debugging. + public var debugDescription: String { self.description } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FileDescriptor.OpenOptions + : CustomStringConvertible, CustomDebugStringConvertible +{ + /// A textual representation of the open options. + @inline(never) + public var description: String { +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + let descriptions: [(Element, StaticString)] = [ + (.nonBlocking, ".nonBlocking"), + (.append, ".append"), + (.create, ".create"), + (.truncate, ".truncate"), + (.exclusiveCreate, ".exclusiveCreate"), + (.sharedLock, ".sharedLock"), + (.exclusiveLock, ".exclusiveLock"), + (.noFollow, ".noFollow"), + (.symlink, ".symlink"), + (.eventOnly, ".eventOnly"), + (.closeOnExec, ".closeOnExec") + ] +#else + let descriptions: [(Element, StaticString)] = [ + (.nonBlocking, ".nonBlocking"), + (.append, ".append"), + (.create, ".create"), + (.truncate, ".truncate"), + (.exclusiveCreate, ".exclusiveCreate"), + (.noFollow, ".noFollow"), + (.closeOnExec, ".closeOnExec") + ] +#endif + + return _buildDescription(descriptions) + } + + /// A textual representation of the open options, suitable for debugging. + public var debugDescription: String { self.description } +} diff --git a/Sources/System/FileHelpers.swift b/Sources/System/FileHelpers.swift new file mode 100644 index 00000000..d083c101 --- /dev/null +++ b/Sources/System/FileHelpers.swift @@ -0,0 +1,125 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FileDescriptor { + /// Runs a closure and then closes the file descriptor, even if an error occurs. + /// + /// - Parameter body: The closure to run. + /// If the closure throws an error, + /// this method closes the file descriptor before it rethrows that error. + /// + /// - Returns: The value returned by the closure. + /// + /// If `body` throws an error + /// or an error occurs while closing the file descriptor, + /// this method rethrows that error. + public func closeAfter(_ body: () throws -> R) throws -> R { + // No underscore helper, since the closure's throw isn't necessarily typed. + let result: R + do { + result = try body() + } catch { + _ = try? self.close() // Squash close error and throw closure's + throw error + } + try self.close() + return result + } + + /// Writes a sequence of bytes to the current offset + /// and then updates the offset. + /// + /// - Parameter sequence: The bytes to write. + /// - Returns: The number of bytes written, equal to the number of elements in `sequence`. + /// + /// This method either writes the entire contents of `sequence`, + /// or throws an error if only part of the content was written. + /// + /// Writes to the position associated with this file descriptor, and + /// increments that position by the number of bytes written. + /// See also ``seek(offset:from:)``. + /// + /// This method either writes the entire contents of `sequence`, + /// or throws an error if only part of the content was written. + /// + /// If `sequence` doesn't implement + /// the method, + /// temporary space will be allocated as needed. + @_alwaysEmitIntoClient + @discardableResult + public func writeAll( + _ sequence: S + ) throws -> Int where S.Element == UInt8 { + return try _writeAll(sequence).get() + } + + @usableFromInline + internal func _writeAll( + _ sequence: S + ) -> Result where S.Element == UInt8 { + sequence._withRawBufferPointer { buffer in + var idx = 0 + while idx < buffer.count { + switch _write( + UnsafeRawBufferPointer(rebasing: buffer[idx...]), retryOnInterrupt: true + ) { + case .success(let numBytes): idx += numBytes + case .failure(let err): return .failure(err) + } + } + assert(idx == buffer.count) + return .success(buffer.count) + } + } + + /// Writes a sequence of bytes to the given offset. + /// + /// - Parameters: + /// - offset: The file offset where writing begins. + /// - sequence: The bytes to write. + /// - Returns: The number of bytes written, equal to the number of elements in `sequence`. + /// + /// This method either writes the entire contents of `sequence`, + /// or throws an error if only part of the content was written. + /// Unlike ``writeAll(_:)``, + /// this method preserves the file descriptor's existing offset. + /// + /// If `sequence` doesn't implement + /// the method, + /// temporary space will be allocated as needed. + @_alwaysEmitIntoClient + @discardableResult + public func writeAll( + toAbsoluteOffset offset: Int64, _ sequence: S + ) throws -> Int where S.Element == UInt8 { + try _writeAll(toAbsoluteOffset: offset, sequence).get() + } + + @usableFromInline + internal func _writeAll( + toAbsoluteOffset offset: Int64, _ sequence: S + ) -> Result where S.Element == UInt8 { + sequence._withRawBufferPointer { buffer in + var idx = 0 + while idx < buffer.count { + switch _write( + toAbsoluteOffset: offset + Int64(idx), + UnsafeRawBufferPointer(rebasing: buffer[idx...]), + retryOnInterrupt: true + ) { + case .success(let numBytes): idx += numBytes + case .failure(let err): return .failure(err) + } + } + assert(idx == buffer.count) + return .success(buffer.count) + } + } +} diff --git a/Sources/System/FileOperations.swift b/Sources/System/FileOperations.swift new file mode 100644 index 00000000..b1ce2358 --- /dev/null +++ b/Sources/System/FileOperations.swift @@ -0,0 +1,312 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +@_implementationOnly import SystemInternals + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FileDescriptor { + /// Opens or creates a file for reading or writing. + /// + /// - Parameters: + /// - path: The location of the file to open. + /// - mode: The read and write access to use. + /// - options: The behavior for opening the file. + /// - permissions: The file permissions to use for created files. + /// - retryOnInterrupt: Whether to retry the open operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: A file descriptor for the open file + /// + /// The corresponding C function is `open`. + @_alwaysEmitIntoClient + public static func open( + _ path: FilePath, + _ mode: FileDescriptor.AccessMode, + options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(), + permissions: FilePermissions? = nil, + retryOnInterrupt: Bool = true + ) throws -> FileDescriptor { + try path.withCString { + try FileDescriptor.open( + $0, mode, options: options, permissions: permissions, retryOnInterrupt: retryOnInterrupt) + } + } + + /// Opens or creates a file for reading or writing. + /// + /// - Parameters: + /// - path: The location of the file to open. + /// - mode: The read and write access to use. + /// - options: The behavior for opening the file. + /// - permissions: The file permissions to use for created files. + /// - retryOnInterrupt: Whether to retry the open operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: A file descriptor for the open file + /// + /// The corresponding C function is `open`. + @_alwaysEmitIntoClient + public static func open( + _ path: UnsafePointer, + _ mode: FileDescriptor.AccessMode, + options: FileDescriptor.OpenOptions = FileDescriptor.OpenOptions(), + permissions: FilePermissions? = nil, + retryOnInterrupt: Bool = true + ) throws -> FileDescriptor { + try FileDescriptor._open( + path, mode, options: options, permissions: permissions, retryOnInterrupt: retryOnInterrupt + ).get() + } + + @usableFromInline + internal static func _open( + _ path: UnsafePointer, + _ mode: FileDescriptor.AccessMode, + options: FileDescriptor.OpenOptions, + permissions: FilePermissions?, + retryOnInterrupt: Bool = true + ) -> Result { + let oFlag = mode.rawValue | options.rawValue + let descOrError: Result = valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + if let permissions = permissions { + return system_open(path, oFlag, permissions.rawValue) + } + precondition(!options.contains(.create), + "Create must be given permissions") + return system_open(path, oFlag) + } + return descOrError.map { FileDescriptor(rawValue: $0) } + } + + /// Deletes a file descriptor. + /// + /// Deletes the file descriptor from the per-process object reference table. + /// If this is the last reference to the underlying object, + /// the object will be deactivated. + /// + /// The corresponding C function is `close`. + @_alwaysEmitIntoClient + public func close() throws { try _close().get() } + + @usableFromInline + internal func _close() -> Result<(), Errno> { + nothingOrErrno(system_close(self.rawValue)) + } + + /// Reposition the offset for the given file descriptor. + /// + /// - Parameters: + /// - offset: The new offset for the file descriptor. + /// - whence: The origin of the new offset. + /// - Returns: The file's offset location, + /// in bytes from the beginning of the file. + /// + /// The corresponding C function is `lseek`. + @_alwaysEmitIntoClient + @discardableResult + public func seek( + offset: Int64, from whence: FileDescriptor.SeekOrigin + ) throws -> Int64 { + try _seek(offset: offset, from: whence).get() + } + + @usableFromInline + internal func _seek( + offset: Int64, from whence: FileDescriptor.SeekOrigin + ) -> Result { + let newOffset = system_lseek(self.rawValue, COffT(offset), whence.rawValue) + return valueOrErrno(Int64(newOffset)) + } + + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "seek") + public func lseek( + offset: Int64, from whence: FileDescriptor.SeekOrigin + ) throws -> Int64 { + try seek(offset: offset, from: whence) + } + + /// Reads bytes at the current file offset into a buffer. + /// + /// - Parameters: + /// - buffer: The region of memory to read into. + /// - retryOnInterrupt: Whether to retry the read operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were read. + /// + /// The property of `buffer` + /// determines the maximum number of bytes that are read into that buffer. + /// + /// After reading, + /// this method increments the file's offset by the number of bytes read. + /// To change the file's offset, + /// call the ``seek(offset:from:)`` method. + /// + /// The corresponding C function is `read`. + @_alwaysEmitIntoClient + public func read( + into buffer: UnsafeMutableRawBufferPointer, + retryOnInterrupt: Bool = true + ) throws -> Int { + try _read(into: buffer, retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _read( + into buffer: UnsafeMutableRawBufferPointer, + retryOnInterrupt: Bool = true + ) throws -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_read(self.rawValue, buffer.baseAddress, buffer.count) + } + } + + /// Reads bytes at the specified offset into a buffer. + /// + /// - Parameters: + /// - offset: The file offset where reading begins. + /// - buffer: The region of memory to read into. + /// - retryOnInterrupt: Whether to retry the read operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were read. + /// + /// The property of `buffer` + /// determines the maximum number of bytes that are read into that buffer. + /// + /// Unlike , + /// this method leaves the file's existing offset unchanged. + /// + /// The corresponding C function is `pread`. + @_alwaysEmitIntoClient + public func read( + fromAbsoluteOffset offset: Int64, + into buffer: UnsafeMutableRawBufferPointer, + retryOnInterrupt: Bool = true + ) throws -> Int { + try _read( + fromAbsoluteOffset: offset, + into: buffer, + retryOnInterrupt: retryOnInterrupt + ).get() + } + + @usableFromInline + internal func _read( + fromAbsoluteOffset offset: Int64, + into buffer: UnsafeMutableRawBufferPointer, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_pread(self.rawValue, buffer.baseAddress, buffer.count, COffT(offset)) + } + } + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "read") + public func pread( + fromAbsoluteOffset offset: Int64, + into buffer: UnsafeMutableRawBufferPointer, + retryOnInterrupt: Bool = true + ) throws -> Int { + try read( + fromAbsoluteOffset: offset, + into: buffer, + retryOnInterrupt: retryOnInterrupt) + } + + /// Writes the contents of a buffer at the current file offset. + /// + /// - Parameters: + /// - buffer: The region of memory that contains the data being written. + /// - retryOnInterrupt: Whether to retry the write operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were written. + /// + /// After writing, + /// this method increments the file's offset by the number of bytes written. + /// To change the file's offset, + /// call the ``seek(offset:from:)`` method. + /// + /// The corresponding C function is `write`. + @_alwaysEmitIntoClient + public func write( + _ buffer: UnsafeRawBufferPointer, + retryOnInterrupt: Bool = true + ) throws -> Int { + try _write(buffer, retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _write( + _ buffer: UnsafeRawBufferPointer, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_write(self.rawValue, buffer.baseAddress, buffer.count) + } + } + + /// Writes the contents of a buffer at the specified offset. + /// + /// - Parameters: + /// - offset: The file offset where writing begins. + /// - buffer: The region of memory that contains the data being written. + /// - retryOnInterrupt: Whether to retry the write operation + /// if it throws ``Errno/interrupted``. + /// The default is `true`. + /// Pass `false` to try only once and throw an error upon interruption. + /// - Returns: The number of bytes that were written. + /// + /// Unlike ``write(_:retryOnInterrupt:)``, + /// this method leaves the file's existing offset unchanged. + /// + /// The corresponding C function is `pwrite`. + @_alwaysEmitIntoClient + public func write( + toAbsoluteOffset offset: Int64, + _ buffer: UnsafeRawBufferPointer, + retryOnInterrupt: Bool = true + ) throws -> Int { + try _write(toAbsoluteOffset: offset, buffer, retryOnInterrupt: retryOnInterrupt).get() + } + + @usableFromInline + internal func _write( + toAbsoluteOffset offset: Int64, + _ buffer: UnsafeRawBufferPointer, + retryOnInterrupt: Bool + ) -> Result { + valueOrErrno(retryOnInterrupt: retryOnInterrupt) { + system_pwrite(self.rawValue, buffer.baseAddress, buffer.count, COffT(offset)) + } + } + + + @_alwaysEmitIntoClient + @available(*, unavailable, renamed: "write") + public func pwrite( + toAbsoluteOffset offset: Int64, + into buffer: UnsafeRawBufferPointer, + retryOnInterrupt: Bool = true + ) throws -> Int { + try write( + toAbsoluteOffset: offset, + buffer, + retryOnInterrupt: retryOnInterrupt) + } +} diff --git a/Sources/System/FilePath.swift b/Sources/System/FilePath.swift new file mode 100644 index 00000000..2fa7cc83 --- /dev/null +++ b/Sources/System/FilePath.swift @@ -0,0 +1,210 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +extension UnsafePointer where Pointee == UInt8 { + internal var _asCChar: UnsafePointer { + UnsafeRawPointer(self).assumingMemoryBound(to: CChar.self) + } +} +extension UnsafePointer where Pointee == CChar { + internal var _asUInt8: UnsafePointer { + UnsafeRawPointer(self).assumingMemoryBound(to: UInt8.self) + } +} +extension UnsafeBufferPointer where Element == UInt8 { + internal var _asCChar: UnsafeBufferPointer { + let base = baseAddress?._asCChar + return UnsafeBufferPointer(start: base, count: self.count) + } +} +extension UnsafeBufferPointer where Element == CChar { + internal var _asUInt8: UnsafeBufferPointer { + let base = baseAddress?._asUInt8 + return UnsafeBufferPointer(start: base, count: self.count) + } +} + +// NOTE: FilePath not frozen for ABI flexibility + +/// A null-terminated sequence of bytes +/// that represents a location in the file system. +/// +/// This structure doesn't give any meaning to the bytes that it contains, +/// except for the requirement that the last byte is a NUL (`0x0`). +/// The file system defines how this string is interpreted; +/// for example, by its choice of string encoding. +/// +/// The code below creates a file path from a string literal, +/// and then uses it to open and append to a log file: +/// +/// let message: String = "This is a log message." +/// let path: FilePath = "/tmp/log" +/// let fd = try FileDescriptor.open(path, .writeOnly, options: .append) +/// try fd.closeAfter { try fd.writeAll(message.utf8) } +/// +/// File paths conform to the +/// and +/// and protocols +/// by performing the protocols' operations on their raw byte contents. +/// This conformance allows file paths to be used, +/// for example, as keys in a dictionary. +/// However, the rules for path equivalence +/// are file-system–specific and have additional considerations +/// like case insensitivity, Unicode normalization, and symbolic links. +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public struct FilePath { + internal var bytes: [CChar] + + /// Creates an empty, null-terminated path. + public init() { + self.bytes = [0] + _invariantCheck() + } +} + +// +// MARK: - Public Interfaces +// +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePath { + /// The length of the file path, excluding the null termination. + public var length: Int { bytes.count - 1 } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePath { + internal init(nullTerminatedBytes: C) where C.Element == CChar { + self.bytes = Array(nullTerminatedBytes) + _invariantCheck() + } + + internal init(byteContents bytes: C) where C.Element == CChar { + var nulTermBytes = Array(bytes) + nulTermBytes.append(0) + self.init(nullTerminatedBytes: nulTermBytes) + } +} + +@_implementationOnly import SystemInternals + +// +// MARK: - CString interfaces +// +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePath { + /// Creates a file path by copying bytes from a null-terminated C string. + /// + /// - Parameter cString: A pointer to a null-terminated C string. + public init(cString: UnsafePointer) { + self.init(nullTerminatedBytes: + UnsafeBufferPointer(start: cString, count: 1 + system_strlen(cString))) + } + + /// Calls the given closure with a pointer to the contents of the file path, + /// represented as a null-terminated C string. + /// + /// - Parameter body: A closure with a pointer parameter + /// that points to a null-terminated C string. + /// If `body` has a return value, + /// that value is also used as the return value for this method. + /// - Returns: The return value, if any, of the `body` closure parameter. + /// + /// The pointer passed as an argument to `body` is valid + /// only during the execution of this method. + /// Don't try to store the pointer for later use. + public func withCString( + _ body: (UnsafePointer) throws -> Result + ) rethrows -> Result { + try bytes.withUnsafeBufferPointer { try body($0.baseAddress!) } + } + + // TODO: in the future, with opaque result types with associated + // type constraints, we want to provide a RAC for terminated + // byte contents and unterminated byte contents. +} + +// +// MARK: - String interfaces +// +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePath: ExpressibleByStringLiteral { + /// Creates a file path from a string literal. + /// + /// - Parameter stringLiteral: A string literal + /// whose UTF-8 contents to use as the contents of the path. + public init(stringLiteral: String) { + self.init(stringLiteral) + } + + /// Creates a file path from a string. + /// + /// - Parameter string: A string + /// whose UTF-8 contents to use as the contents of the path. + public init(_ string: String) { + var str = string + self = str.withUTF8 { FilePath(byteContents: $0._asCChar) } + } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension String { + /// Creates a string by interpreting the file path's content as UTF-8. + /// + /// - Parameter path: The file path to be interpreted as UTF-8. + /// + /// If the content of the file path + /// isn't a well-formed UTF-8 string, + /// this initializer removes invalid bytes or replaces them with U+FFFD. + /// This means that, depending on the semantics of the specific file system, + /// conversion to a string and back to a path + /// might result in a value that's different from the original path. + public init(decoding path: FilePath) { + self = path.withCString { String(cString: $0) } + } + + @available(*, deprecated, renamed: "String.init(decoding:)") + public init(_ path: FilePath) { + self.init(decoding: path) + } + + /// Creates a string from a file path, validating its UTF-8 contents. + /// + /// - Parameter path: The file path be interpreted as UTF-8. + /// + /// If the contents of the file path + /// isn't a well-formed UTF-8 string, + /// this initializer returns `nil`. + public init?(validatingUTF8 path: FilePath) { + guard let str = path.withCString({ String(validatingUTF8: $0) }) else { + return nil + } + self = str + } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePath: CustomStringConvertible, CustomDebugStringConvertible { + /// A textual representation of the file path. + @inline(never) + public var description: String { String(decoding: self) } + + /// A textual representation of the file path, suitable for debugging. + public var debugDescription: String { self.description.debugDescription } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePath: Hashable, Codable {} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePath { + fileprivate func _invariantCheck() { + precondition(bytes.last! == 0) + _debugPrecondition(bytes.firstIndex(of: 0) == bytes.count - 1) + } +} diff --git a/Sources/System/FilePermissions.swift b/Sources/System/FilePermissions.swift new file mode 100644 index 00000000..0b3217b1 --- /dev/null +++ b/Sources/System/FilePermissions.swift @@ -0,0 +1,177 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +/// The access permissions for a file. +/// +/// The following example +/// creates an instance of the `FilePermissions` structure +/// from a raw octal literal and compares it +/// to a file permission created using named options: +/// +/// let perms = FilePermissions(rawValue: 0o644) +/// perms == [.ownerReadWrite, .groupRead, .otherRead] // true +@frozen +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public struct FilePermissions: OptionSet, Hashable, Codable { + /// The raw C file permissions. + @_alwaysEmitIntoClient + public let rawValue: CModeT + + /// Create a strongly-typed file permission from a raw C value. + @_alwaysEmitIntoClient + public init(rawValue: CModeT) { self.rawValue = rawValue } + + @_alwaysEmitIntoClient + private init(_ raw: CModeT) { self.init(rawValue: raw) } + + /// Indicates that other users have read-only permission. + @_alwaysEmitIntoClient + public static var otherRead: FilePermissions { FilePermissions(0o4) } + + /// Indicates that other users have write-only permission. + @_alwaysEmitIntoClient + public static var otherWrite: FilePermissions { FilePermissions(0o2) } + + /// Indicates that other users have execute-only permission. + @_alwaysEmitIntoClient + public static var otherExecute: FilePermissions { FilePermissions(0o1) } + + /// Indicates that other users have read-write permission. + @_alwaysEmitIntoClient + public static var otherReadWrite: FilePermissions { FilePermissions(0o6) } + + /// Indicates that other users have read-execute permission. + @_alwaysEmitIntoClient + public static var otherReadExecute: FilePermissions { FilePermissions(0o5) } + + /// Indicates that other users have write-execute permission. + @_alwaysEmitIntoClient + public static var otherWriteExecute: FilePermissions { FilePermissions(0o3) } + + /// Indicates that other users have read, write, and execute permission. + @_alwaysEmitIntoClient + public static var otherReadWriteExecute: FilePermissions { FilePermissions(0o7) } + + /// Indicates that the group has read-only permission. + @_alwaysEmitIntoClient + public static var groupRead: FilePermissions { FilePermissions(0o40) } + + /// Indicates that the group has write-only permission. + @_alwaysEmitIntoClient + public static var groupWrite: FilePermissions { FilePermissions(0o20) } + + /// Indicates that the group has execute-only permission. + @_alwaysEmitIntoClient + public static var groupExecute: FilePermissions { FilePermissions(0o10) } + + /// Indicates that the group has read-write permission. + @_alwaysEmitIntoClient + public static var groupReadWrite: FilePermissions { FilePermissions(0o60) } + + /// Indicates that the group has read-execute permission. + @_alwaysEmitIntoClient + public static var groupReadExecute: FilePermissions { FilePermissions(0o50) } + + /// Indicates that the group has write-execute permission. + @_alwaysEmitIntoClient + public static var groupWriteExecute: FilePermissions { FilePermissions(0o30) } + + /// Indicates that the group has read, write, and execute permission. + @_alwaysEmitIntoClient + public static var groupReadWriteExecute: FilePermissions { FilePermissions(0o70) } + + /// Indicates that the owner has read-only permission. + @_alwaysEmitIntoClient + public static var ownerRead: FilePermissions { FilePermissions(0o400) } + + /// Indicates that the owner has write-only permission. + @_alwaysEmitIntoClient + public static var ownerWrite: FilePermissions { FilePermissions(0o200) } + + /// Indicates that the owner has execute-only permission. + @_alwaysEmitIntoClient + public static var ownerExecute: FilePermissions { FilePermissions(0o100) } + + /// Indicates that the owner has read-write permission. + @_alwaysEmitIntoClient + public static var ownerReadWrite: FilePermissions { FilePermissions(0o600) } + + /// Indicates that the owner has read-execute permission. + @_alwaysEmitIntoClient + public static var ownerReadExecute: FilePermissions { FilePermissions(0o500) } + + /// Indicates that the owner has write-execute permission. + @_alwaysEmitIntoClient + public static var ownerWriteExecute: FilePermissions { FilePermissions(0o300) } + + /// Indicates that the owner has read, write, and execute permission. + @_alwaysEmitIntoClient + public static var ownerReadWriteExecute: FilePermissions { FilePermissions(0o700) } + + /// Indicates that the file is executed as the owner. + /// + /// For more information, see the `setuid(2)` man page. + @_alwaysEmitIntoClient + public static var setUserID: FilePermissions { FilePermissions(0o4000) } + + /// Indicates that the file is executed as the group. + /// + /// For more information, see the `setgid(2)` man page. + @_alwaysEmitIntoClient + public static var setGroupID: FilePermissions { FilePermissions(0o2000) } + + /// Indicates that executable's text segment + /// should be kept in swap space even after it exits. + /// + /// For more information, see the `chmod(2)` man page's + /// discussion of `S_ISVTX` (the sticky bit). + @_alwaysEmitIntoClient + public static var saveText: FilePermissions { FilePermissions(0o1000) } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension FilePermissions + : CustomStringConvertible, CustomDebugStringConvertible +{ + /// A textual representation of the file permissions. + @inline(never) + public var description: String { + let descriptions: [(Element, StaticString)] = [ + (.ownerReadWriteExecute, ".ownerReadWriteExecute"), + (.ownerReadWrite, ".ownerReadWrite"), + (.ownerReadExecute, ".ownerReadExecute"), + (.ownerWriteExecute, ".ownerWriteExecute"), + (.ownerRead, ".ownerRead"), + (.ownerWrite, ".ownerWrite"), + (.ownerExecute, ".ownerExecute"), + (.groupReadWriteExecute, ".groupReadWriteExecute"), + (.groupReadWrite, ".groupReadWrite"), + (.groupReadExecute, ".groupReadExecute"), + (.groupWriteExecute, ".groupWriteExecute"), + (.groupRead, ".groupRead"), + (.groupWrite, ".groupWrite"), + (.groupExecute, ".groupExecute"), + (.otherReadWriteExecute, ".otherReadWriteExecute"), + (.otherReadWrite, ".otherReadWrite"), + (.otherReadExecute, ".otherReadExecute"), + (.otherWriteExecute, ".otherWriteExecute"), + (.otherRead, ".otherRead"), + (.otherWrite, ".otherWrite"), + (.otherExecute, ".otherExecute"), + (.setUserID, ".setUserID"), + (.setGroupID, ".setGroupID"), + (.saveText, ".saveText") + ] + + return _buildDescription(descriptions) + } + + /// A textual representation of the file permissions, suitable for debugging. + public var debugDescription: String { self.description } +} diff --git a/Sources/System/LinuxPlatformConstants.swift b/Sources/System/LinuxPlatformConstants.swift new file mode 100644 index 00000000..3a197b94 --- /dev/null +++ b/Sources/System/LinuxPlatformConstants.swift @@ -0,0 +1,487 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +#if os(Linux) + +// Ugh, this is really bad. For Darwin, we can at least rely on +// these values not changing much, but in theory they could change +// per Linux flavor or version (if no ABI) + +// MARK: errno + +@_alwaysEmitIntoClient +internal var _EPERM: CInt { 1 } + +@_alwaysEmitIntoClient +internal var _ENOENT: CInt { 2 } + +@_alwaysEmitIntoClient +internal var _ESRCH: CInt { 3 } + +@_alwaysEmitIntoClient +internal var _EINTR: CInt { 4 } + +@_alwaysEmitIntoClient +internal var _EIO: CInt { 5 } + +@_alwaysEmitIntoClient +internal var _ENXIO: CInt { 6 } + +@_alwaysEmitIntoClient +internal var _E2BIG: CInt { 7 } + +@_alwaysEmitIntoClient +internal var _ENOEXEC: CInt { 8 } + +@_alwaysEmitIntoClient +internal var _EBADF: CInt { 9 } + +@_alwaysEmitIntoClient +internal var _ECHILD: CInt { 10 } + +@_alwaysEmitIntoClient +internal var _EAGAIN: CInt { 11 } + +@_alwaysEmitIntoClient +internal var _ENOMEM: CInt { 12 } + +@_alwaysEmitIntoClient +internal var _EACCES: CInt { 13 } + +@_alwaysEmitIntoClient +internal var _EFAULT: CInt { 14 } + +@_alwaysEmitIntoClient +internal var _ENOTBLK: CInt { 15 } + +@_alwaysEmitIntoClient +internal var _EBUSY: CInt { 16 } + +@_alwaysEmitIntoClient +internal var _EEXIST: CInt { 17 } + +@_alwaysEmitIntoClient +internal var _EXDEV: CInt { 18 } + +@_alwaysEmitIntoClient +internal var _ENODEV: CInt { 19 } + +@_alwaysEmitIntoClient +internal var _ENOTDIR: CInt { 20 } + +@_alwaysEmitIntoClient +internal var _EISDIR: CInt { 21 } + +@_alwaysEmitIntoClient +internal var _EINVAL: CInt { 22 } + +@_alwaysEmitIntoClient +internal var _ENFILE: CInt { 23 } + +@_alwaysEmitIntoClient +internal var _EMFILE: CInt { 24 } + +@_alwaysEmitIntoClient +internal var _ENOTTY: CInt { 25 } + +@_alwaysEmitIntoClient +internal var _ETXTBSY: CInt { 26 } + +@_alwaysEmitIntoClient +internal var _EFBIG: CInt { 27 } + +@_alwaysEmitIntoClient +internal var _ENOSPC: CInt { 28 } + +@_alwaysEmitIntoClient +internal var _ESPIPE: CInt { 29 } + +@_alwaysEmitIntoClient +internal var _EROFS: CInt { 30 } + +@_alwaysEmitIntoClient +internal var _EMLINK: CInt { 31 } + +@_alwaysEmitIntoClient +internal var _EPIPE: CInt { 32 } + +@_alwaysEmitIntoClient +internal var _EDOM: CInt { 33 } + +@_alwaysEmitIntoClient +internal var _ERANGE: CInt { 34 } + +@_alwaysEmitIntoClient +internal var _EDEADLK: CInt { 35 } + +@_alwaysEmitIntoClient +internal var _ENAMETOOLONG: CInt { 36 } + +@_alwaysEmitIntoClient +internal var _ENOLCK: CInt { 37 } + +@_alwaysEmitIntoClient +internal var _ENOSYS: CInt { 38 } + +@_alwaysEmitIntoClient +internal var _ENOTEMPTY: CInt { 39 } + +@_alwaysEmitIntoClient +internal var _ELOOP: CInt { 40 } + +@_alwaysEmitIntoClient +internal var _EWOULDBLOCK: CInt { _EAGAIN } + +@_alwaysEmitIntoClient +internal var _ENOMSG: CInt { 42 } + +@_alwaysEmitIntoClient +internal var _EIDRM: CInt { 43 } + +@_alwaysEmitIntoClient +internal var _ECHRNG: CInt { 44 } + +@_alwaysEmitIntoClient +internal var _EL2NSYNC: CInt { 45 } + +@_alwaysEmitIntoClient +internal var _EL3HLT: CInt { 46 } + +@_alwaysEmitIntoClient +internal var _EL3RST: CInt { 47 } + +@_alwaysEmitIntoClient +internal var _ELNRNG: CInt { 48 } + +@_alwaysEmitIntoClient +internal var _EUNATCH: CInt { 49 } + +@_alwaysEmitIntoClient +internal var _ENOCSI: CInt { 50 } + +@_alwaysEmitIntoClient +internal var _EL2HLT: CInt { 51 } + +@_alwaysEmitIntoClient +internal var _EBADE: CInt { 52 } + +@_alwaysEmitIntoClient +internal var _EBADR: CInt { 53 } + +@_alwaysEmitIntoClient +internal var _EXFULL: CInt { 54 } + +@_alwaysEmitIntoClient +internal var _ENOANO: CInt { 55 } + +@_alwaysEmitIntoClient +internal var _EBADRQC: CInt { 56 } + +@_alwaysEmitIntoClient +internal var _EBADSLT: CInt { 57 } + +@_alwaysEmitIntoClient +internal var _EDEADLOCK: CInt { _EDEADLK } + +@_alwaysEmitIntoClient +internal var _EBFONT: CInt { 59 } + +@_alwaysEmitIntoClient +internal var _ENOSTR: CInt { 60 } + +@_alwaysEmitIntoClient +internal var _ENODATA: CInt { 61 } + +@_alwaysEmitIntoClient +internal var _ETIME: CInt { 62 } + +@_alwaysEmitIntoClient +internal var _ENOSR: CInt { 63 } + +@_alwaysEmitIntoClient +internal var _ENONET: CInt { 64 } + +@_alwaysEmitIntoClient +internal var _ENOPKG: CInt { 65 } + +@_alwaysEmitIntoClient +internal var _EREMOTE: CInt { 66 } + +@_alwaysEmitIntoClient +internal var _ENOLINK: CInt { 67 } + +@_alwaysEmitIntoClient +internal var _EADV: CInt { 68 } + +@_alwaysEmitIntoClient +internal var _ESRMNT: CInt { 69 } + +@_alwaysEmitIntoClient +internal var _ECOMM: CInt { 70 } + +@_alwaysEmitIntoClient +internal var _EPROTO: CInt { 71 } + +@_alwaysEmitIntoClient +internal var _EMULTIHOP: CInt { 72 } + +@_alwaysEmitIntoClient +internal var _EDOTDOT: CInt { 73 } + +@_alwaysEmitIntoClient +internal var _EBADMSG: CInt { 74 } + +@_alwaysEmitIntoClient +internal var _EOVERFLOW: CInt { 75 } + +@_alwaysEmitIntoClient +internal var _ENOTUNIQ: CInt { 76 } + +@_alwaysEmitIntoClient +internal var _EBADFD: CInt { 77 } + +@_alwaysEmitIntoClient +internal var _EREMCHG: CInt { 78 } + +@_alwaysEmitIntoClient +internal var _ELIBACC: CInt { 79 } + +@_alwaysEmitIntoClient +internal var _ELIBBAD: CInt { 80 } + +@_alwaysEmitIntoClient +internal var _ELIBSCN: CInt { 81 } + +@_alwaysEmitIntoClient +internal var _ELIBMAX: CInt { 82 } + +@_alwaysEmitIntoClient +internal var _ELIBEXEC: CInt { 83 } + +@_alwaysEmitIntoClient +internal var _EILSEQ: CInt { 84 } + +@_alwaysEmitIntoClient +internal var _ERESTART: CInt { 85 } + +@_alwaysEmitIntoClient +internal var _ESTRPIPE: CInt { 86 } + +@_alwaysEmitIntoClient +internal var _EUSERS: CInt { 87 } + +@_alwaysEmitIntoClient +internal var _ENOTSOCK: CInt { 88 } + +@_alwaysEmitIntoClient +internal var _EDESTADDRREQ: CInt { 89 } + +@_alwaysEmitIntoClient +internal var _EMSGSIZE: CInt { 90 } + +@_alwaysEmitIntoClient +internal var _EPROTOTYPE: CInt { 91 } + +@_alwaysEmitIntoClient +internal var _ENOPROTOOPT: CInt { 92 } + +@_alwaysEmitIntoClient +internal var _EPROTONOSUPPORT: CInt { 93 } + +@_alwaysEmitIntoClient +internal var _ESOCKTNOSUPPORT: CInt { 94 } + +@_alwaysEmitIntoClient +internal var _EOPNOTSUPP: CInt { 95 } + +@_alwaysEmitIntoClient +internal var _ENOTSUP: CInt { _EOPNOTSUPP } + +@_alwaysEmitIntoClient +internal var _EPFNOSUPPORT: CInt { 96 } + +@_alwaysEmitIntoClient +internal var _EAFNOSUPPORT: CInt { 97 } + +@_alwaysEmitIntoClient +internal var _EADDRINUSE: CInt { 98 } + +@_alwaysEmitIntoClient +internal var _EADDRNOTAVAIL: CInt { 99 } + +@_alwaysEmitIntoClient +internal var _ENETDOWN: CInt { 100 } + +@_alwaysEmitIntoClient +internal var _ENETUNREACH: CInt { 101 } + +@_alwaysEmitIntoClient +internal var _ENETRESET: CInt { 102 } + +@_alwaysEmitIntoClient +internal var _ECONNABORTED: CInt { 103 } + +@_alwaysEmitIntoClient +internal var _ECONNRESET: CInt { 104 } + +@_alwaysEmitIntoClient +internal var _ENOBUFS: CInt { 105 } + +@_alwaysEmitIntoClient +internal var _EISCONN: CInt { 106 } + +@_alwaysEmitIntoClient +internal var _ENOTCONN: CInt { 107 } + +@_alwaysEmitIntoClient +internal var _ESHUTDOWN: CInt { 108 } + +@_alwaysEmitIntoClient +internal var _ETOOMANYREFS: CInt { 109 } + +@_alwaysEmitIntoClient +internal var _ETIMEDOUT: CInt { 110 } + +@_alwaysEmitIntoClient +internal var _ECONNREFUSED: CInt { 111 } + +@_alwaysEmitIntoClient +internal var _EHOSTDOWN: CInt { 112 } + +@_alwaysEmitIntoClient +internal var _EHOSTUNREACH: CInt { 113 } + +@_alwaysEmitIntoClient +internal var _EALREADY: CInt { 114 } + +@_alwaysEmitIntoClient +internal var _EINPROGRESS: CInt { 115 } + +@_alwaysEmitIntoClient +internal var _ESTALE: CInt { 116 } + +@_alwaysEmitIntoClient +internal var _EUCLEAN: CInt { 117 } + +@_alwaysEmitIntoClient +internal var _ENOTNAM: CInt { 118 } + +@_alwaysEmitIntoClient +internal var _ENAVAIL: CInt { 119 } + +@_alwaysEmitIntoClient +internal var _EISNAM: CInt { 120 } + +@_alwaysEmitIntoClient +internal var _EREMOTEIO: CInt { 121 } + +@_alwaysEmitIntoClient +internal var _EDQUOT: CInt { 122 } + +@_alwaysEmitIntoClient +internal var _ENOMEDIUM: CInt { 123 } + +@_alwaysEmitIntoClient +internal var _EMEDIUMTYPE: CInt { 124 } + +@_alwaysEmitIntoClient +internal var _ECANCELED: CInt { 125 } + +@_alwaysEmitIntoClient +internal var _ENOKEY: CInt { 126 } + +@_alwaysEmitIntoClient +internal var _EKEYEXPIRED: CInt { 127 } + +@_alwaysEmitIntoClient +internal var _EKEYREVOKED: CInt { 128 } + +@_alwaysEmitIntoClient +internal var _EKEYREJECTED: CInt { 129 } + +@_alwaysEmitIntoClient +internal var _EOWNERDEAD: CInt { 130 } + +@_alwaysEmitIntoClient +internal var _ENOTRECOVERABLE: CInt { 131 } + +@_alwaysEmitIntoClient +internal var _ERFKILL: CInt { 132 } + +@_alwaysEmitIntoClient +internal var _EHWPOISON: CInt { 133 } + + +// MARK: File Operations + +@_alwaysEmitIntoClient +internal var _O_ACCMODE: CInt { 0o00000003 } + +@_alwaysEmitIntoClient +internal var _O_RDONLY: CInt { 0o00000000 } + +@_alwaysEmitIntoClient +internal var _O_WRONLY: CInt { 0o00000001 } + +@_alwaysEmitIntoClient +internal var _O_RDWR: CInt { 0o00000002 } + +@_alwaysEmitIntoClient +internal var _O_CREAT: CInt { 0o00000100 } + +@_alwaysEmitIntoClient +internal var _O_EXCL: CInt { 0o00000200 } + +@_alwaysEmitIntoClient +internal var _O_NOCTTY: CInt { 0o00000400 } + +@_alwaysEmitIntoClient +internal var _O_TRUNC: CInt { 0o00001000 } + +@_alwaysEmitIntoClient +internal var _O_APPEND: CInt { 0o00002000 } + +@_alwaysEmitIntoClient +internal var _O_NONBLOCK: CInt { 0o00004000 } + +@_alwaysEmitIntoClient +internal var _O_DSYNC: CInt { 0o00010000 } + +@_alwaysEmitIntoClient +internal var _FASYNC: CInt { 0o00020000 } + +@_alwaysEmitIntoClient +internal var _O_DIRECT: CInt { 0o00040000 } + +@_alwaysEmitIntoClient +internal var _O_LARGEFILE: CInt { 0o00100000 } + +@_alwaysEmitIntoClient +internal var _O_DIRECTORY: CInt { 0o00200000 } + +@_alwaysEmitIntoClient +internal var _O_NOFOLLOW: CInt { 0o00400000 } + +@_alwaysEmitIntoClient +internal var _O_NOATIME: CInt { 0o01000000 } + +@_alwaysEmitIntoClient +internal var _O_CLOEXEC: CInt { 0o02000000 } + + +@_alwaysEmitIntoClient +internal var _SEEK_SET: CInt { 0 } + +@_alwaysEmitIntoClient +internal var _SEEK_CUR: CInt { 1 } + +@_alwaysEmitIntoClient +internal var _SEEK_END: CInt { 2 } + +#endif diff --git a/Sources/System/Platform.swift b/Sources/System/Platform.swift new file mode 100644 index 00000000..41fef79f --- /dev/null +++ b/Sources/System/Platform.swift @@ -0,0 +1,20 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +@_implementationOnly import SystemInternals + +// Public typealiases that can't be reexported from SystemInternals + +/// The C `mode_t` type. +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +public typealias CModeT = UInt16 +#else +public typealias CModeT = UInt32 +#endif diff --git a/Sources/System/Util.swift b/Sources/System/Util.swift new file mode 100644 index 00000000..2d966784 --- /dev/null +++ b/Sources/System/Util.swift @@ -0,0 +1,96 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +// Results in errno if i == -1 +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +internal func valueOrErrno( + _ i: I +) -> Result { + i == -1 ? .failure(Errno.current) : .success(i) +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +internal func nothingOrErrno( + _ i: I +) -> Result<(), Errno> { + valueOrErrno(i).map { _ in () } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +internal func valueOrErrno( + retryOnInterrupt: Bool, _ f: () -> I +) -> Result { + repeat { + switch valueOrErrno(f()) { + case .success(let r): return .success(r) + case .failure(let err): + guard retryOnInterrupt && err == .interrupted else { return .failure(err) } + break + } + } while true +} + +// Run a precondition for debug client builds +internal func _debugPrecondition( + _ condition: @autoclosure () -> Bool, _ message: StaticString = StaticString(), + file: StaticString = #file, line: UInt = #line +) { + // Only check in debug mode. + if _slowPath(_isDebugAssertConfiguration()) { precondition(condition()) } +} + +extension OpaquePointer { + internal var _isNULL: Bool { OpaquePointer(bitPattern: Int(bitPattern: self)) == nil } +} + +extension Sequence { + // Tries to recast contiguous pointer if available, otherwise allocates memory. + internal func _withRawBufferPointer( + _ body: (UnsafeRawBufferPointer) throws -> R + ) rethrows -> R { + guard let result = try self.withContiguousStorageIfAvailable({ + try body(UnsafeRawBufferPointer($0)) + }) else { + return try Array(self).withUnsafeBytes(body) + } + return result + } +} + +extension OptionSet { + // Helper method for building up a comma-separated list of options + // + // Taking an array of descriptions reduces code size vs + // a series of calls due to avoiding register copies. Make sure + // to pass an array literal and not an array built up from a series of + // append calls, else that will massively bloat code size. This takes + // StaticStrings because otherwise we get a warning about getting evicted + // from the shared cache. + @inline(never) + internal func _buildDescription( + _ descriptions: [(Element, StaticString)] + ) -> String { + var copy = self + var result = "[" + + for (option, name) in descriptions { + if _slowPath(copy.contains(option)) { + result += name.description + copy.remove(option) + if !copy.isEmpty { result += ", " } + } + } + + if _slowPath(!copy.isEmpty) { + result += "\(Self.self)(rawValue: \(copy.rawValue))" + } + result += "]" + return result + } +} diff --git a/Sources/SystemInternals/Exports.swift b/Sources/SystemInternals/Exports.swift new file mode 100644 index 00000000..c45bf329 --- /dev/null +++ b/Sources/SystemInternals/Exports.swift @@ -0,0 +1,91 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +import CSystem + +// TODO: Should CSystem just include all the header files we need? + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +import Darwin +#elseif os(Linux) || os(FreeBSD) || os(Android) +import Glibc +#else +#error("Unsupported Platform") +#endif + +public typealias COffT = off_t + +// MARK: syscalls and variables + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) +public var system_errno: CInt { + get { Darwin.errno } + set { Darwin.errno = newValue } +} +#else +public var system_errno: CInt { + get { Glibc.errno } + set { Glibc.errno = newValue } +} +#endif + +public func system_open(_ path: UnsafePointer, _ oflag: Int32) -> CInt { + open(path, oflag) +} + +public func system_open( + _ path: UnsafePointer, _ oflag: Int32, _ mode: mode_t +) -> CInt { + open(path, oflag, mode) +} + +public func system_close(_ fd: Int32) -> Int32 { + close(fd) +} + +public func system_read( + _ fd: Int32, _ buf: UnsafeMutableRawPointer!, _ nbyte: Int +) -> Int { + read(fd, buf, nbyte) +} + +public func system_pread( + _ fd: Int32, _ buf: UnsafeMutableRawPointer!, _ nbyte: Int, _ offset: off_t +) -> Int { + pread(fd, buf, nbyte, offset) +} + +public func system_lseek( + _ fd: Int32, _ off: off_t, _ whence: Int32 +) -> off_t { + lseek(fd, off, whence) +} + +public func system_write( + _ fd: Int32, _ buf: UnsafeRawPointer!, _ nbyte: Int +) -> Int { + write(fd, buf, nbyte) +} + +public func system_pwrite( + _ fd: Int32, _ buf: UnsafeRawPointer!, _ nbyte: Int, _ offset: off_t +) -> Int { + pwrite(fd, buf, nbyte, offset) +} + +// MARK: C stdlib decls + +public func system_strerror(_ __errnum: Int32) -> UnsafeMutablePointer! { + strerror(__errnum) +} + +public func system_strlen(_ s: UnsafePointer) -> Int { + strlen(s) +} + diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift new file mode 100644 index 00000000..00319974 --- /dev/null +++ b/Tests/LinuxMain.swift @@ -0,0 +1,17 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +import XCTest + +import SystemTests + +var tests = [XCTestCaseEntry]() +tests += SystemTests.__allTests() + +XCTMain(tests) diff --git a/Tests/SystemTests/ErrnoTest.swift b/Tests/SystemTests/ErrnoTest.swift new file mode 100644 index 00000000..5c026b3f --- /dev/null +++ b/Tests/SystemTests/ErrnoTest.swift @@ -0,0 +1,174 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +import XCTest +import SystemPackage + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +final class ErrnoTest: XCTestCase { + func testConstants() { + XCTAssert(EPERM == Errno.notPermitted.rawValue) + XCTAssert(ENOENT == Errno.noSuchFileOrDirectory.rawValue) + XCTAssert(ESRCH == Errno.noSuchProcess.rawValue) + XCTAssert(EINTR == Errno.interrupted.rawValue) + XCTAssert(EIO == Errno.ioError.rawValue) + XCTAssert(ENXIO == Errno.noSuchAddressOrDevice.rawValue) + XCTAssert(E2BIG == Errno.argListTooLong.rawValue) + XCTAssert(ENOEXEC == Errno.execFormatError.rawValue) + XCTAssert(EBADF == Errno.badFileDescriptor.rawValue) + XCTAssert(ECHILD == Errno.noChildProcess.rawValue) + XCTAssert(EDEADLK == Errno.deadlock.rawValue) + XCTAssert(ENOMEM == Errno.noMemory.rawValue) + XCTAssert(EACCES == Errno.permissionDenied.rawValue) + XCTAssert(EFAULT == Errno.badAddress.rawValue) + XCTAssert(ENOTBLK == Errno.notBlockDevice.rawValue) + XCTAssert(EBUSY == Errno.resourceBusy.rawValue) + XCTAssert(EEXIST == Errno.fileExists.rawValue) + XCTAssert(EXDEV == Errno.improperLink.rawValue) + XCTAssert(ENODEV == Errno.operationNotSupportedByDevice.rawValue) + XCTAssert(ENOTDIR == Errno.notDirectory.rawValue) + XCTAssert(EISDIR == Errno.isDirectory.rawValue) + XCTAssert(EINVAL == Errno.invalidArgument.rawValue) + XCTAssert(ENFILE == Errno.tooManyOpenFilesInSystem.rawValue) + XCTAssert(EMFILE == Errno.tooManyOpenFiles.rawValue) + XCTAssert(ENOTTY == Errno.inappropriateIOCTLForDevice.rawValue) + XCTAssert(ETXTBSY == Errno.textFileBusy.rawValue) + XCTAssert(EFBIG == Errno.fileTooLarge.rawValue) + XCTAssert(ENOSPC == Errno.noSpace.rawValue) + XCTAssert(ESPIPE == Errno.illegalSeek.rawValue) + XCTAssert(EROFS == Errno.readOnlyFileSystem.rawValue) + XCTAssert(EMLINK == Errno.tooManyLinks.rawValue) + XCTAssert(EPIPE == Errno.brokenPipe.rawValue) + XCTAssert(EDOM == Errno.outOfDomain.rawValue) + XCTAssert(ERANGE == Errno.outOfRange.rawValue) + XCTAssert(EAGAIN == Errno.resourceTemporarilyUnavailable.rawValue) + XCTAssert(EINPROGRESS == Errno.nowInProgress.rawValue) + XCTAssert(EALREADY == Errno.alreadyInProcess.rawValue) + XCTAssert(ENOTSOCK == Errno.notSocket.rawValue) + XCTAssert(EDESTADDRREQ == Errno.addressRequired.rawValue) + XCTAssert(EMSGSIZE == Errno.messageTooLong.rawValue) + XCTAssert(EPROTOTYPE == Errno.protocolWrongTypeForSocket.rawValue) + XCTAssert(ENOPROTOOPT == Errno.protocolNotAvailable.rawValue) + XCTAssert(EPROTONOSUPPORT == Errno.protocolNotSupported.rawValue) + XCTAssert(ESOCKTNOSUPPORT == Errno.socketTypeNotSupported.rawValue) + XCTAssert(ENOTSUP == Errno.notSupported.rawValue) + XCTAssert(EPFNOSUPPORT == Errno.protocolFamilyNotSupported.rawValue) + XCTAssert(EAFNOSUPPORT == Errno.addressFamilyNotSupported.rawValue) + XCTAssert(EADDRINUSE == Errno.addressInUse.rawValue) + XCTAssert(EADDRNOTAVAIL == Errno.addressNotAvailable.rawValue) + XCTAssert(ENETDOWN == Errno.networkDown.rawValue) + XCTAssert(ENETUNREACH == Errno.networkUnreachable.rawValue) + XCTAssert(ENETRESET == Errno.networkReset.rawValue) + XCTAssert(ECONNABORTED == Errno.connectionAbort.rawValue) + XCTAssert(ECONNRESET == Errno.connectionReset.rawValue) + XCTAssert(ENOBUFS == Errno.noBufferSpace.rawValue) + XCTAssert(EISCONN == Errno.socketIsConnected.rawValue) + XCTAssert(ENOTCONN == Errno.socketNotConnected.rawValue) + XCTAssert(ESHUTDOWN == Errno.socketShutdown.rawValue) + XCTAssert(ETIMEDOUT == Errno.timedOut.rawValue) + XCTAssert(ECONNREFUSED == Errno.connectionRefused.rawValue) + XCTAssert(ELOOP == Errno.tooManySymbolicLinkLevels.rawValue) + XCTAssert(ENAMETOOLONG == Errno.fileNameTooLong.rawValue) + XCTAssert(EHOSTDOWN == Errno.hostIsDown.rawValue) + XCTAssert(EHOSTUNREACH == Errno.noRouteToHost.rawValue) + XCTAssert(ENOTEMPTY == Errno.directoryNotEmpty.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssert(EPROCLIM == Errno.tooManyProcesses.rawValue) +#endif + + XCTAssert(EUSERS == Errno.tooManyUsers.rawValue) + XCTAssert(EDQUOT == Errno.diskQuotaExceeded.rawValue) + XCTAssert(ESTALE == Errno.staleNFSFileHandle.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssert(EBADRPC == Errno.rpcUnsuccessful.rawValue) + XCTAssert(ERPCMISMATCH == Errno.rpcVersionMismatch.rawValue) + XCTAssert(EPROGUNAVAIL == Errno.rpcProgramUnavailable.rawValue) + XCTAssert(EPROGMISMATCH == Errno.rpcProgramVersionMismatch.rawValue) + XCTAssert(EPROCUNAVAIL == Errno.rpcProcedureUnavailable.rawValue) +#endif + + XCTAssert(ENOLCK == Errno.noLocks.rawValue) + XCTAssert(ENOSYS == Errno.noFunction.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssert(EFTYPE == Errno.badFileTypeOrFormat.rawValue) + XCTAssert(EAUTH == Errno.authenticationError.rawValue) + XCTAssert(ENEEDAUTH == Errno.needAuthenticator.rawValue) + XCTAssert(EPWROFF == Errno.devicePowerIsOff.rawValue) + XCTAssert(EDEVERR == Errno.deviceError.rawValue) +#endif + + XCTAssert(EOVERFLOW == Errno.overflow.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssert(EBADEXEC == Errno.badExecutable.rawValue) + XCTAssert(EBADARCH == Errno.badCPUType.rawValue) + XCTAssert(ESHLIBVERS == Errno.sharedLibraryVersionMismatch.rawValue) + XCTAssert(EBADMACHO == Errno.malformedMachO.rawValue) +#endif + + XCTAssert(ECANCELED == Errno.canceled.rawValue) + XCTAssert(EIDRM == Errno.identifierRemoved.rawValue) + XCTAssert(ENOMSG == Errno.noMessage.rawValue) + XCTAssert(EILSEQ == Errno.illegalByteSequence.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssert(ENOATTR == Errno.attributeNotFound.rawValue) +#endif + + XCTAssert(EBADMSG == Errno.badMessage.rawValue) + XCTAssert(EMULTIHOP == Errno.multiHop.rawValue) + XCTAssert(ENODATA == Errno.noData.rawValue) + XCTAssert(ENOLINK == Errno.noLink.rawValue) + XCTAssert(ENOSR == Errno.noStreamResources.rawValue) + XCTAssert(ENOSTR == Errno.notStream.rawValue) + XCTAssert(EPROTO == Errno.protocolError.rawValue) + XCTAssert(ETIME == Errno.timeout.rawValue) + XCTAssert(EOPNOTSUPP == Errno.notSupportedOnSocket.rawValue) + + // From headers but not man page + XCTAssert(EWOULDBLOCK == Errno.wouldBlock.rawValue) + XCTAssert(ETOOMANYREFS == Errno.tooManyReferences.rawValue) + XCTAssert(EREMOTE == Errno.tooManyRemoteLevels.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssert(ENOPOLICY == Errno.noSuchPolicy.rawValue) +#endif + + XCTAssert(ENOTRECOVERABLE == Errno.notRecoverable.rawValue) + XCTAssert(EOWNERDEAD == Errno.previousOwnerDied.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssert(EQFULL == Errno.outputQueueFull.rawValue) + XCTAssert(ELAST == Errno.lastErrnoValue.rawValue) +#endif + } + + func testPatternMatching() { + func throwsEPERM() throws { + throw Errno.notPermitted + } + + do { + try throwsEPERM() + } catch Errno.noSuchProcess { + XCTAssert(false) + } catch Errno.notPermitted { + // pass + } catch { + XCTAssert(false) + } + } + + // TODO: `_code/_domain` for NSError bridging + + // TODO: `description` +} diff --git a/Tests/SystemTests/FileOperationsTest.swift b/Tests/SystemTests/FileOperationsTest.swift new file mode 100644 index 00000000..d3f74162 --- /dev/null +++ b/Tests/SystemTests/FileOperationsTest.swift @@ -0,0 +1,17 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +import XCTest +import SystemPackage + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +final class FileOperationsTest: XCTestCase { + // TODO: Mocking based testing +} + diff --git a/Tests/SystemTests/FilePathTest.swift b/Tests/SystemTests/FilePathTest.swift new file mode 100644 index 00000000..441d9a0a --- /dev/null +++ b/Tests/SystemTests/FilePathTest.swift @@ -0,0 +1,76 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +import XCTest +import SystemPackage + +extension UnsafePointer where Pointee == UInt8 { + internal var _asCChar: UnsafePointer { + UnsafeRawPointer(self).assumingMemoryBound(to: CChar.self) + } +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +func filePathFromUnterminatedBytes(_ bytes: S) -> FilePath where S.Element == UInt8 { + var array = Array(bytes) + assert(array.last != 0, "already null terminated") + array += [0] + + return array.withUnsafeBufferPointer { + FilePath(cString: $0.baseAddress!._asCChar) + } +} +let invalidBytes: [UInt8] = [0x2F, 0x61, 0x2F, 0x62, 0x2F, 0x83] + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +final class FilePathTest: XCTestCase { + struct TestPath { + let filePath: FilePath + let string: String + let validUTF8: Bool + } + + let testPaths: [TestPath] = [ + // empty + TestPath(filePath: FilePath(), string: String(), validUTF8: true), + + // valid ascii + TestPath(filePath: "/a/b/c", string: "/a/b/c", validUTF8: true), + + // valid utf8 + TestPath(filePath: "/あ/🧟‍♀️", string: "/あ/🧟‍♀️", validUTF8: true), + + // invalid utf8 + TestPath(filePath: filePathFromUnterminatedBytes(invalidBytes), string: String(decoding: invalidBytes, as: UTF8.self), validUTF8: false), + ] + + func testFilePath() { + + XCTAssertEqual(0, FilePath().length) + + for testPath in testPaths { + + XCTAssertEqual(testPath.string, String(decoding: testPath.filePath)) + + if testPath.validUTF8 { + XCTAssertEqual(testPath.filePath, FilePath(testPath.string)) + XCTAssertEqual(testPath.string, String(validatingUTF8: testPath.filePath)) + } else { + XCTAssertNotEqual(testPath.filePath, FilePath(testPath.string)) + XCTAssertNil(String(validatingUTF8: testPath.filePath)) + } + + testPath.filePath.withCString { + XCTAssertEqual(testPath.string, String(cString: $0)) + XCTAssertEqual(testPath.filePath, FilePath(cString: $0)) + } + } + } +} + diff --git a/Tests/SystemTests/FileTypesTest.swift b/Tests/SystemTests/FileTypesTest.swift new file mode 100644 index 00000000..f6dc7f07 --- /dev/null +++ b/Tests/SystemTests/FileTypesTest.swift @@ -0,0 +1,69 @@ +/* + This source file is part of the Swift System open source project + + Copyright (c) 2020 Apple Inc. and the Swift System project authors + Licensed under Apache License v2.0 with Runtime Library Exception + + See https://swift.org/LICENSE.txt for license information +*/ + +import XCTest +import SystemPackage + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +final class FileDescriptorTest: XCTestCase { + // Test the constants match the C header values. For various reasons, + func testConstants() { + XCTAssertEqual(O_RDONLY, FileDescriptor.AccessMode.readOnly.rawValue) + XCTAssertEqual(O_WRONLY, FileDescriptor.AccessMode.writeOnly.rawValue) + XCTAssertEqual(O_RDWR, FileDescriptor.AccessMode.readWrite.rawValue) + + XCTAssertEqual(O_NONBLOCK, FileDescriptor.OpenOptions.nonBlocking.rawValue) + XCTAssertEqual(O_APPEND, FileDescriptor.OpenOptions.append.rawValue) + XCTAssertEqual(O_CREAT, FileDescriptor.OpenOptions.create.rawValue) + XCTAssertEqual(O_TRUNC, FileDescriptor.OpenOptions.truncate.rawValue) + XCTAssertEqual(O_EXCL, FileDescriptor.OpenOptions.exclusiveCreate.rawValue) + XCTAssertEqual(O_NOFOLLOW, FileDescriptor.OpenOptions.noFollow.rawValue) + XCTAssertEqual(O_CLOEXEC, FileDescriptor.OpenOptions.closeOnExec.rawValue) + + // BSD only +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssertEqual(O_SHLOCK, FileDescriptor.OpenOptions.sharedLock.rawValue) + XCTAssertEqual(O_EXLOCK, FileDescriptor.OpenOptions.exclusiveLock.rawValue) + XCTAssertEqual(O_SYMLINK, FileDescriptor.OpenOptions.symlink.rawValue) + XCTAssertEqual(O_EVTONLY, FileDescriptor.OpenOptions.eventOnly.rawValue) +#endif + + XCTAssertEqual(SEEK_SET, FileDescriptor.SeekOrigin.start.rawValue) + XCTAssertEqual(SEEK_CUR, FileDescriptor.SeekOrigin.current.rawValue) + XCTAssertEqual(SEEK_END, FileDescriptor.SeekOrigin.end.rawValue) + +#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS) + XCTAssertEqual(SEEK_HOLE, FileDescriptor.SeekOrigin.nextHole.rawValue) + XCTAssertEqual(SEEK_DATA, FileDescriptor.SeekOrigin.nextData.rawValue) +#endif + } + + // TODO: test string conversion + // TODO: test option set string conversion + +} + +// @available(macOS 10.16, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +final class FilePermissionsTest: XCTestCase { + + func testPermissions() { + // TODO: exhaustive tests + + XCTAssert(FilePermissions(rawValue: 0o664) == [.ownerReadWrite, .groupReadWrite, .otherRead]) + XCTAssert(FilePermissions(rawValue: 0o644) == [.ownerReadWrite, .groupRead, .otherRead]) + XCTAssert(FilePermissions(rawValue: 0o777) == [.otherReadWriteExecute, .groupReadWriteExecute, .ownerReadWriteExecute]) + + // From the docs for FilePermissions + do { + let perms = FilePermissions(rawValue: 0o644) + XCTAssert(perms == [.ownerReadWrite, .groupRead, .otherRead]) + XCTAssert(perms.contains(.ownerRead)) + } + } +} diff --git a/Tests/SystemTests/XCTestManifests.swift b/Tests/SystemTests/XCTestManifests.swift new file mode 100644 index 00000000..0e923a57 --- /dev/null +++ b/Tests/SystemTests/XCTestManifests.swift @@ -0,0 +1,49 @@ +#if !canImport(ObjectiveC) +import XCTest + +extension ErrnoTest { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__ErrnoTest = [ + ("testConstants", testConstants), + ("testPatternMatching", testPatternMatching), + ] +} + +extension FileDescriptorTest { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FileDescriptorTest = [ + ("testConstants", testConstants), + ] +} + +extension FilePathTest { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FilePathTest = [ + ("testFilePath", testFilePath), + ] +} + +extension FilePermissionsTest { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__FilePermissionsTest = [ + ("testPermissions", testPermissions), + ] +} + +public func __allTests() -> [XCTestCaseEntry] { + return [ + testCase(ErrnoTest.__allTests__ErrnoTest), + testCase(FileDescriptorTest.__allTests__FileDescriptorTest), + testCase(FilePathTest.__allTests__FilePathTest), + testCase(FilePermissionsTest.__allTests__FilePermissionsTest), + ] +} +#endif