Skip to content

Commit faca3bc

Browse files
yosuke-wolfsslejohnstown
authored andcommitted
wolfsftp: keep the remote file when resuming a put
- wolfSSH_SFTP_Put() adds WOLFSSH_FXF_TRUNC to the destination open only when the write offset is zero. - STATE_PUT_LOOKUP_OFFSET clears a saved offset when the local file is no larger than it. - A new STATE_PUT_STAT_REMOTE stats the destination when the saved offset is nonzero, and clears the offset unless the reported size matches it exactly, or the stat returns WS_SFTP_STATUS_NOT_OK or WS_PERMISSIONS. Other stat failures re-save the offset and move to STATE_PUT_CLEANUP; a want-read or want-write keeps the state. WS_SFTP_PUT_STATE carries the attributes both states read. - The Windows server open maps WOLFSSH_FXF_CREAT to OPEN_ALWAYS and reserves CREATE_ALWAYS for an open that also asked for WOLFSSH_FXF_TRUNC; the disabled TRUNCATE_EXISTING mapping is dropped. - tests/api.c adds test_wolfSSH_SFTP_PutResume(), five cases over the resume paths, built where the hosted file wrappers are available. Issue: F-11659
1 parent 7595a95 commit faca3bc

2 files changed

Lines changed: 342 additions & 6 deletions

File tree

src/wolfsftp.c

Lines changed: 76 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ typedef struct WS_SFTP_GET_STATE {
283283
enum WS_SFTP_PUT_STATE_ID {
284284
STATE_PUT_INIT,
285285
STATE_PUT_LOOKUP_OFFSET,
286+
STATE_PUT_STAT_REMOTE,
286287
STATE_PUT_OPEN_REMOTE,
287288
STATE_PUT_OPEN_LOCAL,
288289
STATE_PUT_WRITE,
@@ -307,6 +308,7 @@ typedef struct WS_SFTP_PUT_STATE {
307308
int rSz;
308309
byte handle[WOLFSSH_MAX_HANDLE];
309310
byte r[WOLFSSH_MAX_SFTP_RW];
311+
WS_SFTP_FILEATRB attrib;
310312
} WS_SFTP_PUT_STATE;
311313

312314

@@ -2654,11 +2656,13 @@ int wolfSSH_SFTP_RecvOpen(WOLFSSH* ssh, int reqId, byte* data, word32 maxSz)
26542656
}
26552657
if (reason & WOLFSSH_FXF_WRITE) {
26562658
desiredAccess |= GENERIC_WRITE;
2657-
if (reason & WOLFSSH_FXF_CREAT)
2658-
creationDisp |= CREATE_ALWAYS;
2659+
if (reason & WOLFSSH_FXF_CREAT) {
2660+
if (reason & WOLFSSH_FXF_TRUNC)
2661+
creationDisp = CREATE_ALWAYS;
2662+
else
2663+
creationDisp = OPEN_ALWAYS;
2664+
}
26592665
#if 0
2660-
if (reason & WOLFSSH_FXF_TRUNC)
2661-
creationDisp |= TRUNCATE_EXISTING;
26622666
if (reason & WOLFSSH_FXF_EXCL)
26632667
creationDisp |= CREATE_NEW;
26642668
if (reason & WOLFSSH_FXF_APPEND)
@@ -9988,6 +9992,7 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume,
99889992
WS_STATUS_CB* statusCb)
99899993
{
99909994
WS_SFTP_PUT_STATE* state = NULL;
9995+
word32 openFlags;
99919996
int ret = WS_SUCCESS;
99929997
int sz;
99939998

@@ -10029,8 +10034,68 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume,
1002910034
if (resume) {
1003010035
/* check if offset was stored */
1003110036
wolfSSH_SFTP_GetOfst(ssh, from, to, state->pOfst);
10037+
10038+
/* a saved offset is only usable while the local file
10039+
* still holds bytes past it */
10040+
if (state->pOfst[0] > 0 || state->pOfst[1] > 0) {
10041+
WMEMSET(&state->attrib, 0, sizeof(state->attrib));
10042+
if (SFTP_GetAttributes(ssh->fs, from, &state->attrib,
10043+
1, ssh->ctx->heap) == WS_SUCCESS
10044+
&& (state->attrib.flags
10045+
& WOLFSSH_FILEATRB_SIZE)) {
10046+
if (state->attrib.sz[1] < state->pOfst[1]
10047+
|| (state->attrib.sz[1] == state->pOfst[1]
10048+
&& state->attrib.sz[0]
10049+
<= state->pOfst[0])) {
10050+
WLOG(WS_LOG_SFTP, "Local file too short");
10051+
state->pOfst[0] = 0;
10052+
state->pOfst[1] = 0;
10053+
}
10054+
}
10055+
}
1003210056
}
1003310057
state->handleSz = WOLFSSH_MAX_HANDLE;
10058+
state->state = STATE_PUT_STAT_REMOTE;
10059+
FALL_THROUGH;
10060+
10061+
case STATE_PUT_STAT_REMOTE:
10062+
WLOG(WS_LOG_SFTP, "SFTP PUT STATE: STAT REMOTE");
10063+
/* a saved offset is only usable if the destination still
10064+
* holds exactly that many bytes */
10065+
if (state->pOfst[0] > 0 || state->pOfst[1] > 0) {
10066+
ret = wolfSSH_SFTP_STAT(ssh, to, &state->attrib);
10067+
if (ret != WS_SUCCESS) {
10068+
if (NoticeError(ssh)) {
10069+
return WS_FATAL_ERROR;
10070+
}
10071+
if (ret != WS_SFTP_STATUS_NOT_OK &&
10072+
ret != WS_PERMISSIONS) {
10073+
WLOG(WS_LOG_SFTP, "Error checking remote file");
10074+
/* put the offset back so a retry can resume */
10075+
if (wolfSSH_SFTP_SaveOfst(ssh, from, to,
10076+
state->pOfst) != WS_SUCCESS) {
10077+
WLOG(WS_LOG_SFTP, "Unable to store offset");
10078+
}
10079+
state->state = STATE_PUT_CLEANUP;
10080+
continue;
10081+
}
10082+
/* NOT_OK collapses every status but a permission
10083+
* failure, so both mean the destination could not be
10084+
* verified and is safe to overwrite */
10085+
WLOG(WS_LOG_SFTP, "Remote file unusable, restarting");
10086+
state->pOfst[0] = 0;
10087+
state->pOfst[1] = 0;
10088+
ssh->error = WS_SUCCESS;
10089+
ret = WS_SUCCESS;
10090+
}
10091+
else if ((state->attrib.flags & WOLFSSH_FILEATRB_SIZE) == 0
10092+
|| state->attrib.sz[0] != state->pOfst[0]
10093+
|| state->attrib.sz[1] != state->pOfst[1]) {
10094+
WLOG(WS_LOG_SFTP, "Size does not match, starting over");
10095+
state->pOfst[0] = 0;
10096+
state->pOfst[1] = 0;
10097+
}
10098+
}
1003410099
state->state = STATE_PUT_OPEN_LOCAL;
1003510100
FALL_THROUGH;
1003610101

@@ -10104,9 +10169,14 @@ int wolfSSH_SFTP_Put(WOLFSSH* ssh, char* from, char* to, byte resume,
1010410169

1010510170
case STATE_PUT_OPEN_REMOTE:
1010610171
WLOG(WS_LOG_SFTP, "SFTP PUT STATE: OPEN REMOTE");
10172+
/* only truncate when starting from the beginning of the file */
10173+
openFlags = WOLFSSH_FXF_WRITE | WOLFSSH_FXF_CREAT;
10174+
if (state->pOfst[0] == 0 && state->pOfst[1] == 0) {
10175+
openFlags |= WOLFSSH_FXF_TRUNC;
10176+
}
10177+
1010710178
/* open file and get handle */
10108-
ret = wolfSSH_SFTP_Open(ssh, to, (WOLFSSH_FXF_WRITE |
10109-
WOLFSSH_FXF_CREAT | WOLFSSH_FXF_TRUNC), NULL,
10179+
ret = wolfSSH_SFTP_Open(ssh, to, openFlags, NULL,
1011010180
state->handle, &state->handleSz);
1011110181
if (ret != WS_SUCCESS) {
1011210182
if (ssh->error == WS_WANT_READ ||

0 commit comments

Comments
 (0)