Skip to content

Fix strict-root mode for files larger than 2 GiB on 32-bit platforms#61

Merged
xakep666 merged 2 commits into
xakep666:masterfrom
Hirador:fix-largefile-32bit
Jul 19, 2026
Merged

Fix strict-root mode for files larger than 2 GiB on 32-bit platforms#61
xakep666 merged 2 commits into
xakep666:masterfrom
Hirador:fix-largefile-32bit

Conversation

@Hirador

@Hirador Hirador commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Problem

In strict-root mode the server cannot open any file larger than 2 GiB on
32-bit platforms. The open fails with:

openat <path>: value too large for defined data type   (EOVERFLOW)

Because every PS3 ISO exceeds 2 GiB, strict-root mode is effectively unusable
for PS3 streaming on 32-bit NAS hardware (e.g. armv5tel / armv7 units). The
relaxed root works only by accident: it uses os.Open, which adds
O_LARGEFILE.

Root cause

strict-root uses os.Root, whose internal openat
(internal/syscall/unix.Openat) is called without O_LARGEFILE. On 32-bit
kernels the kernel then rejects files whose size does not fit in a 32-bit
off_t with EOVERFLOW. os.Open / syscall.Open do OR in O_LARGEFILE,
which is why the relaxed root is unaffected.

Fix

Wrap *os.Root so its Open/Create forward O_LARGEFILE through
os.Root.OpenFile, which passes the flag verbatim to openat. This preserves
all of os.Root's symlink-safe, traversal-safe behaviour. O_LARGEFILE is 0
on 64-bit and on non-Linux platforms, so the change is a no-op there.

  • pkg/fs/strict_root.goStrictSystemRoot wrapper
  • pkg/fs/largefile_linux.go / largefile_other.go — platform constant
  • cmd/ps3netsrv-go/server.go — use the wrapper for strict-root

Testing

  • Verified a >2 GiB PS3 ISO now opens and streams in strict-root mode on a
    32-bit armv5tel QNAP NAS (previously failed with EOVERFLOW).
  • Cross-compiles cleanly for linux/arm, linux/amd64, darwin/arm64,
    windows/amd64; go vet clean.

In strict-root mode files are opened through os.Root, whose internal openat
does not set O_LARGEFILE. On 32-bit kernels this makes opening any file larger
than 2 GiB fail with EOVERFLOW ("value too large for defined data type").
Since every PS3 ISO exceeds 2 GiB, strict-root mode cannot stream PS3 games on
32-bit hosts (e.g. armv5tel/armv7 NAS units); the relaxed root works only
because it uses os.Open, which does add O_LARGEFILE.

Wrap *os.Root so its Open/Create forward O_LARGEFILE via os.Root.OpenFile
(which passes the flag verbatim to openat), preserving os.Root's symlink-safe
traversal. O_LARGEFILE is 0 on 64-bit and non-Linux platforms, so the change
is a no-op there.
Comment thread pkg/fs/largefile_linux.go Outdated
Comment thread pkg/fs/largefile_linux.go Outdated
@xakep666

Copy link
Copy Markdown
Owner

Thank you for this fix. I think this should be filed as an issue in Go repository if O_LARGEFILE is used in regular Open but not used in Root.Open.

Address review feedback on the strict-root large-file fix:

- Move the StrictSystemRoot wrapper and the openLargeFile constant from
  pkg/fs into internal/osutil, alongside the other os-specific helpers.
  The constant cannot live in pkg/fs and be referenced from a wrapper
  there while osutil imports pkg/fs (filetimes) without creating an import
  cycle, so the wrapper moves too; it only depends on os.
- Drop the redundant "//go:build linux" tag. Instead of a filename OS
  suffix, use an explicit "linux || solaris || aix || zos" constraint,
  which are exactly the platforms where golang.org/x/sys/unix defines
  O_LARGEFILE. A plain "unix" constraint would fail to compile on
  darwin/BSD, where the constant does not exist.

cmd/ps3netsrv-go/server.go now calls osutil.NewStrictSystemRoot.
@Hirador

Hirador commented Jul 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed in the latest commit:

  • Moved into internal/osutil. The openLargeFile constant now lives there. I moved the StrictSystemRoot wrapper along with it rather than keeping it in pkg/fs, because internal/osutil already imports pkg/fs (via filetimes.go), so referencing the constant from a wrapper in pkg/fs would create an import cycle. The wrapper only depends on os, so it sits fine next to the other os-specific helpers. cmd/ps3netsrv-go/server.go now calls osutil.NewStrictSystemRoot.

  • Dropped the redundant //go:build linux. Instead of a filename OS suffix I used an explicit constraint. I went with //go:build linux || solaris || aix || zos rather than a plain unix one: those are exactly the platforms where golang.org/x/sys/unix defines O_LARGEFILE (zerrors_{linux_*,solaris_amd64,aix_*,zos_*}.go). A unix constraint would pull in darwin and the BSDs, where the constant is not defined and the build would fail — I confirmed GOOS=darwin builds now but breaks under a plain unix tag. The _other.go file carries the complementary !(...) constraint.

Verified go vet clean and cross-compiles for linux/{arm GOARM=5, 386, amd64}, darwin/arm64 and windows/amd64.

@xakep666
xakep666 merged commit 90f60fd into xakep666:master Jul 19, 2026
7 checks passed
@xakep666

Copy link
Copy Markdown
Owner

Thank you! I'll tag a new release later when finish work on socket-activation feature.

@Hirador
Hirador deleted the fix-largefile-32bit branch July 20, 2026 07:52
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.

2 participants