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 3 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
11 changes: 5 additions & 6 deletions client.go
Original file line number Diff line number Diff line change
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 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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
93 changes: 93 additions & 0 deletions stat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
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)
)

// Legacy export:
Copy link
Collaborator

Choose a reason for hiding this comment

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

[golint] godoc should start with S_IFMT

(I know the comments on the pre-existing code wasn’t ideal, and you’re just moving it around.)

//
// Go defines S_IFMT on windows, plan9 and js/wasm as 0x1f000 instead of
// 0xf000. None of the the other S_IFxyz values include the "1" (in 0x1f000)
// which prevents them from matching the bitmask.
const S_IFMT = uint32(sshfx.ModeType)
103 changes: 0 additions & 103 deletions stat_plan9.go

This file was deleted.

Loading