From 93b8807c5d2d1a31dacf524d00adbc3e6236e286 Mon Sep 17 00:00:00 2001 From: Artem Redkin Date: Mon, 29 Apr 2019 16:17:14 +0100 Subject: [PATCH] Add ability to open a file for writing (#975) * Add ability to open a file for writing Motivation: Users can use NonBlockingFileIO for reading files, but if one wants to write a file, there is no way to achieve that, apart from opening file manually. I think having to inits to open files for reading and writing would be convinient. Modifications: Allow to open files for writing. Result: Users can now open files for writing and reading in the same way. --- Sources/NIO/FileHandle.swift | 51 +++++++++++++++++++++++++++-- Sources/NIO/NonBlockingFileIO.swift | 21 ++++++++++-- 2 files changed, 67 insertions(+), 5 deletions(-) diff --git a/Sources/NIO/FileHandle.swift b/Sources/NIO/FileHandle.swift index 5064474b..8cd41423 100644 --- a/Sources/NIO/FileHandle.swift +++ b/Sources/NIO/FileHandle.swift @@ -81,12 +81,57 @@ public final class NIOFileHandle: FileDescriptor { } extension NIOFileHandle { + /// `Mode` represents file access modes. + public struct Mode: OptionSet { + public let rawValue: UInt8 + + public init(rawValue: UInt8) { + self.rawValue = rawValue + } + + internal var posixFlags: CInt { + switch self { + case [.read, .write]: + return O_RDWR + case .read: + return O_RDONLY + case .write: + return O_WRONLY + default: + preconditionFailure("Unsupported mode value") + } + } + + /// Opens file for reading + public static let read = Mode(rawValue: 1 << 0) + /// Opens file for writing + public static let write = Mode(rawValue: 1 << 1) + } + + /// `Flags` allows to specify additional flags to `Mode`, such as permission for file creation. + public struct Flags { + internal var posixMode: mode_t + internal var posixFlags: CInt + + public static let `default` = Flags(posixMode: 0, posixFlags: 0) + + /// Allows file creation when opening file for writing. File owner is set to the effective user ID of the process. + /// + /// - parameters: + /// - posixMode: `file mode` applied when file is created. Default permissions are: read and write for file owner, read for owners group and others. + public static func allowFileCreation(posixMode: mode_t = S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH) -> Flags { + return Flags(posixMode: posixMode, posixFlags: O_CREAT) + } + } + /// Open a new `NIOFileHandle`. /// /// - parameters: - /// - path: the path of the file to open. The ownership of the file descriptor is transferred to this `NIOFileHandle` and so it will be closed once `close` is called. - public convenience init(path: String) throws { - let fd = try Posix.open(file: path, oFlag: O_RDONLY | O_CLOEXEC) + /// - path: The path of the file to open. The ownership of the file descriptor is transferred to this `NIOFileHandle` and so it will be closed once `close` is called. + /// - mode: Access mode. Default mode is `.read`. + /// - flags: Additional POSIX flags. + public convenience init(path: String, mode: Mode = .read, flags: Flags = .default) throws { + let fd = try Posix.open(file: path, oFlag: mode.posixFlags | O_CLOEXEC | flags.posixFlags, mode: flags.posixMode) self.init(descriptor: fd) } } diff --git a/Sources/NIO/NonBlockingFileIO.swift b/Sources/NIO/NonBlockingFileIO.swift index af865052..cf19ae97 100644 --- a/Sources/NIO/NonBlockingFileIO.swift +++ b/Sources/NIO/NonBlockingFileIO.swift @@ -253,7 +253,7 @@ public struct NonBlockingFileIO { } } - /// Open the file at `path` on a private thread pool which is separate from any `EventLoop` thread. + /// Open the file at `path` for reading on a private thread pool which is separate from any `EventLoop` thread. /// /// This function will return (a future) of the `NIOFileHandle` associated with the file opened and a `FileRegion` /// comprising of the whole file. The caller must close the returned `NIOFileHandle` when it's no longer needed. @@ -261,7 +261,7 @@ public struct NonBlockingFileIO { /// - note: The reason this returns the `NIOFileHandle` and the `FileRegion` is that both the opening of a file as well as the querying of its size are blocking. /// /// - parameters: - /// - path: The path of the file to be opened. + /// - path: The path of the file to be opened for reading. /// - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire. /// - returns: An `EventLoopFuture` containing the `NIOFileHandle` and the `FileRegion` comprising the whole file. public func openFile(path: String, eventLoop: EventLoop) -> EventLoopFuture<(NIOFileHandle, FileRegion)> { @@ -277,4 +277,21 @@ public struct NonBlockingFileIO { } } + /// Open the file at `path` with specified access mode and POSIX flags on a private thread pool which is separate from any `EventLoop` thread. + /// + /// This function will return (a future) of the `NIOFileHandle` associated with the file opened. + /// The caller must close the returned `NIOFileHandle` when it's no longer needed. + /// + /// - parameters: + /// - path: The path of the file to be opened for writing. + /// - mode: File access mode. + /// - flags: Additional POSIX flags. + /// - eventLoop: The `EventLoop` on which the returned `EventLoopFuture` will fire. + /// - returns: An `EventLoopFuture` containing the `NIOFileHandle` and the `FileRegion` comprising the whole file. + public func openFile(path: String, mode: NIOFileHandle.Mode, flags: NIOFileHandle.Flags = .default, eventLoop: EventLoop) -> EventLoopFuture { + return self.threadPool.runIfActive(eventLoop: eventLoop) { + return try NIOFileHandle(path: path, mode: mode, flags: flags) + } + } + }