Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion cmd/ps3netsrv-go/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -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]{
Expand Down
14 changes: 14 additions & 0 deletions internal/osutil/largefile.go
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions internal/osutil/largefile_other.go
Original file line number Diff line number Diff line change
@@ -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
31 changes: 31 additions & 0 deletions internal/osutil/strict_root.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading