Skip to content

Withdraw the seven -(volume-serial) routes - #358

Merged
mgrossmann merged 7 commits into
mainfrom
issue-336-disable-volser-routes
Aug 25, 2026
Merged

mgrossmann merged 7 commits into
mainfrom
issue-336-disable-volser-routes

Conversation

@mgrossmann

@mgrossmann mgrossmann commented Aug 25, 2026 •

Copy link
Copy Markdown
Contributor

Withdraws the seven -({volume-serial}) routes rather than leaving them
advertising something they do not do. Interim resolution of #336 — the issue
stays open for the implementation, which is now scoped and partly blocked.

What changed

  • src/mvsmf.c — the seven registrations are gone, replaced by a comment
    recording why and what restoring them needs.
  • tests/curl-datasets.sh — six scattered assertions replaced by one
    section.
  • Docs — seven endpoint pages, docs/endpoints/README.md,
    docs/examples.md, and a snapshot note on the route table in
    docs/uss-spec.md.

Deleting the registrations is the whole fix

No fallthrough is possible: {dataset-name} stops at /, ( and ) in
is_pattern_match(), so -(VOL)/X.Y matches none of the cataloged patterns
and reaches the router's own not-found path. It cannot be served as a data set
literally named -(VOL).

The old tests were green the whole time

Six assertions covered these routes and all six passed — against the cataloged
path, because the handlers ignored the volser and did the ordinary thing. The
replacement asserts 404 and reason 7: 7 is the router's not-found, while a
handler that reached a missing data set answers reason 4. Without the reason,
the assertions would go green again for a route that exists and merely failed
to find its target.

Two further checks: the refused PUTs must not have written to the data set or
created the member. A 404 that still wrote would be the old bug wearing a new
status.

The volser used is the correct one for the fixture, taken from the listing.
That is deliberate — routing never reaches a volume lookup, so a right volume
must be refused exactly like a wrong one.

What implementing them actually needs

Half is ours, half is not, which is why this lands as a withdrawal.

Read and write need nothing new. __dsalc already parses VOLSER= and
emits DALVLSER via __txvols(); fopen() opens "DD:ddname" and
"DD:ddname(MEMBER)"; __dscbdv(dsn, vol, &dscb) has always taken a volume
(mvsMF just feeds it __locate()'s answer); and __listvl() resolves a volser
to a cuu should UNIT turn out to be required. That is roughly six fopen()
call sites in dsapi.c.

DELETE and rename cannot be expressed at all. remove() and rename()
reach a data set through IDCAMS DELETE / ALTER, i.e. the catalog — and ALTER
operates on catalog entries, so an uncataloged rename has no spelling. libc370
has no volume-addressed SCRATCH (SVC 29) or RENAME (SVC 30) wrapper. Filed as
mvslovers/libc370#143. Routing them through IDCAMS instead would walk back
into the SYSDSN ENQ escalation of #342, which is why __delmem() exists as a
STOW-under-SHR path in the first place.

One assumption in that plan is measured only as far as the parser: __dsalc
parses VOLSER=, but it is unverified that SVC 99 accepts DALVLSER without
DALUNIT for an uncataloged data set. Noted in the libc370 issue with a
JCL-level probe that needs no build.

There is also a mvsMF-side consequence worth recording before anyone starts:
the catalog-based diagnosis (why_open_failed(), dataset_cataloged()) has to
move to OBTAIN-by-volume on these routes, or an uncataloged data set comes back
with the wrong reason for its 404.

Verification

Measured on mvsdev, HTTPD 4.0.1 (744A836), against a module confirmed to
be this branch — GET /zosmf/test?fn=version returns 25e269d, equal to the
branch HEAD, so the run is not measuring the previous build.

  • tests/curl-datasets.sh — 277 passed, 0 failed, including the sixteen new
    assertions.
  • tests/curl-jobs.sh — 121 passed, 0 failed, 2 skipped. Run because the
    httpd bump touches the jobs API's substrate; the STC's TIOT shows HASPCKPT
    and HASPACE1 allocated, so httpd#256 does not apply to this stand.
  • Console clean afterwards: no abend, no HTTPD908E, server still answering.

Spot-checked on the wire beyond the suite — all three withdrawn shapes return
{"rc":4,"category":6,"reason":7,"message":"Not Found"}, i.e. the router's
not-found and not a handler's reason 4, while the cataloged
GET /zosmf/restfiles/ds/SYS1.MACLIB/member still answers 200.

One note for whoever activates this next: the stand had been upgraded to httpd
4.0.1, which installs into HTTPD.V4R0M1.LINKLIB. The STC read the new library,
MVSMF was not in it, and every /zosmf/* answered 503 with IEA703I 806-04 —
module not found, not the storage case HTTPD908E is usually blamed for. That
also means make deploy cannot recover it, since it uploads and submits through
the mvsMF API itself. tests/jcl/mvsmfact.jcl is corrected here and now records
how to read the STEPLIB out of the running task with /.dm when the jobs API is
the thing that is down.

Also in this branch

Dependencies

  • httpd >=4.0.0 → >=4.0.1 (project.toml + mbt.lock). 4.0.0 was
    withdrawn and is gone from the release list, so the old floor named a version
    that can no longer be fetched. No code changed between the two — the load
    modules are functionally identical and the FMID is unchanged. What 4.0.1
    corrects is the shipped STC procedure, which had stopped allocating
    HASPCKPT and HASPACE1 when HTTPJES2 was removed. Nothing else in the
    server opens them, but a CGI module is dispatched by LINK into HTTPD's own
    task and opens every ddname against the STC's — and mvsMF's jobs API opens
    both by name. Without them the job list and spool retrieval answer 500 while
    a by-jobid path answers 404 for a job that exists; submit is unaffected
    (httpd#256).

    That fix does not arrive through this bump. It lands on a stand by adding
    the two DD statements to the PROCLIB member — and not by copying the new
    procedure, whose STEPLIB names the 4.0.1 library.

  • ufsd >=1.2.0 → >=1.2.1. The lock already resolved to 1.2.1, so
    nothing staged or linked changes; this only stops the declared range from
    admitting a build the lock would never pick. 1.2.1 is the S0C4-at-startup fix
    (ufsd#64), which bites only when the LINKLIB is in the APF list.

One trap met on the way, worth recording: a plain make deps takes the version
straight from mbt.lock without re-checking the declared range
(mbtdeps.py:197). It reported >=4.0.1 -> 4.0.0 and changed nothing. The lock
only moves under make deps ARGS=--update.

Ranking

They were registered against the same handlers as the cataloged forms and
no handler ever read HTTP_volume-serial, so the operand was captured and
discarded: a request naming the wrong volume was answered as if it had
named the right one. Accepting the syntax and ignoring it is worse than
not offering it, because the answer looks correct.

Deleting the registrations is enough to make them 404 and cannot serve
them by another route: {dataset-name} stops at '/', '(' and ')' in
is_pattern_match(), so "-(VOL)/X.Y" matches none of the cataloged patterns
and reaches the router's own not-found path.

Implementing them properly is half ours and half not. Reading and writing
by volume needs nothing new -- __dsalcf() already emits DALVLSER from
VOLSER=, fopen() opens "DD:ddname", and __listvl() resolves a volser to a
device if UNIT turns out to be required. DELETE and rename do: remove()
and rename() reach a data set through the catalog only, and libc370 has no
volume-addressed SCRATCH or RENAME to call instead (mvslovers/libc370#143).
Routing those through IDCAMS would reintroduce the SYSDSN ENQ escalation
of #342.

The six suite assertions that covered these routes passed throughout,
because they were measuring the cataloged path. They are replaced by one
section that asserts 404 *and* reason 7 -- the router's own not-found,
distinct from a handler's reason 4 -- plus a check that the refused writes
created nothing.

Docs corrected in the same edit: seven endpoint pages, the endpoint README,
the cookbook, and a snapshot note in uss-spec.md.

Refs #336
mbt.lock already resolved to 1.2.1, so this changes nothing about what is
staged or linked -- it stops the declared range from admitting a build that
the lock would never pick anyway.

1.2.1 is the S0C4-at-startup fix (ufsd#64): UFSD is link-edited AC(1), so
fetched from an APF-authorized library the job pack area lands in subpool
252 key 0 while the STC runs problem state key 8, and the first store into
its own two counters takes a protection exception. Not observable where the
LINKLIB is outside the APF list and the STC authorizes itself through SVC
244, which is the stock TK4-/TK5 case.
The routes are withdrawn (PR #358), so nothing there answers a client
wrongly any more and the reason it was ranked first is gone. What is left
is a feature request, half of which waits on libc370#143 for a
volume-addressed SCRATCH/RENAME. Tier 1 is empty; #210 leads.

Also records what the withdrawal turned up: the six suite assertions that
covered these routes were green throughout, because they were measuring
the cataloged path.
4.0.0 was withdrawn and is gone from the release list, so the >=4.0.0 floor
named a version that can no longer be fetched.

No code changed between the two: the load modules are functionally
identical and the FMID is unchanged. What 4.0.1 corrects is the shipped STC
procedure, which had stopped allocating HASPCKPT and HASPACE1 when
HTTPJES2 was removed -- on the reasoning that nothing else in the server
opened them. Nothing else in the *server* does, but a CGI module is
dispatched by LINK into HTTPD's own task and opens every ddname against the
STC's, and mvsMF's jobs API opens both by name. Without them the job list
and spool retrieval answer 500 while a by-jobid path answers 404 for a job
that exists; submit is unaffected (httpd#256).

That fix therefore lands on a stand by adding the two DD statements to the
PROCLIB member, not by this bump -- and not by copying the new procedure,
whose STEPLIB names the 4.0.1 library.

The lock had to be rewritten with `make deps ARGS=--update`. A plain
`make deps` takes the version from mbt.lock without re-checking the
declared range (mbtdeps.py:197), so it reported ">=4.0.1 -> 4.0.0" and
changed nothing.
The job copied into HTTPD.LINKLIB. httpd 4.0.1 installs into a library of
its own, so after the upgrade the STC reads HTTPD.V4R0M1.LINKLIB and a copy
into the old name activates nothing -- it succeeds, prints IEB144I, and
changes what the server serves not at all.

The comment block told the reader to establish the name through the jobs
API. That method needs mvsMF, so it is unavailable in the one situation
where the name matters most: a server that cannot load MVSMF at all, which
is exactly what an upgrade into a fresh library produces. Records the /.dm
walk instead -- PSATOLD -> TCBTIO -> TIOT -> the STEPLIB entry's JFCB in
SWA -- which reads HTTPD's own storage from inside HTTPD and needs nothing
but the display module.
@mgrossmann
mgrossmann merged commit 04104cd into main Aug 25, 2026
2 checks passed
@mgrossmann
mgrossmann deleted the issue-336-disable-volser-routes branch August 25, 2026 16:33
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.

1 participant