SmbSharp 1.0.16
See the version list below for details.
dotnet add package SmbSharp --version 1.0.16
NuGet\Install-Package SmbSharp -Version 1.0.16
<PackageReference Include="SmbSharp" Version="1.0.16" />
<PackageVersion Include="SmbSharp" Version="1.0.16" />
<PackageReference Include="SmbSharp" />
paket add SmbSharp --version 1.0.16
#r "nuget: SmbSharp, 1.0.16"
#:package SmbSharp@1.0.16
#addin nuget:?package=SmbSharp&version=1.0.16
#tool nuget:?package=SmbSharp&version=1.0.16
SmbSharp
A cross-platform .NET library for SMB/CIFS file operations. Works seamlessly on Windows using native UNC paths and on Linux using smbclient.
Features
- ✅ Cross-Platform: Windows (native UNC) and Linux (smbclient)
- ✅ Dual Authentication: Kerberos and username/password authentication
- ✅ Stream-Based API: Efficient, memory-friendly file operations
- ✅ Async/Await: Full async support with cancellation tokens
- ✅ Dependency Injection: Built-in ASP.NET Core DI integration
- ✅ Health Checks: Monitor SMB share connectivity with ASP.NET Core health checks
- ✅ Multiple .NET Versions: Supports .NET Core 3.1, .NET 6, .NET 8, and .NET 10
- ✅ Secure: Passwords passed via environment variables, not command-line arguments
- ✅ Well-Documented: Comprehensive XML documentation with IntelliSense support
Installation
NuGet Package Manager
Install-Package SmbSharp
.NET CLI
dotnet add package SmbSharp
Package Reference
<PackageReference Include="SmbSharp" Version="1.0.0" />
Platform Requirements
Windows
- No additional requirements - uses native UNC path support
Linux
- Requires
smbclientto be installed:# Debian/Ubuntu sudo apt-get install smbclient # RHEL/CentOS sudo yum install samba-client # Alpine Linux apk add samba-client
Docker
Add smbclient to your Dockerfile:
Debian/Ubuntu-based images:
FROM mcr.microsoft.com/dotnet/aspnet:8.0
RUN apt-get update && apt-get install -y smbclient && rm -rf /var/lib/apt/lists/*
Alpine-based images:
FROM mcr.microsoft.com/dotnet/aspnet:8.0-alpine
RUN apk add --no-cache samba-client
RHEL/CentOS-based images:
FROM mcr.microsoft.com/dotnet/aspnet:8.0-rhel
RUN yum install -y samba-client && yum clean all
Quick Start
Using Dependency Injection (Recommended)
Kerberos Authentication (Default)
// Program.cs
builder.Services.AddSmbSharp();
// Usage in a controller/service
public class MyService
{
private readonly IFileHandler _fileHandler;
public MyService(IFileHandler fileHandler)
{
_fileHandler = fileHandler;
}
public async Task<IEnumerable<string>> GetFiles()
{
return await _fileHandler.EnumerateFilesAsync("//server/share/folder");
}
}
Username/Password Authentication
// Program.cs - Direct credentials
builder.Services.AddSmbSharp("username", "password", "DOMAIN");
// Or using configuration
builder.Services.AddSmbSharp(options =>
{
options.UseKerberos = false;
options.Username = "username";
options.Password = "password";
options.Domain = "DOMAIN";
});
// Or from appsettings.json
builder.Services.AddSmbSharp(options =>
{
builder.Configuration.GetSection("SmbSharp").Bind(options);
});
Direct Instantiation (Without Dependency Injection)
using Microsoft.Extensions.Logging;
using SmbSharp.Business;
// Without logging
var handler = FileHandler.CreateWithKerberos();
var handler = FileHandler.CreateWithCredentials("username", "password", "DOMAIN");
// With console logging (for debugging)
using var loggerFactory = LoggerFactory.Create(builder =>
{
builder
.SetMinimumLevel(LogLevel.Debug)
.AddConsole();
});
var handler = FileHandler.CreateWithKerberos(loggerFactory);
var handler = FileHandler.CreateWithCredentials("username", "password", "DOMAIN", loggerFactory);
// Usage
var files = await handler.EnumerateFilesAsync("//server/share/folder");
Note: To use console logging, you need to add the Microsoft.Extensions.Logging.Console package:
dotnet add package Microsoft.Extensions.Logging.Console
Path Format
SmbSharp accepts SMB paths in multiple formats for flexibility:
// Forward slashes (recommended for cross-platform code)
await fileHandler.EnumerateFilesAsync("//server/share/folder");
// Backslashes (Windows UNC format)
await fileHandler.EnumerateFilesAsync("\\\\server\\share\\folder");
// Mixed (automatically normalized)
await fileHandler.EnumerateFilesAsync("//server/share\\folder");
Note: All path formats are automatically normalized internally. Forward slashes (/) are recommended for cross-platform compatibility, but backslashes (\) are fully supported for Windows-style UNC paths.
Usage Examples
List Files in a Directory
var files = await fileHandler.EnumerateFilesAsync("//server/share/folder");
foreach (var file in files)
{
Console.WriteLine(file);
}
Read a File
await using var stream = await fileHandler.ReadFileAsync("//server/share/folder", "file.txt");
using var reader = new StreamReader(stream);
var content = await reader.ReadToEndAsync();
Write a File (String Content)
await fileHandler.WriteFileAsync("//server/share/folder/file.txt", "Hello, World!");
Write a File (Stream)
await using var fileStream = File.OpenRead("local-file.txt");
await fileHandler.WriteFileAsync("//server/share/folder/file.txt", fileStream);
Write with Different Modes
// Overwrite existing file (default)
await fileHandler.WriteFileAsync("//server/share/file.txt", stream, FileWriteMode.Overwrite);
// Create only if doesn't exist (fails if exists)
await fileHandler.WriteFileAsync("//server/share/file.txt", stream, FileWriteMode.CreateNew);
// Append to existing file
await fileHandler.WriteFileAsync("//server/share/file.txt", stream, FileWriteMode.Append);
Delete a File
await fileHandler.DeleteFileAsync("//server/share/folder/file.txt");
Move a File
await fileHandler.MoveFileAsync(
"//server/share/folder/old.txt",
"//server/share/folder/new.txt"
);
Note: On Linux, move operations download and re-upload the file, which can be slow for large files. The operation is atomic with automatic retry logic - if the source deletion fails after copying, it retries once before rolling back the destination to maintain consistency.
Create a Directory
await fileHandler.CreateDirectoryAsync("//server/share/newfolder");
Test Connectivity
bool canConnect = await fileHandler.CanConnectAsync("//server/share");
if (canConnect)
{
Console.WriteLine("Successfully connected!");
}
Using Cancellation Tokens
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
try
{
var files = await fileHandler.EnumerateFilesAsync(
"//server/share/folder",
cts.Token
);
}
catch (OperationCanceledException)
{
Console.WriteLine("Operation timed out!");
}
Authentication
Kerberos Authentication
On Linux, ensure you have a valid Kerberos ticket before using the library:
kinit username@DOMAIN.COM
Verify your ticket:
klist
Username/Password Authentication
Credentials are securely passed to smbclient via environment variables, not command-line arguments, preventing exposure in process listings.
Health Checks
SmbSharp includes built-in health check support for ASP.NET Core applications to monitor SMB share connectivity.
Single Share Health Check
// Program.cs
builder.Services.AddSmbSharp();
builder.Services.AddHealthChecks()
.AddSmbShareCheck("//server/share/folder");
Named Health Check with Options
builder.Services.AddHealthChecks()
.AddSmbShareCheck(
directoryPath: "//server/share/folder",
name: "primary_smb_share",
failureStatus: HealthStatus.Degraded,
tags: new[] { "smb", "storage" },
timeout: TimeSpan.FromSeconds(10)
);
Multiple Share Health Checks
var shares = new Dictionary<string, string>
{
{ "primary", "//server1/share1" },
{ "backup", "//server2/share2" },
{ "archive", "//server3/share3" }
};
builder.Services.AddHealthChecks()
.AddSmbShareChecks(shares, tags: new[] { "smb" });
Health Check Endpoint
// Program.cs
var app = builder.Build();
app.MapHealthChecks("/health");
// Or with detailed response
app.MapHealthChecks("/health", new HealthCheckOptions
{
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});
Example Response
Healthy:
{
"status": "Healthy",
"results": {
"smb_share": {
"status": "Healthy",
"description": "Successfully connected to SMB share: //server/share/folder"
}
}
}
Unhealthy:
{
"status": "Unhealthy",
"results": {
"smb_share": {
"status": "Unhealthy",
"description": "Unable to connect to SMB share: //server/share/folder"
}
}
}
Health Check Logging
When health checks fail, error logs are automatically generated to help troubleshoot connectivity issues:
// Enable logging in your appsettings.json
{
"Logging": {
"LogLevel": {
"SmbSharp.HealthChecks.SmbShareHealthCheck": "Error"
}
}
}
Example error log:
[Error] Health check failed: Unable to connect to SMB share: //server/share/folder
[Error] Health check failed for SMB share //server/share/folder: Access denied
API Reference
IFileHandler Interface
| Method | Description |
|---|---|
EnumerateFilesAsync(directory, cancellationToken) |
Lists all files in a directory |
ReadFileAsync(directory, fileName, cancellationToken) |
Opens a file for reading as a stream |
WriteFileAsync(filePath, content, cancellationToken) |
Writes a string to a file |
WriteFileAsync(filePath, stream, cancellationToken) |
Writes a stream to a file |
WriteFileAsync(filePath, stream, writeMode, cancellationToken) |
Writes a stream with specific write mode |
DeleteFileAsync(filePath, cancellationToken) |
Deletes a file |
MoveFileAsync(sourcePath, destPath, cancellationToken) |
Moves a file |
CreateDirectoryAsync(directoryPath, cancellationToken) |
Creates a directory |
CanConnectAsync(directoryPath, cancellationToken) |
Tests connectivity to a share |
FileWriteMode Enum
| Value | Description |
|---|---|
Overwrite |
Creates a new file or overwrites existing (default) |
CreateNew |
Creates only if file doesn't exist (throws if exists) |
Append |
Appends to existing file or creates new |
Error Handling
The library throws specific exceptions for different error scenarios:
try
{
await fileHandler.ReadFileAsync("//server/share", "file.txt");
}
catch (FileNotFoundException ex)
{
// File or path doesn't exist
}
catch (UnauthorizedAccessException ex)
{
// Access denied or authentication failed
}
catch (DirectoryNotFoundException ex)
{
// Network path not found
}
catch (IOException ex)
{
// Other SMB/network errors
}
catch (PlatformNotSupportedException ex)
{
// Running on unsupported platform (not Windows/Linux)
}
Debugging
To troubleshoot authentication issues or see exactly what commands are being sent to smbclient, enable debug logging:
// In Program.cs or appsettings.json
builder.Logging.SetMinimumLevel(LogLevel.Debug);
// Or configure specific loggers
builder.Logging.AddFilter("SmbSharp.Infrastructure.ProcessWrapper", LogLevel.Debug);
Example debug output:
Executing process: smbclient //server/share -U "username" -c "ls folder/*"
Environment variables set: PASSWD
Process exited with code: 0
Note: Passwords are never logged. Only environment variable names (like PASSWD) are shown, not their values.
Performance Considerations
Windows
- Uses native UNC paths - very efficient
- All operations are direct file system calls
- Move operations are atomic and instant (metadata-only)
Linux
- Uses smbclient subprocess - some overhead
- Read operations download to temp file (auto-cleaned)
- Write operations upload from temp file
- Move operations = download + upload + delete (can be slow for large files)
- For large files, consider alternative approaches or be aware of 2x disk space + network transfer
Security Features
- ✅ Passwords passed via environment variables (not visible in process listings)
- ✅ Command injection protection (input escaping)
- ✅ Comprehensive input validation
- ✅ Path traversal protection
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License - see the LICENSE file for details.
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Email: diego@dancourt.org
Acknowledgments
Built with ❤️ for the .NET community.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 is compatible. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp3.1 is compatible. |
-
.NETCoreApp 3.1
-
net10.0
-
net6.0
-
net8.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 1.1.2 | 87 | 2/10/2026 | |
| 1.1.1 | 80 | 2/10/2026 | |
| 1.1.0 | 65 | 2/7/2026 | |
| 1.0.16 | 114 | 2/4/2026 | |
| 1.0.15 | 58 | 2/4/2026 | |
| 1.0.14 | 67 | 2/4/2026 | |
| 1.0.13 | 54 | 2/4/2026 | |
| 1.0.12 | 60 | 2/4/2026 | |
| 1.0.11 | 59 | 2/3/2026 | |
| 1.0.10 | 52 | 2/3/2026 | |
| 1.0.9 | 54 | 2/3/2026 | |
| 1.0.8 | 55 | 2/3/2026 | |
| 1.0.7 | 52 | 2/3/2026 | |
| 1.0.6 | 49 | 2/3/2026 | |
| 1.0.5 | 60 | 2/3/2026 | |
| 1.0.4 | 56 | 2/2/2026 | |
| 1.0.3 | 49 | 2/2/2026 | |
| 1.0.2 | 51 | 2/2/2026 | |
| 1.0.1 | 59 | 1/30/2026 | |
| 1.0.0 | 60 | 1/29/2026 |
v1.0.16: Make MoveFileAsync atomic on Linux with retry logic. If source deletion fails after copying, it retries once before rolling back the destination to maintain consistency. Updated documentation to reflect atomicity guarantee.