Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for zos/s390x #582

Merged
merged 4 commits into from Apr 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 4 additions & 5 deletions client.go
Expand Up @@ -2113,13 +2113,12 @@ func normaliseError(err error) error {
// Unsupported flags are ignored.
func toPflags(f int) uint32 {
var out uint32
switch f & os.O_WRONLY {
case os.O_WRONLY:
out |= sshFxfWrite
switch f & (os.O_RDONLY | os.O_WRONLY | os.O_RDWR) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider that POSIX standard requires the assertion: O_RDONLY | O_WRONLY == O_RDWR[1]

However, due to constant folding, I think we’re still fine here regardless.

1: https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html

case os.O_RDONLY:
out |= sshFxfRead
}
if f&os.O_RDWR == os.O_RDWR {
case os.O_WRONLY:
out |= sshFxfWrite
case os.O_RDWR:
out |= sshFxfRead | sshFxfWrite
}
if f&os.O_APPEND == os.O_APPEND {
Expand Down
7 changes: 7 additions & 0 deletions ls_formatting_test.go
Expand Up @@ -3,6 +3,7 @@ package sftp
import (
"os"
"regexp"
"runtime"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -101,6 +102,12 @@ func runLsTestHelper(t *testing.T, result, expectedType, path string) {
dateTime := strings.Join(fields[5:8], " ")
filename := fields[8]

if runtime.GOOS == "zos" {
// User and Group are always only uppercase characters on z/OS
user = strings.ToLower(user)
group = strings.ToLower(group)
}

// permissions (len 10, "drwxr-xr-x")
const (
rwxs = "[-r][-w][-xsS]"
Expand Down
9 changes: 7 additions & 2 deletions request-server_test.go
Expand Up @@ -780,8 +780,13 @@ func TestRequestReaddir(t *testing.T) {
}
}
_, err := p.cli.ReadDir("/foo_01")
assert.Equal(t, &StatusError{Code: sshFxFailure,
msg: " /foo_01: not a directory"}, err)
if runtime.GOOS == "zos" {
assert.Equal(t, &StatusError{Code: sshFxFailure,
msg: " /foo_01: EDC5135I Not a directory."}, err)
} else {
assert.Equal(t, &StatusError{Code: sshFxFailure,
msg: " /foo_01: not a directory"}, err)
}
_, err = p.cli.ReadDir("/does_not_exist")
assert.Equal(t, os.ErrNotExist, err)
di, err := p.cli.ReadDir("/")
Expand Down
9 changes: 7 additions & 2 deletions server_test.go
Expand Up @@ -99,9 +99,14 @@ func TestInvalidExtendedPacket(t *testing.T) {
// test that server handles concurrent requests correctly
func TestConcurrentRequests(t *testing.T) {
skipIfWindows(t)
filename := "/etc/passwd"
if runtime.GOOS == "plan9" {
var filename string
switch runtime.GOOS {
case "plan9":
filename = "/lib/ndb/local"
case "zos":
filename = "/etc/.shrc"
default:
filename = "/etc/passwd"
}
client, server := clientServerPair(t)
defer client.Close()
Expand Down
3 changes: 2 additions & 1 deletion stat_posix.go
@@ -1,5 +1,6 @@
//go:build !plan9
//go:build !plan9 && !zos
// +build !plan9
// +build !zos

package sftp

Expand Down
137 changes: 137 additions & 0 deletions stat_zos.go
puellanivis marked this conversation as resolved.
Show resolved Hide resolved
@@ -0,0 +1,137 @@
//go:build zos
// +build zos

package sftp

import (
"os"
"syscall"
)

const EBADF = syscall.EBADF

// z/OS syscall constants are not compatible with the stat structure returned
// from the server. Define the OpenBSD ones here instead.
const (
S_IFMT = 0xF000
S_IFIFO = 0x1000
S_IFCHR = 0x2000
S_IFDIR = 0x4000
S_IFBLK = 0x6000
S_IFREG = 0x8000
S_IFLNK = 0xA000
S_IFSOCK = 0xC000
puellanivis marked this conversation as resolved.
Show resolved Hide resolved
)

func wrapPathError(filepath string, err error) error {
if errno, ok := err.(syscall.Errno); ok {
return &os.PathError{Path: filepath, Err: errno}
}
return err
}

// translateErrno translates a syscall error number to a SFTP error code.
func translateErrno(errno syscall.Errno) uint32 {
switch errno {
case 0:
return sshFxOk
case syscall.ENOENT:
return sshFxNoSuchFile
case syscall.EACCES, syscall.EPERM:
return sshFxPermissionDenied
}

return sshFxFailure
}

func translateSyscallError(err error) (uint32, bool) {
switch e := err.(type) {
case syscall.Errno:
return translateErrno(e), true
case *os.PathError:
debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
if errno, ok := e.Err.(syscall.Errno); ok {
return translateErrno(errno), true
}
}
return 0, false
}

// isRegular returns true if the mode describes a regular file.
func isRegular(mode uint32) bool {
return mode&S_IFMT == S_IFREG
}

// toFileMode converts sftp filemode bits to the os.FileMode specification
func toFileMode(mode uint32) os.FileMode {
var fm = os.FileMode(mode & 0777)

switch mode & S_IFMT {
case S_IFBLK:
fm |= os.ModeDevice
case S_IFCHR:
fm |= os.ModeDevice | os.ModeCharDevice
case S_IFDIR:
fm |= os.ModeDir
case S_IFIFO:
fm |= os.ModeNamedPipe
case S_IFLNK:
fm |= os.ModeSymlink
case S_IFREG:
// nothing to do
case S_IFSOCK:
fm |= os.ModeSocket
}

if mode&syscall.S_ISUID != 0 {
fm |= os.ModeSetuid
}
if mode&syscall.S_ISGID != 0 {
fm |= os.ModeSetgid
}
if mode&syscall.S_ISVTX != 0 {
fm |= os.ModeSticky
}

return fm
}

// fromFileMode converts from the os.FileMode specification to sftp filemode bits
func fromFileMode(mode os.FileMode) uint32 {
ret := uint32(mode & os.ModePerm)

switch mode & os.ModeType {
case os.ModeDevice | os.ModeCharDevice:
ret |= S_IFCHR
case os.ModeDevice:
ret |= S_IFBLK
case os.ModeDir:
ret |= S_IFDIR
case os.ModeNamedPipe:
ret |= S_IFIFO
case os.ModeSymlink:
ret |= S_IFLNK
case 0:
ret |= S_IFREG
case os.ModeSocket:
ret |= S_IFSOCK
}

if mode&os.ModeSetuid != 0 {
ret |= syscall.S_ISUID
}
if mode&os.ModeSetgid != 0 {
ret |= syscall.S_ISGID
}
if mode&os.ModeSticky != 0 {
ret |= syscall.S_ISVTX
}

return ret
}

const (
s_ISUID = syscall.S_ISUID
s_ISGID = syscall.S_ISGID
s_ISVTX = syscall.S_ISVTX
)
3 changes: 2 additions & 1 deletion syscall_good.go
@@ -1,6 +1,7 @@
//go:build !plan9 && !windows && (!js || !wasm)
//go:build !plan9 && !windows && !zos && (!js || !wasm)
// +build !plan9
// +build !windows
// +build !zos
// +build !js !wasm

package sftp
Expand Down