Skip to content

Commit

Permalink
Document file permissions (#9)
Browse files Browse the repository at this point in the history
* Document file permissions

Rename it back to `FilePermission` since "file mode" is a superset of
information.
  • Loading branch information
dduan authored Sep 22, 2018
1 parent c0221ba commit 10edfd4
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 79 deletions.
56 changes: 0 additions & 56 deletions Sources/Pathos/FileMode.swift

This file was deleted.

80 changes: 80 additions & 0 deletions Sources/Pathos/FilePermission.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#if os(Linux)
import Glibc
#else
import Darwin
#endif

/// Represents the POSIX file permisssion bits. These bits determines read/write/execution access to a file as
/// well as some miscelleneous information.
public struct FilePermission: OptionSet {
/// The file permission as the native `mode_t` type. A de-abstraction to help interact with POSIX APIs directly.
public let rawValue: mode_t

/// Creates a `FilePermission` from native `mode_t`.
public init(rawValue: mode_t) {
self.rawValue = rawValue & 0o7777
}

/// This is equivalent to `[.ownerRead, .ownerWrite, .ownerExecute]` (`S_IRWXU`).
public static let ownerAll = FilePermission(rawValue: S_IRWXU)

/// Read permission bit for the owner of the file (`S_IRUSR`).
public static let ownerRead = FilePermission(rawValue: S_IRUSR)

/// Write permission bit for the owner of the file (`S_IWUSR`).
public static let ownerWrite = FilePermission(rawValue: S_IWUSR)

/// Execute (for ordinary files) or search (for directories) permission bit for the owner of the file (`S_IXUSR`).
public static let ownerExecute = FilePermission(rawValue: S_IXUSR)

/// This is equivalent to `[.groupRead, .groupWrite, .groupExecute]` (`S_IRWXG`).
public static let groupAll = FilePermission(rawValue: S_IRWXG)

/// Read permission bit for the group owner of the file (`S_IRGRP`).
public static let groupRead = FilePermission(rawValue: S_IRGRP)

/// Write permission bit for the group owner of the file (`S_IWGRP`).
public static let groupWrite = FilePermission(rawValue: S_IWGRP)

/// Execute or search permission bit for the group owner of the file (`S_IXGRP`).
public static let groupExecute = FilePermission(rawValue: S_IXGRP)

/// This is equivalent to `[.otherRead, .otherWrite, .otherExecute]` (`S_IRWXO`).
public static let otherAll = FilePermission(rawValue: S_IRWXO)

/// Read permission bit for other users (`S_IROTH`).
public static let otherRead = FilePermission(rawValue: S_IROTH)

/// Write permission bit for other users (`S_IWOTH`).
public static let otherWrite = FilePermission(rawValue: S_IWOTH)

/// Execute or search permission bit for other users (`S_IXOTH`).
public static let otherExecute = FilePermission(rawValue: S_IXOTH)

/// This is the set-user-ID on execute bit.
/// See [Process Persona](http://www.gnu.org/software/libc/manual/html_node/Process-Persona.html#Process-Persona)
/// to learm more.
public static let setUserIDOnExecution = FilePermission(rawValue: S_ISUID) /* set user id on execution */

/// This is the set-group-ID on execute bit
/// See [Process Persona](http://www.gnu.org/software/libc/manual/html_node/Process-Persona.html#Process-Persona)
/// to learm more.
public static let setGroupIDOnExecution = FilePermission(rawValue: S_ISGID) /* set group id on execution */

/// This is the sticky bit.
///
/// For a directory it gives permission to delete a file in that directory only if you own that file.
/// Ordinarily, a user can either delete all the files in a directory or cannot delete any of them (based on
/// whether the user has write permission for the directory). The same restriction applies—you must have
/// both write permission for the directory and own the file you want to delete. The one exception is that
/// the owner of the directory can delete any file in the directory, no matter who owns it (provided the
/// owner has given himself write permission for the directory). This is commonly used for the /tmp
/// directory, where anyone may create files but not delete files created by other users.
public static let saveSwappedTextAfterUser = FilePermission(rawValue: S_ISVTX) /* save swapped text even after use */
}

extension FilePermission: ExpressibleByIntegerLiteral {
public init(integerLiteral: UInt16) {
self.rawValue = mode_t(integerLiteral)
}
}
12 changes: 6 additions & 6 deletions Sources/Pathos/manipulate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ import Darwin
///
/// - Parameters:
/// - path: The path at witch the directory is to be created.
/// - mode: Access flags (combined with the proccess's `umask`) for the directory to be created.
/// - permissions: Access flags (combined with the proccess's `umask`) for the directory to be created.
/// - createParents: If `true`, any missing parents of this path are created as needed; they are created
/// with the default permissions without taking mode into account (mimicking the POSIX
/// `mkdir -p` command). If `false`, a missing parent will cause a `SystemError`. Defaults
/// to `false`
/// - existOkay: If `false`, a `SystemError` is thrown if the target directory (or any of it's parent, if it
/// needs creation) already exists.
/// - Throws: A `SystemError`.
public func makeDirectory(atPath path: String, mode: FileMode = 0o0755, createParents: Bool = false, existOkay: Bool = false) throws {
public func makeDirectory(atPath path: String, permission: FilePermission = 0o0755, createParents: Bool = false, existOkay: Bool = false) throws {
func _makeDirectory() throws {
if mkdir(path, mode.rawValue) != 0 {
if mkdir(path, permission.rawValue) != 0 {
// Cannot rely on checking for EEXIST, since the operating system
// could give priority to other errors like EACCES or EROFS
let error = errno
Expand Down Expand Up @@ -83,7 +83,7 @@ extension PathRepresentable {
/// Create a directory at `pathString`.
///
/// - Parameters:
/// - mode: Access flags (combined with the proccess's `umask`) for the directory to be created.
/// - permission: Access flags (combined with the proccess's `umask`) for the directory to be created.
/// - createParents: If `true`, any missing parents of this path are created as needed; they are created
/// with the default permissions without taking mode into account (mimicking the POSIX
/// `mkdir -p` command). If `false`, a missing parent will cause a `SystemError`.
Expand All @@ -92,9 +92,9 @@ extension PathRepresentable {
/// if it needs creation) already exists.
/// - Returns: `true` if a directory is created, `false` if an error occured and the directory was not
/// created.
public func makeDirectory(createParents: Bool = false, mode: FileMode = 0o0755, existOkay: Bool = false) -> Bool {
public func makeDirectory(createParents: Bool = false, permission: FilePermission = 0o0755, existOkay: Bool = false) -> Bool {
do {
try makeDirectory(atPath:mode:createParents:existOkay:)(self.pathString, mode, createParents, existOkay)
try makeDirectory(atPath:permission:createParents:existOkay:)(self.pathString, permission, createParents, existOkay)
} catch {
return false
}
Expand Down
12 changes: 6 additions & 6 deletions Sources/Pathos/permissions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,28 +6,28 @@ import Darwin

// TODO: missing unit tests.
// TODO: missing docstring.
public func permissions(forPath path: String) throws -> FileMode {
public func permissions(forPath path: String) throws -> FilePermission {
var status = stat()
if stat(path, &status) != 0 {
throw SystemError(posixErrorCode: errno)
}
return FileMode(rawValue: status.st_mode)
return FilePermission(rawValue: status.st_mode)
}

// TODO: missing unit tests.
// TODO: missing docstring.
public func setPermissions(forPath path: String, _ mode: FileMode) throws {
if chmod(path, mode.rawValue) != 0 {
public func setPermissions(forPath path: String, _ permission: FilePermission) throws {
if chmod(path, permission.rawValue) != 0 {
throw SystemError(posixErrorCode: errno)
}
}

extension PathRepresentable {
// TODO: missing unit tests.
// TODO: missing docstring.
public var permissions: FileMode {
public var permissions: FilePermission {
get {
return (try? permissions(forPath:)(self.pathString)) ?? FileMode(rawValue: 0)
return (try? permissions(forPath:)(self.pathString)) ?? FilePermission(rawValue: 0)
}
set {
try? setPermissions(forPath:_:)(self.pathString, newValue)
Expand Down
22 changes: 11 additions & 11 deletions Sources/Pathos/readWrite.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import Glibc
import Darwin
#endif

func _writeAtPath(_ path: String, bytes: UnsafeRawPointer, byteCount: Int, createIfNecessary: Bool, mode: FileMode?) throws {
func _writeAtPath(_ path: String, bytes: UnsafeRawPointer, byteCount: Int, createIfNecessary: Bool, permission: FilePermission?) throws {
let oflag = createIfNecessary ? O_WRONLY | O_CREAT : O_WRONLY
let fd: Int32
if let mode = mode {
fd = open(path, oflag, mode.rawValue)
if let permission = permission {
fd = open(path, oflag, permission.rawValue)
} else {
fd = open(path, oflag)
}
Expand Down Expand Up @@ -50,16 +50,16 @@ public func readString(atPath path: String) throws -> String {

// TODO: missing unit tests.
// TODO: missing docstring.
public func writeBytes<Bytes>(atPath path: String, _ bytes: Bytes, createIfNecessary: Bool = true, mode: FileMode? = nil) throws where Bytes: Collection, Bytes.Element == UInt8 {
public func writeBytes<Bytes>(atPath path: String, _ bytes: Bytes, createIfNecessary: Bool = true, permission: FilePermission? = nil) throws where Bytes: Collection, Bytes.Element == UInt8 {
let buffer = [UInt8](bytes)
try _writeAtPath(path, bytes: buffer, byteCount: buffer.count, createIfNecessary: createIfNecessary, mode: mode)
try _writeAtPath(path, bytes: buffer, byteCount: buffer.count, createIfNecessary: createIfNecessary, permission: permission)
}

// TODO: missing unit tests.
// TODO: missing docstring.
public func writeString(atPath path: String, _ string: String, createIfNecessary: Bool = true, mode: FileMode? = nil) throws {
public func writeString(atPath path: String, _ string: String, createIfNecessary: Bool = true, permission: FilePermission? = nil) throws {
try string.utf8CString.withUnsafeBytes { bytes in
try _writeAtPath(path, bytes: bytes.baseAddress!, byteCount: bytes.count, createIfNecessary: createIfNecessary, mode: mode)
try _writeAtPath(path, bytes: bytes.baseAddress!, byteCount: bytes.count, createIfNecessary: createIfNecessary, permission: permission)
}
}

Expand All @@ -76,9 +76,9 @@ extension PathRepresentable {

// TODO: missing unit tests.
// TODO: missing docstring.
public func writeBytes<Bytes>(bytes: Bytes, createIfNecessary: Bool = true, mode: FileMode? = nil) -> Bool where Bytes: Collection, Bytes.Element == UInt8 {
public func writeBytes<Bytes>(bytes: Bytes, createIfNecessary: Bool = true, permission: FilePermission? = nil) -> Bool where Bytes: Collection, Bytes.Element == UInt8 {
do {
try writeBytes(atPath:_:createIfNecessary:mode:)(self.pathString, bytes, createIfNecessary, mode)
try writeBytes(atPath:_:createIfNecessary:permission:)(self.pathString, bytes, createIfNecessary, permission)
} catch {
return false
}
Expand All @@ -87,9 +87,9 @@ extension PathRepresentable {

// TODO: missing unit tests.
// TODO: missing docstring.
public func writeString(string: String, createIfNecessary: Bool = true, mode: FileMode? = nil) -> Bool {
public func writeString(string: String, createIfNecessary: Bool = true, permission: FilePermission? = nil) -> Bool {
do {
try writeString(atPath:_:createIfNecessary:mode:)(self.pathString, string, createIfNecessary, mode)
try writeString(atPath:_:createIfNecessary:permission:)(self.pathString, string, createIfNecessary, permission)
} catch {
return false
}
Expand Down

0 comments on commit 10edfd4

Please sign in to comment.