diff --git a/.gitmodules b/.gitmodules index dba494b..58d8a96 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "Dependencies/libsmb2"] path = Dependencies/libsmb2 - url = https://github.com/amosavian/libsmb2.git + url = https://github.com/sahlberg/libsmb2 diff --git a/AMSMB2/AMSMB2.swift b/AMSMB2/AMSMB2.swift index c35ea83..973186c 100644 --- a/AMSMB2/AMSMB2.swift +++ b/AMSMB2/AMSMB2.swift @@ -919,7 +919,7 @@ public class SMB2Manager: NSObject, NSSecureCoding, Codable, NSCopying, CustomRe - Returns: a `Data` object which contains file contents. */ open func contents( - atPath path: String, range: R? = Range?.none, progress: ReadProgressHandler + atPath path: String, range: R? = Range?.none, progress: ReadProgressHandler = nil ) async throws -> Data where R.Bound: FixedWidthInteger { try await withCheckedThrowingContinuation { continuation in contents( @@ -1719,48 +1719,42 @@ extension SMB2Manager { context: SMB2Context, from stream: InputStream, toPath: String, offset: Int64? = nil, chunkSize: Int = 0, progress: WriteProgressHandler ) throws { - let file = offset == nil ? try SMB2FileHandle(forCreatingIfNotExistsAtPath: toPath, on: context) : try SMB2FileHandle( - forOutputAtPath: toPath, - on: context - ) + let file: SMB2FileHandle + if let offset { + try context.truncate(toPath, toLength: .init(offset)) + file = try SMB2FileHandle(forOutputAtPath: toPath, on: context) + try file.lseek(offset: offset, whence: .set) + } else { + file = try SMB2FileHandle(forCreatingIfNotExistsAtPath: toPath, on: context) + } let chunkSize = chunkSize > 0 ? chunkSize : file.optimizedWriteSize var totalWritten: UInt64 = 0 - do { - if let offset { - try file.lseek(offset: offset, whence: .set) - try file.ftruncate(toLength: UInt64(offset)) - } - - try stream.withOpenStream { - while true { - let segment = try stream.readData(maxLength: chunkSize) - if segment.isEmpty { - break - } - let written = try file.pwrite(data: segment, offset: UInt64(offset ?? 0) + totalWritten) - if written != segment.count { - throw POSIXError( - .EIO, description: "Inconsistency in writing to SMB file handle." - ) - } + try stream.withOpenStream { + while true { + let segment = try stream.readData(maxLength: chunkSize) + if segment.isEmpty { + break + } + let written = try file.pwrite(data: segment, offset: UInt64(offset ?? 0) + totalWritten) + if written != segment.count { + throw POSIXError( + .EIO, description: "Inconsistency in writing to SMB file handle." + ) + } - totalWritten += UInt64(segment.count) - var offset = try file.lseek(offset: 0, whence: .current) - if offset > totalWritten { - offset = Int64(totalWritten) - } - if let shouldContinue = progress?(Int64(totalWritten)), !shouldContinue { - break - } + totalWritten += UInt64(segment.count) + var offset = try file.lseek(offset: 0, whence: .current) + if offset > totalWritten { + offset = Int64(totalWritten) + } + if let shouldContinue = progress?(Int64(totalWritten)), !shouldContinue { + break } } - - try file.fsync() - } catch { - try? context.unlink(toPath) - throw error } + + try file.fsync() } } diff --git a/AMSMB2Tests/SMB2ManagerTests.swift b/AMSMB2Tests/SMB2ManagerTests.swift index 6fea134..91216ff 100644 --- a/AMSMB2Tests/SMB2ManagerTests.swift +++ b/AMSMB2Tests/SMB2ManagerTests.swift @@ -160,7 +160,46 @@ class SMB2ManagerTests: XCTestCase { XCTAssertEqual(initialAttribs.contentModificationDate, newAttribs.contentModificationDate) XCTAssertEqual(newAttribs.creationDate, Date(timeIntervalSinceReferenceDate: 0)) } + + func testFileRename() async throws { + let file = "renametest.dat" + let renamedFile = "renamed.dat" + let size: Int = random(max: 0x000800) + let smb = SMB2Manager(url: server, credential: credential)! + let data = randomData(size: size) + + addTeardownBlock { + try? await smb.removeFile(atPath: file) + try? await smb.removeFile(atPath: renamedFile) + } + + try await smb.connectShare(name: share, encrypted: encrypted) + try await smb.write(data: data, toPath: file, progress: nil) + + try await smb.moveItem(atPath: file, toPath: renamedFile) + let renamedData = try await smb.contents(atPath: renamedFile) + XCTAssertEqual(data, renamedData) + } + + func testFileTruncate() async throws { + let file = "trunctest.dat" + let size: Int = random(max: 0x000800) + let smb = SMB2Manager(url: server, credential: credential)! + let data = randomData(size: size) + addTeardownBlock { + try? await smb.removeFile(atPath: file) + } + + try await smb.connectShare(name: share, encrypted: encrypted) + try await smb.write(data: data, toPath: file, progress: nil) + + try await smb.truncateFile(atPath: file, atOffset: 0x000200) + let truncData = try await smb.contents(atPath: file) + XCTAssertEqual(truncData.count, 0x000200) + XCTAssertEqual(data.prefix(truncData.count), truncData) + } + func testListing() async throws { let smb = SMB2Manager(url: server, credential: credential)! try await smb.connectShare(name: share, encrypted: encrypted) diff --git a/Dependencies/libsmb2 b/Dependencies/libsmb2 index 1d85526..1c7d607 160000 --- a/Dependencies/libsmb2 +++ b/Dependencies/libsmb2 @@ -1 +1 @@ -Subproject commit 1d855265e7d2b87f534c3ce1a799fb5ccff8ccba +Subproject commit 1c7d60724a05ca14770c66da8eea6b49e1215376