API Reference
SmbClient
SmbClient is the entry point for all SMB operations. You obtain an
instance by calling the connect() factory function, which negotiates
the protocol, authenticates, and returns a connected client. SmbClient
implements Closeable and should be closed when you are finished to
release the underlying TCP connection.
connect()
Suspend function that establishes an authenticated SMB session with the remote host.
suspend fun SmbClient.Companion.connect(
host: String,
credentials: SmbCredentials,
port: Int = 445,
config: SmbConfig = SmbConfig()
): SmbClient | Parameter | Type | Default | Description |
|---|---|---|---|
host | String | — | Hostname or IP address of the SMB server |
credentials | SmbCredentials | — | Authentication credentials (e.g. NtlmCredentials) |
port | Int | 445 | TCP port for the SMB service |
config | SmbConfig | SmbConfig() | Optional configuration for encryption, chunking, licensing, etc. |
Throws:
SmbLicenseException— license is missing, expired, or invalidSmbConnectionException— TCP connection or protocol negotiation failedSmbAuthenticationException— credentials were rejected by the server
connectShare()
Connects to a named share on the server and returns an SmbShare for
performing file operations.
suspend fun connectShare(shareName: String): SmbShare | Parameter | Type | Description |
|---|---|---|
shareName | String | Name of the share to connect to (e.g. "Documents") |
close()
Closes the SMB session and releases the underlying TCP connection. Safe to call multiple times.
fun close() Example
import com.ctreesoft.smb.SmbClient
import com.ctreesoft.smb.SmbConfig
import com.ctreesoft.smb.auth.NtlmCredentials
import com.ctreesoft.smb.license.SmbLicense
val license = SmbLicense.fromFile("/path/to/smb.lic")
val config = SmbConfig(license = license)
val credentials = NtlmCredentials("user", "password", "DOMAIN")
val client = SmbClient.connect("192.168.1.100", credentials, config = config)
try {
val share = client.connectShare("Documents")
// ... perform file operations ...
share.close()
} finally {
client.close()
} NtlmCredentials
A data class implementing SmbCredentials that provides NTLM
authentication parameters. This is the standard authentication method for
SMB connections to Windows servers and Samba.
data class NtlmCredentials(
val username: String,
val password: String,
val domain: String = ""
) : SmbCredentials | Parameter | Type | Default | Description |
|---|---|---|---|
username | String | — | The user account name |
password | String | — | The user password |
domain | String | "" | Windows domain or workgroup name. Empty string for standalone servers. |
Example
// Domain-joined user
val corporate = NtlmCredentials("jsmith", "s3cret", "ACME")
// Standalone server — domain can be omitted
val local = NtlmCredentials("admin", "password123") SmbConfig
A data class that controls session-level behavior including transfer chunk sizing, SMB3 encryption, custom socket factories, address resolution, and licensing. All fields have sensible defaults — you only need to provide a license for production use.
data class SmbConfig(
val transferChunkSize: TransferChunkSize = TransferChunkSize.Default,
val forceEncryption: Boolean = false,
val socketFactory: SmbSocketFactory? = null,
val resolveAddresses: Boolean = false,
val license: SmbLicense? = null
) | Parameter | Type | Default | Description |
|---|---|---|---|
transferChunkSize | TransferChunkSize | Default | Controls the maximum read/write chunk size per SMB request |
forceEncryption | Boolean | false | When true, requires SMB3 encryption. Connection fails if the server does not support it. |
socketFactory | SmbSocketFactory? | null | Custom socket factory for managed environments (e.g. BlackBerry Dynamics) |
resolveAddresses | Boolean | false | When true, resolves hostnames to IP addresses before connecting. Useful when DNS returns names that the transport layer cannot resolve. |
license | SmbLicense? | null | License for production use. See Licensing. |
TransferChunkSize
Controls the maximum number of bytes read or written in a single SMB request. Larger chunks reduce round trips but use more memory.
| Mode | Description |
|---|---|
TransferChunkSize.Default | 64 KB per request. Safe for all servers and networks. |
TransferChunkSize.ServerMax | Negotiates up to the server maximum (typically 8 MB). Best throughput on fast networks. |
TransferChunkSize.Custom(bytes: Int) | Explicit chunk size in bytes. Clamped to the server maximum at runtime. |
Example
import com.ctreesoft.smb.SmbConfig
import com.ctreesoft.smb.TransferChunkSize
import com.ctreesoft.smb.license.SmbLicense
// Minimal config with just a license
val config = SmbConfig(
license = SmbLicense.fromFile("/path/to/smb.lic")
)
// High-throughput config with encryption
val fastConfig = SmbConfig(
transferChunkSize = TransferChunkSize.ServerMax,
forceEncryption = true,
license = SmbLicense.fromFile("/path/to/smb.lic")
)
// Custom chunk size
val customConfig = SmbConfig(
transferChunkSize = TransferChunkSize.Custom(256 * 1024) // 256 KB
) SmbShare
Represents a connected share on an SMB server. All file and directory operations
are performed through this class. Paths are relative to the share root and use
backslash (\) or forward slash (/) separators — both
are accepted and normalized internally.
listDirectory()
Returns a Flow of SmbFileEntry objects for each item in
the specified directory. Results are emitted as they arrive from the server.
fun listDirectory(path: String): Flow<SmbFileEntry> | Parameter | Type | Description |
|---|---|---|
path | String | Directory path relative to the share root. Use "" for the root. |
readFile()
Opens a file for reading and passes an Okio Source to the provided
block. The file handle is closed automatically when the block returns.
suspend fun <T> readFile(
path: String,
block: suspend (Source) -> T
): T | Parameter | Type | Description |
|---|---|---|
path | String | File path relative to the share root |
block | suspend (Source) -> T | Lambda that reads from the source and returns a result |
writeFile()
Opens a file for writing (creating or overwriting) and passes an Okio
Sink to the provided block. The file handle is closed and
flushed automatically when the block returns.
suspend fun writeFile(
path: String,
block: suspend (Sink) -> Unit
) | Parameter | Type | Description |
|---|---|---|
path | String | File path relative to the share root |
block | suspend (Sink) -> Unit | Lambda that writes data to the sink |
createDirectory()
Creates a directory at the specified path.
suspend fun createDirectory(path: String) delete()
Deletes a single file or empty directory.
suspend fun delete(path: String) deleteRecursively()
Deletes a directory and all of its contents recursively.
suspend fun deleteRecursively(path: String) copyFile()
Copies a file from one path to another within the same share. Uses server-side copy when supported.
suspend fun copyFile(sourcePath: String, destinationPath: String) | Parameter | Type | Description |
|---|---|---|
sourcePath | String | Path of the file to copy |
destinationPath | String | Path for the new copy |
rename()
Renames or moves a file or directory within the same share.
suspend fun rename(sourcePath: String, destinationPath: String) | Parameter | Type | Description |
|---|---|---|
sourcePath | String | Current path of the file or directory |
destinationPath | String | New path for the file or directory |
close()
Disconnects from the share. Safe to call multiple times.
fun close() Example
import com.ctreesoft.smb.SmbClient
import com.ctreesoft.smb.SmbConfig
import com.ctreesoft.smb.auth.NtlmCredentials
import com.ctreesoft.smb.license.SmbLicense
import kotlinx.coroutines.flow.collect
import okio.buffer
val license = SmbLicense.fromFile("/path/to/smb.lic")
val client = SmbClient.connect(
host = "192.168.1.100",
credentials = NtlmCredentials("user", "password"),
config = SmbConfig(license = license)
)
val share = client.connectShare("Projects")
// List files in a directory
share.listDirectory("reports/2026").collect { entry ->
println("${entry.name} — ${entry.size} bytes")
}
// Read a file into a string
val text = share.readFile("reports/2026/summary.txt") { source ->
source.buffer().readUtf8()
}
// Write a file
share.writeFile("reports/2026/notes.txt") { sink ->
sink.buffer().use { buf ->
buf.writeUtf8("Meeting notes for Q1...")
}
}
// Create, copy, rename, delete
share.createDirectory("reports/2026/archive")
share.copyFile("reports/2026/summary.txt", "reports/2026/archive/summary-backup.txt")
share.rename("reports/2026/notes.txt", "reports/2026/archive/notes.txt")
share.delete("reports/2026/archive/summary-backup.txt")
share.deleteRecursively("reports/2026/archive")
share.close()
client.close() SmbFileEntry
A data class representing a file or directory entry returned by
listDirectory(). All timestamp fields use java.time.Instant.
data class SmbFileEntry(
val name: String,
val size: Long,
val isDirectory: Boolean,
val isReadOnly: Boolean,
val isHidden: Boolean,
val creationTime: Instant,
val lastModifiedTime: Instant,
val lastAccessTime: Instant
) | Property | Type | Description |
|---|---|---|
name | String | File or directory name (not the full path) |
size | Long | File size in bytes. Always 0 for directories. |
isDirectory | Boolean | true if this entry is a directory |
isReadOnly | Boolean | true if the read-only attribute is set |
isHidden | Boolean | true if the hidden attribute is set |
creationTime | Instant | When the file was created |
lastModifiedTime | Instant | When the file was last written to |
lastAccessTime | Instant | When the file was last read |
Example
share.listDirectory("documents").collect { entry ->
if (entry.isDirectory) {
println("[DIR] ${entry.name}")
} else {
println("[FILE] ${entry.name} (${entry.size} bytes, modified ${entry.lastModifiedTime})")
}
} SmbLicense
Factory for loading license files required for production use. License files are Ed25519-signed plain-text documents issued by CTreeSoft. See Licensing for the file format and embedding instructions.
Factory Methods
object SmbLicense {
fun fromFile(file: File): SmbLicense
fun fromStream(inputStream: InputStream): SmbLicense
fun fromString(text: String): SmbLicense
} | Method | Parameter | Description |
|---|---|---|
fromFile | file: File | Load a license from a .lic file on disk |
fromStream | inputStream: InputStream | Load a license from any input stream (Android assets, classpath resources, etc.) |
fromString | text: String | Parse a license from its raw text content |
Example
// From a file
val license = SmbLicense.fromFile(File("/etc/myapp/smb.lic"))
// From Android assets
val license = SmbLicense.fromStream(context.assets.open("smb.lic"))
// From classpath resources (JVM)
val stream = Thread.currentThread().contextClassLoader
.getResourceAsStream("smb.lic")
val license = SmbLicense.fromStream(stream!!) SmbSocketFactory
A functional interface for providing custom Socket instances. This is
useful in managed environments such as BlackBerry Dynamics, where all network I/O
must go through a vendor-provided socket implementation.
fun interface SmbSocketFactory {
fun createSocket(): Socket
} Example
// BlackBerry Dynamics example
import com.good.gd.net.GDSocket
val config = SmbConfig(
socketFactory = SmbSocketFactory { GDSocket() },
license = SmbLicense.fromFile("/path/to/smb.lic")
)
val client = SmbClient.connect("server.corp.local", credentials, config = config) Exception Hierarchy
All exceptions thrown by smb-kotlin extend the abstract SmbException base
class. Each subclass represents a specific failure mode so you can catch precisely the
errors you want to handle.
SmbException (abstract)
├── SmbConnectionException
│ └── SmbConnectionLostException
├── SmbAuthenticationException
├── SmbAccessDeniedException
├── SmbFileNotFoundException
├── SmbShareNotFoundException
├── SmbFileExistsException
├── SmbDirectoryNotEmptyException
├── SmbDiskFullException
├── SmbProtocolException
└── SmbLicenseException | Exception | When Thrown |
|---|---|
SmbConnectionException | TCP connection failed or protocol negotiation was rejected |
SmbConnectionLostException | An established connection was dropped mid-operation |
SmbAuthenticationException | NTLM authentication failed (bad username, password, or domain) |
SmbAccessDeniedException | The authenticated user lacks permission for the requested operation |
SmbFileNotFoundException | The target file or directory does not exist |
SmbShareNotFoundException | The named share does not exist on the server |
SmbFileExistsException | A create or rename would overwrite an existing entry |
SmbDirectoryNotEmptyException | delete() was called on a non-empty directory (use deleteRecursively() instead) |
SmbDiskFullException | The server reported insufficient disk space |
SmbProtocolException | The server sent a malformed or unexpected SMB response |
SmbLicenseException | License is missing, expired, or signature verification failed |
Example
import com.ctreesoft.smb.*
try {
val client = SmbClient.connect("server", credentials, config = config)
val share = client.connectShare("Documents")
share.readFile("secret/report.docx") { source ->
source.buffer().readByteArray()
}
} catch (e: SmbAuthenticationException) {
println("Login failed: ${e.message}")
} catch (e: SmbAccessDeniedException) {
println("Permission denied: ${e.message}")
} catch (e: SmbFileNotFoundException) {
println("File not found: ${e.message}")
} catch (e: SmbConnectionLostException) {
println("Connection dropped: ${e.message}")
} catch (e: SmbException) {
println("SMB error: ${e.message}")
}