Repository navigation
v1.1.0
FTPD 1.1.0 is about uploads: how much space they get, what happens when they run out of it, and where the data set lands. Eleven issues closed — #100, #121, #122, #124, #127, #128, #129, #133, #135, #143 and #148 — three of them reported from outside, and one change that touches every installation. It is not a drop-in for 1.0.x: the data set names lose their version qualifier, so the started task's STEPLIB and the APF entry are re-pointed once. Your FTPDPRM0 needs no change — no keyword changed meaning, and the three new ones default to the old behaviour. Built against libc370 1.0.6 and libufs from ufsd 1.3.0 (the build requires ufsd >= 1.3.0; UFSD itself stays optional at runtime, see the guide's prerequisites). The FMID is TFTP110 and it deletes TFTP100.
This release was published once and withdrawn. If you installed the
earlier build, or tried to, read Upgrading from 1.0.x below — the procedure
has changed and is now shorter, not longer.
Read this first: what an operator or a client notices
- The data sets are
FTPD.LINKLIB,FTPD.SAMPLIBandFTPD.AFTPDLODnow (#121), notFTPD.V1R0Mx.*. After the install, scratch the old libraries, re-pointSTEPLIBand update the APF entry — the install job cannot do that for you, and untilSTEPLIBmoves you are still running 1.0.x with no error anywhere. - No migration step before the install (#143). The SYSMOD carries
++VER(Z038) DELETE(TFTP100); SMP transfers ownership of the module itself during theAPPLY. - On an upgrade the
APPLYstep endsCOND CODE 0004onHMA2461 SYSMOD TFTP100 NOT FOUND ON SMPSCDS LIBRARY. Expected; the install job gates itsACCEPTaccordingly. A fresh install endsRC 00. - An upload that runs out of space answers
552, not451(#129, #135).552is permanent: a conforming client no longer retries it. A genuine write error still answers451. - A failed upload no longer leaves its data set behind (#129). A data set FTPD allocated for the transfer is scratched (
FTPD074I); an upload into an existing data set is never scratched. - Unused space is released at close (#128) for data sets FTPD allocated itself.
DEFUNITandDEFVOLUMEare honoured now (#133), and so areSITE UNIT=andSITE VOLUME=. If your parmlib names a volume, new data sets land there from this release on; a volume that is not online is reported at startup (FTPD059W) and ignored.- STOR space is configurable (#100):
DEFPRIMARY,DEFSECONDARYandDEFSPACETYPEinFTPDPRM0,SITE PRIMARY=/SECONDARY=/TRACKS/CYLINDERS/BLOCKSper session. The defaults are the old hardcodedTRK(10,5). - A UFS upload that failed no longer answers
226(#148):552for out of space, the UFS error for any other write failure,426for a broken data connection. Expect the552at about 4.06 MB per file — a property of the UFS on-disk format, not a full disk. - An
IEAAPF00entry forFTPD.LINKLIBdictates its volume. If you already have one, putVOL=SER=on the allocation job before you run it (see Install).
Fixed, and measured on MVS
| before | now | |
|---|---|---|
| an upload past the allocation (#127, #129, #135) | SB37/SD37/SE37, answered 451 — retryable, and every retry met the same full data set |
552, permanent, with advice on how to allocate more |
| STOR space (#100) | SPACE=TRK(10,5), fixed — about 85 tracks, ~4 MB at FB/80/3120 |
DEFPRIMARY/DEFSECONDARY/DEFSPACETYPE, overridable by SITE |
| an unparseable space value | silently 10 in one code path, 100 in another |
refused, with a message naming the default it fell back to |
the session after an SD37 (#129) |
300 seconds to a client timeout; the DD left allocated, so the scratch got IDCAMS RC=8 |
ends promptly; the data set is gone; a retry from a fresh allocation works |
| the data set of a failed upload (#129) | left catalogued, holding the partial upload | scratched, FTPD074I |
| unused space after a small upload (#128) | kept | released at close |
| TYPE A upload, more than 256 bytes per receive (#122, #124) | lines missing, fragments spliced together | 5 rounds of 2 MB: byte-identical every time |
DEFUNIT, DEFVOLUME, SITE UNIT=, SITE VOLUME= (#133) |
parsed and ignored | honoured; configured values checked at startup |
SITE VOLUME= naming a volume that is not mounted (#133) |
session hung, IEF238D outstanding on the console |
a reply to the client |
| a failed UFS upload (#148) | 226 Transfer complete |
552, the UFS error, or 426 |
| data set names (#121) | FTPD.V1R0M2.LINKLIB, … — new names every patch |
FTPD.LINKLIB, … — one APF entry, one IPL, ever |
| replacing the previous FMID (#143) | the withdrawn first build: a manual step before the install, silent when skipped | DELETE(TFTP100) during the APPLY |
| 1.0.x | 1.1.0 | |
|---|---|---|
| FMID | TFTP100 |
TFTP110 |
| Libraries | FTPD.V1R0M2.LINKLIB, … |
FTPD.LINKLIB, … |
| libc370 | 1.0.4 | 1.0.6 |
| libufs (ufsd) | 1.2.2 | 1.3.0 |
Install
Download ftpd-1.1.0-dist.zip (or .tar.gz); the guide and every file you need are inside:
README.md |
the installation guide |
ftpd-1.1.0-load.xmit |
the FTPD load module |
ftpd-1.1.0-samplib.xmit |
the STC procedure and the configuration pattern |
ftpd-1.1.0-alloc.jcl |
allocate the data sets — run once |
ftpd-1.1.0-inst.jcl |
receive, apply, accept |
Upload the two XMITs in binary, submit the allocation job once, then the install job — the only thing to edit is the two data set names you uploaded to. The SYSMOD travels inline in the job, so there is no third file to transfer. FMID is TFTP110.
The documentation in the repository:
- Installation guide — the same file as the package's
README.md; section 2a covers the upgrade from 1.0.x - Removing FTPD
- RAKF setup
One thing to check before you run the allocation job. On MVS 3.8j an IEAAPF00 entry is dsname volser, not a name alone. If you have already added FTPD.LINKLIB to the APF list, that entry does not merely permit a volume — it dictates one, and the allocation job says UNIT=SYSDA with no VOL=SER. Put the volume in:
//LINKLIB DD DSN=FTPD.LINKLIB,DISP=(NEW,CATLG,DELETE),
// UNIT=SYSDA,VOL=SER=your-apf-volume,
Get it wrong and you have a library that exists, holds the right module, passes every check — and is not authorised. Nothing reports it, because FTPD falls back to the SVC 244 route and starts anyway. The startup message is the only place the two are distinguishable: on a system where you added an APF entry, FTPD008I AUTHORIZED BY SVC (MODULE KEY 8) means the entry is not covering the library. Found the hard way during an install of UFSD 1.3.0 on a development system.
The two caveats from 1.0.x still apply to the shipped material:
- On TK5,
SYS2.PARMLIBdoes not exist — putFTPDPRM0inSYS1.PARMLIBand start with/S FTPD,D='SYS1.PARMLIB'. - The two JES DDs in the procedure,
HASPCKPTandHASPACE1, carry the unit and volume of the system FTPD was built on. Point them at your own JES2 checkpoint and spool, or delete both if you do not want the JES interface. Both are required together: withHASPACE1missing, every JES command answers451.
Not using SMP? Receive ftpd-1.1.0-load.xmit into a library and point the procedure's STEPLIB at it.
Upgrading from 1.0.x
Install it. The FMID takes care of itself — that is #143, described below.
What the install job cannot do for you is the data sets, and the reason is worth thirty seconds. SMP records the ddname an element was installed through, never the data set name. Both FTPD.V1R0M2.LINKLIB and FTPD.LINKLIB end in LINKLIB, so both installs use ddname LINKLIB — and when 1.1.0's APPLY deletes the old load module it resolves that ddname in its own job, which points at the new library. FTPD.V1R0M2.LINKLIB is never opened.
So after the install:
- Scratch the old libraries. Look them up rather than assuming the patch level — ISPF 3.4 on
FTPD.*, orLISTCAT LEVEL(FTPD). Expect three:FTPD.V1R0M?.LINKLIB,.AFTPDLOD,.SAMPLIB. They are invisible to SMP now, so nothing will ever mention them again. - Repoint the started task's
STEPLIBatFTPD.LINKLIB. This is the step that decides whether you are running 1.1.0 at all. If you skipped step 1 the old library still exists and still holds a working module: FTPD starts, serves, and logs no error — it is simply the old one. - Update the APF entry. The last time it will need changing, because the name no longer carries a version. One IPL ever, not one per release.
Stop FTPD before you begin: FTPD.LINKLIB is the library it loads from.
One return code that looks wrong and is not. On an upgrade the APPLY step ends COND CODE 0004, not 0000:
HMA2270 APPLY PROCESSING SUCCESSFULLY COMPLETED FOR SYSMOD TFTP110
HMA2461 SYSMOD TFTP100 NOT FOUND ON SMPSCDS LIBRARY
HMA2050 APPLY PROCESSING COMPLETED - HIGHEST RETURN CODE IS 04
HMA2461 is SMP noting that the deleted level has no backup entry to keep — it never will, because it was deleted rather than superseded. HMA2270 is the line that says the APPLY worked. The install job expects this and gates its ACCEPT accordingly. A fresh install has nothing to delete and ends RC 00.
Your FTPDPRM0 and your RAKF definitions are unaffected. No keyword changed meaning; the three new ones default to the old behaviour.
What is running
FTPD005I stamps the library the module was actually linked against, so a deploy that did not take cannot hide:
FTPD000I FTPD 1.1.0 (23011CD) STARTING
FTPD005I LIBC370 1.0.6
If that second line still says 1.0.4, the STC is running the 1.0.2 module.
Full changelog
Why the first build was withdrawn (#141, #143)
The withdrawn build carried a migration step that had to be run by hand before the install job, and warned about it at length: SMP keys element ownership on the module name, so a new FMID could not replace an element the old one owned. Skip the step and RECEIVE, APPLY and ACCEPT all reported success over an empty target library.
meltdown03 hit it within the day:
I get an empty
FTPD.LINKLIBdataset only after running theFTPDALCand
FTPDINSjobs completed with CC 0000.
followed by "looks like I did not read the instructions and need to do extra steps" — which is the correct diagnosis and exactly the wrong shape for an install job. A step that is mandatory, silent when skipped, and documented somewhere else is a step that will be skipped.
It is gone. The SYSMOD now carries ++VER(Z038) DELETE(TFTP100) and SMP transfers the ownership itself during the APPLY. Nothing has to be prepared and nothing has to be removed first. Measured on a development system both ways, with throwaway ids and throwaway module names, in the shape this release actually has — same ddnames, different data sets:
HMA2240 SUCCESSFULLY DELETED LMOD FTPD ON LINKLIB LIBRARY
HMA2380 COPY SUCCESSFUL - MOD=FTPD - LMOD=FTPD - LIBRARY=LINKLIB - ...
HMA2270 APPLY PROCESSING SUCCESSFULLY COMPLETED FOR SYSMOD TFTP110
That came with a policy change: one FMID per release, each deleting its predecessor. TFTP110 is 1.1.0 and nothing else. A SYSMOD id is seven characters, three of which are the version digits, so no version component can ever exceed 9.
Added
-
Configurable STOR space (#100). Three new
FTPDPRM0keywords, named after the z/OSFTP.DATAstatements they mirror:DEFPRIMARY=10 1-16777215 DEFSECONDARY=5 0-16777215; 0 means no secondary extents DEFSPACETYPE=TRACK TRACK | CYLINDER | BLOCK (TRK/CYL/BLK too)The defaults are deliberately the old hardcoded numbers, so an existing installation that adds nothing to its parmlib behaves exactly as before. A system that receives large files wants something like
DEFSPACETYPE=CYLINDER,DEFPRIMARY=10,DEFSECONDARY=10— 160 cylinders, about 2400 tracks. Note that the 16-extent limit (1 primary + 15 secondary) and the single volume stay either way, so a largerPRIMARYbuys more than a largerSECONDARY. Each is validated at startup and reported in theCONFIGdump:FTPD058I DEFPRIMARY=10 DEFSECONDARY=5 DEFSPACETYPE=TRACKSITE PRIMARY=,SITE SECONDARY=andSITE TRACKS/CYLINDERS/BLOCKSoverride them for the rest of the session, and a bare keyword resets to what the parmlib configured rather than to a built-in constant. The fallbacks that used to paper over an unparseable value are gone: a bad number is refused with a message naming the default it fell back to, instead of silently becoming10in one code path and100in another — both of which were really in 1.0.2.Keyword Effect SITE PRIMARY=nprimary space for the next data set SITE SECONDARY=nsecondary extent size; 0for noneSITE TRACKS/CYLINDERS/BLOCKSthe unit the two numbers are in; BLOCKScounts inBLKSIZESITE UNIT=/SITE VOLUME=where it is placed — honoured since this release SITE RECFM=/LRECL=/BLKSIZE=as before -
Unused space is released at close (#128). A data set that got
CYL(10,10)and received 200 KB no longer keeps the ten cylinders.RLSEis what lets a site raiseDEFPRIMARYwithout every small upload permanently occupying the generous default. It needed a library change — libc370#167,__txrlse()was present and never called — and applies only to data sets FTPD allocated itself.
Changed
-
Data set names without a version (#121). Rob Prins: "Please remove the mid qualifier of the data set names. E.g.: FTPD.LINKLIB is enough." They used to carry the full version —
FTPD.V1R0M1.LINKLIBfor 1.0.1,FTPD.V1R0M2.LINKLIBfor 1.0.2 — while the FMID only moved with the minor release. So every patch landed in a fresh set of data sets that nothing pointed at, left the predecessor's standing, and needed the STC'sSTEPLIBre-edited before it became live at all. On the APF route it also needed a newIEAAPF00entry and an IPL, every time. Now:FTPD.LINKLIB the load module (APF this one, once, forever) FTPD.SAMPLIB the STC procedure and the configuration pattern FTPD.AFTPDLOD SMP's distribution library FTPD.FTPDLOAD staging; the install job scratches it when it is doneThe APF entry now needs one IPL ever, not one per release, because the entry names a data set that no longer changes.
-
TFTP110deletesTFTP100, one FMID per release (#143) — see Why the first build was withdrawn above. -
Out of space is a permanent failure, and says so (#129, #135). The reply is now
552, the code RFC 959 reserves for exceeded storage allocation, and it tells the client what to do about it:552 Requested file action aborted: out of space on the data set. Allocate more space with SITE PRIMARY/SECONDARY and CYLINDERS, or ask the administrator to raise DEFPRIMARY.A
552is permanent: clients do not retry it. A genuine write error — bad track, device failure — still answers451with the byte count actually received, because that one is worth retrying. The two were one reply before. -
The 256-byte receive cap is gone again (#124), because libc370#154 fixed the defect behind it in the library and 1.0.6 carries that fix. One cap is enough, and the library's is in the right place — see #122 below.
Fixed
-
An upload that ran out of space and lied about it (#127). Rob Prins reported an
x37ABEND during an upload, with the diagnosis attached: "It looks like that the allocation is too small. The maximum size is 85 tracks (TRK(10,5))." — and one more sentence that turned out to be the harder half: "And please don't retry after the abendx37." He was right on both counts, and the second one was the real defect.STORallocatedSPACE=TRK(10,5)with no way to change it, so an upload past ~85 tracks — about 4 MB at FB/80/3120 — hit anSB37/SD37/SE37. FTPD then answered451, which RFC 959 defines as a transient condition: the client is entitled to retry, and a well-behaved one does. The retry met the same too-small data set, which was still catalogued, still holding the partial upload, and still exactly as full. Every attempt failed the same way, and the data set nobody could use stayed on DASD. Four changes came out of that: #100, #128, #129 and #135. -
The failed upload no longer leaves a data set behind (#129). If FTPD allocated the data set for this transfer and the transfer failed, FTPD scratches it:
FTPD074I SCRATCHED USER.UPLOAD.DATA AFTER A FAILED TRANSFEROnly data sets FTPD created in that transfer — an upload into an existing data set is never scratched, whatever happens to it.
-
The recovery survives the close (#129). The first fix looked complete and was not. Under an
SD37,fclose()never returned: stdio flushed the pending buffer into a DCB whose last write had already abended, and abended again inside the recovery routine. The recovery had already replied, so nothing was visibly wrong in the log — but the data connection was never closed, and the client sat waiting for a socket that would never shut down. Measured: 300 seconds to a client timeout, and the DD left allocated to the address space, so the scratch that followed gotIDCAMS RC=8. The fix is an ordering — close the data connection before touching the FILE — plus two library changes: libc370#168 gave stdio a way to abandon a FILE whose last write failed (__fabandon()), and libc370#176 stopped stdio re-driving a WRITE on a dead DCB. With 1.0.6 the x37 no longer reaches FTPD as an ABEND at all: it arrives asferror()witherrno == ENOSPC, which is what #135 is about — the mechanism changed underneath the 552 and the reply had to move with it. The end-to-end test is in the suite: upload past the allocation, expect552, expect the session to end promptly rather than in five minutes, expect the data set gone, and expect a retry from a fresh allocation to work. -
Uploaded text arriving with lines missing (#122). meltdown03 uploaded a JCL member in ASCII mode and got it back damaged — not garbled characters, but missing lines, with a fragment of one line spliced onto another:
000152 //SYSUT2 DD DSN=&&RXCAT,DISP=(,PASS),UNIT=VIO, 000153 // SPAORACF TO REMOVE RACF INDICATOR <<<< something bad happened hereThis is the X'75' restart defect, and it is not in FTPD. The socket read copies in 256-byte segments and the instruction is restartable; on a partial copy the count and the pointer disagree, and bytes are lost from the middle of the buffer. It only shows on receives larger than 256 bytes per call, which is why binary FB/80 transfers (80 bytes a call) never reproduced it and TYPE A transfers (4096) did. FTPD shipped a workaround first — cap every data-connection receive at one segment — and then removed it again in #124, because the library fixed it. Re-measured after the removal, TYPE A, 5 rounds of 2 MB: byte-identical every time. The send side is not fixed (#137) — see Still open.
-
DEFUNITandDEFVOLUMEdid nothing (#133). Both were parsed, stored, dumped by/F FTPD,CONFIG— and never read by anything.SITE UNIT=andSITE VOLUME=were accepted and equally ignored. Every data set FTPD created landed wherever the system chose, which for most systems means a public pack rather than the one the administrator named. They are honoured now, so you can keep uploads off a system pack. Because a wrong value here is expensive, the configured ones are checked once at startup against the volumes actually online:FTPD059W DEFVOLUME=PUB009 IS NOT ONLINE -- NEW DATA SETS WILL BE PLACED BY THE SYSTEM FTPD060W DEFUNIT=3350 DOES NOT MATCH DEFVOLUME=PUB000 (3390) -- DEFUNIT IGNOREDA misconfigured volume is reported and ignored rather than failing every upload. A volume a client names with
SITE VOLUME=is not silently corrected — but it is validated, because of what testing it turned up: SVC 99 asks the operator for an unmounted volume instead of failing. A singleSITE VOLUME=NOVOL9hung the session and leftIEF238Doutstanding on the console, waiting for a reply nobody knew to give. FTPD now checks the volume before allocating and setsS99NOMNT, so a bad name comes back as a reply to the client rather than a message to the operator. (libc370#181 tracks the library half.) -
A UFS upload that failed still said it worked (#148). Found while relinking against ufsd 1.3.0, which fixed
libufsreporting fewer bytes than a failed write had really moved — and FTPD turned out to be throwing the number away regardless.ftpd_ufs_stor()checked nothing.ufs_fwrite()'s count went unread,ufs_ferror()was never asked, and a data connection that broke left the receive loop exactly the way a completed transfer did. The reply was226 Transfer completein all three cases. A226is the client's cue to delete its copy — which, on a failed upload, is the only whole one left. There are three answers now:what happened reply out of space 552— permanent, so a conforming client does not retryany other write failure the UFS error, mapped as elsewhere the data connection broke 426, per RFC 959clean end of data 226, as beforeThis is the argument #129 made for MVS data sets, applied to the path that never got it. Expect the
552more often than "disk full" suggests. UFS implements no double or triple indirect blocks, so a single file cannot exceed 16 direct plus 1024 single-indirect blocks — about 4.06 MB at the usual 4096-byte block size, on an otherwise empty filesystem. That is an ordinary size for a file someone uploads, and it is a per-file ceiling rather than a full volume, so the reply does not claim the filesystem is full. It is a documented property of the UFS on-disk format, not a bug in this release. Only the MVS side ofSTORwas covered before, and only since 1.1.0. UFS uploads have been answering226over truncated files since the UFS support went in.
Still open
The identity model is unchanged from 1.0.0, and so is the warning that came with it. The STC's resting identity is still STC/STCGROUP, which holds ALTER on every data set (#97); JES access still has no authorization model, JESOWNER being a client-set display filter rather than a control (#90); the ACEE switch is still not fully serialized against concurrent sessions (#64); and catalog calls still run under the STC identity rather than the session's (#123). Keep the FTPD identity scoped to what the started task itself needs — never give it broad data set authority.
The X'75' defect is only fixed on the receive side (#137). Every download that sends more than 256 bytes per call has the same exposure #122 had, and the library fix for it is still an open design question (libc370#160) — capping the send changes what every caller sees per call, so it needs measuring before patching.
The remaining known limits: a UFS create that fails for want of inodes still answers 452 although a full inode table lasts until the volume is reformatted (#151); shutdown can take up to ~5 seconds to notice idle sessions (#66); a NULL from __listpd()/__listds() is still read as an empty listing rather than checked for a storage shortage (#118); and a handful of z/OS SITE keywords are still missing (#130).
Thank you, Rob Prins and meltdown03 — three of the eleven issues in this release started as a report from outside and two came with the diagnosis already attached, and the report that caused the withdrawal (#141) came from someone installing the build on the day it shipped. #148 came out of comparing notes with the UFSD side while relinking against 1.3.0.
Full Changelog: v1.0.2...v1.1.0