Skip to content

Commit 29c687e

Browse files
author
Carlos Cabanero
committed
Fixes for overwrite mode on SFTP during copy operation
- Removed flags from BlinkFiles::create. It was used as a way to pass it down to the open but it made things more confusing. - Create now uses by default the expected flags everywhere. - Additional tests on SFTP.
1 parent 9391d32 commit 29c687e

File tree

9 files changed

+67
-19
lines changed

9 files changed

+67
-19
lines changed

Blink/Commands/ssh/CopyFiles.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ public class BlinkCopy: NSObject {
252252
let newFileName = (self.command.destination.filePath as NSString).lastPathComponent
253253
let parentPath = (self.command.destination.filePath as NSString).deletingLastPathComponent
254254
return d.cloneWalkTo(parentPath)
255-
.flatMap { $0.create(name: newFileName, flags: O_WRONLY, mode: S_IRWXU) }
255+
.flatMap { $0.create(name: newFileName, mode: S_IRWXU) }
256256
.flatMap { $0.close() }
257257
.flatMap { _ in d.cloneWalkTo(self.command.destination.filePath) }
258258
.eraseToAnyPublisher()

BlinkCode/CodeFileSystem.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ class CodeFileSystem {
164164
if !(options.create ?? false) {
165165
return .fail(error: CodeFileSystemError.fileNotFound(uri: self.uri))
166166
}
167-
return parentT.create(name: fileName, flags: O_WRONLY, mode: 0o644)
167+
return parentT.create(name: fileName, mode: 0o644)
168168
}
169169
// 2. Write the content to the file
170170
.flatMap { file -> AnyPublisher<Int, Error> in

BlinkFiles/BlinkFiles.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public protocol Translator: CopierFrom {
6262

6363
// Creates the file name at the current walked path with the given permissions.
6464
// Default mode = S_IRWXU
65-
func create(name: String, flags: Int32, mode: mode_t) -> AnyPublisher<File, Error>
65+
func create(name: String, mode: mode_t) -> AnyPublisher<File, Error>
6666
// Creates the directory name at the current walked path with the given permissions.
6767
// Default mode = S_IRWUX | S_IRWXG | S_IRWXO
6868
func mkdir(name: String, mode: mode_t) -> AnyPublisher<Translator, Error>

BlinkFiles/CopyFiles.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,10 @@ extension Translator {
201201

202202
let fullFile: String
203203
let file: AnyPublisher<File, Error>
204-
// If we are in a directory, we create the file, otherwise we open truncated.
204+
// If we are a directory, we create the file. If we are a file, we open truncated.
205205
if self.isDirectory {
206206
fullFile = (self.current as NSString).appendingPathComponent(name)
207-
file = self.create(name: name, flags: O_WRONLY, mode: S_IRWXU)
207+
file = self.create(name: name, mode: S_IRWXU)
208208
} else {
209209
fullFile = self.current
210210
file = self.open(flags: O_WRONLY | O_TRUNC)

BlinkFiles/LocalFiles.swift

+2-3
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,7 @@ public class Local : Translator {
134134
}.eraseToAnyPublisher()
135135
}
136136

137-
// // TODO Change permissions to more generic open options
138-
public func create(name: String, flags: Int32, mode: mode_t = S_IRWXU) -> AnyPublisher<File, Error> {
137+
public func create(name: String, mode: mode_t = S_IRWXU) -> AnyPublisher<File, Error> {
139138
if fileType != .typeDirectory {
140139
return fail(msg: "Not a directory.")
141140
}
@@ -148,7 +147,7 @@ public class Local : Translator {
148147
throw LocalFileError(msg: "Could not create file.")
149148
}
150149

151-
return try LocalFile(at: absPath, flags: flags)
150+
return try LocalFile(at: absPath, flags: O_WRONLY|O_CREAT|O_TRUNC)
152151
}.eraseToAnyPublisher()
153152
}
154153

BlinkFilesTests/LocalFilesTests.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ class LocalFilesTests: XCTestCase {
176176
f.walkTo("/Users/carlos/Xcode_12.0.1.xip")
177177
.flatMap { $0.open(flags: O_RDONLY) }
178178
.flatMap { srcFile -> AnyPublisher<Int, Error> in
179-
return dst.create(name: "Docker-copy.dmg", flags: O_WRONLY, mode: 0o644)
179+
return dst.create(name: "Docker-copy.dmg", mode: 0o644)
180180
.flatMap { dstFile in
181181
return (srcFile as! WriterTo).writeTo(dstFile)
182182
}.eraseToAnyPublisher()

SSH/SCP.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -383,7 +383,7 @@ extension SCPClient {
383383

384384
// Flow when receiving a file to copy. Create on Translator and then Write to it.
385385
func copyFileTo(translator: Translator, usingName name: String, length: UInt64, mode: mode_t) -> CopyProgressInfoPublisher {
386-
return translator.create(name: name, flags: O_RDWR, mode: mode).flatMap { file -> CopyProgressInfoPublisher in
386+
return translator.create(name: name, mode: mode).flatMap { file -> CopyProgressInfoPublisher in
387387
var totalWritten: UInt64 = 0
388388

389389
return self.writeTo(file, length: length).map { written in

SSH/SFTP.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,7 @@ public class SFTPTranslator: BlinkFiles.Translator {
247247
}.eraseToAnyPublisher()
248248
}
249249

250-
public func create(name: String, flags: Int32, mode: mode_t = S_IRWXU) -> AnyPublisher<BlinkFiles.File, Error> {
250+
public func create(name: String, mode: mode_t = S_IRWXU) -> AnyPublisher<BlinkFiles.File, Error> {
251251
if fileType != .typeDirectory {
252252
return .fail(error: FileError(title: "Not a directory.", in: session))
253253
}
@@ -257,7 +257,7 @@ public class SFTPTranslator: BlinkFiles.Translator {
257257
defer { ssh_channel_set_blocking(self.channel, 0) }
258258

259259
let filePath = (self.path as NSString).appendingPathComponent(name)
260-
guard let file = sftp_open(sftp, filePath, flags | O_CREAT, mode) else {
260+
guard let file = sftp_open(sftp, filePath, O_WRONLY|O_CREAT|O_TRUNC, mode) else {
261261
throw FileError(in: self.session)
262262
}
263263

SSHTests/SFTPTests.swift

+56-7
Original file line numberDiff line numberDiff line change
@@ -133,19 +133,23 @@ class SFTPTests: XCTestCase {
133133
}
134134

135135
func testWrite() throws {
136-
let expectation = self.expectation(description: "Buffer Written")
136+
let writeExpectation = self.expectation(description: "File Written")
137137

138138
//var connection: SSHClient?
139-
//var sftp: SFTPClient?
139+
var root: SFTPTranslator? = nil
140140
var totalWritten = 0
141141

142142
let gen = RandomInputGenerator(fast: true)
143143

144-
let cancellable = SSHClient.dialWithTestConfig()
144+
let cancelWrite = SSHClient.dialWithTestConfig()
145145
.flatMap() { $0.requestSFTP() }
146-
.tryMap() { try SFTPTranslator(on: $0) }
146+
.tryMap() { t in
147+
let r = try SFTPTranslator(on: t)
148+
root = r
149+
return r
150+
}
147151
.flatMap() { $0.walkTo("/tmp") }
148-
.flatMap() { $0.create(name: "newfile", flags: O_WRONLY, mode: S_IRWXU) }
152+
.flatMap() { $0.create(name: "newfile", mode: S_IRWXU) }
149153
.flatMap() { file in
150154
return gen.read(max: 5 * 1024 * 1024)
151155
.flatMap() { data in
@@ -154,7 +158,7 @@ class SFTPTests: XCTestCase {
154158
}.sink(receiveCompletion: { completion in
155159
switch completion {
156160
case .finished:
157-
expectation.fulfill()
161+
writeExpectation.fulfill()
158162
case .failure(let error):
159163
// Problem here is we can have both SFTP and SSHError
160164
if let err = error as? SSH.FileError {
@@ -169,6 +173,51 @@ class SFTPTests: XCTestCase {
169173

170174
waitForExpectations(timeout: 15, handler: nil)
171175
XCTAssert(totalWritten == 5 * 1024 * 1024, "Did not write all data")
176+
177+
totalWritten = 0
178+
let overwriteExpectation = self.expectation(description: "File Overwritten")
179+
180+
guard let root = root else {
181+
XCTFail("No root translator.")
182+
return
183+
}
184+
185+
let cancelOverwrite = root.walkTo("/tmp")
186+
.flatMap() { $0.create(name: "newfile", mode: S_IRWXU) }
187+
.flatMap() { file in
188+
return gen.read(max: 4 * 1024 * 1024)
189+
.flatMap() { data in
190+
return file.write(data, max: data.count)
191+
}
192+
}.sink(receiveCompletion: { completion in
193+
switch completion {
194+
case .finished:
195+
overwriteExpectation.fulfill()
196+
case .failure(let error):
197+
// Problem here is we can have both SFTP and SSHError
198+
if let err = error as? SSH.FileError {
199+
XCTFail(err.description)
200+
} else {
201+
XCTFail("Crash")
202+
}
203+
}
204+
}, receiveValue: { written in
205+
totalWritten += written
206+
})
207+
208+
waitForExpectations(timeout: 15, handler: nil)
209+
XCTAssert(totalWritten == 4 * 1024 * 1024, "Did not write all data")
210+
211+
let statExpectation = self.expectation(description: "File Stat")
212+
let cancelStat = root.walkTo("/tmp/newfile")
213+
.flatMap { (t: Translator) -> AnyPublisher<FileAttributes, Error> in t.stat() }
214+
.assertNoFailure()
215+
.sink { (stats: FileAttributes) in
216+
XCTAssertTrue(stats[.size] as! Int == 4 * 1024 * 1024)
217+
statExpectation.fulfill()
218+
}
219+
220+
waitForExpectations(timeout: 15, handler: nil)
172221
}
173222

174223
func testWriteToWriter() throws {
@@ -190,7 +239,7 @@ class SFTPTests: XCTestCase {
190239
}.flatMap() { f -> AnyPublisher<Int, Error> in
191240
let file = f as! SFTPFile
192241
return translator!.walkTo("/tmp/")
193-
.flatMap { $0.create(name: "linux.tar.xz", flags: O_WRONLY, mode: S_IRWXU) }
242+
.flatMap { $0.create(name: "linux.tar.xz", mode: S_IRWXU) }
194243
.flatMap() { file.writeTo($0) }.eraseToAnyPublisher()
195244
}.sink(receiveCompletion: { completion in
196245
switch completion {

0 commit comments

Comments
 (0)