diff --git a/cmd/ps3netsrv-go/server.go b/cmd/ps3netsrv-go/server.go index 9194297..f107848 100644 --- a/cmd/ps3netsrv-go/server.go +++ b/cmd/ps3netsrv-go/server.go @@ -138,10 +138,13 @@ func (sapp *serverApp) server() error { sysRoot := fs.SystemRoot(fs.NewRelaxedSystemRoot(sapp.Root)) if sapp.StrictRoot { - sysRoot, err = os.OpenRoot(sapp.Root) + root, err := os.OpenRoot(sapp.Root) if err != nil { return fmt.Errorf("open root failed: %w", err) } + // Wrap so large files (>2 GiB, i.e. every PS3 ISO) can be opened on + // 32-bit platforms; os.Root's openat omits O_LARGEFILE. + sysRoot = osutil.NewStrictSystemRoot(root) } s := server.Server[handler.State]{ diff --git a/internal/osutil/largefile.go b/internal/osutil/largefile.go new file mode 100644 index 0000000..a90db70 --- /dev/null +++ b/internal/osutil/largefile.go @@ -0,0 +1,14 @@ +//go:build linux || solaris || aix || zos + +package osutil + +import "golang.org/x/sys/unix" + +// openLargeFile is O_LARGEFILE on 32-bit targets (0x8000 on 386, 0x20000 on +// arm, ...) and 0 on 64-bit ones. Without it, os.Root's openat rejects files +// larger than 2 GiB on a 32-bit kernel with EOVERFLOW ("value too large for +// defined data type"), which breaks streaming of PS3 ISOs on legacy 32-bit NAS +// hardware. The constant is only defined by x/sys/unix on the platforms in the +// build constraint above (Linux, Solaris, AIX, z/OS); see largefile_other.go +// for the rest. +const openLargeFile = unix.O_LARGEFILE diff --git a/internal/osutil/largefile_other.go b/internal/osutil/largefile_other.go new file mode 100644 index 0000000..f64d510 --- /dev/null +++ b/internal/osutil/largefile_other.go @@ -0,0 +1,8 @@ +//go:build !(linux || solaris || aix || zos) + +package osutil + +// openLargeFile has no effect on platforms where x/sys/unix does not define +// O_LARGEFILE (darwin, the BSDs, Windows, ...); large-file access there does +// not depend on an explicit open flag. +const openLargeFile = 0 diff --git a/internal/osutil/strict_root.go b/internal/osutil/strict_root.go new file mode 100644 index 0000000..7cc594a --- /dev/null +++ b/internal/osutil/strict_root.go @@ -0,0 +1,31 @@ +package osutil + +import "os" + +// StrictSystemRoot wraps *os.Root to make it usable on 32-bit platforms. +// +// os.Root opens files via a raw openat that does not set O_LARGEFILE, so on a +// 32-bit kernel any file larger than 2 GiB fails to open with EOVERFLOW. Every +// PS3 ISO exceeds that, which makes strict-root mode unusable for PS3 streaming +// on legacy 32-bit NAS hardware. This wrapper forwards O_LARGEFILE through +// os.Root.OpenFile (which passes the flag verbatim to openat) while keeping all +// of os.Root's symlink-safe, path-traversal-safe traversal. +// +// It satisfies pkg/fs.SystemRoot: Open/Create are overridden here, and the +// embedded *os.Root supplies Stat, Remove and Mkdir. +type StrictSystemRoot struct { + *os.Root +} + +// NewStrictSystemRoot wraps an already-opened *os.Root. +func NewStrictSystemRoot(root *os.Root) StrictSystemRoot { + return StrictSystemRoot{Root: root} +} + +func (r StrictSystemRoot) Open(path string) (*os.File, error) { + return r.Root.OpenFile(path, os.O_RDONLY|openLargeFile, 0) +} + +func (r StrictSystemRoot) Create(path string) (*os.File, error) { + return r.Root.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC|openLargeFile, 0666) +}