Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tests #1

Merged
merged 1 commit into from
Jul 18, 2024
Merged
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
9 changes: 9 additions & 0 deletions .github/renovate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": ["config:base"],
"packageRules": [
{
"matchUpdateTypes": ["minor", "patch", "pin", "digest"],
"automerge": true
}
]
}
25 changes: 25 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Test
on:
pull_request:
branches: [main]
workflow_dispatch:

jobs:
test:
runs-on: macos-13
steps:
- uses: actions/checkout@v4
- name: Test
run: |
set -ex

brew install docker docker-compose colima
colima start --runtime docker

docker compose up --detach

prefix="./Tests/SMBClientTests/Fixtures"
chmod -R 777 ${prefix}/shares
docker cp ${prefix}/shares samba:/

swift test
3 changes: 2 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ let package = Package(
),
.testTarget(
name: "SMBClientTests",
dependencies: ["SMBClient"]
dependencies: ["SMBClient"],
resources: [.copy("Fixtures")]
),
]
)
12 changes: 12 additions & 0 deletions Sources/SMBClient/Messages/ErrorCodes.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ public enum ErrorCodes {
public static let noMoreFiles: UInt32 = 0x80000006
public static let stoppedOnSymlink: UInt32 = 0x8000002D
public static let notImplemented: UInt32 = 0xC0000002
public static let invalidInfoClass: UInt32 = 0xC0000003
public static let invalidParameter: UInt32 = 0xC000000D
public static let noSuchDevice: UInt32 = 0xC000000E
public static let noSuchFile: UInt32 = 0xC000000F
Expand All @@ -34,8 +35,11 @@ public enum ErrorCodes {
public static let fileIsADirectory: UInt32 = 0xC00000BA
public static let notSupported: UInt32 = 0xC00000BB
public static let networkNameDeleted: UInt32 = 0xC00000C9
public static let badNetworkName: UInt32 = 0xC00000CC
public static let notADirectory: UInt32 = 0xC0000103
public static let fileClosed: UInt32 = 0xC0000128
public static let userSessionDeleted: UInt32 = 0xC0000203
public static let connectionRefused: UInt32 = 0xC0000236
public static let networkSessionExpired: UInt32 = 0xC000035C
public static let smbTooManyUIDs: UInt32 = 0xC000205A

Expand Down Expand Up @@ -63,6 +67,8 @@ public enum ErrorCodes {
return "The create operation stopped after reaching a symbolic link."
case ErrorCodes.notImplemented:
return "The requested operation is not implemented."
case ErrorCodes.invalidInfoClass:
return "The specified information class is not a valid information class for the specified object."
case ErrorCodes.invalidParameter:
return "The parameter specified in the request is not valid."
case ErrorCodes.noSuchFile:
Expand Down Expand Up @@ -103,10 +109,16 @@ public enum ErrorCodes {
return "The client request is not supported."
case ErrorCodes.networkNameDeleted:
return "The network name specified by the client has been deleted on the server. This error is returned if the client specifies an incorrect TID or the share on the server represented by the TID was deleted."
case ErrorCodes.badNetworkName:
return "The specified share name cannot be found on the remote server."
case ErrorCodes.notADirectory:
return "A requested opened file is not a directory."
case ErrorCodes.fileClosed:
return "An I/O request other than close and several other special case operations was attempted using a file object that had already been closed."
case ErrorCodes.userSessionDeleted:
return "The user session specified by the client has been deleted on the server. This error is returned by the server if the client sends an incorrect UID."
case ErrorCodes.connectionRefused:
return "The transport-connection attempt was refused by the remote system."
case ErrorCodes.networkSessionExpired:
return "The client's session has expired; therefore, the client MUST re-authenticate to continue accessing remote resources."
case ErrorCodes.smbTooManyUIDs:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import Foundation

public enum FileSystemInfoClass: UInt8 {
case fileFsVolumeInformation = 0x01
case fileFsLabelInformation = 0x02
case fileFsSizeInformation = 0x03
case fileFsDeviceInformation = 0x04
case fileFsAttributeInformation = 0x05
case fileFsControlInformation = 0x06
case fileFsFullSizeInformation = 0x07
case fileFsObjectIdInformation = 0x08
case fileFsDriverPathInformation = 0x09
case fileFsVolumeFlagsInformation = 0x0A
case fileFsSectorSizeInformation = 0x0B
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import Foundation

public enum InfoType: UInt8 {
case file = 0x01
case fileSystem = 0x02
case security = 0x03
case quota = 0x04
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import Foundation

public struct SecurityDescriptor: OptionSet {
public let rawValue: UInt8

public init(rawValue: UInt8) {
self.rawValue = rawValue
}

public static let owner = SecurityDescriptor(rawValue: 0x00000001)
public static let group = SecurityDescriptor(rawValue: 0x00000002)
public static let dacl = SecurityDescriptor(rawValue: 0x00000004)
public static let sacl = SecurityDescriptor(rawValue: 0x00000008)
public static let label = SecurityDescriptor(rawValue: 0x00000010)
public static let attribute = SecurityDescriptor(rawValue: 0x00000020)
public static let scope = SecurityDescriptor(rawValue: 0x00000040)
public static let backup = SecurityDescriptor(rawValue: 0x00000080)
}
85 changes: 33 additions & 52 deletions Sources/SMBClient/Messages/QueryInfo.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

enum QueryInfo {
public enum QueryInfo {
public struct Request {
public let header: Header
public let structureSize: UInt16
Expand All @@ -15,69 +15,33 @@ enum QueryInfo {
public let fileId: Data
public let buffer: Data

public enum InfoType: UInt8 {
case file = 0x01
case fileSystem = 0x02
case security = 0x03
case quota = 0x04
}

public enum FileSystemInfoClass: UInt8 {
case fileFsVolumeInformation = 0x01
case fileFsLabelInformation = 0x02
case fileFsSizeInformation = 0x03
case fileFsDeviceInformation = 0x04
case fileFsAttributeInformation = 0x05
case fileFsControlInformation = 0x06
case fileFsFullSizeInformation = 0x07
case fileFsObjectIdInformation = 0x08
case fileFsDriverPathInformation = 0x09
case fileFsVolumeFlagsInformation = 0x0A
case fileFsSectorSizeInformation = 0x0B
}

public struct SecurityDescriptor: OptionSet {
public let rawValue: UInt8

public static let owner = SecurityDescriptor(rawValue: 0x00000001)
public static let group = SecurityDescriptor(rawValue: 0x00000002)
public static let dacl = SecurityDescriptor(rawValue: 0x00000004)
public static let sacl = SecurityDescriptor(rawValue: 0x00000008)
public static let label = SecurityDescriptor(rawValue: 0x00000010)
public static let attribute = SecurityDescriptor(rawValue: 0x00000020)
public static let scope = SecurityDescriptor(rawValue: 0x00000040)
public static let backup = SecurityDescriptor(rawValue: 0x00000080)
}

public struct Flags: OptionSet {
public let rawValue: UInt32

public init(rawValue: UInt32) {
self.rawValue = rawValue
}

public static let restartScans = Flags(rawValue: 0x00000001)
public static let returnSingleEntry = Flags(rawValue: 0x00000002)
public static let indexSpecified = Flags(rawValue: 0x00000004)
}

public init(
flags: Header.Flags = [],
headerFlags: Header.Flags = [],
messageId: UInt64,
treeId: UInt32,
sessionId: UInt64,
infoType: InfoType,
fileInfoClass: FileInfoClass,
outputBufferLength: UInt32,
inputBufferOffset: UInt16,
reserved: UInt16,
inputBufferLength: UInt32,
additionalInformation: UInt32,
flags2: Flags = [.restartScans],
fileId: Data,
buffer: Data
flags: Flags = [],
fileId: Data
) {
header = Header(
creditCharge: 1,
command: .queryInfo,
creditRequest: 64,
flags: flags,
flags: headerFlags,
nextCommand: 0,
messageId: messageId,
treeId: treeId,
Expand All @@ -87,14 +51,14 @@ enum QueryInfo {
structureSize = 41
self.infoType = infoType
self.fileInfoClass = fileInfoClass
self.outputBufferLength = outputBufferLength
self.inputBufferOffset = inputBufferOffset
self.reserved = reserved
self.inputBufferLength = inputBufferLength
self.additionalInformation = additionalInformation
self.flags = flags2
self.outputBufferLength = 1124
self.inputBufferOffset = 0
self.reserved = 0
self.inputBufferLength = 0
self.additionalInformation = 0
self.flags = flags
self.fileId = fileId
self.buffer = buffer
self.buffer = Data()
}

public func encoded() -> Data {
Expand All @@ -114,4 +78,21 @@ enum QueryInfo {
return data
}
}

public struct Response {
public let header: Header
public let structureSize: UInt16
public let outputBufferOffset: UInt16
public let outputBufferLength: UInt32
public let buffer: Data

public init(data: Data) {
let byteReader = ByteReader(data)
header = Header(data: data)
structureSize = byteReader.read()
outputBufferOffset = byteReader.read()
outputBufferLength = byteReader.read()
buffer = byteReader.read(count: Int(outputBufferLength))
}
}
}
34 changes: 0 additions & 34 deletions Sources/SMBClient/Messages/SetInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,6 @@ enum SetInfo {
public let fileId: Data
public let buffer: Data

public enum InfoType: UInt8 {
case file = 0x01
case fileSystem = 0x02
case security = 0x03
case quota = 0x04
}

public enum FileSystemInfoClass: UInt8 {
case fileFsVolumeInformation = 0x01
case fileFsLabelInformation = 0x02
case fileFsSizeInformation = 0x03
case fileFsDeviceInformation = 0x04
case fileFsAttributeInformation = 0x05
case fileFsControlInformation = 0x06
case fileFsFullSizeInformation = 0x07
case fileFsObjectIdInformation = 0x08
case fileFsDriverPathInformation = 0x09
case fileFsVolumeFlagsInformation = 0x0A
case fileFsSectorSizeInformation = 0x0B
}

public struct SecurityDescriptor: OptionSet {
public let rawValue: UInt8

public static let owner = SecurityDescriptor(rawValue: 0x00000001)
public static let group = SecurityDescriptor(rawValue: 0x00000002)
public static let dacl = SecurityDescriptor(rawValue: 0x00000004)
public static let sacl = SecurityDescriptor(rawValue: 0x00000008)
public static let label = SecurityDescriptor(rawValue: 0x00000010)
public static let attribute = SecurityDescriptor(rawValue: 0x00000020)
public static let scope = SecurityDescriptor(rawValue: 0x00000040)
public static let backup = SecurityDescriptor(rawValue: 0x00000080)
}

public struct Flags: OptionSet {
public let rawValue: UInt32

Expand Down
19 changes: 16 additions & 3 deletions Sources/SMBClient/SMBClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ public class SMBClient {
public let port: Int
public var share: String? { session.connectedTree }

public let session: Session

public var onDisconnected: (Error) -> Void {
didSet {
session.onDisconnected = onDisconnected
}
}

private let session: Session

public init(host: String) {
self.host = host
port = 445
Expand Down Expand Up @@ -86,7 +86,20 @@ public class SMBClient {
try await session.deleteFile(path: path)
}

public func download(content: Data, path: String) async throws -> Data {
public func fileStat(path: String) async throws -> FileStat {
let response = try await session.fileStat(path: path)
return FileStat(response)
}

public func existFile(path: String) async throws -> Bool {
try await session.existFile(path: path)
}

public func existDirectory(path: String) async throws -> Bool {
try await session.existDirectory(path: path)
}

public func download(path: String) async throws -> Data {
let fileReader = fileReader(path: path)

let data = try await fileReader.download()
Expand Down
Loading
Loading