What's New
- Recoverable notification overflow - When a server can no longer list individual changes, typically because too many happened at once, a directory watcher now ends with the identifiable
SMB.SMBStatus.notifyEnumDirinstead of an unknown status. Re-read the directory and start a new watcher:
do {
for try await changes in watcher {
handle(changes)
}
}
catch SMB.Error.ntStatus(.notifyEnumDir, _, _, _) {
let entries = try await connection.listDirectory(at: "Inbox")
rescan(entries)
}- Non-atomic uploads replace the destination -
uploadFile(atomic: false)now defaultsoptionsto[.create, .truncate], so the remote file ends up identical to the local one. Resumed uploads (from: .offset(byte:)) never truncate, even if.truncateis passed.
Bug Fixes
- Fixed errors being reported with the NT status and message of an earlier, unrelated failure, for example a lock conflict after a failed open surfacing as
STATUS_OBJECT_NAME_NOT_FOUND. - Fixed directory watchers failing with
STATUS_IO_TIMEOUTafter staying idle longer than the connection's command timeout. - Fixed a potential use of freed memory when cancelling a directory watcher while its notify request was being sent or its reply received.
- Fixed
downloadFileleaving a local temporary file behind when an atomic resume failed before transferring, for example when the local file was shorter than the resume offset. - Fixed
uploadFileleaving a remote temporary file behind when an atomic resumed upload failed or was cancelled while seeding it. - Fixed
copyFilesilently creating an empty file when the source was a directory; it now throwsremotePathIsNotAFile. - Fixed
copyFileleaving a partial destination when the copy failed, which made retries fail because the destination existed. - Resolved a crash in
makeLinkwhen the link destination was too long for a reparse point; it now throwsENAMETOOLONG. - Resolved a crash in
FilesystemStat.freeBytesandavailableByteswhen server-reported values overflowed; they now saturate atUInt64.max. - Fixed
File.writere-copying the remaining data on every write when given a transfer chunk size larger than the server's maximum; chunks are now clamped as documented.
Migration Guide
Non-atomic uploads without explicit options now create the destination if needed and truncate existing content. If you relied on writing into an existing file without truncating it, pass options: [] explicitly.
Before:
// Failed if "log.bin" did not exist; left stale bytes if it was longer than the local file.
try await connection.uploadFile(local: localURL, remote: "log.bin", atomic: false) { _, _, _, _ in true }After:
// Creates or replaces "log.bin" so it matches the local file.
try await connection.uploadFile(local: localURL, remote: "log.bin", atomic: false) { _, _, _, _ in true }
// Previous behavior: write into the existing file without creating or truncating it.
try await connection.uploadFile(local: localURL, remote: "log.bin", options: [], atomic: false) { _, _, _, _ in true }