Skip to content

Stop reading a dataset once the client is gone - #107

Merged
mgrossmann merged 1 commit into
mainfrom
issue-106-retr-send-return
Aug 18, 2026
Merged

Stop reading a dataset once the client is gone#107
mgrossmann merged 1 commit into
mainfrom
issue-106-retr-send-return

Conversation

@mgrossmann

Copy link
Copy Markdown
Contributor

Fixes #106

The change

The three MVS send loops in ftpd_mvs_retr() (TYPE I, A and E) now break when ftpd_data_send() returns negative, so a transfer stops where the connection did instead of reading the data set to EOF for a client that is no longer there. The JES and UFS paths have always done this; MVS datasets were the outlier.

The completion path told the same untruth and is fixed with it: it replied 250 Transfer completed successfully unconditionally, so an aborted transfer looked like a good one both to the client and in the log, and the byte counters included records that never left the machine. An aborted transfer now answers 426 with the number of bytes that actually went out, and the log line says aborting/ABORTED rather than done. Because the loops break, total is once again the honest count feeding sess->bytes_sent and total_bytes_out.

Verified live on MVS

Build 50F1D64 (this branch), activated into FTPD.V1R0M0.LINKLIB, STC restarted — the startup line confirms the running module: FTPD000I FTPD 1.0.1-DEV (50F1D64).

The case from #106RETR SMPBKUP.SMPPTS (2693 tracks, ~51 MB), client stops reading and then drops the data connection:

before after
session active after client vanished ~90 s 0.7 s
control reply 250 Transfer completed successfully. 426 Transfer aborted: data connection lost after 228672 bytes.
remaining data set read to EOF from DASD not read

F FTPD,SESSIONS right afterwards: 0 / 10. The control connection survives the aborted transfer — QUIT still answered 221 — which is correct: a dead data connection is not a dead session.

Regression, normal transfers still report 250:

  • TYPE I, FTPD.SAMP.XMIT read to completion → 250, 10000 bytes.
  • TYPE A, same data set → 250, 7972 bytes (shorter than binary because ASCII mode trims trailing blanks and adds CRLF — expected).
  • TYPE A on an empty data set → 250, 0 bytes. Checked that this is genuinely an empty data set (GET /zosmf/restfiles/ds/IBMUSER.CURL.MALF returns 0 bytes) and not the new break firing early.

Scope note

ftpd_ufs_retr() (src/ftpd#ufs.c:465) breaks correctly on a send error but then still replies 226 Transfer complete — the same "aborted transfer reported as successful" flaw this PR fixes for MVS datasets. Left alone deliberately to keep this change to one file and one path; happy to follow up if wanted.

The three MVS send loops in ftpd_mvs_retr() discarded the return value
of ftpd_data_send(). When the data connection died mid-transfer the
loop kept going: it read every remaining record from DASD and issued a
doomed send() for each one, until end of dataset. The JES and UFS paths
have always tested the return; MVS datasets were the outlier.

Measured on a RETR of a 2693-track data set whose client stopped
reading and vanished: the session stayed active for about a minute and
a half afterwards, grinding through the rest of the data set for a
client that was not there. Nothing leaked -- it finishes -- but it
finishes by wasting the I/O, holding a worker, and writing one error
line per record, since ftpd_data_send() logs every failure.

All three loops now break on a negative return, so the transfer stops
where the connection did.

The completion path told the same untruth. It replied 250 "Transfer
completed successfully" unconditionally, so an aborted transfer looked
like a good one to the client and in the log, and the byte counters
included records that never left the machine. An aborted transfer now
answers 426 with the number of bytes that actually went out, and the
log line says so too. Because the loops break, total is once again the
honest count.

Fixes #106
@mgrossmann
mgrossmann merged commit 8fd8732 into main Aug 18, 2026
1 check passed
@mgrossmann
mgrossmann deleted the issue-106-retr-send-return branch August 18, 2026 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RETR of an MVS dataset reads to EOF after the client is gone: ftpd_data_send() return ignored in three loops

1 participant