Skip to content

Commit

Permalink
Merge pull request #582 from dustin-ward/master
Browse files Browse the repository at this point in the history
Add support for zos/s390x
  • Loading branch information
drakkan committed Apr 6, 2024
2 parents dbd0165 + 3588ee8 commit 3c39a36
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 258 deletions.
11 changes: 5 additions & 6 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) {
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 All @@ -2143,7 +2142,7 @@ func toPflags(f int) uint32 {
// setuid, setgid and sticky in m, because we've historically supported those
// bits, and we mask off any non-permission bits.
func toChmodPerm(m os.FileMode) (perm uint32) {
const mask = os.ModePerm | s_ISUID | s_ISGID | s_ISVTX
const mask = os.ModePerm | os.FileMode(s_ISUID|s_ISGID|s_ISVTX)
perm = uint32(m & mask)

if m&os.ModeSetuid != 0 {
Expand Down
2 changes: 1 addition & 1 deletion client_integration_test.go
Expand Up @@ -974,7 +974,7 @@ func TestClientSetuid(t *testing.T) {
f.Close()

const allPerm = os.ModePerm | os.ModeSetuid | os.ModeSetgid | os.ModeSticky |
s_ISUID | s_ISGID | s_ISVTX
os.FileMode(s_ISUID|s_ISGID|s_ISVTX)

for _, c := range []struct {
goPerm os.FileMode
Expand Down
42 changes: 42 additions & 0 deletions errno_plan9.go
@@ -0,0 +1,42 @@
package sftp

import (
"os"
"syscall"
)

var EBADF = syscall.NewError("fd out of range or not open")

func wrapPathError(filepath string, err error) error {
if errno, ok := err.(syscall.ErrorString); 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.ErrorString) uint32 {
switch errno {
case "":
return sshFxOk
case syscall.ENOENT:
return sshFxNoSuchFile
case syscall.EPERM:
return sshFxPermissionDenied
}

return sshFxFailure
}

func translateSyscallError(err error) (uint32, bool) {
switch e := err.(type) {
case syscall.ErrorString:
return translateErrno(e), true
case *os.PathError:
debug("statusFromError,pathError: error is %T %#v", e.Err, e.Err)
if errno, ok := e.Err.(syscall.ErrorString); ok {
return translateErrno(errno), true
}
}
return 0, false
}
45 changes: 45 additions & 0 deletions errno_posix.go
@@ -0,0 +1,45 @@
//go:build !plan9
// +build !plan9

package sftp

import (
"os"
"syscall"
)

const EBADF = syscall.EBADF

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
}
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
89 changes: 89 additions & 0 deletions stat.go
@@ -0,0 +1,89 @@
package sftp

import (
"os"

sshfx "github.com/pkg/sftp/internal/encoding/ssh/filexfer"
)

// isRegular returns true if the mode describes a regular file.
func isRegular(mode uint32) bool {
return sshfx.FileMode(mode)&sshfx.ModeType == sshfx.ModeRegular
}

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

switch sshfx.FileMode(mode) & sshfx.ModeType {
case sshfx.ModeDevice:
fm |= os.ModeDevice
case sshfx.ModeCharDevice:
fm |= os.ModeDevice | os.ModeCharDevice
case sshfx.ModeDir:
fm |= os.ModeDir
case sshfx.ModeNamedPipe:
fm |= os.ModeNamedPipe
case sshfx.ModeSymlink:
fm |= os.ModeSymlink
case sshfx.ModeRegular:
// nothing to do
case sshfx.ModeSocket:
fm |= os.ModeSocket
}

if sshfx.FileMode(mode)&sshfx.ModeSetUID != 0 {
fm |= os.ModeSetuid
}
if sshfx.FileMode(mode)&sshfx.ModeSetGID != 0 {
fm |= os.ModeSetgid
}
if sshfx.FileMode(mode)&sshfx.ModeSticky != 0 {
fm |= os.ModeSticky
}

return fm
}

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

switch mode & os.ModeType {
case os.ModeDevice | os.ModeCharDevice:
ret |= sshfx.ModeCharDevice
case os.ModeDevice:
ret |= sshfx.ModeDevice
case os.ModeDir:
ret |= sshfx.ModeDir
case os.ModeNamedPipe:
ret |= sshfx.ModeNamedPipe
case os.ModeSymlink:
ret |= sshfx.ModeSymlink
case 0:
ret |= sshfx.ModeRegular
case os.ModeSocket:
ret |= sshfx.ModeSocket
}

if mode&os.ModeSetuid != 0 {
ret |= sshfx.ModeSetUID
}
if mode&os.ModeSetgid != 0 {
ret |= sshfx.ModeSetGID
}
if mode&os.ModeSticky != 0 {
ret |= sshfx.ModeSticky
}

return uint32(ret)
}

const (
s_ISUID = uint32(sshfx.ModeSetUID)
s_ISGID = uint32(sshfx.ModeSetGID)
s_ISVTX = uint32(sshfx.ModeSticky)
)

// S_IFMT is a legacy export. `sshfx.ModeType` should be used instead
const S_IFMT = uint32(sshfx.ModeType)
103 changes: 0 additions & 103 deletions stat_plan9.go

This file was deleted.

0 comments on commit 3c39a36

Please sign in to comment.