Skip to content

Ship the document root as a UFS disk image, uploaded with IND$FILE - #253

Merged
mgrossmann merged 4 commits into
release-4.0.0-smp-distributionfrom
issue-252-webroot-image
Aug 24, 2026
Merged

Ship the document root as a UFS disk image, uploaded with IND$FILE#253
mgrossmann merged 4 commits into
release-4.0.0-smp-distributionfrom
issue-252-webroot-image

Conversation

@mgrossmann

@mgrossmann mgrossmann commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #252.

static/index.html was in the repository and in no release artifact: the
package installed a server whose DOCROOT had nothing behind it.

The transport, and why it is neither SMP nor XMIT

  • SMP has no element type for a DSORG=PS/RECFM=U image, and site content is
    what an APPLY must never touch.
  • TSO RECEIVE allocates its own target and refuses to merge into an existing
    dataset -- a first-install-only transport for something an operator replaces.
    (xmit370 could not represent the format either: directory -> PDS,
    --recfm fb|f, LRECL 80.)

So the image ships as a plain file in the archive and goes up over IND$FILE,
which needs nothing on the target beyond a 3270 session -- no mvsMF, no ftpd.

What is in the change

make webroot builds build/webroot/httpd-webroot.img from static/ with a pinned ufsd-utils, fetched into .mbt/tools/ when it is not on PATH. 1 MB = 256 blocks; --owner/--group are set so a release artifact carries no build-machine userid. package and dist depend on it. Under build/, not dist/: the release workflow publishes dist/* as Release assets, and the image belongs in the archive rather than beside it under a version-less name
project.toml [distribution] extra puts the image in the zip/tar.gz -- verified, the archive is 6 files now
samplib/httpwebr allocates HTTPD.@VRM@.WEBROOT.UFS: SPACE=(4096,256), RECFM=U BLKSIZE=4096, primary extent only. Substitution verified in the packed XMIT (HTTPD.V4R0M0D.WEBROOT.UFS)
docs/installation.md new section 9 -- allocate, upload, mount, verify, replace. Sections 9-11 renumbered to 10-12, anchors and step references updated
smp-todo.md O3 rewritten: U1-U6 replaced by the decided path, only the SHA256 item left

Measured, not assumed

  • Blocking: RECFM=U buffers to BLKSIZE through __fputc (libc370
    @@fputc.c:24), so BLKSIZE(4096) writes whole 4096-byte blocks. The 1 MB
    image is 256 of them, no remainder.
  • Options: binary is IND$FILE's default; RECFM then defaults to U
    (ind_file370 indparse.c:249).
  • Pre-allocation: IND$FILE allocates an existing dataset DISP=SHR and takes
    its DCB (indmain.c:190, :273). That is why the documented upload carries
    no options at all -- and why it does not depend on which IND$FILE build a
    system has.
  • The trap: the same DISP=SHR rewrites a dataset UFSD has mounted.
    UNMOUNT PATH=/www is a numbered step in the doc, not a footnote.
  • Detection: a BLKSIZE mismatch surfaces at MOUNT as UFSD062E SUPERBLOCK VALIDATION FAILED (ufsd ufsd#sbl.c:69), not as a corrupt page.
  • / serves it: httpget.c:68 appends index.html to a directory request.

Encoding: nothing to configure, and no HTTPPRM0 change

http_send_file() translates UFS files with the hard-coded IBM-1047 table,
independent of CODEPAGE= (src/httpfile.c:70) -- exactly what ufsd-utils
writes. Measured end to end: ufsd-utils cp -> stored bytes -> ibm1047_etoa
returns static/index.html byte for byte, UTF-8 sequences included. Over all
256 byte values only 0x85 and 0xF7 fail to round-trip; the Makefile says so.

CODEPAGE=IBM1047 in the shipped HTTPPRM0 was considered and rejected on
measurement
. cc370 compiles literals in CP037 (common/src/mvs370.c), and
CODEPAGE= governs those: under IBM1047 a [ compiled as 0xBA leaves as
0xDD, which would break every mvsMF array response -- the primary route in the
shipped configuration -- plus HTTPD's own login form and error pages. Checked
against a live server: [ currently arrives as 0x5B.

Verification

  • make package -- clean, 6 files in the archive, image included
  • make dist -- re-renders with the image present
  • image inspected with ufsd-utils ls -l / info: 256 blocks, IBMUSER/SYSPROG,
    index.html 3573 bytes
  • samplib XMIT listed and extracted: HTTPWEBR present, @VRM@ substituted
  • ufsd-utils bootstrap exercised from an empty .mbt/tools/

Not verified on MVS: the upload itself. IND$FILE is present on the reference
system (SYS2.CMDLIB), but a 3270 transfer is interactive -- every claim about
its behaviour above comes from the ind_file370 sources.

static/index.html was in the repository and in no release artifact, so the
package installed a server whose DOCROOT had nothing behind it.

A UFS disk cannot travel the way the rest of the package does, and not for
lack of tooling. SMP has no element type for a DSORG=PS/RECFM=U image and
must never touch site content; TSO RECEIVE allocates its own target and
refuses to merge into an existing dataset, which makes it a
first-install-only transport for something an operator replaces. So the
image ships as a plain file in the archive and goes up over IND$FILE, which
needs nothing on the target beyond a 3270 session.

  make webroot    builds dist/httpd-webroot.img from static/ with a pinned
                  ufsd-utils (fetched into .mbt/tools/ when absent, because
                  release CI runs the shared workflow and cannot install
                  anything of its own).  package and dist depend on it, and
                  [distribution] extra puts it in the zip/tar.gz.
  samplib/httpwebr  allocates HTTPD.@vrm@.WEBROOT.UFS -- SPACE=(4096,256),
                  RECFM=U BLKSIZE=4096, primary extent only.
  installation.md 9  the operator's side: allocate, upload, mount, verify,
                  and unmount before replacing.

Measured rather than assumed:

  Blocking      RECFM=U buffers to BLKSIZE through __fputc (libc370
                @@fputc.c:24), so BLKSIZE(4096) writes whole 4096-byte
                blocks; the 1 MB image is 256 of them with no remainder.
  Options       binary is IND$FILE's default and RECFM then defaults to U
                (ind_file370 indparse.c:249).
  Pre-allocate  IND$FILE allocates an existing dataset DISP=SHR and takes
                its DCB (indmain.c:190, :273), so the documented upload
                needs no options and does not depend on which IND$FILE
                build a system carries.
  The trap      that same DISP=SHR rewrites a dataset UFSD has mounted, so
                UNMOUNT is a numbered step rather than a footnote.
  Detection     a BLKSIZE mismatch surfaces at MOUNT as UFSD062E
                (ufsd ufsd#sbl.c:69), not as a corrupt page.
  Encoding      nothing to configure: http_send_file() translates UFS files
                with the hard-coded IBM-1047 table independent of CODEPAGE=
                (httpfile.c:70), which is what ufsd-utils writes.  The
                round trip was measured byte for byte over the whole file
                and over all 256 values; only 0x85 and 0xF7 do not survive.

The version in the dataset name settles what smp-todo called U6: a shipped
image cannot land on a webroot the site built for itself.

Fixes #252
The ranking said nothing open blocks 4.0.0, which was true and unhelpful:
the release work is in smp-todo.md (the FMID check and the dry run), not
in the tracker at all. Say so, and record that O3 closed with #252.
…ne changes

The prerequisite list was files only.  Adding one is covered -- make expands
the wildcard when it parses the Makefile, so the new file is in the list and
is newer than the image -- but deleting one is not: the image stays newer
than everything still there, and the archive would go on shipping the file
that was removed.  A directory's mtime moves when an entry is added or
removed, so the directories join the file list.

Measured both ways with the image backdated so the comparison is not decided
by whole-second timestamps: adding rebuilds, deleting rebuilds and drops the
file from the image, an unchanged tree does nothing.

Also two notes rather than behaviour: why the webroot dataset is the one name
with a fourth qualifier, and that the emulator types IND$FILE into whatever
field the cursor is on -- it does not find a command line for you.
dist/ is not a scratch directory: the release workflow publishes every file
in it as a GitHub Release asset (`gh release create ... dist/*`).  An image
built there therefore shipped twice -- once inside the archive, where it
belongs beside the README and the jobs, and once on its own as
`httpd-webroot.img`, a name with no version in it sitting between
httpd-<version>-load.xmit and httpd-<version>-dist.zip.

Nothing else moves: `extra` takes the basename, so the file inside the
archive is called what it was called before, and the installation guide is
unchanged.
@mgrossmann
mgrossmann merged commit fa65b47 into release-4.0.0-smp-distribution Aug 24, 2026
1 check passed
@mgrossmann
mgrossmann deleted the issue-252-webroot-image branch August 24, 2026 18:59
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