Repository: apple/swift-nio
File: Sources/NIOPosix/NonBlockingFileIO.swift (Swift)

Code before:
    ///
    /// - parameters:
    ///     - path: The path of the link.
    ///     - destination: Target path where this link will point to.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func symlink(path: String, to destination: String, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            try Posix.symlink(pathname: path, destination: destination)
        }
    }

    /// Returns target of the symbolic link at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the link to read.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` containing link target.
    public func readlink(path: String, eventLoop: EventLoop) -> EventLoopFuture<String> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            let maxLength = Int(PATH_MAX)
            let pointer = UnsafeMutablePointer<CChar>.allocate(capacity: maxLength)
            defer {
                pointer.deallocate()
            }
            try Posix.readlink(pathname: path, outPath: pointer, outPathSize: maxLength)
            return String(cString: pointer)
        }
    }

    /// Removes symbolic link at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the link to remove.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func unlink(path: String, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            try Posix.unlink(pathname: path)
        }
    }

    /// Creates directory at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the directory to be created.
    ///     - withIntermediateDirectories: Whether intermediate directories should be created.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func createDirectory(path: String, withIntermediateDirectories createIntermediates: Bool, mode: NIOPOSIXFileMode, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {

Review comment (suggestion):
I don't think this is right. Per the manual page:

> readlink() places the contents of the symbolic link path in the buffer buf, which has size bufsize.  Readlink does not append a NUL character to buf.

We need the return value, and then we can use the buffer pointer API to initialise the `String` using `String(decoding:as:)`.

Code after:
    ///
    /// - parameters:
    ///     - path: The path of the link.
    ///     - destination: Target path where this link will point to.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func symlink(path: String, to destination: String, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            try Posix.symlink(pathname: path, destination: destination)
        }
    }

    /// Returns target of the symbolic link at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the link to read.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` containing link target.
    public func readlink(path: String, eventLoop: EventLoop) -> EventLoopFuture<String> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            let maxLength = Int(PATH_MAX)
            let pointer = UnsafeMutableBufferPointer<CChar>.allocate(capacity: maxLength)
            defer {
                pointer.deallocate()
            }
            let length = try Posix.readlink(pathname: path, outPath: pointer.baseAddress!, outPathSize: maxLength)
            return String(decoding: UnsafeRawBufferPointer(pointer).prefix(length), as: UTF8.self)
        }
    }

    /// Removes symbolic link at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the link to remove.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` which is fulfilled if the rename was successful or fails on error.
    public func unlink(path: String, eventLoop: EventLoop) -> EventLoopFuture<Void> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            try Posix.unlink(pathname: path)
        }
    }

    /// List contents of the directory at `path` on a private thread pool which is separate from any `EventLoop` thread.
    ///
    /// - parameters:
    ///     - path: The path of the directory to list the content of.
    ///     - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire.
    /// - returns: An `EventLoopFuture` containing the directory entries.
    public func listDirectory(path: String, eventLoop: EventLoop) -> EventLoopFuture<[NIODirectoryEntry]> {
        return self.threadPool.runIfActive(eventLoop: eventLoop) {
            let dir = try Posix.opendir(pathname: path)