Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ let swiftSystem: PackageDescription.Target.Dependency = .product(name: "SystemPa

// These platforms require a dependency on `NIOPosix` from `NIOHTTP1` to maintain backward
// compatibility with previous NIO versions.
let historicalNIOPosixDependencyRequired: [Platform] = [.macOS, .iOS, .tvOS, .watchOS, .linux, .android]
let historicalNIOPosixDependencyRequired: [Platform] = [.macOS, .iOS, .tvOS, .watchOS, .linux, .android, .custom("freebsd"), .custom("FreeBSD")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These platforms do not require a historical NIOPosix dependency, as they did not compile before.


let swiftSettings: [SwiftSetting] = []

Expand Down Expand Up @@ -60,6 +60,7 @@ let package = Package(
"NIOConcurrencyHelpers",
"_NIOBase64",
"CNIODarwin",
"CNIOFreeBSD",
"CNIOLinux",
"CNIOWindows",
"CNIOWASI",
Expand Down Expand Up @@ -93,6 +94,7 @@ let package = Package(
dependencies: [
"CNIOLinux",
"CNIODarwin",
"CNIOFreeBSD",
"CNIOWindows",
"NIOConcurrencyHelpers",
"NIOCore",
Expand Down Expand Up @@ -161,6 +163,10 @@ let package = Package(
.define("__APPLE_USE_RFC_3542")
]
),
.target(
name: "CNIOFreeBSD",
dependencies: []
),
.target(
name: "CNIOWindows",
dependencies: []
Expand Down Expand Up @@ -260,6 +266,7 @@ let package = Package(
"NIOPosix",
"CNIOLinux",
"CNIODarwin",
"CNIOFreeBSD",
swiftAtomics,
swiftCollections,
swiftSystem,
Expand Down Expand Up @@ -484,6 +491,7 @@ let package = Package(
"NIOEmbedded",
"CNIOLinux",
"CNIODarwin",
"CNIOFreeBSD",
"NIOTLS",
],
swiftSettings: swiftSettings
Expand Down
51 changes: 51 additions & 0 deletions Sources/CNIOFreeBSD/include/CNIOFreeBSD.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Copyright (c) 2025 Apple Inc. and the SwiftNIO project authors

// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifndef C_NIO_FREEBSD_H
#define C_NIO_FREEBSD_H

#ifdef __FreeBSD__
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <time.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fts.h>
#include <net/if_dl.h>

extern const int CNIOFreeBSD_AT_EMPTY_PATH;

extern const int CNIOFreeBSD_IPTOS_ECN_NOTECT;
extern const int CNIOFreeBSD_IPTOS_ECN_MASK;
extern const int CNIOFreeBSD_IPTOS_ECN_ECT0;
extern const int CNIOFreeBSD_IPTOS_ECN_ECT1;
extern const int CNIOFreeBSD_IPTOS_ECN_CE;
extern const int CNIOFreeBSD_IPV6_RECVPKTINFO;
extern const int CNIOFreeBSD_IPV6_PKTINFO;

int CNIOFreeBSD_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags);
int CNIOFreeBSD_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout);

// cmsghdr handling
struct cmsghdr *CNIOFreeBSD_CMSG_FIRSTHDR(const struct msghdr *);
struct cmsghdr *CNIOFreeBSD_CMSG_NXTHDR(const struct msghdr *, const struct cmsghdr *);
const void *CNIOFreeBSD_CMSG_DATA(const struct cmsghdr *);
void *CNIOFreeBSD_CMSG_DATA_MUTABLE(struct cmsghdr *);
size_t CNIOFreeBSD_CMSG_LEN(size_t);
size_t CNIOFreeBSD_CMSG_SPACE(size_t);

const char* CNIOFreeBSD_dirent_dname(struct dirent* ent);

#endif // __FreeBSD__
#endif // C_NIO_FREEBSD_H
79 changes: 79 additions & 0 deletions Sources/CNIOFreeBSD/shim.c

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I erred on calling this just CNIOBSD in #3394. Would it be better to do the same here, so that we don't need to have one CNIO.* module per operating system? Or should I go and rename CNIOBSD to CNIOOpenBSD there?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the two have similar-enough libcs?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite similar, especially due to pedigree, but there is definitely divergence, such as functionality in FreeBSD that OpenBSD does not have.

Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the SwiftNIO open source project
//
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Copyright (c) 2017-2018 Apple Inc. and the SwiftNIO project authors
// Copyright (c) 2025 Apple Inc. and the SwiftNIO project authors

elsewhere too

// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of SwiftNIO project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//
#ifdef __FreeBSD__
#include <CNIOFreeBSD.h>
#include <err.h>
#include <sysexits.h>
#include <stdlib.h>
#include <limits.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#include <arpa/inet.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <sys/uio.h>
#include "dirent.h"

int CNIOFreeBSD_sendmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags) {
return sendmmsg(sockfd, msgvec, vlen, flags);
}

int CNIOFreeBSD_recvmmsg(int sockfd, struct mmsghdr *msgvec, unsigned int vlen, int flags, struct timespec *timeout) {
return recvmmsg(sockfd, msgvec, vlen, flags, timeout);
}

struct cmsghdr *CNIOFreeBSD_CMSG_FIRSTHDR(const struct msghdr *mhdr) {
assert(mhdr != NULL);
return CMSG_FIRSTHDR(mhdr);
}

struct cmsghdr *CNIOFreeBSD_CMSG_NXTHDR(const struct msghdr *mhdr, const struct cmsghdr *cmsg) {
assert(mhdr != NULL);
assert(cmsg != NULL); // Not required by Darwin but Linux needs this so we should match.
return CMSG_NXTHDR(mhdr, cmsg);
}

const void *CNIOFreeBSD_CMSG_DATA(const struct cmsghdr *cmsg) {
assert(cmsg != NULL);
return CMSG_DATA(cmsg);
}

void *CNIOFreeBSD_CMSG_DATA_MUTABLE(struct cmsghdr *cmsg) {
assert(cmsg != NULL);
return CMSG_DATA(cmsg);
}

size_t CNIOFreeBSD_CMSG_LEN(size_t payloadSizeBytes) {
return CMSG_LEN(payloadSizeBytes);
}

size_t CNIOFreeBSD_CMSG_SPACE(size_t payloadSizeBytes) {
return CMSG_SPACE(payloadSizeBytes);
}

const int CNIOFreeBSD_IPTOS_ECN_NOTECT = IPTOS_ECN_NOTECT;
const int CNIOFreeBSD_IPTOS_ECN_MASK = IPTOS_ECN_MASK;
const int CNIOFreeBSD_IPTOS_ECN_ECT0 = IPTOS_ECN_ECT0;
const int CNIOFreeBSD_IPTOS_ECN_ECT1 = IPTOS_ECN_ECT1;
const int CNIOFreeBSD_IPTOS_ECN_CE = IPTOS_ECN_CE;
const int CNIOFreeBSD_IPV6_RECVPKTINFO = IPV6_RECVPKTINFO;
const int CNIOFreeBSD_IPV6_PKTINFO = IPV6_PKTINFO;
const int CNIOFreeBSD_AT_EMPTY_PATH = AT_EMPTY_PATH;

const char* CNIOFreeBSD_dirent_dname(struct dirent* ent) {
return ent->d_name;
}

#endif // __APPLE__
2 changes: 1 addition & 1 deletion Sources/CNIOSHA1/c_nio_sha1.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
#endif
#ifdef __ANDROID__
#include <sys/endian.h>
#elif defined(__linux__) || defined(__APPLE__) || defined(__wasm32__)
#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__wasm32__)
#include <sys/types.h>
#elif defined(_WIN32) || defined(_WIN64)
#ifndef LITTLE_ENDIAN
Expand Down
11 changes: 11 additions & 0 deletions Sources/NIOConcurrencyHelpers/NIOLock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import wasi_pthread
#if os(Windows)
@usableFromInline
typealias LockPrimitive = SRWLOCK
#elseif os(FreeBSD) || os(OpenBSD)
@usableFromInline
typealias LockPrimitive = pthread_mutex_t?
#else
@usableFromInline
typealias LockPrimitive = pthread_mutex_t
Expand All @@ -51,10 +54,18 @@ extension LockOperations {
#if os(Windows)
InitializeSRWLock(mutex)
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
#if os(FreeBSD)
var attr: pthread_mutexattr_t? = nil
#else
var attr = pthread_mutexattr_t()
#endif
pthread_mutexattr_init(&attr)
debugOnly {
#if os(FreeBSD)
pthread_mutexattr_settype(&attr, Int32(PTHREAD_MUTEX_ERRORCHECK.rawValue))
#else
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
#endif
}

let err = pthread_mutex_init(mutex, &attr)
Expand Down
14 changes: 14 additions & 0 deletions Sources/NIOConcurrencyHelpers/lock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ public final class Lock {
#if os(Windows)
fileprivate let mutex: UnsafeMutablePointer<SRWLOCK> =
UnsafeMutablePointer.allocate(capacity: 1)
#elseif os(FreeBSD) || os(OpenBSD)
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t?> =
UnsafeMutablePointer.allocate(capacity: 1)
#else
fileprivate let mutex: UnsafeMutablePointer<pthread_mutex_t> =
UnsafeMutablePointer.allocate(capacity: 1)
Expand All @@ -53,10 +56,18 @@ public final class Lock {
#if os(Windows)
InitializeSRWLock(self.mutex)
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
#if os(FreeBSD)
var attr: pthread_mutexattr_t? = nil
#else
var attr = pthread_mutexattr_t()
#endif
pthread_mutexattr_init(&attr)
debugOnly {
#if os(FreeBSD)
pthread_mutexattr_settype(&attr, Int32(PTHREAD_MUTEX_ERRORCHECK.rawValue))
#else
pthread_mutexattr_settype(&attr, .init(PTHREAD_MUTEX_ERRORCHECK))
#endif
}

let err = pthread_mutex_init(self.mutex, &attr)
Expand Down Expand Up @@ -134,6 +145,9 @@ public final class ConditionLock<T: Equatable> {
#if os(Windows)
private let cond: UnsafeMutablePointer<CONDITION_VARIABLE> =
UnsafeMutablePointer.allocate(capacity: 1)
#elseif (os(FreeBSD) || os(OpenBSD)) && ((compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded)))
private let cond: UnsafeMutablePointer<pthread_cond_t?> =
UnsafeMutablePointer.allocate(capacity: 1)
#elseif (compiler(<6.1) && !os(WASI)) || (compiler(>=6.1) && _runtime(_multithreaded))
private let cond: UnsafeMutablePointer<pthread_cond_t> =
UnsafeMutablePointer.allocate(capacity: 1)
Expand Down
7 changes: 7 additions & 0 deletions Sources/NIOCore/BSDSocketAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ private let sysInet_ntop:
@convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? =
inet_ntop
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = inet_pton
#elseif os(FreeBSD)
import Glibc

private let sysInet_ntop:
@convention(c) (CInt, UnsafeRawPointer?, UnsafeMutablePointer<CChar>?, socklen_t) -> UnsafePointer<CChar>? =
__inet_ntop
private let sysInet_pton: @convention(c) (CInt, UnsafePointer<CChar>?, UnsafeMutableRawPointer?) -> CInt = __inet_pton
#else
#error("The BSD Socket module was unable to identify your C library.")
#endif
Expand Down
4 changes: 2 additions & 2 deletions Sources/NIOCore/Interfaces.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ extension ifaddrs {
fileprivate var dstaddr: UnsafeMutablePointer<sockaddr>? {
#if os(Linux) || os(Android)
return self.ifa_ifu.ifu_dstaddr
#elseif canImport(Darwin)
#elseif canImport(Darwin) || os(FreeBSD)
return self.ifa_dstaddr
#endif
}

fileprivate var broadaddr: UnsafeMutablePointer<sockaddr>? {
#if os(Linux) || os(Android)
return self.ifa_ifu.ifu_broadaddr
#elseif canImport(Darwin)
#elseif canImport(Darwin) || os(FreeBSD)
return self.ifa_dstaddr
#endif
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/NIOCore/SocketOptionProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ import CNIOLinux
import WinSDK
#elseif canImport(WASILibc)
@preconcurrency import WASILibc
#elseif os(FreeBSD)
import Glibc
#else
#error("The Socket Option provider module was unable to identify your C library.")
#endif
Expand Down
7 changes: 7 additions & 0 deletions Sources/NIOFS/DirectoryEntries.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//

import CNIODarwin
import CNIOFreeBSD
import CNIOLinux
import NIOConcurrencyHelpers
import NIOCore
Expand Down Expand Up @@ -556,6 +557,8 @@ private struct DirectoryEnumerator: Sendable {
// Empty is checked for above, root can't exist within a directory, and directory
// items must be a single path component.
name = FilePath.Component(platformString: CNIODarwin_dirent_dname(entry))!
#elseif os(FreeBSD)
name = FilePath.Component(platformString: CNIOFreeBSD_dirent_dname(entry))!
#else
name = FilePath.Component(platformString: CNIOLinux_dirent_dname(entry))!
#endif
Expand Down Expand Up @@ -598,7 +601,11 @@ private struct DirectoryEnumerator: Sendable {
while entries.count < count {
switch Libc.ftsRead(fts) {
case .success(.some(let entry)):
#if os(FreeBSD)
let info = FTSInfo(rawValue: UInt16(entry.pointee.fts_info))
#else
let info = FTSInfo(rawValue: entry.pointee.fts_info)
#endif
switch info {
case .directoryPreOrder:
let entry = DirectoryEntry(path: NIOFilePath(entry.path), type: .directory)!
Expand Down
4 changes: 3 additions & 1 deletion Sources/NIOFS/FileInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import SystemPackage

#if canImport(Darwin)
import Darwin
#elseif os(FreeBSD)
import Glibc
#elseif canImport(Glibc)
@preconcurrency import Glibc
import CNIOLinux
Expand Down Expand Up @@ -149,7 +151,7 @@ extension FileInfo {

/// A time interval consisting of whole seconds and nanoseconds.
public struct Timespec: Hashable, Sendable {
#if canImport(Darwin)
#if canImport(Darwin) || os(FreeBSD)
private static let utimeOmit = Int(UTIME_OMIT)
private static let utimeNow = Int(UTIME_NOW)
#elseif canImport(Glibc) || canImport(Musl) || canImport(Android)
Expand Down
21 changes: 20 additions & 1 deletion Sources/NIOFS/FileSystem.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1284,6 +1284,25 @@ extension FileSystem {
while offset < sourceInfo.size {
// sendfile(2) limits writes to 0x7ffff000 in size
let size = min(Int(sourceInfo.size) - offset, 0x7fff_f000)
#if os(FreeBSD)
var _offset: Int? = offset
var _dOffset: Int? = nil
let result = Syscall.copy_file_range(
from: sourceFD,
offset: &_offset,
to: destinationFD,
destOffset: &_dOffset,
size: size,
flags: 0
).mapError { errno in
FileSystemError.copy_file_range(
errno: errno,
from: sourcePath,
to: destinationPath,
location: .here()
)
}
#else
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we factor out this split to a helper function?

let result = Syscall.sendfile(
to: destinationFD,
from: sourceFD,
Expand All @@ -1297,7 +1316,7 @@ extension FileSystem {
location: .here()
)
}

#endif
switch result {
case let .success(bytesCopied):
offset += bytesCopied
Expand Down
Loading
Loading