From c771381d2cd8157dd5b9667b10b0002e869c0ff5 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 12 Dec 2023 15:35:48 +0200 Subject: [PATCH 1/6] fix: add support for attrs in sshFxpOpenPacket for Server --- packet.go | 17 ++++++-- packet_test.go | 18 ++++++++ server.go | 113 +++++++++++++++++++++++++++++++++++++++---------- server_test.go | 26 ++++++++++++ 4 files changed, 148 insertions(+), 26 deletions(-) diff --git a/packet.go b/packet.go index 1232ff1e..597b4356 100644 --- a/packet.go +++ b/packet.go @@ -681,12 +681,13 @@ type sshFxpOpenPacket struct { ID uint32 Path string Pflags uint32 - Flags uint32 // ignored + Flags uint32 + Attrs interface{} } func (p *sshFxpOpenPacket) id() uint32 { return p.ID } -func (p *sshFxpOpenPacket) MarshalBinary() ([]byte, error) { +func (p *sshFxpOpenPacket) marshalPacket() ([]byte, []byte, error) { l := 4 + 1 + 4 + // uint32(length) + byte(type) + uint32(id) 4 + len(p.Path) + 4 + 4 @@ -698,7 +699,14 @@ func (p *sshFxpOpenPacket) MarshalBinary() ([]byte, error) { b = marshalUint32(b, p.Pflags) b = marshalUint32(b, p.Flags) - return b, nil + payload := marshal(nil, p.Attrs) + + return b, payload, nil +} + +func (p *sshFxpOpenPacket) MarshalBinary() ([]byte, error) { + header, payload, err := p.marshalPacket() + return append(header, payload...), err } func (p *sshFxpOpenPacket) UnmarshalBinary(b []byte) error { @@ -709,9 +717,10 @@ func (p *sshFxpOpenPacket) UnmarshalBinary(b []byte) error { return err } else if p.Pflags, b, err = unmarshalUint32Safe(b); err != nil { return err - } else if p.Flags, _, err = unmarshalUint32Safe(b); err != nil { + } else if p.Flags, b, err = unmarshalUint32Safe(b); err != nil { return err } + p.Attrs = b return nil } diff --git a/packet_test.go b/packet_test.go index cbee5e4c..f5ad88b6 100644 --- a/packet_test.go +++ b/packet_test.go @@ -387,6 +387,24 @@ func TestSendPacket(t *testing.T) { 0x0, 0x0, 0x0, 0x0, }, }, + { + packet: &sshFxpOpenPacket{ + ID: 3, + Path: "/foo", + Pflags: flags(os.O_WRONLY | os.O_CREATE | os.O_TRUNC), + Flags: sshFileXferAttrPermissions, + Attrs: []uint8{0x0, 0x0, 0x1, 0xed}, // 0o755 + }, + want: []byte{ + 0x0, 0x0, 0x0, 0x19, + 0x3, + 0x0, 0x0, 0x0, 0x3, + 0x0, 0x0, 0x0, 0x4, '/', 'f', 'o', 'o', + 0x0, 0x0, 0x0, 0x1a, + 0x0, 0x0, 0x0, 0x4, + 0x0, 0x0, 0x1, 0xed, + }, + }, { packet: &sshFxpWritePacket{ ID: 124, diff --git a/server.go b/server.go index 2e419f59..2b9a2d14 100644 --- a/server.go +++ b/server.go @@ -467,6 +467,59 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket { return statusFromError(p.ID, err) } + // Both `sshFileXferAttrPermissions` and `sshFileXferAttrACmodTime` are set + // by e.g. `sftp`. Just in case, we handle all other cases as well. + if b, ok := p.Attrs.([]byte); ok { + if (p.Flags & sshFileXferAttrSize) != 0 { + var size uint64 + if size, b, err = unmarshalUint64Safe(b); err == nil { + err = f.Truncate(int64(size)) + } + } + if err != nil { + _ = f.Close() + return statusFromError(p.ID, err) + } + if (p.Flags & sshFileXferAttrUIDGID) != 0 { + var uid uint32 + var gid uint32 + if uid, b, err = unmarshalUint32Safe(b); err != nil { + } else if gid, b, err = unmarshalUint32Safe(b); err != nil { + } else { + err = f.Chown(int(uid), int(gid)) + } + } + if err != nil { + _ = f.Close() + return statusFromError(p.ID, err) + } + if (p.Flags & sshFileXferAttrPermissions) != 0 { + var mode uint32 + if mode, b, err = unmarshalUint32Safe(b); err == nil { + err = f.Chmod(os.FileMode(mode)) + } + } + if err != nil { + _ = f.Close() + return statusFromError(p.ID, err) + } + if (p.Flags & sshFileXferAttrACmodTime) != 0 { + var atime uint32 + var mtime uint32 + if atime, b, err = unmarshalUint32Safe(b); err != nil { + } else if mtime, _, err = unmarshalUint32Safe(b); err != nil { + } else { + atimeT := time.Unix(int64(atime), 0) + mtimeT := time.Unix(int64(mtime), 0) + err = os.Chtimes(f.Name(), atimeT, mtimeT) + } + } + if err != nil { + _ = f.Close() + return statusFromError(p.ID, err) + } + } + handle := svr.nextHandle(f) return &sshFxpHandlePacket{ID: p.ID, Handle: handle} } @@ -509,33 +562,41 @@ func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket { err = os.Truncate(p.Path, int64(size)) } } + if err != nil { + return statusFromError(p.ID, err) + } + if (p.Flags & sshFileXferAttrUIDGID) != 0 { + var uid uint32 + var gid uint32 + if uid, b, err = unmarshalUint32Safe(b); err != nil { + } else if gid, b, err = unmarshalUint32Safe(b); err != nil { + } else { + err = os.Chown(p.Path, int(uid), int(gid)) + } + } + if err != nil { + return statusFromError(p.ID, err) + } if (p.Flags & sshFileXferAttrPermissions) != 0 { var mode uint32 if mode, b, err = unmarshalUint32Safe(b); err == nil { err = os.Chmod(p.Path, os.FileMode(mode)) } } + if err != nil { + return statusFromError(p.ID, err) + } if (p.Flags & sshFileXferAttrACmodTime) != 0 { var atime uint32 var mtime uint32 if atime, b, err = unmarshalUint32Safe(b); err != nil { - } else if mtime, b, err = unmarshalUint32Safe(b); err != nil { + } else if mtime, _, err = unmarshalUint32Safe(b); err != nil { } else { atimeT := time.Unix(int64(atime), 0) mtimeT := time.Unix(int64(mtime), 0) err = os.Chtimes(p.Path, atimeT, mtimeT) } } - if (p.Flags & sshFileXferAttrUIDGID) != 0 { - var uid uint32 - var gid uint32 - if uid, b, err = unmarshalUint32Safe(b); err != nil { - } else if gid, _, err = unmarshalUint32Safe(b); err != nil { - } else { - err = os.Chown(p.Path, int(uid), int(gid)) - } - } - return statusFromError(p.ID, err) } @@ -556,33 +617,41 @@ func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket { err = f.Truncate(int64(size)) } } + if err != nil { + return statusFromError(p.ID, err) + } + if (p.Flags & sshFileXferAttrUIDGID) != 0 { + var uid uint32 + var gid uint32 + if uid, b, err = unmarshalUint32Safe(b); err != nil { + } else if gid, b, err = unmarshalUint32Safe(b); err != nil { + } else { + err = f.Chown(int(uid), int(gid)) + } + } + if err != nil { + return statusFromError(p.ID, err) + } if (p.Flags & sshFileXferAttrPermissions) != 0 { var mode uint32 if mode, b, err = unmarshalUint32Safe(b); err == nil { err = f.Chmod(os.FileMode(mode)) } } + if err != nil { + return statusFromError(p.ID, err) + } if (p.Flags & sshFileXferAttrACmodTime) != 0 { var atime uint32 var mtime uint32 if atime, b, err = unmarshalUint32Safe(b); err != nil { - } else if mtime, b, err = unmarshalUint32Safe(b); err != nil { + } else if mtime, _, err = unmarshalUint32Safe(b); err != nil { } else { atimeT := time.Unix(int64(atime), 0) mtimeT := time.Unix(int64(mtime), 0) err = os.Chtimes(f.Name(), atimeT, mtimeT) } } - if (p.Flags & sshFileXferAttrUIDGID) != 0 { - var uid uint32 - var gid uint32 - if uid, b, err = unmarshalUint32Safe(b); err != nil { - } else if gid, _, err = unmarshalUint32Safe(b); err != nil { - } else { - err = f.Chown(int(uid), int(gid)) - } - } - return statusFromError(p.ID, err) } diff --git a/server_test.go b/server_test.go index 87beece5..755d6ed0 100644 --- a/server_test.go +++ b/server_test.go @@ -208,6 +208,32 @@ func TestOpenStatRace(t *testing.T) { checkServerAllocator(t, server) } +func TestOpenWithPermissions(t *testing.T) { + client, server := clientServerPair(t) + defer client.Close() + defer server.Close() + + tmppath := path.Join(os.TempDir(), "open_permissions") + pflags := flags(os.O_RDWR | os.O_CREATE | os.O_TRUNC) + ch := make(chan result, 2) + id1 := client.nextID() + client.dispatchRequest(ch, &sshFxpOpenPacket{ + ID: id1, + Path: tmppath, + Pflags: pflags, + Flags: sshFileXferAttrPermissions, + Attrs: []byte{0x0, 0x0, 0x1, 0xe5}, // 0o745 -- a slightly strange permission to test. + }) + <-ch + stat, err := os.Stat(tmppath) + assert.NoError(t, err) + if !assert.Equal(t, os.FileMode(0o745), stat.Mode()&os.ModePerm) { + t.Logf("stat.Mode() = %v", stat.Mode()) + } + os.Remove(tmppath) + checkServerAllocator(t, server) +} + // Ensure that proper error codes are returned for non existent files, such // that they are mapped back to a 'not exists' error on the client side. func TestStatNonExistent(t *testing.T) { From 8218e927edb01a04bd92aabf0f57ac2c4ec65997 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Tue, 12 Dec 2023 16:41:45 +0200 Subject: [PATCH 2/6] avoid changing permission on existing files --- server.go | 49 +++++++++++++++++++++++++++++++++++-------------- server_test.go | 19 +++++++++++++++++++ 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/server.go b/server.go index 2b9a2d14..97bd86a7 100644 --- a/server.go +++ b/server.go @@ -462,22 +462,29 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket { osFlags |= os.O_EXCL } - f, err := os.OpenFile(svr.toLocalPath(p.Path), osFlags, 0o644) - if err != nil { - return statusFromError(p.ID, err) - } + name := svr.toLocalPath(p.Path) + mode := os.FileMode(0o644) + + var applyAttrs []func(*os.File) error // Both `sshFileXferAttrPermissions` and `sshFileXferAttrACmodTime` are set // by e.g. `sftp`. Just in case, we handle all other cases as well. - if b, ok := p.Attrs.([]byte); ok { + // Only apply for newly created files. + var err error + useAttrs := true + if _, err := os.Stat(name); err == nil { + useAttrs = false + } + if b, ok := p.Attrs.([]byte); useAttrs && ok { if (p.Flags & sshFileXferAttrSize) != 0 { var size uint64 if size, b, err = unmarshalUint64Safe(b); err == nil { - err = f.Truncate(int64(size)) + applyAttrs = append(applyAttrs, func(f *os.File) error { + return f.Truncate(int64(size)) + }) } } if err != nil { - _ = f.Close() return statusFromError(p.ID, err) } if (p.Flags & sshFileXferAttrUIDGID) != 0 { @@ -486,21 +493,22 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket { if uid, b, err = unmarshalUint32Safe(b); err != nil { } else if gid, b, err = unmarshalUint32Safe(b); err != nil { } else { - err = f.Chown(int(uid), int(gid)) + applyAttrs = append(applyAttrs, func(f *os.File) error { + return f.Chown(int(uid), int(gid)) + }) } } if err != nil { - _ = f.Close() return statusFromError(p.ID, err) } if (p.Flags & sshFileXferAttrPermissions) != 0 { - var mode uint32 - if mode, b, err = unmarshalUint32Safe(b); err == nil { - err = f.Chmod(os.FileMode(mode)) + var attrMode uint32 + if attrMode, b, err = unmarshalUint32Safe(b); err == nil { + // Optionally, we could apply umask here. + mode = os.FileMode(attrMode) } } if err != nil { - _ = f.Close() return statusFromError(p.ID, err) } if (p.Flags & sshFileXferAttrACmodTime) != 0 { @@ -511,10 +519,23 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket { } else { atimeT := time.Unix(int64(atime), 0) mtimeT := time.Unix(int64(mtime), 0) - err = os.Chtimes(f.Name(), atimeT, mtimeT) + applyAttrs = append(applyAttrs, func(f *os.File) error { + return os.Chtimes(f.Name(), atimeT, mtimeT) + }) } } if err != nil { + return statusFromError(p.ID, err) + } + } + + f, err := os.OpenFile(name, osFlags, mode) + if err != nil { + return statusFromError(p.ID, err) + } + + for _, applyAttr := range applyAttrs { + if err := applyAttr(f); err != nil { _ = f.Close() return statusFromError(p.ID, err) } diff --git a/server_test.go b/server_test.go index 755d6ed0..d0e2b8ca 100644 --- a/server_test.go +++ b/server_test.go @@ -217,6 +217,9 @@ func TestOpenWithPermissions(t *testing.T) { pflags := flags(os.O_RDWR | os.O_CREATE | os.O_TRUNC) ch := make(chan result, 2) id1 := client.nextID() + id2 := client.nextID() + + // New files should have their permissions set. client.dispatchRequest(ch, &sshFxpOpenPacket{ ID: id1, Path: tmppath, @@ -230,6 +233,22 @@ func TestOpenWithPermissions(t *testing.T) { if !assert.Equal(t, os.FileMode(0o745), stat.Mode()&os.ModePerm) { t.Logf("stat.Mode() = %v", stat.Mode()) } + + // Existing files should not have their permissions changed. + client.dispatchRequest(ch, &sshFxpOpenPacket{ + ID: id2, + Path: tmppath, + Pflags: pflags, + Flags: sshFileXferAttrPermissions, + Attrs: []byte{0x0, 0x0, 0x1, 0xed}, // 0o755 + }) + <-ch + stat, err = os.Stat(tmppath) + assert.NoError(t, err, "mode should not have changed") + if !assert.Equal(t, os.FileMode(0o745), stat.Mode()&os.ModePerm) { + t.Logf("stat.Mode() = %v", stat.Mode()) + } + os.Remove(tmppath) checkServerAllocator(t, server) } From 211f87b4a632417f3286c725ce1a5fccf4890c83 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Sat, 16 Dec 2023 16:50:24 +0200 Subject: [PATCH 3/6] simplify attrs for sshFxpOpenPacket --- server.go | 77 +++++-------------------------------------------------- 1 file changed, 6 insertions(+), 71 deletions(-) diff --git a/server.go b/server.go index 97bd86a7..3e5fcf46 100644 --- a/server.go +++ b/server.go @@ -462,85 +462,20 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket { osFlags |= os.O_EXCL } - name := svr.toLocalPath(p.Path) mode := os.FileMode(0o644) - - var applyAttrs []func(*os.File) error - - // Both `sshFileXferAttrPermissions` and `sshFileXferAttrACmodTime` are set - // by e.g. `sftp`. Just in case, we handle all other cases as well. - // Only apply for newly created files. - var err error - useAttrs := true - if _, err := os.Stat(name); err == nil { - useAttrs = false - } - if b, ok := p.Attrs.([]byte); useAttrs && ok { - if (p.Flags & sshFileXferAttrSize) != 0 { - var size uint64 - if size, b, err = unmarshalUint64Safe(b); err == nil { - applyAttrs = append(applyAttrs, func(f *os.File) error { - return f.Truncate(int64(size)) - }) - } - } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrUIDGID) != 0 { - var uid uint32 - var gid uint32 - if uid, b, err = unmarshalUint32Safe(b); err != nil { - } else if gid, b, err = unmarshalUint32Safe(b); err != nil { - } else { - applyAttrs = append(applyAttrs, func(f *os.File) error { - return f.Chown(int(uid), int(gid)) - }) - } - } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrPermissions) != 0 { - var attrMode uint32 - if attrMode, b, err = unmarshalUint32Safe(b); err == nil { - // Optionally, we could apply umask here. - mode = os.FileMode(attrMode) - } - } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrACmodTime) != 0 { - var atime uint32 - var mtime uint32 - if atime, b, err = unmarshalUint32Safe(b); err != nil { - } else if mtime, _, err = unmarshalUint32Safe(b); err != nil { - } else { - atimeT := time.Unix(int64(atime), 0) - mtimeT := time.Unix(int64(mtime), 0) - applyAttrs = append(applyAttrs, func(f *os.File) error { - return os.Chtimes(f.Name(), atimeT, mtimeT) - }) - } - } - if err != nil { - return statusFromError(p.ID, err) - } + // Like OpenSSH, we only handle permissions here if the file is + // being created. Otherwise, the permissions are ignored. + if b, ok := p.Attrs.([]byte); ok && (p.Flags&sshFileXferAttrPermissions) != 0 { + fs, _ := unmarshalFileStat(p.Flags, b) + mode = os.FileMode(fs.Mode) } + name := svr.toLocalPath(p.Path) f, err := os.OpenFile(name, osFlags, mode) if err != nil { return statusFromError(p.ID, err) } - for _, applyAttr := range applyAttrs { - if err := applyAttr(f); err != nil { - _ = f.Close() - return statusFromError(p.ID, err) - } - } - handle := svr.nextHandle(f) return &sshFxpHandlePacket{ID: p.ID, Handle: handle} } From a407de13056a75b6be8de25f74f3a6cb065c6f0d Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Sat, 16 Dec 2023 16:54:07 +0200 Subject: [PATCH 4/6] switch to toFileMode --- server.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server.go b/server.go index 3e5fcf46..4b2923d7 100644 --- a/server.go +++ b/server.go @@ -467,7 +467,7 @@ func (p *sshFxpOpenPacket) respond(svr *Server) responsePacket { // being created. Otherwise, the permissions are ignored. if b, ok := p.Attrs.([]byte); ok && (p.Flags&sshFileXferAttrPermissions) != 0 { fs, _ := unmarshalFileStat(p.Flags, b) - mode = os.FileMode(fs.Mode) + mode = toFileMode(fs.Mode) } name := svr.toLocalPath(p.Path) @@ -536,7 +536,7 @@ func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket { if (p.Flags & sshFileXferAttrPermissions) != 0 { var mode uint32 if mode, b, err = unmarshalUint32Safe(b); err == nil { - err = os.Chmod(p.Path, os.FileMode(mode)) + err = os.Chmod(p.Path, toFileMode(mode)) } } if err != nil { @@ -591,7 +591,7 @@ func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket { if (p.Flags & sshFileXferAttrPermissions) != 0 { var mode uint32 if mode, b, err = unmarshalUint32Safe(b); err == nil { - err = f.Chmod(os.FileMode(mode)) + err = f.Chmod(toFileMode(mode)) } } if err != nil { From 268f8b004bdb2a504f92a09782f5d633f74608bb Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Sat, 16 Dec 2023 16:56:54 +0200 Subject: [PATCH 5/6] fix rogue os.FileMode conversion in FileStat --- request-attrs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/request-attrs.go b/request-attrs.go index b5c95b4a..50efc13f 100644 --- a/request-attrs.go +++ b/request-attrs.go @@ -52,7 +52,7 @@ func (r *Request) AttrFlags() FileAttrFlags { // FileMode returns the Mode SFTP file attributes wrapped as os.FileMode func (a FileStat) FileMode() os.FileMode { - return os.FileMode(a.Mode) + return toFileMode(a.Mode) } // Attributes parses file attributes byte blob and return them in a From cb4604138d5c7d14569e5fc3946a76e8a6807ce3 Mon Sep 17 00:00:00 2001 From: Mathias Fredriksson Date: Sat, 16 Dec 2023 17:11:14 +0200 Subject: [PATCH 6/6] clean up attr usage in stat --- server.go | 105 ++++++++++++------------------------------------------ 1 file changed, 22 insertions(+), 83 deletions(-) diff --git a/server.go b/server.go index 4b2923d7..8b362eb4 100644 --- a/server.go +++ b/server.go @@ -505,55 +505,11 @@ func (p *sshFxpReaddirPacket) respond(svr *Server) responsePacket { } func (p *sshFxpSetstatPacket) respond(svr *Server) responsePacket { - // additional unmarshalling is required for each possibility here - b := p.Attrs.([]byte) - var err error - p.Path = svr.toLocalPath(p.Path) debug("setstat name \"%s\"", p.Path) - if (p.Flags & sshFileXferAttrSize) != 0 { - var size uint64 - if size, b, err = unmarshalUint64Safe(b); err == nil { - err = os.Truncate(p.Path, int64(size)) - } - } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrUIDGID) != 0 { - var uid uint32 - var gid uint32 - if uid, b, err = unmarshalUint32Safe(b); err != nil { - } else if gid, b, err = unmarshalUint32Safe(b); err != nil { - } else { - err = os.Chown(p.Path, int(uid), int(gid)) - } - } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrPermissions) != 0 { - var mode uint32 - if mode, b, err = unmarshalUint32Safe(b); err == nil { - err = os.Chmod(p.Path, toFileMode(mode)) - } - } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrACmodTime) != 0 { - var atime uint32 - var mtime uint32 - if atime, b, err = unmarshalUint32Safe(b); err != nil { - } else if mtime, _, err = unmarshalUint32Safe(b); err != nil { - } else { - atimeT := time.Unix(int64(atime), 0) - mtimeT := time.Unix(int64(mtime), 0) - err = os.Chtimes(p.Path, atimeT, mtimeT) - } - } - return statusFromError(p.ID, err) + b := p.Attrs.([]byte) + return statusFromError(p.ID, applyAttrsToFile(p.Path, p.Flags, b)) } func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket { @@ -562,53 +518,36 @@ func (p *sshFxpFsetstatPacket) respond(svr *Server) responsePacket { return statusFromError(p.ID, EBADF) } - // additional unmarshalling is required for each possibility here + debug("fsetstat name \"%s\"", f.Name()) b := p.Attrs.([]byte) - var err error + return statusFromError(p.ID, applyAttrsToFile(f.Name(), p.Flags, b)) +} - debug("fsetstat name \"%s\"", f.Name()) - if (p.Flags & sshFileXferAttrSize) != 0 { - var size uint64 - if size, b, err = unmarshalUint64Safe(b); err == nil { - err = f.Truncate(int64(size)) +func applyAttrsToFile(name string, flags uint32, attrs []byte) error { + fs, _ := unmarshalFileStat(flags, attrs) + if (flags & sshFileXferAttrSize) != 0 { + if err := os.Truncate(name, int64(fs.Size)); err != nil { + return err } } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrUIDGID) != 0 { - var uid uint32 - var gid uint32 - if uid, b, err = unmarshalUint32Safe(b); err != nil { - } else if gid, b, err = unmarshalUint32Safe(b); err != nil { - } else { - err = f.Chown(int(uid), int(gid)) + if (flags & sshFileXferAttrUIDGID) != 0 { + if err := os.Chown(name, int(fs.UID), int(fs.GID)); err != nil { + return err } } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrPermissions) != 0 { - var mode uint32 - if mode, b, err = unmarshalUint32Safe(b); err == nil { - err = f.Chmod(toFileMode(mode)) + if (flags & sshFileXferAttrPermissions) != 0 { + if err := os.Chmod(name, toFileMode(fs.Mode)); err != nil { + return err } } - if err != nil { - return statusFromError(p.ID, err) - } - if (p.Flags & sshFileXferAttrACmodTime) != 0 { - var atime uint32 - var mtime uint32 - if atime, b, err = unmarshalUint32Safe(b); err != nil { - } else if mtime, _, err = unmarshalUint32Safe(b); err != nil { - } else { - atimeT := time.Unix(int64(atime), 0) - mtimeT := time.Unix(int64(mtime), 0) - err = os.Chtimes(f.Name(), atimeT, mtimeT) + if (flags & sshFileXferAttrACmodTime) != 0 { + atimeT := time.Unix(int64(fs.Atime), 0) + mtimeT := time.Unix(int64(fs.Mtime), 0) + if err := os.Chtimes(name, atimeT, mtimeT); err != nil { + return err } } - return statusFromError(p.ID, err) + return nil } func statusFromError(id uint32, err error) *sshFxpStatusPacket {